source: proto/parabix2/parabix2.py @ 311

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

Use Python byte strings for demo_validate_xmlchar.

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