c.rl assumes syntactically correct C now.
[ohcount] / ext / ohcount_native / ragel_parsers / c.rl
1 /************************* Required for every parser *************************/
2
3 // the name of the language
4 const char *LANG = "c";
5
6 // the languages entities
7 const char *c_entities[] = {
8   "space", "comment", "string", "number", "preproc", "keyword",
9   "identifier", "operator", "escaped_newline", "newline", "any"
10 };
11
12 // constants associated with the entities
13 enum {
14   SPACE = 0, COMMENT, STRING, NUMBER, PREPROC, KEYWORD,
15   IDENTIFIER, OPERATOR, ESCAPED_NL, NEWLINE, ANY
16 };
17
18 // do not change the following variables
19
20 // used for newlines inside patterns like strings and comments that can have
21 // newlines in them
22 #define INTERNAL_NL -1
23
24 // required by Ragel
25 int cs, act;
26 char *p, *pe, *eof, *ts, *te;
27
28 // used for calculating offsets from buffer start for start and end positions
29 char *buffer_start;
30 #define cint(c) ((int) (c - buffer_start))
31
32 // state flags for line and comment counting
33 int whole_line_comment;
34 int line_contains_code;
35
36 // the beginning of a line in the buffer for line and comment counting
37 char *line_start;
38
39 // state variable for the current entity being matched
40 int entity;
41
42 /*****************************************************************************/
43
44 %%{
45   machine c;
46   write data;
47   include common "common.rl";
48
49   action c_callback {
50     switch(entity) {
51     case SPACE:
52     case ANY:
53       if (!line_start) line_start = ts;
54       break;
55     //case COMMENT:
56     //case STRING:
57     case NUMBER:
58     //case PREPROC:
59     case KEYWORD:
60     case IDENTIFIER:
61     case OPERATOR:
62       if (!line_contains_code && !line_start) line_start = ts;
63       line_contains_code = 1;
64       break;
65     case ESCAPED_NL:
66     case INTERNAL_NL:
67       if (c_callback && p > line_start) {
68         if (line_contains_code)
69           c_callback(LANG, "lcode", cint(line_start), cint(p));
70         else if (whole_line_comment)
71           c_callback(LANG, "lcomment", cint(line_start), cint(p));
72         else
73           c_callback(LANG, "lblank", cint(line_start), cint(p));
74         whole_line_comment = 0;
75         line_contains_code = 0;
76         line_start = p;
77       }
78       break;
79     case NEWLINE:
80       if (c_callback && te > line_start) {
81         if (line_contains_code)
82           c_callback(LANG, "lcode", cint(line_start), cint(te));
83         else if (whole_line_comment)
84           c_callback(LANG, "lcomment", cint(line_start), cint(te));
85         else
86           c_callback(LANG, "lblank", cint(ts), cint(te));
87       }
88       whole_line_comment = 0;
89       line_contains_code = 0;
90       line_start = 0;
91     }
92     if (c_callback && entity != INTERNAL_NL)
93       c_callback(LANG, c_entities[entity], cint(ts), cint(te));
94   }
95
96   c_line_comment =
97     '//' @comment (
98       escaped_newline %{ entity = INTERNAL_NL; } %c_callback
99       |
100       ws
101       |
102       nonnewline @comment
103     )*;
104   c_block_comment =
105     '/*' @comment (
106       newline %{ entity = INTERNAL_NL; } %c_callback
107       |
108       ws
109       |
110       nonnewline @comment
111     )* :>> '*/';
112   c_comment = c_line_comment | c_block_comment;
113
114   c_sq_str =
115     '\'' @code (
116       newline %{ entity = INTERNAL_NL; } %c_callback
117       |
118       ws
119       |
120       [^'\\] @code
121       |
122       '\\' any @code
123     )* '\'';
124   c_dq_str =
125     '"' @code (
126       newline %{ entity = INTERNAL_NL; } %c_callback
127       |
128       ws
129       |
130       [^"\\] @code
131       |
132       '\\' any @code
133     )* '"';
134   c_string = c_sq_str | c_dq_str;
135
136   c_number = float | integer;
137
138   c_preproc_word =
139     'define' | 'elif' | 'else' | 'endif' | 'error' | 'if' | 'ifdef' |
140     'ifndef' | 'import' | 'include' | 'line' | 'pragma' | 'undef' |
141     'using';
142   c_preproc =
143     ('#' when no_code) ws* c_preproc_word
144     (
145       escaped_newline %{ entity = INTERNAL_NL; } %c_callback
146       |
147       ws
148       |
149       nonnewline @code
150     )*;
151
152   c_identifier = (alpha | '_') (alnum | '_')*;
153
154   c_keyword =
155     'and' | 'and_eq' | 'asm' | 'auto' | 'bitand' | 'bitor' | 'bool' |
156     'break' | 'case' | 'catch' | 'char' | 'class' | 'compl' | 'const' |
157     'const_cast' | 'continue' | 'default' | 'delete' | 'do' | 'double' |
158     'dynamic_cast' | 'else' | 'enum' | 'explicit' | 'export' | 'extern' |
159     'false' | 'float' | 'for' | 'friend' | 'goto' | 'if' | 'inline' | 'int' |
160     'long' | 'mutable' | 'namespace' | 'new' | 'not' | 'not_eq' |
161     'operator' | 'or' | 'or_eq' | 'private' | 'protected' | 'public' |
162     'register' | 'reinterpret_cast' | 'return' | 'short' | 'signed' |
163     'sizeof' | 'static' | 'static_cast' | 'struct' | 'switch' |
164     'template' | 'this' | 'throw' | 'true' | 'try' | 'typedef' | 'typeid' |
165     'typename' | 'union' | 'unsigned' | 'using' | 'virtual' | 'void' |
166     'volatile' | 'wchar_t' | 'while' | 'xor' | 'xor_eq';
167
168   c_operator = [+\-/*%<>!=^&|?~:;.,()\[\]{}@];
169
170   c_line := |*
171     spaces            ${ entity = SPACE;       } => c_callback;
172     c_comment         ${ entity = COMMENT;     } => c_callback;
173     c_string          ${ entity = STRING;      } => c_callback;
174     c_number          ${ entity = NUMBER;      } => c_callback;
175     c_preproc         ${ entity = PREPROC;     } => c_callback;
176     c_identifier      ${ entity = IDENTIFIER;  } => c_callback;
177     c_keyword         ${ entity = KEYWORD;     } => c_callback;
178     c_operator        ${ entity = OPERATOR;    } => c_callback;
179     escaped_newline   ${ entity = ESCAPED_NL;  } => c_callback;
180     newline           ${ entity = NEWLINE;     } => c_callback;
181     nonprintable_char ${ entity = ANY;         } => c_callback;
182   *|;
183 }%%
184
185 /* Parses a string buffer with C/C++ code.
186  *
187  * @param *buffer The string to parse.
188  * @param length The length of the string to parse.
189  * @param *c_callback Callback function called for each entity. Entities are
190  *   the ones defined in the lexer as well as 3 additional entities used by
191  *   Ohcount for counting lines: lcode, lcomment, lblank.
192  */
193 void parse_c(char *buffer, int length,
194   void (*c_callback) (const char *lang, const char *entity, int start, int end)
195   ) {
196   p = buffer;
197   pe = buffer + length;
198   eof = pe;
199
200   buffer_start = buffer;
201   whole_line_comment = 0;
202   line_contains_code = 0;
203   line_start = 0;
204   entity = 0;
205
206   %% write init;
207   %% write exec;
208
209   // no newline at EOF; get contents of last line
210   if ((whole_line_comment || line_contains_code) && c_callback) {
211     if (line_contains_code)
212       c_callback(LANG, "lcode", cint(line_start), cint(pe));
213     else if (whole_line_comment)
214       c_callback(LANG, "lcomment", cint(line_start), cint(pe));
215   }
216 }