tlang/source/tlang/compiler/symbols/check.d

608 lines
13 KiB
D
Raw Normal View History

2021-03-30 16:35:16 +01:00
module compiler.symbols.check;
import compiler.lexer.core : Token;
2021-03-30 16:35:16 +01:00
import std.conv : to;
import std.string : isNumeric, cmp;
import misc.utils;
import gogga;
2021-03-30 16:35:16 +01:00
/**
* All allowed symbols
* TODO: There should be a symbol class with sub-types
*/
public enum SymbolType
{
LE_SYMBOL,
IDENT_TYPE,
NUMBER_LITERAL,
CHARACTER_LITERAL,
STRING_LITERAL,
SEMICOLON,
LBRACE,
RBRACE,
ASSIGN,
COMMA,
OCURLY,
CCURLY,
MODULE,
2021-06-09 11:43:59 +01:00
NEW,
2021-03-30 16:35:16 +01:00
IF,
ELSE,
2021-06-09 11:55:59 +01:00
DISCARD,
2021-03-30 16:35:16 +01:00
WHILE,
CLASS,
INHERIT_OPP,
TILDE,
FOR,
SUPER,
THIS,
SWITCH,
RETURN,
PUBLIC,
PRIVATE,
PROTECTED,
STATIC,
CASE,
GOTO,
DO,
DOT,
2021-03-30 16:35:16 +01:00
DELETE,
STRUCT,
SUB,
ADD,
DIVIDE,
STAR,
AMPERSAND,
EQUALS,
GREATER_THAN,
SMALLER_THAN,
GREATER_THAN_OR_EQUALS,
SMALLER_THAN_OR_EQUALS,
CAST,
Check - Added new symbol types `EXTERN`, `EXTERN_EFUNC` and `EXTERN_EVAR` and related back-mappings Parser - `parseFuncDef()` now accepts a default argument (set to `true`) on whether to expect a body for a function or not, in the not case expect a semi-colon - this helps with extern support - Likewise because `parseFuncDef(bool wantsBody = true)` is called by `parseTypedDeclaration()` we have added same argument to `parseTypedDeclaration(bool wantsBody = true)` - Ensure we pass the parameter from `parseTypedDeclaration()` into `parseFuncDef(bool)` in function definition case - Implemented `parseExtern()` for extern support - `parse()` supports `SymbolType.EXTERN` now Data - Added `ExternStmt` to represent the parser node derived from a call to `parseExtern()` - The `Entity` parser node type now has an `isExternal()` flag to know if the entity is marked for `extern` link time or TLang internal time (default) Typechecker - Implemented `processPseudoEntities(Container)` which loops through the given container and finds all extern statements and then extracts those nodes, parents them to the given container and marks them as external (pseudo-handling support) - Added first call inside `beginCheck()` to be a call to `processPseudoEntities(modulle)` Dependency - Added useless no-op check for `ExternStmt` - it does nothing DGen - In `emitFunctionSignature()`, prepend the string `extern ` to the signatur if the given `Function` entity is marked as external (`isExternal()` is true) - In `emitFunctionDefinitions()` do not emit a function body at all (or anything, no signature) if the `Function` is marked as external (`isExternal()` is true) - Added entry point test for `simple_extern.t`
2023-01-15 18:48:40 +00:00
EXTERN,
EXTERN_EFUNC,
EXTERN_EVAR,
2021-03-30 16:35:16 +01:00
UNKNOWN
}
/* TODO: Later build classes specific to symbol */
public bool isType(string tokenStr)
{
return cmp(tokenStr, "byte") == 0 || cmp(tokenStr, "ubyte") == 0
|| cmp(tokenStr, "short") == 0 || cmp(tokenStr, "ushort") == 0
|| cmp(tokenStr, "int") == 0 || cmp(tokenStr, "uint") == 0 || cmp(tokenStr,
"long") == 0 || cmp(tokenStr, "ulong") == 0 || cmp(tokenStr, "void") == 0;
}
public bool isPathIdentifier(string token)
{
/* This is used to prevent the first character from not being number */
bool isFirstRun = true;
/* Whether we found a dot or not */
bool isDot;
foreach (char character; token)
{
if(isFirstRun)
{
/* Only allow underscore of letter */
if(isCharacterAlpha(character) || character == '_')
{
}
else
{
return false;
}
isFirstRun = false;
}
else
{
/* Check for dot */
if(character == '.')
{
isDot = true;
}
else if(isCharacterAlpha(character) || character == '_' || isCharacterNumber(character))
{
}
else
{
return false;
}
}
}
if(token.length)
{
if(token[token.length-1] == '.')
{
return false;
}
}
return isDot;
}
public bool isIdentifier(string token)
{
/* This is used to prevent the first character from not being number */
bool isFirstRun = true;
foreach (char character; token)
{
if(isFirstRun)
{
/* Only allow underscore of letter */
if(isCharacterAlpha(character) || character == '_')
{
}
else
{
return false;
}
isFirstRun = false;
}
else
{
if(isCharacterAlpha(character) || character == '_' || isCharacterNumber(character))
{
}
else
{
return false;
}
}
}
return true;
}
public bool isAccessor(Token token)
{
return getSymbolType(token) == SymbolType.PUBLIC ||
getSymbolType(token) == SymbolType.PRIVATE ||
getSymbolType(token) == SymbolType.PROTECTED;
}
public bool isModifier(Token token)
{
return getSymbolType(token) == SymbolType.STATIC;
}
2021-03-30 16:35:16 +01:00
public bool isIdentifier_NoDot(Token tokenIn)
{
/* Make sure it isn't any other type of symbol */
if(getSymbolType(tokenIn) == SymbolType.IDENT_TYPE)
{
return isIdentifier(tokenIn.getToken());
}
else
{
return false;
}
}
public bool isIdentifier_Dot(Token tokenIn)
{
/* Make sure it isn't any other type of symbol */
if(getSymbolType(tokenIn) == SymbolType.IDENT_TYPE)
{
return isPathIdentifier(tokenIn.getToken()) || isIdentifier(tokenIn.getToken());
}
else
{
return false;
}
}
public SymbolType getSymbolType(Token tokenIn)
{
string token = tokenIn.getToken();
/* TODO: Get symbol type of token */
/* Character literal check */
if (token[0] == '\'')
{
/* TODO: Add escape sequnece support */
if (token[2] == '\'')
{
return SymbolType.CHARACTER_LITERAL;
}
}
/* String literal check */
else if (token[0] == '\"' && token[token.length - 1] == '\"')
{
return SymbolType.STRING_LITERAL;
}
/* Number literal check */
else if (isNumeric(token))
{
return SymbolType.NUMBER_LITERAL;
}
/* `struct` */
else if(cmp(token, "struct") == 0)
{
return SymbolType.STRUCT;
}
2021-03-30 16:35:16 +01:00
/* `if` */
else if(cmp(token, "if") == 0)
{
return SymbolType.IF;
}
/* `else` */
else if(cmp(token, "else") == 0)
{
return SymbolType.ELSE;
}
/* `while` */
else if(cmp(token, "while") == 0)
{
return SymbolType.WHILE;
}
/* class keyword */
else if(cmp(token, "class") == 0)
{
return SymbolType.CLASS;
}
/* static keyword */
else if(cmp(token, "static") == 0)
{
return SymbolType.STATIC;
}
/* private keyword */
else if(cmp(token, "private") == 0)
{
return SymbolType.PRIVATE;
}
/* public keyword */
else if(cmp(token, "public") == 0)
{
return SymbolType.PUBLIC;
}
/* protected keyword */
else if(cmp(token, "protected") == 0)
{
return SymbolType.PROTECTED;
}
/* return keyword */
else if(cmp(token, "return") == 0)
{
return SymbolType.RETURN;
}
/* switch keyword */
else if(cmp(token, "switch") == 0)
{
return SymbolType.SWITCH;
}
/* this keyword */
else if(cmp(token, "this") == 0)
{
return SymbolType.THIS;
}
/* super keyword */
else if(cmp(token, "super") == 0)
{
return SymbolType.SUPER;
}
/* for keyword */
else if(cmp(token, "for") == 0)
{
return SymbolType.FOR;
}
/* case keyword */
else if(cmp(token, "case") == 0)
{
return SymbolType.CASE;
}
/* goto keyword */
else if(cmp(token, "goto") == 0)
{
return SymbolType.GOTO;
}
/* do keyword */
else if(cmp(token, "do") == 0)
{
return SymbolType.DO;
}
/* delete keyword */
else if(cmp(token, "delete") == 0)
{
return SymbolType.DELETE;
}
Check - Added new symbol types `EXTERN`, `EXTERN_EFUNC` and `EXTERN_EVAR` and related back-mappings Parser - `parseFuncDef()` now accepts a default argument (set to `true`) on whether to expect a body for a function or not, in the not case expect a semi-colon - this helps with extern support - Likewise because `parseFuncDef(bool wantsBody = true)` is called by `parseTypedDeclaration()` we have added same argument to `parseTypedDeclaration(bool wantsBody = true)` - Ensure we pass the parameter from `parseTypedDeclaration()` into `parseFuncDef(bool)` in function definition case - Implemented `parseExtern()` for extern support - `parse()` supports `SymbolType.EXTERN` now Data - Added `ExternStmt` to represent the parser node derived from a call to `parseExtern()` - The `Entity` parser node type now has an `isExternal()` flag to know if the entity is marked for `extern` link time or TLang internal time (default) Typechecker - Implemented `processPseudoEntities(Container)` which loops through the given container and finds all extern statements and then extracts those nodes, parents them to the given container and marks them as external (pseudo-handling support) - Added first call inside `beginCheck()` to be a call to `processPseudoEntities(modulle)` Dependency - Added useless no-op check for `ExternStmt` - it does nothing DGen - In `emitFunctionSignature()`, prepend the string `extern ` to the signatur if the given `Function` entity is marked as external (`isExternal()` is true) - In `emitFunctionDefinitions()` do not emit a function body at all (or anything, no signature) if the `Function` is marked as external (`isExternal()` is true) - Added entry point test for `simple_extern.t`
2023-01-15 18:48:40 +00:00
/* efunc keyword */
else if(cmp(token, "efunc") == 0)
{
return SymbolType.EXTERN_EFUNC;
}
/* evar keyword */
else if(cmp(token, "evar") == 0)
{
return SymbolType.EXTERN_EVAR;
}
/* extern keyword */
else if(cmp(token, "extern") == 0)
{
return SymbolType.EXTERN;
}
2021-03-30 16:35:16 +01:00
/* module keyword */
else if(cmp(token, "module") == 0)
{
return SymbolType.MODULE;
}
2021-06-09 11:43:59 +01:00
/* new keyword */
else if(cmp(token, "new") == 0)
{
return SymbolType.NEW;
}
/* cast keyword */
else if(cmp(token, "cast") == 0)
{
return SymbolType.CAST;
}
2021-06-09 11:55:59 +01:00
/* discard keyword */
else if(cmp(token, "discard") == 0)
{
return SymbolType.DISCARD;
}
2021-03-30 16:35:16 +01:00
/* An identifier/type (of some sorts) - further inspection in parser is needed */
else if(isPathIdentifier(token) || isIdentifier(token))
{
return SymbolType.IDENT_TYPE;
}
/* Semi-colon `;` check */
else if (token[0] == ';')
{
return SymbolType.SEMICOLON;
}
/* Equality `==` check */
else if(cmp(token, "==") == 0)
{
return SymbolType.EQUALS;
}
2021-03-30 16:35:16 +01:00
/* Assign `=` check */
else if (token[0] == '=')
{
return SymbolType.ASSIGN;
}
/* Left-brace check */
else if (token[0] == '(')
{
return SymbolType.LBRACE;
}
/* Right-brace check */
else if (token[0] == ')')
{
return SymbolType.RBRACE;
}
/* Left-curly check */
else if (token[0] == '{')
{
return SymbolType.OCURLY;
}
/* Right-curly check */
else if (token[0] == '}')
{
return SymbolType.CCURLY;
}
/* Comma check */
else if (token[0] == ',')
{
return SymbolType.COMMA;
}
/* Inheritance operator check */
else if (token[0] == ':')
{
return SymbolType.INHERIT_OPP;
}
/* Tilde operator check */
else if (token[0] == '~')
{
return SymbolType.TILDE;
}
/* Dot operator check */
else if (token[0] == '.')
{
return SymbolType.DOT;
}
/* Add `+` operator check */
else if(token[0] == '+')
{
return SymbolType.ADD;
}
/* Subtraction `-` operator check */
else if(token[0] == '-')
{
return SymbolType.SUB;
}
/* Multiply `*` operator check */
else if(token[0] == '*')
{
return SymbolType.STAR;
}
/* Divide `/` operator check */
else if(token[0] == '/')
{
return SymbolType.DIVIDE;
}
/* Ampersand `&` operator check */
else if(token[0] == '&')
{
return SymbolType.AMPERSAND;
}
/* Greater than `>` operator check */
else if(token[0] == '>')
{
return SymbolType.GREATER_THAN;
}
/* Smaller than `<` operator check */
else if(token[0] == '<')
{
return SymbolType.SMALLER_THAN;
}
/* Greater than or equals to `>=` operator check */
else if(cmp(">=", token) == 0)
{
return SymbolType.GREATER_THAN_OR_EQUALS;
}
/* Smaller than or equals to `<=` operator check */
else if(cmp("<=", token) == 0)
{
return SymbolType.SMALLER_THAN_OR_EQUALS;
}
2021-03-30 16:35:16 +01:00
return SymbolType.UNKNOWN;
}
public bool isMathOp(Token token)
{
string tokenStr = token.getToken();
return tokenStr[0] == '+' || tokenStr[0] == '-' ||
tokenStr[0] == '*' || tokenStr[0] == '/';
}
public bool isBinaryOp(Token token)
{
string tokenStr = token.getToken();
return tokenStr[0] == '&' || cmp("&&", tokenStr) == 0 ||
tokenStr[0] == '|' || cmp("||", tokenStr) == 0 ||
tokenStr[0] == '^' || tokenStr[0] == '~' ||
tokenStr[0] == '<' || tokenStr[0] == '>' ||
cmp(">=", tokenStr) == 0 || cmp("<=", tokenStr) == 0 ||
cmp("==", tokenStr) == 0;
}
/**
* Returns the corresponding character for a given SymbolType
*
* For example <code>SymbolType.ADD</code> returns +
*
* Params:
* symbolIn = The symbol to lookup against
* Returns: The corresponding character
*
*/
public string getCharacter(SymbolType symbolIn)
{
if(symbolIn == SymbolType.ADD)
{
return "+";
}
else if(symbolIn == SymbolType.STAR)
{
return "*";
}
else if(symbolIn == SymbolType.SUB)
{
return "-";
}
else if(symbolIn == SymbolType.DIVIDE)
{
return "/";
}
App - Added newline to release info print - Fixed module docstring Commands - Added new command-line options: `syntaxcheck`, `typecheck` - Added todo to `help` command - Re-ordered commands for order of appearance in help text Compiler - Added docstring to `beginCompilation(string[])` function Mapper - Added debug print of the Container being used for the symbol lookup CodeEmitter - Re-worked CodeEmitter class to use a single so-called "selected queue" - Added methods to move back and forth between said "selected queue", get the length, etc. - Remove old queue-specific methods DGen - Use the new CodeEmitter "selected-queue" functionality - Emit function definitions now supported Exceptions - Added this keyword Check - Added support for SymbolTYpe.OCURLY and SymbolType.CCURLY to `getCharacter(SymbolType)` Data - Added a `hasParams()` method to the Function entity type TypeChecker - Added support for emitting function definitions (required DNode.poes = [] (cleaning), codeQueue cleaning etc.) - Added `getInitQueue()` method to make a copy of the current "scratchpad" `codeQueue` - Build up a copy of the global queue now (make a copy similiar to what we did for `getInitQueue()` but inline) - Added a debug print Dependency - Added a FIXME note for issue #46 - Added a TODO relating to `static DNode[] poes` Test cases - Added test case `simple_function_decls.t` to test function definition code emit - Updated test case `simple_variables.t` to note that the T code generates invalid C code README - Build instructions now generate coverage files (`.lst`s) - Updated link to documentation
2022-12-14 17:49:08 +00:00
else if(symbolIn == SymbolType.OCURLY)
{
return "{";
}
else if(symbolIn == SymbolType.CCURLY)
{
return "}";
}
Lexer - Fixed missing flushing for issue #65 (see "Flushing fix ✅") - Added unit test for flushing fix VariableDeclaration (Instruction) - Added support for the embedding of a VariableAssignmentInstr inside (added a getter too) (a part of issue #66) - Conditional support for if statements: Added two new instructions (IfStatementInstruction and BranchInstruction). See issue #64 DGen - Added depth increment/decrement on enter/leave scope of `transform()` - Correct tabbing for nested if-statements using new method `genTabs(ulong)` (which uses the above mechanism). Makes code emitted for if statements (issue #64) look nicer. - Updated VariableDeclarations (with assignments) handling in `transform()` in the manner similar to BinOpInstr (see issue #66) - Added a TODO for formatting BinOpInstr's `transform()` a little more aesthetically nicer - Added code emitting support for if statements (the `IfStatementInstruction` instruction) (see issue #64) - Updated `emitEntryPoint()` to only emit testing C code for the correct input test file Parser - `parseIf()` now returns an instance of IfStatement which couples multiple `Branch` objects consisting of `Statement[]` and `Expression` - Ensured that each `Statement` of the generated `Statement[]` from `parseBody()` for a given `Branch` is parented to said Branch using `parentToContainer()` - Ensured each generated `Branch` in `Branch[]` is parented to the generated `IfStatement` using `parentToContainer()` - `parseBody()` now adds to its `Statement[]` build-up array the generated `IfStatement` from the call to `parseIf()` Check - Added support for back-mapping `SymbolType.EQUALS` to `getCharacter(SymbolType)` Data - Added `Branch` parser node which is a Container for body statements (`Statement[]`) - Added `IfStatement` parser node which is a Container of `Statement[]` which are actually `Branch[]` TypeChecker - Moved import for `reverse` to top of module - Implemented `tailPopInstr()` method which will pop from the back of the `codeQueue` "scratchpad" - Fixes handling of `StaticVariableDeclaration` and `VariableAssignmentNode` (fixes issue #66) - Added handling for IfStatement entities (if statement support #64) Resolution - Added a debug statement to `resolveUp(Container, string)` to print out the container to lookup from and the name being looked up Dependency - Added a default `toString()` to the DNode class which prints `[DNode: <entity toString()]` - Added a TODO and debug print related to issues #9 - Disabled InitScope.STATIC check for now as it caused issues with if statement parsing (probably due to VIRTUAL being default and therefore skipping if statment processing) - issue #69 - Cleaned up handling of Entity type `Variable` (variable declarations) - removed repeated code - Undid the VarAss->(depends on)->VarDec, reverted back to VarDec->(depends on)->VarAss, fixed by #66 (and closes it and #11) - Added support for `IfStatement` (if statements) in `generalPass(Container, Context)` Test cases - Added new test case testing nested if statements (`nested_conditions.t`) - Added another test case for if statements, `simple_conditions.t`
2022-12-19 13:37:55 +00:00
else if(symbolIn == SymbolType.EQUALS)
{
return "==";
}
Instruction - Added a new instruction, `ForLoop`, which contains a pre-run Instruction and a `Branch` instruction, coupled with some flags DGen - Added a TODO for WhileLoops (we need to implement do-while loops) - Implemented C code emitting in `emit()` for `ForLoop` instruction Check - Added missing back-mapping for `SymbolType.SMALLER_THAN` Data - Added new parser node type `ForLoop` Parser - Fixed typo in `parseWhile()` - Implemented `parseDoWhile()` for do-while loops - Implemented `parseFor()` for for-loops - Implemented `parseStatement()` for singular statement parsing - `parseStatement()` can now have the terminating symbol specified, defaults to `SymbolType.SEMICOLON` - `parseName()` and `parseAssignment()` now also accept a terminating symbol parameter as per `parseStatement()`'s behavior - `parseBody()` now makes multiple calls to `parseStatement()` for singular Statement parsing (dead code below still to be removed) - Removed commented-out unittests - Unittests that read from files now have the file source code embedded - Added unit test for while loops, for-loops (unfinished) and some other smaller language constructs (roughly 70% coverage) TypeChecker (CodeGen) - Do-while loops will fail if used (for now) - Added for-loop code generation Dependency - Implemented `generalStatement()` for statement processing - `generalPass()` now makes calls to `generalStatement()` Tests - Added `simple_for_loops.t` to test for-loops - Added `simple_do_while.t` to test do-while loops
2023-01-11 08:43:29 +00:00
else if(symbolIn == SymbolType.SMALLER_THAN)
{
return "<";
}
2023-01-11 09:56:12 +00:00
else if(symbolIn == SymbolType.SMALLER_THAN_OR_EQUALS)
{
return "<=";
}
else if(symbolIn == SymbolType.GREATER_THAN)
{
return ">";
}
else if(symbolIn == SymbolType.GREATER_THAN_OR_EQUALS)
{
return ">=";
}
Instruction - Added `getOperator()` and `getOperand()` methods to `UnaryOpInstr` - Added new instruction `PointerDereferenceAssignmentInstruction` for pointer support DGen - Updated `transform()` to emit code for instruction type `UnaryOpInstr` - Updated `transform()` to emit code for instruction type `PointerDereferenceAssignmentInstruction` - Added testing emit code in `emitEntryPoint()` for pointer testing Parser - Updated `parseName()` to trigger `parseTypedDeclaration()` on occurene of `SymbolType.STAR` (for pointer type declarations) - Added pointer-type support for function parameters (so far only single) in `parseFuncDef()` - `parseExpression()` terminates on occurence of a single `=` (ASSIGN) operator - Declaring of pointers of any depth implemented in `parseTypedDeclaration()` - Added support for pointer dereferncing assignments with the addition of `parseDerefAssignment()` - `parseStatement()` will now call `parseDerefAssignment()` on occurence of a `SymbolType.STAR` - Added a unittest for testing pointers - Finished unittest for for loops Check - Added backmapping for `SymbolType.ASSIGN` -> `&` Data - Added new parser node type `PointerDereferenceAssignment` for pointer support in the parser TypeChecker - Because function parameters are type che cked upon function call I had to add typechecking code for pointer support in the `UnaryOperatorExpression` case - Added code generation support for `PointerDereferenceAssignment` type Dependency - Added support for `PointerDereferenceAssignment` type (pointer support) to `generalStatement()` Tests - Added pointer test `simple_pointer.t`
2023-01-12 08:53:48 +00:00
else if(symbolIn == SymbolType.AMPERSAND)
{
return "&";
}
Check - Added new symbol types `EXTERN`, `EXTERN_EFUNC` and `EXTERN_EVAR` and related back-mappings Parser - `parseFuncDef()` now accepts a default argument (set to `true`) on whether to expect a body for a function or not, in the not case expect a semi-colon - this helps with extern support - Likewise because `parseFuncDef(bool wantsBody = true)` is called by `parseTypedDeclaration()` we have added same argument to `parseTypedDeclaration(bool wantsBody = true)` - Ensure we pass the parameter from `parseTypedDeclaration()` into `parseFuncDef(bool)` in function definition case - Implemented `parseExtern()` for extern support - `parse()` supports `SymbolType.EXTERN` now Data - Added `ExternStmt` to represent the parser node derived from a call to `parseExtern()` - The `Entity` parser node type now has an `isExternal()` flag to know if the entity is marked for `extern` link time or TLang internal time (default) Typechecker - Implemented `processPseudoEntities(Container)` which loops through the given container and finds all extern statements and then extracts those nodes, parents them to the given container and marks them as external (pseudo-handling support) - Added first call inside `beginCheck()` to be a call to `processPseudoEntities(modulle)` Dependency - Added useless no-op check for `ExternStmt` - it does nothing DGen - In `emitFunctionSignature()`, prepend the string `extern ` to the signatur if the given `Function` entity is marked as external (`isExternal()` is true) - In `emitFunctionDefinitions()` do not emit a function body at all (or anything, no signature) if the `Function` is marked as external (`isExternal()` is true) - Added entry point test for `simple_extern.t`
2023-01-15 18:48:40 +00:00
else if(symbolIn == SymbolType.SEMICOLON)
{
return ";";
}
else
{
gprintln("getCharacter: No back-mapping for "~to!(string)(symbolIn), DebugType.ERROR);
assert(false);
}
}
2021-03-30 16:35:16 +01:00
/* Test: Character literal */
unittest
{
SymbolType symbol = getSymbolType(new Token("'c'", 0, 0));
assert(symbol == SymbolType.CHARACTER_LITERAL);
}
/* Test: String literals */
unittest
{
SymbolType symbol = getSymbolType(new Token("\"hello\"", 0, 0));
assert(symbol == SymbolType.STRING_LITERAL);
}
/* Test: Number literals */
unittest
{
SymbolType symbol = getSymbolType(new Token("2121", 0, 0));
assert(symbol == SymbolType.NUMBER_LITERAL);
symbol = getSymbolType(new Token("2121a", 0, 0));
assert(symbol != SymbolType.NUMBER_LITERAL);
}
/* Test: Identifer tests */
unittest
{
SymbolType symbol = getSymbolType(new Token("_yolo2", 0, 0));
assert(symbol == SymbolType.IDENT_TYPE);
symbol = getSymbolType(new Token("2_2ff", 0, 0));
assert(symbol != SymbolType.IDENT_TYPE);
}
/* Test: Identifier type detection */
unittest
{
assert(isPathIdentifier("hello.e.e"));
assert(!isPathIdentifier("hello"));
assert(!isIdentifier("hello.e.e"));
assert(isIdentifier("hello"));
/* TODO: Add support for the below in lexer */
assert(isPathIdentifier("hello._a.e"));
assert(isPathIdentifier("hello._2._e"));
}