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