From 176c8e70f56bbbbe5ab2063f22000b9af0180e85 Mon Sep 17 00:00:00 2001 From: Nagi5Yeq Date: Fri, 5 Jun 2020 01:25:59 +0800 Subject: add case statement support --- Lexer.l | 11 +++++++- Parser.y | 25 +++++++++++++++--- node/Node.cc | 41 ++++++++++++++++++++++++++++++ node/StatementNode.cc | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++ node/StatementNode.hh | 18 +++++++++++++ t/array.pas | 45 +++++++++++++++++++++++++++++++++ t/qsort.pas | 42 +++++++++++++++++++++++++++++++ t/trivial.pas | 16 ++++++++++++ traveler/Traveler.hh | 4 +-- 9 files changed, 266 insertions(+), 6 deletions(-) create mode 100644 t/array.pas create mode 100644 t/qsort.pas diff --git a/Lexer.l b/Lexer.l index c65d358..c75e552 100644 --- a/Lexer.l +++ b/Lexer.l @@ -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> statement open_statement closed_statement normal_statement %type > assign_statement %type > repeat_statement +%type > switch_statement +%type > case_statements +%type >> 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(ctx);} | CONTINUE {$$=std::make_shared(ctx);} @@ -250,6 +254,21 @@ repeat_statement : REPEAT statement UNTIL expression {$$=std::make_shared(ctx, $4, $2);} ; +switch_statement + : CASE expression OF case_statements ENDS {$$=std::make_shared(ctx, $2, std::move($4), nullptr);} + | CASE expression OF case_statements ELSE statement SEMICOLON ENDS {$$=std::make_shared(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 PrevAction = nullptr; + std::vector> actions; + for (auto&& child : CasePairs_) { + std::shared_ptr 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 CaseVariable, + std::list&& CasePairs, std::shared_ptr 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 type = + std::dynamic_pointer_cast(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 PrevAcion = nullptr; + llvm::BasicBlock* PrevBlock = nullptr; + for (auto&& CaseChild : CasePairs_) { + llvm::Value* CaseValue = std::get<0>(CaseChild)->CodeGen(); + std::shared_ptr CaseType = + std::dynamic_pointer_cast( + 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(CaseValue); + std::shared_ptr 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>; + +class SwitchStatementNode : public BaseNode { + public: + SwitchStatementNode(Context* context, + std::shared_ptr CaseVariable, + std::list&& CasePairs, + std::shared_ptr DefaultAction); + virtual Value CodeGen() override; + virtual int Travel(Traveler& traveler) override; + + protected: + std::shared_ptr CaseVariable_; + std::list CasePairs_; + std::shared_ptr 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] identifier); template - Traveler& operator<<(T value) { - out_ << value; + Traveler& operator<<(T&& value) { + out_ << std::forward(value); return *this; } -- cgit