source: proto/parabix2/parabix2_compilable.py @ 412

Last change on this file since 412 was 412, checked in by eamiri, 9 years ago

error--> error_mask, unnecessary augmentation removed

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