source: proto/parabix2/parabix2.py @ 570

Last change on this file since 570 was 570, checked in by lindanl, 9 years ago

error check for empty PI names

File size: 30.4 KB
Line 
1#
2# parabix2.py
3#
4# Parallel XML Parsing with Bitstream Addition
5# - Complete prototype for all bitstream computations in Parabix2
6#
7# Robert D. Cameron
8# August 20, 2009
9#
10#----------------------------------------------------------------------------
11#
12# We use python's unlimited precision integers for unbounded bit streams.
13# This permits simple logical operations on the entire stream.
14# Assumption: bitstreams are little-endian (e.g., as on x86).
15#
16#----------------------------------------------------------------------------
17#
18
19
20import bitutil
21
22import byteclass
23
24import u8u16
25
26import sys
27
28
29def validate_xmlchar(u8, control, lex, EOF_mask):
30        r"""Compute an error stream marking characters illegal in XML:
31        (1) Control characters in the range 0x00-0x1F except HT, LF, CR
32        (2) OxFFFF and OxFFFE, having UTF-8 encodings 0xEF 0xBF 0XBF and 0xEF 0xBF 0xBE.
33
34        >>> demo_validate_xmlchar('plaintext (good: \x09) (bad: \x03) (bad \xEF\xBF\xBF) (good \xEF\xBF\xBC)')
35        input high nybbles: 7666676772266663202226663202226662ebb22266662ebb2
36        input low nybbles : 0c19e4584087ff4a09908214a039082140fff9087ff40ffc9
37        illegal XML chars : __________________________1_________1_____________
38"""
39        EF_BF_pending = bitutil.Advance(u8.xEF_scope & u8.xBF)
40        return (EF_BF_pending & (u8.xBE | u8.xBF)) | (control.x00_x1F &~ lex.WS & EOF_mask)
41
42
43def demo_validate_xmlchar(u8data):
44        lgth = len(u8data)
45        (bit, EOF_mask) = bitutil.transpose_streams(u8data)
46        (u8, control, lex) = byteclass.classify_bytes(bit)
47        bitutil.print_aligned_streams([('input high nybbles', bitutil.high_nybble_stream(u8data)), 
48                              ('input low nybbles', bitutil.low_nybble_stream(u8data)),
49                              ('illegal XML chars', bitutil.bitstream2string(validate_xmlchar(u8, control, lex, EOF_mask), lgth+1))])
50
51def normalize_line_breaks(control, bit):
52        r"""Convert CRs to LFs and mark CRLF occurrences for deletion.
53
54        >>> demo_line_breaks('ab \r\n  cd \r  ef \r ')
55        input high nybbles: 662002266202266202
56        input low nybbles : 120da00340d00560d0
57        CR                : ___1______1_____1_
58        LF                : ____1_____________
59        CRLF              : ____1_____________
60"""
61        control.CRLF = control.CR_scope & control.LF
62        # Convert CRs to LFs (flip bits 5, 6 and 7 with xor).
63        bit[5] ^= control.CR
64        bit[6] ^= control.CR
65        bit[7] ^= control.CR
66        return (control, bit)
67
68def demo_line_breaks(u8data):
69        lgth = len(u8data)
70        (bit, EOF_mask) = bitutil.transpose_streams(u8data)
71        (u8, control, lex) = byteclass.classify_bytes(bit)
72        (control, bit) = normalize_line_breaks(control, bit)
73        bitutil.print_aligned_u8_byte_streams([('input high nybbles', bitutil.high_nybble_stream(u8data)), 
74                              ('input low nybbles', bitutil.low_nybble_stream(u8data)),
75                              ('CR', bitutil.bitstream2string(control.CR, lgth)),
76                              ('LF', bitutil.bitstream2string(control.LF, lgth)),
77                              ('CRLF', bitutil.bitstream2string(control.CRLF, lgth))])
78
79
80
81
82
83def add_multiliterals(lex):
84        """Extend the byte-based lexical item streams for some important
85        multibyte literals.
86       
87        >>> demo_multiliterals("  <?php?>  <!--  -->  <![CDATA[  ]]> ")
88        input data  :   <?php?>  <!--  -->  <![CDATA[  ]]>
89        PI_start    : ___1_________________________________
90        CtCD_start  : ____________1__________1_____________
91        EndTag_start: _____________________________________
92        CD_end      : ___________________________________1_
93        DoubleHyphen: ______________1___1__________________
94        PI_end      : ________1____________________________
95        """
96
97        lex.PI_start = lex.LAngle_scope & lex.QMark
98        lex.CtCD_start = lex.LAngle_scope & lex.Exclam
99        lex.EndTag_start = lex.LAngle_scope & lex.Slash
100        lex.CD_end = bitutil.Advance(lex.RBracket_scope & lex.RBracket) & lex.RAngle
101        lex.DoubleHyphen = lex.Hyphen_scope & lex.Hyphen
102        lex.PI_end = lex.QMark_scope & lex.RAngle
103        return lex
104
105def demo_multiliterals(u8data):
106        lgth = len(u8data)
107        (bit, EOF_mask) = bitutil.transpose_streams(u8data)
108        (u8, control, lex) = byteclass.classify_bytes(bit)
109        lex = add_multiliterals(lex)
110        bitutil.print_aligned_u8_byte_streams([('input data', u8data), 
111                              ('PI_start', bitutil.bitstream2string(lex.PI_start, lgth)),
112                              ('CtCD_start', bitutil.bitstream2string(lex.CtCD_start, lgth)),
113                              ('EndTag_start', bitutil.bitstream2string(lex.EndTag_start, lgth)),
114                              ('CD_end', bitutil.bitstream2string(lex.CD_end, lgth)),
115                              ('DoubleHyphen', bitutil.bitstream2string(lex.DoubleHyphen, lgth)),
116                              ('PI_end', bitutil.bitstream2string(lex.PI_end, lgth))])
117
118class CtCDPI_callouts:
119        CD_span = 0
120        Ct_span = 0
121        PI_mask = 0
122        PI_name = 0
123        CtCDPI_mask = 0
124        error = 0
125       
126def parse_CtCDPI(lex, EOF_mask):
127        """Parse all comments, CDATA sections and processing instructions.
128       
129        Return bitstreams marking the extent of these markup items,
130        excluding initial and final bracketting.
131       
132        >>> demo_CtCDPI(' <?php?>  <!-- example -->  <![CDATA[  shift: a<<1 ]]> ')
133        input data :  <?php?>  <!-- example -->  <![CDATA[  shift: a<<1 ]]>
134        CD_span    : ______________________________11111111111111111111111__
135        Ct_span    : _____________111111111111______________________________
136        PI_span    : __11111________________________________________________
137        CtCDPI_mask: __111111___111111111111111___1111111111111111111111111_
138        error      : ________________________________________________________
139       
140        Comments are terminated by double-hyphen; immediately require closing ">".
141       
142        >>> demo_CtCDPI(' <!--  <?php?>  --   <!-- -->')
143        input data :  <!--  <?php?>  --   <!-- -->
144        CD_span    : _____________________________
145        Ct_span    : ____11111111111111______1111_
146        PI_span    : _____________________________
147        CtCDPI_mask: __11111111111111111___1111111
148        error      : __________________1___________
149
150        >>> demo_CtCDPI("<?PI_target??>")
151        input data : <?PI_target??>
152        CD_span    : ______________
153        Ct_span    : ______________
154        PI_span    : _111111111111_
155        CtCDPI_mask: _1111111111111
156        error      : ____________1__
157
158
159"""
160        callouts = CtCDPI_callouts()
161        PI_starts = 0
162        PI_ends = 0
163        Ct_starts = 0
164        Ct_ends = 0
165        CD_starts = 0
166        CD_ends = 0
167        CtCDPI_starts = 0
168        PI_name_ends = 0
169        PI_namestarts = 0
170        # Scanning streams
171        CtCDPI_scan = ~(lex.CtCD_start | lex.PI_start) & EOF_mask
172        Ct_end_scan = ~lex.DoubleHyphen & EOF_mask
173        CD_end_scan = ~lex.CD_end & EOF_mask
174        PI_end_scan = ~lex.PI_end & EOF_mask
175        #
176        # Initiate the scan
177        CtCDPI_Cursor = 1
178        CtCDPI_Cursor = bitutil.ScanThru(CtCDPI_Cursor, CtCDPI_scan)
179        CtCDPI_Cursor &= EOF_mask
180        while CtCDPI_Cursor:
181                CtCDPI_starts |= CtCDPI_Cursor
182                PI_Cursor = CtCDPI_Cursor & lex.PI_start
183                CD_Ct_Cursor = bitutil.Advance(CtCDPI_Cursor & ~PI_Cursor)
184                CD_Cursor = CD_Ct_Cursor & lex.LBracket
185                Ct_Cursor = bitutil.Advance(CD_Ct_Cursor & lex.Hyphen) 
186                PI_starts |= PI_Cursor
187                CD_starts |= CD_Cursor
188                Ct_starts |= Ct_Cursor
189                Ct_Cursor = bitutil.Advance(Ct_Cursor)
190                Ct_end_scan |= Ct_Cursor
191                #PI_Cursor = bitutil.ScanThru(PI_Cursor, PI_end_scan)
192                PI_Cursor = bitutil.Advance(PI_Cursor)
193                PI_namestarts |= PI_Cursor
194                PI_name_end = bitutil.ScanThru(PI_Cursor, lex.NameScan)
195                PI_name_ends |= PI_name_end
196                callouts.PI_name |= PI_name_end - PI_Cursor
197                PI_Cursor = bitutil.ScanThru(PI_name_end, PI_end_scan)
198                CD_Cursor = bitutil.ScanThru(CD_Cursor, CD_end_scan)
199                Ct_Cursor = bitutil.Advance(bitutil.ScanThru(Ct_Cursor, Ct_end_scan))
200                PI_ends |= PI_Cursor
201                CD_ends |= CD_Cursor
202                Ct_ends |= Ct_Cursor
203                CtCDPI_Cursor = PI_Cursor | CD_Cursor | Ct_Cursor
204                CtCDPI_Cursor = bitutil.ScanThru(CtCDPI_Cursor, CtCDPI_scan)
205                CtCDPI_Cursor &= EOF_mask
206        # End of loop: no remaining CtCDPI_Cursor
207        callouts.CD_span = CD_ends - CD_starts
208        callouts.Ct_span = Ct_ends - Ct_starts
209        callouts.PI_span = PI_ends - PI_starts
210       
211        callouts.CtCDPI_mask |= bitutil.Advance(CD_ends | Ct_ends | PI_ends) - CtCDPI_starts
212        callouts.error = Ct_ends & ~lex.RAngle | Ct_starts & ~ lex.Hyphen
213        callouts.error |= bitutil.Advance(PI_name_ends & ~ lex.WS) & ~ lex.PI_end
214        callouts.error |= PI_namestarts & PI_name_ends
215        # If any of the Comment, CDATA or PI markups are unterminated, it is an error.
216        callouts.error |= callouts.CtCDPI_mask &~ EOF_mask
217        return callouts
218
219def demo_CtCDPI(u8data):
220        lgth = len(u8data)
221        (bit, EOF_mask) = bitutil.transpose_streams(u8data)
222        (u8, control, lex) = byteclass.classify_bytes(bit)
223        lex = add_multiliterals(lex)
224        markup = parse_CtCDPI(lex, EOF_mask)
225        bitutil.print_aligned_u8_byte_streams([('input data', u8data), 
226                              ('CD_span', bitutil.bitstream2string(markup.CD_span, lgth)),
227                              ('Ct_span', bitutil.bitstream2string(markup.Ct_span, lgth)),
228                              ('PI_span', bitutil.bitstream2string(markup.PI_span, lgth)),
229                              ('CtCDPI_mask', bitutil.bitstream2string(markup.CtCDPI_mask, lgth)),
230                              ('error', bitutil.bitstream2string(markup.error, lgth+1))])
231
232
233class ref_callouts:
234        GenRefs = 0
235        DecRefs = 0
236        HexRefs = 0
237        delmask = 0
238        error = 0
239
240def parse_refs(lex, CtCDPI_mask):
241        """Parse and call out all general and character references.
242        Mark all but the closing semicolon for deletion.
243       
244        >>> demo_refs(" &gt;  &#13;  &#x0a;  ")
245        input data       :  &gt;  &#13;  &#x0a; 
246        entity refs      : __11__________________
247        decimal char refs: _________11___________
248        hex char refs    : _________________11___
249        ref delmask      : _111___1111___11111___
250        errors           : _______________________
251
252        Empty numeric references are reported as errors.
253        >>> demo_refs(" &#;       &#x; ")
254        input data       :  &#;       &#x;
255        entity refs      : ________________
256        decimal char refs: ________________
257        hex char refs    : ________________
258        ref delmask      : _11________111__
259        errors           : ___1__________1__
260
261        Improperly terminated or unterminated references (lacking ";") are also errors.
262        >>> demo_refs("  &gt:  &#456a;  &#xab:  &unterminated")
263        input data       :   &gt:  &#456a;  &#xab:  &unterminated
264        entity refs      : ___111____________________111111111111
265        decimal char refs: __________111_________________________
266        hex char refs    : ____________________11________________
267        ref delmask      : __1111__11111____11111___1111111111111
268        errors           : ______1______1________1_______________1
269"""
270        CallOuts = ref_callouts()
271        Ref2 = lex.RefStart_scope &~ CtCDPI_mask
272        NumRef2 = Ref2 & lex.Hash
273        GenRef2 = Ref2 &~ lex.Hash
274        NumRef3 = bitutil.Advance(NumRef2)
275        HexRef3 = NumRef3 & lex.x
276        DecRef3 = NumRef3 &~ lex.x
277        HexRef4 = bitutil.Advance(HexRef3) 
278        GenRefEnds = bitutil.ScanThru(GenRef2, lex.NameScan)
279        DecRefEnds = bitutil.ScanThru(DecRef3, lex.Digit)
280        HexRefEnds = bitutil.ScanThru(HexRef4, lex.Hex)
281        # Error checks
282        # At least one digit required for DecRef, one hex digit for HexRef.
283        error1 = DecRef3 &~ lex.Digit
284        error2 = HexRef4 &~ lex.Hex
285        # Semicolon terminator required (also covers unterminated at EOF).
286        error3 = (GenRefEnds | DecRefEnds | HexRefEnds) &~ lex.Semicolon
287        CallOuts.GenRefs = GenRefEnds - GenRef2
288        CallOuts.DecRefs = DecRefEnds - DecRef3
289        CallOuts.HexRefs = HexRefEnds - HexRef4
290        # Mark references for deletion, but leave the trailing semicolon as
291        # the point for insertion of the "expansion" text (most often a
292        # single character).
293        CallOuts.delmask = (GenRefEnds | DecRefEnds | HexRefEnds) - lex.RefStart
294        CallOuts.error = error1 | error2 | error3
295        return CallOuts
296
297def demo_refs(u8data):
298        lgth = len(u8data)
299        (bit, EOF_mask) = bitutil.transpose_streams(u8data)
300        (u8, control, lex) = byteclass.classify_bytes(bit)
301        callouts = parse_refs(lex, 0)
302        bitutil.print_aligned_u8_byte_streams([('input data', u8data), 
303                              ('entity refs', bitutil.bitstream2string(callouts.GenRefs, lgth)),
304                              ('decimal char refs', bitutil.bitstream2string(callouts.DecRefs, lgth)),
305                              ('hex char refs', bitutil.bitstream2string(callouts.HexRefs, lgth)),
306                              ('ref delmask', bitutil.bitstream2string(callouts.delmask, lgth)),
307                              ('errors', bitutil.bitstream2string(callouts.error, lgth+1))])
308
309
310class tag_callouts:
311        ElemNames = 0
312        AttNames = 0
313        AttVals = 0
314        Tags = 0
315        EmptyTagEnds = 0
316        EndTags = 0
317        error = 0
318       
319        # POTENTIAL ADDITIONAL FIELDS
320        # StartTagEnds = 0
321        # EmptyTagEnds = 0     
322        # EndTagEnds = 0
323
324def parse_tags(lex, CtCDPI_mask, EOF_mask):
325        """Parse start, empty and end tags, calling out element names, attribute
326        names and values, empty tag positions, and tag extents.
327
328        >>> demo_tags("<root><t1>text</t1><t2 a1='foo' a2 = 'fie'>more</t2><tag3 att3='b'/></root>")
329        input data      : <root><t1>text</t1><t2 a1='foo' a2 = 'fie'>more</t2><tag3 att3='b'/></root>
330        element names   : _1111__11___________11_______________________________1111__________________
331        attribute names : _______________________11_______11________________________1111_____________
332        attribute values: __________________________11111______11111_____________________111_________
333        empty tag marks : ___________________________________________________________________1_______
334        end tags        : _______________111______________________________111__________________11111_
335        start/empty tags: _1111__11___________1111111111111111111111___________11111111111111________
336        errors          : ____________________________________________________________________________
337
338        Attributes can use double quotes.
339
340        >>> demo_tags('<dquote_atts a1="1234" attribute2="4321"/>')
341        input data      : <dquote_atts a1="1234" attribute2="4321"/>
342        element names   : _11111111111______________________________
343        attribute names : _____________11________1111111111_________
344        attribute values: ________________111111____________111111__
345        empty tag marks : _________________________________________1
346        end tags        : __________________________________________
347        start/empty tags: _1111111111111111111111111111111111111111_
348        errors          : ___________________________________________
349
350        Syntax errors of various types are identified with the error stream.
351
352        1. Element name missing errors.
353
354        >>> demo_tags("< noname='flawed'/> ")
355        input data      : < noname='flawed'/>
356        element names   : ____________________
357        attribute names : __111111____________
358        attribute values: _________11111111___
359        empty tag marks : __________________1_
360        end tags        : ____________________
361        start/empty tags: _11111111111111111__
362        errors          : _1___________________
363
364        2. Missing attribute names.
365
366        >>> demo_tags("<noatt ='flawed'/>  <one_att a1='good' = 'bad'> oops </one_att>")
367        input data      : <noatt ='flawed'/>  <one_att a1='good' = 'bad'> oops </one_att>
368        element names   : _11111_______________1111111___________________________________
369        attribute names : _____________________________11________________________________
370        attribute values: ________11111111________________111111___11111_________________
371        empty tag marks : _________________1_____________________________________________
372        end tags        : ______________________________________________________11111111_
373        start/empty tags: _1111111111111111____1111111111111111111111111_________________
374        errors          : _______1_______________________________1________________________
375
376        3. Missing or incorrect = sign.
377
378        >>> demo_tags('<errata plusforeq+"5678" noequals"90" />')
379        input data      : <errata plusforeq+"5678" noequals"90" />
380        element names   : _111111_________________________________
381        attribute names : ________111111111________11111111_______
382        attribute values: __________________111111__________111111
383        empty tag marks : ________________________________________
384        end tags        : ________________________________________
385        start/empty tags: _111111111111111111111111111111111111111
386        errors          : _________________1_______________11______
387
388        4.  Missing whitespace
389
390        >>> demo_tags("<jammed att='value'att2='v2' />")
391        input data      : <jammed att='value'att2='v2' />
392        element names   : _111111________________________
393        attribute names : ________111________1111________
394        attribute values: ____________1111111_____1111___
395        empty tag marks : ______________________________1
396        end tags        : _______________________________
397        start/empty tags: _11111111111111111111111111111_
398        errors          : ___________________1____________
399
400        5.  Extra whitespace in an empty tag.
401
402        >>> demo_tags("<extrawhite / >")
403        input data      : <extrawhite / >
404        element names   : _1111111111____
405        attribute names : _______________
406        attribute values: _______________
407        empty tag marks : _____________1_
408        end tags        : _______________
409        start/empty tags: _111111111111__
410        errors          : _____________1__
411
412        6.  Unterminated or incorrectly terminated attribute values
413
414        >>> demo_tags("<badattvalues a='blud<   b='455>   ")
415        input data      : <badattvalues a='blud<   b='455>   
416        element names   : _111111111111______________________
417        attribute names : ______________1__________1_________
418        attribute values: ________________111111_____11111111
419        empty tag marks : ___________________________________
420        end tags        : ___________________________________
421        start/empty tags: _111111111111111111111_111111111111
422        errors          : _____________________11____________1
423
424        7.  Unterminated tags
425
426        >>> demo_tags("<unterminated a='245'  ")
427        input data      : <unterminated a='245' 
428        element names   : _111111111111__________
429        attribute names : ______________1________
430        attribute values: ________________11111__
431        empty tag marks : _______________________
432        end tags        : _______________________
433        start/empty tags: _1111111111111111111111
434        errors          : _______________________1
435
436"""
437        callouts = tag_callouts()
438       
439        # Delimiters for scans.
440        DQuoteScan = ~(lex.DQuote | lex.LAngle) & EOF_mask
441        SQuoteScan = ~(lex.SQuote | lex.LAngle) & EOF_mask
442        AttListDelim = lex.Slash | lex.RAngle
443       
444        # Start the parallel parsing by inspecting the character
445        # after the opening "<" of a tag.
446        LAngleFollow = lex.LAngle_scope &~ CtCDPI_mask
447        ElemNamePositions = LAngleFollow & ~lex.Slash
448        EndTagSeconds = LAngleFollow & lex.Slash
449       
450        # Start Tag/Empty Element Tag Parsing
451
452        # Advance all cursors by scanning through the tag name.
453        ElemNameFollows = bitutil.ScanThru(ElemNamePositions, lex.NameScan)
454        # Must have at least one name character for a legal start tag.
455        # Mark any occurrences of null names as errors.
456        ParseError = ElemNamePositions & ElemNameFollows
457        callouts.ElemNames = ElemNameFollows - ElemNamePositions
458       
459        # Initialize the accumulators for attribute name and value positions.
460        AttNameStarts = 0 
461        AttNameFollows = 0
462        EqToCheck = 0
463        AttValStarts = 0
464        AttValEnds = 0
465        AttValFollows = 0
466
467        # After the element name, there may or may not be an attlist.
468        AfterWS = bitutil.ScanThru(ElemNameFollows, lex.WS)
469        AttListEnd = AfterWS & AttListDelim
470        AttNameStart = AfterWS & ~AttListDelim
471        # At least one WS character is required between ElemNames and AttNames.
472        ParseError |= ElemNameFollows & AttNameStart
473
474        #
475        # The following loop iterates through attributes within a start tag.
476        # Because all start tags are processed in parallel, the number of
477        # iterations is the maximum number of attributes found in any one
478        # start tag, plus one.
479        while AttNameStart:
480                AttNameStarts |= AttNameStart
481                AttNameFollow = bitutil.ScanThru(AttNameStart, lex.NameScan)
482                AttNameFollows |= AttNameFollow
483                # Scan through WS to the expected '=' delimiter.
484                EqExpected = bitutil.ScanThru(AttNameFollow, lex.WS)
485                EqToCheck |= EqExpected
486                AttValPos = bitutil.ScanThru(bitutil.Advance(EqExpected), lex.WS)
487                AttValStarts |= AttValPos
488                DQuoteAttVal = AttValPos & lex.DQuote
489                SQuoteAttVal = AttValPos & lex.SQuote
490                DQuoteAttEnd = bitutil.ScanThru(bitutil.Advance(DQuoteAttVal), DQuoteScan)
491                SQuoteAttEnd = bitutil.ScanThru(bitutil.Advance(SQuoteAttVal), SQuoteScan)
492                AttValEnd = DQuoteAttEnd | SQuoteAttEnd
493                AttValEnds |= AttValEnd
494                AttValFollow = bitutil.Advance(AttValEnd)
495                AttValFollows |= AttValFollow
496                AfterWS = bitutil.ScanThru(AttValFollow, lex.WS)
497                AttListEnd |= AfterWS & AttListDelim
498                AttNameStart = AfterWS & ~AttListDelim
499
500        # No more attribute values to process when AttNameStart == 0.
501
502        callouts.AttNames = AttNameFollows - AttNameStarts
503        callouts.AttVals = AttValFollows - AttValStarts
504        STagEnds = AttListEnd & lex.RAngle
505        # Mark any "/" characters found as the ends of empty element tags.
506        callouts.EmptyTagMarks = bitutil.Advance(AttListEnd & lex.Slash)
507        callouts.Tags = (STagEnds | callouts.EmptyTagMarks) - ElemNamePositions
508
509        # Check for errors.
510        ParseError |= AttValFollows & AttNameStarts # No intervening WS.
511        ParseError |= AttNameStarts & AttNameFollows # Null AttName
512        ParseError |= EqToCheck & ~lex.Equals # = not found where expected.
513        ParseError |= AttValStarts & ~ (lex.DQuote | lex.SQuote)
514        ParseError |= AttValEnds & ~ (lex.DQuote | lex.SQuote)
515        ParseError |= callouts.EmptyTagMarks & ~lex.RAngle
516
517        # End Tag Parsing
518        EndTagEnds = bitutil.ScanThru(bitutil.ScanThru(bitutil.Advance(EndTagSeconds), lex.NameScan), lex.WS)
519        ParseError |= EndTagEnds & ~lex.RAngle
520        callouts.EndTags = EndTagEnds - EndTagSeconds
521        callouts.error = ParseError
522
523        # POTENTIAL ADDITIONAL FIELDS
524        # callouts.StartTagEnds = STagEnds
525        # callouts.EmptyTagEnds = bitutil.Advance(callouts.EmptyTagMarks)
526        # callouts.EndTagEnds = EndTagEnds
527       
528        return callouts
529
530def demo_tags(u8data):
531        lgth = len(u8data)
532        (bit, EOF_mask) = bitutil.transpose_streams(u8data)
533        (u8, control, lex) = byteclass.classify_bytes(bit)
534        lex = add_multiliterals(lex)
535        markup1 = parse_CtCDPI(lex, EOF_mask)
536        callouts = parse_tags(lex, markup1.CtCDPI_mask, EOF_mask)
537        bitutil.print_aligned_u8_byte_streams([('input data', u8data), 
538                              ('element names', bitutil.bitstream2string(callouts.ElemNames, lgth)),
539                              ('attribute names', bitutil.bitstream2string(callouts.AttNames, lgth)),
540                              ('attribute values', bitutil.bitstream2string(callouts.AttVals, lgth)),
541                              ('empty tag marks', bitutil.bitstream2string(callouts.EmptyTagMarks, lgth)),
542                              ('end tags', bitutil.bitstream2string(callouts.EndTags, lgth)),
543                              ('start/empty tags', bitutil.bitstream2string(callouts.Tags, lgth)),
544                              ('errors', bitutil.bitstream2string(callouts.error, lgth+1))])
545
546
547
548def validate_no_CD_end(lex, markup1, tags):
549        """Find illegal occurrences of ]]> in text (outside of markup).
550
551        >>> demo_validate_no_CD_end(' <!-- OK: ]]>  --> <![CDATA OK  ]]>  ]]> <tag att=" ]]> "/> ]]>  <?php ]]> ?> ')
552        input data :  <!-- OK: ]]>  --> <![CDATA OK  ]]>  ]]> <tag att=" ]]> "/> ]]>  <?php ]]> ?>
553        CtCDPI_mask: __1111111111111111__111111111111111_______________________________11111111111_
554        tags       : __________________________________________1111111111111111____________________
555        illegal ]]>: _______________________________________1______________________1_______________
556"""
557        return lex.CD_end & ~(markup1.CtCDPI_mask | tags.Tags)
558
559def demo_validate_no_CD_end(u8data):
560        lgth = len(u8data)
561        (bit, EOF_mask) = bitutil.transpose_streams(u8data)
562        (u8, control, lex) = byteclass.classify_bytes(bit)
563        lex = add_multiliterals(lex)
564        markup1 = parse_CtCDPI(lex, EOF_mask)
565        tags = parse_tags(lex, markup1.CtCDPI_mask, EOF_mask)
566        error = validate_no_CD_end(lex, markup1, tags)
567        bitutil.print_aligned_u8_byte_streams([('input data', u8data), 
568                              ('CtCDPI_mask', bitutil.bitstream2string(markup1.CtCDPI_mask, lgth)),
569                              ('tags', bitutil.bitstream2string(tags.Tags, lgth)),
570                              ('illegal ]]>', bitutil.bitstream2string(error, lgth))])
571
572
573def prevalidate_names(u8, lex, name_stream, nmtoken_stream):
574        """  Fully validate ASCII-based names and identify non-ASCII positions within names.
575        >>> demo_prevalidate_names("<good -bad='hyphen'/><_OK/><:funny:butOK/><1problem a='b' d423='x'>")
576        input data      : <good -bad='hyphen'/><_OK/><:funny:butOK/><1problem a='b' d423='x'>
577        names           : _1111_1111____________111___111111111111___11111111_1_____1111_____
578        name_start_check: ______1____________________________________1_______________________
579        name_check      : ___________________________________________________________________
580"""
581        name_start = name_stream &~ bitutil.Advance(name_stream)
582        name_start_check = name_start & ~lex.ASCII_name_start
583        name_check = (name_stream &~ name_start | nmtoken_stream) & ~lex.ASCII_name_char & ~u8.suffix
584        return (name_start_check, name_check)
585
586def demo_prevalidate_names(u8data):
587        lgth = len(u8data)
588        (bit, EOF_mask) = bitutil.transpose_streams(u8data)
589        (u8, control, lex) = byteclass.classify_bytes(bit)
590        lex = add_multiliterals(lex)
591        markup1 = parse_CtCDPI(lex, EOF_mask)
592        callouts = parse_tags(lex, markup1.CtCDPI_mask, EOF_mask)
593        name_stream = callouts.ElemNames | callouts.AttNames
594        (name_start_check, name_check) = prevalidate_names(u8, lex, name_stream, 0)
595        bitutil.print_aligned_u8_byte_streams([('input data', u8data), 
596                              ('names', bitutil.bitstream2string(name_stream, lgth)),
597                              ('name_start_check', bitutil.bitstream2string(name_start_check, lgth)),
598                              ('name_check', bitutil.bitstream2string(name_check, lgth))])
599
600def validate_namespace_names(lex, name_stream):
601        """ Ensure that any colon (namespace separator) in a name divides the
602        name into nonempty namespace prefix and local-part components, neither
603        of which contain colons.
604        >>> demo_validate_namespace_names("<a:b  xmlns: = '3' :x = '1' y: = '2'/>")
605        input data  : <a:b  xmlns: = '3' :x = '1' y: = '2'/>
606        names       : _111__111111_______11_______11________
607        NCname_error: ____________1______1__________1_______
608        >>> demo_validate_namespace_names("<a  xmlns:g = '3' a:x = '1' y::b = '2'/>")
609        input data  : <a  xmlns:g = '3' a:x = '1' y::b = '2'/>
610        names       : _1__1111111_______111_______1111________
611        NCname_error: ______________________________1_________
612        """
613        name_cursor = name_stream & ~bitutil.Advance(name_stream)
614        void_prefix_err = name_cursor & lex.Colon
615        namespace_sep = bitutil.ScanThru(name_cursor, lex.NameScan &~ lex.Colon) & lex.Colon
616        local_part_start = bitutil.Advance(namespace_sep)
617        local_part_err = local_part_start &~ lex.NameScan
618        colon2_err = bitutil.ScanThru(local_part_start, lex.NameScan &~ lex.Colon) & lex.Colon
619        return void_prefix_err | local_part_err | colon2_err
620       
621def demo_validate_namespace_names(u8data):
622        lgth = len(u8data)
623        (bit, EOF_mask) = bitutil.transpose_streams(u8data)
624        (u8, control, lex) = byteclass.classify_bytes(bit)
625        lex = add_multiliterals(lex)
626        markup1 = parse_CtCDPI(lex, EOF_mask)
627        callouts = parse_tags(lex, markup1.CtCDPI_mask, EOF_mask)
628        name_stream = callouts.ElemNames | callouts.AttNames
629        name_check = validate_namespace_names(lex, name_stream)
630        bitutil.print_aligned_u8_byte_streams([('input data', u8data), 
631                              ('names', bitutil.bitstream2string(name_stream, lgth)),
632                              ('NCname_error', bitutil.bitstream2string(name_check, lgth))])
633
634
635def parabix_parse(u8data):
636        # Transpose to parallel bit streams and prepare an EOF mask.
637        (bit, EOF_mask) = bitutil.transpose_streams(u8data)
638
639        # Classify bytes for UTF-8 processing, whitespace and control
640        # processing and XML lexical analysis.
641        #(u8, control, lex) = byteclass.classify_bytes(bit)
642        (u8, control, lex) = byteclass.classify_bytes_with_shift1opt(bit)
643
644        # Validate UTF-8 multibyte sequences and determine the UTF-8 scope streams.
645        u8 = u8u16.validate_utf8(u8)
646
647        # Rule out the illegal characters for XML.
648        xmlchar_error = validate_xmlchar(u8, control, lex, EOF_mask)
649
650        # Find and normalize bare CR or CRLF combinations.
651        (control, bit) = normalize_line_breaks(control, bit)
652
653        # Compute XML multilterals such as <?, </, --, ]]>.
654        lex = add_multiliterals(lex)
655
656        # Parse all comments, CDATA sections and processing instructions.
657        markup1 = parse_CtCDPI(lex, EOF_mask)
658
659        # All remaining "<" must be tag start characters; parse tags.
660        tags = parse_tags(lex, markup1.CtCDPI_mask, EOF_mask)
661
662        # All remaining "&" must be reference start characters; parse them.
663        refs = parse_refs(lex, markup1.CtCDPI_mask)
664
665        # Ensure that no occurrence of ]]> occurs outside of markup.
666        CD_end_error = validate_no_CD_end(lex, markup1, tags)
667
668        # Convert to UTF-16 bit streams.
669        (u16hi, u16lo, u16delmask) = u8u16.u8u16(u8, bit)
670
671        # Consolidate and check for errors
672        error = u8.error | xmlchar_error | markup1.error | tags.error | CD_end_error | refs.error
673
674        # Consolidate the deletion_masks
675        delmask = control.CRLF | refs.delmask | u16delmask # | markup1.CDATA_delimiters
676
677        return (markup1, tags, refs, u16hi, u16lo, delmask, error, lex, u16delmask, EOF_mask)
678
679
680def demo_parabix(u8data):
681
682        lgth = len(u8data)
683       
684        (markup1, tags, refs, u16hi, u16lo, delmask, error, lex, u16delmask, EOF_mask) = parabix_parse(u8data)
685        bitutil.print_aligned_u8_byte_streams([('input data', u8data), 
686                              ('input high nybbles', bitutil.high_nybble_stream(u8data)), 
687                              ('input low nybbles', bitutil.low_nybble_stream(u8data)),
688                              ('CD_span', bitutil.bitstream2string(markup1.CD_span, lgth)),
689                              ('Ct_span', bitutil.bitstream2string(markup1.Ct_span, lgth)),
690                              ('PI_span', bitutil.bitstream2string(markup1.PI_span, lgth)),
691                              ('CtCDPI_mask', bitutil.bitstream2string(markup1.CtCDPI_mask, lgth)),
692                              ('entity refs', bitutil.bitstream2string(refs.GenRefs, lgth)),
693                              ('decimal char refs', bitutil.bitstream2string(refs.DecRefs, lgth)),
694                              ('hex char refs', bitutil.bitstream2string(refs.HexRefs, lgth)),
695                              ('element names', bitutil.bitstream2string(tags.ElemNames, lgth)),
696                              ('attribute names', bitutil.bitstream2string(tags.AttNames, lgth)),
697                              ('attribute values', bitutil.bitstream2string(tags.AttVals, lgth)),
698                              ('empty tag marks', bitutil.bitstream2string(tags.EmptyTagMarks, lgth)),
699                              ('end tags', bitutil.bitstream2string(tags.EndTags, lgth)),
700                              ('start/empty tags', bitutil.bitstream2string(tags.Tags, lgth)),
701                              ('delmask', bitutil.bitstream2string(delmask, lgth)),
702                              ('u16delmask', bitutil.bitstream2string(u16delmask, lgth)),
703                              ('errors', bitutil.bitstream2string(error, lgth+1))])
704
705def demo_u16delmask(u8data):
706
707        u8len = len(u8data)
708       
709        # Transpose to parallel bit streams and prepare an EOF mask.
710        (bit, EOF_mask) = bitutil.transpose_streams(u8data)
711
712        # Classify bytes for UTF-8 processing, whitespace and control
713        # processing and XML lexical analysis.
714        (u8, control, lex) = byteclass.classify_bytes(bit)
715
716        # Validate UTF-8 multibyte sequences and determine the UTF-8 scope streams.
717        u8 = u8u16.validate_utf8(u8)   
718       
719        # Convert to UTF-16 bit streams.
720        (u16hi, u16lo, delmask) = u8u16.u8u16(u8, bit)
721       
722        # Inverse transpose
723        U16H = bitutil.filter_bytes(bitutil.inverse_transpose(u16hi, u8len), delmask)
724        U16L = bitutil.filter_bytes(bitutil.inverse_transpose(u16lo, u8len), delmask)
725       
726        # Construct UTF-16 data buffer
727        bytes = bitutil.merge_bytes(U16L, U16H)
728       
729        U16data = bytes.decode('utf16')
730       
731        bitutil.print_aligned_u8_byte_streams([('input data', u8data), 
732                                ('u16delmask', bitutil.bitstream2string(delmask, u8len)),               
733                                    ('errors', bitutil.bitstream2string(u8.error, u8len+1))])
734        return
735
736if __name__ == "__main__":
737        import doctest
738        doctest.testmod()
739       
740        if len(sys.argv) > 1:
741                u8data = bitutil.readfile(sys.argv[1]) 
742#               demo_validate_xmlchar(u8data)
743#               demo_line_breaks(u8data)
744#               demo_multiliterals(u8data)
745#               demo_CtCDPI(u8data)
746#               demo_refs(u8data)
747#               demo_tags(u8data)
748#               demo_validate_no_CD_end(u8data)         
749#               demo_u16delmask(u8data)         
750                demo_parabix(u8data)
751#               demo_u16delmask(u8data)
752        else:
753                print("Usage: python parabix2.py <file>")       
754               
755 
756       
757       
Note: See TracBrowser for help on using the repository browser.