Moved common variables into ragel_parser_macros.h.
[ohcount] / PARSER_DOC
1 PARSER_DOC written by Mitchell Foral
2
3 Overview:
4   I will assume the reader has a decent knowledge of how Ragel works and the
5   Ragel syntax. If not, please review the Ragel manual found at:
6     http://research.cs.queensu.ca/~thurston/ragel/
7
8   All parsers must at least:
9     * Call a callback function when a line of code is parsed.
10     * Call a callback function when a line of comment is parsed.
11     * Call a callback function when a blank line is parsed.
12   Additionally a parser can call the callback function for each position of
13   entities parsed.
14
15   Take a look at c.rl and even keep it open for reference when reading this
16   document to better understand how parsers work and how to write one.
17
18 Writing a Parser:
19   First create your parser in ext/ohcount_native/ragel_parsers/. Its name
20   should be the language you are parsing with a '.rl' extension. Every parser
21   must have the following at the top:
22
23 /************************* Required for every parser *************************/
24 #include "ragel_parser_macros.h"
25
26 // the name of the language
27 const char *C_LANG = "c";
28
29 // the languages entities
30 const char *c_entities[] = {
31   "space", "comment", "string", "number", "preproc",
32   "keyword", "identifier", "operator", "any"
33 };
34
35 // constants associated with the entities
36 enum {
37   C_SPACE = 0, C_COMMENT, C_STRING, C_NUMBER, C_PREPROC,
38   C_KEYWORD, C_IDENTIFIER, C_OPERATOR, C_ANY
39 };
40
41 /*****************************************************************************/
42
43   And the following at the bottom:
44
45 /* Parses a string buffer with C/C++ code.
46  *
47  * @param *buffer The string to parse.
48  * @param length The length of the string to parse.
49  * @param count Integer flag specifying whether or not to count lines. If yes,
50  *   uses the Ragel machine optimized for counting. Otherwise uses the Ragel
51  *   machine optimized for returning entity positions.
52  * @param *callback Callback function. If count is set, callback is called for
53  *   every line of code, comment, or blank with 'lcode', 'lcomment', and
54  *   'lblank' respectively. Otherwise callback is called for each entity found.
55  */
56 void parse_c(char *buffer, int length, int count,
57   void (*callback) (const char *lang, const char *entity, int start, int end)
58   ) {
59   p = buffer;
60   pe = buffer + length;
61   eof = pe;
62
63   buffer_start = buffer;
64   whole_line_comment = 0;
65   line_contains_code = 0;
66   line_start = 0;
67   entity = 0;
68
69   %% write init;
70   cs = (count) ? c_en_c_line : c_en_c_entity;
71   %% write exec;
72
73   // if no newline at EOF; callback contents of last line
74   if (count) { process_last_line(C_LANG) }
75 }
76
77   (Your parser will go between these two blocks.)
78
79   The code can be found in the existing c.rl parser. You will need to change:
80     * [lang]_LANG - Set the variable name to be [lang]_LANG and its value to be
81       the name of your language to parse. [lang] is your language name. So if
82       you are writing a C parser, it would be C_LANG.
83     * [lang]_entities - Set the variable name to be [lang]_entities (e.g.
84       c_entries) The value is an array of string entities your language has.
85       For example C has comment, string, number, etc. entities. You should
86       definately have "space", and "any" entities. "any" entities are typically
87       used for entity machines (discussed later) and match any character that
88       is not recognized so the parser does not do something unpredictable.
89     * enum - Change the value of the enum to correspond with your entities. So
90       if in your parser you look up [lang]_entities[ENTITY], you will get the
91       associated entity's string name.
92     * parse_[lang] - Set the function name to parse_[lang] where again, [lang]
93       is the name of your language. In the case of C, it is parse_c.
94     * [lang]_en_[lang]_line - The line counting machine.
95     * [lang]_en_[lang]_entity - The entity machine.
96
97     You may be asking why you have to rename variables and functions. Well if
98     variables have the same name in header files (which is what parsers are),
99     the compiler complains. Also, when you have languages embedded inside each
100     other, any identifiers with the same name can easily be mixed up. It is also
101     important to prefix your Ragel definitions with your language to avoid
102     conflicts with other parsers.
103
104   Additional variables available to parsers are in the "ragel_parser_macros.h"
105   file. Take a look at it and try to understand what the variables are used for.
106   They will make more sense later on.
107
108   Now you can define your Ragel parser. Name your machine after your language,
109   'write data', and include 'common.rl', a file with common Ragel definitions,
110   actions, etc. For example:
111     %%{
112       machine c;
113       write data;
114       include "common.rl";
115
116       ...
117     }%%
118
119   Before you begin to write patterns for each entity in your language, you need
120   to understand how the parser should work.
121
122   Each parser has two machines: one optimized for counting lines of code,
123   comments, and blanks; the other for identifying entity positions in the
124   buffer.
125
126   Line Counting Machine:
127     This machine should be written as a line-by-line parser for multiple lines.
128     This means you match any combination of entities except a newline up until
129     you do reach a newline. If the line contains only spaces, or nothing at all,
130     it is blank. If the line contains spaces at first, but then a comment, or
131     just simply a comment, the line is a comment. If the line contains anything
132     but a comment after spaces (if there are any), it is a line of code. You
133     will do this using a Ragel scanner.
134     The callback function will be called for each line parsed.
135
136     Scanner Parser Structure:
137       A scanner parser will look like this:
138         [lang]_line := |*
139           entity1 ${ entity = ENTITY1; } => [lang]_ccallback;
140           entity2 ${ entity = ENTITY2; } => [lang]_ccallback;
141           ...
142           entityn ${ entity = ENTITYN; } => [lang]_ccallback;
143         *|;
144       (As usual, replace [lang] with your language name.)
145       Each entity is the pattern for an entity to match, the last one typically
146       being the newline entity. For each match, the variable is set to a
147       constant defined in the enum, and the main action is called (you will need
148       to create this action above the scanner).
149
150       When you detect whether or not a line is code or comment, you should call
151       the appropriate 'code' or 'comment' action defined in common.rl as soon
152       as possible. It is not necessary to worry about whether or not these
153       actions are called more than once for a given line; the first call to
154       either sets the status of the line permanently. Sometimes you cannot call
155       'code' or 'comment' for one reason or another. Do not worry, as this is
156       discussed later.
157
158       When you reach a newline, you will need to decide whether the current line
159       is a line of code, comment, or blank. This is easy. Simply check if the
160       line_contains_code or whole_line_comment variables are set to 1. If
161       neither of them are, the line is blank. Then call the callback function
162       (not action) with an "lcode", "lcomment", or "lblank" string, and the
163       start and end positions of that line (including the newline). The start
164       position of the line is in the line_start variable. It should be set at
165       the beginning of every line either through the 'code' or 'comment'
166       actions, or manually in the main action. Finally the line_contains_code,
167       whole_line_comment, and line_start state variables must be reset. All this
168       should be done within the main action shown below.
169       Note: For most parsers, the std_newline(lang) macro is sufficient and does
170       everything in the main action mentioned above. The lang parameter is the
171       [lang]_LANG string.
172
173     Main Action Structure:
174       The main action looks like this:
175         action [lang]_ccallback {
176           switch(entity) {
177           when ENTITY1:
178             ...
179             break;
180           when ENTITY2:
181             ...
182             break;
183           ...
184           when ENTITYN:
185             ...
186             break;
187           }
188         }
189
190     Defining Patterns for Entities:
191       Now it is time to write patterns for each entity in your language. That
192       does not seem very hard, except when your entity can cover multiple lines.
193       Comments and strings in particular can do this. To make an accurate line
194       counter, you will need to count the lines covered by multi-line entities.
195       When you detect a newline inside your multi-line entity, you should set
196       the entity variable to be INTERNAL_NL (-2) and call the main action. The
197       main action should have a case for INTERNAL_NL separate from the newline
198       entity. In it, you will check if the current line is code or comment and
199       call the callback function with the appropriate string ("lcode" or
200       "lcomment") and beginning and end of the line (including the newline).
201       Afterwards, you will reset the line_contains_code and whole_line_comment
202       state variables. Then set the line_start variable to be p, the current
203       Ragel buffer position. Because line_contains_code and whole_line_comment
204       have been reset, any non-newline and non-space character in the multi-line
205       pattern should set line_contains_code or whole_line_comment back to 1.
206       Otherwise you would count the line as blank.
207       Note: For most parsers, the std_internal_newline(lang) macro is sufficient
208       and does everything in the main action mentioned above. The lang parameter
209       is the [lang]_LANG string.
210
211       For multi-line matches, it is important to call the 'code' or 'comment'
212       actions (mentioned earlier) before an internal newline is detected so the
213       line_contains_code and whole_line_comment variables are properly set. For
214       other entities, you can use the 'code' macro inside the main action which
215       executes the same code as the Ragel 'code' action. Other C macros are
216       'comment' and 'ls', the latter is typically used for the SPACE entity when
217       defining line_start.
218
219     Notes:
220       * You can be a bit sloppy with the line counting machine. For example the
221         only C entities that can contain newlines are strings and comments, so
222         INTERNAL_NL would only be necessary inside them. Other than those,
223         anything other than spaces is considered code, so do not waste your time
224         defining specific patterns for other entities.
225
226   Entity Identifying Machine:
227     This machine does not have to be written as a line-by-line parser. It only
228     has to identify the positions of language entities, such as whitespace,
229     comments, strings, etc. in sequence. As a result they can be written much
230     faster and more easily with less thought than a line counter. Using a
231     scanner is most efficient.
232     The callback function will be called for each entity parsed.
233
234     Scanner Structure:
235       [lang]_entity := |*
236         entity1 ${ entity = ENTITY1; } => [lang]_ecallback;
237         entity2 ${ entity = ENTITY2; } => [lang]_ecallback;
238         ...
239         entityn ${ entity = ENTITYN; } => [lang]_ecallback;
240       *|;
241
242     Main Action Structure:
243       action [lang]_ecallback {
244         callback([lang]_LANG, entity, cint(ts), cint(te));
245       }