1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
/* Check for illegal return/break/continue usage inside a STC-lib c_auto* block (RAII).
* Copyright Tyge Løvset, (c) 2023.
*/
%{
#include <stdbool.h>
enum { LOOP=1<<0, AUTO=1<<1 };
enum { NORMAL, BRACES, BRACESDONE };
static int braces_lev = 0, block_lev = 0;
static int state = NORMAL;
static unsigned int block[64] = {0}, block_type = 0;
const char* fname;
int errors = 0, warnings = 0;
%}
ID [_a-zA-Z][_a-zA-Z0-9]*
STR \"([^"\\]|\\.)*\"
%option never-interactive noyymore noyywrap nounistd
%x cmt
%x prep
%%
\/\/.* ; // line cmt
\/\* BEGIN(cmt);
<cmt>\n ++yylineno;
<cmt>\*\/ BEGIN(INITIAL);
<cmt>. ;
^[ \t]*#.*\\\n { ++yylineno; BEGIN(prep); }
<prep>.*\\\n ++yylineno;
<prep>.* BEGIN(INITIAL);
^[ \t]*#.* ;
{STR} ;
'\\?.' ;
c_foreach |
c_forpair |
c_forrange |
c_forfilter |
c_formatch |
c_fortoken |
for |
while |
switch { block_type |= LOOP; state = BRACES; }
do { block_type |= LOOP; state = BRACESDONE; }
c_defer |
c_scope |
c_with |
c_auto { block_type = AUTO; state = BRACES; }
\( { if (state == BRACES) ++braces_lev; }
\) { if (state == BRACES && --braces_lev == 0) {
state = BRACESDONE;
}
}
if { if (state == BRACESDONE) {
if (block_type == AUTO) {
printf("%s:%d: warning: 'if' after c_auto* not enclosed in curly braces.\n"
" Make sure to enclose 'if - else' statement in { } after c_auto*.\n",
fname, yylineno);
++warnings;
}
state = BRACES;
}
}
;[ \t]*else ;
; { if (state == BRACESDONE) {
block_type = block[block_lev];
state = NORMAL;
}
}
\{ { if (state != BRACES) { block[++block_lev] = block_type; state = NORMAL; } }
\} { if (state != BRACES) block_type = block[--block_lev]; }
return { if (block_type == AUTO) {
printf("%s:%d: error: 'return' used inside a c_auto* scope.\n"
" Use 'continue' to exit the current c_auto* scope before return.\n"
, fname, yylineno);
++errors;
} else if (block_type & AUTO) {
printf("%s:%d: error: 'return' used in a loop inside a c_auto* scope.\n"
" Use 'break' to exit loop, then 'continue' to exit c_auto*.\n"
, fname, yylineno);
++errors;
}
}
break { if (block_type == AUTO) {
printf("%s:%d: error: 'break' used inside a c_auto* scope.\n"
" Use 'continue' to exit the current c_auto* scope.\n"
, fname, yylineno);
++errors;
}
}
{ID} ;
\n ++yylineno;
. ;
%%
#include <string.h>
int main(int argc, char **argv)
{
if (argc == 1 || strcmp(argv[1], "--help") == 0) {
printf("usage: %s [--help] {C-file... | -}\n", argv[0]);
return 0;
}
for (int i=1; i<argc; ++i) {
if (strcmp(argv[i], "-") == 0) {
fname = "<stdin>";
yyin = stdin;
} else {
fname = argv[i];
yyin = fopen(fname, "r");
}
yylineno = 1;
braces_lev = 0, block_lev = 0;
block_type = 0, state = NORMAL;
yylex();
fclose(yyin);
}
if (errors + warnings)
printf("%d error%s, %d warning%s.\n", errors, errors == 1? "":"s",
warnings, warnings == 1? "":"s");
return errors;
}
|