source: trunk/src/engine.c @ 19

Last change on this file since 19 was 19, checked in by cameron, 11 years ago

Refactoring byte-space lexical recognizers => bytelex.h

File size: 18.4 KB
Line 
1/*  engine.c - Parabix XML parsing engine.
2    Copyright (c) 2007, Robert D. Cameron.
3    Licensed to the public under the Open Software License 3.0.
4    Licensed to International Characters, Inc., under the Academic
5    Free License 3.0.
6*/
7
8#include "ilax.h"
9#include "engine.h"
10#include "xmlbuffer.h"
11#include "bytelex.h"
12#include "bitlex.h"
13#include "charsets/ext_ascii_8.h"
14#include "charsets/ext_ascii_16.h"
15
16#include <assert.h>
17#include <stdlib.h>
18#include <errno.h>
19#include <string.h>
20
21ParsingEngine::ParsingEngine (char * filename) {
22#ifdef BUFFER_PROFILING
23        bitstream_timer = init_BOM_timer(BUFFER_BLOCKS * BLOCKSIZE);
24        lextranspose_timer = init_BOM_timer(BUFFER_BLOCKS * BLOCKSIZE);
25        scanner_timer = init_BOM_timer(BUFFER_BLOCKS * BLOCKSIZE);
26#endif
27
28#define OFFSET32_SENTINEL
29
30  /* Install sentinels for every lexical item stream*/
31#ifndef OPTIMIZE_SHORT_SCAN
32  BitBlock sentinel_value = simd_const_1(1);
33#endif
34#ifdef OPTIMIZE_SHORT_SCAN
35  BitBlock sentinel_value = bitblock_sfli(simd_const_1(1), 8*sizeof(unsigned long));
36#endif
37  for (int j = MarkupStart; j < LexicalItemCount; j++) {
38    buf.item_stream[j][BUFFER_BLOCKS] = sentinel_value;
39  }
40  buffer_base_pos = 0;
41  buffer_rel_pos = 0;
42  xml_buf = new XML_Buffer::XML_Buffer(filename, BLOCKSIZE);
43}
44
45void ParsingEngine::InitLexer() {
46  unsigned char * sentinel = (unsigned char *) "<![--?>]]>/'>\"><!";
47  if (xml_buf->PrepareBytes(0, 4) < 4) {
48    printf("No XML input document.\n");
49    exit(-1);
50  }
51  unsigned char * XML_signature = xml_buf->BytePtr(0);
52  xml_buf->InstallPadding(sentinel);
53
54  Charset_Family family = Charset_Family_Detect(XML_signature);
55  switch (family) {
56    case Ext_ASCII_8:
57      printf("Ext_ASCII_8 document detected.\n");
58      lex = new Ext_ASCII_8_Lexer::Ext_ASCII_8_Lexer(xml_buf, &buf);
59      //lex = new Lexer::Lexer(xml_buf, &buf);
60      //xml_buf->InstallPadding(sentinel);
61      break;
62    case Ext_ASCII_16BE:
63      printf("Ext_ASCII_16BE document detected.\n");
64      lex = new Ext_ASCII_16BE_Lexer::Ext_ASCII_16BE_Lexer(xml_buf, &buf);
65      break;
66    case Ext_ASCII_16LE:
67      printf("Ext_ASCII_16LE document detected.\n");
68      lex = new Ext_ASCII_16LE_Lexer::Ext_ASCII_16LE_Lexer(xml_buf, &buf);
69      break;
70    default:
71      printf("Error: Charset family %i detected, but not supported.\n", family);
72      exit(-1);
73  }
74  avail_code_units = lex-> AdvanceBuffer(0);
75}
76
77
78inline void ParsingEngine::AdvanceToNewBasePosn(int advance_amt) {
79  buffer_base_pos += advance_amt;
80  buffer_rel_pos = 0;
81}
82
83inline unsigned char * ParsingEngine::cur() const {
84  return &((unsigned char *) buf.x8data)[buffer_rel_pos];
85}
86
87
88inline int ParsingEngine::AbsPos() const {
89  return buffer_base_pos + buffer_rel_pos;
90}
91
92
93inline int ParsingEngine::BufferRelPos() const {
94  return buffer_rel_pos;
95}
96
97
98inline bool ParsingEngine::at_EOF () const {
99  return (buffer_rel_pos >= avail_code_units) && 
100         (avail_code_units < BUFFER_BLOCKS * BLOCKSIZE + LOOKAHEAD_POSITIONS);
101}
102
103inline void ParsingEngine::Advance(int n) {
104  buffer_rel_pos += n;
105#ifndef OMIT_BITBUFFER_LIMIT_TEST_IN_ADVANCE
106  if (buffer_rel_pos >= BUFFER_BLOCKS * BLOCKSIZE) {
107    FinalizeBuffer_action();
108#ifdef BUFFER_PROFILING
109    end_BOM_interval(scanner_timer);
110#endif
111    AdvanceToNewBasePosn(buffer_rel_pos);
112    avail_code_units = lex->AdvanceBuffer(AbsPos());
113  }
114#endif
115}
116
117inline void ParsingEngine::ASCII_ScanTo(int item) {
118#ifdef DEBUG_BYTESCAN
119  int p1 = AbsPos();
120#endif
121  switch (item) {
122    case NonWS: while (at_WhiteSpace<ASCII>(cur())) Advance(1); break;
123    case MarkupStart: while(!AtChar<ASCII,'<'>(cur()) && !AtChar<ASCII,'&'>(cur()) && !at_CDATA_End<ASCII>(cur())) Advance(1); break;
124    case CD_End_check: while(!at_CDATA_End<ASCII>(cur())) Advance(1); break;
125    case Hyphen: while(!AtChar<ASCII,'-'>(cur())) Advance(1); break;
126    case QMark: while(!AtChar<ASCII,'?'>(cur())) Advance(1); break;
127    case DQuote: while(!AtChar<ASCII,'<'>(cur()) && !AtChar<ASCII,'&'>(cur()) && !AtChar<ASCII,'"'>(cur())) Advance(1); break;
128    case SQuote: while(!AtChar<ASCII,'<'>(cur()) && !AtChar<ASCII,'&'>(cur()) && !AtChar<ASCII,'\''>(cur())) Advance(1); break;
129    case NameFollow: while(!at_WhiteSpace<ASCII>(cur()) && !AtChar<ASCII,';'>(cur()) && !AtChar<ASCII,'/'>(cur()) && !AtChar<ASCII,'>'>(cur())
130                      && !AtChar<ASCII,'='>(cur()) && !AtChar<ASCII,'?'>(cur())) Advance(1); break;
131  }
132#ifdef DEBUG_BYTESCAN
133  printf("ASCII_ScanTo(%i) %i -> %i\n", item, p1, AbsPos());
134#endif
135}
136
137#ifndef OPTIMIZE_SHORT_SCAN
138#ifdef BYTESPACE_SCAN
139inline void ParsingEngine::ScanTo(int item) {
140  ASCII_ScanTo(item);
141}
142#endif
143
144#ifndef BYTESPACE_SCAN
145inline void ParsingEngine::ScanTo(int item) {
146  buffer_rel_pos = bitstream_scan(buf.item_stream[item], 
147                                      buffer_rel_pos);
148  while (buffer_rel_pos >= BUFFER_BLOCKS * BLOCKSIZE) {
149    FinalizeBuffer_action();
150#ifdef BUFFER_PROFILING
151    end_BOM_interval(scanner_timer);
152#endif
153    AdvanceToNewBasePosn(buffer_rel_pos);
154    avail_code_units = lex->AdvanceBuffer(AbsPos());
155    buffer_rel_pos = bitstream_scan0(buf.item_stream[item]);
156  }
157}
158#endif
159#endif
160
161#ifdef OPTIMIZE_SHORT_SCAN
162inline void ParsingEngine::ScanTo(int item) {
163  SIMD_type * stream = buf.item_stream[item];
164  unsigned long * bitstream_ptr = (unsigned long *) (((intptr_t) stream) + buffer_rel_pos/8);
165  unsigned long bitstream_slice = *bitstream_ptr >> buffer_rel_pos % 8;
166  if (bitstream_slice != 0) {
167    buffer_rel_pos += __builtin_ctzl(bitstream_slice);
168  }
169  else {
170    buffer_rel_pos = (buffer_rel_pos & -8) + 8*sizeof(unsigned long);
171    buffer_rel_pos += bitstream_scan0((SIMD_type *) &bitstream_ptr[1]);
172    while (buffer_rel_pos >= BUFFER_BLOCKS * BLOCKSIZE) {
173      buffer_rel_pos = BUFFER_BLOCKS * BLOCKSIZE;
174      FinalizeBuffer_action();
175#ifdef BUFFER_PROFILING
176      end_BOM_interval(scanner_timer);
177#endif
178      AdvanceToNewBasePosn(buffer_rel_pos);
179      avail_code_units = lex->AdvanceBuffer(AbsPos());
180      buffer_rel_pos = bitstream_scan0(buf.item_stream[item]);
181    }
182  }
183}
184#endif
185
186
187/* Parse a markup item beginning '<' */
188inline void ParsingEngine::Parse_Markup () {
189        int markup_start = AbsPos();
190        if (at_ElementTag_Start<ASCII>(cur())) {
191                Parse_StartTag();
192        }
193        else if (at_EndTag_Start<ASCII>(cur())) {
194                Parse_EndTag();
195        }
196        else if (at_Comment_Start<ASCII>(cur())) {
197                Parse_Comment();
198        }
199        else if (at_CDATA_Start<ASCII>(cur())) {
200                Parse_CDATA();
201        }
202        else if (at_PI_Start<ASCII>(cur())) {
203                Parse_PI();
204        }
205        else {
206                Advance(1);
207                Error_action(markup_start, AbsPos());
208        }
209}
210
211/* Parse a comment beginning "<!--" */
212inline void ParsingEngine::Parse_Comment () {
213        int markup_start = AbsPos();
214        Advance(4); /* Skip "<!--". */
215        ScanTo(Hyphen);
216        while (!at_DoubleHyphen<ASCII>(cur())) {
217                Advance(2); /* Skip hyphen-nonhyphen pair */
218                ScanTo(Hyphen); 
219        }
220        if (at_Comment_End<ASCII>(cur())) {
221                Advance(3); /* Skip "-->". */
222                Comment_action(markup_start, AbsPos());
223        }
224        else {
225                Advance(2);  /* "--" */
226                Error_action(markup_start, AbsPos());
227        }
228}
229
230/* Parse an end tag beginning "</" */
231inline void ParsingEngine::Parse_EndTag () {
232        int markup_start = AbsPos();
233#ifndef OMIT_ADVANCE_PRIOR_TO_EXCLUSIVE_SCAN
234        Advance(2); /* Skip "</". */
235#endif
236        ScanTo(NameFollow);
237        if (AtChar<ASCII,'>'>(cur())) {
238                Advance(1);
239                EndTag_action(markup_start, AbsPos());
240        }
241        else {
242                ScanTo(NonWS);
243                if (AtChar<ASCII,'>'>(cur())) {
244                        Advance(1);
245                        EndTag_action(markup_start, AbsPos());
246                }
247                else Error_action(markup_start, AbsPos());
248        }
249}
250
251/* Parse a CDATA section beginning "<![CDATA". */
252inline void ParsingEngine::Parse_CDATA () {
253        int markup_start = AbsPos();
254        Advance(8); /* Skip "<![CDATA". */
255        if (!AtChar<ASCII,'['>(cur())) {
256                Error_action(markup_start, AbsPos());
257        }
258        else {
259                ScanTo(CD_End_check);
260                while (!at_CDATA_End<ASCII>(cur())) {
261                        Advance(1);
262                        ScanTo(CD_End_check);
263                }
264                Advance(3); /* Skip "]]>". */
265                CDATA_action(markup_start, AbsPos());
266        }
267}
268
269inline void ParsingEngine::Parse_Reference () {
270        int markup_start = AbsPos();
271        /* Advance(1);  // skip "&" */
272        ScanTo(NameFollow);  /* Name delimiter */
273        if (!AtChar<ASCII,';'>(cur())) {
274                Error_action(markup_start, AbsPos());
275        }
276        else {
277                Advance(1);
278                Reference_action(markup_start, AbsPos());
279        }
280}
281
282inline void ParsingEngine::Parse_PI () {
283        int markup_start = AbsPos();
284        Advance(2); /* Skip "<?". */
285        int target_start = AbsPos();
286        // Check for illegal [Xx][Mm][Ll] target.
287        if (at_XxMmLll_WS<ASCII>(cur())) {
288                Advance(4);
289                Error_action(markup_start, AbsPos());
290                return;
291        } 
292        ScanTo(NameFollow);  /* Name delimiter */
293        PI_Target_action(target_start, AbsPos());
294        ScanTo(QMark);
295        while (!at_PI_End<ASCII>(cur())) {
296                Advance(1);
297                ScanTo(QMark);
298        }
299        Advance(2); /* Skip "?>". */
300        PI_action(markup_start, AbsPos());
301}
302 
303/* Parse a start or empty element tag. */
304inline void ParsingEngine::Parse_StartTag () {
305        int markup_start = AbsPos();
306        int att_name_start;
307        int att_val_start;
308        int att_name_end, att_val_end;
309        lexical_item Quote_stream;
310        ScanTo(NameFollow);  /* Name delimiter: WS, "/" or ">" */
311        ElementName_action(markup_start+1, AbsPos());
312        /* The following test optimizes the most common case of a
313        start tag with no attributes.  */
314        if (AtChar<ASCII,'>'>(cur())) {
315                Advance(1);
316                StartTag_action(markup_start, AbsPos());
317        }
318        else {
319                ScanTo(NonWS);
320                if (AtChar<ASCII,'>'>(cur())) {
321                        Advance(1);
322                        StartTag_action(markup_start, AbsPos());
323                }
324                else if (at_EmptyElementDelim<ASCII>(cur())) {
325                        Advance(2);
326                        EmptyElement_action(markup_start, AbsPos());
327                }
328                else do {
329                        /* Must be an attribute-value pair or error. */
330                        att_name_start = AbsPos();
331                        ScanTo(NameFollow);
332                        att_name_end = AbsPos();
333                        /* The following optimized tests handle the frequently occurring
334                        case that there are no blanks on either side of the equals sign.
335                        In many cases, the very first test handles 100% of actual
336                        attribute-value pairs encountered. */
337                        if (at_EqualsDQuote<ASCII>(cur())) {
338                                Advance(2); 
339                                att_val_start = AbsPos();
340                                Quote_stream = DQuote;
341                        }
342                        else if (at_EqualsSQuote<ASCII>(cur())) {
343                                Advance(2); 
344                                att_val_start = AbsPos();
345                                Quote_stream = SQuote;
346                        }
347                        else {
348                                ScanTo(NonWS);
349                                if (!AtChar<ASCII,'='>(cur())) {
350                                        Error_action(markup_start, AbsPos()); 
351                                        break;
352                                }
353                                ScanTo(NonWS);
354                                att_val_start = AbsPos()+1;
355                                if (AtChar<ASCII,'"'>(cur())) {
356                                        Advance(1); 
357                                        Quote_stream = DQuote;
358                                }
359                                else if (AtChar<ASCII,'\''>(cur())) {
360                                        Advance(1); 
361                                        Quote_stream = SQuote;
362                                }
363                                else {
364                                        Error_action(markup_start, AbsPos()); 
365                                        break;
366                                }
367                        }
368                        ScanTo(Quote_stream);
369                        while (AtChar<ASCII,'&'>(cur())) {
370                                Parse_Reference();
371                                ScanTo(Quote_stream);
372                        }
373                        if (AtChar<ASCII,'<'>(cur())) {
374                                Error_action(markup_start, AbsPos()); 
375                                break;
376                        }
377                        att_val_end = AbsPos();
378                        Advance(1); 
379                        if (at_xmlns<ASCII>(cur())) {
380                                Namespace_action(att_name_start, att_name_end,
381                                                 att_val_start, att_val_end);
382                        }
383                        else {
384                                AttributeValue_action(att_name_start, att_name_end,
385                                                      att_val_start, att_val_end);
386                        }
387                        /* Now check for end or repeat. Avoid whitespace scan if possible.*/
388                        if (AtChar<ASCII,'>'>(cur())) {
389                                Advance(1);
390                                StartTag_action(markup_start, AbsPos());
391                                break;
392                        }
393                        else if (at_EmptyElementDelim<ASCII>(cur())) {
394                                Advance(2);
395                                EmptyElement_action(markup_start, AbsPos());
396                                break;
397                        }
398                        ScanTo(NonWS);
399                        if (AtChar<ASCII,'>'>(cur())) {
400                                Advance(1);
401                                StartTag_action(markup_start, AbsPos());
402                                break;
403                        }
404                        else if (at_EmptyElementDelim<ASCII>(cur())) {
405                                Advance(2);
406                                EmptyElement_action(markup_start, AbsPos());
407                                break;
408                        }
409                        else if (AbsPos() == att_val_end + 1) { 
410                                /* No WS following att value */
411                                Error_action(markup_start, AbsPos());
412                                break;
413                        }
414                } while (1);
415        }
416}
417
418
419inline void ParsingEngine::ParseContent () {
420
421        int text_start = AbsPos();
422        do {
423                ScanTo(MarkupStart); /* '<', '&', or ']' for ']]>' test */
424/*              if (AtChar<ASCII,'<'>(cur())) {
425                        if (AbsPos() > text_start) Text_action(text_start, AbsPos());
426                        Parse_Markup();
427                }*/
428                int markup_start = AbsPos();
429                if (at_ElementTag_Start<ASCII>(cur())) {
430                        if (AbsPos() > text_start) Text_action(text_start, AbsPos());
431                        Parse_StartTag();
432                }
433                else if (at_EndTag_Start<ASCII>(cur())) {
434                        if (AbsPos() > text_start) Text_action(text_start, AbsPos());
435                        Parse_EndTag();
436                }
437                else if (at_Comment_Start<ASCII>(cur())) {
438                        if (AbsPos() > text_start) Text_action(text_start, AbsPos());
439                        Parse_Comment();
440                }
441                else if (AtChar<ASCII,'&'>(cur())) {
442                        if (AbsPos() > text_start) Text_action(text_start, AbsPos());
443                        Parse_Reference();
444                }
445                else if (at_CDATA_Start<ASCII>(cur())) {
446                        if (AbsPos() > text_start) Text_action(text_start, AbsPos());
447                        Parse_CDATA();
448                }
449                else if (at_PI_Start<ASCII>(cur())) {
450                        if (AbsPos() > text_start) Text_action(text_start, AbsPos());
451                        Parse_PI();
452                }
453                else if (at_CDATA_End<ASCII>(cur())) {
454                        if (AbsPos() > text_start) Text_action(text_start, AbsPos());
455                        Advance(3);
456                        Error_action(AbsPos()-3, AbsPos());
457                }
458                else if (at_EOF()) {
459                        if (AbsPos() > text_start) Text_action(text_start, AbsPos());
460                        break;
461                }
462                else {
463                        Advance(1);
464                        continue;
465                }
466                text_start = AbsPos();
467        } while (1);
468#ifdef BUFFER_PROFILING
469        printf("Bit stream computation.\n");
470        dump_BOM_table(bitstream_timer);
471        printf("Lexical stream transposition.\n");
472        dump_BOM_table(lextranspose_timer);
473        printf("Scanning.\n");
474        dump_BOM_table(scanner_timer);
475#endif
476}
477
478//
479// The following does not yet validate the syntax of EncNames.
480// EncName ::= [A-Za-z] ([A-Za-z0-9._] | '-')*
481// Future approach: first use lookup in EncNameTable,
482//           if not found, do case convert, try again,
483//             (avoids cost of case convert normally)
484//           if not found, validate syntax of EncNames,
485//           report error or EncName unknown.
486//
487void ParsingEngine::ReadXmlInfo(Entity_Declaration_Info& xml_info) {
488  int BOM = lex->BOM_size(0);
489  xml_info.has_ByteOrderMark = BOM > 0;
490  xml_info.has_version_decl = false;
491  xml_info.has_encoding_decl = false;
492  xml_info.has_standalone_decl = false;
493  Advance(BOM);
494  int decl_start = AbsPos();
495  // It is possible that there is no XML declaration.
496  if (!at_XmlDecl_start<ASCII>(cur())) return;
497  // Otherwise, the XML declaration exists and must have
498  // at least version information.
499  xml_info.has_version_decl = true;
500  Advance(6);
501  ASCII_ScanTo(NonWS);
502  if (!at_version<ASCII>(cur())) {Error_action(decl_start, AbsPos()); return;}
503  Advance(7);
504  ASCII_ScanTo(NonWS);
505  if (!AtChar<ASCII,'='>(cur())) {Error_action(decl_start, AbsPos()); return;}
506  Advance(1);
507  ASCII_ScanTo(NonWS);
508  if (at_1_0<ASCII>(cur())) xml_info.version = 0;
509  else if (at_1_1<ASCII>(cur())) xml_info.version = 1;
510  else {Error_action(decl_start, AbsPos()); return;}
511  Advance(5);
512  if (at_PI_End<ASCII>(cur())) {Advance(2); return;}
513  if (!at_WhiteSpace<ASCII>(cur())) {Error_action(decl_start, AbsPos()); return;}
514  ASCII_ScanTo(NonWS);
515  if (at_encoding<ASCII>(cur())) {
516      xml_info.has_encoding_decl = true;
517      Advance(8);
518      ASCII_ScanTo(NonWS);
519      if (!AtChar<ASCII,'='>(cur())) {Error_action(decl_start, AbsPos()); return;}
520      Advance(1);
521      ASCII_ScanTo(NonWS);
522      xml_info.encoding_start_pos = AbsPos()+1;
523      if (AtChar<ASCII,'"'>(cur())) {
524        Advance(1);
525        ASCII_ScanTo(DQuote);
526        if (!AtChar<ASCII,'"'>(cur())) {Error_action(decl_start, AbsPos()); return;}
527      }
528      else if (AtChar<ASCII,'\''>(cur())) {
529        Advance(1);
530        ASCII_ScanTo(SQuote);
531        if (!AtChar<ASCII,'\''>(cur())) {Error_action(decl_start, AbsPos()); return;}
532      }
533      else {Error_action(decl_start, AbsPos()); return;}
534      xml_info.encoding_end_pos = AbsPos();
535      Advance(1);
536      if (at_PI_End<ASCII>(cur())) {Advance(2); return;}
537      if (!at_WhiteSpace<ASCII>(cur())) {Error_action(decl_start, AbsPos()); return;}
538      ASCII_ScanTo(NonWS);
539  }
540  if (at_standalone<ASCII>(cur())) {
541      xml_info.has_standalone_decl = true;
542      Advance(10);
543      ASCII_ScanTo(NonWS);
544      if (!AtChar<ASCII,'='>(cur())) {Error_action(decl_start, AbsPos()); return;}
545      Advance(1);
546      ASCII_ScanTo(NonWS);
547      if (at_yes<ASCII>(cur())) {Advance(5); xml_info.standalone = true;}
548      else if (at_no<ASCII>(cur())) {Advance(4); xml_info.standalone = false;}
549      else {Error_action(decl_start, AbsPos()); return;}
550      ASCII_ScanTo(NonWS);
551  }
552  if (at_PI_End<ASCII>(cur())) {Advance(2); return;}
553  else {Error_action(decl_start, AbsPos()); return;}
554}
555
556// Similar to reading the XML_declaration of the document entity,
557// ReadTextDeclaration reads the text declaration of an external
558// parsed entity.  This is not really needed at present, for DTDless
559// processing.
560void ParsingEngine::ReadTextDeclaration(Entity_Declaration_Info& xml_info) {
561  int BOM = lex->BOM_size(0);
562  xml_info.has_ByteOrderMark = BOM > 0;
563  xml_info.has_version_decl = false;
564  xml_info.has_encoding_decl = false;
565  xml_info.has_standalone_decl = false;
566  Advance(BOM);
567  int decl_start = AbsPos();
568  // It is possible that there is no XML declaration.
569  if (!at_XmlDecl_start<ASCII>(cur())) return;
570  // Otherwise, the text declaration exists and may have
571  // version information.
572  Advance(6);
573  ASCII_ScanTo(NonWS);
574  if (at_version<ASCII>(cur())) {
575    xml_info.has_version_decl = true;
576    Advance(7);
577    ASCII_ScanTo(NonWS);
578    if (!AtChar<ASCII,'='>(cur())) {Error_action(decl_start, AbsPos()); return;}
579    Advance(1);
580    ASCII_ScanTo(NonWS);
581    if (at_1_0<ASCII>(cur())) xml_info.version = 0;
582    else if (at_1_1<ASCII>(cur())) xml_info.version = 1;
583    else {Error_action(decl_start, AbsPos()); return;}
584    Advance(5);
585    // Must have whitespace character before declaration.
586    if (!at_WhiteSpace<ASCII>(cur())) {Error_action(decl_start, AbsPos()); return;}
587    ASCII_ScanTo(NonWS);
588  }
589  if (!at_encoding<ASCII>(cur())) {Error_action(decl_start, AbsPos()); return;}
590  xml_info.has_encoding_decl = true;
591  Advance(8);
592  ASCII_ScanTo(NonWS);
593  if (!AtChar<ASCII,'='>(cur())) {Error_action(decl_start, AbsPos()); return;}
594  Advance(1);
595  ASCII_ScanTo(NonWS);
596  xml_info.encoding_start_pos = AbsPos()+1;
597  if (AtChar<ASCII,'"'>(cur())) {
598      Advance(1);
599      ASCII_ScanTo(DQuote);
600      if (!AtChar<ASCII,'"'>(cur())) {Error_action(decl_start, AbsPos()); return;}
601  }
602  else if (AtChar<ASCII,'\''>(cur())) {
603      Advance(1);
604      ASCII_ScanTo(SQuote);
605      if (!AtChar<ASCII,'\''>(cur())) {Error_action(decl_start, AbsPos()); return;}
606  }
607  else {Error_action(decl_start, AbsPos()); return;}
608  xml_info.encoding_end_pos = AbsPos();
609  Advance(1);
610  ASCII_ScanTo(NonWS);
611  if (at_PI_End<ASCII>(cur())) {Advance(2); return;}
612  else {Error_action(decl_start, AbsPos()); return;}
613}
Note: See TracBrowser for help on using the repository browser.