source: proto/parabix2/parabix2.py @ 844

Last change on this file since 844 was 844, checked in by cameron, 8 years ago

Unify line/column reporting.

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