386 lines
9.2 KiB
C
386 lines
9.2 KiB
C
|
|
||
|
/*************/
|
||
|
/*GEMWIRE */
|
||
|
/* ERYTHRO*/
|
||
|
/*************/
|
||
|
|
||
|
#include <Defs.h>
|
||
|
#include <Data.h>
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Turn a token type into its appropriate
|
||
|
* primitive type.
|
||
|
*
|
||
|
* This is where we do redirections like:
|
||
|
* short -> s16
|
||
|
* long -> s64
|
||
|
* int -> s32
|
||
|
* char -> u8
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
static char* Types[5] = { "none", "char", "int", "long", "void" };
|
||
|
static int TypeSize[5] = { 0, 1, 4, 8, 0}; // in BYTES
|
||
|
|
||
|
int ParseType(int Token) {
|
||
|
switch(Token) {
|
||
|
case TY_CHAR:
|
||
|
return RET_CHAR;
|
||
|
|
||
|
case TY_VOID:
|
||
|
return RET_VOID;
|
||
|
|
||
|
case TY_INT:
|
||
|
return RET_INT;
|
||
|
|
||
|
default:
|
||
|
DieDecimal("Illegal variable type", Token);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int PrimitiveSize(int Type) {
|
||
|
if(Type < RET_NONE || Type > RET_VOID)
|
||
|
DieMessage("Checking size of bad data type", Types[Type]);
|
||
|
|
||
|
return TypeSize[Type];
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Given two types, determine if they are compatible.
|
||
|
*
|
||
|
* Depending on the value of STRICT, it will try to
|
||
|
* fit the right value into the left value.
|
||
|
*
|
||
|
* This is valid, for ie. a char into an int, as int is larger than char.
|
||
|
* This is called widening the char.
|
||
|
*
|
||
|
* If STRICT is set, it will only allow widening the left to the right.
|
||
|
* This means you cannot `char a; int b; b = 15000; a = b;`
|
||
|
* As this would shrink the int and lose resolution.
|
||
|
*
|
||
|
* NOTE: THIS IS NOT THE DEFAULT BEHAVIOUR
|
||
|
* By default, you CAN shrink an int into a char, a la shifting down.
|
||
|
*
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
int TypesCompatible(int* Left, int* Right, int STRICT) {
|
||
|
|
||
|
int LeftSize, RightSize;
|
||
|
|
||
|
// Same types are compatible. No shrinking required
|
||
|
if(*Left == *Right) {
|
||
|
*Left = *Right = 0;
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
LeftSize = PrimitiveSize(*Left);
|
||
|
RightSize = PrimitiveSize(*Right);
|
||
|
|
||
|
|
||
|
// Types of size 0 are incompatible
|
||
|
if((LeftSize == 0) || (RightSize == 0))
|
||
|
return 0;
|
||
|
|
||
|
|
||
|
/* char x;
|
||
|
* int y;
|
||
|
* y = 15;
|
||
|
*
|
||
|
* x = y;
|
||
|
* x needs to be widened, y copied in, then x shrunk back down
|
||
|
* AKA, the left must be widened.
|
||
|
*/
|
||
|
if(LeftSize < RightSize) {
|
||
|
*Left = OP_WIDEN;
|
||
|
*Right = 0;
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* char x;
|
||
|
* int y;
|
||
|
*
|
||
|
* x = 15;
|
||
|
*
|
||
|
* y = x;
|
||
|
* x must be widened to fit into y.
|
||
|
* if STRICT mode, this is not allowed.
|
||
|
* By default, this is valid.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
if(LeftSize > RightSize) {
|
||
|
if(STRICT)
|
||
|
return 0; // Not compatible if STRICT
|
||
|
|
||
|
*Left = 0;
|
||
|
*Right = OP_WIDEN;
|
||
|
return 1; // Compatible by default
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Any other cases left, by default, are compatible.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
*Left = *Right = 0;
|
||
|
return 1;
|
||
|
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Handles the declaration of a type of a variable.
|
||
|
* int newVar;
|
||
|
*
|
||
|
* It verifies that we have the `int` keyword followed by a
|
||
|
* unique, non-keyword identifier.
|
||
|
*
|
||
|
* It then stores this variable into the symbol table.
|
||
|
*
|
||
|
* //TODO: Assemble this into the symbol table.
|
||
|
* //TODO: int i = 5;
|
||
|
*
|
||
|
*/
|
||
|
void BeginVariableDeclaration(void) {
|
||
|
int ID;
|
||
|
|
||
|
int Type = ParseType(CurrentToken.type);
|
||
|
//printf("type: %s\n", Types[Type]);
|
||
|
Tokenise(&CurrentToken);
|
||
|
VerifyToken(TY_IDENTIFIER, "ident");
|
||
|
//printf("Identifier: %s\n", CurrentIdentifier);
|
||
|
|
||
|
ID = AddSymbol(CurrentIdentifier, Type, ST_VAR);
|
||
|
AsNewSymb(ID);
|
||
|
|
||
|
VerifyToken(LI_SEMIC, ";");
|
||
|
}
|
||
|
|
||
|
struct ASTNode* ParseFunction() {
|
||
|
struct ASTNode* Tree;
|
||
|
struct ASTNode* FinalStatement;
|
||
|
int SymbolSlot, BreakLabel, Type;
|
||
|
|
||
|
Type = ParseType(CurrentToken.type);
|
||
|
Tokenise(&CurrentToken);
|
||
|
VerifyToken(KW_FUNC, "::");
|
||
|
VerifyToken(TY_IDENTIFIER, "ident");
|
||
|
|
||
|
printf("\nIdentified function %s\n", CurrentIdentifier);
|
||
|
|
||
|
BreakLabel = NewLabel();
|
||
|
|
||
|
SymbolSlot = AddFunctionSymbol(CurrentIdentifier, Type, ST_FUNC, BreakLabel);
|
||
|
CurrentFunction = SymbolSlot;
|
||
|
|
||
|
VerifyToken(LI_LPARE, "(");
|
||
|
VerifyToken(LI_RPARE, ")");
|
||
|
|
||
|
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, SymbolSlot);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Handles the logic for return.
|
||
|
* //TODO: No brackets
|
||
|
* //TODO: Type inference
|
||
|
*
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
struct ASTNode* ReturnStatement() {
|
||
|
struct ASTNode* Tree;
|
||
|
int ReturnType, FunctionType;
|
||
|
|
||
|
|
||
|
if(Symbols[CurrentFunction].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;
|
||
|
|
||
|
if(!TypesCompatible(&ReturnType, &FunctionType, 0))
|
||
|
Die("Returning a value of incorrect type for function");
|
||
|
|
||
|
|
||
|
if(ReturnType)
|
||
|
Tree = ConstructASTBranch(ReturnType, FunctionType, Tree, 0);
|
||
|
|
||
|
Tree = ConstructASTBranch(OP_RET, RET_NONE, Tree, 0);
|
||
|
|
||
|
printf("\t\tReturning from function %s\n", Symbols[CurrentFunction].Name);
|
||
|
|
||
|
VerifyToken(LI_RPARE, ")"); // TODO: OPTIONALISE!
|
||
|
|
||
|
return Tree;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Handles the assignment of variables.
|
||
|
*
|
||
|
* You can assign variables with an assignment,
|
||
|
* a statement, a function or a literal.
|
||
|
*
|
||
|
* This means we need to do some recursive parsing.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
struct ASTNode* ParseIdentifier() {
|
||
|
struct ASTNode* Left, *Right, *Tree;
|
||
|
int LeftType, RightType;
|
||
|
int ID;
|
||
|
|
||
|
VerifyToken(TY_IDENTIFIER, "ident");
|
||
|
|
||
|
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;
|
||
|
|
||
|
if(!TypesCompatible(&LeftType, &RightType, 1))
|
||
|
Die("Incompatible variable types");
|
||
|
|
||
|
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)
|
||
|
Die("Invalid Comparison in if statement");
|
||
|
|
||
|
VerifyToken(LI_RPARE, ")");
|
||
|
|
||
|
True = ParseCompound();
|
||
|
|
||
|
if(CurrentToken.type == KW_ELSE) {
|
||
|
Tokenise(&CurrentToken);
|
||
|
False = ParseCompound();
|
||
|
}
|
||
|
|
||
|
return ConstructASTNode(OP_IF, RET_NONE, Condition, True, False, 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)
|
||
|
Die("Bad Comparison inside while()");
|
||
|
|
||
|
VerifyToken(LI_RPARE, ")");
|
||
|
|
||
|
Body = ParseCompound();
|
||
|
|
||
|
return ConstructASTNode(OP_LOOP, RET_NONE, Condition, NULL, Body, 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)
|
||
|
Die("Bad comparison in for");
|
||
|
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, 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, 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, 0);
|
||
|
}
|
||
|
|
||
|
struct ASTNode* PrintStatement(void) {
|
||
|
struct ASTNode* Tree;
|
||
|
int LeftType, RightType;
|
||
|
|
||
|
VerifyToken(KW_PRINT, "print");
|
||
|
|
||
|
Tree = ParsePrecedenceASTNode(0);
|
||
|
|
||
|
LeftType = RET_INT;
|
||
|
RightType = Tree->ExprType;
|
||
|
|
||
|
if(!TypesCompatible(&LeftType, &RightType, 0))
|
||
|
DieDecimal("Attempting to print an invalid type:", RightType);
|
||
|
|
||
|
if(RightType)
|
||
|
Tree = ConstructASTBranch(RightType, RET_INT, Tree, 0);
|
||
|
|
||
|
Tree = ConstructASTBranch(OP_PRINT, RET_NONE, Tree, 0);
|
||
|
|
||
|
//ParseAST(Tree);
|
||
|
|
||
|
return Tree;
|
||
|
|
||
|
}
|