tlang/source/tlang/compiler/parsing/core.d

1812 lines
55 KiB
D
Raw Normal View History

module compiler.parsing.core;
2021-03-03 09:08:34 +00:00
import gogga;
import std.conv : to;
import std.string : isNumeric, cmp;
2021-03-30 16:35:16 +01:00
import compiler.symbols.check;
import compiler.symbols.data;
2021-03-03 11:30:56 +00:00
import compiler.lexer : Token;
import core.stdc.stdlib;
2021-03-17 08:05:56 +00:00
import misc.exceptions : TError;
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
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
*/
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);
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
{
throw new SyntaxError(this, symbol, actualType);
// 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
*/
public static void expect(string message)
{
2021-03-17 08:33:22 +00:00
//throw new TError(message);
gprintln(message, DebugType.ERROR);
if(isUnitTest)
{
throw new TError(message);
assert(false);
}
else
{
throw new TError(message);
//exit(0); /* TODO: Exit code */ /* TODO: Version that returns or asserts for unit tests */
}
}
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
}
private bool hasTokens()
{
return tokenPtr < tokens.length;
}
2021-03-03 11:30:56 +00:00
private Token getCurrentToken()
{
/* TODO: Throw an exception here when we try get more than we can */
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
*/
2021-03-03 15:09:08 +00:00
private void parseIf()
{
gprintln("parseIf(): Enter", DebugType.WARNING);
while (hasTokens())
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)
{
/* Pop off the `if` */
nextToken();
2021-03-03 15:09:08 +00:00
/* Expect an opening brace `(` */
expect(SymbolType.LBRACE, getCurrentToken());
nextToken();
2021-03-03 15:35:48 +00:00
/* Parse an expression (for the condition) */
parseExpression();
expect(SymbolType.RBRACE, getCurrentToken());
2021-03-03 15:35:48 +00:00
/* Opening { */
nextToken();
expect(SymbolType.OCURLY, getCurrentToken());
/* Parse the if' statement's body AND expect a closing curly */
parseBody();
expect(SymbolType.CCURLY, getCurrentToken());
nextToken();
}
2021-03-20 16:36:25 +00:00
/* If we get an else as the next symbol */
else if (getSymbolType(getCurrentToken()) == SymbolType.ELSE)
{
/* Pop off the `else` */
nextToken();
/* Check if we have an `if` after the `{` (so an "else if" statement) */
if (getSymbolType(getCurrentToken()) == SymbolType.IF)
{
/* Pop off the `if` */
nextToken();
/* Expect an opening brace `(` */
expect(SymbolType.LBRACE, getCurrentToken());
nextToken();
/* Parse an expression (for the condition) */
parseExpression();
expect(SymbolType.RBRACE, getCurrentToken());
/* Opening { */
nextToken();
expect(SymbolType.OCURLY, getCurrentToken());
/* Parse the if' statement's body AND expect a closing curly */
parseBody();
expect(SymbolType.CCURLY, getCurrentToken());
nextToken();
}
/* Check for opening curly (just an "else" statement) */
else if (getSymbolType(getCurrentToken()) == SymbolType.OCURLY)
{
2021-03-20 16:31:03 +00:00
/* Parse the if' statement's body (starting with `{` AND expect a closing curly */
parseBody();
expect(SymbolType.CCURLY, getCurrentToken());
nextToken();
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-20 16:50:48 +00:00
/* Error out if no `{` or `if` */
else
{
2021-03-20 16:36:25 +00:00
expect("Expected either if (for else if) or { for (else)");
}
}
/* If we get anything else, then we are done with if statement */
else
{
break;
}
}
2021-03-03 15:35:48 +00:00
gprintln("parseIf(): Leave", DebugType.WARNING);
2021-03-03 15:09:08 +00:00
}
2021-03-03 18:50:06 +00:00
private void parseWhile()
{
gprintln("parseWhile(): Enter", DebugType.WARNING);
/* 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) */
parseExpression();
expect(SymbolType.RBRACE, getCurrentToken());
/* Openening { */
nextToken();
expect(SymbolType.OCURLY, getCurrentToken());
/* Parse the while' statement's body AND expect a closing curly */
parseBody();
expect(SymbolType.CCURLY, getCurrentToken());
nextToken();
gprintln("parseWhile(): Leave", DebugType.WARNING);
2021-03-03 18:50:06 +00:00
}
2021-10-27 14:52:43 +01:00
public VariableAssignmentStdAlone parseAssignment()
2021-04-01 14:49:02 +01:00
{
/* Generated Assignment statement */
2021-10-27 14:52:43 +01:00
VariableAssignmentStdAlone assignment;
2021-04-01 14:49:02 +01:00
/* The identifier being assigned to */
string identifier = getCurrentToken().getToken();
nextToken();
nextToken();
2021-04-01 14:59:33 +01:00
gprintln(getCurrentToken());
2021-04-01 14:49:02 +01:00
/* Expression */
Expression assignmentExpression = parseExpression();
2021-04-01 14:59:33 +01:00
2021-10-27 14:52:43 +01:00
assignment = new VariableAssignmentStdAlone(identifier, assignmentExpression);
2021-04-01 14:49:02 +01:00
/* TODO: Support for (a=1)? */
/* Expect a semicolon */
expect(SymbolType.SEMICOLON, getCurrentToken());
nextToken();
return assignment;
}
public Statement parseName()
{
Statement ret;
/* Save the name or type */
string nameTYpe = getCurrentToken().getToken();
/* 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 21:05:58 +01:00
/* Expect a semi-colon */
expect(SymbolType.SEMICOLON, getCurrentToken());
nextToken();
}
/* If we have an identifier/type then declaration */
else if(type == SymbolType.IDENT_TYPE)
{
previousToken();
ret = parseTypedDeclaration();
}
2021-04-01 14:49:02 +01:00
/* Assignment */
else if(type == SymbolType.ASSIGN)
{
previousToken();
ret = parseAssignment();
}
/* Any other case */
else
{
2021-04-01 14:59:33 +01:00
gprintln(getCurrentToken);
expect("Error expected ( for var/func def");
2021-04-01 14:59:33 +01:00
}
return ret;
}
2021-05-04 17:58:07 +01:00
/* TODO: Implement me, and call me */
private Struct parseStruct()
{
gprintln("parseStruct(): Enter", DebugType.WARNING);
Struct generatedStruct;
Statement[] statements;
2021-05-04 17:58:07 +01: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 */
expect(SymbolType.OCURLY, getCurrentToken());
nextToken();
while(true)
{
/* Get current token */
SymbolType symbolType = getSymbolType(getCurrentToken());
/* The possibly valid returned struct member (Entity) */
Statement structMember;
2021-05-31 16:51:16 +01: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()`
*/
/* 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();
}
/* If is is a modifier */
else if(isModifier(getCurrentToken()))
{
structMember = parseInitScope();
}
2021-05-31 17:06:09 +01:00
/* If closing brace then exit */
else if(symbolType == SymbolType.CCURLY)
{
break;
}
/* 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");
}
/* Append to struct's body */
statements ~= structMember;
2021-05-31 16:51:16 +01:00
2021-05-31 17:06:09 +01: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-06-01 09:00:21 +01:00
/* Generate a new Struct with the given body Statement(s) */
2021-05-31 21:36:11 +01:00
generatedStruct = new Struct(structName);
2021-06-01 09:00:21 +01:00
generatedStruct.addStatements(statements);
2021-05-31 21:36:11 +01:00
/* Expect closing brace (sanity) */
expect(SymbolType.CCURLY, getCurrentToken());
2021-05-04 17:58:07 +01:00
/* Consume the closing curly brace */
nextToken();
2021-05-04 17:58:07 +01:00
gprintln("parseStruct(): Leave", DebugType.WARNING);
return generatedStruct;
}
2021-03-21 15:57:56 +00:00
private Statement[] parseBody()
2021-03-03 14:30:56 +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
*/
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
2021-03-03 14:30:56 +00:00
/* If it is a type */
if (symbol == SymbolType.IDENT_TYPE)
2021-03-03 14:30:56 +00:00
{
2021-04-01 14:59:33 +01:00
/* Might be a function, might be a variable, or assignment */
statements ~= parseName();
2021-03-03 14:30:56 +00:00
}
/* If it is an accessor */
else if (isAccessor(tok))
{
statements ~= parseAccessor();
}
/* If it is a modifier */
else if(isModifier(tok))
{
statements ~= parseInitScope();
}
2021-03-03 15:09:08 +00:00
/* If it is a branch */
2021-03-05 10:35:58 +00:00
else if (symbol == SymbolType.IF)
2021-03-03 15:09:08 +00:00
{
parseIf();
}
2021-03-03 18:50:06 +00:00
/* If it is a while loop */
2021-03-05 10:35:58 +00:00
else if (symbol == SymbolType.WHILE)
2021-03-03 18:50:06 +00:00
{
parseWhile();
}
/* If it is a function call (further inspection needed) */
else if (symbol == SymbolType.IDENT_TYPE)
2021-03-03 16:34:38 +00:00
{
/* Function calls can have dotted identifiers */
2021-03-03 16:34:38 +00:00
parseFuncCall();
}
/* If it is closing the body `}` */
2021-03-05 10:35:58 +00:00
else if (symbol == SymbolType.CCURLY)
2021-03-03 14:30:56 +00:00
{
// gprintln("Error");
// nextToken();
gprintln("parseBody(): Exiting body by }", DebugType.WARNING);
2021-03-05 10:35:58 +00:00
closedBeforeExit = true;
2021-03-03 14:30:56 +00:00
break;
}
/* If it is a class definition */
2021-03-05 10:35:58 +00:00
else if (symbol == SymbolType.CLASS)
{
/* Parse the class and add its statements */
statements ~= parseClass();
}
/* If it is a struct definition */
else if(symbol == SymbolType.STRUCT)
{
/* Parse the struct and add it to the statements */
statements ~= parseStruct();
}
/* Error out */
else
{
expect("parseBody(): Unknown symbol: " ~ getCurrentToken()
.getToken());
}
2021-03-03 14:30:56 +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)
{
expect("Expected closing } but ran out of tokens");
}
gprintln("parseBody(): Leave", DebugType.WARNING);
2021-03-21 15:57:56 +00:00
return statements;
2021-03-03 14:30:56 +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 13:52:22 +01:00
private InitScope getInitScope(Token token)
{
if(getSymbolType(token) == SymbolType.STATIC)
{
return InitScope.STATIC;
}
else
{
return InitScope.UNKNOWN;
}
}
2021-06-04 13:47:39 +01: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 13:52:22 +01:00
/* Save and consume the init-scope */
InitScope initScope = getInitScope(getCurrentToken());
2021-06-04 13:47:39 +01:00
nextToken();
/* 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 13:47:39 +01:00
return entity;
}
2021-03-21 10:39:55 +00:00
/* STATUS: Not being used yet */
private Entity parseAccessor()
{
Entity entity;
/* Save and consume the accessor */
AccessorType accessorType = getAccessorType(getCurrentToken());
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)
{
/* 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");
}
}
/* If static */
else if(symbolType == SymbolType.STATIC)
{
entity = parseInitScope();
}
/* Error out */
else
{
expect("Expected either function definition, variable declaration, struct definition or class definition");
}
entity.setAccessorType(accessorType);
return entity;
}
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;
2021-03-22 09:18:56 +00:00
Variable[] args;
2021-03-21 15:57:56 +00:00
}
private funcDefPair parseFuncDef()
{
2021-03-05 10:35:58 +00:00
gprintln("parseFuncDef(): Enter", DebugType.WARNING);
2021-03-21 15:57:56 +00:00
Statement[] statements;
2021-03-22 09:18:56 +00:00
Variable[] argumentList;
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();
/* 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 */
if(getSymbolType(getCurrentToken()) == SymbolType.IDENT_TYPE)
2021-03-03 14:30:56 +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
/* Get the identifier (This CAN NOT be dotted) */
expect(SymbolType.IDENT_TYPE, getCurrentToken());
if(!isIdentifier_NoDot(getCurrentToken()))
{
expect("Identifier can not be path");
}
2021-03-21 08:02:23 +00:00
string identifier = getCurrentToken().getToken();
nextToken();
2021-03-22 09:18:56 +00:00
/* Add the local variable (parameter variable) */
argumentList ~= new Variable(type, identifier);
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 `,` */
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
{
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();
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;
bruh.args = argumentList;
2021-03-21 15:57:56 +00:00
return bruh;
}
2021-06-09 11:55:59 +01: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
*/
private Expression parseDiscard()
{
/* Consume the `discard` */
nextToken();
/* Parse the following expression */
Expression expression = parseExpression();
/* Expect a semi-colon */
expect(SymbolType.SEMICOLON, getCurrentToken());
nextToken();
return expression;
2021-06-09 11:55:59 +01:00
}
2021-06-09 16:44:21 +01:00
/**
* Parses the `new Class()` expression
*/
2021-06-09 11:55:59 +01:00
/**
* Parses an expression
*
* TODO:
*
* 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-05 10:35:58 +00:00
gprintln("parseExpression(): Enter", DebugType.WARNING);
/* The expression to be returned */
Expression[] retExpression;
2021-03-21 15:57:56 +00:00
void addRetExp(Expression e)
{
retExpression ~= e;
}
Expression removeExp()
{
Expression poppedExp = retExpression[retExpression.length-1];
retExpression.length--;
return poppedExp;
}
bool hasExp()
{
return retExpression.length != 0;
}
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
/* TODO: Implement expression parsing */
/**
* 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)
{
SymbolType symbol = getSymbolType(getCurrentToken());
2021-03-03 15:28:39 +00:00
gprintln(retExpression);
/* If it is a number literal */
if (symbol == SymbolType.NUMBER_LITERAL)
{
/* TODO: Do number checking here to get correct NUmberLiteral */
NumberLiteral numberLiteral = new NumberLiteral(getCurrentToken().getToken());
/* Add expression to stack */
addRetExp(numberLiteral);
/* Get the next token */
nextToken();
}
/* If it is a maths operator */
/* TODO: Handle all operators here (well most), just include bit operators */
else if (isMathOp(getCurrentToken()) || isBinaryOp(getCurrentToken()))
{
SymbolType operatorType = getSymbolType(getCurrentToken());
/* TODO: Save operator, also pass to constructor */
/* TODO: Parse expression or pass arithemetic (I think latter) */
nextToken();
OperatorExpression opExp;
/* Check if unary or not (if so no expressions on stack) */
if(!hasExp())
{
/* Only `*`, `+` and `-` are valid or `~` */
if(operatorType == SymbolType.STAR || operatorType == SymbolType.ADD || operatorType == SymbolType.SUB || operatorType == SymbolType.TILDE)
{
/* 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);
}
/* 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");
}
}
else
{
expect("Expected *, + or - as unary operators but got "~to!(string)(operatorType));
}
}
/* 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);
}
/* If it is a string literal */
else if (symbol == SymbolType.STRING_LITERAL)
{
/* Add the string to the stack */
addRetExp(new StringExpression(getCurrentToken().getToken()));
/* Get the next token */
nextToken();
}
/* If it is an identifier */
else if (symbol == SymbolType.IDENT_TYPE)
{
string identifier = getCurrentToken().getToken();
nextToken();
Expression toAdd;
/* If the symbol is `(` then function call */
if (getSymbolType(getCurrentToken()) == SymbolType.LBRACE)
{
/* TODO: Implement function call parsing */
previousToken();
toAdd = parseFuncCall();
}
else
{
/* TODO: Leave the token here */
/* TODO: Just leave it, yeah */
2021-03-28 21:05:58 +01:00
// expect("poes");
2021-03-29 20:06:37 +01:00
toAdd = new VariableExpression(identifier);
/**
* FIXME: To properly support function handles I think we are going to need a new type
* Well not here, this should technically be IdentExpression.
*/
}
/* TODO: Change this later, for now we doing this */
addRetExp(toAdd);
}
2021-03-17 20:25:07 +00:00
/* Detect if this expression is coming to an end, then return */
else if (symbol == SymbolType.SEMICOLON || symbol == SymbolType.RBRACE || symbol == SymbolType.COMMA)
{
break;
}
/**
2021-03-16 08:36:50 +00:00
* For ()
*/
else if (symbol == SymbolType.LBRACE)
{
2021-03-16 05:30:18 +00:00
/* Consume the `(` */
nextToken();
2021-03-16 05:30:18 +00:00
/* Parse the inner expression till terminator */
2021-03-29 13:48:47 +01:00
addRetExp(parseExpression());
2021-03-16 05:30:18 +00:00
/* Consume the terminator */
nextToken();
}
/**
* `new` operator
*/
else if(symbol == SymbolType.NEW)
{
/* Cosume the `new` */
nextToken();
/* Get the identifier */
string identifier = getCurrentToken().getToken();
nextToken();
2021-06-09 11:55:59 +01:00
NewExpression toAdd;
FunctionCall functionCallPart;
/* If the symbol is `(` then function call */
if (getSymbolType(getCurrentToken()) == SymbolType.LBRACE)
{
/* TODO: Implement function call parsing */
previousToken();
functionCallPart = parseFuncCall();
}
/* If not an `(` */
else
{
/* Raise a syntax error */
expect(SymbolType.LBRACE, getCurrentToken());
}
/* Create a NewExpression with the associated FunctionCall */
toAdd = new NewExpression(functionCallPart);
/* Add the expression */
addRetExp(toAdd);
}
/* 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);
addRetExp(binOp);
}
else
{
//gprintln("parseExpression(): NO MATCH", DebugType.ERROR);
/* TODO: Something isn't right here */
expect("Expected expression terminator ) or ;");
}
}
2021-03-29 13:48:47 +01:00
gprintln(retExpression);
2021-03-05 10:35:58 +00:00
gprintln("parseExpression(): Leave", DebugType.WARNING);
2021-03-21 15:57:56 +00:00
/* TODO: DO check here for retExp.length = 1 */
expressionStackSanityCheck();
2021-03-21 15:57:56 +00:00
return retExpression[0];
}
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;
/* Expect an identifier (CAN NOT be dotted) */
2021-03-03 12:42:57 +00:00
nextToken();
expect(SymbolType.IDENT_TYPE, getCurrentToken());
if(!isIdentifier_NoDot(getCurrentToken()))
{
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());
/* 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-21 15:57:56 +00:00
funcDefPair pair = parseFuncDef();
2021-03-05 10:35:58 +00:00
2021-03-21 15:57:56 +00:00
generated = new Function(identifier, type, pair.bodyStatements, pair.args);
import std.stdio;
writeln(to!(string)((cast(Function)generated).getVariables()));
}
/* Check for semi-colon (var dec) */
2021-03-05 10:35:58 +00:00
else if (symbolType == SymbolType.SEMICOLON)
{
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);
}
/* Check for `=` (var dec) */
2021-03-05 10:35:58 +00:00
else if (symbolType == SymbolType.ASSIGN)
{
nextToken();
/* Now parse an expression */
2021-03-21 15:57:56 +00:00
Expression expression = parseExpression();
VariableAssignment varAssign = new VariableAssignment(expression);
2021-03-03 15:28:39 +00:00
/**
* The symbol that returned us from `parseExpression` must
* be a semi-colon
*/
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
Variable variable = new Variable(type, identifier);
2021-03-21 15:57:56 +00:00
variable.addAssignment(varAssign);
varAssign.setVariable(variable);
2021-03-21 15:57:56 +00:00
generated = variable;
}
else
{
expect("Expected one of the following: (, ; or =");
}
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
}
/**
* Parses a class definition
*
* This is called when there is an occurrence of
* a token `class`
*/
private Clazz parseClass()
{
2021-03-05 10:35:58 +00:00
gprintln("parseClass(): Enter", DebugType.WARNING);
2021-03-21 15:57:56 +00:00
Clazz generated;
/* Pop off the `class` */
nextToken();
/* Get the class's name (CAN NOT be dotted) */
expect(SymbolType.IDENT_TYPE, getCurrentToken());
if(!isIdentifier_NoDot(getCurrentToken()))
{
expect("Class name in declaration cannot be path");
}
string className = getCurrentToken().getToken();
2021-03-05 10:35:58 +00:00
gprintln("parseClass(): Class name found '" ~ className ~ "'");
nextToken();
2021-03-21 15:57:56 +00:00
generated = new Clazz(className);
2021-03-30 19:05:16 +01:00
string[] inheritList;
/* TODO: If we have the inherit symbol `:` */
if(getSymbolType(getCurrentToken()) == SymbolType.INHERIT_OPP)
{
/* TODO: Loop until `}` */
/* Consume the inheritance operator `:` */
nextToken();
while(true)
{
/* Check if it is an identifier (may be dotted) */
expect(SymbolType.IDENT_TYPE, getCurrentToken());
2021-03-30 19:05:16 +01:00
inheritList ~= getCurrentToken().getToken();
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-06-07 15:55:48 +01: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 19:05:16 +01:00
/* Add inherit list */
generated.addInherit(inheritList);
2021-06-07 15:55:48 +01:00
// /* TODO: Technically we should be more specific, this does too much */
// /* Parse a body */
// Statement[] statements = parseBody();
generated.addStatements(statements);
/* Parent each Statement to the container */
parentToContainer(generated, statements);
2021-03-05 09:38:00 +00:00
/* Pop off the ending `}` */
nextToken();
2021-03-05 10:35:58 +00:00
gprintln("parseClass(): Leave", DebugType.WARNING);
return generated;
}
private void parentToContainer(Container container, Statement[] statements)
{
foreach(Statement statement; statements)
{
if(statement !is null)
{
statement.parentTo(container);
}
}
}
private void parseStatement()
{
2021-03-05 10:35:58 +00:00
gprintln("parseStatement(): Enter", DebugType.WARNING);
/* TODO: Implement parse statement */
/**
* TODO: We should remove the `;` from parseFuncCall
* And rather do the following here:
*
* 1. parseFuncCall()
* 2. expect(;)
*/
2021-03-05 10:35:58 +00:00
gprintln("parseStatement(): Leave", DebugType.WARNING);
}
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
/* TODO: Save name */
string functionName = getCurrentToken().getToken();
Expression[] arguments;
2021-03-03 16:34:38 +00:00
nextToken();
/* Expect an opening brace `(` */
expect(SymbolType.LBRACE, getCurrentToken());
nextToken();
/* If next token is RBRACE we don't expect arguments */
if(getSymbolType(getCurrentToken()) == SymbolType.RBRACE)
{
}
/* If not expect arguments */
else
{
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-03 16:34:38 +00:00
nextToken();
2021-03-05 10:35:58 +00:00
gprintln("parseFuncCall(): Leave", DebugType.WARNING);
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 */
/**
* 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 20:06:37 +01: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 20:06:37 +01:00
Module modulle;
2021-03-21 19:43:01 +00:00
2021-03-03 09:08:34 +00:00
/* TODO: Do parsing here */
/* Expect `module` and module name and consume them (and `;`) */
expect(SymbolType.MODULE, getCurrentToken());
nextToken();
2021-03-21 19:43:01 +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();
nextToken();
2021-03-21 19:43:01 +00:00
expect(SymbolType.SEMICOLON, getCurrentToken());
nextToken();
2021-03-29 20:06:37 +01:00
/* Initialize Module */
modulle = new Module(programName);
2021-03-21 19:43:01 +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 */
/* We can have an import or vardef or funcdef */
2021-03-03 12:42:57 +00:00
while (hasTokens())
{
/* Get the token */
Token tok = getCurrentToken();
SymbolType symbol = getSymbolType(tok);
2021-03-05 10:35:58 +00:00
gprintln("parse(): Token: " ~ tok.getToken());
/* If it is a type */
if (symbol == SymbolType.IDENT_TYPE)
2021-03-03 12:42:57 +00:00
{
2021-04-01 14:59:33 +01:00
/* Might be a function, might be a variable, or assignment */
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))
{
Entity entity = parseAccessor();
/* Everything at the Module level is static */
entity.setModifierType(InitScope.STATIC);
/* TODO: Tets case has classes which null statement, will crash */
modulle.addStatement(entity);
2021-03-21 11:32:51 +00:00
}
/* If it is a class */
2021-03-05 10:35:58 +00:00
else if (symbol == SymbolType.CLASS)
{
Clazz clazz = parseClass();
/* Everything at the Module level is static */
clazz.setModifierType(InitScope.STATIC);
/* Add the class definition to the program */
2021-03-29 20:06:37 +01:00
modulle.addStatement(clazz);
}
/* If it is a struct definition */
else if(symbol == SymbolType.STRUCT)
{
Struct ztruct = parseStruct();
/* Everything at the Module level is static */
ztruct.setModifierType(InitScope.STATIC);
/* Add the struct definition to the program */
modulle.addStatement(ztruct);
}
2021-06-09 11:55:59 +01:00
/* If it is a `discard` statement */
else if(symbol == SymbolType.DISCARD)
{
Expression expression = parseDiscard();
modulle.addStatement(expression);
}
2021-03-03 12:42:57 +00:00
else
{
2021-03-05 10:35:58 +00:00
expect("parse(): Unknown '" ~ tok.getToken() ~ "'");
}
}
2021-03-05 10:35:58 +00:00
gprintln("parse(): Leave", DebugType.WARNING);
2021-03-21 19:43:01 +00:00
/* Parent each Statement to the container (the module) */
parentToContainer(modulle, modulle.getStatements());
2021-03-29 20:06:37 +01:00
return modulle;
2021-03-03 09:08:34 +00:00
}
2021-03-03 11:30:56 +00:00
}
2021-06-08 09:53:53 +01:00
// unittest
// {
// /* TODO: Add some unit tests */
// import std.file;
// import std.stdio;
// import compiler.lexer;
2021-06-08 09:53:53 +01:00
// isUnitTest = true;
2021-03-21 12:27:31 +00:00
2021-06-08 09:53:53 +01:00
// string sourceFile = "source/tlang/testing/basic1.t";
2021-06-08 09:53:53 +01:00
// File sourceFileFile;
// sourceFileFile.open(sourceFile); /* TODO: Error handling with ANY file I/O */
// ulong fileSize = sourceFileFile.size();
// byte[] fileBytes;
// fileBytes.length = fileSize;
// fileBytes = sourceFileFile.rawRead(fileBytes);
// sourceFileFile.close();
2021-06-08 09:53:53 +01:00
// /* TODO: Open source file */
// string sourceCode = cast(string)fileBytes;
// // string sourceCode = "hello \"world\"|| ";
// //string sourceCode = "hello \"world\"||"; /* TODO: Implement this one */
// // string sourceCode = "hello;";
// Lexer currentLexer = new Lexer(sourceCode);
// assert(currentLexer.performLex());
2021-06-08 09:53:53 +01:00
// Parser parser = new Parser(currentLexer.getTokens());
// parser.parse();
// }
unittest
{
/* TODO: Add some unit tests */
import std.file;
import std.stdio;
import compiler.lexer;
2021-03-21 12:27:31 +00:00
isUnitTest = true;
// string sourceFile = "source/tlang/testing/basic1.t";
// File sourceFileFile;
// sourceFileFile.open(sourceFile); /* TODO: Error handling with ANY file I/O */
// ulong fileSize = sourceFileFile.size();
// byte[] fileBytes;
// fileBytes.length = fileSize;
// fileBytes = sourceFileFile.rawRead(fileBytes);
// sourceFileFile.close();
// /* TODO: Open source file */
// string sourceCode = cast(string)fileBytes;
// // string sourceCode = "hello \"world\"|| ";
// //string sourceCode = "hello \"world\"||"; /* TODO: Implement this one */
// // string sourceCode = "hello;";
// Lexer currentLexer = new Lexer(sourceCode);
// assert(currentLexer.performLex());
// Parser parser = new Parser(currentLexer.getTokens());
// parser.parse();
}
unittest
{
import std.file;
import std.stdio;
import compiler.lexer;
string sourceFile = "source/tlang/testing/simple1_module_positive.t";
File sourceFileFile;
sourceFileFile.open(sourceFile); /* TODO: Error handling with ANY file I/O */
ulong fileSize = sourceFileFile.size();
byte[] fileBytes;
fileBytes.length = fileSize;
fileBytes = sourceFileFile.rawRead(fileBytes);
sourceFileFile.close();
/* TODO: Open source file */
string sourceCode = cast(string)fileBytes;
// string sourceCode = "hello \"world\"|| ";
//string sourceCode = "hello \"world\"||"; /* TODO: Implement this one */
// string sourceCode = "hello;";
Lexer currentLexer = new Lexer(sourceCode);
assert(currentLexer.performLex());
Parser parser = new Parser(currentLexer.getTokens());
try
{
Module modulle = parser.parse();
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;
string sourceFile = "source/tlang/testing/simple2_name_recognition.t";
File sourceFileFile;
sourceFileFile.open(sourceFile); /* TODO: Error handling with ANY file I/O */
ulong fileSize = sourceFileFile.size();
byte[] fileBytes;
fileBytes.length = fileSize;
fileBytes = sourceFileFile.rawRead(fileBytes);
sourceFileFile.close();
/* TODO: Open source file */
string sourceCode = cast(string)fileBytes;
Lexer currentLexer = new Lexer(sourceCode);
assert(currentLexer.performLex());
Parser parser = new Parser(currentLexer.getTokens());
try
{
Module modulle = parser.parse();
/* Module name must be myModule */
assert(cmp(modulle.getName(), "myModule")==0);
TypeChecker tc = new TypeChecker(modulle);
2021-06-08 11:49:25 +01: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
*/
/* There should exist two Module-level classes named `myClass1` and `myClass2` */
2021-06-08 11:49:25 +01: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;
assert(clazz1);
2021-06-08 11:49:25 +01:00
assert(clazz1);
/**
2021-06-08 11:49:25 +01:00
* Resolve members of myClass1
*
2021-06-08 11:49:25 +01: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 11:49:25 +01:00
Entity myClass1_myClass2_1 = tc.getResolver().resolveBest(modulle, "myModule.myClass1.myClass2");
Entity myClass1_myClass2_2 = tc.getResolver().resolveBest(clazz1, "myClass2");
2021-06-08 11:51:23 +01:00
Entity myClass2 = tc.getResolver().resolveBest(modulle, "myClass2");
Entity myClass1_myClass2_4 = tc.getResolver().resolveBest(modulle, "myClass1.myClass2");
2021-06-08 11:49:25 +01:00
Entity myClass1_myClass2_5 = tc.getResolver().resolveBest(clazz2, "myClass1.myClass2");
2021-06-08 11:51:23 +01: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 11:52:27 +01: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;
/**
* 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);
/**
* 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
*
* Both should be variables
*/
assert(myClass1_myClass2_5 != myClass2);
Entity innerVariable = tc.getResolver().resolveBest(c_myClass1_myClass2_5, "inner");
Entity outerVariable = tc.getResolver().resolveBest(c_myClass2, "outer");
assert(innerVariable !is null);
assert(outerVariable !is null);
assert(cast(Variable)innerVariable);
assert(cast(Variable)outerVariable);
innerVariable = tc.getResolver().resolveBest(c_myClass2, "inner");
outerVariable = tc.getResolver().resolveBest(c_myClass1_myClass2_5, "outer");
assert(innerVariable is null);
2021-06-08 12:00:21 +01:00
assert(outerVariable is null);
/**
* 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);
}
catch(TError)
{
assert(false);
}
}