From b7f8d8666e3ecd0c79d3d5b461cf753bae88b94b Mon Sep 17 00:00:00 2001 From: Curle Date: Sat, 5 Mar 2022 23:42:01 +0000 Subject: [PATCH] Implement break/continue statements. --- include/Defs.h | 16 ++++++++-- src/Assembler.c | 38 ++++++++++++++--------- src/Dump.c | 6 ++++ src/Lexer.c | 8 +++++ src/Parser.c | 17 +++++++--- src/Pointers.c | 2 +- src/Statements.c | 70 +++++++++++++++++++++++++++++++++++++++++- tests/breakcontinue.er | 21 +++++++++++++ 8 files changed, 153 insertions(+), 25 deletions(-) create mode 100644 tests/breakcontinue.er diff --git a/include/Defs.h b/include/Defs.h index 68d1f84..5ab0d05 100644 --- a/include/Defs.h +++ b/include/Defs.h @@ -90,6 +90,8 @@ enum TokenTypes { TY_VOID, // "void" type keyword KW_FUNC, // :: function name incoming + KW_BREAK, // "break" keyword + KW_CONTINUE, // "continue" keyword KW_PRINT, KW_IF, @@ -176,6 +178,8 @@ enum SyntaxOps { OP_PRINT, // Print statement OP_FUNC = 40, // Define a function + OP_BREAK, // Break out of the loop + OP_CONTINUE, // Continue the loop }; @@ -271,6 +275,8 @@ struct FileData { long CurrentLine; // The column of the file we are currently working on, -1 if it is finished long CurrentColumn; + // The depth of the loop currently being parsed. + long CurrentLoopDepth; // The symbol currently being lexed - TokenTypes index and integer value. struct Token CurrentSymbol; @@ -434,7 +440,11 @@ struct ASTNode* CallFunction(); struct ASTNode* ReturnStatement(); -int ParseOptionalPointer(struct SymbolTableEntry** Composite); +struct ASTNode* BreakStatement(); + +struct ASTNode* ContinueStatement(); + +int ReadTypeOrKeyword(struct SymbolTableEntry** Composite); int ValueAt(int Type); @@ -503,7 +513,7 @@ void DieBinary(char* Error, int Number); * * * * C O D E G E N E R A T I O N * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -int AssembleTree(struct ASTNode* Node, int Register, int ParentOp); +int AssembleTree(struct ASTNode* Node, int Register, int LoopBeginLabel, int LoopEndLabel, int ParentOp); void DeallocateAllRegisters(); @@ -583,7 +593,7 @@ int AsCompareJmp(int Operation, int RegisterLeft, int RegisterRight, int Label); int AsCompare(int Operation, int RegisterLeft, int RegisterRight); -int AsIf(struct ASTNode* Node); +int AsIf(struct ASTNode* Node, int LoopStartLabel, int LoopEndLabel); int NewLabel(void); diff --git a/src/Assembler.c b/src/Assembler.c index d232e09..b6dd080 100644 --- a/src/Assembler.c +++ b/src/Assembler.c @@ -64,7 +64,7 @@ static int Started = 0; * @return dependant on the Node. Typically the Register that stores the result of the Node's operation. * */ -int AssembleTree(struct ASTNode* Node, int Register, int ParentOp) { +int AssembleTree(struct ASTNode* Node, int Register, int LoopBeginLabel, int LoopEndLabel, int ParentOp) { int LeftVal, RightVal; if (!Started && OptDumpTree) DumpTree(Node, 0); @@ -73,15 +73,15 @@ int AssembleTree(struct ASTNode* Node, int Register, int ParentOp) { printf("Current operation: %d\r\n", Node->Operation); switch (Node->Operation) { case OP_IF: - return AsIf(Node); + return AsIf(Node, LoopBeginLabel, LoopEndLabel); case OP_LOOP: return AsWhile(Node); case OP_COMP: - AssembleTree(Node->Left, -1, Node->Operation); + AssembleTree(Node->Left, -1, -1, -1, Node->Operation); DeallocateAllRegisters(); - AssembleTree(Node->Right, -1, Node->Operation); + AssembleTree(Node->Right, -1, -1, -1, Node->Operation); DeallocateAllRegisters(); return -1; @@ -90,17 +90,17 @@ int AssembleTree(struct ASTNode* Node, int Register, int ParentOp) { case OP_FUNC: AsFunctionPreamble(Node->Symbol); - AssembleTree(Node->Left, -1, Node->Operation); + AssembleTree(Node->Left, -1, -1, -1, Node->Operation); AsFunctionEpilogue(Node->Symbol); return -1; } if (Node->Left) - LeftVal = AssembleTree(Node->Left, -1, Node->Operation); + LeftVal = AssembleTree(Node->Left, -1, -1, -1, Node->Operation); if (Node->Right) - RightVal = AssembleTree(Node->Right, LeftVal, Node->Operation); + RightVal = AssembleTree(Node->Right, LeftVal, -1, -1, Node->Operation); switch (Node->Operation) { case OP_ADD: @@ -130,6 +130,14 @@ int AssembleTree(struct ASTNode* Node, int Register, int ParentOp) { RightVal = AsLoad(Node->Size); return AsMul(LeftVal, RightVal); } + + case OP_BREAK: + AsJmp(LoopEndLabel); + return -1; + case OP_CONTINUE: + AsJmp(LoopBeginLabel); + return -1; + case OP_ADDRESS: return AsAddr(Node->Symbol); @@ -353,7 +361,7 @@ int AsAlignMemory(int Type, int Offset, int Direction) { } // Assemble an If statement -int AsIf(struct ASTNode* Node) { +int AsIf(struct ASTNode* Node, int LoopStartLabel, int LoopEndLabel) { int FalseLabel, EndLabel; FalseLabel = NewLabel(); @@ -362,11 +370,11 @@ int AsIf(struct ASTNode* Node) { // Left is the condition - AssembleTree(Node->Left, FalseLabel, Node->Operation); + AssembleTree(Node->Left, FalseLabel, -1, -1, Node->Operation); DeallocateAllRegisters(); // Middle is the true block - AssembleTree(Node->Middle, -1, Node->Operation); + AssembleTree(Node->Middle, -1, LoopStartLabel, LoopEndLabel, Node->Operation); DeallocateAllRegisters(); // Right is the optional else @@ -376,7 +384,7 @@ int AsIf(struct ASTNode* Node) { AsLabel(FalseLabel); if (Node->Right) { - AssembleTree(Node->Right, -1, Node->Operation); + AssembleTree(Node->Right, -1, LoopStartLabel, LoopEndLabel, Node->Operation); DeallocateAllRegisters(); AsLabel(EndLabel); } @@ -467,14 +475,14 @@ int AsWhile(struct ASTNode* Node) { AsLabel(BodyLabel); // Assemble the condition - this should include a jump to end! - AssembleTree(Node->Left, BreakLabel, Node->Operation); + AssembleTree(Node->Left, BreakLabel, BodyLabel, BreakLabel, Node->Operation); DeallocateAllRegisters(); // Assemble the body - AssembleTree(Node->Right, -1, Node->Operation); + AssembleTree(Node->Right, -1, BodyLabel, BreakLabel, Node->Operation); DeallocateAllRegisters(); - // Jump back to the body - as we've already failed the condition check if we get here + // Jump back to the body - as awe've already failed the condition check if we get here AsJmp(BodyLabel); // Set up the label to break out of the loop. @@ -877,7 +885,7 @@ int AsCallWrapper(struct ASTNode* Node) { int Register, Args = 0; while (CompositeTree) { - Register = AssembleTree(CompositeTree->Right, -1, CompositeTree->Operation); + Register = AssembleTree(CompositeTree->Right, -1, -1, -1, CompositeTree->Operation); AsCopyArgs(Register, CompositeTree->Size); if (Args == 0) Args = CompositeTree->Size; DeallocateAllRegisters(); diff --git a/src/Dump.c b/src/Dump.c index 6c7cf21..e5a4dae 100644 --- a/src/Dump.c +++ b/src/Dump.c @@ -67,6 +67,12 @@ void DumpTree(struct ASTNode* Node, int level) { case OP_COMP: fprintf(stdout, "\n\n"); return; + case OP_CONTINUE: + fprintf(stdout, "OP_CONTINUE\n"); + return; + case OP_BREAK: + fprintf(stdout, "OP_BREAK\n"); + return; case OP_FUNC: fprintf(stdout, "OP_FUNC %s\n", Node->Symbol->Name); return; diff --git a/src/Lexer.c b/src/Lexer.c index c499bdd..ef3cf7d 100644 --- a/src/Lexer.c +++ b/src/Lexer.c @@ -288,9 +288,17 @@ static int ReadKeyword(char* Str) { return KW_ALIAS; break; + case 'b': + if (!strcmp(Str, "break")) + return KW_BREAK; + break; + case 'c': if (!strcmp(Str, "char")) return TY_CHAR; + if (!strcmp(Str, "continue")) + return KW_CONTINUE; + break; case 'e': diff --git a/src/Parser.c b/src/Parser.c index f8fbd8c..995b9a2 100644 --- a/src/Parser.c +++ b/src/Parser.c @@ -172,7 +172,7 @@ int ReadAlias(struct SymbolTableEntry** Composite) { Tokenise(); - Type = ParseOptionalPointer(Composite); + Type = ReadTypeOrKeyword(Composite); if (FindAlias(CurrentIdentifier) != NULL) DieMessage("Redefinition of type", CurrentIdentifier); @@ -481,7 +481,7 @@ struct ASTNode* ParseStatement(void) { case TY_LONG: case TY_INT: printf("\t\tNew Variable: %s\n", CurrentIdentifier); - Type = ParseOptionalPointer(NULL); + Type = ReadTypeOrKeyword(NULL); VerifyToken(TY_IDENTIFIER, "ident"); BeginVariableDeclaration(Type, NULL, SC_LOCAL); VerifyToken(LI_SEMIC, ";"); // TODO: single line assignment? @@ -499,6 +499,12 @@ struct ASTNode* ParseStatement(void) { case KW_RETURN: return ReturnStatement(); + case KW_BREAK: + return BreakStatement(); + + case KW_CONTINUE: + return ContinueStatement(); + default: ParsePrecedenceASTNode(0); } @@ -534,7 +540,8 @@ struct ASTNode* ParseCompound() { Tree = ParseStatement(); if (Tree && (Tree->Operation == OP_PRINT || Tree->Operation == OP_ASSIGN - || Tree->Operation == OP_RET || Tree->Operation == OP_CALL)) + || Tree->Operation == OP_RET || Tree->Operation == OP_CALL + || Tree->Operation == OP_BREAK || Tree->Operation == OP_CONTINUE)) VerifyToken(LI_SEMIC, ";"); if (Tree) { @@ -581,7 +588,7 @@ void ParseGlobals() { break; printf("New definition incoming..\r\n\n"); - Type = ParseOptionalPointer(&Composite); + Type = ReadTypeOrKeyword(&Composite); //TODO: converge pathways on this block? if (CurrentFile->CurrentSymbol.type == KW_FUNC) { @@ -607,7 +614,7 @@ void ParseGlobals() { Tree = ParseFunction(Type); if (Tree && CurrentFile->AllowDefinitions) { printf("\nBeginning assembler creation of new function %s\n", Tree->Symbol->Name); - AssembleTree(Tree, -1, 0); + AssembleTree(Tree, -1, -1, -1, 0); FreeLocals(); } else { printf("\nFunction prototype saved\r\n"); diff --git a/src/Pointers.c b/src/Pointers.c index 83a6497..6c83280 100644 --- a/src/Pointers.c +++ b/src/Pointers.c @@ -71,7 +71,7 @@ int ValueAt(int Type) { * */ -int ParseOptionalPointer(struct SymbolTableEntry** Composite) { +int ReadTypeOrKeyword(struct SymbolTableEntry** Composite) { int Type; diff --git a/src/Statements.c b/src/Statements.c index f91ad6f..307706f 100644 --- a/src/Statements.c +++ b/src/Statements.c @@ -34,7 +34,7 @@ static int ReadDeclarationList(struct SymbolTableEntry* FunctionSymbol, int Stor PrototypePointer = FunctionSymbol->Start; while (CurrentFile->CurrentSymbol.type != End) { - TokenType = ParseOptionalPointer(&Composite); + TokenType = ReadTypeOrKeyword(&Composite); VerifyToken(TY_IDENTIFIER, "identifier"); printf("\tReading a new element: %s of type %d, scope %s\n", CurrentIdentifier, TokenType, ScopeNames[Storage]); @@ -291,6 +291,7 @@ struct ASTNode* ParseFunction(int Type) { CurrentFile->FunctionEntry = OldFunction; + CurrentFile->CurrentLoopDepth = 0; Tree = ParseCompound(); if (Type != RET_VOID) { @@ -433,7 +434,9 @@ struct ASTNode* WhileStatement() { VerifyToken(LI_RPARE, ")"); + CurrentFile->CurrentLoopDepth++; Body = ParseCompound(); + CurrentFile->CurrentLoopDepth--; return ConstructASTNode(OP_LOOP, RET_NONE, Condition, NULL, Body, NULL, 0); } @@ -489,7 +492,9 @@ struct ASTNode* ForStatement() { Postop = ParseStatement(); VerifyToken(LI_RPARE, ")"); + CurrentFile->CurrentLoopDepth++; Body = ParseCompound(); + CurrentFile->CurrentLoopDepth--; // 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); @@ -539,6 +544,69 @@ struct ASTNode* PrintStatement(void) { } +/** + * 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 + * jne exit + * + * : jump exit + * jump while + * exit: + * + * + * + * @return the AST of this statement + */ +struct ASTNode* BreakStatement() { + if (CurrentFile->CurrentLoopDepth == 0) + Die("Unable to break without a loop"); + + 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 + * jne exit + * + * : jump while + * jump while + * 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); +} + /* * Handles the surrounding logic for all of the logical and semantic * postfixes. diff --git a/tests/breakcontinue.er b/tests/breakcontinue.er new file mode 100644 index 0000000..ea3eab6 --- /dev/null +++ b/tests/breakcontinue.er @@ -0,0 +1,21 @@ +import "tests/import/defs.eh" + +int :: main() { + int x; + x = 5; + + while (x < 15) { + printf("%d\n", x); + if (x =? 12) { + break; + } + + if (x =? 10) { + continue; + } + + x = x + 1; + } + + return (0); +} \ No newline at end of file