source: proto/parabix2/parabix2.py @ 564

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

Exclude ? from PI target.

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