source: proto/parabix2/parabix2.py @ 315

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

Revert explicit conversion to UTF-8 on file read.

File size: 26.5 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_streams(u8data)
45        (u8, control, lex) = byteclass.classify_bytes(bit)
46        bitutil.print_aligned_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        lgth = len(u8data)
69        (bit, EOF_mask) = bitutil.transpose_streams(u8data)
70        (u8, control, lex) = byteclass.classify_bytes(bit)
71        (control, bit) = normalize_line_breaks(control, bit)
72        bitutil.print_aligned_u8_byte_streams([('input high nybbles', bitutil.high_nybble_stream(u8data)), 
73                              ('input low nybbles', bitutil.low_nybble_stream(u8data)),
74                              ('CR', bitutil.bitstream2string(control.CR, lgth)),
75                              ('LF', bitutil.bitstream2string(control.LF, lgth)),
76                              ('CRLF', bitutil.bitstream2string(control.CRLF, lgth))])
77
78
79
80
81
82def add_multiliterals(lex):
83        """Extend the byte-based lexical item streams for some important
84        multibyte literals.
85       
86        >>> demo_multiliterals("  <?php?>  <!--  -->  <![CDATA[  ]]> ")
87        input data  :   <?php?>  <!--  -->  <![CDATA[  ]]>
88        PI_start    : ___1_________________________________
89        CtCD_start  : ____________1__________1_____________
90        EndTag_start: _____________________________________
91        CD_end      : ___________________________________1_
92        DoubleHyphen: ______________1___1__________________
93        PI_end      : ________1____________________________
94        """
95
96        LAngleFollow = bitutil.Advance(lex.LAngle)
97        lex.PI_start = LAngleFollow & lex.QMark
98        lex.CtCD_start = LAngleFollow & lex.Exclam
99        lex.EndTag_start = LAngleFollow & lex.Slash
100        lex.CD_end = bitutil.Advance(bitutil.Advance(lex.RBracket) & lex.RBracket) & lex.RAngle
101        lex.DoubleHyphen = bitutil.Advance(lex.Hyphen) & lex.Hyphen
102        lex.PI_end = bitutil.Advance(lex.QMark) & lex.RAngle
103        return lex
104
105def demo_multiliterals(u8data):
106        lgth = len(u8data)
107        (bit, EOF_mask) = bitutil.transpose_streams(u8data)
108        (u8, control, lex) = byteclass.classify_bytes(bit)
109        lex = add_multiliterals(lex)
110        bitutil.print_aligned_u8_byte_streams([('input data', u8data), 
111                              ('PI_start', bitutil.bitstream2string(lex.PI_start, lgth)),
112                              ('CtCD_start', bitutil.bitstream2string(lex.CtCD_start, lgth)),
113                              ('EndTag_start', bitutil.bitstream2string(lex.EndTag_start, lgth)),
114                              ('CD_end', bitutil.bitstream2string(lex.CD_end, lgth)),
115                              ('DoubleHyphen', bitutil.bitstream2string(lex.DoubleHyphen, lgth)),
116                              ('PI_end', bitutil.bitstream2string(lex.PI_end, lgth))])
117
118class CtCDPI_callouts:
119        CD_span = 0
120        Ct_span = 0
121        PI_mask = 0
122        CtCDPI_mask = 0
123        error = 0
124       
125def parse_CtCDPI(lex, EOF_mask):
126        """Parse all comments, CDATA sections and processing instructions.
127       
128        Return bitstreams marking the extent of these markup items,
129        excluding initial and final bracketting.
130       
131        >>> demo_CtCDPI(' <?php?>  <!-- example -->  <![CDATA[  shift: a<<1 ]]> ')
132        input data :  <?php?>  <!-- example -->  <![CDATA[  shift: a<<1 ]]>
133        CD_span    : ______________________________11111111111111111111111__
134        Ct_span    : _____________111111111111______________________________
135        PI_span    : __11111________________________________________________
136        CtCDPI_mask: __111111___111111111111111___1111111111111111111111111_
137        error      : ________________________________________________________
138       
139        Comments are terminated by double-hyphen; immediately require closing ">".
140       
141        >>> demo_CtCDPI(' <!--  <?php?>  --   <!-- -->')
142        input data :  <!--  <?php?>  --   <!-- -->
143        CD_span    : _____________________________
144        Ct_span    : ____11111111111111______1111_
145        PI_span    : _____________________________
146        CtCDPI_mask: __11111111111111111___1111111
147        error      : __________________1___________
148
149"""
150        callouts = CtCDPI_callouts()
151        PI_starts = 0
152        PI_ends = 0
153        Ct_starts = 0
154        Ct_ends = 0
155        CD_starts = 0
156        CD_ends = 0
157        CtCDPI_starts = 0
158        # Scanning streams
159        CtCDPI_scan = ~(lex.CtCD_start | lex.PI_start) & EOF_mask
160        Ct_end_scan = ~lex.DoubleHyphen & EOF_mask
161        CD_end_scan = ~lex.CD_end & EOF_mask
162        PI_end_scan = ~lex.PI_end & EOF_mask
163        #
164        # Initiate the scan
165        CtCDPI_Cursor = 1
166        CtCDPI_Cursor = bitutil.ScanThru(CtCDPI_Cursor, CtCDPI_scan)
167        CtCDPI_Cursor &= EOF_mask
168        while CtCDPI_Cursor > 0:
169                CtCDPI_starts |= CtCDPI_Cursor
170                PI_Cursor = CtCDPI_Cursor & lex.PI_start
171                CD_Ct_Cursor = bitutil.Advance(CtCDPI_Cursor & ~PI_Cursor)
172                CD_Cursor = CD_Ct_Cursor & lex.LBracket
173                Ct_Cursor = bitutil.Advance(CD_Ct_Cursor & lex.Hyphen) 
174                PI_starts |= PI_Cursor
175                CD_starts |= CD_Cursor
176                Ct_starts |= Ct_Cursor
177                Ct_Cursor = bitutil.Advance(Ct_Cursor)
178                PI_Cursor = bitutil.ScanThru(PI_Cursor, PI_end_scan)
179                CD_Cursor = bitutil.ScanThru(CD_Cursor, CD_end_scan)
180                Ct_Cursor = bitutil.Advance(bitutil.ScanThru(Ct_Cursor, Ct_end_scan))
181                PI_ends |= PI_Cursor
182                CD_ends |= CD_Cursor
183                Ct_ends |= Ct_Cursor
184                CtCDPI_Cursor = PI_Cursor | CD_Cursor | Ct_Cursor
185                CtCDPI_Cursor = bitutil.ScanThru(CtCDPI_Cursor, CtCDPI_scan)
186                CtCDPI_Cursor &= EOF_mask
187        # End of loop: no remaining CtCDPI_Cursor
188        callouts.CD_span = CD_ends - CD_starts
189        callouts.Ct_span = Ct_ends - Ct_starts
190        callouts.PI_span = PI_ends - PI_starts
191       
192        callouts.CtCDPI_mask |= bitutil.Advance(CD_ends | Ct_ends | PI_ends) - CtCDPI_starts
193        callouts.error = Ct_ends & ~lex.RAngle
194        # If any of the Comment, CDATA or PI markups are unterminated, it is an error.
195        callouts.error |= callouts.CtCDPI_mask &~ EOF_mask
196        return callouts
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_u8_byte_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
276def demo_refs(u8data):
277        lgth = len(u8data)
278        (bit, EOF_mask) = bitutil.transpose_streams(u8data)
279        (u8, control, lex) = byteclass.classify_bytes(bit)
280        callouts = parse_refs(lex, 0)
281        bitutil.print_aligned_u8_byte_streams([('input data', u8data), 
282                              ('entity refs', bitutil.bitstream2string(callouts.GenRefs, lgth)),
283                              ('decimal char refs', bitutil.bitstream2string(callouts.DecRefs, lgth)),
284                              ('hex char refs', bitutil.bitstream2string(callouts.HexRefs, lgth)),
285                              ('ref delmask', bitutil.bitstream2string(callouts.delmask, lgth)),
286                              ('errors', bitutil.bitstream2string(callouts.error, lgth+1))])
287
288
289class tag_callouts:
290        ElemNames = 0
291        AttNames = 0
292        AttVals = 0
293        Tags = 0
294        EmptyTagEnds = 0
295        EndTags = 0
296        error = 0
297       
298        # POTENTIAL ADDITIONAL FIELDS
299        # StartTagEnds = 0
300        # EmptyTagEnds = 0     
301        # EndTagEnds = 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        # POTENTIAL ADDITIONAL FIELDS
503        # callouts.StartTagEnds = STagEnds
504        # callouts.EmptyTagEnds = bitutil.Advance(callouts.EmptyTagMarks)
505        # callouts.EndTagEnds = EndTagEnds
506       
507        return callouts
508
509def demo_tags(u8data):
510        lgth = len(u8data)
511        (bit, EOF_mask) = bitutil.transpose_streams(u8data)
512        (u8, control, lex) = byteclass.classify_bytes(bit)
513        lex = add_multiliterals(lex)
514        markup1 = parse_CtCDPI(lex, EOF_mask)
515        callouts = parse_tags(lex, markup1.CtCDPI_mask, EOF_mask)
516        bitutil.print_aligned_u8_byte_streams([('input data', u8data), 
517                              ('element names', bitutil.bitstream2string(callouts.ElemNames, lgth)),
518                              ('attribute names', bitutil.bitstream2string(callouts.AttNames, lgth)),
519                              ('attribute values', bitutil.bitstream2string(callouts.AttVals, lgth)),
520                              ('empty tag marks', bitutil.bitstream2string(callouts.EmptyTagMarks, lgth)),
521                              ('end tags', bitutil.bitstream2string(callouts.EndTags, lgth)),
522                              ('start/empty tags', bitutil.bitstream2string(callouts.Tags, lgth)),
523                              ('errors', bitutil.bitstream2string(callouts.error, lgth+1))])
524
525
526
527def validate_no_CD_end(lex, markup1, tags):
528        """Find illegal occurrences of ]]> in text (outside of markup).
529
530        >>> demo_validate_no_CD_end(' <!-- OK: ]]>  --> <![CDATA OK  ]]>  ]]> <tag att=" ]]> "/> ]]>  <?php ]]> ?> ')
531        input data :  <!-- OK: ]]>  --> <![CDATA OK  ]]>  ]]> <tag att=" ]]> "/> ]]>  <?php ]]> ?>
532        CtCDPI_mask: __1111111111111111__111111111111111_______________________________11111111111_
533        tags       : __________________________________________1111111111111111____________________
534        illegal ]]>: _______________________________________1______________________1_______________
535"""
536        return lex.CD_end & ~(markup1.CtCDPI_mask | tags.Tags)
537
538def demo_validate_no_CD_end(u8data):
539        lgth = len(u8data)
540        (bit, EOF_mask) = bitutil.transpose_streams(u8data)
541        (u8, control, lex) = byteclass.classify_bytes(bit)
542        lex = add_multiliterals(lex)
543        markup1 = parse_CtCDPI(lex, EOF_mask)
544        tags = parse_tags(lex, markup1.CtCDPI_mask, EOF_mask)
545        error = validate_no_CD_end(lex, markup1, tags)
546        bitutil.print_aligned_u8_byte_streams([('input data', u8data), 
547                              ('CtCDPI_mask', bitutil.bitstream2string(markup1.CtCDPI_mask, lgth)),
548                              ('tags', bitutil.bitstream2string(tags.Tags, lgth)),
549                              ('illegal ]]>', bitutil.bitstream2string(error, lgth))])
550
551
552
553def parabix_parse(u8data):
554        # Transpose to parallel bit streams and prepare an EOF mask.
555        (bit, EOF_mask) = bitutil.transpose_streams(u8data)
556
557        # Classify bytes for UTF-8 processing, whitespace and control
558        # processing and XML lexical analysis.
559        (u8, control, lex) = byteclass.classify_bytes(bit)
560
561        # Validate UTF-8 multibyte sequences and determine the UTF-8 scope streams.
562        u8 = u8u16.validate_utf8(u8)
563
564        # Rule out the illegal characters for XML.
565        xmlchar_error = validate_xmlchar(u8, control, lex, EOF_mask)
566
567        # Find and normalize bare CR or CRLF combinations.
568        (control, bit) = normalize_line_breaks(control, bit)
569
570        # Compute XML multilterals such as <?, </, --, ]]>.
571        lex = add_multiliterals(lex)
572
573        # Parse all comments, CDATA sections and processing instructions.
574        markup1 = parse_CtCDPI(lex, EOF_mask)
575
576        # All remaining "<" must be tag start characters; parse tags.
577        tags = parse_tags(lex, markup1.CtCDPI_mask, EOF_mask)
578
579        # All remaining "&" must be reference start characters; parse them.
580        refs = parse_refs(lex, markup1.CtCDPI_mask)
581
582        # Ensure that no occurrence of ]]> occurs outside of markup.
583        CD_end_error = validate_no_CD_end(lex, markup1, tags)
584
585        # Convert to UTF-16 bit streams.
586        (u16hi, u16lo, u16delmask) = u8u16.u8u16(u8, bit)
587
588        # Consolidate and check for errors
589        error = u8.error | xmlchar_error | markup1.error | tags.error | CD_end_error | refs.error
590
591        # Consolidate the deletion_masks
592        delmask = control.CRLF | refs.delmask | u16delmask # | markup1.CDATA_delimiters
593
594        return (markup1, tags, refs, u16hi, u16lo, delmask, error, lex, u16delmask, EOF_mask)
595
596
597def demo_parabix(u8data):
598
599        lgth = len(u8data)
600       
601        (markup1, tags, refs, u16hi, u16lo, delmask, error, lex, u16delmask, EOF_mask) = parabix_parse(u8data)
602        bitutil.print_aligned_u8_byte_streams([('input data', u8data), 
603                              ('input high nybbles', bitutil.high_nybble_stream(u8data)), 
604                              ('input low nybbles', bitutil.low_nybble_stream(u8data)),
605                              ('CD_span', bitutil.bitstream2string(markup1.CD_span, lgth)),
606                              ('Ct_span', bitutil.bitstream2string(markup1.Ct_span, lgth)),
607                              ('PI_span', bitutil.bitstream2string(markup1.PI_span, lgth)),
608                              ('CtCDPI_mask', bitutil.bitstream2string(markup1.CtCDPI_mask, lgth)),
609                              ('entity refs', bitutil.bitstream2string(refs.GenRefs, lgth)),
610                              ('decimal char refs', bitutil.bitstream2string(refs.DecRefs, lgth)),
611                              ('hex char refs', bitutil.bitstream2string(refs.HexRefs, lgth)),
612                              ('element names', bitutil.bitstream2string(tags.ElemNames, lgth)),
613                              ('attribute names', bitutil.bitstream2string(tags.AttNames, lgth)),
614                              ('attribute values', bitutil.bitstream2string(tags.AttVals, lgth)),
615                              ('empty tag marks', bitutil.bitstream2string(tags.EmptyTagMarks, lgth)),
616                              ('end tags', bitutil.bitstream2string(tags.EndTags, lgth)),
617                              ('start/empty tags', bitutil.bitstream2string(tags.Tags, lgth)),
618                              ('delmask', bitutil.bitstream2string(delmask, lgth)),
619                              ('u16delmask', bitutil.bitstream2string(u16delmask, lgth)),
620                              ('errors', bitutil.bitstream2string(error, lgth+1))])
621
622def demo_u16delmask(u8data):
623
624        u8len = len(u8data)
625       
626        # Transpose to parallel bit streams and prepare an EOF mask.
627        (bit, EOF_mask) = bitutil.transpose_streams(u8data)
628
629        # Classify bytes for UTF-8 processing, whitespace and control
630        # processing and XML lexical analysis.
631        (u8, control, lex) = byteclass.classify_bytes(bit)
632
633        # Validate UTF-8 multibyte sequences and determine the UTF-8 scope streams.
634        u8 = u8u16.validate_utf8(u8)   
635       
636        # Convert to UTF-16 bit streams.
637        (u16hi, u16lo, delmask) = u8u16.u8u16(u8, bit)
638       
639        # Inverse transpose
640        U16H = bitutil.filter_bytes(bitutil.inverse_transpose(u16hi, u8len), delmask)
641        U16L = bitutil.filter_bytes(bitutil.inverse_transpose(u16lo, u8len), delmask)
642       
643        # Construct UTF-16 data buffer
644        bytes = bitutil.merge_bytes(U16L, U16H)
645       
646        U16data = bytes.decode('utf16')
647       
648        bitutil.print_aligned_u8_byte_streams([('input data', u8data), 
649                                ('u16delmask', bitutil.bitstream2string(delmask, u8len)),               
650                                    ('errors', bitutil.bitstream2string(u8.error, u8len+1))])
651        return
652
653if __name__ == "__main__":
654        import doctest
655        doctest.testmod()
656       
657        if len(sys.argv) > 1:
658                u8data = bitutil.readfile(sys.argv[1]) 
659#               demo_validate_xmlchar(u8data)
660#               demo_line_breaks(u8data)
661#               demo_multiliterals(u8data)
662#               demo_CtCDPI(u8data)
663#               demo_refs(u8data)
664#               demo_tags(u8data)
665#               demo_validate_no_CD_end(u8data)         
666#               demo_u16delmask(u8data)         
667                demo_parabix(u8data)
668#               demo_u16delmask(u8data)
669        else:
670                print("Usage: python parabix2.py <file>")       
671               
672 
673       
674       
Note: See TracBrowser for help on using the repository browser.