source: proto/xmlschema/parabix2.py @ 4170

Last change on this file since 4170 was 2219, checked in by shiyangy, 7 years ago

project checkin

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