OTWO-1213 Works around lost encoding in Ruby/C binding layer
[ohcount] / src / parsers / oberon.rl
1 // oberon.rl, 
2 // derived from code written by Mitchell Foral. mitchell<att>caladbolg<dott>net
3
4
5 /************************* Required for every parser *************************/
6 #ifndef OHCOUNT_OBERON_PARSER_H
7 #define OHCOUNT_OBERON_PARSER_H
8
9 #include "../parser_macros.h"
10
11 // the name of the language
12 const char *OBERON_LANG = LANG_OBERON;
13
14 // the languages entities
15 const char *oberon_entities[] = {
16   "space", "comment", "string", "any"
17 };
18
19 // constants associated with the entities
20 enum {
21   OBERON_SPACE = 0, OBERON_COMMENT, OBERON_STRING, OBERON_ANY
22 };
23
24 /*****************************************************************************/
25
26 %%{
27   machine oberon;
28   write data;
29   include common "common.rl";
30
31   # Line counting machine
32
33   action oberon_ccallback {
34     switch(entity) {
35     case OBERON_SPACE:
36       ls
37       break;
38     case OBERON_ANY:
39       code
40       break;
41     case INTERNAL_NL:
42       std_internal_newline(OBERON_LANG)
43       break;
44     case NEWLINE:
45       std_newline(OBERON_LANG)
46     }
47   }
48
49
50   # Oberon comments
51
52   action oberon_comment_nc_res { nest_count = 0; }
53   action oberon_comment_nc_inc { nest_count++; }
54   action oberon_comment_nc_dec { nest_count--; }
55
56   oberon_comment =
57     '(*' >oberon_comment_nc_res @comment (
58       newline %{ entity = INTERNAL_NL; } %oberon_ccallback
59       |
60       ws
61       |
62       '(*' @oberon_comment_nc_inc @comment
63       |
64       '*)' @oberon_comment_nc_dec @comment
65       |
66       ^space @comment
67     )* :>> ('*)' when { nest_count == 0 }) @comment;
68
69
70   # Oberon string literals
71
72   oberon_singlequoted_string = '\'' @code [^\r\n\f"]* '\'';
73   oberon_doublequoted_string = '"' @code [^\r\n\f"]* '"';
74
75   oberon_string = oberon_singlequoted_string | oberon_doublequoted_string;
76
77
78   # Line counter
79
80   oberon_line := |*
81     spaces          ${ entity = OBERON_SPACE; } => oberon_ccallback;
82     oberon_comment;
83     oberon_string;
84     newline         ${ entity = NEWLINE;      } => oberon_ccallback;
85     ^space          ${ entity = OBERON_ANY;   } => oberon_ccallback;
86   *|;
87
88
89   # Entity machine
90
91   action oberon_ecallback {
92     callback(OBERON_LANG, oberon_entities[entity], cint(ts), cint(te),
93              userdata);
94   }
95
96   oberon_comment_entity = '(*' >oberon_comment_nc_res (
97     '(*' @oberon_comment_nc_inc
98     |
99     '*)' @oberon_comment_nc_dec
100     |
101     any
102   )* :>> ('*)' when { nest_count == 0 });
103
104   oberon_string_entity = sq_str | dq_str;
105
106   oberon_entity := |*
107     space+                ${ entity = OBERON_SPACE;   } => oberon_ecallback;
108     oberon_comment_entity ${ entity = OBERON_COMMENT; } => oberon_ecallback;
109     oberon_string_entity  ${ entity = OBERON_STRING;  } => oberon_ecallback;
110     # TODO: detecting other entities may be useful to differentiate dialects
111     ^space;
112   *|;
113 }%%
114
115 /************************* Required for every parser *************************/
116
117 /* Parses a string buffer with Oberon code.
118  *
119  * @param *buffer The string to parse.
120  * @param length The length of the string to parse.
121  * @param count Integer flag specifying whether or not to count lines. If yes,
122  *   uses the Ragel machine optimized for counting. Otherwise uses the Ragel
123  *   machine optimized for returning entity positions.
124  * @param *callback Callback function. If count is set, callback is called for
125  *   every line of code, comment, or blank with 'lcode', 'lcomment', and
126  *   'lblank' respectively. Otherwise callback is called for each entity found.
127  */
128 void parse_oberon(char *buffer, int length, int count,
129                   void (*callback) (const char *lang, const char *entity, int s,
130                                     int e, void *udata),
131                   void *userdata
132   ) {
133   init
134
135   int nest_count = 0;
136
137   %% write init;
138   cs = (count) ? oberon_en_oberon_line : oberon_en_oberon_entity;
139   %% write exec;
140
141   // if no newline at EOF; callback contents of last line
142   if (count) { process_last_line(OBERON_LANG) }
143 }
144
145 #endif
146
147 /*****************************************************************************/