Implement break/continue statements.
This commit is contained in:
parent
4e62bbdc51
commit
b7f8d8666e
|
@ -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);
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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':
|
||||
|
|
17
src/Parser.c
17
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");
|
||||
|
|
|
@ -71,7 +71,7 @@ int ValueAt(int Type) {
|
|||
*
|
||||
*/
|
||||
|
||||
int ParseOptionalPointer(struct SymbolTableEntry** Composite) {
|
||||
int ReadTypeOrKeyword(struct SymbolTableEntry** Composite) {
|
||||
|
||||
int Type;
|
||||
|
||||
|
|
|
@ -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
21
tests/breakcontinue.er
Normal 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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user