Skip to content

Commit

Permalink
supports default
Browse files Browse the repository at this point in the history
  • Loading branch information
EthanSK committed Mar 26, 2019
1 parent e65dfe1 commit 77aa2ce
Show file tree
Hide file tree
Showing 18 changed files with 165 additions and 146 deletions.
2 changes: 2 additions & 0 deletions include/ast/blocks/scopeBlock.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

#include "sequenceBlock.hpp"

class ScopeBlock;
typedef const ScopeBlock* ScopeBlockPtr;

class ScopeBlock : public SequenceBlock //aka compound statement, used to represent content between curly braces everywhere eg loops, conditions, pure scope, etc...
{
Expand Down
16 changes: 9 additions & 7 deletions include/ast/conditions/switch/switchCase.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,19 @@
#include "rvalue.hpp"
#include "utils.hpp"

class SwitchCase;
typedef const SwitchCase *SwitchCasePtr;

class SwitchCase : public Statement
{
public:
public:
SwitchCase(StatementPtr condition, StatementPtr scopeBlock);
void generateIL(std::vector<Instr> &instrs, ILContext &context, std::string destReg) const override;
virtual void generateIL(std::vector<Instr> &instrs, ILContext &context, std::string destReg) const override;
StatementPtr getCondition() const; //the thing for the case that it needs to equal to
StatementPtr getScopeBlock() const; //execution block

protected:
StatementPtr getCondition() const; //the thing for the case that it needs to equal to
StatementPtr getScopeBlock() const; //execution block

void printC(std::ostream &os) const override;
protected:
virtual void printC(std::ostream &os) const override;
};

#endif
7 changes: 5 additions & 2 deletions include/ast/conditions/switch/switchDefault.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@
#include <string>
#include <iostream>
#include <memory>
#include "statement.hpp"
#include "switchCase.hpp"

class SwitchDefault : public Statement
class SwitchDefault;
typedef const SwitchDefault* SwitchDefaultPtr;

class SwitchDefault : public SwitchCase
{
public:
SwitchDefault(StatementPtr scopeBlock);
Expand Down
4 changes: 3 additions & 1 deletion include/ast/conditions/switch/switchStatement.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
#include <memory>
#include "statement.hpp"
#include "scopeBlock.hpp"
#include "switchCase.hpp"

#include "utils.hpp"

class SwitchStatement;
typedef const SwitchStatement* SwitchStatementPtr;

class SwitchStatement : public Statement
{
Expand Down
8 changes: 4 additions & 4 deletions include/context/ilContext.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ class ILContext : public Context
void registerFuncSymbol(std::string symbolName);
bool isFuncSymbolDefined(std::string symbolName) const;

// void pushLoopLabels(std::string startLabel, std::string endLabel);
// void popLoopLabels(); //pops last element
// std::tuple<std::string, std::string> getLastLoopLabel();
void pushLoopLabels(std::string startLabel, std::string endLabel);
void popLoopLabels(); //pops last element
std::tuple<std::string, std::string> getLastLoopLabel();

private:
std::set<std::string> _funcSymbols;
std::unordered_map<std::string, int> _registeredNames;
// std::vector<std::tuple<std::string, std::string>> _loopLabelStack; //to keep track of labels for break and continue for eg. - tuple.0 is loop start label and tuple.1 is loop end label
std::vector<std::tuple<std::string, std::string>> _loopLabelStack; //to keep track of labels for break and continue for eg. - tuple.0 is loop start label and tuple.1 is loop end label
};

#endif
1 change: 0 additions & 1 deletion src/ast/blocks/scopeBlock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ void ScopeBlock::generateIL(std::vector<Instr> &instrs, ILContext &context, std:
instrs.push_back(Instr("scd"));
}


std::vector<StatementPtr> ScopeBlock::getBranches() const
{
return branches;
Expand Down
15 changes: 0 additions & 15 deletions src/ast/conditions/switch/switchCase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,9 @@ void SwitchCase::printC(std::ostream &os) const

void SwitchCase::generateIL(std::vector<Instr> &instrs, ILContext &context, std::string destReg) const
{

//getScopeBlock->generateIL(instrs, context, destReg);
// instrs.push_back(Instr::makeLabel(case_lb));

RValuePtr rvalue = Utils::tryCast<RValue>(getCondition(), "condition of switch case must be an rvalue");
if (!rvalue->isConstant()) { throw "condition of switch case must be a constant"; }

int caseConstant = rvalue->evalConst();
instrs.push_back(Instr("li", destReg, std::to_string(caseConstant)));//switch case returned through destReg
// getScopeBlock()->generateIL(instrs, context, "_root"); //because switch block can have switch cases or regular ndes, the IL gen should be done from the scope block

// std::string caseSkip_lb = context.makeLabelName("caseSkip_" + std::to_string(caseConstant)); //caseConstant for prettyness
// std::string switchCaseReg = context.makeName("switchCase"); //the a in switch (a) //the b in case b:
// context.getLastSwitchCase()->generateIL(instrs, context, switchCaseReg); //evaluate and put in temp reg (like defining a variable)
// // put caseConstant in temp reg too (with li)
// instrs.push_back(Instr("li", "$t0", std::to_string(caseConstant)));
// // then compare the two reg's
// instrs.push_back(Instr("bne", caseSkip_lb, switchCaseReg, "$t0"));
// getScopeBlock()->generateIL(instrs, context, destReg);
// instrs.push_back(Instr::makeLabel(caseSkip_lb));
}
22 changes: 4 additions & 18 deletions src/ast/conditions/switch/switchDefault.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
#include "switchDefault.hpp"
#include "integerLiteral.hpp"

SwitchDefault::SwitchDefault(StatementPtr scopeBlock)
{
SwitchDefault::SwitchDefault(StatementPtr scopeBlock) : SwitchCase(new IntegerLiteral(1), scopeBlock)
{
branches.push_back(scopeBlock);
}

Expand All @@ -17,20 +18,5 @@ void SwitchDefault::printC(std::ostream &os) const

void SwitchDefault::generateIL(std::vector<Instr> &instrs, ILContext &context, std::string destReg) const
{
// RValuePtr rvalue = Utils::tryCast<RValue>(getCondition(), "condition of switch case must be an rvalue");
// if (!rvalue->isConstant())
// {
// throw "condition of switch case must be a constant";
// }
// int caseConstant = rvalue->evalConst();
// std::string case_lb = context.makeLabelName("case_" + std::to_string(caseConstant)); //caseConstant for prettyness
// std::string switchCaseReg = context.makeName("switchCase"); //the a in switch (a)
// std::string caseConditionReg = context.makeName("switchCondition"); //the b in case b:
// context.getLastSwitchCase()->generateIL(instrs, context, switchCaseReg); //evaluate and put in temp reg (like defining a variable)
// // put caseConstant in temp reg too (with li)
// instrs.push_back(Instr("li", caseConditionReg, std::to_string(caseConstant)));
// // then compare the two reg's
// instrs.push_back(Instr("beq", case_lb, switchCaseReg, caseConditionReg));
// instrs.push_back(Instr::makeLabel(case_lb));
// getScopeBlock()->generateIL(instrs, context, destReg);
//do nothing - there is no constant to evaluate and scope block generated from switch statement loop
}
126 changes: 56 additions & 70 deletions src/ast/conditions/switch/switchStatement.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#include "switchStatement.hpp"
#include "switchCase.hpp"
#include "switchDefault.hpp"

SwitchStatement::SwitchStatement(StatementPtr caseExpr, StatementPtr switchBlock)
{
Expand All @@ -21,85 +23,69 @@ void SwitchStatement::printC(std::ostream &os) const

void SwitchStatement::generateIL(std::vector<Instr> &instrs, ILContext &context, std::string destReg) const
{
// std::string else_lb = context.makeLabelName("else");
// std::string endif_lb = context.makeLabelName("endif");

// context.compileInput(getCondition(), instrs, "$t0");
// instrs.push_back(Instr("bez", else_lb, "$t0"));
// getIfScopeBlock()->generateIL(instrs, context, destReg);
// instrs.push_back(Instr("b", endif_lb));
// instrs.push_back(Instr::makeLabel(else_lb));
// getElseScopeBlock()->generateIL(instrs, context, destReg);
// instrs.push_back(Instr::makeLabel(endif_lb));

//could loop through cases and default, and make a label for each case
//we then check if each condition is equal to the case, and if so jump to that label
//else jump to the default if there is any, or the end of the switch statement
//we don't have the binary operator like == or > to generate IL on. we have to do it differently to normal if statement. We have to go though every case and check if it is == to it.
//because there can exist dead code that we do NOT want to execute in the switch statement, when looping, we need to try cast to a switch case node to ensure it is correct. we need to try catch block it so execution continues.
//we could have an actual switch block node, and that is responsible for looping through the cases and evaluating the correct one, but it would need to know the initial switch case from the parent switch node, which it doesn't know. it only knows its children. therefore we need to somehow access branhces from the switch blokc. maybe make a getter for the scope block?

// auto scopeBlock = Utils::tryCast<ScopeBlock>(getSwitchBlock(), "switch block must be a scope block");

// for (size_t i = 0; i < scopeBlock->branches.size(); i++)
// {
// //make a label and block for each switch case

// }

//wait, can't we just make the labels in the switch case node?
//the dest reg in the getSwitchBlock will contain the result. the context could contain the value we are matching against?
// we can have a stack for switch cases to evaluate in the context, and when tryna figure out the cases it uses the top most stack value.

//wait what am i on about, we need to know the case at runtime not compile time. it needs to generate labels ad blocks for every case, and do an eq on the genIL on the case dest reg (return value) and the constant of the switch conditions
//and that is why we need a for loop, the eq needs to be done on every case (because cases are known at compile time, the are just constants)

ScopeBlock *switchBlock = Utils::tryCast<ScopeBlock>(getSwitchBlock(), "switch block must be a scope block"); //just to get access to branches
// switcblock->branches contain all the cases / default

//first we wanna branch to the correct switch case label. then it will automatically execute everything after that, whic is what we want.
//we ALWAYS want to execute the scope block of the switch case, because if we have branched there, it means we have ALREADY MET the condition.
//we just need to be be able to READ the condition from the loop below and add appropriate branches.
//to find the first switch case, we need to try to cast to switch case in the loop, and get the condition branch (branch[0]), and add the beq/bne whatever it is label
//we first need to set up all the branches, then after have all the corresponding labels with their blocks. all the branches need to be at the top before.
//we use switchInstrs to push all the branches at the front of the vector, and all the labels/switch case blocks to the end of the vector
// acc do we need that?? can't we just bne or somethning after each label
ScopeBlockPtr switchBlock = Utils::tryCast<ScopeBlock>(getSwitchBlock(), "switch block must be a scope block"); //just to get access to branches
std::vector<StatementPtr> switchBlockBranches = switchBlock->getBranches();
std::string switchEnd_lb = context.makeName("switchEnd"); //the a in switch (a)

//we could set up labels and scope blocks first, the branch past all that, then beq back...nah would mess up at the end.

//first set up beq's for switch cases
//keep track of labels being created, maybe ith a map of nodeid : label ?
std::vector<std::string> labels;
Instr branchToDefaultInstr;
bool hasDefault = false;
for (size_t i = 0; i < switchBlockBranches.size(); i++)
{
try
{ //is a switch case (or default)
SwitchCasePtr switchCase = Utils::tryCast<SwitchCase>(switchBlockBranches[i], "node is not a switch case");
std::string switchCaseReg = context.makeName("switchCase"); //the a in switch (a)
std::string case_lb = context.makeLabelName("case_lb");

try
{ //is default
SwitchDefaultPtr switchDefault = Utils::tryCast<SwitchDefault>(switchBlockBranches[i], "node is not a default class");
hasDefault = true;
branchToDefaultInstr = Instr("b", case_lb);
}
catch (std::string)
{ //is not default
getCase()->generateIL(instrs, context, switchCaseReg);
switchCase->generateIL(instrs, context, "$t0"); //switch case generateIL just returns the condition constant
instrs.push_back(Instr("beq", case_lb, switchCaseReg, "$t0"));
}
labels.push_back(case_lb); //even if it's from default, it should stay in place as if there is no break it can continue executing the other lines
}
catch (std::string)
{ //not a switch case
}
}
if (hasDefault)
{
instrs.push_back(branchToDefaultInstr);
}
instrs.push_back(Instr("b", switchEnd_lb));

//OHHHH ok so we only need the branch stuff if the node is a switch case, otherwise we can just add the IL for the node and it will work as expected
for (size_t i = 0; i < switchBlock->getBranches().size(); i++)
//now set up the branch labels with the corresponding block IL
int labelIndex = 0;
for (size_t i = 0; i < switchBlockBranches.size(); i++)
{
//if there is corresponding label for node, make label
try
{ //is a switch case
SwitchCase *switchCase = Utils::tryCast<SwitchCase>(switchBlock->getBranches()[i], "node is not a switch case");
// RValuePtr rvalue = Utils::tryCast<RValue>(switchCase->getCondition(), "condition of switch case must be an rvalue");
// if (!rvalue->isConstant())
// {
// throw "condition of switch case must be a constant";
// }
// int caseConstant = rvalue->evalConst();
std::string caseSkip_lb = context.makeLabelName("caseSkip_" + std::to_string(caseConstant)); //caseConstant for readability
std::string switchCaseReg = context.makeName("switchCase"); //the a in switch (a)
getCase()->generateIL(instrs, context, switchCaseReg);
switchCase->generateIL(instrs, context, "$t0"); //$t0 holds the case condition contsant - the b in case b:
// instrs.push_back(Instr("bne", caseSkip_lb, switchCaseReg, "$t0"));
isBranchSwitchCase = true;
SwitchCasePtr switchCase = Utils::tryCast<SwitchCase>(switchBlockBranches[i], "node is not a switch case");
std::string label = labels[labelIndex++];
instrs.push_back(Instr::makeLabel(label));
switchCase->getScopeBlock()->generateIL(instrs, context, destReg);
instrs.push_back(Instr::makeLabel(caseSkip_lb));

// beq getCase == switchCase.getCondition to caseSkip_lb
//this ^ can be done inside the switchCase genIL, if the context feeds it the correct getCase to check against.
//need to somehow store the label for later us
}
catch (const std::exception &e)
catch (std::string)
{ //not a switch case

// std::cerr << e.what() << '\n';
//it actually doesn't matter if it could not be cast, it may very well be valid. this whole try catch acts as an optional cast
switchBlock->getBranches()[i]->generateIL(instrs, context, destReg);
switchBlockBranches[i]->generateIL(instrs, context, destReg);
}
//scope IL should be added here, for every node! except the ones before the first case, that's ded. or we can generate the first branch instruction at hte very top of the switch insrts

//the label should go after that then if there is a label
}
instrs.push_back(Instr::makeLabel(switchEnd_lb));

//then after we have set all the labels with their blocks, we need to do an eq to jump to the correct block
//we can do a beq as it is in our IL spec.
//we need to beq to the label if the switch case constant value is eq to the getCase constant value .that should actually be done i nthe for loop as we need to check it for each case.
Expand Down
6 changes: 3 additions & 3 deletions src/c_parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
%type <expr> PARAMETER_DECLARATION INIT_DECLARATOR


%type <expr> TYPE_NAME ABSTRACT_DECLARATOR DIRECT_ABSTRACT_DECLARATOR INITIALIZER_LIST STATEMENT COMPOUND_STATEMENT LABELED_STATEMENT
%type <expr> TYPE_NAME ABSTRACT_DECLARATOR DIRECT_ABSTRACT_DECLARATOR STATEMENT COMPOUND_STATEMENT LABELED_STATEMENT
%type <expr> EXPRESSION_STATEMENT SELECTION_STATEMENT ITERATION_STATEMENT JUMP_STATEMENT EXTERNAL_DECLARATION FUNCTION_DEFINITION

%type <exprList> STATEMENT_LIST PARAMETER_LIST TRANSLATION_UNIT DECLARATION_LIST ARGUMENT_EXPRESSION_LIST IDENTIFIER_LIST
Expand Down Expand Up @@ -452,8 +452,8 @@ STATEMENT

//Switch statements
LABELED_STATEMENT
: T_IDENTIFIER T_COLON STATEMENT { $$ = new SwitchCase($1, $3); }
| T_CASE CONSTANT_EXPRESSION T_COLON STATEMENT { $$ = new SwitchCase($2, $4); }
//: T_IDENTIFIER T_COLON STATEMENT { $$ = new SwitchCase($1, $3); }
: T_CASE CONSTANT_EXPRESSION T_COLON STATEMENT { $$ = new SwitchCase($2, $4); }
| T_DEFAULT T_COLON STATEMENT { $$ = new SwitchDefault($3); }
;

Expand Down
13 changes: 13 additions & 0 deletions test/compiler/tests/conditionals/switch_break.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
int switch_break()
{
int a = 69;
switch (a)
{
case 69:
a++;
break;
case 90:
a += 100;
}
return a;
}
6 changes: 6 additions & 0 deletions test/compiler/tests/conditionals/switch_break_driver.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
int switch_break_driver();

int main()
{
return !(switch_break_driver() == 70);
}
26 changes: 26 additions & 0 deletions test/compiler/tests/conditionals/switch_complex.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
int switch_complex()
{
int a = 3;
int b = 0;
switch (a)
{
b = 4;
case 69:
{
return 69;
}
case 3:

b += 5;
a = b + 6;

case 5:
a += 45;
a++;
break;
default:
return 2;
}

return a;
}
6 changes: 6 additions & 0 deletions test/compiler/tests/conditionals/switch_complex_driver.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
int switch_complex();

int main()
{
return !(switch_complex() == 57);
}
11 changes: 11 additions & 0 deletions test/compiler/tests/conditionals/switch_no_match.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
int switch_no_match()
{
int a = 49;
switch (a)
{
case 69:
return a;
return 70;
}
return 80;
}
6 changes: 6 additions & 0 deletions test/compiler/tests/conditionals/switch_no_match_driver.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
int switch_no_match();

int main()
{
return !(switch_no_match() == 80); /*should not enter switch as no matches*/
}
Loading

0 comments on commit 77aa2ce

Please sign in to comment.