source: proto/parabix2/parabix2.py @ 303

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

Updated all demo methods to explicitly encode u8data as utf-8.
Updated all demo methods to display utf-8 aligned byte streams.
Added UTF-16 deletion mask demo.
Added support to read from UTF-8 encoded source files in main.

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