OTWO-1213 Works around lost encoding in Ruby/C binding layer
[ohcount] / src / parsers / c.rl
1 // c.rl written by Mitchell Foral. mitchell<att>caladbolg<dott>net.
2
3 /************************* Required for every parser *************************/
4 #ifndef OHCOUNT_C_PARSER_H
5 #define OHCOUNT_C_PARSER_H
6
7 #include "../parser_macros.h"
8
9 // the name of the language
10 const char *C_LANG = LANG_C;
11
12 // the languages entities
13 const char *c_entities[] = {
14   "space", "comment", "string", "number", "preproc",
15   "keyword", "identifier", "operator", "any"
16 };
17
18 // constants associated with the entities
19 enum {
20   C_SPACE = 0, C_COMMENT, C_STRING, C_NUMBER, C_PREPROC,
21   C_KEYWORD, C_IDENTIFIER, C_OPERATOR, C_ANY
22 };
23
24 /*****************************************************************************/
25
26 %%{
27   machine c;
28   write data;
29   include common "common.rl";
30
31   # Line counting machine
32
33   action c_ccallback {
34     switch(entity) {
35     case C_SPACE:
36       ls
37       break;
38     case C_ANY:
39       code
40       break;
41     case INTERNAL_NL:
42       std_internal_newline(C_LANG)
43       break;
44     case NEWLINE:
45       std_newline(C_LANG)
46     }
47   }
48
49   c_line_comment =
50     '//' @comment (
51       escaped_newline %{ entity = INTERNAL_NL; } %c_ccallback
52       |
53       ws
54       |
55       (nonnewline - ws) @comment
56     )*;
57   c_block_comment =
58     '/*' @comment (
59       newline %{ entity = INTERNAL_NL; } %c_ccallback
60       |
61       ws
62       |
63       (nonnewline - ws) @comment
64     )* :>> '*/';
65   c_comment = c_line_comment | c_block_comment;
66
67   c_sq_str =
68     '\'' @code (
69       escaped_newline %{ entity = INTERNAL_NL; } %c_ccallback
70       |
71       ws
72       |
73       [^\t '\\] @code
74       |
75       '\\' nonnewline @code
76     )* '\'';
77   c_dq_str =
78     '"' @code (
79       escaped_newline %{ entity = INTERNAL_NL; } %c_ccallback
80       |
81       ws
82       |
83       [^\t "\\] @code
84       |
85       '\\' nonnewline @code
86     )* '"';
87   c_string = c_sq_str | c_dq_str;
88
89   c_line := |*
90     spaces    ${ entity = C_SPACE; } => c_ccallback;
91     c_comment;
92     c_string;
93     newline   ${ entity = NEWLINE; } => c_ccallback;
94     ^space    ${ entity = C_ANY;   } => c_ccallback;
95   *|;
96
97   # Entity machine
98
99   action c_ecallback {
100     callback(C_LANG, c_entities[entity], cint(ts), cint(te), userdata);
101   }
102
103   c_line_comment_entity = '//' (escaped_newline | nonnewline)*;
104   c_block_comment_entity = '/*' any* :>> '*/';
105   c_comment_entity = c_line_comment_entity | c_block_comment_entity;
106
107   c_string_entity = sq_str_with_escapes | dq_str_with_escapes;
108
109   c_number_entity = float | integer;
110
111   c_preproc_word =
112     'define' | 'elif' | 'else' | 'endif' | 'error' | 'if' | 'ifdef' |
113     'ifndef' | 'import' | 'include' | 'line' | 'pragma' | 'undef' |
114     'using' | 'warning';
115   # TODO: find some way of making preproc match the beginning of a line.
116   # Putting a 'when starts_line' conditional throws an assertion error.
117   c_preproc_entity =
118     '#' space* (c_block_comment_entity space*)?
119       c_preproc_word (escaped_newline | nonnewline)*;
120
121   c_identifier_entity = (alpha | '_') (alnum | '_')*;
122
123   c_keyword_entity =
124     'and' | 'and_eq' | 'asm' | 'auto' | 'bitand' | 'bitor' | 'bool' |
125     'break' | 'case' | 'catch' | 'char' | 'class' | 'compl' | 'const' |
126     'const_cast' | 'continue' | 'default' | 'delete' | 'do' | 'double' |
127     'dynamic_cast' | 'else' | 'enum' | 'explicit' | 'export' | 'extern' |
128     'false' | 'float' | 'for' | 'friend' | 'goto' | 'if' | 'inline' | 'int' |
129     'long' | 'mutable' | 'namespace' | 'new' | 'not' | 'not_eq' |
130     'operator' | 'or' | 'or_eq' | 'private' | 'protected' | 'public' |
131     'register' | 'reinterpret_cast' | 'return' | 'short' | 'signed' |
132     'sizeof' | 'static' | 'static_cast' | 'struct' | 'switch' |
133     'template' | 'this' | 'throw' | 'true' | 'try' | 'typedef' | 'typeid' |
134     'typename' | 'union' | 'unsigned' | 'using' | 'virtual' | 'void' |
135     'volatile' | 'wchar_t' | 'while' | 'xor' | 'xor_eq';
136
137   c_operator_entity = [+\-/*%<>!=^&|?~:;.,()\[\]{}];
138
139   c_entity := |*
140     space+              ${ entity = C_SPACE;      } => c_ecallback;
141     c_comment_entity    ${ entity = C_COMMENT;    } => c_ecallback;
142     c_string_entity     ${ entity = C_STRING;     } => c_ecallback;
143     c_number_entity     ${ entity = C_NUMBER;     } => c_ecallback;
144     c_preproc_entity    ${ entity = C_PREPROC;    } => c_ecallback;
145     c_identifier_entity ${ entity = C_IDENTIFIER; } => c_ecallback;
146     c_keyword_entity    ${ entity = C_KEYWORD;    } => c_ecallback;
147     c_operator_entity   ${ entity = C_OPERATOR;   } => c_ecallback;
148     ^(space | digit)    ${ entity = C_ANY;        } => c_ecallback;
149   *|;
150 }%%
151
152 /************************* Required for every parser *************************/
153
154 /* Parses a string buffer with C/C++ code.
155  *
156  * @param *buffer The string to parse.
157  * @param length The length of the string to parse.
158  * @param count Integer flag specifying whether or not to count lines. If yes,
159  *   uses the Ragel machine optimized for counting. Otherwise uses the Ragel
160  *   machine optimized for returning entity positions.
161  * @param *callback Callback function. If count is set, callback is called for
162  *   every line of code, comment, or blank with 'lcode', 'lcomment', and
163  *   'lblank' respectively. Otherwise callback is called for each entity found.
164  */
165 void parse_c(char *buffer, int length, int count,
166              void (*callback) (const char *lang, const char *entity, int s,
167                                int e, void *udata),
168              void *userdata
169   ) {
170   init
171
172   %% write init;
173   cs = (count) ? c_en_c_line : c_en_c_entity;
174   %% write exec;
175
176   // if no newline at EOF; callback contents of last line
177   if (count) { process_last_line(C_LANG) }
178 }
179
180 const char *CPP_LANG = LANG_CPP;
181 const char *ORIG_C_LANG = LANG_C;
182 void parse_cpp(char *buffer, int length, int count,
183                void (*callback) (const char *lang, const char *entity, int s,
184                                  int e, void *udata),
185                void *userdata
186   ) {
187   C_LANG = CPP_LANG;
188   parse_c(buffer, length, count, callback, userdata);
189   C_LANG = ORIG_C_LANG;
190 }
191
192 const char *CSHARP_LANG = LANG_CSHARP;
193 void parse_csharp(char *buffer, int length, int count,
194                   void (*callback) (const char *lang, const char *entity, int s,
195                                     int e, void *udata),
196                   void *userdata
197   ) {
198   C_LANG = CSHARP_LANG;
199   parse_c(buffer, length, count, callback, userdata);
200   C_LANG = ORIG_C_LANG;
201 }
202
203 const char *VALA_LANG = LANG_VALA;
204 void parse_vala(char *buffer, int length, int count,
205                 void (*callback) (const char *lang, const char *entity, int s,
206                                   int e, void *udata),
207                 void *userdata
208   ) {
209   C_LANG = VALA_LANG;
210   parse_c(buffer, length, count, callback, userdata);
211   C_LANG = ORIG_C_LANG;
212 }
213
214 const char *CUDA_LANG = LANG_CUDA;
215 void parse_cuda(char *buffer, int length, int count,
216                 void (*callback) (const char *lang, const char *entity, int s,
217                                   int e, void *udata),
218                 void *userdata
219   ) {
220   C_LANG = CUDA_LANG;
221   parse_c(buffer, length, count, callback, userdata);
222   C_LANG = ORIG_C_LANG;
223 }
224
225 const char *EC_LANG = LANG_EC;
226 void parse_ec(char *buffer, int length, int count,
227               void (*callback) (const char *lang, const char *entity, int s,
228                                 int e, void *udata),
229               void *userdata
230   ) {
231   C_LANG = EC_LANG;
232   parse_c(buffer, length, count, callback, userdata);
233   C_LANG = ORIG_C_LANG;
234 }
235
236 #endif
237
238 /*****************************************************************************/