2021-03-30 16:02:13 +00:00
|
|
|
module compiler.parsing.core;
|
2021-03-03 09:08:34 +00:00
|
|
|
|
|
|
|
import gogga;
|
|
|
|
import std.conv : to;
|
2021-03-03 12:04:26 +00:00
|
|
|
import std.string : isNumeric, cmp;
|
2021-03-30 15:35:16 +00:00
|
|
|
import compiler.symbols.check;
|
|
|
|
import compiler.symbols.data;
|
2021-03-03 11:30:56 +00:00
|
|
|
import compiler.lexer : Token;
|
2021-03-03 13:32:28 +00:00
|
|
|
import core.stdc.stdlib;
|
2021-03-17 08:05:56 +00:00
|
|
|
import misc.exceptions : TError;
|
2021-06-08 08:45:32 +00:00
|
|
|
import compiler.parsing.exceptions;
|
2021-03-17 08:05:56 +00:00
|
|
|
|
|
|
|
// public final class ParserError : TError
|
|
|
|
// {
|
|
|
|
|
|
|
|
// }
|
2021-03-03 09:08:34 +00:00
|
|
|
|
2021-03-21 11:01:09 +00:00
|
|
|
|
|
|
|
|
2021-03-21 12:23:18 +00:00
|
|
|
bool isUnitTest;
|
|
|
|
|
2021-03-03 09:08:34 +00:00
|
|
|
public final class Parser
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Tokens management
|
|
|
|
*/
|
2021-03-03 11:30:56 +00:00
|
|
|
private Token[] tokens;
|
|
|
|
private Token currentToken;
|
2021-03-03 09:08:34 +00:00
|
|
|
private ulong tokenPtr;
|
|
|
|
|
2021-03-05 10:35:58 +00:00
|
|
|
/**
|
|
|
|
* Crashes the program if the given token is not a symbol
|
|
|
|
* the same as the givne expected one
|
|
|
|
*/
|
2021-06-08 08:45:32 +00:00
|
|
|
public void expect(SymbolType symbol, Token token)
|
2021-03-03 09:08:34 +00:00
|
|
|
{
|
|
|
|
/* TODO: Do checking here to see if token is a type of given symbol */
|
2021-03-03 11:30:56 +00:00
|
|
|
SymbolType actualType = getSymbolType(token);
|
2021-03-03 12:04:26 +00:00
|
|
|
bool isFine = actualType == symbol;
|
2021-03-03 09:08:34 +00:00
|
|
|
|
|
|
|
/* TODO: Crash program if not */
|
2021-03-03 12:42:57 +00:00
|
|
|
if (!isFine)
|
2021-03-03 09:08:34 +00:00
|
|
|
{
|
2022-12-13 12:17:40 +00:00
|
|
|
throw new SyntaxError(this, symbol, token);
|
2021-06-08 08:45:32 +00:00
|
|
|
// expect("Expected symbol of type " ~ to!(string)(symbol) ~ " but got " ~ to!(
|
|
|
|
// string)(actualType) ~ " with " ~ token.toString());
|
2021-03-03 09:08:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-05 10:35:58 +00:00
|
|
|
/**
|
|
|
|
* Crashes the parser with the given message
|
|
|
|
*/
|
2021-03-05 10:01:57 +00:00
|
|
|
public static void expect(string message)
|
|
|
|
{
|
2021-03-17 08:33:22 +00:00
|
|
|
//throw new TError(message);
|
|
|
|
gprintln(message, DebugType.ERROR);
|
2021-03-21 12:23:18 +00:00
|
|
|
|
|
|
|
if(isUnitTest)
|
|
|
|
{
|
2021-06-08 08:30:15 +00:00
|
|
|
throw new TError(message);
|
2021-03-21 12:23:18 +00:00
|
|
|
assert(false);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-06-08 08:30:15 +00:00
|
|
|
throw new TError(message);
|
|
|
|
//exit(0); /* TODO: Exit code */ /* TODO: Version that returns or asserts for unit tests */
|
2021-03-21 12:23:18 +00:00
|
|
|
}
|
2021-03-05 10:01:57 +00:00
|
|
|
}
|
|
|
|
|
2021-03-05 10:35:58 +00:00
|
|
|
/**
|
|
|
|
* Costructs a new parser with the given set of Tokens
|
|
|
|
*/
|
2021-03-03 11:30:56 +00:00
|
|
|
this(Token[] tokens)
|
2021-03-03 09:08:34 +00:00
|
|
|
{
|
|
|
|
this.tokens = tokens;
|
|
|
|
currentToken = tokens[0];
|
|
|
|
}
|
|
|
|
|
2021-03-03 11:30:56 +00:00
|
|
|
/**
|
|
|
|
* Moves the token pointer to the next token
|
|
|
|
*
|
|
|
|
* Returns true if successful, false otherwise
|
|
|
|
* (if we have exhausted the tokens source)
|
|
|
|
*/
|
2021-03-03 14:30:56 +00:00
|
|
|
private void nextToken()
|
2021-03-03 11:30:56 +00:00
|
|
|
{
|
2021-03-03 14:30:56 +00:00
|
|
|
tokenPtr++;
|
2021-03-03 11:30:56 +00:00
|
|
|
}
|
|
|
|
|
2021-03-03 12:04:26 +00:00
|
|
|
private bool hasTokens()
|
|
|
|
{
|
|
|
|
return tokenPtr < tokens.length;
|
|
|
|
}
|
|
|
|
|
2021-03-03 11:30:56 +00:00
|
|
|
private Token getCurrentToken()
|
|
|
|
{
|
2021-03-03 18:04:54 +00:00
|
|
|
/* TODO: Throw an exception here when we try get more than we can */
|
2021-03-03 12:04:26 +00:00
|
|
|
return tokens[tokenPtr];
|
2021-03-03 11:30:56 +00:00
|
|
|
}
|
|
|
|
|
2021-11-01 16:25:43 +00:00
|
|
|
private void previousToken()
|
|
|
|
{
|
|
|
|
tokenPtr--;
|
|
|
|
}
|
|
|
|
|
2021-03-05 10:35:58 +00:00
|
|
|
/**
|
|
|
|
* Parses if statements
|
|
|
|
*
|
|
|
|
* TODO: Check kanban
|
2022-12-19 13:37:55 +00:00
|
|
|
* TOOD: THis should return something
|
2021-03-05 10:35:58 +00:00
|
|
|
*/
|
2022-12-19 13:37:55 +00:00
|
|
|
private IfStatement parseIf()
|
2021-03-03 15:09:08 +00:00
|
|
|
{
|
2021-03-05 09:58:51 +00:00
|
|
|
gprintln("parseIf(): Enter", DebugType.WARNING);
|
|
|
|
|
2022-12-19 13:37:55 +00:00
|
|
|
IfStatement ifStmt;
|
|
|
|
Branch[] branches;
|
|
|
|
|
2021-03-18 17:20:17 +00:00
|
|
|
while (hasTokens())
|
2022-12-19 13:37:55 +00:00
|
|
|
{
|
|
|
|
Expression currentBranchCondition;
|
|
|
|
Statement[] currentBranchBody;
|
|
|
|
|
2021-03-20 16:36:25 +00:00
|
|
|
/* This will only be called once (it is what caused a call to parseIf()) */
|
|
|
|
if (getSymbolType(getCurrentToken()) == SymbolType.IF)
|
2021-03-18 17:20:17 +00:00
|
|
|
{
|
|
|
|
/* Pop off the `if` */
|
|
|
|
nextToken();
|
2021-03-03 15:09:08 +00:00
|
|
|
|
2021-03-18 17:20:17 +00:00
|
|
|
/* Expect an opening brace `(` */
|
|
|
|
expect(SymbolType.LBRACE, getCurrentToken());
|
|
|
|
nextToken();
|
2021-03-03 15:35:48 +00:00
|
|
|
|
2021-03-18 17:20:17 +00:00
|
|
|
/* Parse an expression (for the condition) */
|
2022-12-19 13:37:55 +00:00
|
|
|
currentBranchCondition = parseExpression();
|
2021-03-18 17:20:17 +00:00
|
|
|
expect(SymbolType.RBRACE, getCurrentToken());
|
2021-03-03 15:35:48 +00:00
|
|
|
|
2021-03-18 17:20:17 +00:00
|
|
|
/* Opening { */
|
|
|
|
nextToken();
|
|
|
|
expect(SymbolType.OCURLY, getCurrentToken());
|
|
|
|
|
|
|
|
/* Parse the if' statement's body AND expect a closing curly */
|
2022-12-19 13:37:55 +00:00
|
|
|
currentBranchBody = parseBody();
|
2021-03-18 17:20:17 +00:00
|
|
|
expect(SymbolType.CCURLY, getCurrentToken());
|
|
|
|
nextToken();
|
2022-12-19 13:37:55 +00:00
|
|
|
|
|
|
|
/* Create a branch node */
|
|
|
|
Branch branch = new Branch(currentBranchCondition, currentBranchBody);
|
|
|
|
parentToContainer(branch, currentBranchBody);
|
|
|
|
branches ~= branch;
|
2021-03-18 17:20:17 +00:00
|
|
|
}
|
2021-03-20 16:36:25 +00:00
|
|
|
/* If we get an else as the next symbol */
|
2021-03-20 16:31:20 +00:00
|
|
|
else if (getSymbolType(getCurrentToken()) == SymbolType.ELSE)
|
2021-03-18 17:20:17 +00:00
|
|
|
{
|
|
|
|
/* Pop off the `else` */
|
|
|
|
nextToken();
|
|
|
|
|
2021-03-18 19:06:10 +00:00
|
|
|
/* Check if we have an `if` after the `{` (so an "else if" statement) */
|
2021-03-20 16:38:16 +00:00
|
|
|
if (getSymbolType(getCurrentToken()) == SymbolType.IF)
|
2021-03-18 17:20:17 +00:00
|
|
|
{
|
2021-03-18 19:06:10 +00:00
|
|
|
/* Pop off the `if` */
|
|
|
|
nextToken();
|
|
|
|
|
2021-03-18 17:20:17 +00:00
|
|
|
/* Expect an opening brace `(` */
|
|
|
|
expect(SymbolType.LBRACE, getCurrentToken());
|
|
|
|
nextToken();
|
|
|
|
|
|
|
|
/* Parse an expression (for the condition) */
|
2022-12-19 13:37:55 +00:00
|
|
|
currentBranchCondition = parseExpression();
|
2021-03-18 17:20:17 +00:00
|
|
|
expect(SymbolType.RBRACE, getCurrentToken());
|
|
|
|
|
|
|
|
/* Opening { */
|
|
|
|
nextToken();
|
|
|
|
expect(SymbolType.OCURLY, getCurrentToken());
|
|
|
|
|
|
|
|
/* Parse the if' statement's body AND expect a closing curly */
|
2022-12-19 13:37:55 +00:00
|
|
|
currentBranchBody = parseBody();
|
2021-03-18 17:20:17 +00:00
|
|
|
expect(SymbolType.CCURLY, getCurrentToken());
|
|
|
|
nextToken();
|
2022-12-19 13:37:55 +00:00
|
|
|
|
|
|
|
/* Create a branch node */
|
|
|
|
Branch branch = new Branch(currentBranchCondition, currentBranchBody);
|
|
|
|
parentToContainer(branch, currentBranchBody);
|
|
|
|
branches ~= branch;
|
2021-03-18 17:20:17 +00:00
|
|
|
}
|
2021-03-18 19:06:10 +00:00
|
|
|
/* Check for opening curly (just an "else" statement) */
|
|
|
|
else if (getSymbolType(getCurrentToken()) == SymbolType.OCURLY)
|
2021-03-18 17:20:17 +00:00
|
|
|
{
|
2021-03-20 16:31:03 +00:00
|
|
|
/* Parse the if' statement's body (starting with `{` AND expect a closing curly */
|
2022-12-19 13:37:55 +00:00
|
|
|
currentBranchBody = parseBody();
|
2021-03-18 17:20:17 +00:00
|
|
|
expect(SymbolType.CCURLY, getCurrentToken());
|
|
|
|
nextToken();
|
|
|
|
|
2022-12-19 13:37:55 +00:00
|
|
|
/* Create a branch node */
|
|
|
|
Branch branch = new Branch(null, currentBranchBody);
|
|
|
|
parentToContainer(branch, currentBranchBody);
|
|
|
|
branches ~= branch;
|
|
|
|
|
2021-03-20 16:36:25 +00:00
|
|
|
/* Exit, this is the end of the if statement as an else is reached */
|
|
|
|
break;
|
2021-03-18 17:20:17 +00:00
|
|
|
}
|
2021-03-20 16:50:48 +00:00
|
|
|
/* Error out if no `{` or `if` */
|
2021-03-18 17:20:17 +00:00
|
|
|
else
|
|
|
|
{
|
2021-03-20 16:36:25 +00:00
|
|
|
expect("Expected either if (for else if) or { for (else)");
|
2021-03-18 17:20:17 +00:00
|
|
|
}
|
|
|
|
}
|
2021-03-20 16:41:26 +00:00
|
|
|
/* If we get anything else, then we are done with if statement */
|
2021-03-18 17:20:17 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2021-03-03 15:35:48 +00:00
|
|
|
|
2021-03-05 09:58:51 +00:00
|
|
|
gprintln("parseIf(): Leave", DebugType.WARNING);
|
2022-12-19 13:37:55 +00:00
|
|
|
|
|
|
|
/* Create the if statement with the branches */
|
|
|
|
ifStmt = new IfStatement(branches);
|
|
|
|
|
2023-01-04 10:03:50 +00:00
|
|
|
/* Parent the branches to the IfStatement */
|
2022-12-19 13:37:55 +00:00
|
|
|
parentToContainer(ifStmt, cast(Statement[])branches);
|
|
|
|
|
|
|
|
return ifStmt;
|
2021-03-03 15:09:08 +00:00
|
|
|
}
|
|
|
|
|
2023-01-04 10:03:50 +00:00
|
|
|
private WhileLoop parseWhile()
|
2021-03-03 18:50:06 +00:00
|
|
|
{
|
2021-03-05 09:58:51 +00:00
|
|
|
gprintln("parseWhile(): Enter", DebugType.WARNING);
|
|
|
|
|
2023-01-04 10:03:50 +00:00
|
|
|
Expression branchCondition;
|
|
|
|
Statement[] branchBody;
|
|
|
|
|
2021-03-05 09:40:06 +00:00
|
|
|
/* Pop off the `while` */
|
|
|
|
nextToken();
|
|
|
|
|
2021-03-03 18:50:06 +00:00
|
|
|
/* Expect an opening brace `(` */
|
|
|
|
expect(SymbolType.LBRACE, getCurrentToken());
|
|
|
|
nextToken();
|
|
|
|
|
|
|
|
/* Parse an expression (for the condition) */
|
2023-01-04 10:03:50 +00:00
|
|
|
branchCondition = parseExpression();
|
2021-03-03 18:50:06 +00:00
|
|
|
expect(SymbolType.RBRACE, getCurrentToken());
|
|
|
|
|
2023-01-11 08:43:29 +00:00
|
|
|
/* Opening { */
|
2021-03-03 18:50:06 +00:00
|
|
|
nextToken();
|
|
|
|
expect(SymbolType.OCURLY, getCurrentToken());
|
|
|
|
|
|
|
|
/* Parse the while' statement's body AND expect a closing curly */
|
2023-01-04 10:03:50 +00:00
|
|
|
branchBody = parseBody();
|
2021-03-03 18:50:06 +00:00
|
|
|
expect(SymbolType.CCURLY, getCurrentToken());
|
|
|
|
nextToken();
|
|
|
|
|
2023-01-04 10:03:50 +00:00
|
|
|
|
|
|
|
/* Create a Branch node coupling the condition and body statements */
|
|
|
|
Branch branch = new Branch(branchCondition, branchBody);
|
|
|
|
|
|
|
|
/* Parent the branchBody to the branch */
|
|
|
|
parentToContainer(branch, branchBody);
|
|
|
|
|
|
|
|
/* Create the while loop with the single branch */
|
|
|
|
WhileLoop whileLoop = new WhileLoop(branch);
|
|
|
|
|
|
|
|
/* Parent the branch to the WhileLoop */
|
|
|
|
parentToContainer(whileLoop, [branch]);
|
|
|
|
|
2021-03-05 09:58:51 +00:00
|
|
|
gprintln("parseWhile(): Leave", DebugType.WARNING);
|
2023-01-04 10:03:50 +00:00
|
|
|
|
|
|
|
return whileLoop;
|
2021-03-03 18:50:06 +00:00
|
|
|
}
|
|
|
|
|
2023-01-11 08:43:29 +00:00
|
|
|
private WhileLoop parseDoWhile()
|
|
|
|
{
|
|
|
|
gprintln("parseDoWhile(): Enter", DebugType.WARNING);
|
|
|
|
|
|
|
|
Expression branchCondition;
|
|
|
|
Statement[] branchBody;
|
|
|
|
|
|
|
|
/* Pop off the `do` */
|
|
|
|
nextToken();
|
|
|
|
|
|
|
|
/* Expect an opening curly `{` */
|
|
|
|
expect(SymbolType.OCURLY, getCurrentToken());
|
|
|
|
|
|
|
|
/* Parse the do-while statement's body AND expect a closing curly */
|
|
|
|
branchBody = parseBody();
|
|
|
|
expect(SymbolType.CCURLY, getCurrentToken());
|
|
|
|
nextToken();
|
|
|
|
|
|
|
|
/* Expect a `while` */
|
|
|
|
expect(SymbolType.WHILE, getCurrentToken());
|
|
|
|
nextToken();
|
|
|
|
|
|
|
|
/* Expect an opening brace `(` */
|
|
|
|
expect(SymbolType.LBRACE, getCurrentToken());
|
|
|
|
nextToken();
|
|
|
|
|
|
|
|
/* Parse the condition */
|
|
|
|
branchCondition = parseExpression();
|
|
|
|
expect(SymbolType.RBRACE, getCurrentToken());
|
|
|
|
nextToken();
|
|
|
|
|
|
|
|
/* Expect a semicolon */
|
|
|
|
expect(SymbolType.SEMICOLON, getCurrentToken());
|
|
|
|
nextToken();
|
|
|
|
|
|
|
|
/* Create a Branch node coupling the condition and body statements */
|
|
|
|
Branch branch = new Branch(branchCondition, branchBody);
|
|
|
|
|
|
|
|
/* Parent the branchBody to the branch */
|
|
|
|
parentToContainer(branch, branchBody);
|
|
|
|
|
|
|
|
/* Create the while loop with the single branch and marked as a do-while loop */
|
|
|
|
WhileLoop whileLoop = new WhileLoop(branch, true);
|
|
|
|
|
|
|
|
/* Parent the branch to the WhileLoop */
|
|
|
|
parentToContainer(whileLoop, [branch]);
|
|
|
|
|
|
|
|
gprintln("parseDoWhile(): Leave", DebugType.WARNING);
|
|
|
|
|
|
|
|
return whileLoop;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Finish implementing this
|
|
|
|
// TODO: We need to properly parent and build stuff
|
|
|
|
// TODO: We ASSUME there is always pre-run, condition and post-iteration
|
|
|
|
public ForLoop parseFor()
|
|
|
|
{
|
|
|
|
gprintln("parseFor(): Enter", DebugType.WARNING);
|
|
|
|
|
|
|
|
Expression branchCondition;
|
|
|
|
Statement[] branchBody;
|
|
|
|
|
|
|
|
/* Pop of the token `for` */
|
|
|
|
nextToken();
|
|
|
|
|
|
|
|
/* Expect an opening smooth brace `(` */
|
|
|
|
expect(SymbolType.LBRACE, getCurrentToken());
|
|
|
|
nextToken();
|
|
|
|
|
|
|
|
/* Expect a single Statement */
|
|
|
|
// TODO: Make optional, add parser lookahead check
|
|
|
|
Statement preRunStatement = parseStatement();
|
|
|
|
|
|
|
|
/* Expect an expression */
|
|
|
|
// TODO: Make optional, add parser lookahead check
|
|
|
|
branchCondition = parseExpression();
|
|
|
|
|
|
|
|
/* Expect a semi-colon, then move on */
|
|
|
|
expect(SymbolType.SEMICOLON, getCurrentToken());
|
|
|
|
nextToken();
|
|
|
|
|
|
|
|
/* Expect a post-iteration statement with `)` as terminator */
|
|
|
|
// TODO: Make optional, add parser lookahead check
|
|
|
|
Statement postIterationStatement = parseStatement(SymbolType.RBRACE);
|
|
|
|
|
|
|
|
/* Expect an opening curly `{` and parse the body */
|
|
|
|
expect(SymbolType.OCURLY, getCurrentToken());
|
|
|
|
branchBody = parseBody();
|
|
|
|
|
|
|
|
/* Expect a closing curly and move on */
|
|
|
|
expect(SymbolType.CCURLY, getCurrentToken());
|
|
|
|
nextToken();
|
|
|
|
|
|
|
|
gprintln("Yo: "~getCurrentToken().toString());
|
|
|
|
|
|
|
|
/* Create the Branch coupling the body statements (+post iteration statement) and condition */
|
|
|
|
Branch forBranch = new Branch(branchCondition, branchBody~postIterationStatement);
|
|
|
|
|
|
|
|
/* Create the for loop */
|
|
|
|
ForLoop forLoop = new ForLoop(forBranch, preRunStatement);
|
|
|
|
|
|
|
|
// TODO: Set `forLoop.hasPostIterate`
|
|
|
|
|
|
|
|
/* Parent the pre-run statement to its for loop */
|
|
|
|
parentToContainer(forLoop, [preRunStatement]);
|
|
|
|
|
|
|
|
/* Parent the body of the branch (body statements + post iteration statement) */
|
|
|
|
parentToContainer(forBranch, branchBody~postIterationStatement);
|
|
|
|
|
|
|
|
/* Parent the Branch to its for loop */
|
|
|
|
parentToContainer(forLoop, [forBranch]);
|
|
|
|
|
|
|
|
gprintln("parseFor(): Leave", DebugType.WARNING);
|
|
|
|
|
|
|
|
return forLoop;
|
|
|
|
}
|
|
|
|
|
|
|
|
public VariableAssignmentStdAlone parseAssignment(SymbolType terminatingSymbol = SymbolType.SEMICOLON)
|
2021-04-01 13:49:02 +00:00
|
|
|
{
|
|
|
|
/* Generated Assignment statement */
|
2021-10-27 13:52:43 +00:00
|
|
|
VariableAssignmentStdAlone assignment;
|
2021-04-01 13:49:02 +00:00
|
|
|
|
|
|
|
/* The identifier being assigned to */
|
|
|
|
string identifier = getCurrentToken().getToken();
|
|
|
|
nextToken();
|
|
|
|
nextToken();
|
2021-04-01 13:59:33 +00:00
|
|
|
gprintln(getCurrentToken());
|
2021-04-01 13:49:02 +00:00
|
|
|
|
|
|
|
/* Expression */
|
|
|
|
Expression assignmentExpression = parseExpression();
|
|
|
|
|
2021-04-01 13:59:33 +00:00
|
|
|
|
2021-10-27 13:52:43 +00:00
|
|
|
assignment = new VariableAssignmentStdAlone(identifier, assignmentExpression);
|
2021-04-01 13:49:02 +00:00
|
|
|
|
|
|
|
/* TODO: Support for (a=1)? */
|
2023-01-11 08:43:29 +00:00
|
|
|
/* Expect a the terminating symbol */
|
|
|
|
// expect(SymbolType.SEMICOLON, getCurrentToken());
|
|
|
|
expect(terminatingSymbol, getCurrentToken());
|
|
|
|
|
|
|
|
/* Move off terminating symbol */
|
2021-04-01 13:49:02 +00:00
|
|
|
nextToken();
|
|
|
|
|
|
|
|
|
|
|
|
return assignment;
|
|
|
|
}
|
|
|
|
|
2023-01-11 08:43:29 +00:00
|
|
|
public Statement parseName(SymbolType terminatingSymbol = SymbolType.SEMICOLON)
|
2021-03-28 19:54:38 +00:00
|
|
|
{
|
|
|
|
Statement ret;
|
|
|
|
|
|
|
|
/* Save the name or type */
|
|
|
|
string nameTYpe = getCurrentToken().getToken();
|
2023-01-12 08:53:48 +00:00
|
|
|
gprintln("parseName(): Current token: "~getCurrentToken().toString());
|
2021-03-28 19:54:38 +00:00
|
|
|
|
|
|
|
/* TODO: The problem here is I don't want to progress the token */
|
|
|
|
|
|
|
|
/* Get next token */
|
|
|
|
nextToken();
|
|
|
|
SymbolType type = getSymbolType(getCurrentToken());
|
|
|
|
|
|
|
|
/* If we have `(` then function call */
|
|
|
|
if(type == SymbolType.LBRACE)
|
|
|
|
{
|
|
|
|
/* TODO: Collect and return */
|
|
|
|
previousToken();
|
|
|
|
parseFuncCall();
|
2021-03-28 20:05:58 +00:00
|
|
|
|
|
|
|
/* Expect a semi-colon */
|
|
|
|
expect(SymbolType.SEMICOLON, getCurrentToken());
|
|
|
|
nextToken();
|
2021-03-28 19:54:38 +00:00
|
|
|
}
|
2023-01-12 08:53:48 +00:00
|
|
|
/**
|
|
|
|
* Either we have:
|
|
|
|
*
|
|
|
|
* 1. `int ptr` (and we looked ahead to `ptr`)
|
|
|
|
* 2. `int* ptr` (and we looked ahead to `*`)
|
|
|
|
*/
|
2021-03-28 19:54:38 +00:00
|
|
|
/* If we have an identifier/type then declaration */
|
2023-01-12 08:53:48 +00:00
|
|
|
else if(type == SymbolType.IDENT_TYPE || type == SymbolType.STAR)
|
2021-03-28 19:54:38 +00:00
|
|
|
{
|
|
|
|
previousToken();
|
|
|
|
ret = parseTypedDeclaration();
|
|
|
|
}
|
2021-04-01 13:49:02 +00:00
|
|
|
/* Assignment */
|
|
|
|
else if(type == SymbolType.ASSIGN)
|
|
|
|
{
|
|
|
|
previousToken();
|
2023-01-11 08:43:29 +00:00
|
|
|
ret = parseAssignment(terminatingSymbol);
|
2021-04-01 13:49:02 +00:00
|
|
|
}
|
2021-03-28 19:54:38 +00:00
|
|
|
/* Any other case */
|
|
|
|
else
|
|
|
|
{
|
2021-04-01 13:59:33 +00:00
|
|
|
gprintln(getCurrentToken);
|
2021-03-28 19:54:38 +00:00
|
|
|
expect("Error expected ( for var/func def");
|
2021-04-01 13:59:33 +00:00
|
|
|
|
2021-03-28 19:54:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2021-05-04 16:58:07 +00:00
|
|
|
/* TODO: Implement me, and call me */
|
|
|
|
private Struct parseStruct()
|
|
|
|
{
|
|
|
|
gprintln("parseStruct(): Enter", DebugType.WARNING);
|
|
|
|
|
|
|
|
Struct generatedStruct;
|
2021-05-31 16:14:15 +00:00
|
|
|
Statement[] statements;
|
2021-05-04 16:58:07 +00:00
|
|
|
|
|
|
|
/* Consume the `struct` that caused `parseStruct` to be called */
|
|
|
|
nextToken();
|
|
|
|
|
|
|
|
/* Expect an identifier here (no dot) */
|
|
|
|
string structName = getCurrentToken().getToken();
|
|
|
|
expect(SymbolType.IDENT_TYPE, getCurrentToken());
|
|
|
|
if(!isIdentifier_NoDot(getCurrentToken()))
|
|
|
|
{
|
|
|
|
expect("Identifier (for struct declaration) cannot be dotted");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Consume the name */
|
|
|
|
nextToken();
|
|
|
|
|
|
|
|
/* TODO: Here we will do a while loop */
|
2021-05-04 17:07:04 +00:00
|
|
|
expect(SymbolType.OCURLY, getCurrentToken());
|
|
|
|
nextToken();
|
|
|
|
|
|
|
|
while(true)
|
|
|
|
{
|
|
|
|
/* Get current token */
|
2021-05-31 16:01:06 +00:00
|
|
|
SymbolType symbolType = getSymbolType(getCurrentToken());
|
|
|
|
|
|
|
|
/* The possibly valid returned struct member (Entity) */
|
|
|
|
Statement structMember;
|
|
|
|
|
2021-05-31 15:51:16 +00:00
|
|
|
/** TODO:
|
|
|
|
* We only want to allow function definitions and variable
|
|
|
|
* declarations here (WIP: for now without assignments)
|
|
|
|
*
|
|
|
|
* parseAccessor() supports those BUT it will also allow classes
|
|
|
|
* and further structs - this we do not want and hence we should
|
|
|
|
* filter out those (raise an error) on checking the type of
|
|
|
|
* Entity returned by `parseAccessor()`
|
|
|
|
*/
|
2021-05-31 16:01:06 +00:00
|
|
|
|
|
|
|
|
|
|
|
/* If it is a type */
|
|
|
|
if (symbolType == SymbolType.IDENT_TYPE)
|
|
|
|
{
|
|
|
|
/* Might be a function, might be a variable, or assignment */
|
|
|
|
structMember = parseName();
|
|
|
|
}
|
|
|
|
/* If it is an accessor */
|
|
|
|
else if (isAccessor(getCurrentToken()))
|
|
|
|
{
|
|
|
|
structMember = parseAccessor();
|
|
|
|
}
|
2021-06-06 13:51:06 +00:00
|
|
|
/* If is is a modifier */
|
|
|
|
else if(isModifier(getCurrentToken()))
|
|
|
|
{
|
|
|
|
structMember = parseInitScope();
|
|
|
|
}
|
2021-05-31 16:06:09 +00:00
|
|
|
/* If closing brace then exit */
|
|
|
|
else if(symbolType == SymbolType.CCURLY)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
2021-05-31 16:01:06 +00:00
|
|
|
|
|
|
|
/* Ensure only function declaration or variable declaration */
|
|
|
|
if(cast(Function)structMember)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
else if(cast(Variable)structMember)
|
|
|
|
{
|
|
|
|
/* Ensure that there is (WIP: for now) no assignment in the variable declaration */
|
|
|
|
Variable variableDeclaration = cast(Variable)structMember;
|
|
|
|
|
|
|
|
/* Raise error if an assignment is present */
|
|
|
|
if(variableDeclaration.getAssignment())
|
|
|
|
{
|
|
|
|
expect("Assignments not allowed in struct body");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Anything else that isn't a assignment-less variable declaration
|
|
|
|
* or a function definition is an error
|
|
|
|
*/
|
|
|
|
else
|
|
|
|
{
|
|
|
|
expect("Only function definitions and variable declarations allowed in struct body");
|
|
|
|
}
|
|
|
|
|
2021-05-31 16:14:15 +00:00
|
|
|
/* Append to struct's body */
|
|
|
|
statements ~= structMember;
|
2021-05-31 16:01:06 +00:00
|
|
|
|
|
|
|
|
2021-05-31 15:51:16 +00:00
|
|
|
|
2021-05-31 16:06:09 +00:00
|
|
|
|
2021-05-04 17:07:04 +00:00
|
|
|
|
|
|
|
/* TODO: Only allow variables here */
|
|
|
|
/* TODO: Only allowe VariableDeclarations (maybe assignments idk) */
|
|
|
|
/* TODO: Might, do what d does and allow function */
|
|
|
|
/* TODO: Which is just a codegen trick and implicit thing really */
|
|
|
|
/* TODO: I mean isn't OOP too lmao */
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-05-04 17:18:30 +00:00
|
|
|
|
2021-06-01 08:00:21 +00:00
|
|
|
/* Generate a new Struct with the given body Statement(s) */
|
2021-05-31 20:36:11 +00:00
|
|
|
generatedStruct = new Struct(structName);
|
2021-06-01 08:00:21 +00:00
|
|
|
generatedStruct.addStatements(statements);
|
2021-05-31 20:36:11 +00:00
|
|
|
|
2021-05-04 17:07:04 +00:00
|
|
|
/* Expect closing brace (sanity) */
|
|
|
|
expect(SymbolType.CCURLY, getCurrentToken());
|
2021-05-04 16:58:07 +00:00
|
|
|
|
2021-05-04 17:11:25 +00:00
|
|
|
/* Consume the closing curly brace */
|
|
|
|
nextToken();
|
|
|
|
|
2021-05-04 16:58:07 +00:00
|
|
|
|
|
|
|
gprintln("parseStruct(): Leave", DebugType.WARNING);
|
|
|
|
|
|
|
|
return generatedStruct;
|
|
|
|
}
|
|
|
|
|
2022-12-13 12:17:40 +00:00
|
|
|
private ReturnStmt parseReturn()
|
|
|
|
{
|
|
|
|
ReturnStmt returnStatement;
|
|
|
|
|
|
|
|
/* Move from `return` onto start of expression */
|
|
|
|
nextToken();
|
|
|
|
|
|
|
|
/* Parse the expression till termination */
|
|
|
|
Expression returnExpression = parseExpression();
|
|
|
|
|
|
|
|
/* Expect a semi-colon as the terminator */
|
|
|
|
gprintln(getCurrentToken());
|
|
|
|
expect(SymbolType.SEMICOLON, getCurrentToken());
|
|
|
|
|
|
|
|
|
|
|
|
/* Move off of the terminator */
|
|
|
|
nextToken();
|
|
|
|
|
|
|
|
/* Create the ReturnStmt */
|
|
|
|
returnStatement = new ReturnStmt(returnExpression);
|
|
|
|
|
|
|
|
return returnStatement;
|
|
|
|
}
|
|
|
|
|
2021-03-21 15:57:56 +00:00
|
|
|
private Statement[] parseBody()
|
2021-03-03 14:30:56 +00:00
|
|
|
{
|
2021-03-05 10:00:13 +00:00
|
|
|
gprintln("parseBody(): Enter", DebugType.WARNING);
|
|
|
|
|
2021-03-03 14:30:56 +00:00
|
|
|
/* TODO: Implement body parsing */
|
2021-03-21 15:57:56 +00:00
|
|
|
Statement[] statements;
|
2021-03-20 19:26:42 +00:00
|
|
|
|
|
|
|
/* Consume the `{` symbol */
|
2021-03-03 14:30:56 +00:00
|
|
|
nextToken();
|
|
|
|
|
2021-03-03 18:16:56 +00:00
|
|
|
/**
|
|
|
|
* If we were able to get a closing token, `}`, then
|
|
|
|
* this will be set to true, else it will be false by
|
|
|
|
* default which implies we ran out of tokens before
|
|
|
|
* we could close te body which is an error we do throw
|
|
|
|
*/
|
2021-03-03 18:04:54 +00:00
|
|
|
bool closedBeforeExit;
|
|
|
|
|
2021-03-05 10:35:58 +00:00
|
|
|
while (hasTokens())
|
2021-03-03 14:30:56 +00:00
|
|
|
{
|
|
|
|
/* Get the token */
|
|
|
|
Token tok = getCurrentToken();
|
|
|
|
SymbolType symbol = getSymbolType(tok);
|
|
|
|
|
2021-03-05 10:35:58 +00:00
|
|
|
gprintln("parseBody(): SymbolType=" ~ to!(string)(symbol));
|
2021-03-03 15:35:48 +00:00
|
|
|
|
2023-01-11 09:23:38 +00:00
|
|
|
|
2021-03-05 09:31:33 +00:00
|
|
|
/* If it is a class definition */
|
2023-01-11 09:23:38 +00:00
|
|
|
if(symbol == SymbolType.CLASS)
|
2021-03-05 09:31:33 +00:00
|
|
|
{
|
2021-03-24 19:50:06 +00:00
|
|
|
/* Parse the class and add its statements */
|
|
|
|
statements ~= parseClass();
|
2021-03-05 09:31:33 +00:00
|
|
|
}
|
2021-05-04 17:10:59 +00:00
|
|
|
/* If it is a struct definition */
|
|
|
|
else if(symbol == SymbolType.STRUCT)
|
|
|
|
{
|
|
|
|
/* Parse the struct and add it to the statements */
|
|
|
|
statements ~= parseStruct();
|
|
|
|
}
|
2023-01-11 09:23:38 +00:00
|
|
|
/* If it is closing the body `}` */
|
|
|
|
else if(symbol == SymbolType.CCURLY)
|
2022-12-13 12:17:40 +00:00
|
|
|
{
|
2023-01-11 09:23:38 +00:00
|
|
|
gprintln("parseBody(): Exiting body by }", DebugType.WARNING);
|
|
|
|
|
|
|
|
closedBeforeExit = true;
|
|
|
|
break;
|
2022-12-13 12:17:40 +00:00
|
|
|
}
|
2021-03-03 17:53:47 +00:00
|
|
|
else
|
|
|
|
{
|
2023-01-11 09:23:38 +00:00
|
|
|
statements ~= parseStatement();
|
2021-03-03 17:53:47 +00:00
|
|
|
}
|
2021-03-03 14:30:56 +00:00
|
|
|
}
|
2021-03-03 17:53:47 +00:00
|
|
|
|
|
|
|
/* TODO: We can sometimes run out of tokens before getting our closing brace, we should fix that here */
|
2021-03-05 10:35:58 +00:00
|
|
|
if (!closedBeforeExit)
|
2021-03-03 18:04:54 +00:00
|
|
|
{
|
2021-03-05 10:03:31 +00:00
|
|
|
expect("Expected closing } but ran out of tokens");
|
2021-03-03 18:04:54 +00:00
|
|
|
}
|
2021-03-05 10:00:13 +00:00
|
|
|
|
|
|
|
gprintln("parseBody(): Leave", DebugType.WARNING);
|
2021-03-21 15:57:56 +00:00
|
|
|
|
|
|
|
return statements;
|
2021-03-03 14:30:56 +00:00
|
|
|
}
|
|
|
|
|
2021-03-21 12:03:13 +00:00
|
|
|
private AccessorType getAccessorType(Token token)
|
|
|
|
{
|
|
|
|
if(getSymbolType(token) == SymbolType.PUBLIC)
|
|
|
|
{
|
|
|
|
return AccessorType.PUBLIC;
|
|
|
|
}
|
|
|
|
else if(getSymbolType(token) == SymbolType.PROTECTED)
|
|
|
|
{
|
|
|
|
return AccessorType.PROTECTED;
|
|
|
|
}
|
|
|
|
else if(getSymbolType(token) == SymbolType.PRIVATE)
|
|
|
|
{
|
|
|
|
return AccessorType.PRIVATE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return AccessorType.UNKNOWN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-04 12:52:22 +00:00
|
|
|
private InitScope getInitScope(Token token)
|
|
|
|
{
|
|
|
|
if(getSymbolType(token) == SymbolType.STATIC)
|
|
|
|
{
|
|
|
|
return InitScope.STATIC;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return InitScope.UNKNOWN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-04 12:47:39 +00:00
|
|
|
|
|
|
|
/* STATUS: Not being used yet */
|
|
|
|
/**
|
|
|
|
* Called in an occurence of the: `static x`
|
|
|
|
*/
|
|
|
|
/* TODO: Anything that isn't static, is non-static => the false boolean should imply non-static */
|
|
|
|
private Entity parseInitScope()
|
|
|
|
{
|
|
|
|
Entity entity;
|
|
|
|
|
2021-06-04 12:52:22 +00:00
|
|
|
/* Save and consume the init-scope */
|
|
|
|
InitScope initScope = getInitScope(getCurrentToken());
|
2021-06-04 12:47:39 +00:00
|
|
|
nextToken();
|
|
|
|
|
2021-06-06 13:33:20 +00:00
|
|
|
/* Get the current token's symbol type */
|
|
|
|
SymbolType symbolType = getSymbolType(getCurrentToken());
|
|
|
|
|
|
|
|
/**
|
|
|
|
* TODO
|
|
|
|
*
|
|
|
|
* Topic of discussion: "What can be static?"
|
|
|
|
*
|
|
|
|
* Structs!
|
|
|
|
* As we might want them to be initted on class load or not (on instance initialization)
|
|
|
|
* Classes
|
|
|
|
* Likewise a class in a class could be initted if static then on outer class load so would inner
|
|
|
|
* If not then only inner class loads on outer instantiation
|
|
|
|
* Variables
|
|
|
|
* Initialize on class reference if static, however if not, then on instance initialization
|
|
|
|
*
|
|
|
|
* Note: There are two meanings for static (if you take C for example, I might add a word for that, `global` rather)
|
|
|
|
* Functions
|
|
|
|
* Journal entry describes this.
|
|
|
|
*
|
|
|
|
* Journal entry also describes this (/journal/static_keyword_addition/)
|
|
|
|
*/
|
|
|
|
/* If class */
|
|
|
|
if(symbolType == SymbolType.CLASS)
|
|
|
|
{
|
|
|
|
/* TODO: Set accessor on returned thing */
|
|
|
|
entity = parseClass();
|
|
|
|
}
|
|
|
|
/* If struct */
|
|
|
|
else if(symbolType == SymbolType.STRUCT)
|
|
|
|
{
|
|
|
|
/* TODO: Set accessor on returned thing */
|
|
|
|
entity = parseStruct();
|
|
|
|
gprintln("Poes"~to!(string)(entity));
|
|
|
|
}
|
|
|
|
/* If typed-definition (function or variable) */
|
|
|
|
else if(symbolType == SymbolType.IDENT_TYPE)
|
|
|
|
{
|
|
|
|
/* TODO: Set accesor on returned thing */
|
|
|
|
entity = cast(Entity)parseName();
|
|
|
|
|
|
|
|
if(!entity)
|
|
|
|
{
|
|
|
|
expect("Accessor got func call when expecting var/func def");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Error out */
|
|
|
|
else
|
|
|
|
{
|
|
|
|
expect("Expected either function definition, variable declaration, struct definition or class definition");
|
|
|
|
}
|
|
|
|
|
|
|
|
entity.setModifierType(initScope);
|
2021-06-04 12:47:39 +00:00
|
|
|
|
|
|
|
return entity;
|
|
|
|
}
|
|
|
|
|
2021-03-21 10:39:55 +00:00
|
|
|
/* STATUS: Not being used yet */
|
2021-03-21 20:51:42 +00:00
|
|
|
private Entity parseAccessor()
|
2021-03-21 10:39:29 +00:00
|
|
|
{
|
2021-03-21 20:51:42 +00:00
|
|
|
Entity entity;
|
2021-03-21 20:01:54 +00:00
|
|
|
|
2021-03-21 10:39:29 +00:00
|
|
|
/* Save and consume the accessor */
|
2021-03-21 12:03:13 +00:00
|
|
|
AccessorType accessorType = getAccessorType(getCurrentToken());
|
2021-03-21 10:39:29 +00:00
|
|
|
nextToken();
|
|
|
|
|
|
|
|
/* TODO: Only allow, private, public, protected */
|
|
|
|
/* TODO: Pass this to call for class prsewr or whatever comes after the accessor */
|
|
|
|
|
|
|
|
/* Get the current token's symbol type */
|
|
|
|
SymbolType symbolType = getSymbolType(getCurrentToken());
|
|
|
|
|
|
|
|
/* If class */
|
|
|
|
if(symbolType == SymbolType.CLASS)
|
|
|
|
{
|
2021-03-21 20:01:54 +00:00
|
|
|
/* TODO: Set accessor on returned thing */
|
2021-03-21 20:51:42 +00:00
|
|
|
entity = parseClass();
|
2021-03-21 10:39:29 +00:00
|
|
|
}
|
2021-05-04 17:17:33 +00:00
|
|
|
/* If struct */
|
|
|
|
else if(symbolType == SymbolType.STRUCT)
|
|
|
|
{
|
|
|
|
/* TODO: Set accessor on returned thing */
|
|
|
|
entity = parseStruct();
|
|
|
|
gprintln("Poes"~to!(string)(entity));
|
|
|
|
}
|
2021-03-21 10:39:29 +00:00
|
|
|
/* If typed-definition (function or variable) */
|
2021-03-28 19:54:38 +00:00
|
|
|
else if(symbolType == SymbolType.IDENT_TYPE)
|
2021-03-21 10:39:29 +00:00
|
|
|
{
|
2021-03-21 20:01:54 +00:00
|
|
|
/* TODO: Set accesor on returned thing */
|
2021-03-28 19:54:38 +00:00
|
|
|
entity = cast(Entity)parseName();
|
|
|
|
|
|
|
|
if(!entity)
|
|
|
|
{
|
|
|
|
expect("Accessor got func call when expecting var/func def");
|
|
|
|
}
|
2021-03-21 10:39:29 +00:00
|
|
|
}
|
2021-06-06 13:33:20 +00:00
|
|
|
/* If static */
|
|
|
|
else if(symbolType == SymbolType.STATIC)
|
|
|
|
{
|
|
|
|
entity = parseInitScope();
|
|
|
|
}
|
2021-03-21 10:39:29 +00:00
|
|
|
/* Error out */
|
|
|
|
else
|
|
|
|
{
|
2021-06-06 13:33:20 +00:00
|
|
|
expect("Expected either function definition, variable declaration, struct definition or class definition");
|
2021-03-21 10:39:29 +00:00
|
|
|
}
|
2021-03-21 20:01:54 +00:00
|
|
|
|
2021-03-21 20:51:42 +00:00
|
|
|
entity.setAccessorType(accessorType);
|
|
|
|
|
|
|
|
return entity;
|
2021-03-21 10:39:29 +00:00
|
|
|
}
|
2021-03-21 10:30:23 +00:00
|
|
|
|
|
|
|
private void parseFunctionArguments()
|
|
|
|
{
|
|
|
|
/* TODO: Use later */
|
|
|
|
/* TODO: Add support for default values for function arguments */
|
|
|
|
}
|
|
|
|
|
2021-03-21 15:57:56 +00:00
|
|
|
private struct funcDefPair
|
|
|
|
{
|
|
|
|
Statement[] bodyStatements;
|
2022-12-17 12:00:16 +00:00
|
|
|
VariableParameter[] params;
|
2021-03-21 15:57:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private funcDefPair parseFuncDef()
|
2021-03-03 13:17:51 +00:00
|
|
|
{
|
2021-03-05 10:35:58 +00:00
|
|
|
gprintln("parseFuncDef(): Enter", DebugType.WARNING);
|
|
|
|
|
2021-03-21 15:57:56 +00:00
|
|
|
Statement[] statements;
|
2022-12-17 12:00:16 +00:00
|
|
|
VariableParameter[] parameterList;
|
2021-03-21 15:57:56 +00:00
|
|
|
funcDefPair bruh;
|
|
|
|
|
|
|
|
|
2021-03-17 20:25:07 +00:00
|
|
|
/* Consume the `(` token */
|
2021-03-03 14:05:45 +00:00
|
|
|
nextToken();
|
|
|
|
|
2021-03-03 14:46:13 +00:00
|
|
|
/* Count for number of parameters processed */
|
|
|
|
ulong parameterCount;
|
|
|
|
|
2021-03-21 08:02:23 +00:00
|
|
|
/* Expecting more arguments */
|
|
|
|
bool moreArgs;
|
|
|
|
|
2021-03-03 14:05:45 +00:00
|
|
|
/* Get command-line arguments */
|
2021-03-05 10:35:58 +00:00
|
|
|
while (hasTokens())
|
2021-03-03 14:05:45 +00:00
|
|
|
{
|
2021-03-21 08:02:23 +00:00
|
|
|
/* Check if the first thing is a type */
|
2021-03-28 19:54:38 +00:00
|
|
|
if(getSymbolType(getCurrentToken()) == SymbolType.IDENT_TYPE)
|
2021-03-03 14:30:56 +00:00
|
|
|
{
|
2021-03-28 19:54:38 +00:00
|
|
|
/* Get the type (this can be doted) */
|
2021-03-21 08:02:23 +00:00
|
|
|
string type = getCurrentToken().getToken();
|
2021-03-03 14:30:56 +00:00
|
|
|
nextToken();
|
2021-03-05 10:35:58 +00:00
|
|
|
|
2023-01-12 08:53:48 +00:00
|
|
|
/* If it is a star `*` */
|
2023-01-12 09:17:32 +00:00
|
|
|
while(getSymbolType(getCurrentToken()) == SymbolType.STAR)
|
2023-01-12 08:53:48 +00:00
|
|
|
{
|
|
|
|
// Make type a pointer
|
|
|
|
type = type~"*";
|
|
|
|
nextToken();
|
|
|
|
}
|
|
|
|
|
2021-03-28 19:54:38 +00:00
|
|
|
/* Get the identifier (This CAN NOT be dotted) */
|
|
|
|
expect(SymbolType.IDENT_TYPE, getCurrentToken());
|
2021-03-28 20:32:44 +00:00
|
|
|
if(!isIdentifier_NoDot(getCurrentToken()))
|
2021-03-28 19:54:38 +00:00
|
|
|
{
|
|
|
|
expect("Identifier can not be path");
|
|
|
|
}
|
2021-03-21 08:02:23 +00:00
|
|
|
string identifier = getCurrentToken().getToken();
|
2021-03-03 17:53:47 +00:00
|
|
|
nextToken();
|
2021-03-16 08:57:04 +00:00
|
|
|
|
2021-03-22 09:18:56 +00:00
|
|
|
|
|
|
|
/* Add the local variable (parameter variable) */
|
2022-12-17 12:00:16 +00:00
|
|
|
parameterList ~= new VariableParameter(type, identifier);
|
2021-03-22 09:18:56 +00:00
|
|
|
|
2021-03-21 08:02:23 +00:00
|
|
|
moreArgs = false;
|
|
|
|
|
|
|
|
parameterCount++;
|
2021-03-03 14:30:56 +00:00
|
|
|
}
|
2021-03-21 08:02:23 +00:00
|
|
|
/* If we get a comma */
|
|
|
|
else if(getSymbolType(getCurrentToken()) == SymbolType.COMMA)
|
2021-03-03 14:30:56 +00:00
|
|
|
{
|
2021-03-21 08:02:23 +00:00
|
|
|
/* Consume the `,` */
|
2021-03-03 14:46:13 +00:00
|
|
|
nextToken();
|
2021-03-21 08:02:23 +00:00
|
|
|
|
|
|
|
moreArgs = true;
|
|
|
|
}
|
|
|
|
/* Check if it is a closing brace */
|
|
|
|
else if(getSymbolType(getCurrentToken()) == SymbolType.RBRACE)
|
|
|
|
{
|
|
|
|
/* Make sure we were not expecting more arguments */
|
|
|
|
if(!moreArgs)
|
|
|
|
{
|
|
|
|
/* Consume the `)` */
|
|
|
|
nextToken();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* Error out if we were and we prematurely ended */
|
|
|
|
else
|
|
|
|
{
|
2021-03-28 19:54:38 +00:00
|
|
|
expect(SymbolType.IDENT_TYPE, getCurrentToken());
|
2021-03-21 08:02:23 +00:00
|
|
|
}
|
2021-03-03 14:30:56 +00:00
|
|
|
}
|
2021-03-21 08:02:23 +00:00
|
|
|
/* Error out */
|
2021-03-03 14:30:56 +00:00
|
|
|
else
|
|
|
|
{
|
2021-03-21 08:02:23 +00:00
|
|
|
expect("Expected either type or )");
|
2021-03-03 14:30:56 +00:00
|
|
|
}
|
2021-03-03 14:05:45 +00:00
|
|
|
}
|
2021-03-05 10:35:58 +00:00
|
|
|
|
2021-03-21 08:02:23 +00:00
|
|
|
expect(SymbolType.OCURLY, getCurrentToken());
|
|
|
|
|
|
|
|
/* Parse the body (and it leaves ONLY when it gets the correct symbol, no expect needed) */
|
2021-03-21 15:57:56 +00:00
|
|
|
statements = parseBody();
|
2021-03-21 08:02:23 +00:00
|
|
|
nextToken();
|
|
|
|
|
2021-03-16 08:57:04 +00:00
|
|
|
gprintln("ParseFuncDef: Parameter count: " ~ to!(string)(parameterCount));
|
2021-03-05 10:35:58 +00:00
|
|
|
gprintln("parseFuncDef(): Leave", DebugType.WARNING);
|
2021-03-21 15:57:56 +00:00
|
|
|
|
|
|
|
bruh.bodyStatements = statements;
|
2022-12-17 12:00:16 +00:00
|
|
|
bruh.params = parameterList;
|
2021-03-28 19:54:38 +00:00
|
|
|
|
2021-03-21 15:57:56 +00:00
|
|
|
return bruh;
|
2021-03-03 13:17:51 +00:00
|
|
|
}
|
|
|
|
|
2021-06-09 10:55:59 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Only a subset of expressions are parsed without coming after
|
|
|
|
* an assignment, functioncall parameters etc
|
|
|
|
*
|
|
|
|
* Therefore instead of mirroring a lot fo what is in expression, for now atleast
|
|
|
|
* I will support everything using discard
|
|
|
|
*
|
|
|
|
* TODO: Remove discard and implement the needed mirrors
|
|
|
|
*/
|
2023-01-13 08:49:47 +00:00
|
|
|
private DiscardStatement parseDiscard()
|
2021-06-09 10:55:59 +00:00
|
|
|
{
|
|
|
|
/* Consume the `discard` */
|
|
|
|
nextToken();
|
|
|
|
|
2021-06-09 10:57:43 +00:00
|
|
|
/* Parse the following expression */
|
|
|
|
Expression expression = parseExpression();
|
|
|
|
|
|
|
|
/* Expect a semi-colon */
|
|
|
|
expect(SymbolType.SEMICOLON, getCurrentToken());
|
|
|
|
nextToken();
|
|
|
|
|
2023-01-13 08:49:47 +00:00
|
|
|
/* Create a `discard` statement */
|
|
|
|
DiscardStatement discardStatement = new DiscardStatement(expression);
|
|
|
|
|
|
|
|
return discardStatement;
|
2021-06-09 10:55:59 +00:00
|
|
|
}
|
|
|
|
|
2021-06-09 15:44:21 +00:00
|
|
|
/**
|
|
|
|
* Parses the `new Class()` expression
|
|
|
|
*/
|
|
|
|
|
2021-06-09 10:55:59 +00:00
|
|
|
|
2021-03-05 09:46:51 +00:00
|
|
|
/**
|
|
|
|
* Parses an expression
|
|
|
|
*
|
2021-03-05 09:58:51 +00:00
|
|
|
* TODO:
|
|
|
|
*
|
2021-03-05 09:46:51 +00:00
|
|
|
* I think we need a loop here to move till we hit a terminator like `)`
|
|
|
|
* in the case of a condition's/function's argument expression or `;` in
|
|
|
|
* the case of a assignment's expression.
|
|
|
|
*
|
|
|
|
* This means we will be able to get the `+` token and process it
|
|
|
|
* We will also terminate on `;` or `)` and that means our `else` can be
|
|
|
|
* left to error out for unknowns then
|
|
|
|
*/
|
2021-03-21 15:57:56 +00:00
|
|
|
private Expression parseExpression()
|
2021-03-03 13:17:51 +00:00
|
|
|
{
|
2021-03-05 10:35:58 +00:00
|
|
|
gprintln("parseExpression(): Enter", DebugType.WARNING);
|
|
|
|
|
2021-03-29 12:35:52 +00:00
|
|
|
/* The expression to be returned */
|
|
|
|
Expression[] retExpression;
|
2021-03-21 15:57:56 +00:00
|
|
|
|
2021-03-29 12:35:52 +00:00
|
|
|
void addRetExp(Expression e)
|
|
|
|
{
|
|
|
|
retExpression ~= e;
|
|
|
|
}
|
|
|
|
|
|
|
|
Expression removeExp()
|
|
|
|
{
|
|
|
|
Expression poppedExp = retExpression[retExpression.length-1];
|
|
|
|
retExpression.length--;
|
|
|
|
|
|
|
|
return poppedExp;
|
|
|
|
}
|
|
|
|
|
2021-03-29 15:31:17 +00:00
|
|
|
bool hasExp()
|
|
|
|
{
|
|
|
|
return retExpression.length != 0;
|
|
|
|
}
|
|
|
|
|
2021-03-29 12:35:52 +00:00
|
|
|
void expressionStackSanityCheck()
|
|
|
|
{
|
|
|
|
/* If we don't have 1 on the stack */
|
|
|
|
if(retExpression.length != 1)
|
|
|
|
{
|
|
|
|
gprintln(retExpression);
|
|
|
|
expect("Expression parsing failed as we had remaining items on the expression parser stack or zero");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: Unless I am wrong we can do a check that retExp should always be length 1 */
|
|
|
|
/* TODO: Makes sure that expressions like 1 1 don't wortk */
|
|
|
|
/* TODO: It must always be consumed */
|
2021-03-21 15:57:56 +00:00
|
|
|
|
2021-03-03 13:17:51 +00:00
|
|
|
/* TODO: Implement expression parsing */
|
|
|
|
|
2021-03-15 08:46:00 +00:00
|
|
|
/**
|
|
|
|
* We loop here until we hit something that closes
|
|
|
|
* an expression, in other words an expression
|
|
|
|
* appears in variable assignments which end with a
|
|
|
|
* `;`, they also appear in conditions which end in
|
|
|
|
* a `)`
|
|
|
|
*/
|
|
|
|
while (true)
|
2021-03-03 15:18:19 +00:00
|
|
|
{
|
2021-03-15 08:46:00 +00:00
|
|
|
SymbolType symbol = getSymbolType(getCurrentToken());
|
2021-03-03 15:28:39 +00:00
|
|
|
|
2021-03-29 12:35:52 +00:00
|
|
|
gprintln(retExpression);
|
|
|
|
|
2021-03-15 08:46:00 +00:00
|
|
|
/* If it is a number literal */
|
|
|
|
if (symbol == SymbolType.NUMBER_LITERAL)
|
2021-03-29 12:35:52 +00:00
|
|
|
{
|
|
|
|
/* TODO: Do number checking here to get correct NUmberLiteral */
|
|
|
|
NumberLiteral numberLiteral = new NumberLiteral(getCurrentToken().getToken());
|
|
|
|
|
|
|
|
/* Add expression to stack */
|
|
|
|
addRetExp(numberLiteral);
|
|
|
|
|
2021-03-15 08:46:00 +00:00
|
|
|
/* Get the next token */
|
|
|
|
nextToken();
|
|
|
|
}
|
|
|
|
/* If it is a maths operator */
|
2022-04-13 07:45:06 +00:00
|
|
|
/* TODO: Handle all operators here (well most), just include bit operators */
|
|
|
|
else if (isMathOp(getCurrentToken()) || isBinaryOp(getCurrentToken()))
|
2021-03-03 15:18:19 +00:00
|
|
|
{
|
2021-03-29 15:31:17 +00:00
|
|
|
SymbolType operatorType = getSymbolType(getCurrentToken());
|
2021-03-29 12:35:52 +00:00
|
|
|
|
|
|
|
/* TODO: Save operator, also pass to constructor */
|
2021-03-15 08:46:00 +00:00
|
|
|
/* TODO: Parse expression or pass arithemetic (I think latter) */
|
2021-03-03 15:18:19 +00:00
|
|
|
nextToken();
|
|
|
|
|
2021-03-29 15:31:17 +00:00
|
|
|
OperatorExpression opExp;
|
2021-03-29 12:35:52 +00:00
|
|
|
|
2021-03-29 15:31:17 +00:00
|
|
|
/* Check if unary or not (if so no expressions on stack) */
|
|
|
|
if(!hasExp())
|
|
|
|
{
|
2022-04-13 07:45:06 +00:00
|
|
|
/* Only `*`, `+` and `-` are valid or `~` */
|
|
|
|
if(operatorType == SymbolType.STAR || operatorType == SymbolType.ADD || operatorType == SymbolType.SUB || operatorType == SymbolType.TILDE)
|
2022-04-12 11:03:40 +00:00
|
|
|
{
|
|
|
|
/* Parse the expression following the unary operator */
|
|
|
|
Expression rhs = parseExpression();
|
|
|
|
|
|
|
|
/* Create UnaryExpression comprised of the operator and the right-hand side expression */
|
|
|
|
opExp = new UnaryOperatorExpression(operatorType, rhs);
|
|
|
|
}
|
2022-04-13 07:45:06 +00:00
|
|
|
/* Support for ampersand (&) */
|
|
|
|
else if(operatorType == SymbolType.AMPERSAND)
|
|
|
|
{
|
|
|
|
/* Expression can only be a `VariableExpression` which accounts for Function Handles and Variable Identifiers */
|
|
|
|
Expression rhs = parseExpression();
|
|
|
|
gprintln("hhshhshshsh");
|
|
|
|
if(cast(VariableExpression)rhs)
|
|
|
|
{
|
|
|
|
/* Create UnaryExpression comprised of the operator and the right-hand side expression */
|
|
|
|
opExp = new UnaryOperatorExpression(operatorType, rhs);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
expect("& operator can only be followed by a variable expression");
|
|
|
|
}
|
|
|
|
}
|
2022-04-12 11:03:40 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
expect("Expected *, + or - as unary operators but got "~to!(string)(operatorType));
|
|
|
|
}
|
2021-03-29 15:31:17 +00:00
|
|
|
}
|
|
|
|
/* If has, then binary */
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Pop left-hand side expression */
|
|
|
|
/* TODO: We should have error checking for `removeExp()` */
|
|
|
|
/* TODO: Make it automatically exit if not enough exps */
|
|
|
|
Expression lhs = removeExp();
|
|
|
|
|
|
|
|
/* Parse expression (the right-hand side) */
|
|
|
|
Expression rhs = parseExpression();
|
|
|
|
|
|
|
|
/* Create BinaryOpertaor Expression */
|
|
|
|
opExp = new BinaryOperatorExpression(operatorType, lhs, rhs);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add operator expression to stack */
|
|
|
|
addRetExp(opExp);
|
2021-03-03 15:18:19 +00:00
|
|
|
}
|
2021-03-15 08:46:00 +00:00
|
|
|
/* If it is a string literal */
|
|
|
|
else if (symbol == SymbolType.STRING_LITERAL)
|
2021-03-03 15:18:19 +00:00
|
|
|
{
|
2021-03-29 12:35:52 +00:00
|
|
|
/* Add the string to the stack */
|
|
|
|
addRetExp(new StringExpression(getCurrentToken().getToken()));
|
|
|
|
|
2021-03-15 08:46:00 +00:00
|
|
|
/* Get the next token */
|
|
|
|
nextToken();
|
2021-03-03 15:18:19 +00:00
|
|
|
}
|
2021-03-15 08:46:00 +00:00
|
|
|
/* If it is an identifier */
|
2021-03-28 19:54:38 +00:00
|
|
|
else if (symbol == SymbolType.IDENT_TYPE)
|
2021-03-15 08:46:00 +00:00
|
|
|
{
|
|
|
|
string identifier = getCurrentToken().getToken();
|
2021-03-03 21:10:21 +00:00
|
|
|
|
2021-03-15 08:46:00 +00:00
|
|
|
nextToken();
|
2021-03-03 21:10:21 +00:00
|
|
|
|
2021-03-29 12:35:52 +00:00
|
|
|
Expression toAdd;
|
|
|
|
|
2021-03-15 08:46:00 +00:00
|
|
|
/* If the symbol is `(` then function call */
|
|
|
|
if (getSymbolType(getCurrentToken()) == SymbolType.LBRACE)
|
|
|
|
{
|
|
|
|
/* TODO: Implement function call parsing */
|
2021-03-28 19:54:38 +00:00
|
|
|
previousToken();
|
2021-03-29 12:35:52 +00:00
|
|
|
toAdd = parseFuncCall();
|
2021-03-15 08:46:00 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* TODO: Leave the token here */
|
|
|
|
/* TODO: Just leave it, yeah */
|
2021-03-28 20:05:58 +00:00
|
|
|
// expect("poes");
|
2021-03-29 19:06:37 +00:00
|
|
|
toAdd = new VariableExpression(identifier);
|
2022-04-13 07:45:06 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* FIXME: To properly support function handles I think we are going to need a new type
|
|
|
|
* Well not here, this should technically be IdentExpression.
|
|
|
|
*/
|
2021-03-15 08:46:00 +00:00
|
|
|
}
|
2021-03-29 12:35:52 +00:00
|
|
|
|
|
|
|
/* TODO: Change this later, for now we doing this */
|
|
|
|
addRetExp(toAdd);
|
2021-03-15 08:46:00 +00:00
|
|
|
}
|
2021-03-17 20:25:07 +00:00
|
|
|
/* Detect if this expression is coming to an end, then return */
|
2023-01-12 08:53:48 +00:00
|
|
|
else if (symbol == SymbolType.SEMICOLON || symbol == SymbolType.RBRACE || symbol == SymbolType.COMMA || symbol == SymbolType.ASSIGN)
|
2021-03-03 21:10:21 +00:00
|
|
|
{
|
2021-03-15 08:46:00 +00:00
|
|
|
break;
|
2021-03-04 21:25:20 +00:00
|
|
|
}
|
2021-03-15 08:50:06 +00:00
|
|
|
/**
|
2021-03-16 08:36:50 +00:00
|
|
|
* For ()
|
2021-03-15 08:50:06 +00:00
|
|
|
*/
|
2021-03-18 17:20:17 +00:00
|
|
|
else if (symbol == SymbolType.LBRACE)
|
2021-03-15 09:29:05 +00:00
|
|
|
{
|
2021-03-16 05:30:18 +00:00
|
|
|
/* Consume the `(` */
|
2021-03-15 09:29:05 +00:00
|
|
|
nextToken();
|
|
|
|
|
2021-03-16 05:30:18 +00:00
|
|
|
/* Parse the inner expression till terminator */
|
2021-03-29 12:48:47 +00:00
|
|
|
addRetExp(parseExpression());
|
2021-03-15 09:29:05 +00:00
|
|
|
|
2021-03-16 05:30:18 +00:00
|
|
|
/* Consume the terminator */
|
2021-03-15 09:29:05 +00:00
|
|
|
nextToken();
|
|
|
|
}
|
2021-06-09 10:44:26 +00:00
|
|
|
/**
|
|
|
|
* `new` operator
|
|
|
|
*/
|
|
|
|
else if(symbol == SymbolType.NEW)
|
|
|
|
{
|
|
|
|
/* Cosume the `new` */
|
|
|
|
nextToken();
|
|
|
|
|
|
|
|
/* Get the identifier */
|
|
|
|
string identifier = getCurrentToken().getToken();
|
|
|
|
nextToken();
|
2021-06-09 10:55:59 +00:00
|
|
|
|
2021-06-09 10:44:26 +00:00
|
|
|
|
2021-06-15 12:27:32 +00:00
|
|
|
NewExpression toAdd;
|
|
|
|
FunctionCall functionCallPart;
|
2021-06-09 10:44:26 +00:00
|
|
|
|
|
|
|
/* If the symbol is `(` then function call */
|
|
|
|
if (getSymbolType(getCurrentToken()) == SymbolType.LBRACE)
|
|
|
|
{
|
|
|
|
/* TODO: Implement function call parsing */
|
|
|
|
previousToken();
|
2021-06-15 12:27:32 +00:00
|
|
|
functionCallPart = parseFuncCall();
|
2021-06-09 10:44:26 +00:00
|
|
|
}
|
|
|
|
/* If not an `(` */
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Raise a syntax error */
|
|
|
|
expect(SymbolType.LBRACE, getCurrentToken());
|
|
|
|
}
|
|
|
|
|
2021-06-15 12:27:32 +00:00
|
|
|
/* Create a NewExpression with the associated FunctionCall */
|
|
|
|
toAdd = new NewExpression(functionCallPart);
|
|
|
|
|
2021-06-09 10:44:26 +00:00
|
|
|
/* Add the expression */
|
|
|
|
addRetExp(toAdd);
|
2021-06-15 12:27:32 +00:00
|
|
|
}
|
|
|
|
/* TODO: New addition (UNTESTED, remove if problem causer) */
|
|
|
|
else if(symbol == SymbolType.DOT)
|
|
|
|
{
|
|
|
|
/* Pop the previous expression */
|
|
|
|
Expression previousExpression = removeExp();
|
|
|
|
|
|
|
|
/* TODO: Get next expression */
|
|
|
|
nextToken();
|
|
|
|
Expression item = parseExpression();
|
|
|
|
|
|
|
|
/* TODO: Construct accessor expression from both and addRetExp */
|
|
|
|
|
|
|
|
BinaryOperatorExpression binOp = new BinaryOperatorExpression(SymbolType.DOT, previousExpression, item);
|
2021-06-09 10:44:26 +00:00
|
|
|
|
2021-06-15 12:27:32 +00:00
|
|
|
addRetExp(binOp);
|
2021-06-09 10:44:26 +00:00
|
|
|
}
|
2021-03-04 21:25:20 +00:00
|
|
|
else
|
|
|
|
{
|
2021-03-15 08:46:00 +00:00
|
|
|
//gprintln("parseExpression(): NO MATCH", DebugType.ERROR);
|
|
|
|
/* TODO: Something isn't right here */
|
|
|
|
expect("Expected expression terminator ) or ;");
|
2021-03-03 21:10:21 +00:00
|
|
|
}
|
|
|
|
}
|
2021-03-03 15:18:19 +00:00
|
|
|
|
2021-03-29 12:48:47 +00:00
|
|
|
|
|
|
|
gprintln(retExpression);
|
2021-03-05 10:35:58 +00:00
|
|
|
gprintln("parseExpression(): Leave", DebugType.WARNING);
|
2021-03-21 15:57:56 +00:00
|
|
|
|
2021-03-29 12:35:52 +00:00
|
|
|
/* TODO: DO check here for retExp.length = 1 */
|
|
|
|
expressionStackSanityCheck();
|
2021-03-21 15:57:56 +00:00
|
|
|
|
2021-03-29 12:35:52 +00:00
|
|
|
return retExpression[0];
|
2021-03-03 13:17:51 +00:00
|
|
|
}
|
|
|
|
|
2021-03-21 15:57:56 +00:00
|
|
|
private TypedEntity parseTypedDeclaration()
|
2021-03-03 12:42:57 +00:00
|
|
|
{
|
2021-03-05 10:35:58 +00:00
|
|
|
gprintln("parseTypedDeclaration(): Enter", DebugType.WARNING);
|
|
|
|
|
2021-03-21 15:57:56 +00:00
|
|
|
|
|
|
|
/* Generated object */
|
|
|
|
TypedEntity generated;
|
|
|
|
|
|
|
|
|
2021-03-03 12:42:57 +00:00
|
|
|
/* TODO: Save type */
|
|
|
|
string type = getCurrentToken().getToken();
|
|
|
|
string identifier;
|
|
|
|
|
2023-01-12 08:53:48 +00:00
|
|
|
|
|
|
|
// TODO: Insert pointer `*`-handling code here
|
2021-03-03 12:42:57 +00:00
|
|
|
nextToken();
|
2023-01-12 08:53:48 +00:00
|
|
|
ulong derefCount = 0;
|
|
|
|
|
|
|
|
/* If we have a star */
|
|
|
|
while(getSymbolType(getCurrentToken()) == SymbolType.STAR)
|
|
|
|
{
|
|
|
|
derefCount+=1;
|
|
|
|
type=type~"*";
|
|
|
|
nextToken();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Expect an identifier (CAN NOT be dotted) */
|
2021-03-28 19:54:38 +00:00
|
|
|
expect(SymbolType.IDENT_TYPE, getCurrentToken());
|
2021-03-28 20:27:09 +00:00
|
|
|
if(!isIdentifier_NoDot(getCurrentToken()))
|
2021-03-28 19:54:38 +00:00
|
|
|
{
|
|
|
|
expect("Identifier cannot be dotted");
|
|
|
|
}
|
2021-03-03 12:42:57 +00:00
|
|
|
identifier = getCurrentToken().getToken();
|
|
|
|
|
|
|
|
nextToken();
|
2021-03-05 10:35:58 +00:00
|
|
|
gprintln("ParseTypedDec: DecisionBtwn FuncDef/VarDef: " ~ getCurrentToken().getToken());
|
2021-03-03 13:17:51 +00:00
|
|
|
|
|
|
|
/* Check if it is `(` (func dec) */
|
|
|
|
SymbolType symbolType = getSymbolType(getCurrentToken());
|
2021-03-05 10:35:58 +00:00
|
|
|
gprintln("ParseTypedDec: SymbolType=" ~ to!(string)(symbolType));
|
|
|
|
if (symbolType == SymbolType.LBRACE)
|
2021-03-03 13:17:51 +00:00
|
|
|
{
|
2021-03-21 15:57:56 +00:00
|
|
|
funcDefPair pair = parseFuncDef();
|
2021-03-05 10:35:58 +00:00
|
|
|
|
2022-12-17 12:00:16 +00:00
|
|
|
generated = new Function(identifier, type, pair.bodyStatements, pair.params);
|
2021-03-21 15:57:56 +00:00
|
|
|
|
|
|
|
import std.stdio;
|
|
|
|
writeln(to!(string)((cast(Function)generated).getVariables()));
|
2022-10-01 18:54:50 +00:00
|
|
|
|
2022-12-17 12:00:16 +00:00
|
|
|
// Parent the parameters of the function to the Function
|
|
|
|
parentToContainer(cast(Container)generated, cast(Statement[])pair.params);
|
2022-10-01 18:54:50 +00:00
|
|
|
|
2022-12-17 12:00:16 +00:00
|
|
|
// Parent the statements that make up the function to the Function
|
2022-10-01 18:54:50 +00:00
|
|
|
parentToContainer(cast(Container)generated, pair.bodyStatements);
|
2021-03-03 13:17:51 +00:00
|
|
|
}
|
|
|
|
/* Check for semi-colon (var dec) */
|
2021-03-05 10:35:58 +00:00
|
|
|
else if (symbolType == SymbolType.SEMICOLON)
|
2021-03-03 13:17:51 +00:00
|
|
|
{
|
|
|
|
nextToken();
|
2021-03-05 10:35:58 +00:00
|
|
|
gprintln("ParseTypedDec: VariableDeclaration: (Type: " ~ type ~ ", Identifier: " ~ identifier ~ ")",
|
|
|
|
DebugType.WARNING);
|
2021-03-21 15:57:56 +00:00
|
|
|
|
|
|
|
generated = new Variable(type, identifier);
|
2021-03-03 13:17:51 +00:00
|
|
|
}
|
|
|
|
/* Check for `=` (var dec) */
|
2021-03-05 10:35:58 +00:00
|
|
|
else if (symbolType == SymbolType.ASSIGN)
|
2021-03-03 13:17:51 +00:00
|
|
|
{
|
|
|
|
nextToken();
|
|
|
|
|
|
|
|
/* Now parse an expression */
|
2021-03-21 15:57:56 +00:00
|
|
|
Expression expression = parseExpression();
|
|
|
|
|
|
|
|
VariableAssignment varAssign = new VariableAssignment(expression);
|
2021-03-03 13:17:51 +00:00
|
|
|
|
2021-03-03 15:28:39 +00:00
|
|
|
/**
|
|
|
|
* The symbol that returned us from `parseExpression` must
|
|
|
|
* be a semi-colon
|
|
|
|
*/
|
2021-03-03 13:17:51 +00:00
|
|
|
expect(SymbolType.SEMICOLON, getCurrentToken());
|
|
|
|
|
|
|
|
nextToken();
|
|
|
|
|
2021-03-05 10:35:58 +00:00
|
|
|
gprintln("ParseTypedDec: VariableDeclarationWithAssingment: (Type: "
|
|
|
|
~ type ~ ", Identifier: " ~ identifier ~ ")", DebugType.WARNING);
|
2021-03-21 15:57:56 +00:00
|
|
|
|
2021-03-23 19:35:13 +00:00
|
|
|
Variable variable = new Variable(type, identifier);
|
2021-03-21 15:57:56 +00:00
|
|
|
variable.addAssignment(varAssign);
|
|
|
|
|
2021-06-15 09:10:50 +00:00
|
|
|
varAssign.setVariable(variable);
|
|
|
|
|
2021-03-21 15:57:56 +00:00
|
|
|
generated = variable;
|
2021-03-03 13:17:51 +00:00
|
|
|
}
|
2021-03-03 13:32:28 +00:00
|
|
|
else
|
|
|
|
{
|
2021-03-05 10:03:31 +00:00
|
|
|
expect("Expected one of the following: (, ; or =");
|
2021-03-03 13:32:28 +00:00
|
|
|
}
|
2021-03-03 12:42:57 +00:00
|
|
|
|
2021-03-03 14:30:56 +00:00
|
|
|
/* TODO: If we outta tokens we should not call this */
|
|
|
|
// gprintln(getCurrentToken());
|
2021-03-05 10:35:58 +00:00
|
|
|
gprintln("parseTypedDeclaration(): Leave", DebugType.WARNING);
|
2021-03-21 15:57:56 +00:00
|
|
|
|
|
|
|
return generated;
|
2021-03-03 12:42:57 +00:00
|
|
|
}
|
|
|
|
|
2021-03-04 21:25:20 +00:00
|
|
|
/**
|
|
|
|
* Parses a class definition
|
|
|
|
*
|
|
|
|
* This is called when there is an occurrence of
|
|
|
|
* a token `class`
|
|
|
|
*/
|
2021-03-21 20:51:42 +00:00
|
|
|
private Clazz parseClass()
|
2021-03-04 21:25:20 +00:00
|
|
|
{
|
2021-03-05 10:35:58 +00:00
|
|
|
gprintln("parseClass(): Enter", DebugType.WARNING);
|
|
|
|
|
2021-03-21 15:57:56 +00:00
|
|
|
Clazz generated;
|
|
|
|
|
2021-03-04 21:25:20 +00:00
|
|
|
/* Pop off the `class` */
|
|
|
|
nextToken();
|
|
|
|
|
2021-03-28 19:54:38 +00:00
|
|
|
/* Get the class's name (CAN NOT be dotted) */
|
|
|
|
expect(SymbolType.IDENT_TYPE, getCurrentToken());
|
2021-03-28 20:29:33 +00:00
|
|
|
if(!isIdentifier_NoDot(getCurrentToken()))
|
2021-03-28 19:54:38 +00:00
|
|
|
{
|
|
|
|
expect("Class name in declaration cannot be path");
|
|
|
|
}
|
2021-03-04 21:25:20 +00:00
|
|
|
string className = getCurrentToken().getToken();
|
2021-03-05 10:35:58 +00:00
|
|
|
gprintln("parseClass(): Class name found '" ~ className ~ "'");
|
2021-03-04 21:25:20 +00:00
|
|
|
nextToken();
|
2021-03-21 07:45:40 +00:00
|
|
|
|
2021-03-21 15:57:56 +00:00
|
|
|
generated = new Clazz(className);
|
|
|
|
|
2021-03-30 18:05:16 +00:00
|
|
|
string[] inheritList;
|
2021-03-21 07:45:40 +00:00
|
|
|
|
|
|
|
/* TODO: If we have the inherit symbol `:` */
|
|
|
|
if(getSymbolType(getCurrentToken()) == SymbolType.INHERIT_OPP)
|
|
|
|
{
|
|
|
|
/* TODO: Loop until `}` */
|
|
|
|
|
|
|
|
/* Consume the inheritance operator `:` */
|
|
|
|
nextToken();
|
|
|
|
|
|
|
|
while(true)
|
|
|
|
{
|
2021-03-28 19:54:38 +00:00
|
|
|
/* Check if it is an identifier (may be dotted) */
|
|
|
|
expect(SymbolType.IDENT_TYPE, getCurrentToken());
|
2021-03-30 18:05:16 +00:00
|
|
|
inheritList ~= getCurrentToken().getToken();
|
2021-03-21 07:45:40 +00:00
|
|
|
nextToken();
|
|
|
|
|
|
|
|
/* Check if we have ended with a `{` */
|
|
|
|
if(getSymbolType(getCurrentToken()) == SymbolType.OCURLY)
|
|
|
|
{
|
|
|
|
/* Exit */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* If we get a comma */
|
|
|
|
else if(getSymbolType(getCurrentToken()) == SymbolType.COMMA)
|
|
|
|
{
|
|
|
|
/* Consume */
|
|
|
|
nextToken();
|
|
|
|
}
|
|
|
|
/* Error out if we get anything else */
|
|
|
|
else
|
|
|
|
{
|
|
|
|
expect("Expected either { or ,");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-03-04 21:25:20 +00:00
|
|
|
|
2021-06-07 14:55:48 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-06-07 15:01:55 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* TODO: Here we will do a while loop */
|
|
|
|
expect(SymbolType.OCURLY, getCurrentToken());
|
|
|
|
nextToken();
|
|
|
|
|
|
|
|
Statement[] statements;
|
|
|
|
|
|
|
|
while(true)
|
|
|
|
{
|
|
|
|
/* Get current token */
|
|
|
|
SymbolType symbolType = getSymbolType(getCurrentToken());
|
|
|
|
|
|
|
|
/* The possibly valid returned struct member (Entity) */
|
|
|
|
Statement structMember;
|
|
|
|
|
|
|
|
/** TODO:
|
|
|
|
* We only want to allow function definitions and variable
|
|
|
|
* declarations here (WIP: for now without assignments)
|
|
|
|
*
|
|
|
|
* parseAccessor() supports those BUT it will also allow classes
|
|
|
|
* and further structs - this we do not want and hence we should
|
|
|
|
* filter out those (raise an error) on checking the type of
|
|
|
|
* Entity returned by `parseAccessor()`
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
/* If it is a type */
|
|
|
|
if (symbolType == SymbolType.IDENT_TYPE)
|
|
|
|
{
|
|
|
|
/* Might be a function, might be a variable, or assignment */
|
|
|
|
structMember = parseName();
|
|
|
|
}
|
|
|
|
/* If it is a class */
|
|
|
|
else if(symbolType == SymbolType.CLASS)
|
|
|
|
{
|
|
|
|
structMember = parseClass();
|
|
|
|
}
|
|
|
|
/* If it is a struct */
|
|
|
|
else if(symbolType == SymbolType.STRUCT)
|
|
|
|
{
|
|
|
|
structMember = parseStruct();
|
|
|
|
}
|
|
|
|
/* If it is an accessor */
|
|
|
|
else if (isAccessor(getCurrentToken()))
|
|
|
|
{
|
|
|
|
structMember = parseAccessor();
|
|
|
|
}
|
|
|
|
/* If is is a modifier */
|
|
|
|
else if(isModifier(getCurrentToken()))
|
|
|
|
{
|
|
|
|
structMember = parseInitScope();
|
|
|
|
}
|
|
|
|
/* If closing brace then exit */
|
|
|
|
else if(symbolType == SymbolType.CCURLY)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
expect("Only classes, structs, instance fields, static fields, functions allowed in class");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Append to struct's body */
|
|
|
|
statements ~= structMember;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* TODO: Only allow variables here */
|
|
|
|
/* TODO: Only allowe VariableDeclarations (maybe assignments idk) */
|
|
|
|
/* TODO: Might, do what d does and allow function */
|
|
|
|
/* TODO: Which is just a codegen trick and implicit thing really */
|
|
|
|
/* TODO: I mean isn't OOP too lmao */
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-03-30 18:05:16 +00:00
|
|
|
/* Add inherit list */
|
|
|
|
generated.addInherit(inheritList);
|
|
|
|
|
2021-06-07 14:55:48 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-06-07 15:01:55 +00:00
|
|
|
// /* TODO: Technically we should be more specific, this does too much */
|
|
|
|
// /* Parse a body */
|
|
|
|
// Statement[] statements = parseBody();
|
2021-03-21 20:51:42 +00:00
|
|
|
generated.addStatements(statements);
|
2021-03-04 21:25:20 +00:00
|
|
|
|
2021-03-29 20:20:47 +00:00
|
|
|
/* Parent each Statement to the container */
|
|
|
|
parentToContainer(generated, statements);
|
|
|
|
|
2021-03-05 09:38:00 +00:00
|
|
|
/* Pop off the ending `}` */
|
2021-03-05 09:21:06 +00:00
|
|
|
nextToken();
|
2021-03-05 10:35:58 +00:00
|
|
|
|
|
|
|
gprintln("parseClass(): Leave", DebugType.WARNING);
|
2021-03-21 20:51:42 +00:00
|
|
|
|
|
|
|
return generated;
|
2021-03-04 21:25:20 +00:00
|
|
|
}
|
|
|
|
|
2021-03-29 20:20:47 +00:00
|
|
|
private void parentToContainer(Container container, Statement[] statements)
|
|
|
|
{
|
|
|
|
foreach(Statement statement; statements)
|
|
|
|
{
|
|
|
|
if(statement !is null)
|
|
|
|
{
|
|
|
|
statement.parentTo(container);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-12 08:53:48 +00:00
|
|
|
private Statement parseDerefAssignment()
|
|
|
|
{
|
|
|
|
gprintln("parseDerefAssignment(): Enter", DebugType.WARNING);
|
|
|
|
|
|
|
|
Statement statement;
|
|
|
|
|
|
|
|
/* Consume the star `*` */
|
|
|
|
nextToken();
|
|
|
|
ulong derefCnt = 1;
|
|
|
|
|
|
|
|
/* Check if there is another star */
|
|
|
|
while(getSymbolType(getCurrentToken()) == SymbolType.STAR)
|
|
|
|
{
|
|
|
|
derefCnt+=1;
|
|
|
|
nextToken();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Expect an expression */
|
|
|
|
Expression pointerExpression = parseExpression();
|
|
|
|
|
|
|
|
/* Expect an assignment operator */
|
|
|
|
expect(SymbolType.ASSIGN, getCurrentToken());
|
|
|
|
nextToken();
|
|
|
|
|
|
|
|
/* Expect an expression */
|
|
|
|
Expression assigmentExpression = parseExpression();
|
|
|
|
|
|
|
|
/* Expect a semicolon */
|
|
|
|
expect(SymbolType.SEMICOLON, getCurrentToken());
|
|
|
|
nextToken();
|
|
|
|
|
|
|
|
// FIXME: We should make a LHSPiinterAssignmentThing
|
|
|
|
statement = new PointerDereferenceAssignment(pointerExpression, assigmentExpression, derefCnt);
|
|
|
|
|
|
|
|
gprintln("parseDerefAssignment(): Leave", DebugType.WARNING);
|
|
|
|
|
|
|
|
return statement;
|
|
|
|
}
|
|
|
|
|
2023-01-11 08:43:29 +00:00
|
|
|
// TODO: This ic currently dead code and ought to be used/implemented
|
|
|
|
private Statement parseStatement(SymbolType terminatingSymbol = SymbolType.SEMICOLON)
|
2021-03-03 21:10:21 +00:00
|
|
|
{
|
2021-03-05 10:35:58 +00:00
|
|
|
gprintln("parseStatement(): Enter", DebugType.WARNING);
|
|
|
|
|
2023-01-11 08:43:29 +00:00
|
|
|
/* Get the token */
|
|
|
|
Token tok = getCurrentToken();
|
|
|
|
SymbolType symbol = getSymbolType(tok);
|
2021-03-03 21:10:21 +00:00
|
|
|
|
2023-01-11 08:43:29 +00:00
|
|
|
gprintln("parseStatement(): SymbolType=" ~ to!(string)(symbol));
|
|
|
|
|
|
|
|
Statement statement;
|
|
|
|
|
|
|
|
/* If it is a type */
|
|
|
|
if(symbol == SymbolType.IDENT_TYPE)
|
|
|
|
{
|
|
|
|
/* Might be a function, might be a variable, or assignment */
|
|
|
|
statement = parseName(terminatingSymbol);
|
|
|
|
}
|
|
|
|
/* If it is an accessor */
|
|
|
|
else if(isAccessor(tok))
|
|
|
|
{
|
|
|
|
statement = parseAccessor();
|
|
|
|
}
|
|
|
|
/* If it is a modifier */
|
|
|
|
else if(isModifier(tok))
|
|
|
|
{
|
|
|
|
statement = parseInitScope();
|
|
|
|
}
|
|
|
|
/* If it is a branch */
|
|
|
|
else if(symbol == SymbolType.IF)
|
|
|
|
{
|
|
|
|
statement = parseIf();
|
|
|
|
}
|
|
|
|
/* If it is a while loop */
|
|
|
|
else if(symbol == SymbolType.WHILE)
|
|
|
|
{
|
|
|
|
statement = parseWhile();
|
|
|
|
}
|
|
|
|
/* If it is a do-while loop */
|
|
|
|
else if(symbol == SymbolType.DO)
|
|
|
|
{
|
|
|
|
statement = parseDoWhile();
|
|
|
|
}
|
|
|
|
/* If it is a for loop */
|
|
|
|
else if(symbol == SymbolType.FOR)
|
|
|
|
{
|
|
|
|
statement = parseFor();
|
|
|
|
}
|
|
|
|
/* If it is a function call (further inspection needed) */
|
|
|
|
else if(symbol == SymbolType.IDENT_TYPE)
|
|
|
|
{
|
|
|
|
/* Function calls can have dotted identifiers */
|
|
|
|
parseFuncCall();
|
|
|
|
}
|
|
|
|
/* If it is the return keyword */
|
|
|
|
//TODO: We should add a flag to prevent return being used in generla bodies? or wait we have a non parseBiody already
|
|
|
|
else if(symbol == SymbolType.RETURN)
|
|
|
|
{
|
|
|
|
/* Parse the return statement */
|
|
|
|
statement = parseReturn();
|
|
|
|
}
|
2023-01-13 08:49:47 +00:00
|
|
|
/* If it is a `discard` statement */
|
|
|
|
else if(symbol == SymbolType.DISCARD)
|
|
|
|
{
|
|
|
|
/* Parse the discard statement */
|
|
|
|
statement = parseDiscard();
|
|
|
|
}
|
2023-01-12 08:53:48 +00:00
|
|
|
/* If it is a dereference assigment (a `*`) */
|
|
|
|
else if(symbol == SymbolType.STAR)
|
|
|
|
{
|
|
|
|
statement = parseDerefAssignment();
|
|
|
|
}
|
2023-01-11 08:43:29 +00:00
|
|
|
/* Error out */
|
|
|
|
else
|
|
|
|
{
|
|
|
|
expect("parseStatement(): Unknown symbol: " ~ getCurrentToken().getToken());
|
|
|
|
}
|
2021-03-05 10:35:58 +00:00
|
|
|
|
|
|
|
gprintln("parseStatement(): Leave", DebugType.WARNING);
|
2023-01-11 08:43:29 +00:00
|
|
|
|
|
|
|
return statement;
|
2021-03-03 21:10:21 +00:00
|
|
|
}
|
|
|
|
|
2021-06-15 12:27:32 +00:00
|
|
|
private FunctionCall parseFuncCall()
|
2021-03-03 16:34:38 +00:00
|
|
|
{
|
2021-03-05 10:35:58 +00:00
|
|
|
gprintln("parseFuncCall(): Enter", DebugType.WARNING);
|
2021-03-03 16:34:38 +00:00
|
|
|
|
2021-03-28 19:54:38 +00:00
|
|
|
/* TODO: Save name */
|
2021-03-29 12:35:52 +00:00
|
|
|
string functionName = getCurrentToken().getToken();
|
|
|
|
|
|
|
|
Expression[] arguments;
|
2021-03-28 19:54:38 +00:00
|
|
|
|
2021-03-03 16:34:38 +00:00
|
|
|
nextToken();
|
|
|
|
|
|
|
|
/* Expect an opening brace `(` */
|
|
|
|
expect(SymbolType.LBRACE, getCurrentToken());
|
|
|
|
nextToken();
|
|
|
|
|
2021-03-29 12:35:52 +00:00
|
|
|
/* If next token is RBRACE we don't expect arguments */
|
|
|
|
if(getSymbolType(getCurrentToken()) == SymbolType.RBRACE)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
/* If not expect arguments */
|
|
|
|
else
|
|
|
|
{
|
2021-03-30 14:25:26 +00:00
|
|
|
while(true)
|
|
|
|
{
|
|
|
|
/* Get the Expression */
|
|
|
|
Expression exp = parseExpression();
|
|
|
|
|
|
|
|
/* Add it to list */
|
|
|
|
arguments ~= exp;
|
|
|
|
|
|
|
|
/* Check if we exiting */
|
|
|
|
if(getSymbolType(getCurrentToken()) == SymbolType.RBRACE)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* If comma expect more */
|
|
|
|
else if(getSymbolType(getCurrentToken()) == SymbolType.COMMA)
|
|
|
|
{
|
|
|
|
nextToken();
|
|
|
|
/* TODO: If rbrace after then error, so save boolean */
|
|
|
|
}
|
|
|
|
/* TODO: Add else, could have exited on `;` which is invalid closing */
|
|
|
|
else
|
|
|
|
{
|
|
|
|
expect("Function call closed on ;, invalid");
|
|
|
|
}
|
|
|
|
}
|
2021-03-29 12:35:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-03-03 16:34:38 +00:00
|
|
|
nextToken();
|
|
|
|
|
2021-03-05 10:35:58 +00:00
|
|
|
gprintln("parseFuncCall(): Leave", DebugType.WARNING);
|
2021-03-29 12:35:52 +00:00
|
|
|
|
|
|
|
return new FunctionCall(functionName, arguments);
|
2021-03-03 16:34:38 +00:00
|
|
|
}
|
|
|
|
|
2021-03-03 14:30:56 +00:00
|
|
|
/* Almost like parseBody but has more */
|
2021-03-05 09:27:58 +00:00
|
|
|
/**
|
|
|
|
* TODO: For certain things like `parseClass` we should
|
|
|
|
* keep track of what level we are at as we shouldn't allow
|
|
|
|
* one to define classes within functions
|
|
|
|
*/
|
|
|
|
/* TODO: Variables should be allowed to have letters in them and underscores */
|
2021-03-29 19:06:37 +00:00
|
|
|
public Module parse()
|
2021-03-03 09:08:34 +00:00
|
|
|
{
|
2021-03-05 10:35:58 +00:00
|
|
|
gprintln("parse(): Enter", DebugType.WARNING);
|
|
|
|
|
2021-03-29 19:06:37 +00:00
|
|
|
Module modulle;
|
2021-03-21 19:43:01 +00:00
|
|
|
|
2021-03-21 11:10:18 +00:00
|
|
|
/* Expect `module` and module name and consume them (and `;`) */
|
|
|
|
expect(SymbolType.MODULE, getCurrentToken());
|
|
|
|
nextToken();
|
2021-03-21 19:43:01 +00:00
|
|
|
|
2021-03-28 19:54:38 +00:00
|
|
|
/* Module name may NOT be dotted (TODO: Maybe it should be yeah) */
|
|
|
|
expect(SymbolType.IDENT_TYPE, getCurrentToken());
|
2021-03-21 19:43:01 +00:00
|
|
|
string programName = getCurrentToken().getToken();
|
2021-03-21 11:10:18 +00:00
|
|
|
nextToken();
|
2021-03-21 19:43:01 +00:00
|
|
|
|
2021-03-21 11:10:18 +00:00
|
|
|
expect(SymbolType.SEMICOLON, getCurrentToken());
|
|
|
|
nextToken();
|
|
|
|
|
2021-03-29 19:06:37 +00:00
|
|
|
/* Initialize Module */
|
|
|
|
modulle = new Module(programName);
|
2021-03-21 19:43:01 +00:00
|
|
|
|
2021-03-21 11:10:18 +00:00
|
|
|
/* TODO: do `hasTokens()` check */
|
|
|
|
/* TODO: We should add `hasTokens()` to the `nextToken()` */
|
|
|
|
/* TODO: And too the `getCurrentTokem()` and throw an error when we have ran out rather */
|
|
|
|
|
2021-03-03 12:04:26 +00:00
|
|
|
/* We can have an import or vardef or funcdef */
|
2021-03-03 12:42:57 +00:00
|
|
|
while (hasTokens())
|
2021-03-03 12:04:26 +00:00
|
|
|
{
|
|
|
|
/* Get the token */
|
|
|
|
Token tok = getCurrentToken();
|
|
|
|
SymbolType symbol = getSymbolType(tok);
|
|
|
|
|
2021-03-05 10:35:58 +00:00
|
|
|
gprintln("parse(): Token: " ~ tok.getToken());
|
2021-03-03 17:53:47 +00:00
|
|
|
|
2021-03-03 12:04:26 +00:00
|
|
|
/* If it is a type */
|
2021-03-28 19:54:38 +00:00
|
|
|
if (symbol == SymbolType.IDENT_TYPE)
|
2021-03-03 12:42:57 +00:00
|
|
|
{
|
2021-04-01 13:59:33 +00:00
|
|
|
/* Might be a function, might be a variable, or assignment */
|
2021-06-07 15:08:21 +00:00
|
|
|
Statement statement = parseName();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* If it is an Entity then mark it as static
|
|
|
|
* as all Entities at module-level are static
|
|
|
|
*/
|
|
|
|
if(cast(Entity)statement)
|
|
|
|
{
|
|
|
|
Entity entity = cast(Entity)statement;
|
|
|
|
entity.setModifierType(InitScope.STATIC);
|
|
|
|
}
|
|
|
|
|
|
|
|
modulle.addStatement(statement);
|
2021-03-03 12:42:57 +00:00
|
|
|
}
|
2021-03-21 11:32:51 +00:00
|
|
|
/* If it is an accessor */
|
|
|
|
else if (isAccessor(tok))
|
|
|
|
{
|
2021-06-07 15:08:21 +00:00
|
|
|
Entity entity = parseAccessor();
|
|
|
|
|
|
|
|
/* Everything at the Module level is static */
|
|
|
|
entity.setModifierType(InitScope.STATIC);
|
2021-03-21 20:01:54 +00:00
|
|
|
|
|
|
|
/* TODO: Tets case has classes which null statement, will crash */
|
2021-06-07 15:08:21 +00:00
|
|
|
modulle.addStatement(entity);
|
2021-03-21 11:32:51 +00:00
|
|
|
}
|
2021-03-05 09:21:06 +00:00
|
|
|
/* If it is a class */
|
2021-03-05 10:35:58 +00:00
|
|
|
else if (symbol == SymbolType.CLASS)
|
2021-03-05 09:21:06 +00:00
|
|
|
{
|
2021-03-21 20:51:42 +00:00
|
|
|
Clazz clazz = parseClass();
|
|
|
|
|
2021-06-07 15:08:21 +00:00
|
|
|
/* Everything at the Module level is static */
|
|
|
|
clazz.setModifierType(InitScope.STATIC);
|
|
|
|
|
2021-03-21 20:51:42 +00:00
|
|
|
/* Add the class definition to the program */
|
2021-03-29 19:06:37 +00:00
|
|
|
modulle.addStatement(clazz);
|
2021-03-05 09:21:06 +00:00
|
|
|
}
|
2021-05-04 17:13:20 +00:00
|
|
|
/* If it is a struct definition */
|
|
|
|
else if(symbol == SymbolType.STRUCT)
|
|
|
|
{
|
|
|
|
Struct ztruct = parseStruct();
|
|
|
|
|
2021-06-07 15:08:21 +00:00
|
|
|
/* Everything at the Module level is static */
|
|
|
|
ztruct.setModifierType(InitScope.STATIC);
|
|
|
|
|
2021-05-04 17:13:20 +00:00
|
|
|
/* Add the struct definition to the program */
|
|
|
|
modulle.addStatement(ztruct);
|
|
|
|
}
|
2021-03-03 12:42:57 +00:00
|
|
|
else
|
2021-03-03 12:04:26 +00:00
|
|
|
{
|
2021-03-05 10:35:58 +00:00
|
|
|
expect("parse(): Unknown '" ~ tok.getToken() ~ "'");
|
2021-03-03 12:04:26 +00:00
|
|
|
}
|
|
|
|
}
|
2021-03-05 10:35:58 +00:00
|
|
|
|
|
|
|
gprintln("parse(): Leave", DebugType.WARNING);
|
2021-03-21 19:43:01 +00:00
|
|
|
|
2021-03-29 20:20:47 +00:00
|
|
|
/* Parent each Statement to the container (the module) */
|
|
|
|
parentToContainer(modulle, modulle.getStatements());
|
|
|
|
|
2021-03-29 19:06:37 +00:00
|
|
|
return modulle;
|
2021-03-03 09:08:34 +00:00
|
|
|
}
|
2021-03-03 11:30:56 +00:00
|
|
|
}
|
|
|
|
|
2023-01-11 08:43:29 +00:00
|
|
|
/**
|
|
|
|
* Basic Module test case
|
|
|
|
*/
|
2021-06-08 08:52:22 +00:00
|
|
|
unittest
|
|
|
|
{
|
|
|
|
|
|
|
|
import std.file;
|
|
|
|
import std.stdio;
|
|
|
|
import compiler.lexer;
|
|
|
|
|
2023-01-11 08:43:29 +00:00
|
|
|
string sourceCode = `
|
|
|
|
module myModule;
|
|
|
|
`;
|
2021-06-08 09:07:30 +00:00
|
|
|
|
|
|
|
Lexer currentLexer = new Lexer(sourceCode);
|
|
|
|
assert(currentLexer.performLex());
|
|
|
|
|
|
|
|
|
|
|
|
Parser parser = new Parser(currentLexer.getTokens());
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
Module modulle = parser.parse();
|
2021-03-21 12:23:18 +00:00
|
|
|
|
2021-06-08 09:07:30 +00:00
|
|
|
assert(cmp(modulle.getName(), "myModule")==0);
|
|
|
|
}
|
|
|
|
catch(TError)
|
|
|
|
{
|
|
|
|
assert(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Naming test for Entity recognition
|
|
|
|
*/
|
|
|
|
unittest
|
|
|
|
{
|
|
|
|
import std.file;
|
|
|
|
import std.stdio;
|
|
|
|
import compiler.lexer;
|
|
|
|
import compiler.typecheck.core;
|
|
|
|
|
2023-01-11 08:43:29 +00:00
|
|
|
string sourceCode = `
|
|
|
|
module myModule;
|
|
|
|
|
|
|
|
class myClass1
|
|
|
|
{
|
|
|
|
class myClass1_1
|
|
|
|
{
|
|
|
|
int entity;
|
|
|
|
}
|
2021-06-08 09:07:30 +00:00
|
|
|
|
2023-01-11 08:43:29 +00:00
|
|
|
class myClass2
|
|
|
|
{
|
|
|
|
int inner;
|
|
|
|
}
|
|
|
|
}
|
2021-06-08 09:07:30 +00:00
|
|
|
|
2023-01-11 08:43:29 +00:00
|
|
|
class myClass2
|
|
|
|
{
|
|
|
|
int outer;
|
2023-01-11 09:23:38 +00:00
|
|
|
}
|
2023-01-11 08:43:29 +00:00
|
|
|
`;
|
2021-06-08 09:07:30 +00:00
|
|
|
|
|
|
|
Lexer currentLexer = new Lexer(sourceCode);
|
|
|
|
assert(currentLexer.performLex());
|
|
|
|
|
|
|
|
|
|
|
|
Parser parser = new Parser(currentLexer.getTokens());
|
2021-03-21 12:23:18 +00:00
|
|
|
|
2021-06-08 09:07:30 +00:00
|
|
|
try
|
|
|
|
{
|
|
|
|
Module modulle = parser.parse();
|
|
|
|
|
|
|
|
/* Module name must be myModule */
|
|
|
|
assert(cmp(modulle.getName(), "myModule")==0);
|
|
|
|
TypeChecker tc = new TypeChecker(modulle);
|
|
|
|
|
2021-06-08 10:49:25 +00:00
|
|
|
/**
|
|
|
|
* There should exist two module-level classes
|
|
|
|
*
|
|
|
|
* 1. Attempt resolving the two without a full-path (relative to module)
|
|
|
|
* 2. Attempt resolving the two with a full-path
|
|
|
|
*/
|
|
|
|
|
2021-06-08 09:07:30 +00:00
|
|
|
/* There should exist two Module-level classes named `myClass1` and `myClass2` */
|
2021-06-08 10:49:25 +00:00
|
|
|
Entity entity1_rel = tc.getResolver().resolveBest(modulle, "myClass1");
|
|
|
|
Entity entity2_rel = tc.getResolver().resolveBest(modulle, "myClass2");
|
|
|
|
assert(entity1_rel);
|
|
|
|
assert(entity2_rel);
|
|
|
|
|
|
|
|
/* Resolve using full-path instead */
|
|
|
|
Entity entity1_fp = tc.getResolver().resolveBest(modulle, "myModule.myClass1");
|
|
|
|
Entity entity2_fp = tc.getResolver().resolveBest(modulle, "myModule.myClass2");
|
|
|
|
assert(entity1_fp);
|
|
|
|
assert(entity2_fp);
|
|
|
|
|
|
|
|
/* These should match respectively */
|
|
|
|
assert(entity1_rel == entity1_fp);
|
|
|
|
assert(entity2_rel == entity2_fp);
|
|
|
|
|
|
|
|
/* These should all be classes */
|
|
|
|
Clazz clazz1 = cast(Clazz)entity1_fp;
|
|
|
|
Clazz clazz2 = cast(Clazz)entity2_fp;
|
2021-06-08 09:07:30 +00:00
|
|
|
assert(clazz1);
|
2021-06-08 10:49:25 +00:00
|
|
|
assert(clazz1);
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-06-08 09:07:30 +00:00
|
|
|
|
|
|
|
/**
|
2021-06-08 10:49:25 +00:00
|
|
|
* Resolve members of myClass1
|
2021-06-08 09:07:30 +00:00
|
|
|
*
|
2021-06-08 10:49:25 +00:00
|
|
|
* 1. Resolve full-path
|
|
|
|
* 2. Resolve relative to myClass1
|
|
|
|
* 3. Resolve relative to module (incorrect)
|
|
|
|
* 4. Resolve relative to module (correct)
|
|
|
|
* 5. Resolve relative to myClass2 (resolves upwards)
|
2021-06-08 09:07:30 +00:00
|
|
|
*/
|
2021-06-08 10:49:25 +00:00
|
|
|
Entity myClass1_myClass2_1 = tc.getResolver().resolveBest(modulle, "myModule.myClass1.myClass2");
|
|
|
|
Entity myClass1_myClass2_2 = tc.getResolver().resolveBest(clazz1, "myClass2");
|
2021-06-08 10:51:23 +00:00
|
|
|
Entity myClass2 = tc.getResolver().resolveBest(modulle, "myClass2");
|
2021-06-08 10:54:29 +00:00
|
|
|
Entity myClass1_myClass2_4 = tc.getResolver().resolveBest(modulle, "myClass1.myClass2");
|
2021-06-08 10:49:25 +00:00
|
|
|
Entity myClass1_myClass2_5 = tc.getResolver().resolveBest(clazz2, "myClass1.myClass2");
|
2021-06-08 09:07:30 +00:00
|
|
|
|
2021-06-08 10:51:23 +00:00
|
|
|
/**
|
|
|
|
* All the above should exist
|
|
|
|
*/
|
|
|
|
assert(myClass1_myClass2_1);
|
|
|
|
assert(myClass1_myClass2_2);
|
|
|
|
assert(myClass2);
|
|
|
|
assert(myClass1_myClass2_4);
|
|
|
|
assert(myClass1_myClass2_5);
|
2021-06-08 10:52:27 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* They should all be classes
|
|
|
|
*/
|
|
|
|
Clazz c_myClass1_myClass2_1 = cast(Clazz)myClass1_myClass2_1;
|
|
|
|
Clazz c_myClass1_myClass2_2 = cast(Clazz)myClass1_myClass2_2;
|
|
|
|
Clazz c_myClass2 = cast(Clazz)myClass2;
|
|
|
|
Clazz c_myClass1_myClass2_4 = cast(Clazz)myClass1_myClass2_4;
|
|
|
|
Clazz c_myClass1_myClass2_5 = cast(Clazz)myClass1_myClass2_5;
|
2021-06-08 10:54:29 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* These should all be equal `myClass1.myClass2`
|
|
|
|
*/
|
|
|
|
assert(c_myClass1_myClass2_1 == c_myClass1_myClass2_2);
|
|
|
|
assert(c_myClass1_myClass2_2 == myClass1_myClass2_4);
|
|
|
|
assert(myClass1_myClass2_4 == myClass1_myClass2_5);
|
|
|
|
|
2021-06-08 10:57:32 +00:00
|
|
|
/**
|
|
|
|
* myClass1.myClass2 != myClass2
|
|
|
|
*
|
|
|
|
* myClass1.myClass2.inner should exist in myClass1.myClass2
|
|
|
|
* myClass2.outer should exist in myClass2
|
|
|
|
*
|
|
|
|
* Vice-versa of the above should not be true
|
2021-06-08 11:03:46 +00:00
|
|
|
*
|
|
|
|
* Both should be variables
|
2021-06-08 10:57:32 +00:00
|
|
|
*/
|
|
|
|
assert(myClass1_myClass2_5 != myClass2);
|
|
|
|
|
2021-06-08 10:57:58 +00:00
|
|
|
Entity innerVariable = tc.getResolver().resolveBest(c_myClass1_myClass2_5, "inner");
|
|
|
|
Entity outerVariable = tc.getResolver().resolveBest(c_myClass2, "outer");
|
2021-06-08 10:57:32 +00:00
|
|
|
assert(innerVariable !is null);
|
|
|
|
assert(outerVariable !is null);
|
2021-06-08 11:03:46 +00:00
|
|
|
assert(cast(Variable)innerVariable);
|
|
|
|
assert(cast(Variable)outerVariable);
|
|
|
|
|
2021-06-08 10:57:58 +00:00
|
|
|
|
2021-06-08 10:58:36 +00:00
|
|
|
innerVariable = tc.getResolver().resolveBest(c_myClass2, "inner");
|
|
|
|
outerVariable = tc.getResolver().resolveBest(c_myClass1_myClass2_5, "outer");
|
|
|
|
assert(innerVariable is null);
|
2021-06-08 11:00:21 +00:00
|
|
|
assert(outerVariable is null);
|
2021-06-08 11:03:46 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* myClass1_1.entity should exist
|
|
|
|
*
|
|
|
|
* 1. Resolve from myClass1.myClass2 relative position
|
|
|
|
*/
|
|
|
|
Entity variableEntity = tc.getResolver().resolveBest(c_myClass1_myClass2_5, "myClass1_1.entity");
|
|
|
|
assert(variableEntity);
|
|
|
|
|
|
|
|
/* Should be a variable */
|
|
|
|
assert(cast(Variable)variableEntity);
|
2021-06-08 09:07:30 +00:00
|
|
|
}
|
|
|
|
catch(TError)
|
|
|
|
{
|
|
|
|
assert(false);
|
|
|
|
}
|
2023-01-11 08:43:29 +00:00
|
|
|
}
|
|
|
|
|
2023-01-13 08:49:47 +00:00
|
|
|
/**
|
|
|
|
* Discard statement test case
|
|
|
|
*/
|
|
|
|
unittest
|
|
|
|
{
|
|
|
|
import std.stdio;
|
|
|
|
import compiler.lexer;
|
|
|
|
import compiler.typecheck.core;
|
|
|
|
|
|
|
|
|
|
|
|
string sourceCode = `
|
|
|
|
module parser_discard;
|
|
|
|
|
|
|
|
void function()
|
|
|
|
{
|
|
|
|
discard function();
|
|
|
|
}
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
|
|
Lexer currentLexer = new Lexer(sourceCode);
|
|
|
|
assert(currentLexer.performLex());
|
|
|
|
|
|
|
|
|
|
|
|
Parser parser = new Parser(currentLexer.getTokens());
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
Module modulle = parser.parse();
|
|
|
|
|
|
|
|
/* Module name must be parser_discard */
|
|
|
|
assert(cmp(modulle.getName(), "parser_discard")==0);
|
|
|
|
TypeChecker tc = new TypeChecker(modulle);
|
|
|
|
|
|
|
|
|
|
|
|
/* Find the function named `function` */
|
|
|
|
Entity func = tc.getResolver().resolveBest(modulle, "function");
|
|
|
|
assert(func);
|
|
|
|
assert(cast(Function)func); // Ensure it is a Funciton
|
|
|
|
|
|
|
|
/* Get the function's body */
|
|
|
|
Container funcContainer = cast(Container)func;
|
|
|
|
assert(funcContainer);
|
|
|
|
Statement[] functionStatements = funcContainer.getStatements();
|
|
|
|
assert(functionStatements.length == 1);
|
|
|
|
|
|
|
|
/* First statement should be a discard */
|
|
|
|
DiscardStatement discard = cast(DiscardStatement)functionStatements[0];
|
|
|
|
assert(discard);
|
|
|
|
|
|
|
|
/* The statement being discarded should be a function call */
|
|
|
|
FunctionCall functionCall = cast(FunctionCall)discard.getExpression();
|
|
|
|
assert(functionCall);
|
|
|
|
}
|
|
|
|
catch(TError e)
|
|
|
|
{
|
|
|
|
assert(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-11 08:43:29 +00:00
|
|
|
/**
|
|
|
|
* Function definition test case
|
|
|
|
*/
|
|
|
|
unittest
|
|
|
|
{
|
|
|
|
import std.stdio;
|
|
|
|
import compiler.lexer;
|
|
|
|
import compiler.typecheck.core;
|
|
|
|
|
|
|
|
|
|
|
|
string sourceCode = `
|
|
|
|
module parser_function_def;
|
|
|
|
|
|
|
|
int myFunction(int i, int j)
|
|
|
|
{
|
|
|
|
int k = i + j;
|
|
|
|
|
|
|
|
return k+1;
|
|
|
|
}
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
|
|
Lexer currentLexer = new Lexer(sourceCode);
|
|
|
|
assert(currentLexer.performLex());
|
|
|
|
|
|
|
|
|
|
|
|
Parser parser = new Parser(currentLexer.getTokens());
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
Module modulle = parser.parse();
|
|
|
|
|
2023-01-13 08:49:47 +00:00
|
|
|
/* Module name must be parser_function_def */
|
2023-01-11 08:43:29 +00:00
|
|
|
assert(cmp(modulle.getName(), "parser_function_def")==0);
|
|
|
|
TypeChecker tc = new TypeChecker(modulle);
|
|
|
|
|
|
|
|
|
|
|
|
/* Find the function named `myFunction` */
|
|
|
|
Entity func = tc.getResolver().resolveBest(modulle, "myFunction");
|
|
|
|
assert(func);
|
|
|
|
assert(cast(Function)func); // Ensure it is a Funciton
|
|
|
|
|
|
|
|
/* Get the function's body */
|
|
|
|
Container funcContainer = cast(Container)func;
|
|
|
|
assert(funcContainer);
|
|
|
|
Statement[] functionStatements = funcContainer.getStatements();
|
|
|
|
|
|
|
|
// Two parameters, 1 local and a return
|
|
|
|
assert(functionStatements.length == 4);
|
|
|
|
|
|
|
|
/* First statement should be a variable (param) */
|
|
|
|
VariableParameter varPar1 = cast(VariableParameter)functionStatements[0];
|
|
|
|
assert(varPar1);
|
|
|
|
assert(cmp(varPar1.getName(), "i") == 0);
|
|
|
|
|
|
|
|
/* Second statement should be a variable (param) */
|
|
|
|
VariableParameter varPar2 = cast(VariableParameter)functionStatements[1];
|
|
|
|
assert(varPar2);
|
|
|
|
assert(cmp(varPar2.getName(), "j") == 0);
|
|
|
|
|
|
|
|
/* ThirdFirst statement should be a variable (local) */
|
|
|
|
Variable varLocal = cast(Variable)functionStatements[2];
|
|
|
|
assert(varLocal);
|
|
|
|
assert(cmp(varLocal.getName(), "k") == 0);
|
|
|
|
|
|
|
|
/* Last statement should be a return */
|
|
|
|
assert(cast(ReturnStmt)functionStatements[3]);
|
|
|
|
}
|
|
|
|
catch(TError e)
|
|
|
|
{
|
|
|
|
assert(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* While loop test case (nested)
|
|
|
|
*/
|
|
|
|
unittest
|
|
|
|
{
|
|
|
|
import std.stdio;
|
|
|
|
import compiler.lexer;
|
|
|
|
import compiler.typecheck.core;
|
|
|
|
|
|
|
|
|
|
|
|
string sourceCode = `
|
|
|
|
module parser_while;
|
|
|
|
|
|
|
|
void function()
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
while(i)
|
|
|
|
{
|
|
|
|
int p = i;
|
|
|
|
|
|
|
|
while(i)
|
|
|
|
{
|
|
|
|
int f;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
|
|
Lexer currentLexer = new Lexer(sourceCode);
|
|
|
|
assert(currentLexer.performLex());
|
|
|
|
|
|
|
|
|
|
|
|
Parser parser = new Parser(currentLexer.getTokens());
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
Module modulle = parser.parse();
|
|
|
|
|
|
|
|
/* Module name must be parser_while */
|
|
|
|
assert(cmp(modulle.getName(), "parser_while")==0);
|
|
|
|
TypeChecker tc = new TypeChecker(modulle);
|
|
|
|
|
|
|
|
|
|
|
|
/* Find the function named `function` */
|
|
|
|
Entity func = tc.getResolver().resolveBest(modulle, "function");
|
|
|
|
assert(func);
|
|
|
|
assert(cast(Function)func); // Ensure it is a Funciton
|
|
|
|
|
|
|
|
/* Get the function's body */
|
|
|
|
Container funcContainer = cast(Container)func;
|
|
|
|
assert(funcContainer);
|
|
|
|
Statement[] functionStatements = funcContainer.getStatements();
|
|
|
|
assert(functionStatements.length == 2);
|
|
|
|
|
|
|
|
/* Find the while loop within the function's body */
|
|
|
|
WhileLoop potentialWhileLoop;
|
|
|
|
foreach(Statement curStatement; functionStatements)
|
|
|
|
{
|
|
|
|
potentialWhileLoop = cast(WhileLoop)curStatement;
|
|
|
|
|
|
|
|
if(potentialWhileLoop)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This must pass if we found the while loop */
|
|
|
|
assert(potentialWhileLoop);
|
|
|
|
|
|
|
|
/* Grab the branch of the while loop */
|
|
|
|
Branch whileBranch = potentialWhileLoop.getBranch();
|
|
|
|
assert(whileBranch);
|
|
|
|
|
|
|
|
/* Ensure that we have one statement in this branch's body and that it is a Variable and next is a while loop */
|
|
|
|
Statement[] whileBranchStatements = whileBranch.getStatements();
|
|
|
|
assert(whileBranchStatements.length == 2);
|
|
|
|
assert(cast(Variable)whileBranchStatements[0]);
|
|
|
|
assert(cast(WhileLoop)whileBranchStatements[1]);
|
|
|
|
|
|
|
|
/* The inner while loop also has a similiar structure, only one variable */
|
|
|
|
WhileLoop innerLoop = cast(WhileLoop)whileBranchStatements[1];
|
|
|
|
Branch innerWhileBranch = innerLoop.getBranch();
|
|
|
|
assert(innerWhileBranch);
|
|
|
|
Statement[] innerWhileBranchStatements = innerWhileBranch.getStatements();
|
|
|
|
assert(innerWhileBranchStatements.length == 1);
|
|
|
|
assert(cast(Variable)innerWhileBranchStatements[0]);
|
|
|
|
}
|
|
|
|
catch(TError e)
|
|
|
|
{
|
|
|
|
assert(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2023-01-12 08:53:48 +00:00
|
|
|
*
|
2023-01-11 08:43:29 +00:00
|
|
|
*/
|
2023-01-12 08:53:48 +00:00
|
|
|
unittest
|
|
|
|
{
|
|
|
|
import std.stdio;
|
|
|
|
import compiler.lexer;
|
|
|
|
import compiler.typecheck.core;
|
|
|
|
|
|
|
|
string sourceCode = `
|
|
|
|
module simple_pointer;
|
|
|
|
|
|
|
|
int j;
|
|
|
|
|
2023-01-12 09:17:32 +00:00
|
|
|
void myFunc(int* ptr, int** ptrPtr, int*** ptrPtrPtr)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
int** funcPtr()
|
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2023-01-12 08:53:48 +00:00
|
|
|
int function(int* ptr)
|
|
|
|
{
|
|
|
|
*ptr = 2+2;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int thing()
|
|
|
|
{
|
|
|
|
int discardExpr = function(&j);
|
|
|
|
int** l;
|
|
|
|
}
|
|
|
|
`;
|
|
|
|
Lexer currentLexer = new Lexer(sourceCode);
|
|
|
|
assert(currentLexer.performLex());
|
|
|
|
|
|
|
|
|
|
|
|
Parser parser = new Parser(currentLexer.getTokens());
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
Module modulle = parser.parse();
|
|
|
|
|
2023-01-13 08:49:47 +00:00
|
|
|
/* Module name must be simple_pointer */
|
2023-01-12 08:53:48 +00:00
|
|
|
assert(cmp(modulle.getName(), "simple_pointer")==0);
|
|
|
|
TypeChecker tc = new TypeChecker(modulle);
|
|
|
|
|
|
|
|
/* Find the function named `function` */
|
|
|
|
Entity funcFunction = tc.getResolver().resolveBest(modulle, "function");
|
|
|
|
assert(funcFunction);
|
|
|
|
assert(cast(Function)funcFunction); // Ensure it is a Function
|
|
|
|
|
|
|
|
/* Find the function named `thing` */
|
|
|
|
Entity funcThing = tc.getResolver().resolveBest(modulle, "thing");
|
|
|
|
assert(funcThing);
|
|
|
|
assert(cast(Function)funcThing); // Ensure it is a Function
|
|
|
|
|
|
|
|
/* Find the variable named `j` */
|
|
|
|
Entity variableJ = tc.getResolver().resolveBest(modulle, "j");
|
|
|
|
assert(variableJ);
|
|
|
|
assert(cast(Variable)variableJ);
|
|
|
|
|
|
|
|
|
|
|
|
/* Get the `function`'s body */
|
|
|
|
Container funcFunctionContainer = cast(Container)funcFunction;
|
|
|
|
assert(funcFunctionContainer);
|
|
|
|
Statement[] funcFunctionStatements = funcFunctionContainer.getStatements();
|
|
|
|
assert(funcFunctionStatements.length == 3); // Remember this includes the parameters
|
|
|
|
|
|
|
|
/* Get the `thing`'s body */
|
|
|
|
Container funcThingContainer = cast(Container)funcThing;
|
|
|
|
assert(funcThingContainer);
|
|
|
|
Statement[] funcThingStatements = funcThingContainer.getStatements();
|
|
|
|
assert(funcThingStatements.length == 2);
|
|
|
|
|
|
|
|
// TODO: Finish this
|
|
|
|
// TODO: Add a check for the Statement types in the bodies, the arguments and the parameters
|
|
|
|
}
|
|
|
|
catch(TError e)
|
|
|
|
{
|
|
|
|
assert(false);
|
|
|
|
}
|
|
|
|
}
|
2023-01-11 08:43:29 +00:00
|
|
|
|
|
|
|
/**
|
2023-01-12 08:53:48 +00:00
|
|
|
* Do-while loop tests (TODO: Add this)
|
2023-01-11 08:43:29 +00:00
|
|
|
*/
|
2023-01-12 08:53:48 +00:00
|
|
|
|
2023-01-11 08:43:29 +00:00
|
|
|
/**
|
2023-01-12 08:53:48 +00:00
|
|
|
* For loop tests (TODO: FInish this)
|
2023-01-11 08:43:29 +00:00
|
|
|
*/
|
|
|
|
unittest
|
|
|
|
{
|
|
|
|
import std.stdio;
|
|
|
|
import compiler.lexer;
|
|
|
|
import compiler.typecheck.core;
|
|
|
|
|
|
|
|
|
|
|
|
string sourceCode = `
|
|
|
|
module parser_for;
|
|
|
|
|
|
|
|
void function()
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
for(int idx = i; idx < i; idx=idx+1)
|
|
|
|
{
|
|
|
|
i = i + 1;
|
|
|
|
|
|
|
|
for(int idxInner = idx; idxInner < idx; idxInner = idxInner +1)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
|
|
Lexer currentLexer = new Lexer(sourceCode);
|
|
|
|
assert(currentLexer.performLex());
|
|
|
|
|
|
|
|
|
|
|
|
Parser parser = new Parser(currentLexer.getTokens());
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
Module modulle = parser.parse();
|
|
|
|
|
2023-01-13 08:49:47 +00:00
|
|
|
/* Module name must be parser_for */
|
2023-01-11 08:43:29 +00:00
|
|
|
assert(cmp(modulle.getName(), "parser_for")==0);
|
|
|
|
TypeChecker tc = new TypeChecker(modulle);
|
|
|
|
|
|
|
|
|
|
|
|
/* Find the function named `function` */
|
|
|
|
Entity func = tc.getResolver().resolveBest(modulle, "function");
|
|
|
|
assert(func);
|
|
|
|
assert(cast(Function)func); // Ensure it is a Funciton
|
|
|
|
|
|
|
|
/* Get the function's body */
|
|
|
|
Container funcContainer = cast(Container)func;
|
|
|
|
assert(funcContainer);
|
|
|
|
Statement[] functionStatements = funcContainer.getStatements();
|
|
|
|
assert(functionStatements.length == 2);
|
|
|
|
|
|
|
|
/* First statement should be a variable declaration */
|
|
|
|
assert(cast(Variable)functionStatements[0]);
|
|
|
|
|
|
|
|
/* Next statement should be a for loop */
|
|
|
|
ForLoop outerLoop = cast(ForLoop)functionStatements[1];
|
|
|
|
assert(outerLoop);
|
|
|
|
|
|
|
|
/* Get the body of the for-loop which should be [preRun, Branch] */
|
|
|
|
Statement[] outerLoopBody = outerLoop.getStatements();
|
|
|
|
assert(outerLoopBody.length == 2);
|
|
|
|
|
|
|
|
/* We should have a preRun Statement */
|
|
|
|
assert(outerLoop.hasPreRunStatement());
|
|
|
|
|
|
|
|
/* The first should be the [preRun, ] which should be a Variable (declaration) */
|
|
|
|
Variable preRunVarDec = cast(Variable)(outerLoopBody[0]);
|
|
|
|
assert(preRunVarDec);
|
|
|
|
|
|
|
|
/* Next up is the branch */
|
|
|
|
Branch outerLoopBranch = cast(Branch)outerLoopBody[1];
|
|
|
|
assert(outerLoopBranch);
|
|
|
|
|
|
|
|
/* The branch should have a condition */
|
|
|
|
Expression outerLoopBranchCondition = outerLoopBranch.getCondition();
|
|
|
|
assert(outerLoopBranchCondition);
|
|
|
|
|
|
|
|
/* The branch should have a body made up of [varAssStdAlone, forLoop, postIteration] */
|
|
|
|
Statement[] outerLoopBranchBody = outerLoopBranch.getStatements();
|
|
|
|
assert(outerLoopBranchBody.length == 3);
|
|
|
|
|
|
|
|
/* Check for [varAssStdAlone, ] */
|
|
|
|
VariableAssignmentStdAlone outerLoopBranchBodyStmt1 = cast(VariableAssignmentStdAlone)outerLoopBranchBody[0];
|
|
|
|
assert(outerLoopBranchBodyStmt1);
|
|
|
|
|
|
|
|
/* Check for [, forLoop, ] */
|
|
|
|
ForLoop innerLoop = cast(ForLoop)outerLoopBranchBody[1];
|
|
|
|
assert(innerLoop);
|
|
|
|
|
|
|
|
/* Check for [, postIteration] */
|
|
|
|
VariableAssignmentStdAlone outerLoopBranchBodyStmt3 = cast(VariableAssignmentStdAlone)outerLoopBranchBody[2];
|
|
|
|
assert(outerLoopBranchBodyStmt3);
|
|
|
|
|
2023-01-12 08:53:48 +00:00
|
|
|
/* Start examining the inner for-loop */
|
|
|
|
Branch innerLoopBranch = innerLoop.getBranch();
|
|
|
|
assert(innerLoopBranch);
|
|
|
|
|
|
|
|
/* The branch should have a condition */
|
|
|
|
Expression innerLoopBranchCondition = innerLoopBranch.getCondition();
|
|
|
|
assert(innerLoopBranchCondition);
|
2023-01-11 08:43:29 +00:00
|
|
|
|
2023-01-12 08:53:48 +00:00
|
|
|
/* The branch should have a body made up of [postIteration] */
|
|
|
|
Statement[] innerLoopBranchBody = innerLoopBranch.getStatements();
|
|
|
|
assert(innerLoopBranchBody.length == 1);
|
2023-01-11 08:43:29 +00:00
|
|
|
}
|
|
|
|
catch(TError e)
|
|
|
|
{
|
|
|
|
assert(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* If statement tests
|
|
|
|
*/
|
|
|
|
unittest
|
|
|
|
{
|
|
|
|
import std.stdio;
|
|
|
|
import compiler.lexer;
|
|
|
|
import compiler.typecheck.core;
|
|
|
|
|
|
|
|
|
|
|
|
string sourceCode = `
|
|
|
|
module parser_if;
|
|
|
|
|
|
|
|
void function()
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
if(i)
|
|
|
|
{
|
|
|
|
int p = -i;
|
|
|
|
}
|
|
|
|
else if(i)
|
|
|
|
{
|
|
|
|
int p = 3+(i*9);
|
|
|
|
}
|
|
|
|
else if(i)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
|
|
Lexer currentLexer = new Lexer(sourceCode);
|
|
|
|
assert(currentLexer.performLex());
|
|
|
|
|
|
|
|
|
|
|
|
Parser parser = new Parser(currentLexer.getTokens());
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
Module modulle = parser.parse();
|
|
|
|
|
2023-01-13 08:49:47 +00:00
|
|
|
/* Module name must be parser_if */
|
2023-01-11 08:43:29 +00:00
|
|
|
assert(cmp(modulle.getName(), "parser_if")==0);
|
|
|
|
TypeChecker tc = new TypeChecker(modulle);
|
|
|
|
|
|
|
|
/* Find the function named `function` */
|
|
|
|
Entity func = tc.getResolver().resolveBest(modulle, "function");
|
|
|
|
assert(func);
|
|
|
|
assert(cast(Function)func); // Ensure it is a Funciton
|
|
|
|
|
|
|
|
/* Get the function's body */
|
|
|
|
Container funcContainer = cast(Container)func;
|
|
|
|
assert(funcContainer);
|
|
|
|
Statement[] functionStatements = funcContainer.getStatements();
|
|
|
|
assert(functionStatements.length == 2);
|
|
|
|
|
|
|
|
/* Second statement is an if statemnet */
|
|
|
|
IfStatement ifStatement = cast(IfStatement)functionStatements[1];
|
|
|
|
assert(ifStatement);
|
|
|
|
|
|
|
|
/* Extract the branches (should be 4) */
|
|
|
|
Branch[] ifStatementBranches = ifStatement.getBranches();
|
|
|
|
assert(ifStatementBranches.length == 4);
|
|
|
|
|
|
|
|
/* First branch should have one statement which is a variable declaration */
|
|
|
|
Statement[] firstBranchBody = ifStatementBranches[0].getStatements();
|
|
|
|
assert(firstBranchBody.length == 1);
|
|
|
|
assert(cast(Variable)firstBranchBody[0]);
|
|
|
|
|
|
|
|
/* Second branch should have one statement which is a variable declaration */
|
|
|
|
Statement[] secondBranchBody = ifStatementBranches[1].getStatements();
|
|
|
|
assert(secondBranchBody.length == 1);
|
|
|
|
assert(cast(Variable)secondBranchBody[0]);
|
|
|
|
|
|
|
|
/* Third branch should have no statements */
|
|
|
|
Statement[] thirdBranchBody = ifStatementBranches[2].getStatements();
|
|
|
|
assert(thirdBranchBody.length == 0);
|
|
|
|
|
|
|
|
/* Forth branch should have no statements */
|
|
|
|
Statement[] fourthBranchBody = ifStatementBranches[3].getStatements();
|
|
|
|
assert(fourthBranchBody.length == 0);
|
|
|
|
|
|
|
|
// TODO: @Tristan Add this
|
|
|
|
}
|
|
|
|
catch(TError e)
|
|
|
|
{
|
|
|
|
assert(false);
|
|
|
|
}
|
2021-06-08 08:52:22 +00:00
|
|
|
}
|