Implement break/continue statements.

This commit is contained in:
Curle 2022-03-05 23:42:01 +00:00
parent 4e62bbdc51
commit b7f8d8666e
8 changed files with 153 additions and 25 deletions

View File

@ -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);

View File

@ -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();

View File

@ -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;

View File

@ -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':

View File

@ -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");

View File

@ -71,7 +71,7 @@ int ValueAt(int Type) {
*
*/
int ParseOptionalPointer(struct SymbolTableEntry** Composite) {
int ReadTypeOrKeyword(struct SymbolTableEntry** Composite) {
int Type;

View File

@ -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 <condition>
* jne exit
* <body>
* <break>: jump exit
* jump while
* exit:
* <loop 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 <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);
}
/*
* Handles the surrounding logic for all of the logical and semantic
* postfixes.

21
tests/breakcontinue.er Normal file
View File

@ -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);
}