summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAidenRushbrooke <72034940+AidenRushbrooke@users.noreply.github.com>2021-12-04 05:24:43 +0000
committerAidenRushbrooke <72034940+AidenRushbrooke@users.noreply.github.com>2021-12-04 05:24:43 +0000
commit43909b350b9084ed33f121a15c5770224cbdc79f (patch)
treeaff7f471784fbb1d1a3597acfeb43624d4ed94ad
parentcc1f6e712520793d5a8c638a6e995c018917eadb (diff)
downloadesotericFORTRAN-43909b350b9084ed33f121a15c5770224cbdc79f.tar.gz
esotericFORTRAN-43909b350b9084ed33f121a15c5770224cbdc79f.zip
Added basic function support
-rw-r--r--src/.vscode/launch.json2
-rw-r--r--src/Compiler/Expression.java16
-rw-r--r--src/Compiler/Parser.java140
-rw-r--r--src/Compiler/Statement.java67
-rw-r--r--src/Compiler/Token.java1
-rw-r--r--src/Compiler/TokenScanner.java3
-rw-r--r--src/Compiler/TokenType.java3
-rw-r--r--src/Compiler/Translator.java78
-rw-r--r--src/Compiler/Utils.java3
-rw-r--r--src/examples/example.ft15
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