source: proto/parabix2/parabix2.py @ 577

Last change on this file since 577 was 571, checked in by lindanl, 9 years ago

Misc determination

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