From 975fb6f000918085d1f5ba4ac6eb95c60411dae9 Mon Sep 17 00:00:00 2001
From: AidenRushbrooke <72034940+AidenRushbrooke@users.noreply.github.com>
Date: Sun, 7 Nov 2021 22:35:33 +0000
Subject: Added support for strings and improved print statement syntax

---
 src/Compiler/Parser.java       | 60 ++++++++++++++++++++++++++++++++---------
 src/Compiler/Statement.java    | 25 ++++++++++++++---
 src/Compiler/TokenScanner.java | 15 +++++++++++
 src/Compiler/TokenType.java    |  6 ++---
 src/Compiler/Translator.java   | 61 +++++++++++++++++++++++++++++++-----------
 5 files changed, 131 insertions(+), 36 deletions(-)

(limited to 'src/Compiler')

diff --git a/src/Compiler/Parser.java b/src/Compiler/Parser.java
index 68851bf..7eb22df 100644
--- a/src/Compiler/Parser.java
+++ b/src/Compiler/Parser.java
@@ -28,19 +28,37 @@ public class Parser {
     //Clean up and reduce code mess
     private Statement declaration(){
         if (matchAndAdvance(TokenType.INT)){
-            if (matchOrError(TokenType.DEFINE, ":: Required for variable definition")){
-                if (matchOrError(TokenType.IDENTIFIER,"Expected variable name.")){
-                    Token varName = getPreviousToken();
-                    return new Statement.VariableDeclaration(varName,"int");
-                } 
-            }
+            matchOrError(TokenType.DEFINE, ":: Required for variable definition");
+            matchOrError(TokenType.IDENTIFIER,"Expected variable name.");
+            Token varName = getPreviousToken();
+            return new Statement.VariableDeclaration(varName,"int");
         } else if (matchAndAdvance(TokenType.REAL)){
-            if (matchOrError(TokenType.DEFINE, ":: Required for variable definition")){
-                if (matchOrError(TokenType.IDENTIFIER,"Expected variable name.")){
-                    Token varName = getPreviousToken();
-                    return new Statement.VariableDeclaration(varName,"real");
-                } 
+            matchOrError(TokenType.DEFINE, ":: Required for variable definition");
+            matchOrError(TokenType.IDENTIFIER,"Expected variable name.");
+            Token varName = getPreviousToken();
+            return new Statement.VariableDeclaration(varName,"real");
+        
+        //Could be improved significatly when verifiying length is a positive integer
+        } else if (matchAndAdvance(TokenType.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();
+            if(!(length instanceof Expression.Literal)){
+                throw error("String length must be a number");
+            }
+            if(!((Expression.Literal)length).type.equals("int")){
+                throw error("String length must be a integer"); 
             }
+            if((int)((Expression.Literal)length).value.value<1){
+                throw error("String length must be greater then 0"); 
+            }
+            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();
+            return new Statement.StringDeclaration(varName,length);
         }
         
         return statement();
@@ -48,8 +66,7 @@ public class Parser {
 
     private Statement statement(){
         if (matchAndAdvance(TokenType.PRINT)){
-            Expression expression = expression();
-            return new Statement.PrintStatement(expression);
+            return printStatement();
         }else if (matchAndAdvance(TokenType.IF)){
             Statement statement = ifStatement();
             return statement;
@@ -57,6 +74,20 @@ public class Parser {
         return expressionStatement();
     }
 
+    private Statement printStatement(){
+        matchOrError(TokenType.STAR, "Syntax, print *, item1, item2...");
+        List<Expression> exprList = new ArrayList<>();
+        while(!matchAndAdvance(TokenType.ENDPRINT)){
+            if(checkEOF()){
+                throw error("Missing close parentheses");
+            }
+            matchOrError(TokenType.COMMA, "Print items must be seperated by ,");
+            Expression expr = expression();
+            exprList.add(expr);
+        }
+        return new Statement.PrintStatement(exprList);
+    }
+
     //Could be cleaned up to handle else statements better
     private Statement ifStatement(){
         Expression condition = expression();
@@ -155,6 +186,9 @@ public class Parser {
                 return new Expression.Literal(getPreviousToken(),"double");
             }
         }
+        if (matchAndAdvance(TokenType.STRING)){
+            return new Expression.Literal(getPreviousToken(), "string");
+        }
 
         if (matchAndAdvance(TokenType.IDENTIFIER)) {
 
diff --git a/src/Compiler/Statement.java b/src/Compiler/Statement.java
index a74009d..b8916aa 100644
--- a/src/Compiler/Statement.java
+++ b/src/Compiler/Statement.java
@@ -48,16 +48,33 @@ abstract class Statement {
 
         @Override
         public String getStatmentType() {
-            return "vardec";
+            return "varDec";
+        }
+
+    }
+
+    static class StringDeclaration extends Statement{
+        StringDeclaration(Token name,Expression length){
+            this.name=name;
+            this.length=length;
+        }
+
+
+        final Token name;
+        final Expression length;
+
+        @Override
+        public String getStatmentType() {
+            return "stringDec";
         }
 
     }
 
     static class PrintStatement extends Statement{
-        PrintStatement(Expression expr){
-            this.expr=expr;
+        PrintStatement(List<Expression> exprList){
+            this.exprList=exprList;
         }
-        final Expression expr;
+        final List<Expression> exprList;
 
         @Override
         public String getStatmentType() {
diff --git a/src/Compiler/TokenScanner.java b/src/Compiler/TokenScanner.java
index 27501fa..f2fcab3 100644
--- a/src/Compiler/TokenScanner.java
+++ b/src/Compiler/TokenScanner.java
@@ -40,6 +40,7 @@ public class TokenScanner {
             case '*': createTokenNull(TokenType.STAR); break;
             case '/': createTokenNull(TokenType.SLASH); break;
             case ';': createTokenNull(TokenType.SEMI_COLON); break;
+            case ',': createTokenNull(TokenType.COMMA); break;
 
             //Some tokens are multiple characters long (==, <=) etc
             //so need to check next char as well
@@ -75,6 +76,17 @@ public class TokenScanner {
                     createTokenNull(TokenType.GREATER);
                     break;
                 }
+            case '"':
+                while(lookAhead()!='"' && !checkEOF()){
+                    currentLoc++;
+                }
+                if(checkEOF()){
+                    Language.displayError("Strings must end with \"");
+                    break;
+                }
+                currentLoc++;
+                createToken(TokenType.STRING, sourceCode.substring(tokenStart, currentLoc+1));
+                break;
             default:
 
                 //Check for numer
@@ -180,8 +192,11 @@ public class TokenScanner {
     static {
         keywords = new HashMap<>();
         keywords.put("int",    TokenType.INT);
+        keywords.put("len",    TokenType.LEN);
         keywords.put("real",    TokenType.REAL);
+        keywords.put("character",    TokenType.STRING);
         keywords.put("print",    TokenType.PRINT);
+        keywords.put("endprint",    TokenType.ENDPRINT);
         keywords.put("if",    TokenType.IF);
         keywords.put("then",    TokenType.THEN);
         keywords.put("endif",    TokenType.ENDIF);
diff --git a/src/Compiler/TokenType.java b/src/Compiler/TokenType.java
index f56bf38..f597d2f 100644
--- a/src/Compiler/TokenType.java
+++ b/src/Compiler/TokenType.java
@@ -4,15 +4,15 @@ package Compiler;
 public enum TokenType {
     EQUALS, LEFT_PAREN, RIGHT_PAREN,
     PLUS, MINUS, SLASH, STAR, SEMI_COLON,
-    COLON,
+    COLON,COMMA,
 
     EQUALITY, GREATER, LESS,
     GREATER_EQUAL, LESS_EQUAL,
     DEFINE,
 
-    NUMBER,IDENTIFIER,
+    NUMBER,IDENTIFIER,STRING,
 
-    INT,REAL,PRINT,IF,THEN,ENDIF,ELSE,
+    INT,REAL,PRINT,ENDPRINT,IF,THEN,ENDIF,ELSE,LEN,
 
     EOF
 }
diff --git a/src/Compiler/Translator.java b/src/Compiler/Translator.java
index 716bafd..d343b80 100644
--- a/src/Compiler/Translator.java
+++ b/src/Compiler/Translator.java
@@ -15,6 +15,7 @@ public class Translator{
 
     public List<String> compileToC(List<Statement> statements){
         CCode.add("#include <stdio.h>");
+        CCode.add("#include <string.h>");
         CCode.add("int main(){");
         try{
             for (Statement statement: statements){
@@ -37,9 +38,12 @@ public class Translator{
             case "exprStmt":
                 evalExpressionStatement((ExpressionStatement)statement);
                 break;
-            case "vardec":
+            case "varDec":
                 evalVariableDeclaration((VariableDeclaration)statement);
                 break;
+            case "stringDec":
+                evalStringDeclaration((StringDeclaration)statement);
+                break;
             case "print":
                 evalPrintStatement((PrintStatement)statement);
                 break;
@@ -52,31 +56,49 @@ public class Translator{
         evaluateExpression(stmt.expr);
     }
 
+    private void evalStringDeclaration(StringDeclaration stringdec){
+        environment.defineVariable(stringdec.name.text, "string");
+        int size = (int)((Expression.Literal)stringdec.length).value.value;
+        size++;
+        CCode.add("char "+stringdec.name.text+"["+size+"];");
+    }
+
     private void evalVariableDeclaration(VariableDeclaration vardec){
         environment.defineVariable(vardec.name.text, vardec.type);
         if(vardec.type.equals("int")){
             CCode.add("int "+vardec.name.text+";");
         } else if(vardec.type.equals("real")){
             CCode.add("float "+vardec.name.text+";");
-        }
+        } 
     }
 
     private void evalPrintStatement(PrintStatement print){
-        if(print.expr instanceof Expression.Literal){
-            if (((Expression.Literal)print.expr).type.equals("int")){
-                CCode.add("printf(\"%d\","+evaluateExpression(print.expr)+");");
-            } else if (((Expression.Literal)print.expr).type.equals("double")){
-                CCode.add("printf(\"%f\","+evaluateExpression(print.expr)+");");
+        String types="";
+        String values="";
+        boolean first=true;
+        for(Expression expr:print.exprList){
+            if(!first){
+                values+=",";
+            }else{
+                first=false;
+            }
+            String exprType="";
+            if(expr instanceof Expression.Literal){
+                exprType=((Expression.Literal)expr).type;
+            }
+            else if (expr instanceof Expression.Variable){
+                exprType=(String)environment.getVariable((((Expression.Variable)expr).name).text);
             }
-        } else if (print.expr instanceof Expression.Variable){
-            Object varValue = environment.getVariable((((Expression.Variable)print.expr).name).text);
-            if (varValue.equals("int")){
-                CCode.add("printf(\"%d\","+evaluateExpression(print.expr)+");");
-            } else if (varValue.equals("real")){
-                CCode.add("printf(\"%f\","+evaluateExpression(print.expr)+");");
-            } 
+            if (exprType.equals("int")){
+                types+="%d";
+            } else if (exprType.equals("double")){
+                types+="%f";
+            } else if (exprType.equals("string")){
+                types+="%s";
+            }
+            values+=evaluateExpression(expr);
         }
-        
+        CCode.add("printf(\""+types+"\","+values+");");  
     }
 
     private void evalIfStatement(IfStatement ifstatement){
@@ -145,7 +167,14 @@ public class Translator{
     }
 
     private void evaluateAssignmentExpression(AssignmentExpression expr){
-        CCode.add(expr.name.text+"="+evaluateExpression(expr.value)+";");
+        if(expr.value instanceof Expression.Literal){
+            if(((Expression.Literal)expr.value).type.equals("string")){
+                CCode.add("strcpy("+expr.name.text+","+evaluateExpression(expr.value)+");");
+            }
+        }
+        else{
+            CCode.add(expr.name.text+"="+evaluateExpression(expr.value)+";");
+        }
     }
 
     private String evaluateVariableExpression(Variable expr){
-- 
cgit v1.2.3