Erythro/src/Statements.c

473 lines
13 KiB
C
Raw Normal View History

/*************/
/*GEMWIRE */
/* ERYTHRO*/
/*************/
#include <Defs.h>
#include <Data.h>
#include <stdbool.h>
static int ReadParameters(struct SymbolTableEntry* FunctionSymbol) {
int TokenType, ParamCount = 0;
struct SymbolTableEntry* PrototypePointer = NULL;
if(FunctionSymbol != NULL)
PrototypePointer = FunctionSymbol->Start;
while(CurrentToken.type != LI_RPARE) {
TokenType = ParseOptionalPointer();
VerifyToken(TY_IDENTIFIER, "identifier");
if(PrototypePointer != NULL) {
if(TokenType != PrototypePointer->Type)
DieDecimal("Function paramater of invalid type at index", ParamCount + 1);
PrototypePointer=PrototypePointer->NextSymbol;
} else {
BeginVariableDeclaration(TokenType, SC_PARAM);
}
ParamCount++;
switch(CurrentToken.type) {
case LI_COM:
Tokenise(&CurrentToken);
break;
case LI_RPARE:
break;
default:
DieDecimal("Unexpected token in parameter", CurrentToken.type);
}
}
if((FunctionSymbol != NULL) && (ParamCount != FunctionSymbol->Length))
DieMessage("Invalid number of parameters in prototyped function", FunctionSymbol->Name);
return ParamCount;
}
/*
* Handles the declaration of a type of a variable.
* int newVar;
*
* It verifies that we have a type keyword followed by a
* unique, non-keyword identifier.
*
* It then stores this variable into the symbol table,
* and returns the new item.
*
*/
struct SymbolTableEntry* BeginVariableDeclaration(int Type, int Scope) {
struct SymbolTableEntry* Symbol = NULL;
switch(Scope) {
case SC_GLOBAL:
if(FindGlobal(CurrentIdentifier) != NULL)
DieMessage("Invalid redeclaration of global variable", CurrentIdentifier);
case SC_LOCAL:
case SC_PARAM:
if(FindLocal(CurrentIdentifier) != NULL)
DieMessage("Invalid redelcaration of local variable", CurrentIdentifier);
}
2020-11-18 20:49:08 +00:00
if(CurrentToken.type == LI_LBRAS) {
Tokenise(&CurrentToken);
2020-11-18 20:49:08 +00:00
if(CurrentToken.type == LI_INT) {
switch(Scope) {
case SC_GLOBAL:
Symbol = AddSymbol(CurrentIdentifier, PointerTo(Type), ST_ARR, Scope, 1, 0);
break;
case SC_LOCAL:
case SC_PARAM:
Die("Local arrays are unimplemented");
}
}
2020-11-18 20:49:08 +00:00
Tokenise(&CurrentToken);
VerifyToken(LI_RBRAS, "]");
} else {
Symbol = AddSymbol(CurrentIdentifier, Type, ST_VAR, Scope, 1, 0);
}
2020-11-18 20:49:08 +00:00
return Symbol;
}
struct ASTNode* ParseFunction(int Type) {
struct ASTNode* Tree;
struct ASTNode* FinalStatement;
struct SymbolTableEntry* OldFunction, *NewFunction = NULL;
int SymbolSlot, BreakLabel, ParamCount, ID;
if((OldFunction = FindSymbol(CurrentIdentifier)) != NULL)
if(OldFunction->Storage != ST_FUNC)
OldFunction = NULL;
if(OldFunction == NULL) {
BreakLabel = NewLabel();
NewFunction = AddSymbol(CurrentIdentifier, Type, ST_FUNC, SC_GLOBAL, BreakLabel, 0);
}
VerifyToken(LI_LPARE, "(");
ParamCount = ReadParameters(OldFunction);
VerifyToken(LI_RPARE, ")");
if(NewFunction) {
NewFunction->Elements = ParamCount;
NewFunction->Start = Params;
OldFunction = NewFunction;
}
Params = ParamsEnd = NULL;
if(CurrentToken.type == LI_SEMIC) {
Tokenise(&CurrentToken);
return NULL;
}
printf("\nIdentified%sfunction %s of return type %s, end label %d\n", (OldFunction == NULL) ? " new " : " overloaded ", OldFunction->Name, TypeNames(Type), BreakLabel);
FunctionEntry = OldFunction;
Tree = ParseCompound();
if(Type != RET_VOID) {
// Functions with one statement have no composite node, so we have to check
FinalStatement = (Tree->Operation == OP_COMP) ? Tree->Right : Tree;
if(FinalStatement == NULL || FinalStatement->Operation != OP_RET) {
Die("Function with non-void type does not return");
}
}
return ConstructASTBranch(OP_FUNC, Tree->ExprType, Tree, OldFunction, BreakLabel);
}
/*
* Handles the logic for return.
* //TODO: No brackets
* //TODO: Type inference
*
*
*/
struct ASTNode* ReturnStatement() {
struct ASTNode* Tree;
int ReturnType;
if(FunctionEntry->Type == RET_VOID)
Die("Attempt to return from void function");
VerifyToken(KW_RETURN, "return");
VerifyToken(LI_LPARE, "("); // TODO: Make optional! Reject?
Tree = ParsePrecedenceASTNode(0);
/*
ReturnType = Tree->ExprType;
FunctionType = Symbols[CurrentFunction].Type;
*/
Tree = MutateType(Tree, FunctionEntry->Type, 0);
if(Tree == NULL)
Die("Returning a value of incorrect type for function");
/*
if(ReturnType)
Tree = ConstructASTBranch(ReturnType, FunctionType, Tree, 0);
*/
Tree = ConstructASTBranch(OP_RET, RET_NONE, Tree, FunctionEntry, 0);
printf("\t\tReturning from function %s\n", FunctionEntry->Name);
VerifyToken(LI_RPARE, ")"); // TODO: OPTIONALISE!
return Tree;
}
/*
2020-09-14 01:05:24 +00:00
* Handles Identifiers.
*
2020-09-14 01:05:24 +00:00
* This is called for any of:
* - Calling a function
* - Assigning an lvalue variable
* - Performing arithmetic on a variable
* - Performing arithmetic with the return values of function calls
*
2020-09-14 01:05:24 +00:00
* For the case where you're assigning an l-value;
* You can assign with another assignment,
* a statement, a function or a literal.
*
*/
/*
struct ASTNode* ParseIdentifier() {
struct ASTNode* Left, *Right, *Tree;
int LeftType, RightType;
int ID;
VerifyToken(TY_IDENTIFIER, "ident");
printf("\t\tAfter parsing, the identifier name is %s, id %d in the symbol table.\n", CurrentIdentifier, FindSymbol(CurrentIdentifier));
if(CurrentToken.type == LI_LPARE)
return CallFunction();
if((ID = FindSymbol(CurrentIdentifier)) == -1) {
printf("Symbol %s not in table. Table contents: %s, %s\n", CurrentIdentifier, Symbols[0].Name, Symbols[1].Name);
DieMessage("Undeclared Variable ", CurrentIdentifier);
}
Right = ConstructASTLeaf(LV_IDENT, Symbols[ID].Type, ID);
VerifyToken(LI_EQUAL, "=");
Left = ParsePrecedenceASTNode(0);
LeftType = Left->ExprType;
RightType = Right->ExprType;
2020-09-14 01:05:24 +00:00
Left = MutateType(Left, RightType, 0);
if(!Left)
Die("Incompatible types in assignment");
if(LeftType)
Left = ConstructASTBranch(LeftType, Right->ExprType, Left, 0);
Tree = ConstructASTNode(OP_ASSIGN, RET_INT, Left, NULL, Right, 0);
return Tree;
}*/
struct ASTNode* IfStatement() {
struct ASTNode* Condition, *True, *False = NULL;
VerifyToken(KW_IF, "if");
VerifyToken(LI_LPARE, "(");
Condition = ParsePrecedenceASTNode(0);
// Limit if(x) to =? != < > <= =>
// No null checking, no arithmetic, no functions.
// TODO: this
if(Condition->Operation < OP_EQUAL || Condition->Operation > OP_GREATE)
Condition = ConstructASTBranch(OP_BOOLCONV, Condition->ExprType, Condition, NULL, 0);
VerifyToken(LI_RPARE, ")");
True = ParseCompound();
if(CurrentToken.type == KW_ELSE) {
Tokenise(&CurrentToken);
False = ParseCompound();
}
return ConstructASTNode(OP_IF, RET_NONE, Condition, True, False, NULL, 0);
}
struct ASTNode* WhileStatement() {
struct ASTNode* Condition, *Body;
VerifyToken(KW_WHILE, "while");
VerifyToken(LI_LPARE, "(");
Condition = ParsePrecedenceASTNode(0);
if(Condition->Operation < OP_EQUAL || Condition->Operation > OP_GREATE)
Condition = ConstructASTBranch(OP_BOOLCONV, Condition->ExprType, Condition, NULL, 0);
VerifyToken(LI_RPARE, ")");
Body = ParseCompound();
return ConstructASTNode(OP_LOOP, RET_NONE, Condition, NULL, Body, NULL, 0);
}
struct ASTNode* ForStatement() {
// for (preop; condition; postop) {
// body
//}
struct ASTNode* Condition, *Body;
struct ASTNode* Preop, *Postop;
struct ASTNode* Tree;
VerifyToken(KW_FOR, "for");
VerifyToken(LI_LPARE, "(");
Preop = ParseStatement();
VerifyToken(LI_SEMIC, ";");
Condition = ParsePrecedenceASTNode(0);
if(Condition->Operation < OP_EQUAL || Condition->Operation > OP_GREATE)
Condition = ConstructASTBranch(OP_BOOLCONV, Condition->ExprType, Condition, NULL, 0);
VerifyToken(LI_SEMIC, ";");
Postop = ParseStatement();
VerifyToken(LI_RPARE, ")");
Body = ParseCompound();
// We need to be able to skip over the body and the postop, so we group them together.
Tree = ConstructASTNode(OP_COMP, RET_NONE, Body, NULL, Postop, NULL, 0);
// 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
Tree = ConstructASTNode(OP_LOOP, RET_NONE, Condition, NULL, Tree, NULL, 0);
// We need to append the postop to the loop, to form the final for loop
return ConstructASTNode(OP_COMP, RET_NONE, Preop, NULL, Tree, NULL, 0);
}
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);
if(!Tree)
DieDecimal("Attempting to print an invalid type:", RightType);
if(RightType)
Tree = ConstructASTBranch(RightType, RET_INT, Tree, NULL, 0);
Tree = ConstructASTBranch(OP_PRINT, RET_NONE, Tree, NULL, 0);
//ParseAST(Tree);
return Tree;
}
struct ASTNode* PostfixStatement() {
struct ASTNode* Tree;
struct SymbolTableEntry* Entry;
Tokenise(&CurrentToken);
// If we get here, we're one of three things:
// - Function
// - Array
// - Variable
if(CurrentToken.type == LI_LPARE)
return CallFunction();
if(CurrentToken.type == LI_LBRAS)
return AccessArray();
// If we get here, we must be a variable.
// There's no guarantees that the variable is in
// the symbol table, though.
if((Entry = FindSymbol(CurrentIdentifier)) == NULL || Entry->Structure != ST_VAR)
DieMessage("Unknown Variable", CurrentIdentifier);
// Here we check for postincrement and postdecrement.
switch(CurrentToken.type) {
case PPMM_PLUS:
Tokenise(&CurrentToken);
Tree = ConstructASTLeaf(OP_POSTINC, Entry->Type, Entry, 0);
break;
case PPMM_MINUS:
Tokenise(&CurrentToken);
Tree = ConstructASTLeaf(OP_POSTDEC, Entry->Type, Entry, 0);
break;
default:
Tree = ConstructASTLeaf(REF_IDENT, Entry->Type, Entry, 0);
}
return Tree;
}
struct ASTNode* PrefixStatement() {
struct ASTNode* Tree;
switch (CurrentToken.type) {
case BOOL_INVERT:
Tokenise(&CurrentToken);
Tree = PrefixStatement();
Tree->RVal = 1;
Tree = ConstructASTBranch(OP_BOOLNOT, Tree->ExprType, Tree, NULL, 0);
break;
case BIT_NOT:
Tokenise(&CurrentToken);
Tree = PrefixStatement();
Tree->RVal = 1;
Tree = ConstructASTBranch(OP_BITNOT, Tree->ExprType, Tree, NULL, 0);
break;
case AR_MINUS:
Tokenise(&CurrentToken);
Tree = PrefixStatement();
Tree = ConstructASTBranch(OP_NEGATE, Tree->ExprType, Tree, NULL, 0);
break;
case PPMM_PLUS:
Tokenise(&CurrentToken);
Tree = PrefixStatement();
if(Tree->Operation != REF_IDENT)
Die("++ not followed by identifier");
Tree = ConstructASTBranch(OP_PREINC, Tree->ExprType, Tree, NULL, 0);
break;
case PPMM_MINUS:
Tokenise(&CurrentToken);
Tree = PrefixStatement();
if(Tree->Operation != REF_IDENT)
Die("-- not followed by identifier");
Tree = ConstructASTBranch(OP_PREDEC, Tree->ExprType, Tree, NULL, 0);
break;
case BIT_AND:
Tokenise(&CurrentToken);
// To allow things like:
// x = &&y;
// We need to recursively parse prefixes;
Tree = PrefixStatement();
if(Tree->Operation != REF_IDENT)
Die("& must be followed by another & or an identifier.");
Tree->Operation = OP_ADDRESS;
Tree->ExprType = PointerTo(Tree->ExprType);
break;
case AR_STAR:
Tokenise(&CurrentToken);
Tree = PrefixStatement();
if(Tree->Operation != REF_IDENT && Tree->Operation != OP_DEREF)
Die("* must be followed by another * or an identifier.");
Tree = ConstructASTBranch(OP_DEREF, ValueAt(Tree->ExprType), Tree, NULL, 0);
break;
default:
Tree = ParsePrimary();
}
return Tree;
}