The range keyword was not functional in 5.16. Wait for after 5.17 to add it.
[external/ragel.git] / rlcodegen / xmlscan.lex
1 /*
2  *  Copyright 2001-2006 Adrian Thurston <thurston@cs.queensu.ca>
3  */
4
5 /*  This file is part of Ragel.
6  *
7  *  Ragel is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  * 
12  *  Ragel is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  * 
17  *  You should have received a copy of the GNU General Public License
18  *  along with Ragel; if not, write to the Free Software
19  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
20  */
21
22 %{
23
24 #define YY_NEVER_INTERACTIVE 1
25 //#define WANT_TOKEN_WRITE
26
27 #include <iostream>
28 #include "vector.h"
29 #include "rlcodegen.h"
30 #include "xmlparse.h"
31 #include "buffer.h"
32
33 using std::cout;
34 using std::cerr;
35 using std::endl;
36
37 Buffer tokbuf;
38 int builtinBrace = 0;
39 bool inlineWhitespace = true;
40 bool handlingInclude = false;
41
42 YYSTYPE *yylval;
43 YYLTYPE *yylloc;
44
45 void garble();
46
47 void extendToken();
48 void extendToken( char *data, int len );
49
50 int emitToken( int token, char *data, int len );
51 int emitNoData( int token );
52 int emitTag( char *data, int len, bool isOpen );
53 void passThrough( char *data );
54 void popInclude();
55 void scannerInit();
56
57 enum InlineBlockType {
58         CurlyDelimited,
59         SemiTerminated
60 } inlineBlockType;
61
62 /* Using a wrapper for the parser, must the lex declaration. */
63 #define YY_DECL int rlcodegen_lex()
64
65 class Perfect_Hash
66 {
67 private:
68     static inline unsigned int hash (const char *str, unsigned int len);
69
70 public:
71     static struct XMLTagHashPair *in_word_set (const char *str, unsigned int len);
72 };
73
74 Vector<bool> shouldEmitXMLData;
75
76 int first_line = 1;
77 int first_column = 1;
78 int last_line = 1;
79 int last_column = 0;
80
81 Buffer xmlData;
82
83 %}
84
85 %x OPEN_TAG
86 %x CLOSE_TAG1
87 %x CLOSE_TAG2
88 %x ATTR_LIST
89 %x ATTR_LITERAL
90
91 WSCHAR [\t\n\v\f\r ]
92 IDENT [a-zA-Z_][a-zA-Z_0-9\-]*
93
94 %%
95
96         /* Numbers in outter code. */
97 <INITIAL>"<" {
98         BEGIN(OPEN_TAG);
99         shouldEmitXMLData.prepend( false );
100         return emitNoData( *yytext );
101 }
102
103 <INITIAL>[^<&]+ {
104         if ( shouldEmitXMLData[0] )
105                 xmlData.append( yytext, yyleng );
106         garble();
107 }
108 <INITIAL>"&amp;" {
109         if ( shouldEmitXMLData[0] )
110                 xmlData.append( "&", 1 );
111         garble();
112 }
113 <INITIAL>"&lt;" {
114         if ( shouldEmitXMLData[0] )
115                 xmlData.append( "<", 1 );
116         garble();
117 }
118 <INITIAL>"&gt;" {
119         if ( shouldEmitXMLData[0] )
120                 xmlData.append( ">", 1 );
121         garble();
122 }
123
124         /* 
125          * Tags
126          */
127
128 <OPEN_TAG>"/" {
129         BEGIN(CLOSE_TAG1);
130         xmlData.append(0);
131         return emitNoData( *yytext );
132 }
133
134 <OPEN_TAG>{IDENT} {
135         BEGIN( ATTR_LIST );
136         return emitTag( yytext, yyleng, true );
137 }
138
139 <OPEN_TAG,CLOSE_TAG1>{WSCHAR}+ {
140         garble();
141 }
142
143 <CLOSE_TAG1>{IDENT} {
144         BEGIN( CLOSE_TAG2 );
145         return emitTag( yytext, yyleng, false );
146 }
147
148 <CLOSE_TAG2>">" {
149         shouldEmitXMLData.remove( 0 );
150         BEGIN(INITIAL);
151         return emitNoData( *yytext );
152 }
153
154 <ATTR_LIST>{IDENT} {
155         return emitToken( XML_Word, yytext, yyleng );
156 }
157
158 <ATTR_LIST>\" {
159         BEGIN(ATTR_LITERAL);
160         extendToken();
161 }
162 <ATTR_LITERAL>\\.                               extendToken( yytext+1, 1 );
163 <ATTR_LITERAL>\\\n                              extendToken( yytext+1, 1 );
164 <ATTR_LITERAL>[^\\"]+                   extendToken( yytext, yyleng );
165
166         /* Terminate a double literal */
167 <ATTR_LITERAL>\" {
168         BEGIN(ATTR_LIST);
169         return emitToken( XML_Literal, 0, 0 );
170 }
171
172 <ATTR_LIST>{WSCHAR}+ {
173         garble();
174 }
175
176 <ATTR_LIST>">" {
177         BEGIN(INITIAL);
178         return emitNoData( *yytext );
179 }
180
181 <ATTR_LIST>. {
182         return emitNoData( *yytext );
183 }
184
185 %%
186
187 /* Write out token data, escaping special charachters. */
188 #ifdef WANT_TOKEN_WRITE
189 void writeToken( int token, char *data )
190 {
191         cout << "token id " << token << " at " << id->fileName << ":" <<
192                         yylloc->first_line << ":" << yylloc->first_column << "-" <<
193                         yylloc->last_line << ":" << yylloc->last_column << " ";
194
195         if ( data != 0 ) {
196                 while ( *data != 0 ) {
197                         switch ( *data ) {
198                         case '\n':      cout << "\\n"; break;
199                         case '\t':      cout << "\\t"; break;
200                         default:        cout << *data; break;
201                         }
202                         data += 1;
203                 }
204         }
205         cout << endl;
206 }
207 #endif
208
209 /* Caclulate line info from yytext. Called on every pattern match. */
210 void updateLineInfo()
211 {
212         /* yytext should always have at least wone char. */
213         assert( yytext[0] != 0 );
214
215         /* Scan through yytext up to the last character. */
216         char *p = yytext;
217         for ( ; p[1] != 0; p++ ) {
218                 if ( p[0] == '\n' ) {
219                         last_line += 1;
220                         last_column = 0;
221                 }
222                 else {
223                         last_column += 1;
224                 }
225         }
226
227         /* Always consider the last character as not a newline. Newlines at the
228          * end of a token are as any old character at the end of the line. */
229         last_column += 1;
230
231         /* The caller may be about to emit a token, be prepared to pass the line
232          * info to the parser. */
233         yylloc->first_line = first_line;
234         yylloc->first_column = first_column;
235         yylloc->last_line = last_line;
236         yylloc->last_column = last_column;
237
238         /* If the last character was indeed a newline, then wrap ahead now. */
239         if ( p[0] == '\n' ) {
240                 last_line += 1;
241                 last_column = 0;
242         }
243 }
244
245
246 /* Eat up a matched pattern that will not be part of a token. */
247 void garble() 
248 {
249         /* Update line information from yytext. */
250         updateLineInfo();
251
252         /* The next token starts ahead of the last token. */
253         first_line = last_line;
254         first_column = last_column + 1;
255 }
256
257 /* Extend a token, but don't add any data to it, more token data expected. */
258 void extendToken() 
259 {
260         /* Update line information from yytext. */
261         updateLineInfo();
262 }
263
264 /* Append data to the end of the token. More token data expected. */
265 void extendToken( char *data, int len )
266 {
267         if ( data != 0 && len > 0 )
268                 tokbuf.append( data, len );
269
270         /* Update line information from yytext. */
271         updateLineInfo();
272 }
273
274
275 /* Append data to the end of a token and emitToken it to the parser. */
276 int emitToken( int token, char *data, int len )
277 {
278         /* Append the data and null terminate. */
279         if ( data != 0 && len > 0 )
280                 tokbuf.append( data, len );
281         tokbuf.append( 0 );
282
283         /* Duplicate the buffer. */
284         yylval->data = new char[tokbuf.length];
285         strcpy( yylval->data, tokbuf.data );
286
287         /* Update line information from yytext. */
288         updateLineInfo();
289
290         /* Write token info. */
291 #ifdef WANT_TOKEN_WRITE
292         writeToken( token, tokbuf.data );
293 #endif
294
295         /* Clear out the buffer. */
296         tokbuf.clear();
297
298         /* The next token starts ahead of the last token. */
299         first_line = last_line;
300         first_column = last_column + 1;
301
302         return token;
303 }
304
305 /* Append data to the end of a token and emitToken it to the parser. */
306 int emitTag( char *data, int len, bool isOpen )
307 {
308         /* Lookup the tag. */
309         int token = TAG_unknown;
310
311         XMLTagHashPair *tag = Perfect_Hash::in_word_set( data, len );
312         if ( tag != 0 )
313                 token = tag->id;
314
315         if ( isOpen ) {
316                 switch ( token ) {
317                 case TAG_host: case TAG_t: case TAG_start_state:
318                 case TAG_action_table: 
319                 case TAG_alphtype: case TAG_state_actions: 
320                 case TAG_entry_points:
321                 case TAG_text: case TAG_goto: 
322                 case TAG_call: case TAG_next:
323                 case TAG_set_act: case TAG_set_tokend:
324                 case TAG_entry: case TAG_option:
325                 case TAG_cond_space: case TAG_c:
326                         shouldEmitXMLData[0] = true;
327                         xmlData.clear();
328                 }
329         }
330
331         return emitToken( token, data, len );
332 }
333
334 /* Emit a token with no data to the parser. */
335 int emitNoData( int token ) 
336 {
337         /* Return null to the parser. */
338         yylval->data = 0;
339
340         /* Update line information from yytext. */
341         updateLineInfo();
342
343         /* Write token info. */
344 #ifdef WANT_TOKEN_WRITE
345         writeToken( token, 0 );
346 #endif
347
348         /* Clear out the buffer. */
349         tokbuf.clear();
350
351         /* The next token starts ahead of the last token. */
352         first_line = last_line;
353         first_column = last_column + 1;
354
355         return token;
356 }
357
358 /* Pass tokens in outter code through to the output. */
359 void passThrough( char *data )
360 {
361         /* If no errors, we are emitting code and we are at the bottom of the
362          * include stack (the source file listed on the command line) then write
363          * out the data. */
364         if ( gblErrorCount == 0 && outputFormat == OutCode )
365                 *outStream << data;
366 }
367
368 /* Init a buffer. */
369 Buffer::Buffer() 
370 :
371         data(0), 
372         length(0),
373         allocated(0)
374 {
375 }
376
377 /* Empty out a buffer on destruction. */
378 Buffer::~Buffer()
379 {
380         empty();
381 }
382
383 /* Free the space allocated for the buffer. */
384 void Buffer::empty()
385 {
386         if ( data != 0 ) {
387                 free( data );
388
389                 data = 0;
390                 length = 0;
391                 allocated = 0;
392         }
393 }
394
395 /* Grow the buffer when to len allocation. */
396 void Buffer::upAllocate( int len )
397 {
398         if ( data == 0 )
399                 data = (char*) malloc( len );
400         else
401                 data = (char*) realloc( data, len );
402         allocated = len;
403 }
404
405 int yywrap()
406 {
407         /* Once processessing of the input is done, signal no more. */
408         return 1;
409 }
410
411 /* Here simply to suppress the unused yyunpt warning. */
412 void thisFuncIsNeverCalled()
413 {
414         yyunput(0, 0);
415 }
416
417 void scannerInit()
418 {
419         /* Set this up in case we are initially given something other
420          * than an opening tag. */
421         shouldEmitXMLData.prepend( false );
422 }
423
424 /* Wrapper for the lexer which stores the locations of the value and location
425  * variables of the parser into globals. The parser is reentrant, however the scanner
426  * does not need to be, so globals work fine. This saves us passing them around
427  * all the helper functions. */
428 int yylex( YYSTYPE *yylval, YYLTYPE *yylloc )
429 {
430         ::yylval = yylval;
431         ::yylloc = yylloc;
432         return rlcodegen_lex();
433 }