diff options
| author | Nagi5Yeq <wu@x-nagi.com> | 2020-06-05 01:25:59 +0800 |
|---|---|---|
| committer | Nagi5Yeq <wu@x-nagi.com> | 2020-06-05 01:25:59 +0800 |
| commit | 176c8e70f56bbbbe5ab2063f22000b9af0180e85 (patch) | |
| tree | 0f291a33e69bd9c840131e170e63c0d245268d7b | |
| parent | baa601da82cce51d2b4bd39a32fdc5b8de5c6452 (diff) | |
add case statement support
| -rw-r--r-- | Lexer.l | 11 | ||||
| -rw-r--r-- | Parser.y | 25 | ||||
| -rw-r--r-- | node/Node.cc | 41 | ||||
| -rw-r--r-- | node/StatementNode.cc | 70 | ||||
| -rw-r--r-- | node/StatementNode.hh | 18 | ||||
| -rw-r--r-- | t/array.pas | 45 | ||||
| -rw-r--r-- | t/qsort.pas | 42 | ||||
| -rw-r--r-- | t/trivial.pas | 16 | ||||
| -rw-r--r-- | traveler/Traveler.hh | 4 |
9 files changed, 266 insertions, 6 deletions
@@ -112,6 +112,7 @@ comment {comment1}|{comment2}|{comment3} "for" {return Parser::make_FOR(loc);} "to" {return Parser::make_TO(true, loc);} "downto" {return Parser::make_TO(false, loc);} +"case" {return Parser::make_CASE(loc);} "break" {return Parser::make_BREAK(loc);} "continue" {return Parser::make_CONTINUE(loc);} @@ -167,7 +168,15 @@ comment {comment1}|{comment2}|{comment3} {white}+ {loc.step();} \n+ {loc.lines(yyleng); loc.step();} -{comment} {} +{comment} { + int i; + for(i=0; i<yyleng; i++){ + if(yytext[i]=='\n'){ + loc.lines(1); + loc.step(); + } + } +} . { pcc::Log(pcc::PCC_ERROR, "unrecognized token \"%s\"", yytext); @@ -44,7 +44,7 @@ YY_DECL; %token FILE_END 0; %token COLON SEMICOLON COMMA DOTDOT DOT ASSIGN LBRACKET RBRACKET LPARENTHESIS RPARENTHESIS %token PROGRAM IDENTIFIER VAR CONST TYPE BEGINS ENDS FUNCTION EXTERN DOTDOTDOT -%token IF THEN ELSE WHILE DO REPEAT UNTIL BREAK CONTINUE FOR TO +%token IF THEN ELSE WHILE DO REPEAT UNTIL BREAK CONTINUE FOR TO CASE %token VOID BOOLEAN CHAR SHORT INTEGER INT64 REAL STRING ARRAY OF RECORD %token ADD SUB MUL REAL_DIV DIV MOD LT LE GT GE EQ NE CARET AT %token AND NOT OR XOR SHL SHR @@ -85,6 +85,9 @@ YY_DECL; %type <std::shared_ptr<pcc::BaseNode>> statement open_statement closed_statement normal_statement %type <std::shared_ptr<pcc::AssignStatementNode>> assign_statement %type <std::shared_ptr<pcc::RepeatStatementNode>> repeat_statement +%type <std::shared_ptr<pcc::SwitchStatementNode>> switch_statement +%type <std::list<pcc::CasePair>> case_statements +%type <std::list<std::shared_ptr<pcc::ExprNode>>> cases %% @@ -121,8 +124,8 @@ var_decl_statement ; var_decls - : var_decls SEMICOLON var_decl {$$=std::move($1); auto NewChilds=std::get<0>($3); for(auto& name: NewChilds){$$.push_back({name, std::get<1>($3)});}} - | var_decl {auto NewChilds=std::get<0>($1); for(auto& name: NewChilds){$$.push_back({name, std::get<1>($1)});}} + : var_decls SEMICOLON var_decl {$$=std::move($1); auto NewChilds=std::get<0>($3); for(auto&& name: NewChilds){$$.push_back({name, std::get<1>($3)});}} + | var_decl {auto NewChilds=std::get<0>($1); for(auto&& name: NewChilds){$$.push_back({name, std::get<1>($1)});}} ; var_decl @@ -232,6 +235,7 @@ normal_statement : statement_block {$$=$1;} | assign_statement {$$=$1;} | repeat_statement {$$=$1;} + | switch_statement {$$=$1;} | expression {$$=$1;} | BREAK {$$=std::make_shared<pcc::BreakStatementNode>(ctx);} | CONTINUE {$$=std::make_shared<pcc::ContinueStatementNode>(ctx);} @@ -250,6 +254,21 @@ repeat_statement : REPEAT statement UNTIL expression {$$=std::make_shared<pcc::RepeatStatementNode>(ctx, $4, $2);} ; +switch_statement + : CASE expression OF case_statements ENDS {$$=std::make_shared<SwitchStatementNode>(ctx, $2, std::move($4), nullptr);} + | CASE expression OF case_statements ELSE statement SEMICOLON ENDS {$$=std::make_shared<SwitchStatementNode>(ctx, $2, std::move($4), $6);} + ; + +case_statements + : case_statements cases COLON statement SEMICOLON {$$=std::move($1); for(auto&& child:$2){$$.push_back({child, $4});}} + | cases COLON statement SEMICOLON {for(auto&& child:$1){$$.push_back({child, $3});}} + ; + +cases + : cases COMMA literal {$$=std::move($1); $$.push_back($3);} + | literal {$$.push_back($1);} + ; + /* ==================[expression part]================== */ expression diff --git a/node/Node.cc b/node/Node.cc index 0dcf8ce..9325160 100644 --- a/node/Node.cc +++ b/node/Node.cc @@ -501,4 +501,45 @@ int ContinueStatementNode::Travel(Traveler& traveler) { traveler << TravelPart::END; return 0; } + +int SwitchStatementNode::Travel(Traveler& traveler) { + traveler << TravelPart::PREFIX; + traveler << TravelPart::NAME_BEGIN << "SwitchStatementNode" + << TravelPart::NAME_END; + traveler << TravelPart::DESCRPTION_BEGIN << "NumCases=" + << CasePairs_.size() + (DefaultAction_ == nullptr ? 0 : 1) + << (DefaultAction_ == nullptr ? "" : " HasDefault") + << TravelPart::DESCRPTION_END; + traveler << TravelPart::CHILD_BEGIN; + CaseVariable_->Travel(traveler); + traveler << TravelPart::CHILD_END; + std::shared_ptr<BaseNode> PrevAction = nullptr; + std::vector<std::shared_ptr<BaseNode>> actions; + for (auto&& child : CasePairs_) { + std::shared_ptr<BaseNode> action = std::get<1>(child); + if (action != PrevAction) { + actions.push_back(action); + PrevAction = action; + } + } + auto lambda = [](Traveler& traveler, auto&& action) { + action->Travel(traveler); + }; + NonterminalContainerTravel( + traveler, + [](Traveler& traveler, auto&& child) { + std::get<0>(child)->Travel(traveler); + }, + CasePairs_); + if (DefaultAction_ == nullptr) { + TerminalContainerTravel(traveler, lambda, actions); + } else { + NonterminalContainerTravel(traveler, lambda, actions); + traveler << TravelPart::LAST_CHILD_BEGIN; + DefaultAction_->Travel(traveler); + traveler << TravelPart::LAST_CHILD_END; + } + traveler << TravelPart::END; + return 0; +} } // namespace pcc diff --git a/node/StatementNode.cc b/node/StatementNode.cc index 07babe2..adce4f0 100644 --- a/node/StatementNode.cc +++ b/node/StatementNode.cc @@ -294,4 +294,74 @@ Value ContinueStatementNode::CodeGen() { context_->SetJumpingFlag(true); return nullptr; } + +SwitchStatementNode::SwitchStatementNode( + Context* context, std::shared_ptr<ExprNode> CaseVariable, + std::list<CasePair>&& CasePairs, std::shared_ptr<BaseNode> DefaultAction) + : BaseNode(context) + , CaseVariable_(CaseVariable) + , CasePairs_(std::move(CasePairs)) + , DefaultAction_(DefaultAction) {} + +Value SwitchStatementNode::CodeGen() { + llvm::IRBuilder<>* builder = context_->GetBuilder(); + Value SwitchValue = CaseVariable_->CodeGen(); + std::shared_ptr<IntegerBaseType> type = + std::dynamic_pointer_cast<IntegerBaseType>(CaseVariable_->GetType()); + if (type == nullptr) { + Log(LogLevel::PCC_ERROR, + "variable of switch statement variable should have a integer-like " + "type, not %s", + CaseVariable_->GetType()->GetCommonName()); + return nullptr; + } + llvm::Function* body = builder->GetInsertBlock()->getParent(); + llvm::BasicBlock* EndBlock = + llvm::BasicBlock::Create(GlobalLLVMContext, "done"); + llvm::BasicBlock* DefaultBlock = + (DefaultAction_ == nullptr + ? EndBlock + : llvm::BasicBlock::Create(GlobalLLVMContext, "default")); + llvm::SwitchInst* inst = + builder->CreateSwitch(SwitchValue, DefaultBlock, CasePairs_.size() + 1); + std::shared_ptr<BaseNode> PrevAcion = nullptr; + llvm::BasicBlock* PrevBlock = nullptr; + for (auto&& CaseChild : CasePairs_) { + llvm::Value* CaseValue = std::get<0>(CaseChild)->CodeGen(); + std::shared_ptr<IntegerBaseType> CaseType = + std::dynamic_pointer_cast<IntegerBaseType>( + std::get<0>(CaseChild)->GetType()); + if (CaseType == nullptr) { + Log(LogLevel::PCC_ERROR, + "case of switch statement variable should have a " + "integer-like " + "type, not %s", + std::get<0>(CaseChild)->GetType()->GetCommonName()); + return nullptr; + } + CaseValue = builder->CreateSExtOrTrunc(CaseValue, type->GetLLVMType()); + llvm::ConstantInt* CaseConst = llvm::cast<llvm::ConstantInt>(CaseValue); + std::shared_ptr<BaseNode> CaseAction = std::get<1>(CaseChild); + if (CaseAction == PrevAcion) { + inst->addCase(CaseConst, PrevBlock); + } else { + llvm::BasicBlock* CaseBlock = + llvm::BasicBlock::Create(GlobalLLVMContext, "case", body); + inst->addCase(CaseConst, CaseBlock); + builder->SetInsertPoint(CaseBlock); + CaseAction->CodeGen(); + builder->CreateBr(EndBlock); + PrevAcion = CaseAction, PrevBlock = CaseBlock; + } + } + if (DefaultAction_ != nullptr) { + body->getBasicBlockList().push_back(DefaultBlock); + builder->SetInsertPoint(DefaultBlock); + DefaultAction_->CodeGen(); + builder->CreateBr(EndBlock); + } + body->getBasicBlockList().push_back(EndBlock); + builder->SetInsertPoint(EndBlock); + return nullptr; +} } // namespace pcc diff --git a/node/StatementNode.hh b/node/StatementNode.hh index b729001..81cbb93 100644 --- a/node/StatementNode.hh +++ b/node/StatementNode.hh @@ -105,6 +105,24 @@ class ContinueStatementNode : public BaseNode { virtual Value CodeGen() override; virtual int Travel(Traveler& traveler) override; }; + +using CasePair = + std::pair<std::shared_ptr<ExprNode>, std::shared_ptr<BaseNode>>; + +class SwitchStatementNode : public BaseNode { + public: + SwitchStatementNode(Context* context, + std::shared_ptr<ExprNode> CaseVariable, + std::list<CasePair>&& CasePairs, + std::shared_ptr<BaseNode> DefaultAction); + virtual Value CodeGen() override; + virtual int Travel(Traveler& traveler) override; + + protected: + std::shared_ptr<ExprNode> CaseVariable_; + std::list<CasePair> CasePairs_; + std::shared_ptr<BaseNode> DefaultAction_; +}; } // namespace pcc #endif diff --git a/t/array.pas b/t/array.pas new file mode 100644 index 0000000..6bf913e --- /dev/null +++ b/t/array.pas @@ -0,0 +1,45 @@ +program arr; + +type + a1=integer; + a2=array[0..5] of a1; + a3=array[0..6] of a2; + a4=array[0..7] of a3; + a5=array[0..8] of a4; + +extern memset(dst:^void; n:integer; c:integer):^void; +extern puts(s:string):integer; + +function assert_equal(a:integer; b:integer):void; +begin + if a<>b then puts('error!') +end; + +function main():integer; +var + a:array[0..9] of a5; + pa1:^a5; + pa2:^a4; + pa3:^a3; + pa4:^a2; + pa5:^a1; +begin + memset(@a, 0, 4*6*7*8*9*10); + a[1][2][3][4][5] := 0x1234567; + pa1 := @a[1]; + pa2 := @a[1][2]; + pa3 := @a[1][2][3]; + pa4 := @a[1][2][3][4]; + pa5 := @a[1][2][3][4][5]; + assert_equal(pa1^[2][3][4][5], 0x1234567); + assert_equal(pa2^[3][4][5], 0x1234567); + assert_equal(pa3^[4][5], 0x1234567); + assert_equal(pa4^[5], 0x1234567); + assert_equal(pa5^, 0x1234567); + + assert_equal((@a[1][2][3])^[4][5], 0x1234567); + assert_equal((@a[1][2][2]+1)^[4][5], 0x1234567); + + puts('good'); + main := 0 +end; diff --git a/t/qsort.pas b/t/qsort.pas new file mode 100644 index 0000000..9f62f94 --- /dev/null +++ b/t/qsort.pas @@ -0,0 +1,42 @@ +program qsort; + +extern srand(seed:integer):void; +extern rand():integer; +extern time(t:integer):integer; +extern printf(fmt:string; ...):integer; + +function qsort(arr:^integer; start:integer; ends:integer):void; +var + i,j,mid,t:integer; +begin + if ends>start then begin + mid := arr[ends]; + i := start; + for j := start to ends-1 do + if arr[j]<mid then begin + t := arr[i]; + arr[i] := arr[j]; + arr[j] := t; + i := i+1; + end; + t := arr[i]; + arr[i] := arr[ends]; + arr[ends] := t; + qsort(arr, start, i-1); + qsort(arr, i+1 ,ends) + end +end; + +function main():integer; +var + arr:array[0..99999] of integer; + i:integer; +begin + srand(time(0)); + for i:=0 to 99999 do + arr[i] := rand() mod 1000; + qsort(@arr, 0, 99999); + for i:=99999 downto 0 do + printf('%d ', arr[i]); + main := 0 +end; diff --git a/t/trivial.pas b/t/trivial.pas index 52a6ebe..e71d037 100644 --- a/t/trivial.pas +++ b/t/trivial.pas @@ -230,3 +230,19 @@ begin pa^.m4[1].m1[2]:=1234; record_test:=a.m3[6]+pa^.m3[a.m1]+a.m4[1].m1[2]; end; + +function case_test(a:integer):integer; +begin + case a of + 1,2,3:case_test:=111; + 4,5,6:case a of + 4:case_test:=222; + 5,6:case a of + 5:case_test:=333; + 6:case_test:=555; + end; + end; + else + case_test:=666; + end +end; diff --git a/traveler/Traveler.hh b/traveler/Traveler.hh index 92f5b99..3dc7ca5 100644 --- a/traveler/Traveler.hh +++ b/traveler/Traveler.hh @@ -31,8 +31,8 @@ class Traveler { Traveler& operator<<(std::shared_ptr<TypeIdentifier> identifier); template <typename T> - Traveler& operator<<(T value) { - out_ << value; + Traveler& operator<<(T&& value) { + out_ << std::forward<T>(value); return *this; } |
