2020-09-10 00:56:16 +00:00
|
|
|
|
|
|
|
/*************/
|
|
|
|
/*GEMWIRE */
|
|
|
|
/* ERYTHRO*/
|
|
|
|
/*************/
|
|
|
|
|
|
|
|
#include <Defs.h>
|
|
|
|
#include <Data.h>
|
2021-01-17 06:37:39 +00:00
|
|
|
#include <stdbool.h>
|
|
|
|
|
2021-01-20 19:22:15 +00:00
|
|
|
/*
|
|
|
|
* Handles reading in a comma-separated list of declarations.
|
|
|
|
* Erythro treats structs, enums and function parameters the same in this regard -
|
|
|
|
* comma separated.
|
|
|
|
*
|
|
|
|
* C and C++ tend to treat enums and structs differently - the former separated by commas,
|
|
|
|
* the latter separated by semicolons.
|
|
|
|
*
|
|
|
|
* Note that since functions are read in through parentheses, and structs/enums are read in
|
|
|
|
* through brackets, the end character is configurable.
|
|
|
|
*
|
|
|
|
* @param FunctionSymbol: The Symbol Table Entry of the current function, if applicable.
|
|
|
|
* @param Storage: The Storage Scope of this declaration list.
|
|
|
|
* @param End: The end token, in terms of TokenTypes enum values.
|
|
|
|
* @return the amount of declarations read in.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int ReadDeclarationList(struct SymbolTableEntry* FunctionSymbol, int Storage, int End) {
|
2021-01-20 01:05:41 +00:00
|
|
|
int TokenType, ParamCount = 0;
|
2022-03-03 00:05:10 +00:00
|
|
|
struct SymbolTableEntry* PrototypePointer = NULL, * Composite;
|
2021-01-18 00:20:58 +00:00
|
|
|
|
2022-03-03 00:05:10 +00:00
|
|
|
if (FunctionSymbol != NULL)
|
2021-01-20 01:05:41 +00:00
|
|
|
PrototypePointer = FunctionSymbol->Start;
|
2021-01-17 06:37:39 +00:00
|
|
|
|
2022-03-05 01:13:45 +00:00
|
|
|
while (CurrentFile->CurrentSymbol.type != End) {
|
2022-03-05 23:42:01 +00:00
|
|
|
TokenType = ReadTypeOrKeyword(&Composite);
|
2021-01-17 06:37:39 +00:00
|
|
|
VerifyToken(TY_IDENTIFIER, "identifier");
|
|
|
|
|
2021-03-15 01:22:47 +00:00
|
|
|
printf("\tReading a new element: %s of type %d, scope %s\n", CurrentIdentifier, TokenType, ScopeNames[Storage]);
|
2021-01-22 01:01:53 +00:00
|
|
|
|
2022-03-03 00:05:10 +00:00
|
|
|
if (PrototypePointer != NULL) {
|
|
|
|
if (TokenType != PrototypePointer->Type)
|
2021-01-22 01:01:53 +00:00
|
|
|
DieDecimal("Function parameter of invalid type at index", ParamCount + 1);
|
2022-03-03 00:05:10 +00:00
|
|
|
PrototypePointer = PrototypePointer->NextSymbol;
|
2021-01-18 00:20:58 +00:00
|
|
|
} else {
|
2021-01-20 19:22:15 +00:00
|
|
|
BeginVariableDeclaration(TokenType, Composite, Storage);
|
2021-01-18 00:20:58 +00:00
|
|
|
}
|
2021-01-17 06:37:39 +00:00
|
|
|
ParamCount++;
|
|
|
|
|
2022-03-05 01:13:45 +00:00
|
|
|
if ((CurrentFile->CurrentSymbol.type != LI_COM) && (CurrentFile->CurrentSymbol.type != End))
|
|
|
|
DieDecimal("Unexpected token in parameter", CurrentFile->CurrentSymbol.type);
|
2022-03-03 00:05:10 +00:00
|
|
|
|
2022-03-05 01:13:45 +00:00
|
|
|
if (CurrentFile->CurrentSymbol.type == LI_COM)
|
2021-01-20 19:22:15 +00:00
|
|
|
Tokenise();
|
2021-01-17 06:37:39 +00:00
|
|
|
}
|
2020-09-10 00:56:16 +00:00
|
|
|
|
2022-03-03 00:05:10 +00:00
|
|
|
if ((FunctionSymbol != NULL) && (ParamCount != FunctionSymbol->Length))
|
2021-01-20 01:05:41 +00:00
|
|
|
DieMessage("Invalid number of parameters in prototyped function", FunctionSymbol->Name);
|
2021-01-18 00:20:58 +00:00
|
|
|
|
2021-01-17 06:37:39 +00:00
|
|
|
return ParamCount;
|
|
|
|
}
|
2020-09-10 00:56:16 +00:00
|
|
|
|
2021-01-20 19:22:15 +00:00
|
|
|
/*
|
2022-03-03 02:44:07 +00:00
|
|
|
* Handles the declaration of a new composite type.
|
|
|
|
* For example, a struct is a composite of multiple different named positions:
|
2021-01-20 19:22:15 +00:00
|
|
|
* struct thisStct { int x, int y, int z };
|
|
|
|
*
|
|
|
|
* Verifies that the current identifier is not used,
|
|
|
|
* verifies that this is not a redefinition (excluding
|
|
|
|
* the case where there is a declaration but no definition)
|
2022-03-03 02:44:07 +00:00
|
|
|
* and then saves it into the appropriate symbol table.
|
2021-01-20 19:22:15 +00:00
|
|
|
*
|
2022-03-03 02:44:07 +00:00
|
|
|
* @return the Symbol Table entry of this new composite.
|
2021-01-20 19:22:15 +00:00
|
|
|
*/
|
|
|
|
|
2022-03-03 02:44:07 +00:00
|
|
|
struct SymbolTableEntry* BeginCompositeDeclaration(int Type) {
|
2022-03-03 00:05:10 +00:00
|
|
|
struct SymbolTableEntry* Composite = NULL, * Member;
|
2022-03-03 02:44:07 +00:00
|
|
|
int Offset = 0, Largest = 0;
|
2021-01-20 19:22:15 +00:00
|
|
|
|
|
|
|
Tokenise();
|
|
|
|
|
2022-03-05 01:13:45 +00:00
|
|
|
if (CurrentFile->CurrentSymbol.type == TY_IDENTIFIER) {
|
2022-03-03 02:44:07 +00:00
|
|
|
Composite = Type == DAT_STRUCT ? FindStruct(CurrentIdentifier) : FindUnion(CurrentIdentifier);
|
2021-01-20 19:22:15 +00:00
|
|
|
Tokenise();
|
|
|
|
}
|
|
|
|
|
2022-03-05 01:13:45 +00:00
|
|
|
if (CurrentFile->CurrentSymbol.type != LI_LBRAC) {
|
2022-03-03 00:05:10 +00:00
|
|
|
if (Composite == NULL)
|
2021-01-20 19:22:15 +00:00
|
|
|
DieMessage("Unknown Struct", CurrentIdentifier);
|
|
|
|
return Composite;
|
|
|
|
}
|
|
|
|
|
2022-03-03 00:05:10 +00:00
|
|
|
if (Composite)
|
2022-03-03 02:44:07 +00:00
|
|
|
DieMessage("Redefinition of composite", CurrentIdentifier);
|
2021-01-20 19:22:15 +00:00
|
|
|
|
2022-03-04 01:11:04 +00:00
|
|
|
Composite = AddSymbol(CurrentIdentifier, Type, ST_RUCT, Type == DAT_STRUCT ? SC_STRUCT : SC_UNION, 0, 0, NULL);
|
2021-01-20 19:22:15 +00:00
|
|
|
Tokenise();
|
2022-03-03 02:44:07 +00:00
|
|
|
printf("Reading a composite declaration.. Type is %s\n", Type == DAT_STRUCT ? "struct" : "union");
|
2021-01-22 01:01:53 +00:00
|
|
|
ReadDeclarationList(NULL, SC_MEMBER, LI_RBRAC);
|
|
|
|
VerifyToken(LI_RBRAC, "}");
|
2021-01-20 19:22:15 +00:00
|
|
|
|
2022-03-03 02:44:07 +00:00
|
|
|
Composite->Start = CompositeMembers;
|
|
|
|
CompositeMembers = CompositeMembersEnd = NULL;
|
2021-01-20 19:22:15 +00:00
|
|
|
|
|
|
|
Member = Composite->Start;
|
2022-03-03 02:44:07 +00:00
|
|
|
printf("\tSetting first entry in composite to %s\r\n", Member->Name);
|
2021-01-20 19:22:15 +00:00
|
|
|
Member->SinkOffset = 0;
|
|
|
|
Offset = TypeSize(Member->Type, Member->CompositeType);
|
|
|
|
|
2022-03-03 00:05:10 +00:00
|
|
|
for (Member = Member->NextSymbol; Member != NULL; Member = Member->NextSymbol) {
|
2022-03-03 02:44:07 +00:00
|
|
|
if (Type == DAT_STRUCT)
|
2023-04-23 16:32:02 +00:00
|
|
|
Member->SinkOffset = Assembler->vtable->AsAlignMemory(Member->Type, Offset, 1);
|
2022-03-03 02:44:07 +00:00
|
|
|
else
|
|
|
|
Member->SinkOffset = 0;
|
|
|
|
|
|
|
|
int CurrentSize = TypeSize(Member->Type, Member->CompositeType);
|
|
|
|
Offset += CurrentSize;
|
|
|
|
Largest = CurrentSize > Largest ? CurrentSize : Largest;
|
2021-01-20 19:22:15 +00:00
|
|
|
}
|
|
|
|
|
2022-03-03 02:44:07 +00:00
|
|
|
Composite->Length = Type == DAT_STRUCT ? Offset : Largest;
|
2021-01-20 19:22:15 +00:00
|
|
|
return Composite;
|
|
|
|
}
|
|
|
|
|
2022-03-04 01:11:04 +00:00
|
|
|
void BeginEnumDeclaration() {
|
|
|
|
struct SymbolTableEntry* Type = NULL;
|
|
|
|
char* Name;
|
|
|
|
int Value = 0;
|
|
|
|
|
|
|
|
Tokenise();
|
|
|
|
|
|
|
|
// enum name
|
2022-03-05 01:13:45 +00:00
|
|
|
if (CurrentFile->CurrentSymbol.type == TY_IDENTIFIER) {
|
2022-03-04 01:11:04 +00:00
|
|
|
Type = FindEnum(CurrentIdentifier);
|
|
|
|
Name = strdup(CurrentIdentifier);
|
|
|
|
Tokenise();
|
|
|
|
}
|
|
|
|
|
|
|
|
// enum name {? if not, enum name var.
|
2022-03-05 01:13:45 +00:00
|
|
|
if (CurrentFile->CurrentSymbol.type != LI_LBRAC) {
|
2022-03-04 01:11:04 +00:00
|
|
|
if (Type == NULL)
|
|
|
|
DieMessage("Undeclared Enum", Name);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Skip the { that we have
|
|
|
|
Tokenise();
|
|
|
|
|
|
|
|
if (Type != NULL)
|
|
|
|
DieMessage("Attempting to redefine enum", Type->Name);
|
|
|
|
else
|
|
|
|
Type = AddSymbol(Name, DAT_ENUM, ST_ENUM, SC_ENUM, 0, 0, NULL);
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
VerifyToken(TY_IDENTIFIER, "Enum Entry");
|
|
|
|
Name = strdup(CurrentIdentifier);
|
|
|
|
|
|
|
|
Type = FindEnumMember(Name);
|
|
|
|
if (Type != NULL)
|
|
|
|
DieMessage("Attempting to redeclare enum value", Name);
|
|
|
|
|
|
|
|
// Parse equality
|
2022-03-05 01:13:45 +00:00
|
|
|
if (CurrentFile->CurrentSymbol.type == LI_EQUAL) {
|
2022-03-04 01:11:04 +00:00
|
|
|
Tokenise();
|
|
|
|
// Expect a number after the equals
|
2022-03-05 01:13:45 +00:00
|
|
|
if (CurrentFile->CurrentSymbol.type != LI_INT)
|
2022-03-04 01:11:04 +00:00
|
|
|
Die("Expected integer to assign enum value to");
|
2022-03-05 01:13:45 +00:00
|
|
|
Value = CurrentFile->CurrentSymbol.value;
|
2022-03-04 01:11:04 +00:00
|
|
|
Tokenise();
|
|
|
|
}
|
|
|
|
|
|
|
|
Type = AddSymbol(Name, DAT_ENUM, ST_ENUM, SC_ENUMENTRY, Value++, 0, NULL);
|
|
|
|
|
|
|
|
// Break on right brace
|
2022-03-05 01:13:45 +00:00
|
|
|
if (CurrentFile->CurrentSymbol.type == LI_RBRAC)
|
2022-03-04 01:11:04 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
VerifyToken(LI_COM, "Comma");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Skip right brace
|
|
|
|
Tokenise();
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-09-10 00:56:16 +00:00
|
|
|
/*
|
|
|
|
* Handles the declaration of a type of a variable.
|
|
|
|
* int newVar;
|
|
|
|
*
|
2021-01-20 01:05:41 +00:00
|
|
|
* It verifies that we have a type keyword followed by a
|
2020-09-10 00:56:16 +00:00
|
|
|
* unique, non-keyword identifier.
|
|
|
|
*
|
2021-01-20 19:22:15 +00:00
|
|
|
* It then stores this variable into the appropriate symbol table,
|
2021-01-20 01:05:41 +00:00
|
|
|
* and returns the new item.
|
2020-09-10 00:56:16 +00:00
|
|
|
*
|
2021-01-20 19:22:15 +00:00
|
|
|
* @return the Symbol Table entry of this new variable.
|
2020-09-10 00:56:16 +00:00
|
|
|
*/
|
2021-01-20 19:22:15 +00:00
|
|
|
struct SymbolTableEntry* BeginVariableDeclaration(int Type, struct SymbolTableEntry* Composite, int Scope) {
|
2021-01-20 01:05:41 +00:00
|
|
|
struct SymbolTableEntry* Symbol = NULL;
|
|
|
|
|
2022-03-03 00:05:10 +00:00
|
|
|
switch (Scope) {
|
2021-01-20 01:05:41 +00:00
|
|
|
case SC_GLOBAL:
|
2022-03-03 00:05:10 +00:00
|
|
|
if (FindGlobal(CurrentIdentifier) != NULL)
|
2021-01-20 01:05:41 +00:00
|
|
|
DieMessage("Invalid redeclaration of global variable", CurrentIdentifier);
|
|
|
|
case SC_LOCAL:
|
|
|
|
case SC_PARAM:
|
2022-03-03 00:05:10 +00:00
|
|
|
if (FindLocal(CurrentIdentifier) != NULL)
|
2021-01-20 19:22:15 +00:00
|
|
|
DieMessage("Invalid redeclaration of local variable", CurrentIdentifier);
|
|
|
|
case SC_MEMBER:
|
2022-03-03 00:05:10 +00:00
|
|
|
if (FindMember(CurrentIdentifier) != NULL)
|
2021-01-20 19:22:15 +00:00
|
|
|
DieMessage("Invalid redeclaration of Enum/Struct member", CurrentIdentifier);
|
2021-01-20 01:05:41 +00:00
|
|
|
}
|
2020-09-10 00:56:16 +00:00
|
|
|
|
2022-03-05 01:13:45 +00:00
|
|
|
if (CurrentFile->CurrentSymbol.type == LI_LBRAS) {
|
2021-01-20 19:22:15 +00:00
|
|
|
Tokenise();
|
2021-01-20 01:05:41 +00:00
|
|
|
|
2022-03-05 01:13:45 +00:00
|
|
|
if (CurrentFile->CurrentSymbol.type == LI_INT) {
|
2022-03-03 00:05:10 +00:00
|
|
|
switch (Scope) {
|
2021-01-20 01:05:41 +00:00
|
|
|
case SC_GLOBAL:
|
2021-01-20 19:22:15 +00:00
|
|
|
Symbol = AddSymbol(CurrentIdentifier, PointerTo(Type), ST_ARR, Scope, 1, 0, NULL);
|
2021-01-20 01:05:41 +00:00
|
|
|
break;
|
|
|
|
case SC_LOCAL:
|
|
|
|
case SC_PARAM:
|
2021-01-20 19:22:15 +00:00
|
|
|
case SC_MEMBER:
|
2021-01-20 01:05:41 +00:00
|
|
|
Die("Local arrays are unimplemented");
|
|
|
|
}
|
2020-09-13 22:41:46 +00:00
|
|
|
}
|
2020-09-10 00:56:16 +00:00
|
|
|
|
2021-01-20 19:22:15 +00:00
|
|
|
Tokenise();
|
2020-11-18 20:49:08 +00:00
|
|
|
VerifyToken(LI_RBRAS, "]");
|
|
|
|
} else {
|
2021-01-20 19:22:15 +00:00
|
|
|
Symbol = AddSymbol(CurrentIdentifier, Type, ST_VAR, Scope, 1, 0, Composite);
|
2020-09-13 22:41:46 +00:00
|
|
|
}
|
2020-11-18 20:49:08 +00:00
|
|
|
|
2021-01-20 01:05:41 +00:00
|
|
|
return Symbol;
|
|
|
|
|
2020-09-10 00:56:16 +00:00
|
|
|
}
|
|
|
|
|
2021-01-20 19:22:15 +00:00
|
|
|
/*
|
|
|
|
* Handles the declaration of a new function.
|
|
|
|
* Verifies that the identifier is not taken (excluding the case
|
|
|
|
* where there is a declaration but no definition)
|
|
|
|
* Parses the list of parameters if present
|
|
|
|
* Saves the function prototype if there is no body
|
|
|
|
* Generates and saves the break-out point label
|
|
|
|
*
|
|
|
|
* @param Type: The return type of the function
|
|
|
|
* @return the AST for this function
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2020-09-13 22:41:46 +00:00
|
|
|
struct ASTNode* ParseFunction(int Type) {
|
2020-09-10 00:56:16 +00:00
|
|
|
struct ASTNode* Tree;
|
|
|
|
struct ASTNode* FinalStatement;
|
2022-03-03 00:05:10 +00:00
|
|
|
struct SymbolTableEntry* OldFunction, * NewFunction = NULL;
|
2022-03-06 01:15:07 +00:00
|
|
|
int BreakLabel = 0, ParamCount = 0;
|
2021-01-18 00:20:58 +00:00
|
|
|
|
2022-03-03 00:05:10 +00:00
|
|
|
if ((OldFunction = FindSymbol(CurrentIdentifier)) != NULL)
|
|
|
|
if (OldFunction->Storage != ST_FUNC)
|
2021-01-20 01:05:41 +00:00
|
|
|
OldFunction = NULL;
|
2022-03-03 00:05:10 +00:00
|
|
|
if (OldFunction == NULL) {
|
2023-04-24 02:03:31 +00:00
|
|
|
BreakLabel = Assembler->vtable->NewLabel();
|
2021-01-20 19:22:15 +00:00
|
|
|
NewFunction = AddSymbol(CurrentIdentifier, Type, ST_FUNC, SC_GLOBAL, BreakLabel, 0, NULL);
|
2021-01-18 00:20:58 +00:00
|
|
|
}
|
2020-09-10 00:56:16 +00:00
|
|
|
|
2021-01-18 00:20:58 +00:00
|
|
|
VerifyToken(LI_LPARE, "(");
|
2021-03-15 01:22:47 +00:00
|
|
|
ParamCount = ReadDeclarationList(OldFunction, SC_PARAM, LI_RPARE);
|
2021-01-18 00:20:58 +00:00
|
|
|
VerifyToken(LI_RPARE, ")");
|
2020-09-10 00:56:16 +00:00
|
|
|
|
2022-03-03 00:05:10 +00:00
|
|
|
printf("\nIdentified%sfunction %s of return type %s, end label %d\n",
|
|
|
|
(OldFunction == NULL) ? " new " : " overloaded ",
|
|
|
|
(OldFunction == NULL) ? NewFunction->Name : OldFunction->Name,
|
|
|
|
TypeNames(Type), BreakLabel);
|
2021-02-22 17:43:06 +00:00
|
|
|
|
2022-03-03 00:05:10 +00:00
|
|
|
if (NewFunction) {
|
2021-01-20 01:05:41 +00:00
|
|
|
NewFunction->Elements = ParamCount;
|
|
|
|
NewFunction->Start = Params;
|
2021-03-15 15:59:15 +00:00
|
|
|
NewFunction->Type = RET_LONG;
|
2021-01-20 01:05:41 +00:00
|
|
|
OldFunction = NewFunction;
|
2021-01-18 00:20:58 +00:00
|
|
|
}
|
2020-09-10 00:56:16 +00:00
|
|
|
|
2021-01-20 01:05:41 +00:00
|
|
|
Params = ParamsEnd = NULL;
|
|
|
|
|
2022-03-05 01:13:45 +00:00
|
|
|
if (CurrentFile->CurrentSymbol.type == LI_SEMIC) {
|
2021-01-20 19:22:15 +00:00
|
|
|
Tokenise();
|
2021-01-18 00:20:58 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2020-11-25 22:53:50 +00:00
|
|
|
|
2022-03-05 01:13:45 +00:00
|
|
|
CurrentFile->FunctionEntry = OldFunction;
|
2020-09-10 00:56:16 +00:00
|
|
|
|
2022-03-05 23:42:01 +00:00
|
|
|
CurrentFile->CurrentLoopDepth = 0;
|
2023-04-24 02:57:58 +00:00
|
|
|
VerifyToken(LI_LBRAC, "{");
|
2020-09-10 00:56:16 +00:00
|
|
|
Tree = ParseCompound();
|
2023-04-24 02:57:58 +00:00
|
|
|
VerifyToken(LI_RBRAC, "}");
|
2020-09-10 00:56:16 +00:00
|
|
|
|
2022-03-03 00:05:10 +00:00
|
|
|
if (Type != RET_VOID) {
|
2020-09-10 00:56:16 +00:00
|
|
|
// Functions with one statement have no composite node, so we have to check
|
|
|
|
FinalStatement = (Tree->Operation == OP_COMP) ? Tree->Right : Tree;
|
|
|
|
|
2022-03-03 00:05:10 +00:00
|
|
|
if (FinalStatement == NULL || FinalStatement->Operation != OP_RET) {
|
2020-09-10 00:56:16 +00:00
|
|
|
Die("Function with non-void type does not return");
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-01-20 01:05:41 +00:00
|
|
|
return ConstructASTBranch(OP_FUNC, Tree->ExprType, Tree, OldFunction, BreakLabel);
|
2020-09-10 00:56:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Handles the logic for return.
|
|
|
|
* //TODO: No brackets
|
|
|
|
* //TODO: Type inference
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct ASTNode* ReturnStatement() {
|
|
|
|
struct ASTNode* Tree;
|
2021-01-20 01:05:41 +00:00
|
|
|
int ReturnType;
|
2020-09-10 00:56:16 +00:00
|
|
|
|
|
|
|
|
2022-03-05 01:13:45 +00:00
|
|
|
if (CurrentFile->FunctionEntry->Type == RET_VOID)
|
2020-09-10 00:56:16 +00:00
|
|
|
Die("Attempt to return from void function");
|
2022-03-03 00:05:10 +00:00
|
|
|
|
2020-09-10 00:56:16 +00:00
|
|
|
VerifyToken(KW_RETURN, "return");
|
|
|
|
|
2023-04-24 02:03:31 +00:00
|
|
|
bool bracketed = OptionallyConsume(LI_LPARE);
|
2020-09-10 00:56:16 +00:00
|
|
|
|
|
|
|
Tree = ParsePrecedenceASTNode(0);
|
|
|
|
|
2022-03-05 01:13:45 +00:00
|
|
|
Tree = MutateType(Tree, CurrentFile->FunctionEntry->Type, 0);
|
2022-03-03 00:05:10 +00:00
|
|
|
if (Tree == NULL)
|
2020-09-10 00:56:16 +00:00
|
|
|
Die("Returning a value of incorrect type for function");
|
2022-03-03 00:05:10 +00:00
|
|
|
|
2020-11-15 06:40:05 +00:00
|
|
|
|
2022-03-05 01:13:45 +00:00
|
|
|
Tree = ConstructASTBranch(OP_RET, RET_NONE, Tree, CurrentFile->FunctionEntry, 0);
|
2020-09-10 00:56:16 +00:00
|
|
|
|
2022-03-05 01:13:45 +00:00
|
|
|
printf("\t\tReturning from function %s\n", CurrentFile->FunctionEntry->Name);
|
2020-09-10 00:56:16 +00:00
|
|
|
|
2023-04-24 02:03:31 +00:00
|
|
|
if (bracketed) VerifyToken(LI_RPARE, ")");
|
2020-09-10 00:56:16 +00:00
|
|
|
|
|
|
|
return Tree;
|
|
|
|
}
|
|
|
|
|
2021-01-20 19:22:15 +00:00
|
|
|
|
2020-09-10 00:56:16 +00:00
|
|
|
/*
|
2021-01-20 19:22:15 +00:00
|
|
|
* Handles the surrounding logic for If statements.
|
|
|
|
*
|
|
|
|
* If statements have the basic form:
|
|
|
|
* * if (condition) body
|
|
|
|
* * if (condition)
|
|
|
|
* body
|
|
|
|
* * if (condition) {
|
|
|
|
* body
|
|
|
|
* }
|
2020-09-10 00:56:16 +00:00
|
|
|
*
|
2021-01-20 19:22:15 +00:00
|
|
|
* Conditions may be any truthy statement (such as a pointer,
|
|
|
|
* object, integer), as conditions not recognized are auto-
|
|
|
|
* matically converted to booleans.
|
2020-09-10 00:56:16 +00:00
|
|
|
*
|
2021-01-20 19:22:15 +00:00
|
|
|
* This meaning, any object that can be resolved to 0 or NULL
|
|
|
|
* can be placed as the condition and used as a check.
|
|
|
|
*
|
|
|
|
* For example:
|
|
|
|
* struct ASTNode* Node = NULL;
|
|
|
|
* if(Node) {
|
|
|
|
* // This will not run, as Node is ((void*)0)
|
|
|
|
* }
|
2020-09-10 00:56:16 +00:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
struct ASTNode* IfStatement() {
|
2022-03-03 00:05:10 +00:00
|
|
|
struct ASTNode* Condition, * True, * False = NULL;
|
2020-09-10 00:56:16 +00:00
|
|
|
|
|
|
|
VerifyToken(KW_IF, "if");
|
|
|
|
VerifyToken(LI_LPARE, "(");
|
|
|
|
|
|
|
|
Condition = ParsePrecedenceASTNode(0);
|
|
|
|
|
|
|
|
// Limit if(x) to =? != < > <= =>
|
|
|
|
// No null checking, no arithmetic, no functions.
|
|
|
|
// TODO: this
|
2022-03-03 00:05:10 +00:00
|
|
|
if (Condition->Operation < OP_EQUAL || Condition->Operation > OP_GREATE)
|
2021-01-20 01:05:41 +00:00
|
|
|
Condition = ConstructASTBranch(OP_BOOLCONV, Condition->ExprType, Condition, NULL, 0);
|
2022-03-03 00:05:10 +00:00
|
|
|
|
2020-09-10 00:56:16 +00:00
|
|
|
VerifyToken(LI_RPARE, ")");
|
|
|
|
|
2022-03-06 01:52:37 +00:00
|
|
|
True = ParseStatement();
|
2020-09-10 00:56:16 +00:00
|
|
|
|
2022-03-05 01:13:45 +00:00
|
|
|
if (CurrentFile->CurrentSymbol.type == KW_ELSE) {
|
2021-01-20 19:22:15 +00:00
|
|
|
Tokenise();
|
2022-03-06 01:52:37 +00:00
|
|
|
False = ParseStatement();
|
2020-09-10 00:56:16 +00:00
|
|
|
}
|
|
|
|
|
2021-01-20 01:05:41 +00:00
|
|
|
return ConstructASTNode(OP_IF, RET_NONE, Condition, True, False, NULL, 0);
|
2020-09-10 00:56:16 +00:00
|
|
|
}
|
|
|
|
|
2021-01-20 19:22:15 +00:00
|
|
|
/*
|
|
|
|
* Handles the surrounding logic for While loops.
|
|
|
|
*
|
|
|
|
* While loops have the basic form:
|
|
|
|
* while ( condition ) { body }
|
|
|
|
*
|
|
|
|
* When reaching the condition (which alike an If statement,
|
|
|
|
* can be any truthy value), if it resolves to true:
|
|
|
|
* The body is executed, and immediately the condition is checked
|
|
|
|
* again.
|
|
|
|
* This repeats until the condition resolves false, at which point
|
|
|
|
* the loop executes no more.
|
|
|
|
*
|
|
|
|
* This can be prototyped as the following pseudo-assembler:
|
|
|
|
*
|
|
|
|
* cond:
|
|
|
|
* check <condition>
|
|
|
|
* jne exit
|
|
|
|
* <body>
|
|
|
|
* jump cond
|
|
|
|
* exit:
|
|
|
|
* <more code>
|
|
|
|
*
|
|
|
|
* @return the AST of this statement
|
|
|
|
*
|
|
|
|
*/
|
2020-09-10 00:56:16 +00:00
|
|
|
struct ASTNode* WhileStatement() {
|
2022-03-03 00:05:10 +00:00
|
|
|
struct ASTNode* Condition, * Body;
|
2020-09-10 00:56:16 +00:00
|
|
|
|
|
|
|
VerifyToken(KW_WHILE, "while");
|
|
|
|
VerifyToken(LI_LPARE, "(");
|
|
|
|
|
|
|
|
Condition = ParsePrecedenceASTNode(0);
|
|
|
|
|
2022-03-03 00:05:10 +00:00
|
|
|
if (Condition->Operation < OP_EQUAL || Condition->Operation > OP_GREATE)
|
2021-01-20 01:05:41 +00:00
|
|
|
Condition = ConstructASTBranch(OP_BOOLCONV, Condition->ExprType, Condition, NULL, 0);
|
2022-03-03 00:05:10 +00:00
|
|
|
|
2020-09-10 00:56:16 +00:00
|
|
|
VerifyToken(LI_RPARE, ")");
|
|
|
|
|
2022-03-05 23:42:01 +00:00
|
|
|
CurrentFile->CurrentLoopDepth++;
|
2022-03-06 01:52:37 +00:00
|
|
|
Body = ParseStatement();
|
2022-03-05 23:42:01 +00:00
|
|
|
CurrentFile->CurrentLoopDepth--;
|
2020-09-10 00:56:16 +00:00
|
|
|
|
2021-01-20 01:05:41 +00:00
|
|
|
return ConstructASTNode(OP_LOOP, RET_NONE, Condition, NULL, Body, NULL, 0);
|
2020-09-10 00:56:16 +00:00
|
|
|
}
|
|
|
|
|
2021-01-20 19:22:15 +00:00
|
|
|
/*
|
|
|
|
* Handles the surrounding logic for For loops.
|
|
|
|
*
|
|
|
|
* They have the basic form of:
|
|
|
|
* for ( init ; condition; iterator) { body }
|
|
|
|
*
|
|
|
|
* The initialiser is run only once upon reaching the for loop.
|
|
|
|
* Then the condition is checked, and if true, the body is executed.
|
|
|
|
* After execution of the body, the iterator is run and the condition
|
|
|
|
* checked again.
|
|
|
|
*
|
|
|
|
* It can be prototyped as the following pseudo-assembler code:
|
|
|
|
*
|
|
|
|
* for:
|
|
|
|
* <init>
|
|
|
|
* cond:
|
|
|
|
* check <condition>
|
|
|
|
* jne exit
|
|
|
|
* <body>
|
|
|
|
* <iterator>
|
|
|
|
* jump cond
|
|
|
|
* exit:
|
|
|
|
* <loop exit>
|
|
|
|
*
|
|
|
|
* In the case of the implementation, "init" is the preoperator,
|
|
|
|
* "iterator" is the postoperator.
|
|
|
|
*
|
|
|
|
* @return the AST of this statement
|
|
|
|
*/
|
2020-09-10 00:56:16 +00:00
|
|
|
struct ASTNode* ForStatement() {
|
2022-03-03 00:05:10 +00:00
|
|
|
struct ASTNode* Condition, * Body;
|
|
|
|
struct ASTNode* Preop, * Postop;
|
2020-09-10 00:56:16 +00:00
|
|
|
|
|
|
|
struct ASTNode* Tree;
|
|
|
|
|
|
|
|
VerifyToken(KW_FOR, "for");
|
|
|
|
VerifyToken(LI_LPARE, "(");
|
|
|
|
|
|
|
|
Preop = ParseStatement();
|
|
|
|
VerifyToken(LI_SEMIC, ";");
|
|
|
|
|
|
|
|
Condition = ParsePrecedenceASTNode(0);
|
|
|
|
|
2022-03-03 00:05:10 +00:00
|
|
|
if (Condition->Operation < OP_EQUAL || Condition->Operation > OP_GREATE)
|
2021-01-20 01:05:41 +00:00
|
|
|
Condition = ConstructASTBranch(OP_BOOLCONV, Condition->ExprType, Condition, NULL, 0);
|
2020-11-24 13:17:01 +00:00
|
|
|
|
2020-09-10 00:56:16 +00:00
|
|
|
VerifyToken(LI_SEMIC, ";");
|
|
|
|
|
|
|
|
Postop = ParseStatement();
|
|
|
|
VerifyToken(LI_RPARE, ")");
|
|
|
|
|
2022-03-05 23:42:01 +00:00
|
|
|
CurrentFile->CurrentLoopDepth++;
|
2022-03-06 01:52:37 +00:00
|
|
|
Body = ParseStatement();
|
2022-03-05 23:42:01 +00:00
|
|
|
CurrentFile->CurrentLoopDepth--;
|
2020-09-10 00:56:16 +00:00
|
|
|
|
|
|
|
// We need to be able to skip over the body and the postop, so we group them together.
|
2021-01-20 01:05:41 +00:00
|
|
|
Tree = ConstructASTNode(OP_COMP, RET_NONE, Body, NULL, Postop, NULL, 0);
|
2020-09-10 00:56:16 +00:00
|
|
|
// We need to be able to jump to the top of the condition and fall through to the body,
|
|
|
|
// so we group it with the last block
|
2021-01-20 01:05:41 +00:00
|
|
|
Tree = ConstructASTNode(OP_LOOP, RET_NONE, Condition, NULL, Tree, NULL, 0);
|
2020-09-10 00:56:16 +00:00
|
|
|
|
|
|
|
// We need to append the postop to the loop, to form the final for loop
|
2021-01-20 01:05:41 +00:00
|
|
|
return ConstructASTNode(OP_COMP, RET_NONE, Preop, NULL, Tree, NULL, 0);
|
2020-09-10 00:56:16 +00:00
|
|
|
}
|
|
|
|
|
2021-01-20 19:22:15 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Handles the surrounding logic for the Print statement.
|
|
|
|
*
|
|
|
|
* This is a legacy hold-over from the early testing, and it
|
2022-03-03 00:05:10 +00:00
|
|
|
* serves merely as a wrapper around the cstdlib printf.er function.
|
2021-01-20 19:22:15 +00:00
|
|
|
*
|
|
|
|
* It does, however (//TODO), attempt to guess the type that you
|
|
|
|
* want to print, which takes a lot of the guesswork out of printing.
|
|
|
|
*
|
|
|
|
* @return the AST of this statement
|
|
|
|
*/
|
2020-09-10 00:56:16 +00:00
|
|
|
struct ASTNode* PrintStatement(void) {
|
|
|
|
struct ASTNode* Tree;
|
|
|
|
int LeftType, RightType;
|
|
|
|
|
|
|
|
VerifyToken(KW_PRINT, "print");
|
|
|
|
|
|
|
|
Tree = ParsePrecedenceASTNode(0);
|
|
|
|
|
|
|
|
LeftType = RET_INT;
|
|
|
|
RightType = Tree->ExprType;
|
|
|
|
|
2020-09-14 01:05:24 +00:00
|
|
|
Tree = MutateType(Tree, RightType, 0);
|
2022-03-03 00:05:10 +00:00
|
|
|
if (!Tree)
|
2020-09-10 00:56:16 +00:00
|
|
|
DieDecimal("Attempting to print an invalid type:", RightType);
|
2022-03-03 00:05:10 +00:00
|
|
|
|
|
|
|
if (RightType)
|
2021-01-20 19:22:15 +00:00
|
|
|
Tree = ConstructASTBranch(Tree->Right->Operation, RET_INT, Tree, NULL, 0);
|
2022-03-03 00:05:10 +00:00
|
|
|
|
2021-01-20 01:05:41 +00:00
|
|
|
Tree = ConstructASTBranch(OP_PRINT, RET_NONE, Tree, NULL, 0);
|
2020-09-10 00:56:16 +00:00
|
|
|
|
|
|
|
//ParseAST(Tree);
|
|
|
|
|
|
|
|
return Tree;
|
2022-03-03 00:05:10 +00:00
|
|
|
|
2020-09-13 01:26:49 +00:00
|
|
|
}
|
|
|
|
|
2023-04-24 02:03:31 +00:00
|
|
|
struct ASTNode* SwitchStatement() {
|
|
|
|
struct ASTNode* left, *root, *c, *casetree=NULL, *casetail;
|
|
|
|
int looping=1, cases=0;
|
|
|
|
int defaultpresent=0;
|
|
|
|
int ASTOp, casevalue;
|
|
|
|
|
|
|
|
printf("\tParsing switch statement\n");
|
2023-04-24 02:57:58 +00:00
|
|
|
CurrentFile->SwitchStatement = true;
|
2023-04-24 02:03:31 +00:00
|
|
|
|
|
|
|
// Skip switch(
|
|
|
|
Tokenise();
|
|
|
|
VerifyToken(LI_LPARE, "(");
|
|
|
|
|
|
|
|
printf("\tSwitch: Reading switch expression\n");
|
|
|
|
// Fetch switch expression
|
|
|
|
left = ParsePrecedenceASTNode(0);
|
|
|
|
// Consume ) {
|
|
|
|
VerifyToken(LI_RPARE, ")");
|
|
|
|
VerifyToken(LI_LBRAC, "{");
|
|
|
|
|
|
|
|
// Verify the switch expression (must be integer-compatible)
|
|
|
|
if (!TypeIsInt(!left->ExprType))
|
|
|
|
Die("Switch expression is not of integer type");
|
|
|
|
|
|
|
|
Safe();
|
|
|
|
|
|
|
|
// Create the root Switch node
|
|
|
|
root = ConstructASTBranch(OP_SWITCH, 0, left, NULL, 0);
|
|
|
|
|
|
|
|
// Iterate down the switch node, generating cases
|
|
|
|
while (looping) {
|
|
|
|
switch (CurrentFile->CurrentSymbol.type) {
|
|
|
|
case LI_RBRAC:
|
|
|
|
if (cases == 0)
|
|
|
|
Die("No cases in switch statement");
|
|
|
|
looping = 0;
|
|
|
|
break;
|
|
|
|
case KW_CASE:
|
|
|
|
if (defaultpresent)
|
|
|
|
Die("Case present after default in switch.");
|
|
|
|
|
|
|
|
ASTOp = OP_CASE;
|
|
|
|
|
|
|
|
Safe();
|
|
|
|
Tokenise();
|
|
|
|
|
|
|
|
// Parse case value
|
|
|
|
left = ParsePrecedenceASTNode(0);
|
|
|
|
if (left->Operation != TERM_INTLITERAL)
|
|
|
|
Die("Expecting integer literal for case value");
|
|
|
|
casevalue = left->IntValue;
|
|
|
|
printf("\t\tSwitch case %d found\n", casevalue);
|
|
|
|
|
|
|
|
// Make sure nothing resolves to the same case value
|
|
|
|
for (c = casetree; c != NULL; c = c->Right)
|
|
|
|
if (casevalue == c->IntValue)
|
|
|
|
Die("Duplicate case ID in switch statement");
|
|
|
|
// Fallthrough so that we get the case tree logic deduplicated
|
|
|
|
case KW_DEFAULT:
|
|
|
|
if (defaultpresent)
|
|
|
|
Die("Duplicate default entries in switch");
|
|
|
|
// Duplicate check because CASE falls through into this block
|
|
|
|
if (CurrentFile->CurrentSymbol.type == KW_DEFAULT) {
|
|
|
|
ASTOp = OP_DEFAULT;
|
|
|
|
defaultpresent = true;
|
|
|
|
Tokenise();
|
|
|
|
|
|
|
|
printf("\t\tSwitch default case found\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
VerifyToken(LI_COLON, ":");
|
|
|
|
Safe();
|
|
|
|
|
2023-04-24 02:57:58 +00:00
|
|
|
left = ParseCompound();
|
2023-04-24 02:03:31 +00:00
|
|
|
OptionallyConsume(LI_SEMIC);
|
|
|
|
cases++;
|
|
|
|
Safe();
|
|
|
|
|
|
|
|
// Append this new case to the tree
|
|
|
|
if (casetree == NULL) {
|
|
|
|
casetree = casetail = ConstructASTBranch(ASTOp, 0, left, NULL, casevalue);
|
|
|
|
} else {
|
|
|
|
casetail->Right = ConstructASTBranch(ASTOp, 0, left, NULL, casevalue);
|
|
|
|
casetail = casetail->Right;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
ErrorReport("Unexpected token in switch statement: %s\n", TokenNames[CurrentFile->CurrentSymbol.type]);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
root->IntValue = cases;
|
|
|
|
root->Right = casetree;
|
|
|
|
|
|
|
|
// Consume the right brace immediately
|
|
|
|
VerifyToken(LI_RBRAC, "}");
|
|
|
|
|
2023-04-24 02:57:58 +00:00
|
|
|
CurrentFile->SwitchStatement = false;
|
2023-04-24 02:03:31 +00:00
|
|
|
return root;
|
|
|
|
}
|
|
|
|
|
2022-03-05 23:42:01 +00:00
|
|
|
/**
|
|
|
|
* Handles the surrounding logic for break statements
|
|
|
|
*
|
|
|
|
* They have the basic form of:
|
|
|
|
* break;
|
|
|
|
*
|
|
|
|
* If there is a loop currently being evaluated, break will insert an immediate jump to the end of the loop.
|
|
|
|
* All locals inside the loop will lose their binding at this point.
|
|
|
|
*
|
|
|
|
* It can be prototyped as the following pseudo-assembler code:
|
|
|
|
*
|
|
|
|
* while:
|
|
|
|
* check <condition>
|
|
|
|
* jne exit
|
|
|
|
* <body>
|
|
|
|
* <break>: jump exit
|
|
|
|
* jump while
|
|
|
|
* exit:
|
|
|
|
* <loop exit>
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* @return the AST of this statement
|
|
|
|
*/
|
|
|
|
struct ASTNode* BreakStatement() {
|
2023-04-24 02:57:58 +00:00
|
|
|
if (CurrentFile->CurrentLoopDepth == 0 && !CurrentFile->SwitchStatement)
|
|
|
|
Die("Unable to break without a loop or switch statement");
|
2022-03-05 23:42:01 +00:00
|
|
|
|
|
|
|
Tokenise();
|
|
|
|
|
|
|
|
return ConstructASTLeaf(OP_BREAK, 0, NULL, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handles the surrounding logic for continue statements
|
|
|
|
*
|
|
|
|
* They have the basic form of:
|
|
|
|
* continue;
|
|
|
|
*
|
|
|
|
* If there is a loop currently being evaluated, continue will insert an immediate jump to the start of the loop.
|
|
|
|
*
|
|
|
|
* It can be prototyped as the following pseudo-assembler code:
|
|
|
|
*
|
|
|
|
* while:
|
|
|
|
* check <condition>
|
|
|
|
* jne exit
|
|
|
|
* <body>
|
|
|
|
* <continue>: jump while
|
|
|
|
* jump while
|
|
|
|
* exit:
|
|
|
|
* <loop exit>
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* @return the AST of this statement
|
|
|
|
*/
|
|
|
|
struct ASTNode* ContinueStatement() {
|
|
|
|
if (CurrentFile->CurrentLoopDepth == 0)
|
|
|
|
Die("Unable to break without a loop");
|
|
|
|
|
|
|
|
Tokenise();
|
|
|
|
|
|
|
|
return ConstructASTLeaf(OP_CONTINUE, 0, NULL, 0);
|
|
|
|
}
|
|
|
|
|
2021-01-20 19:22:15 +00:00
|
|
|
/*
|
|
|
|
* Handles the surrounding logic for all of the logical and semantic
|
|
|
|
* postfixes.
|
|
|
|
*
|
|
|
|
* Postfixes are tokens that are affixed to the end of another, and
|
|
|
|
* change behaviour in some way. These can be added calculations,
|
|
|
|
* some form of transformation, or other.
|
|
|
|
*
|
|
|
|
* A current list of postfixes:
|
|
|
|
* * (): Call a function
|
|
|
|
* * []: Index or define an array.
|
|
|
|
* * ++: Increment a variable AFTER it is returned
|
|
|
|
* NOTE: there is a prefix variant of this for incrementing BEFOREhand.
|
|
|
|
* * --: Decrement a variable AFTER it is returned
|
|
|
|
* NOTE: there is a prefix variant of this for decrementing BEFOREhand.
|
|
|
|
*
|
|
|
|
* Planned postfixes:
|
|
|
|
* * >>: Arithmetic-Shift-Right a variable by one (Divide by two)
|
|
|
|
* NOTE: there is a prefix variant of this for shifting left - multiplying by two.
|
|
|
|
*
|
|
|
|
* @return the AST of the statement plus its' postfix
|
|
|
|
*/
|
2020-11-23 21:42:32 +00:00
|
|
|
struct ASTNode* PostfixStatement() {
|
|
|
|
struct ASTNode* Tree;
|
2021-01-20 01:05:41 +00:00
|
|
|
struct SymbolTableEntry* Entry;
|
2020-11-23 21:42:32 +00:00
|
|
|
|
2022-03-04 01:11:04 +00:00
|
|
|
// Early exit if we find an enum value
|
|
|
|
if ((Entry = FindEnumMember(CurrentIdentifier)) != NULL) {
|
|
|
|
Tokenise();
|
|
|
|
return ConstructASTLeaf(TERM_INTLITERAL, RET_INT, NULL, Entry->IntValue);
|
|
|
|
}
|
|
|
|
|
2021-01-20 19:22:15 +00:00
|
|
|
Tokenise();
|
2022-03-03 00:05:10 +00:00
|
|
|
|
2022-03-05 01:13:45 +00:00
|
|
|
if (CurrentFile->CurrentSymbol.type == LI_LPARE)
|
2020-11-23 21:42:32 +00:00
|
|
|
return CallFunction();
|
2022-03-03 00:05:10 +00:00
|
|
|
|
2022-03-05 01:13:45 +00:00
|
|
|
if (CurrentFile->CurrentSymbol.type == LI_LBRAS)
|
2020-11-23 21:42:32 +00:00
|
|
|
return AccessArray();
|
2022-03-03 00:05:10 +00:00
|
|
|
|
2020-11-23 21:42:32 +00:00
|
|
|
// If we get here, we must be a variable.
|
2021-01-20 19:22:15 +00:00
|
|
|
// (as functions have been called and arrays have been indexed)
|
|
|
|
// Check that the variable is recognized..
|
2020-11-23 21:42:32 +00:00
|
|
|
|
2022-03-03 00:05:10 +00:00
|
|
|
if ((Entry = FindSymbol(CurrentIdentifier)) == NULL ||
|
|
|
|
(Entry->Structure != ST_VAR && Entry->Structure != ST_FUNC)) {
|
2021-03-15 01:22:47 +00:00
|
|
|
DumpAllLists();
|
2020-11-23 21:42:32 +00:00
|
|
|
DieMessage("Unknown Variable", CurrentIdentifier);
|
2021-03-15 01:22:47 +00:00
|
|
|
}
|
2020-11-23 21:42:32 +00:00
|
|
|
|
|
|
|
// Here we check for postincrement and postdecrement.
|
|
|
|
|
2022-03-05 01:13:45 +00:00
|
|
|
switch (CurrentFile->CurrentSymbol.type) {
|
2021-07-04 23:07:04 +00:00
|
|
|
case LI_DOT:
|
|
|
|
return AccessMember(false);
|
|
|
|
case LI_ARROW:
|
|
|
|
return AccessMember(true);
|
2020-11-23 21:42:32 +00:00
|
|
|
case PPMM_PLUS:
|
2021-01-20 19:22:15 +00:00
|
|
|
Tokenise();
|
2021-01-20 01:05:41 +00:00
|
|
|
Tree = ConstructASTLeaf(OP_POSTINC, Entry->Type, Entry, 0);
|
2020-11-23 21:42:32 +00:00
|
|
|
break;
|
|
|
|
case PPMM_MINUS:
|
2021-01-20 19:22:15 +00:00
|
|
|
Tokenise();
|
2021-01-20 01:05:41 +00:00
|
|
|
Tree = ConstructASTLeaf(OP_POSTDEC, Entry->Type, Entry, 0);
|
2020-11-23 21:42:32 +00:00
|
|
|
break;
|
|
|
|
default:
|
2021-01-20 01:05:41 +00:00
|
|
|
Tree = ConstructASTLeaf(REF_IDENT, Entry->Type, Entry, 0);
|
2020-11-23 21:42:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return Tree;
|
2022-03-03 00:05:10 +00:00
|
|
|
|
2020-11-23 21:42:32 +00:00
|
|
|
}
|
|
|
|
|
2021-01-20 19:22:15 +00:00
|
|
|
/*
|
|
|
|
* Handles the surrounding logic for all of the logical and semantic
|
|
|
|
* prefixes.
|
|
|
|
*
|
|
|
|
* Prefixes are tokens that are affixed to the start of another, and
|
|
|
|
* change behaviour in some way. These can be added calculations,
|
|
|
|
* some form of transformation, or other.
|
|
|
|
*
|
|
|
|
* A current list of prefixes:
|
|
|
|
* * !: Invert the boolean result of a statement or truthy value.
|
|
|
|
* * ~: Invert the individual bits in a number
|
|
|
|
* * -: Invert the number around the axis of 0 (negative->positive, positive->negative)
|
|
|
|
* * ++: Increment a variable BEFORE it is returned.
|
|
|
|
* NOTE: there is a postfix variant of this for incrementing AFTER the fact.
|
|
|
|
* * --: Decrement a variable BEFORE it is returned.
|
|
|
|
* NOTE: there is a postfix variant of this for decrementing AFTER the fact.
|
|
|
|
* * &: Dereference the following object (Get the address that contains it)
|
|
|
|
* * *: Get the object pointed at by the number following
|
|
|
|
*
|
|
|
|
* Planned prefixes:
|
|
|
|
* * <<: Arithmetic-Shift-Left a variable by one (Multiply by two)
|
|
|
|
* NOTE: there is a postfix variant of this for shifting right - dividing by two.
|
|
|
|
*
|
|
|
|
* @return the AST of this statement, plus its' prefixes and any postfixes.
|
|
|
|
*/
|
2020-09-13 01:26:49 +00:00
|
|
|
struct ASTNode* PrefixStatement() {
|
|
|
|
struct ASTNode* Tree;
|
|
|
|
|
2022-03-05 01:13:45 +00:00
|
|
|
switch (CurrentFile->CurrentSymbol.type) {
|
2020-11-23 21:42:32 +00:00
|
|
|
case BOOL_INVERT:
|
2021-01-20 19:22:15 +00:00
|
|
|
Tokenise();
|
2020-11-23 21:42:32 +00:00
|
|
|
Tree = PrefixStatement();
|
|
|
|
Tree->RVal = 1;
|
2021-01-20 01:05:41 +00:00
|
|
|
Tree = ConstructASTBranch(OP_BOOLNOT, Tree->ExprType, Tree, NULL, 0);
|
2020-11-23 21:42:32 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case BIT_NOT:
|
2021-01-20 19:22:15 +00:00
|
|
|
Tokenise();
|
2020-11-23 21:42:32 +00:00
|
|
|
Tree = PrefixStatement();
|
|
|
|
Tree->RVal = 1;
|
2021-01-20 01:05:41 +00:00
|
|
|
Tree = ConstructASTBranch(OP_BITNOT, Tree->ExprType, Tree, NULL, 0);
|
2020-11-23 21:42:32 +00:00
|
|
|
break;
|
2022-03-03 00:05:10 +00:00
|
|
|
|
2020-11-24 13:17:01 +00:00
|
|
|
case AR_MINUS:
|
2021-01-20 19:22:15 +00:00
|
|
|
Tokenise();
|
2020-11-24 13:17:01 +00:00
|
|
|
Tree = PrefixStatement();
|
2022-03-03 00:05:10 +00:00
|
|
|
|
2021-01-20 01:05:41 +00:00
|
|
|
Tree = ConstructASTBranch(OP_NEGATE, Tree->ExprType, Tree, NULL, 0);
|
2020-11-24 13:17:01 +00:00
|
|
|
break;
|
2020-11-23 21:42:32 +00:00
|
|
|
|
|
|
|
case PPMM_PLUS:
|
2021-01-20 19:22:15 +00:00
|
|
|
Tokenise();
|
2020-11-23 21:42:32 +00:00
|
|
|
Tree = PrefixStatement();
|
|
|
|
|
2022-03-03 00:05:10 +00:00
|
|
|
if (Tree->Operation != REF_IDENT)
|
2020-11-23 21:42:32 +00:00
|
|
|
Die("++ not followed by identifier");
|
2021-01-20 01:05:41 +00:00
|
|
|
Tree = ConstructASTBranch(OP_PREINC, Tree->ExprType, Tree, NULL, 0);
|
2020-11-23 21:42:32 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case PPMM_MINUS:
|
2021-01-20 19:22:15 +00:00
|
|
|
Tokenise();
|
2020-11-23 21:42:32 +00:00
|
|
|
Tree = PrefixStatement();
|
|
|
|
|
2022-03-03 00:05:10 +00:00
|
|
|
if (Tree->Operation != REF_IDENT)
|
2020-11-23 21:42:32 +00:00
|
|
|
Die("-- not followed by identifier");
|
2022-03-03 00:05:10 +00:00
|
|
|
|
2021-01-20 01:05:41 +00:00
|
|
|
Tree = ConstructASTBranch(OP_PREDEC, Tree->ExprType, Tree, NULL, 0);
|
2020-11-23 21:42:32 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case BIT_AND:
|
2021-01-20 19:22:15 +00:00
|
|
|
Tokenise();
|
2020-09-13 01:26:49 +00:00
|
|
|
|
|
|
|
// To allow things like:
|
|
|
|
// x = &&y;
|
|
|
|
// We need to recursively parse prefixes;
|
|
|
|
Tree = PrefixStatement();
|
|
|
|
|
2022-03-03 00:05:10 +00:00
|
|
|
if (Tree->Operation != REF_IDENT)
|
2020-09-13 01:26:49 +00:00
|
|
|
Die("& must be followed by another & or an identifier.");
|
|
|
|
|
|
|
|
Tree->Operation = OP_ADDRESS;
|
|
|
|
Tree->ExprType = PointerTo(Tree->ExprType);
|
|
|
|
break;
|
|
|
|
case AR_STAR:
|
2021-01-20 19:22:15 +00:00
|
|
|
Tokenise();
|
2020-09-13 01:26:49 +00:00
|
|
|
|
|
|
|
Tree = PrefixStatement();
|
|
|
|
|
2022-03-03 00:05:10 +00:00
|
|
|
if (Tree->Operation != REF_IDENT && Tree->Operation != OP_DEREF)
|
2020-09-13 01:26:49 +00:00
|
|
|
Die("* must be followed by another * or an identifier.");
|
2020-11-15 06:40:05 +00:00
|
|
|
|
2021-01-20 01:05:41 +00:00
|
|
|
Tree = ConstructASTBranch(OP_DEREF, ValueAt(Tree->ExprType), Tree, NULL, 0);
|
2020-09-13 01:26:49 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
Tree = ParsePrimary();
|
2022-03-03 00:05:10 +00:00
|
|
|
|
2020-09-13 01:26:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return Tree;
|
2020-09-10 00:56:16 +00:00
|
|
|
}
|