/*************/ /*GEMWIRE */ /* ERYTHRO*/ /*************/ #include #include /* * 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; }