summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNagi5Yeq <wu@x-nagi.com>2020-06-05 01:25:59 +0800
committerNagi5Yeq <wu@x-nagi.com>2020-06-05 01:25:59 +0800
commit176c8e70f56bbbbe5ab2063f22000b9af0180e85 (patch)
tree0f291a33e69bd9c840131e170e63c0d245268d7b
parentbaa601da82cce51d2b4bd39a32fdc5b8de5c6452 (diff)
add case statement support
-rw-r--r--Lexer.l11
-rw-r--r--Parser.y25
-rw-r--r--node/Node.cc41
-rw-r--r--node/StatementNode.cc70
-rw-r--r--node/StatementNode.hh18
-rw-r--r--t/array.pas45
-rw-r--r--t/qsort.pas42
-rw-r--r--t/trivial.pas16
-rw-r--r--traveler/Traveler.hh4
9 files changed, 266 insertions, 6 deletions
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<yyleng; i++){
+ if(yytext[i]=='\n'){
+ loc.lines(1);
+ loc.step();
+ }
+ }
+}
. {
pcc::Log(pcc::PCC_ERROR, "unrecognized token \"%s\"", yytext);
diff --git a/Parser.y b/Parser.y
index 9f9a7b4..82999fb 100644
--- a/Parser.y
+++ b/Parser.y
@@ -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;
}