OTWO-1213 Works around lost encoding in Ruby/C binding layer
[ohcount] / src / parsers / xml.rl
1 // xml.rl written by Mitchell Foral. mitchell<att>caladbolg<dott>net.
2
3 /************************* Required for every parser *************************/
4 #ifndef OHCOUNT_XML_PARSER_H
5 #define OHCOUNT_XML_PARSER_H
6
7 #include "../parser_macros.h"
8
9 // the name of the language
10 const char *XML_LANG = LANG_XML;
11
12 // the languages entities
13 const char *xml_entities[] = {
14   "space", "comment", "doctype",
15   "tag", "entity", "any"
16 };
17
18 // constants associated with the entities
19 enum {
20   XML_SPACE = 0, XML_COMMENT, XML_DOCTYPE,
21   XML_TAG, XML_ENTITY, XML_ANY
22 };
23
24 /*****************************************************************************/
25
26 %%{
27   machine xml;
28   write data;
29   include common "common.rl";
30
31   # Line counting machine
32
33   action xml_ccallback {
34     switch(entity) {
35     case XML_SPACE:
36       ls
37       break;
38     case XML_ANY:
39       code
40       break;
41     case INTERNAL_NL:
42       std_internal_newline(XML_LANG)
43       break;
44     case NEWLINE:
45       std_newline(XML_LANG)
46       break;
47     case CHECK_BLANK_ENTRY:
48       check_blank_entry(XML_LANG)
49     }
50   }
51
52   xml_comment =
53     '<!--' @comment (
54       newline %{ entity = INTERNAL_NL; } %xml_ccallback
55       |
56       ws
57       |
58       (nonnewline - ws) @comment
59     )* :>> '-->';
60
61   xml_sq_str = '\'' [^\r\n\f']* '\'' @code;
62   xml_dq_str = '"' [^\r\n\f"]* '"' @code;
63   xml_cdata_str =
64     '<![CDATA[' @code (
65       newline %{ entity = INTERNAL_NL; } %xml_ccallback
66       |
67       ws
68       |
69       (nonnewline - ws) @code
70     )* :>> ']]>';
71   xml_string = xml_sq_str | xml_dq_str | xml_cdata_str;
72
73   xml_line := |*
74     spaces       ${ entity = XML_SPACE; } => xml_ccallback;
75     xml_comment;
76     xml_string;
77     newline      ${ entity = NEWLINE;   } => xml_ccallback;
78     ^space       ${ entity = XML_ANY;   } => xml_ccallback;
79   *|;
80
81   # Entity machine
82
83   action xml_ecallback {
84     callback(XML_LANG, xml_entities[entity], cint(ts), cint(te), userdata);
85   }
86
87   xml_comment_entity = '<!--' any* :>> '-->';
88
89   xml_entity := |*
90     space+             ${ entity = XML_SPACE;   } => xml_ecallback;
91     xml_comment_entity ${ entity = XML_COMMENT; } => xml_ecallback;
92     # TODO:
93     ^space;
94   *|;
95 }%%
96
97 /************************* Required for every parser *************************/
98
99 /* Parses a string buffer with XML markup.
100  *
101  * @param *buffer The string to parse.
102  * @param length The length of the string to parse.
103  * @param count Integer flag specifying whether or not to count lines. If yes,
104  *   uses the Ragel machine optimized for counting. Otherwise uses the Ragel
105  *   machine optimized for returning entity positions.
106  * @param *callback Callback function. If count is set, callback is called for
107  *   every line of code, comment, or blank with 'lcode', 'lcomment', and
108  *   'lblank' respectively. Otherwise callback is called for each entity found.
109  */
110 void parse_xml(char *buffer, int length, int count,
111                void (*callback) (const char *lang, const char *entity, int s,
112                                  int e, void *udata),
113                void *userdata
114   ) {
115   init
116
117   %% write init;
118   cs = (count) ? xml_en_xml_line : xml_en_xml_entity;
119   %% write exec;
120
121   // if no newline at EOF; callback contents of last line
122   if (count) { process_last_line(XML_LANG) }
123 }
124
125 #endif
126
127 /*****************************************************************************/