OTWO-1213 Works around lost encoding in Ruby/C binding layer
[ohcount] / src / parsers / boo.rl
1 // boo.rl written by Mitchell Foral. mitchell<att>caladbolg<dott>net
2
3 /************************* Required for every parser *************************/
4 #ifndef OHCOUNT_BOO_PARSER_H
5 #define OHCOUNT_BOO_PARSER_H
6
7 #include "../parser_macros.h"
8
9 // the name of the language
10 const char *BOO_LANG = LANG_BOO;
11
12 // the languages entities
13 const char *boo_entities[] = {
14   "space", "comment", "string", "any"
15 };
16
17 // constants associated with the entities
18 enum {
19   BOO_SPACE = 0, BOO_COMMENT, BOO_STRING, BOO_ANY
20 };
21
22 /*****************************************************************************/
23
24 %%{
25   machine boo;
26   write data;
27   include common "common.rl";
28
29   # Line counting machine
30
31   action boo_ccallback {
32     switch(entity) {
33     case BOO_SPACE:
34       ls
35       break;
36     case BOO_ANY:
37       code
38       break;
39     case INTERNAL_NL:
40       std_internal_newline(BOO_LANG)
41       break;
42     case NEWLINE:
43       std_newline(BOO_LANG)
44     }
45   }
46
47   action boo_comment_nc_res { nest_count = 0; }
48   action boo_comment_nc_inc { nest_count++; }
49   action boo_comment_nc_dec { nest_count--; }
50
51   boo_line_comment = ('#' | '//') @comment nonnewline*;
52   boo_block_comment =
53     '/*' >boo_comment_nc_res @comment (
54       newline %{ entity = INTERNAL_NL; } %boo_ccallback
55       |
56       ws
57       |
58       '/*' @boo_comment_nc_inc @comment
59       |
60       '*/' @boo_comment_nc_dec @comment
61       |
62       ^space @comment
63     )* :>> ('*/' when { nest_count == 0 }) @comment;
64   boo_doc_comment =
65     '"""' @comment (
66       newline %{ entity = INTERNAL_NL; } %boo_ccallback
67       |
68       ws
69       |
70       (nonnewline - ws) @comment
71     )* :>> '"""' @comment;
72   boo_comment = boo_line_comment | boo_block_comment | boo_doc_comment;
73
74   boo_char = '\'' ([^\r\n\f'\\] | '\\' nonnewline) '\'';
75   boo_dq_str =
76     '"' ([^"] | '"' [^"] @{ fhold; }) @{ fhold; } # make sure it's not """
77       ([^\r\n\f"\\] | '\\' nonnewline)* '"';
78   boo_regex = '/' [^*/] @{ fhold; } ([^\r\n\f/\\] | '\\' nonnewline)* '/';
79   boo_string = (boo_char | boo_dq_str | boo_regex) @code;
80
81   boo_line := |*
82     spaces       ${ entity = BOO_SPACE; } => boo_ccallback;
83     boo_comment;
84     boo_string;
85     newline      ${ entity = NEWLINE;   } => boo_ccallback;
86     ^space       ${ entity = BOO_ANY;   } => boo_ccallback;
87   *|;
88
89   # Entity machine
90
91   action boo_ecallback {
92     callback(BOO_LANG, boo_entities[entity], cint(ts), cint(te), userdata);
93   }
94
95   boo_line_comment_entity = ('#' | '//') nonnewline*;
96   boo_block_comment_entity = '/*' >boo_comment_nc_res (
97     '/*' @boo_comment_nc_inc
98     |
99     '*/' @boo_comment_nc_dec
100     |
101     any
102   )* :>> ('*/' when { nest_count == 0 });
103   boo_comment_entity = boo_line_comment_entity | boo_block_comment_entity;
104
105   boo_entity := |*
106     space+             ${ entity = BOO_SPACE;   } => boo_ecallback;
107     boo_comment_entity ${ entity = BOO_COMMENT; } => boo_ecallback;
108     # TODO:
109     ^space;
110   *|;
111 }%%
112
113 /************************* Required for every parser *************************/
114
115 /* Parses a string buffer with Boo code.
116  *
117  * @param *buffer The string to parse.
118  * @param length The length of the string to parse.
119  * @param count Integer flag specifying whether or not to count lines. If yes,
120  *   uses the Ragel machine optimized for counting. Otherwise uses the Ragel
121  *   machine optimized for returning entity positions.
122  * @param *callback Callback function. If count is set, callback is called for
123  *   every line of code, comment, or blank with 'lcode', 'lcomment', and
124  *   'lblank' respectively. Otherwise callback is called for each entity found.
125  */
126 void parse_boo(char *buffer, int length, int count,
127                void (*callback) (const char *lang, const char *entity, int s,
128                                  int e, void *udata),
129                void *userdata
130   ) {
131   init
132
133   int nest_count = 0;
134
135   %% write init;
136   cs = (count) ? boo_en_boo_line : boo_en_boo_entity;
137   %% write exec;
138
139   // if no newline at EOF; callback contents of last line
140   if (count) { process_last_line(BOO_LANG) }
141 }
142
143 #endif
144
145 /*****************************************************************************/