Add switch statement, case and default handling, wire in the error handler for a sample program
This commit is contained in:
parent
70ae06af44
commit
4e47cdcaf6
|
@ -21,4 +21,4 @@ add_executable(Erythro
|
||||||
src/Symbols.c
|
src/Symbols.c
|
||||||
src/Types.c
|
src/Types.c
|
||||||
src/Importer.c
|
src/Importer.c
|
||||||
src/assemble/JVMAssembler.c)
|
src/assemble/JVMAssembler.c src/Errors.c)
|
||||||
|
|
|
@ -373,8 +373,7 @@ void DisplayUsage(char* ProgName);
|
||||||
void Tokenise();
|
void Tokenise();
|
||||||
|
|
||||||
void VerifyToken(int Type, char* TokenExpected);
|
void VerifyToken(int Type, char* TokenExpected);
|
||||||
|
bool OptionallyConsume(int Type);
|
||||||
void RejectToken(struct Token* Token);
|
|
||||||
|
|
||||||
static int ReadIdentifier(int Char, char* Buffer, int Limit);
|
static int ReadIdentifier(int Char, char* Buffer, int Limit);
|
||||||
|
|
||||||
|
@ -585,7 +584,7 @@ struct AssemblerVtable {
|
||||||
int (*AsIf)(struct ASTNode*, int, int);
|
int (*AsIf)(struct ASTNode*, int, int);
|
||||||
int (*AsWhile)(struct ASTNode*);
|
int (*AsWhile)(struct ASTNode*);
|
||||||
int (*AsSwitch)(struct ASTNode*);
|
int (*AsSwitch)(struct ASTNode*);
|
||||||
int (*AsSwitchTable)(int, int, int, int*, int*, int);
|
void (*AsSwitchTable)(int, int, int, int*, int*, int);
|
||||||
int (*NewLabel)();
|
int (*NewLabel)();
|
||||||
void (*AsJmp)(int);
|
void (*AsJmp)(int);
|
||||||
void (*AsLabel)(int);
|
void (*AsLabel)(int);
|
||||||
|
|
25
src/Dump.c
25
src/Dump.c
|
@ -16,12 +16,12 @@ static int GenerateSrg() {
|
||||||
* Walk the Node tree, and dump the AST tree to stdout.
|
* Walk the Node tree, and dump the AST tree to stdout.
|
||||||
*/
|
*/
|
||||||
void DumpTree(struct ASTNode* Node, int level) {
|
void DumpTree(struct ASTNode* Node, int level) {
|
||||||
int Lfalse, Lstart, Lend;
|
int Lstart, Lend;
|
||||||
|
|
||||||
// Handle weirdo loops and conditions first.
|
// Handle weirdo loops and conditions first.
|
||||||
switch (Node->Operation) {
|
switch (Node->Operation) {
|
||||||
case OP_IF:
|
case OP_IF:
|
||||||
Lfalse = GenerateSrg();
|
GenerateSrg();
|
||||||
for (int i = 0; i < level; i++)
|
for (int i = 0; i < level; i++)
|
||||||
fprintf(stdout, " ");
|
fprintf(stdout, " ");
|
||||||
fprintf(stdout, "IF");
|
fprintf(stdout, "IF");
|
||||||
|
@ -43,7 +43,15 @@ void DumpTree(struct ASTNode* Node, int level) {
|
||||||
for (int i = 0; i < level; i++)
|
for (int i = 0; i < level; i++)
|
||||||
fprintf(stdout, " ");
|
fprintf(stdout, " ");
|
||||||
fprintf(stdout, "LOOP starts at %d\n", Lstart);
|
fprintf(stdout, "LOOP starts at %d\n", Lstart);
|
||||||
Lend = GenerateSrg();
|
GenerateSrg();
|
||||||
|
DumpTree(Node->Left, level + 2);
|
||||||
|
DumpTree(Node->Right, level + 2);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case OP_SWITCH:
|
||||||
|
for (int i = 0; i < level; i++)
|
||||||
|
fprintf(stdout, " ");
|
||||||
|
fprintf(stdout, "SWITCH\n");
|
||||||
DumpTree(Node->Left, level + 2);
|
DumpTree(Node->Left, level + 2);
|
||||||
DumpTree(Node->Right, level + 2);
|
DumpTree(Node->Right, level + 2);
|
||||||
return;
|
return;
|
||||||
|
@ -187,6 +195,17 @@ void DumpTree(struct ASTNode* Node, int level) {
|
||||||
fprintf(stdout, "OP_BOOLCONV\n");
|
fprintf(stdout, "OP_BOOLCONV\n");
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
case OP_DEFAULT:
|
||||||
|
fprintf(stdout, "OP_DEFAULT\n");
|
||||||
|
DumpTree(Node->Left, level + 2);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case OP_CASE:
|
||||||
|
fprintf(stdout, "OP_CASE %d\n", Node->IntValue);
|
||||||
|
DumpTree(Node->Left, level + 2);
|
||||||
|
DumpTree(Node->Right, level);
|
||||||
|
return;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
DieDecimal("Unknown Dump Operator", Node->Operation);
|
DieDecimal("Unknown Dump Operator", Node->Operation);
|
||||||
}
|
}
|
||||||
|
|
10
src/Errors.c
10
src/Errors.c
|
@ -72,12 +72,16 @@ void ErrorReport(char* message, ...) {
|
||||||
printErrorLine(file, line - 1);
|
printErrorLine(file, line - 1);
|
||||||
printHelpLine(line, strbuf);
|
printHelpLine(line, strbuf);
|
||||||
printLine(file, line);
|
printLine(file, line);
|
||||||
printLine(file, line + 1);
|
if (!feof(file))
|
||||||
|
printLine(file, line + 1);
|
||||||
} else {
|
} else {
|
||||||
printErrorLine(file, line);
|
printErrorLine(file, line);
|
||||||
printHelpLine(line, strbuf);
|
printHelpLine(line, strbuf);
|
||||||
printLine(file, line + 1);
|
|
||||||
printLine(file, line + 2);
|
if (!feof(file))
|
||||||
|
printLine(file, line + 1);
|
||||||
|
if (!feof(file))
|
||||||
|
printLine(file, line + 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
30
src/Lexer.c
30
src/Lexer.c
|
@ -103,21 +103,13 @@ void VerifyToken(int Type, char* TokenExpected) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct Token* RejectedToken = NULL;
|
bool OptionallyConsume(int Type) {
|
||||||
|
if (CurrentFile->CurrentSymbol.type == Type) {
|
||||||
/*
|
Tokenise();
|
||||||
* Rejected Tokens and the Overread Stream are identical concepts.
|
return true;
|
||||||
* This was implemented first, but it is no longer used.
|
}
|
||||||
* TODO: Refactor this function out.
|
return false;
|
||||||
*/
|
|
||||||
|
|
||||||
void RejectToken(struct Token* Token) {
|
|
||||||
if (RejectedToken != NULL)
|
|
||||||
Die("Cannot reject two tokens in a row!");
|
|
||||||
|
|
||||||
RejectedToken = Token;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
* * * * L I T E R A L S A N D I D E N T I F I E R S * * * *
|
* * * * L I T E R A L S A N D I D E N T I F I E R S * * * *
|
||||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||||
|
@ -127,7 +119,7 @@ void RejectToken(struct Token* Token) {
|
||||||
* Currently only supports the decimal numbers, despite the
|
* Currently only supports the decimal numbers, despite the
|
||||||
* FindDigitFromPos function allowing conversion.
|
* FindDigitFromPos function allowing conversion.
|
||||||
*
|
*
|
||||||
* The functon loops over the characters, multiplying by 10 and adding
|
* The function loops over the characters, multiplying by 10 and adding
|
||||||
* the new value on top, until a non-numeric character is found.
|
* the new value on top, until a non-numeric character is found.
|
||||||
* At that point, it returns the non-numeric character to the Overread Stream
|
* At that point, it returns the non-numeric character to the Overread Stream
|
||||||
* and returns the calculated number.
|
* and returns the calculated number.
|
||||||
|
@ -409,12 +401,6 @@ void Tokenise() {
|
||||||
int Char, TokenType;
|
int Char, TokenType;
|
||||||
struct Token* Token = &CurrentFile->CurrentSymbol;
|
struct Token* Token = &CurrentFile->CurrentSymbol;
|
||||||
|
|
||||||
if (RejectedToken != NULL) {
|
|
||||||
Token = RejectedToken;
|
|
||||||
RejectedToken = NULL;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Char = FindChar();
|
Char = FindChar();
|
||||||
|
|
||||||
switch (Char) {
|
switch (Char) {
|
||||||
|
@ -575,7 +561,7 @@ void Tokenise() {
|
||||||
if (Char == ':') {
|
if (Char == ':') {
|
||||||
Token->type = KW_FUNC;
|
Token->type = KW_FUNC;
|
||||||
} else {
|
} else {
|
||||||
ReturnCharToStream(Char);
|
Token->type = LI_COLON;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,7 @@ char* TokenNames[] = {
|
||||||
"Integer literal",
|
"Integer literal",
|
||||||
"String literal",
|
"String literal",
|
||||||
"Statement End",
|
"Statement End",
|
||||||
|
"Colon",
|
||||||
|
|
||||||
"Compound Block Start",
|
"Compound Block Start",
|
||||||
"Compound Block End",
|
"Compound Block End",
|
||||||
|
@ -72,6 +73,10 @@ char* TokenNames[] = {
|
||||||
"Break keyword",
|
"Break keyword",
|
||||||
"Continue keyword",
|
"Continue keyword",
|
||||||
|
|
||||||
|
"Switch Keyword",
|
||||||
|
"Default Keyword",
|
||||||
|
"Case Keyword",
|
||||||
|
|
||||||
"Print Keyword",
|
"Print Keyword",
|
||||||
"If keyword",
|
"If keyword",
|
||||||
"Else keyword",
|
"Else keyword",
|
||||||
|
|
|
@ -286,18 +286,16 @@ struct ASTNode* ParsePrecedenceASTNode(int PreviousTokenPrecedence) {
|
||||||
// int LeftType, RightType;
|
// int LeftType, RightType;
|
||||||
int NodeType, OpType;
|
int NodeType, OpType;
|
||||||
|
|
||||||
printf("Left node branch\r\n");
|
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
LeftNode = PrefixStatement();
|
LeftNode = PrefixStatement();
|
||||||
|
|
||||||
NodeType = CurrentFile->CurrentSymbol.type;
|
NodeType = CurrentFile->CurrentSymbol.type;
|
||||||
|
|
||||||
if (NodeType == LI_SEMIC || NodeType == LI_RPARE || NodeType == LI_RBRAS || NodeType == LI_COM) {
|
if (NodeType == LI_SEMIC || NodeType == LI_COLON || NodeType == LI_RPARE || NodeType == LI_RBRAS || NodeType == LI_COM || NodeType == LI_INT) {
|
||||||
LeftNode->RVal = 1;
|
LeftNode->RVal = 1;
|
||||||
return LeftNode;
|
return LeftNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("Operator expected\r\n");
|
|
||||||
while ((OperatorPrecedence(NodeType) > PreviousTokenPrecedence) ||
|
while ((OperatorPrecedence(NodeType) > PreviousTokenPrecedence) ||
|
||||||
(IsRightExpr(OpType) && OperatorPrecedence(OpType) == PreviousTokenPrecedence)) {
|
(IsRightExpr(OpType) && OperatorPrecedence(OpType) == PreviousTokenPrecedence)) {
|
||||||
Tokenise();
|
Tokenise();
|
||||||
|
@ -306,7 +304,6 @@ struct ASTNode* ParsePrecedenceASTNode(int PreviousTokenPrecedence) {
|
||||||
|
|
||||||
RightNode = ParsePrecedenceASTNode(Precedence[NodeType]);
|
RightNode = ParsePrecedenceASTNode(Precedence[NodeType]);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* While parsing this node, we may need to widen some types.
|
* While parsing this node, we may need to widen some types.
|
||||||
* This requires a few functions and checks.
|
* This requires a few functions and checks.
|
||||||
|
@ -472,6 +469,7 @@ struct ASTNode* GetExpressionList() {
|
||||||
* * If Statement
|
* * If Statement
|
||||||
* * While Statement
|
* * While Statement
|
||||||
* * For Statement
|
* * For Statement
|
||||||
|
* * Switch Statement
|
||||||
* * Return Statement
|
* * Return Statement
|
||||||
* * Numeric literals and variables
|
* * Numeric literals and variables
|
||||||
* * Binary Expressions
|
* * Binary Expressions
|
||||||
|
@ -497,6 +495,9 @@ struct ASTNode* ParseStatement(void) {
|
||||||
VerifyToken(LI_SEMIC, ";"); // TODO: single line assignment?
|
VerifyToken(LI_SEMIC, ";"); // TODO: single line assignment?
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
case KW_SWITCH:
|
||||||
|
return SwitchStatement();
|
||||||
|
|
||||||
case KW_IF:
|
case KW_IF:
|
||||||
return IfStatement();
|
return IfStatement();
|
||||||
|
|
||||||
|
|
108
src/Statements.c
108
src/Statements.c
|
@ -262,7 +262,7 @@ struct ASTNode* ParseFunction(int Type) {
|
||||||
if (OldFunction->Storage != ST_FUNC)
|
if (OldFunction->Storage != ST_FUNC)
|
||||||
OldFunction = NULL;
|
OldFunction = NULL;
|
||||||
if (OldFunction == NULL) {
|
if (OldFunction == NULL) {
|
||||||
BreakLabel = NewLabel();
|
BreakLabel = Assembler->vtable->NewLabel();
|
||||||
NewFunction = AddSymbol(CurrentIdentifier, Type, ST_FUNC, SC_GLOBAL, BreakLabel, 0, NULL);
|
NewFunction = AddSymbol(CurrentIdentifier, Type, ST_FUNC, SC_GLOBAL, BreakLabel, 0, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -324,7 +324,7 @@ struct ASTNode* ReturnStatement() {
|
||||||
|
|
||||||
VerifyToken(KW_RETURN, "return");
|
VerifyToken(KW_RETURN, "return");
|
||||||
|
|
||||||
VerifyToken(LI_LPARE, "("); // TODO: Make optional! Reject?
|
bool bracketed = OptionallyConsume(LI_LPARE);
|
||||||
|
|
||||||
Tree = ParsePrecedenceASTNode(0);
|
Tree = ParsePrecedenceASTNode(0);
|
||||||
|
|
||||||
|
@ -337,7 +337,7 @@ struct ASTNode* ReturnStatement() {
|
||||||
|
|
||||||
printf("\t\tReturning from function %s\n", CurrentFile->FunctionEntry->Name);
|
printf("\t\tReturning from function %s\n", CurrentFile->FunctionEntry->Name);
|
||||||
|
|
||||||
VerifyToken(LI_RPARE, ")"); // TODO: OPTIONALISE!
|
if (bracketed) VerifyToken(LI_RPARE, ")");
|
||||||
|
|
||||||
return Tree;
|
return Tree;
|
||||||
}
|
}
|
||||||
|
@ -543,6 +543,107 @@ struct ASTNode* PrintStatement(void) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ASTNode* SwitchStatement() {
|
||||||
|
struct ASTNode* left, *root, *c, *casetree=NULL, *casetail;
|
||||||
|
int looping=1, cases=0;
|
||||||
|
int defaultpresent=0;
|
||||||
|
int ASTOp, casevalue;
|
||||||
|
|
||||||
|
printf("\tParsing switch statement\n");
|
||||||
|
|
||||||
|
// Skip switch(
|
||||||
|
Tokenise();
|
||||||
|
VerifyToken(LI_LPARE, "(");
|
||||||
|
|
||||||
|
printf("\tSwitch: Reading switch expression\n");
|
||||||
|
// Fetch switch expression
|
||||||
|
left = ParsePrecedenceASTNode(0);
|
||||||
|
// Consume ) {
|
||||||
|
VerifyToken(LI_RPARE, ")");
|
||||||
|
VerifyToken(LI_LBRAC, "{");
|
||||||
|
|
||||||
|
// Verify the switch expression (must be integer-compatible)
|
||||||
|
if (!TypeIsInt(!left->ExprType))
|
||||||
|
Die("Switch expression is not of integer type");
|
||||||
|
|
||||||
|
Safe();
|
||||||
|
|
||||||
|
// Create the root Switch node
|
||||||
|
root = ConstructASTBranch(OP_SWITCH, 0, left, NULL, 0);
|
||||||
|
|
||||||
|
// Iterate down the switch node, generating cases
|
||||||
|
while (looping) {
|
||||||
|
switch (CurrentFile->CurrentSymbol.type) {
|
||||||
|
case LI_RBRAC:
|
||||||
|
if (cases == 0)
|
||||||
|
Die("No cases in switch statement");
|
||||||
|
looping = 0;
|
||||||
|
break;
|
||||||
|
case KW_CASE:
|
||||||
|
if (defaultpresent)
|
||||||
|
Die("Case present after default in switch.");
|
||||||
|
|
||||||
|
ASTOp = OP_CASE;
|
||||||
|
|
||||||
|
Safe();
|
||||||
|
Tokenise();
|
||||||
|
|
||||||
|
// Parse case value
|
||||||
|
left = ParsePrecedenceASTNode(0);
|
||||||
|
if (left->Operation != TERM_INTLITERAL)
|
||||||
|
Die("Expecting integer literal for case value");
|
||||||
|
casevalue = left->IntValue;
|
||||||
|
printf("\t\tSwitch case %d found\n", casevalue);
|
||||||
|
|
||||||
|
// Make sure nothing resolves to the same case value
|
||||||
|
for (c = casetree; c != NULL; c = c->Right)
|
||||||
|
if (casevalue == c->IntValue)
|
||||||
|
Die("Duplicate case ID in switch statement");
|
||||||
|
// Fallthrough so that we get the case tree logic deduplicated
|
||||||
|
case KW_DEFAULT:
|
||||||
|
if (defaultpresent)
|
||||||
|
Die("Duplicate default entries in switch");
|
||||||
|
// Duplicate check because CASE falls through into this block
|
||||||
|
if (CurrentFile->CurrentSymbol.type == KW_DEFAULT) {
|
||||||
|
ASTOp = OP_DEFAULT;
|
||||||
|
defaultpresent = true;
|
||||||
|
Tokenise();
|
||||||
|
|
||||||
|
printf("\t\tSwitch default case found\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
VerifyToken(LI_COLON, ":");
|
||||||
|
Safe();
|
||||||
|
|
||||||
|
left = ParseStatement();
|
||||||
|
OptionallyConsume(LI_SEMIC);
|
||||||
|
cases++;
|
||||||
|
Safe();
|
||||||
|
|
||||||
|
// Append this new case to the tree
|
||||||
|
if (casetree == NULL) {
|
||||||
|
casetree = casetail = ConstructASTBranch(ASTOp, 0, left, NULL, casevalue);
|
||||||
|
} else {
|
||||||
|
casetail->Right = ConstructASTBranch(ASTOp, 0, left, NULL, casevalue);
|
||||||
|
casetail = casetail->Right;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
ErrorReport("Unexpected token in switch statement: %s\n", TokenNames[CurrentFile->CurrentSymbol.type]);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
root->IntValue = cases;
|
||||||
|
root->Right = casetree;
|
||||||
|
|
||||||
|
// Consume the right brace immediately
|
||||||
|
VerifyToken(LI_RBRAC, "}");
|
||||||
|
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the surrounding logic for break statements
|
* Handles the surrounding logic for break statements
|
||||||
*
|
*
|
||||||
|
@ -571,7 +672,6 @@ struct ASTNode* BreakStatement() {
|
||||||
Die("Unable to break without a loop");
|
Die("Unable to break without a loop");
|
||||||
|
|
||||||
Tokenise();
|
Tokenise();
|
||||||
Tokenise();
|
|
||||||
|
|
||||||
return ConstructASTLeaf(OP_BREAK, 0, NULL, 0);
|
return ConstructASTLeaf(OP_BREAK, 0, NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -296,6 +296,68 @@ static int AsWhile(struct ASTNode* Node) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void AsSwitchTable(int reg, int cases, int toplabel, int* caselabel, int* caseval, int defaultlabel) {
|
||||||
|
int i, label;
|
||||||
|
|
||||||
|
label = NewLabel();
|
||||||
|
AsLabel(label);
|
||||||
|
|
||||||
|
// Add a default case even if not present.
|
||||||
|
if (cases == 0) {
|
||||||
|
caseval[0] = 0;
|
||||||
|
caselabel[0] = defaultlabel;
|
||||||
|
cases = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(OutputFile, "\t.quad\t%d\n", cases);
|
||||||
|
for (i = 0; i < cases; i++)
|
||||||
|
fprintf(OutputFile, "\t.quad\t%d, L%d\n", caseval[i], caselabel[i]);
|
||||||
|
fprintf(OutputFile, "\t.quad\tL%d\n", defaultlabel);
|
||||||
|
|
||||||
|
AsLabel(toplabel);
|
||||||
|
fprintf(OutputFile, "\tmovq\t%s, %%rax\n", Registers[reg]);
|
||||||
|
fprintf(OutputFile, "\tleaq\tL%d(%%rip), %%rdx\n", label);
|
||||||
|
fprintf(OutputFile, "\tjmp\tswitch\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int AsSwitch(struct ASTNode* root) {
|
||||||
|
int* caseval, *caselabel;
|
||||||
|
int Ljump, Lend;
|
||||||
|
int i, reg, defaultlabel=0, cases=0;
|
||||||
|
struct ASTNode* c;
|
||||||
|
|
||||||
|
caseval = (int*) malloc((root->IntValue + 1) * sizeof(int));
|
||||||
|
caselabel = (int*) malloc((root->IntValue + 1) * sizeof(int));
|
||||||
|
|
||||||
|
Ljump = NewLabel();
|
||||||
|
Lend = NewLabel();
|
||||||
|
|
||||||
|
defaultlabel = Lend;
|
||||||
|
|
||||||
|
reg = AssembleTree(root->Left, -1, -1, -1, 0);
|
||||||
|
AsJmp(Ljump);
|
||||||
|
DeallocateAllRegisters();
|
||||||
|
|
||||||
|
for (i = 0, c = root->Right; c != NULL; i++, c = c->Right) {
|
||||||
|
caselabel[i] = NewLabel();
|
||||||
|
caseval[i] = c->IntValue;
|
||||||
|
AsLabel(caselabel[i]);
|
||||||
|
|
||||||
|
if (c->Operation == OP_DEFAULT)
|
||||||
|
defaultlabel = caselabel[i];
|
||||||
|
else
|
||||||
|
cases++;
|
||||||
|
|
||||||
|
AssembleTree(c->Left, -1, -1, Lend, 0);
|
||||||
|
DeallocateAllRegisters();
|
||||||
|
}
|
||||||
|
|
||||||
|
AsJmp(Lend);
|
||||||
|
AsSwitchTable(reg, cases, Ljump, caselabel, caseval, defaultlabel);
|
||||||
|
AsLabel(Lend);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
// Load a value into a register.
|
// Load a value into a register.
|
||||||
static int AsLoad(int Value) {
|
static int AsLoad(int Value) {
|
||||||
int Register = RetrieveRegister();
|
int Register = RetrieveRegister();
|
||||||
|
@ -885,9 +947,27 @@ static int AsBooleanConvert(int Register, int Operation, int Label) {
|
||||||
static void AssemblerPreamble() {
|
static void AssemblerPreamble() {
|
||||||
DeallocateAllRegisters();
|
DeallocateAllRegisters();
|
||||||
fputs(
|
fputs(
|
||||||
"\t.text\n", /*
|
"\t.text\n"
|
||||||
".LC0:\n"
|
"switch:\n"
|
||||||
"\t.string\t\"%d\\n\"\n", */
|
"\t\tpushq %rsi\n"
|
||||||
|
"\t\tmovq %rdx, %rsi\n"
|
||||||
|
"\t\tmovq %rax, %rbx\n"
|
||||||
|
"\t\tcld\n"
|
||||||
|
"\t\tlodsq\n"
|
||||||
|
"\t\tmovq %rax, %rcx\n"
|
||||||
|
"next:\n"
|
||||||
|
"\t\tlodsq\n"
|
||||||
|
"\t\tmovq %rax, %rdx\n"
|
||||||
|
"\t\tlodsq\n"
|
||||||
|
"\t\tcmpq %rdx, %rbx\n"
|
||||||
|
"\t\tjnz no\n"
|
||||||
|
"\t\tpopq %rsi\n"
|
||||||
|
"\t\tjmp *%rax\n"
|
||||||
|
"no:\n"
|
||||||
|
"\t\tloop next\n"
|
||||||
|
"\t\tlodsq\n"
|
||||||
|
"\t\tpopq %rsi\n"
|
||||||
|
"\t\tjmp *%rax\n",
|
||||||
OutputFile);
|
OutputFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -991,13 +1071,16 @@ static int AssembleTree(struct ASTNode* Node, int Register, int LoopBeginLabel,
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
case OP_CALL:
|
case OP_CALL:
|
||||||
return (AsCallWrapper(Node));
|
return AsCallWrapper(Node);
|
||||||
|
|
||||||
case OP_FUNC:
|
case OP_FUNC:
|
||||||
AsFunctionPreamble(Node->Symbol);
|
AsFunctionPreamble(Node->Symbol);
|
||||||
AssembleTree(Node->Left, -1, LoopBeginLabel, LoopEndLabel, Node->Operation);
|
AssembleTree(Node->Left, -1, LoopBeginLabel, LoopEndLabel, Node->Operation);
|
||||||
AsFunctionEpilogue(Node->Symbol);
|
AsFunctionEpilogue(Node->Symbol);
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
case OP_SWITCH:
|
||||||
|
return AsSwitch(Node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1219,6 +1302,8 @@ static const struct AssemblerVtable Win32ASMVtable = {
|
||||||
.AsStrGlobalVar = AsStrGlobalVar,
|
.AsStrGlobalVar = AsStrGlobalVar,
|
||||||
.AsStrLocalVar = AsStrLocalVar,
|
.AsStrLocalVar = AsStrLocalVar,
|
||||||
.AsSub = AsSub,
|
.AsSub = AsSub,
|
||||||
|
.AsSwitch = AsSwitch,
|
||||||
|
.AsSwitchTable = AsSwitchTable,
|
||||||
.AsWhile = AsWhile,
|
.AsWhile = AsWhile,
|
||||||
.DeallocateAllRegisters = DeallocateAllRegisters,
|
.DeallocateAllRegisters = DeallocateAllRegisters,
|
||||||
.RetrieveRegister = RetrieveRegister,
|
.RetrieveRegister = RetrieveRegister,
|
||||||
|
|
27
tests/switch.er
Normal file
27
tests/switch.er
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import "tests/print.em"
|
||||||
|
|
||||||
|
int :: main() {
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
|
||||||
|
y = 0;
|
||||||
|
|
||||||
|
for (x = 0; x < 5; x++) {
|
||||||
|
switch(x) {
|
||||||
|
case 1: {
|
||||||
|
y = 5;
|
||||||
|
break; }
|
||||||
|
case 2: {
|
||||||
|
y = 7;
|
||||||
|
break; }
|
||||||
|
case 3:
|
||||||
|
y = 9;
|
||||||
|
default:
|
||||||
|
y = 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%d\n", y);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user