source: proto/parabix2/parabix2.py @ 360

Last change on this file since 360 was 360, checked in by cameron, 9 years ago

Moved bit stream Compiler directory out of parabix2

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