From c730a168d02c33905e512a4592dd075e91e4d2a9 Mon Sep 17 00:00:00 2001 From: Stanislav Gromov Date: Tue, 31 Dec 2019 14:25:23 +0700 Subject: [PATCH 01/14] Implement switch expressions --- source/compiler/sc.h | 1 + source/compiler/sc1.c | 2 +- source/compiler/sc3.c | 139 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 141 insertions(+), 1 deletion(-) diff --git a/source/compiler/sc.h b/source/compiler/sc.h index d5576510..55c57546 100644 --- a/source/compiler/sc.h +++ b/source/compiler/sc.h @@ -705,6 +705,7 @@ SC_FUNC void check_tagmismatch(int formaltag,int actualtag,int allowcoerce,int e SC_FUNC void check_tagmismatch_multiple(int formaltags[],int numtags,int actualtag,int errline); SC_FUNC char *funcdisplayname(char *dest,char *funcname); SC_FUNC int constexpr(cell *val,int *tag,symbol **symptr); +SC_FUNC constvalue *insert_constval(constvalue *prev,constvalue *next,const char *name,cell val,int index); SC_FUNC constvalue *append_constval(constvalue_root *table,const char *name,cell val,int index); SC_FUNC constvalue *find_constval(constvalue_root *table,char *name,int index); SC_FUNC void delete_consttable(constvalue_root *table); diff --git a/source/compiler/sc1.c b/source/compiler/sc1.c index a50409ea..6cda7c31 100644 --- a/source/compiler/sc1.c +++ b/source/compiler/sc1.c @@ -5437,7 +5437,7 @@ static void destructsymbols(symbol *root,int level) popreg(sPRI); } -static constvalue *insert_constval(constvalue *prev,constvalue *next, +SC_FUNC constvalue *insert_constval(constvalue *prev,constvalue *next, const char *name,cell val,int index) { constvalue *cur; diff --git a/source/compiler/sc3.c b/source/compiler/sc3.c index 11d7c03e..d0f1cbcf 100644 --- a/source/compiler/sc3.c +++ b/source/compiler/sc3.c @@ -1657,6 +1657,145 @@ static int hier2(value *lval) } /* if */ return FALSE; } /* case */ + case tSWITCH: { + int swdefault,casecount,firstcase; + int swtag,csetag,exprtag; + int lbl_table,lbl_exit,lbl_case; + int ident,index; + int bck_allowtags; + cell cidx; + constvalue_root caselist = { NULL, NULL}; /* case list starts empty */ + constvalue *cse,*csp,*newval; + char labelname[sNAMEMAX+1]; + needtoken('('); + ident=expression(&val,&swtag,NULL,TRUE); + if (ident==iARRAY || ident==iREFARRAY) + error(33,"-unknown-"); /* array must be indexed */ + /* generate the code for the switch statement, the label is the address + * of the case table (to be generated later). + */ + lbl_table=getlabel(); + lbl_case=0; /* just to avoid a compiler warning */ + ffswitch(lbl_table); + lbl_exit=getlabel(); /* get label number for jumping out of switch */ + casecount=0; + swdefault=FALSE; + firstcase=TRUE; + do { + needtoken(tTERM); + ident=lex(&val,&st); + if (ident==')') + break; + lbl_case=getlabel(); + setlabel(lbl_case); + bck_allowtags=sc_allowtags; + sc_allowtags=FALSE; /* do not allow tagnames here */ + if (ident==tLABEL && st[0]=='_' && st[1]=='\0') { + if (swdefault!=FALSE) + error(16); /* multiple defaults in switch */ + swdefault=TRUE; + } else { + if (ident!=tLABEL) + lexpush(); + else + error(220); /* expression with tag override must appear between parentheses */ + if (swdefault!=FALSE) + error(15); /* "default" case must be last in switch statement */ + do { + casecount++; + stgget(&index,&cidx); /* mark position in code generator */ + ident=expression(&val,&csetag,NULL,TRUE); + stgdel(index,cidx); /* scratch generated code */ + if (ident!=iCONSTEXPR) + error(8); /* must be constant expression */ + check_tagmismatch(swtag,csetag,TRUE,-1); + /* Search the insertion point (the table is kept in sorted order, so + * that advanced abstract machines can sift the case table with a + * binary search). Check for duplicate case values at the same time. + */ + for (csp=NULL, cse=caselist.first; + cse!=NULL && cse->valuenext) + /* nothing */; + if (cse!=NULL && cse->value==val) + error(40,val); /* duplicate "case" label */ + /* Since the label is stored as a string in the "constvalue", the + * size of an identifier must be at least 8, as there are 8 + * hexadecimal digits in a 32-bit number. + */ + #if sNAMEMAX < 8 + #error Length of identifier (sNAMEMAX) too small. + #endif + assert(csp==NULL || csp->next==cse); + newval=insert_constval(csp,cse,itoh(lbl_case),val,0); + if (csp==NULL) + caselist.first=newval; + if (matchtoken(tDBLDOT)) { + cell end; + stgget(&index,&cidx); /* mark position in code generator */ + ident=expression(&end,&csetag,NULL,TRUE); + stgdel(index,cidx); /* scratch generated code */ + if (ident!=iCONSTEXPR) + error(8); /* must be constant expression */ + if (end<=val) + error(50); /* invalid range */ + while (++val<=end) { + casecount++; + /* find the new insertion point */ + for (csp=NULL, cse=caselist.first; + cse!=NULL && cse->valuenext) + /* nothing */; + if (cse!=NULL && cse->value==val) + error(40,val); /* duplicate "case" label */ + assert(csp==NULL || csp->next==cse); + insert_constval(csp,cse,itoh(lbl_case),val,0); + } /* while */ + } /* if */ + } while (matchtoken(',')); + needtoken(':'); /* ':' ends the case */ + } /* if */ + sc_allowtags=bck_allowtags; /* reset */ + ident=expression(NULL,&exprtag,NULL,FALSE); + if (ident==iARRAY || ident==iREFARRAY) + error(33,"-unknown-"); /* array must be indexed */ + if (firstcase) { + tag=exprtag; + firstcase=FALSE; + } else { + check_tagmismatch(tag,exprtag,TRUE,-1); + } /* if */ + jumplabel(lbl_exit); + } while (!matchtoken(')')); + #if !defined NDEBUG + /* verify that the case table is sorted (unfortunately, duplicates can + * occur; there really shouldn't be duplicate cases, but the compiler + * may not crash or drop into an assertion for a user error). */ + for (cse=caselist.first; cse!=NULL && cse->next!=NULL; cse=cse->next) + assert(cse->value <= cse->next->value); + #endif + /* generate the table here, before lbl_exit (general jump target) */ + setlabel(lbl_table); + assert(swdefault==FALSE || swdefault==TRUE); + if (swdefault==FALSE) { + /* store lbl_exit as the "none-matched" label in the switch table */ + strcpy(labelname,itoh(lbl_exit)); + } else { + /* lbl_case holds the label of the "default" clause */ + strcpy(labelname,itoh(lbl_case)); + } /* if */ + ffcase(casecount,labelname,TRUE); + /* generate the rest of the table */ + for (cse=caselist.first; cse!=NULL; cse=cse->next) + ffcase(cse->value,cse->name,FALSE); + + setlabel(lbl_exit); + delete_consttable(&caselist); /* clear list of case labels */ + clear_value(lval); + lval->ident=iEXPRESSION; + lval->tag=tag; + return FALSE; + } /* case */ case t__STATIC_ASSERT: case t__STATIC_CHECK: { int use_warning=(tok==t__STATIC_CHECK); From bedde559c3bb1d40affa18eda0a9a3c64563cc12 Mon Sep 17 00:00:00 2001 From: Stanislav Gromov Date: Sun, 15 Mar 2020 18:25:32 +0700 Subject: [PATCH 02/14] When doing case ranges, continue searching for the new insertion point from the previously inserted value, don't start over from the beginning of the list --- source/compiler/sc3.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/source/compiler/sc3.c b/source/compiler/sc3.c index d0f1cbcf..40388c4a 100644 --- a/source/compiler/sc3.c +++ b/source/compiler/sc3.c @@ -1741,15 +1741,12 @@ static int hier2(value *lval) error(50); /* invalid range */ while (++val<=end) { casecount++; - /* find the new insertion point */ - for (csp=NULL, cse=caselist.first; - cse!=NULL && cse->valuenext) - /* nothing */; + csp=newval; + cse=newval->next; if (cse!=NULL && cse->value==val) error(40,val); /* duplicate "case" label */ - assert(csp==NULL || csp->next==cse); - insert_constval(csp,cse,itoh(lbl_case),val,0); + assert(csp!=NULL && csp->next==cse); + newval=insert_constval(csp,cse,itoh(lbl_case),val,0); } /* while */ } /* if */ } while (matchtoken(',')); From a837f4b27b59bc2ed10a348b374cb3578c7ea033 Mon Sep 17 00:00:00 2001 From: Stanislav Gromov Date: Sun, 14 Mar 2021 21:17:56 +0700 Subject: [PATCH 03/14] Add tests --- source/compiler/tests/switch_expressions.meta | 15 ++++++++++ source/compiler/tests/switch_expressions.pwn | 28 +++++++++++++++++++ .../tests/switch_expressions_pcode.meta | 26 +++++++++++++++++ .../tests/switch_expressions_pcode.pwn | 13 +++++++++ 4 files changed, 82 insertions(+) create mode 100644 source/compiler/tests/switch_expressions.meta create mode 100644 source/compiler/tests/switch_expressions.pwn create mode 100644 source/compiler/tests/switch_expressions_pcode.meta create mode 100644 source/compiler/tests/switch_expressions_pcode.pwn diff --git a/source/compiler/tests/switch_expressions.meta b/source/compiler/tests/switch_expressions.meta new file mode 100644 index 00000000..a38382cb --- /dev/null +++ b/source/compiler/tests/switch_expressions.meta @@ -0,0 +1,15 @@ +{ + 'test_type': 'runtime', + 'output': """ +The discount for 1 items is 0% +The discount for 3 items is 0% +The discount for 4 items is 10% +The discount for 7 items is 10% +The discount for 10 items is 10% +The discount for 11 items is 15% +The discount for 20 items is 15% +The discount for 50 items is 15% +switch_expressions.amx returns 0 + """, + 'should_fail': False +} diff --git a/source/compiler/tests/switch_expressions.pwn b/source/compiler/tests/switch_expressions.pwn new file mode 100644 index 00000000..b4f6ff6a --- /dev/null +++ b/source/compiler/tests/switch_expressions.pwn @@ -0,0 +1,28 @@ +#include + +GetDiscount(numitems) +{ + assert(numitems > 0); + return switch (numitems; + 1, 2, 3: 0; + 4..10: 10; + _: 15; + ); +} + +PrintDiscount(numitems) +{ + printf("The discount for %d items is %d%c\n", numitems, GetDiscount(numitems), '%'); +} + +main() +{ + PrintDiscount(1); + PrintDiscount(3); + PrintDiscount(4); + PrintDiscount(7); + PrintDiscount(10); + PrintDiscount(11); + PrintDiscount(20); + PrintDiscount(50); +} diff --git a/source/compiler/tests/switch_expressions_pcode.meta b/source/compiler/tests/switch_expressions_pcode.meta new file mode 100644 index 00000000..a2c2b756 --- /dev/null +++ b/source/compiler/tests/switch_expressions_pcode.meta @@ -0,0 +1,26 @@ +{ + 'test_type': 'pcode_check', + 'code_pattern': r""" +[0-9a-f]+ proc +[0-9a-f]+ load.s.pri 0000000c +[0-9a-f]+ switch [0-9a-f]+ +[0-9a-f]+ zero.pri +[0-9a-f]+ jump [0-9a-f]+ +[0-9a-f]+ const.pri 0000000a +[0-9a-f]+ jump [0-9a-f]+ +[0-9a-f]+ const.pri 0000000f +[0-9a-f]+ jump [0-9a-f]+ +[0-9a-f]+ casetbl 0000000a [0-9a-f]+ + 00000001 [0-9a-f]+ + 00000002 [0-9a-f]+ + 00000003 [0-9a-f]+ + 00000004 [0-9a-f]+ + 00000005 [0-9a-f]+ + 00000006 [0-9a-f]+ + 00000007 [0-9a-f]+ + 00000008 [0-9a-f]+ + 00000009 [0-9a-f]+ + 0000000a [0-9a-f]+ +[0-9a-f]+ retn +""" +} diff --git a/source/compiler/tests/switch_expressions_pcode.pwn b/source/compiler/tests/switch_expressions_pcode.pwn new file mode 100644 index 00000000..b3b8d054 --- /dev/null +++ b/source/compiler/tests/switch_expressions_pcode.pwn @@ -0,0 +1,13 @@ +GetDiscount(numitems) +{ + return switch (numitems; + 1, 2, 3: 0; + 4..10: 10; + _: 15; + ); +} + +main() +{ + GetDiscount(1); +} From 705bb44d84e97caa9904b87839207d3d69feab84 Mon Sep 17 00:00:00 2001 From: Stanislav Gromov Date: Mon, 3 May 2021 17:25:15 +0700 Subject: [PATCH 04/14] Allow implicit default cases by making the `_:` part optional --- source/compiler/sc3.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/source/compiler/sc3.c b/source/compiler/sc3.c index 40388c4a..c3eedf29 100644 --- a/source/compiler/sc3.c +++ b/source/compiler/sc3.c @@ -1702,9 +1702,19 @@ static int hier2(value *lval) if (swdefault!=FALSE) error(15); /* "default" case must be last in switch statement */ do { - casecount++; stgget(&index,&cidx); /* mark position in code generator */ ident=expression(&val,&csetag,NULL,TRUE); + /* if the next token is ";" or ")", then this must be an implicit default case */ + if (matchtoken(';') || matchtoken(')')) { + lexpush(); + if (swdefault!=FALSE) + error(16); /* multiple defaults in switch */ + swdefault=TRUE; + exprtag=csetag; + sc_allowtags=bck_allowtags; /* reset */ + goto skip_impl_default; + } /* if */ + casecount++; stgdel(index,cidx); /* scratch generated code */ if (ident!=iCONSTEXPR) error(8); /* must be constant expression */ @@ -1754,6 +1764,7 @@ static int hier2(value *lval) } /* if */ sc_allowtags=bck_allowtags; /* reset */ ident=expression(NULL,&exprtag,NULL,FALSE); + skip_impl_default: if (ident==iARRAY || ident==iREFARRAY) error(33,"-unknown-"); /* array must be indexed */ if (firstcase) { From 94b4e0750a7c7fbd21f259e6794959b10d285aee Mon Sep 17 00:00:00 2001 From: Stanislav Gromov Date: Mon, 3 May 2021 18:47:10 +0700 Subject: [PATCH 05/14] Update tests --- source/compiler/tests/switch_expressions.meta | 8 +++++++ source/compiler/tests/switch_expressions.pwn | 24 +++++++++++++++++++ .../tests/switch_expressions_pcode.meta | 22 +++++++++++++++++ .../tests/switch_expressions_pcode.pwn | 12 ++++++++++ 4 files changed, 66 insertions(+) diff --git a/source/compiler/tests/switch_expressions.meta b/source/compiler/tests/switch_expressions.meta index a38382cb..77136123 100644 --- a/source/compiler/tests/switch_expressions.meta +++ b/source/compiler/tests/switch_expressions.meta @@ -9,6 +9,14 @@ The discount for 10 items is 10% The discount for 11 items is 15% The discount for 20 items is 15% The discount for 50 items is 15% +The discount for 1 items is 0% +The discount for 3 items is 0% +The discount for 4 items is 10% +The discount for 7 items is 10% +The discount for 10 items is 10% +The discount for 11 items is 15% +The discount for 20 items is 15% +The discount for 50 items is 15% switch_expressions.amx returns 0 """, 'should_fail': False diff --git a/source/compiler/tests/switch_expressions.pwn b/source/compiler/tests/switch_expressions.pwn index b4f6ff6a..d381808e 100644 --- a/source/compiler/tests/switch_expressions.pwn +++ b/source/compiler/tests/switch_expressions.pwn @@ -15,6 +15,22 @@ PrintDiscount(numitems) printf("The discount for %d items is %d%c\n", numitems, GetDiscount(numitems), '%'); } +GetDiscount2(numitems) +{ + // Alternative syntax with implicit default case and optional ";" after it. + assert(numitems > 0); + return switch (numitems; + 1, 2, 3: 0; + 4..10: 10; + 15 + ); +} + +PrintDiscount2(numitems) +{ + printf("The discount for %d items is %d%c\n", numitems, GetDiscount2(numitems), '%'); +} + main() { PrintDiscount(1); @@ -25,4 +41,12 @@ main() PrintDiscount(11); PrintDiscount(20); PrintDiscount(50); + PrintDiscount2(1); + PrintDiscount2(3); + PrintDiscount2(4); + PrintDiscount2(7); + PrintDiscount2(10); + PrintDiscount2(11); + PrintDiscount2(20); + PrintDiscount2(50); } diff --git a/source/compiler/tests/switch_expressions_pcode.meta b/source/compiler/tests/switch_expressions_pcode.meta index a2c2b756..2ecf2a67 100644 --- a/source/compiler/tests/switch_expressions_pcode.meta +++ b/source/compiler/tests/switch_expressions_pcode.meta @@ -22,5 +22,27 @@ 00000009 [0-9a-f]+ 0000000a [0-9a-f]+ [0-9a-f]+ retn + +[0-9a-f]+ proc +[0-9a-f]+ load.s.pri 0000000c +[0-9a-f]+ switch [0-9a-f]+ +[0-9a-f]+ zero.pri +[0-9a-f]+ jump [0-9a-f]+ +[0-9a-f]+ const.pri 0000000a +[0-9a-f]+ jump [0-9a-f]+ +[0-9a-f]+ const.pri 0000000f +[0-9a-f]+ jump [0-9a-f]+ +[0-9a-f]+ casetbl 0000000a [0-9a-f]+ + 00000001 [0-9a-f]+ + 00000002 [0-9a-f]+ + 00000003 [0-9a-f]+ + 00000004 [0-9a-f]+ + 00000005 [0-9a-f]+ + 00000006 [0-9a-f]+ + 00000007 [0-9a-f]+ + 00000008 [0-9a-f]+ + 00000009 [0-9a-f]+ + 0000000a [0-9a-f]+ +[0-9a-f]+ retn """ } diff --git a/source/compiler/tests/switch_expressions_pcode.pwn b/source/compiler/tests/switch_expressions_pcode.pwn index b3b8d054..541a4c40 100644 --- a/source/compiler/tests/switch_expressions_pcode.pwn +++ b/source/compiler/tests/switch_expressions_pcode.pwn @@ -7,7 +7,19 @@ GetDiscount(numitems) ); } +GetDiscount2(numitems) +{ + // Alternative syntax with implicit default case and optional ";" after it. + assert(numitems > 0); + return switch (numitems; + 1, 2, 3: 0; + 4..10: 10; + 15 + ); +} + main() { GetDiscount(1); + GetDiscount2(1); } From 8a4424c0a99e3bd726e5f522c83f28cd94eb4ed5 Mon Sep 17 00:00:00 2001 From: Stanislav Gromov Date: Mon, 3 May 2021 18:48:04 +0700 Subject: [PATCH 06/14] Rename the runtime test --- ...{switch_expressions.meta => switch_expressions_runtime.meta} | 2 +- .../{switch_expressions.pwn => switch_expressions_runtime.pwn} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename source/compiler/tests/{switch_expressions.meta => switch_expressions_runtime.meta} (93%) rename source/compiler/tests/{switch_expressions.pwn => switch_expressions_runtime.pwn} (100%) diff --git a/source/compiler/tests/switch_expressions.meta b/source/compiler/tests/switch_expressions_runtime.meta similarity index 93% rename from source/compiler/tests/switch_expressions.meta rename to source/compiler/tests/switch_expressions_runtime.meta index 77136123..5fce488d 100644 --- a/source/compiler/tests/switch_expressions.meta +++ b/source/compiler/tests/switch_expressions_runtime.meta @@ -17,7 +17,7 @@ The discount for 10 items is 10% The discount for 11 items is 15% The discount for 20 items is 15% The discount for 50 items is 15% -switch_expressions.amx returns 0 +switch_expressions_runtime.amx returns 0 """, 'should_fail': False } diff --git a/source/compiler/tests/switch_expressions.pwn b/source/compiler/tests/switch_expressions_runtime.pwn similarity index 100% rename from source/compiler/tests/switch_expressions.pwn rename to source/compiler/tests/switch_expressions_runtime.pwn From e5e9bd290e4a0eda5c248ac98d0c45a5ef7e4455 Mon Sep 17 00:00:00 2001 From: Stanislav Gromov Date: Mon, 3 May 2021 19:21:30 +0700 Subject: [PATCH 07/14] Add more thorough syntax tests --- .../tests/switch_expressions_syntax.meta | 16 ++++ .../tests/switch_expressions_syntax.pwn | 84 +++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 source/compiler/tests/switch_expressions_syntax.meta create mode 100644 source/compiler/tests/switch_expressions_syntax.pwn diff --git a/source/compiler/tests/switch_expressions_syntax.meta b/source/compiler/tests/switch_expressions_syntax.meta new file mode 100644 index 00000000..671815fb --- /dev/null +++ b/source/compiler/tests/switch_expressions_syntax.meta @@ -0,0 +1,16 @@ +{ + 'test_type': 'output_check', + 'errors': """ +switch_expressions_syntax.pwn(7 -- 8) : error 008: must be a constant expression; assumed zero +switch_expressions_syntax.pwn(15 -- 17) : error 015: "default" case must be the last case in switch statement +switch_expressions_syntax.pwn(23 -- 25) : error 016: multiple defaults in "switch" +switch_expressions_syntax.pwn(31 -- 32) : error 001: expected token: ";", but found "-integer value-" +switch_expressions_syntax.pwn(31 -- 34) : error 001: expected token: ";", but found "-label-" +switch_expressions_syntax.pwn(40 -- 41) : error 001: expected token: ":", but found "-integer value-" +switch_expressions_syntax.pwn(40 -- 43) : error 029: invalid expression, assumed zero +switch_expressions_syntax.pwn(49 -- 51) : error 040: duplicate "case" label (value 1) +switch_expressions_syntax.pwn(58 -- 59) : error 050: invalid range +switch_expressions_syntax.pwn(66 -- 68) : error 040: duplicate "case" label (value 1) +switch_expressions_syntax.pwn(66 -- 69) : error 040: duplicate "case" label (value 3) +""" +} diff --git a/source/compiler/tests/switch_expressions_syntax.pwn b/source/compiler/tests/switch_expressions_syntax.pwn new file mode 100644 index 00000000..a73d352f --- /dev/null +++ b/source/compiler/tests/switch_expressions_syntax.pwn @@ -0,0 +1,84 @@ +#pragma semicolon 1 + +Func() return 0; + +test_NonConstCaseValue(value) +{ + return switch (value; + Func(): 0; // error 008: must be a constant expression; assumed zero + _: 1 + ); +} + +test_CaseAfterDefault(value) +{ + return switch (value; + _: 0; + 1: 1 // error 015: "default" case must be the last case in switch statement + ); +} + +test_MultipleDefaults(value) +{ + return switch (value; + _: 0; + _: 1 // error 016: multiple defaults in "switch" + ); +} + +test_MissingSemicolon(value) +{ + return switch (value + 1: 0; // error 001: expected token: ";", but found "-integer value-" + 2: 1 + _: 2 // error 001: expected token: ";", but found "-label-" + ); +} + +test_MissingColon(value) +{ + return switch (value; + 1 0; // error 001: expected token: ":", but found "-integer value-" + 2: 1; + _ 2 // error 029: invalid expression, assumed zero + ); +} + +test_DuplicateCases(value) +{ + return switch (value; + 1: 0; + 1: 1; // error 040: duplicate "case" label (value 1) + _: 2 + ); +} + +test_InvalidRange(value) +{ + return switch (value; + 1..1: 0; // error 050: invalid range + _: 1 + ); +} + +test_OverlayingRanges(value) +{ + return switch (value; + 1..10: 0; + 0..9: 1; // error 040: duplicate "case" label (value 1) + 3..8: 2; // error 040: duplicate "case" label (value 3) + _: 3 + ); +} + +main() +{ + test_NonConstCaseValue(0); + test_CaseAfterDefault(0); + test_MultipleDefaults(0); + test_MissingSemicolon(0); + test_MissingColon(0); + test_DuplicateCases(0); + test_InvalidRange(0); + test_OverlayingRanges(0); +} \ No newline at end of file From 18f7fa860fe9c6f7aef557773f2d6c932c385272 Mon Sep 17 00:00:00 2001 From: Stanislav Gromov Date: Mon, 3 May 2021 19:59:11 +0700 Subject: [PATCH 08/14] Check the tag of the range end value --- source/compiler/sc3.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source/compiler/sc3.c b/source/compiler/sc3.c index c3eedf29..473c0c84 100644 --- a/source/compiler/sc3.c +++ b/source/compiler/sc3.c @@ -1749,6 +1749,7 @@ static int hier2(value *lval) error(8); /* must be constant expression */ if (end<=val) error(50); /* invalid range */ + check_tagmismatch(swtag,csetag,TRUE,-1); while (++val<=end) { casecount++; csp=newval; From 8a6693ffadfdc34077955ea6b9665dcc2e57b8d1 Mon Sep 17 00:00:00 2001 From: Stanislav Gromov Date: Mon, 3 May 2021 23:32:20 +0700 Subject: [PATCH 09/14] Properly handle named constants --- source/compiler/sc3.c | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/source/compiler/sc3.c b/source/compiler/sc3.c index 473c0c84..d53c0d13 100644 --- a/source/compiler/sc3.c +++ b/source/compiler/sc3.c @@ -1682,6 +1682,7 @@ static int hier2(value *lval) swdefault=FALSE; firstcase=TRUE; do { + int got_cseval=FALSE; /* true if the case value gets misinterpreted by lex() as a label */ needtoken(tTERM); ident=lex(&val,&st); if (ident==')') @@ -1695,15 +1696,29 @@ static int hier2(value *lval) error(16); /* multiple defaults in switch */ swdefault=TRUE; } else { - if (ident!=tLABEL) + if (ident!=tLABEL) { lexpush(); - else - error(220); /* expression with tag override must appear between parentheses */ + } else { + symbol *csesym=findloc(st); + if (csesym==NULL) + csesym=findglb(st,sGLOBAL); + if (csesym!=NULL) { + markusage(csesym,uREAD); + ident=csesym->ident; + val=csesym->addr; + csetag=csesym->tag; + got_cseval=TRUE; + } else { + error(220); /* expression with tag override must appear between parentheses */ + } /* if */ + } /* if */ if (swdefault!=FALSE) error(15); /* "default" case must be last in switch statement */ do { - stgget(&index,&cidx); /* mark position in code generator */ - ident=expression(&val,&csetag,NULL,TRUE); + if (!got_cseval) { + stgget(&index,&cidx); /* mark position in code generator */ + ident=expression(&val,&csetag,NULL,TRUE); + } /* if */ /* if the next token is ";" or ")", then this must be an implicit default case */ if (matchtoken(';') || matchtoken(')')) { lexpush(); @@ -1715,7 +1730,8 @@ static int hier2(value *lval) goto skip_impl_default; } /* if */ casecount++; - stgdel(index,cidx); /* scratch generated code */ + if (!got_cseval) + stgdel(index,cidx); /* scratch generated code */ if (ident!=iCONSTEXPR) error(8); /* must be constant expression */ check_tagmismatch(swtag,csetag,TRUE,-1); @@ -1740,7 +1756,7 @@ static int hier2(value *lval) newval=insert_constval(csp,cse,itoh(lbl_case),val,0); if (csp==NULL) caselist.first=newval; - if (matchtoken(tDBLDOT)) { + if (!got_cseval && matchtoken(tDBLDOT)) { cell end; stgget(&index,&cidx); /* mark position in code generator */ ident=expression(&end,&csetag,NULL,TRUE); @@ -1761,7 +1777,8 @@ static int hier2(value *lval) } /* while */ } /* if */ } while (matchtoken(',')); - needtoken(':'); /* ':' ends the case */ + if (!got_cseval) + needtoken(':'); /* ':' ends the case */ } /* if */ sc_allowtags=bck_allowtags; /* reset */ ident=expression(NULL,&exprtag,NULL,FALSE); From 87b76af9fb4f0cb7df3cc5560667c2497707e603 Mon Sep 17 00:00:00 2001 From: Stanislav Gromov Date: Mon, 3 May 2021 23:55:18 +0700 Subject: [PATCH 10/14] Fix assertion failure due to the case list not being sorted properly on duplicate case values --- source/compiler/sc3.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/source/compiler/sc3.c b/source/compiler/sc3.c index d53c0d13..979abfaf 100644 --- a/source/compiler/sc3.c +++ b/source/compiler/sc3.c @@ -1768,8 +1768,11 @@ static int hier2(value *lval) check_tagmismatch(swtag,csetag,TRUE,-1); while (++val<=end) { casecount++; - csp=newval; - cse=newval->next; + /* find the new insertion point */ + for (csp=NULL, cse=caselist.first; + cse!=NULL && cse->valuenext) + /* nothing */; if (cse!=NULL && cse->value==val) error(40,val); /* duplicate "case" label */ assert(csp!=NULL && csp->next==cse); From f38a8e8812a5ec49c69f2cea7edcb0885add0568 Mon Sep 17 00:00:00 2001 From: Stanislav Gromov Date: Tue, 4 May 2021 15:03:49 +0700 Subject: [PATCH 11/14] Add a new error for situations when a switch expression doesn't have a default case --- source/compiler/sc3.c | 1 + source/compiler/sc5.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/source/compiler/sc3.c b/source/compiler/sc3.c index 979abfaf..c62987b1 100644 --- a/source/compiler/sc3.c +++ b/source/compiler/sc3.c @@ -1807,6 +1807,7 @@ static int hier2(value *lval) setlabel(lbl_table); assert(swdefault==FALSE || swdefault==TRUE); if (swdefault==FALSE) { + error(95); /* switch expression must contain a "default" case */ /* store lbl_exit as the "none-matched" label in the switch table */ strcpy(labelname,itoh(lbl_exit)); } else { diff --git a/source/compiler/sc5.c b/source/compiler/sc5.c index ae126ec9..afc85dcd 100644 --- a/source/compiler/sc5.c +++ b/source/compiler/sc5.c @@ -133,7 +133,8 @@ static char *errmsg[] = { /*091*/ "ambiguous constant; tag override is required (symbol \"%s\")\n", /*092*/ "functions may not return arrays of unknown size (symbol \"%s\")\n", /*093*/ "\"__addressof\" operator is invalid in preprocessor expressions\n", -/*094*/ "division by zero\n" +/*094*/ "division by zero\n", +/*095*/ "switch expression must contain a \"default\" case\n" }; static char *fatalmsg[] = { From 7d8adc53be742e04fddcb37a0dc7ad4977fd2bb6 Mon Sep 17 00:00:00 2001 From: Stanislav Gromov Date: Tue, 4 May 2021 20:08:05 +0700 Subject: [PATCH 12/14] Update tests --- .../tests/switch_expressions_syntax.meta | 6 ++++ .../tests/switch_expressions_syntax.pwn | 34 ++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/source/compiler/tests/switch_expressions_syntax.meta b/source/compiler/tests/switch_expressions_syntax.meta index 671815fb..a305debf 100644 --- a/source/compiler/tests/switch_expressions_syntax.meta +++ b/source/compiler/tests/switch_expressions_syntax.meta @@ -12,5 +12,11 @@ switch_expressions_syntax.pwn(49 -- 51) : error 040: duplicate "case" label (val switch_expressions_syntax.pwn(58 -- 59) : error 050: invalid range switch_expressions_syntax.pwn(66 -- 68) : error 040: duplicate "case" label (value 1) switch_expressions_syntax.pwn(66 -- 69) : error 040: duplicate "case" label (value 3) +switch_expressions_syntax.pwn(77 -- 78) : warning 213: tag mismatch: expected tag "Tag", but found none ("_") +switch_expressions_syntax.pwn(77 -- 80) : warning 213: tag mismatch: expected tag "Tag", but found none ("_") +switch_expressions_syntax.pwn(77 -- 81) : warning 213: tag mismatch: expected tag "Tag", but found none ("_") +switch_expressions_syntax.pwn(89 -- 91) : warning 213: tag mismatch: expected tag "Tag", but found none ("_") +switch_expressions_syntax.pwn(89 -- 93) : warning 213: tag mismatch: expected tag "Tag", but found none ("_") +switch_expressions_syntax.pwn(98 -- 100) : error 095: switch expression must contain a "default" case """ } diff --git a/source/compiler/tests/switch_expressions_syntax.pwn b/source/compiler/tests/switch_expressions_syntax.pwn index a73d352f..7df8e7c3 100644 --- a/source/compiler/tests/switch_expressions_syntax.pwn +++ b/source/compiler/tests/switch_expressions_syntax.pwn @@ -71,6 +71,35 @@ test_OverlayingRanges(value) ); } +test_TagMismatchCase(value) +{ + const Tag:TaggedConst = Tag:1; + return switch (Tag:value; + 0: 0; // warning 213: tag mismatch: expected tag "Tag", but found none ("_") + TaggedConst: 1; + (Tag:2)..3: 2; // warning 213: tag mismatch: expected tag "Tag", but found none ("_") + 4..(Tag:5): 3; // warning 213: tag mismatch: expected tag "Tag", but found none ("_") + (Tag:6)..(Tag:7): 4; + _: 5 + ); +} + +Tag:test_TagMismatchExpr(value) +{ + return switch (value; + 0: Tag:0; + 1: 1; // warning 213: tag mismatch: expected tag "Tag", but found none ("_") + _: 2 // warning 213: tag mismatch: expected tag "Tag", but found none ("_") + ); +} + +test_MissingDefault(value) +{ + return switch (value; + 0: 0 + ); // error 095: switch expression must contain a "default" case +} + main() { test_NonConstCaseValue(0); @@ -81,4 +110,7 @@ main() test_DuplicateCases(0); test_InvalidRange(0); test_OverlayingRanges(0); -} \ No newline at end of file + test_TagMismatchCase(0); + test_TagMismatchExpr(0); + test_MissingDefault(0); +} From 7a7866983e9766dea3ca46280f3ca13e7a3aea9a Mon Sep 17 00:00:00 2001 From: Stanislav Gromov Date: Sun, 2 Jan 2022 20:55:53 +0700 Subject: [PATCH 13/14] Require return value if a function is used in a switch expression case --- source/compiler/sc3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/compiler/sc3.c b/source/compiler/sc3.c index c62987b1..fb1e5286 100644 --- a/source/compiler/sc3.c +++ b/source/compiler/sc3.c @@ -1784,7 +1784,7 @@ static int hier2(value *lval) needtoken(':'); /* ':' ends the case */ } /* if */ sc_allowtags=bck_allowtags; /* reset */ - ident=expression(NULL,&exprtag,NULL,FALSE); + ident=expression(NULL,&exprtag,NULL,TRUE); skip_impl_default: if (ident==iARRAY || ident==iREFARRAY) error(33,"-unknown-"); /* array must be indexed */ From 05c347e4e3293b49db79b5332df7c3c7dffd9ff5 Mon Sep 17 00:00:00 2001 From: Stanislav Gromov Date: Sun, 2 Jan 2022 20:56:02 +0700 Subject: [PATCH 14/14] Update tests --- .../tests/switch_expressions_syntax.meta | 36 ++++++++++--------- .../tests/switch_expressions_syntax.pwn | 10 ++++++ 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/source/compiler/tests/switch_expressions_syntax.meta b/source/compiler/tests/switch_expressions_syntax.meta index a305debf..abb3e62d 100644 --- a/source/compiler/tests/switch_expressions_syntax.meta +++ b/source/compiler/tests/switch_expressions_syntax.meta @@ -1,22 +1,24 @@ { 'test_type': 'output_check', 'errors': """ -switch_expressions_syntax.pwn(7 -- 8) : error 008: must be a constant expression; assumed zero -switch_expressions_syntax.pwn(15 -- 17) : error 015: "default" case must be the last case in switch statement -switch_expressions_syntax.pwn(23 -- 25) : error 016: multiple defaults in "switch" -switch_expressions_syntax.pwn(31 -- 32) : error 001: expected token: ";", but found "-integer value-" -switch_expressions_syntax.pwn(31 -- 34) : error 001: expected token: ";", but found "-label-" -switch_expressions_syntax.pwn(40 -- 41) : error 001: expected token: ":", but found "-integer value-" -switch_expressions_syntax.pwn(40 -- 43) : error 029: invalid expression, assumed zero -switch_expressions_syntax.pwn(49 -- 51) : error 040: duplicate "case" label (value 1) -switch_expressions_syntax.pwn(58 -- 59) : error 050: invalid range -switch_expressions_syntax.pwn(66 -- 68) : error 040: duplicate "case" label (value 1) -switch_expressions_syntax.pwn(66 -- 69) : error 040: duplicate "case" label (value 3) -switch_expressions_syntax.pwn(77 -- 78) : warning 213: tag mismatch: expected tag "Tag", but found none ("_") -switch_expressions_syntax.pwn(77 -- 80) : warning 213: tag mismatch: expected tag "Tag", but found none ("_") -switch_expressions_syntax.pwn(77 -- 81) : warning 213: tag mismatch: expected tag "Tag", but found none ("_") -switch_expressions_syntax.pwn(89 -- 91) : warning 213: tag mismatch: expected tag "Tag", but found none ("_") -switch_expressions_syntax.pwn(89 -- 93) : warning 213: tag mismatch: expected tag "Tag", but found none ("_") -switch_expressions_syntax.pwn(98 -- 100) : error 095: switch expression must contain a "default" case +switch_expressions_syntax.pwn(8 -- 9) : error 008: must be a constant expression; assumed zero +switch_expressions_syntax.pwn(16 -- 18) : error 015: "default" case must be the last case in switch statement +switch_expressions_syntax.pwn(24 -- 26) : error 016: multiple defaults in "switch" +switch_expressions_syntax.pwn(32 -- 33) : error 001: expected token: ";", but found "-integer value-" +switch_expressions_syntax.pwn(32 -- 35) : error 001: expected token: ";", but found "-label-" +switch_expressions_syntax.pwn(41 -- 42) : error 001: expected token: ":", but found "-integer value-" +switch_expressions_syntax.pwn(41 -- 44) : error 029: invalid expression, assumed zero +switch_expressions_syntax.pwn(50 -- 52) : error 040: duplicate "case" label (value 1) +switch_expressions_syntax.pwn(59 -- 60) : error 050: invalid range +switch_expressions_syntax.pwn(67 -- 69) : error 040: duplicate "case" label (value 1) +switch_expressions_syntax.pwn(67 -- 70) : error 040: duplicate "case" label (value 3) +switch_expressions_syntax.pwn(78 -- 79) : warning 213: tag mismatch: expected tag "Tag", but found none ("_") +switch_expressions_syntax.pwn(78 -- 81) : warning 213: tag mismatch: expected tag "Tag", but found none ("_") +switch_expressions_syntax.pwn(78 -- 82) : warning 213: tag mismatch: expected tag "Tag", but found none ("_") +switch_expressions_syntax.pwn(90 -- 92) : warning 213: tag mismatch: expected tag "Tag", but found none ("_") +switch_expressions_syntax.pwn(90 -- 94) : warning 213: tag mismatch: expected tag "Tag", but found none ("_") +switch_expressions_syntax.pwn(99 -- 101) : error 095: switch expression must contain a "default" case +switch_expressions_syntax.pwn(106 -- 107) : warning 209: function "FuncNoRetVal" should return a value +switch_expressions_syntax.pwn(106 -- 108) : warning 209: function "FuncNoRetVal" should return a value """ } diff --git a/source/compiler/tests/switch_expressions_syntax.pwn b/source/compiler/tests/switch_expressions_syntax.pwn index 7df8e7c3..40fbc769 100644 --- a/source/compiler/tests/switch_expressions_syntax.pwn +++ b/source/compiler/tests/switch_expressions_syntax.pwn @@ -1,6 +1,7 @@ #pragma semicolon 1 Func() return 0; +FuncNoRetVal(){} test_NonConstCaseValue(value) { @@ -100,6 +101,14 @@ test_MissingDefault(value) ); // error 095: switch expression must contain a "default" case } +test_NoReturnValue(value) +{ + return switch (value; + 0: FuncNoRetVal(); // warning 209: function "FuncNoRetVal" should return a value + _: FuncNoRetVal(); // warning 209: function "FuncNoRetVal" should return a value + ); +} + main() { test_NonConstCaseValue(0); @@ -113,4 +122,5 @@ main() test_TagMismatchCase(0); test_TagMismatchExpr(0); test_MissingDefault(0); + test_NoReturnValue(0); }