source: proto/parabix2/parabix2.py @ 540

Last change on this file since 540 was 528, checked in by lindanl, 9 years ago

PI target name extraction; error check

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