source: proto/parabix2/parabix2.py @ 292

Last change on this file since 292 was 292, checked in by ksherdy, 10 years ago

Fix doctest.

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