source: proto/JSON/json_prototype.py @ 3380

Last change on this file since 3380 was 3241, checked in by ksherdy, 6 years ago

Del readme.

File size: 24.1 KB
RevLine 
[672]1# -*- coding: utf-8 -*-
2#
3# json_prototype.py
4#
5# Ken Herdy
6# Oct. 13, 2010
7#
8#----------------------------------------------------------------------------
9#
[873]10# We use Python's unlimited precision integers for unbounded bit streams.
[672]11# This permits simple logical operations on the entire stream.
12# Assumption: bitstreams are little-endian (e.g., as on x86).
13#
[873]14# This Python module implements JSON functionality as a working Python
15# prototype.
[672]16#----------------------------------------------------------------------------
[717]17#
18# TODO
[672]19#
[718]20# - Conditionally validate JSON text as valid UTF 8, (if bit0).     
21# - Conditionally validate JSON Unicode \u4HEXDIG and \u4HEXDIG\u4HEXDIG character escape sequences, (if \u escape sequence).
[717]22#
[672]23
24import bitutil
25import byteclass
[682]26import u8u16
[675]27import math
[672]28import sys
29
[768]30debug = False#True
[672]31filename = "json_prototype.py"
32
33# Globals
[682]34#
[713]35# Bitstream function definitions input/output *only* bitstream type variables.
36#
37# Global declarations allow debug blocks in bitstream processing definitions.
38#
39# Do not shadow the global variables 'u8data' or 'lgth' with local variable declartions.
40
[675]41u8data = ""
[672]42lgth = 0
43
[755]44def simd_const_4(hexdigit, EOF_mask):
[873]45        """
[684]46        IDISA library function.
47        """
48        lgth = bitutil.count_leading_zeroes(~EOF_mask)/4
49        return int(hexdigit*(lgth+1),16)&EOF_mask
50       
[686]51def parse_escape(Lex, EOF_mask):
[873]52        """
[744]53        We translate stream abtraction logic to native block-by-block implementation logic.
54       
[686]55        Marks escaped characters.
[873]56       
57        Does not mark escaped characters.
58        Escape characters are either escaped and unmarked or the following character in an odd length run is marked.
[686]59        """
[3241]60        odd = simd_const_4('a',EOF_mask) 
61        even = simd_const_4('5',EOF_mask) 
[684]62       
[686]63        start = Lex.RSolidus &~ bitutil.Advance(Lex.RSolidus)
[684]64       
65        even_start = start & even
[686]66        even_final = (even_start + Lex.RSolidus) & ~Lex.RSolidus
[684]67        even_escape = even_final & odd
68       
69        odd_start = start & odd
[686]70        odd_final = (odd_start + Lex.RSolidus) & ~Lex.RSolidus
[684]71        odd_escape = (odd_final & even)
[686]72
[755]73        Escape = (even_escape | odd_escape) 
[684]74       
[755]75        return Escape
[684]76       
77def demo_parse_escape(u8data):
78        global lgth
79        lgth = len(u8data)
[713]80       
[684]81        (bit, EOF_mask) = bitutil.transpose_streams(u8data)
[686]82        (u8, Lex, Ctrl) = byteclass.classify_bytes(bit)
83        (escape) = parse_escape(Lex,EOF_mask)
[684]84
85        bitutil.print_aligned_streams([('Input Data', u8data), 
[686]86                              ('Lex.RSolidus', bitutil.bitstream2string(Lex.RSolidus, lgth)),   
[684]87                              ('escape', bitutil.bitstream2string(escape, lgth)),       
88                              ('EOF_Mask', bitutil.bitstream2string(EOF_mask, lgth+1))])
89        return
90
[713]91def parallel_prefix_parity(strm):
[873]92        """
[744]93        We translate stream abtraction logic to native block-by-block implementation logic.
[684]94       
[739]95        x y | x XOR y
[684]96        ------------
[739]97        0 0 | 0
98        0 1 | 1
99        1 0 | 1
100        0 0 | 0
[684]101
102        Let n = length(bitstream).
103
[739]104        Define Pk,i as the parity of the bit range [k,(k+(2^i))-1] for each bit position k, 1 <= k <= n.
[684]105       
[739]106        Base Case: Pk,0 = bitstream at position k, for all k, n >= k >= 1.
[684]107        Inductive Step: Pk,i+1 = (Pk,i << 2^i) XOR Pk,i.
108       
[739]109        Pk,ceil(log(n) denotes the parity of the bit position k, n >= k >= 1.
[684]110       
[739]111        bitstream[k] = 1 => odd parity
112        bitstream[k] = 0 => even parity
[684]113        """
[713]114        global lgth
115
[684]116        t1 = strm
117        for i in range(0,int(math.ceil(math.log(lgth,2)))):
118                t2 = t1 ^ (t1 << pow(2,i))
119                t1 = t2
120        return t2 
121
122def demo_parallel_prefix_parity(u8data):
123        r"""
[686]124        >>> demo_parallel_prefix_parity('"data" "data""data" "data" "data')
125        Input Data: "data" "data""data" "data" "data
126        Lex.DQuote: 1____1_1____11____1_1____1_1____
127        Parity    : 11111__11111_11111__11111__11111
128        EOF_Mask  : 11111111111111111111111111111111_
[684]129        <BLANKLINE>
[713]130        """     
[684]131        global lgth
132        lgth = len(u8data)
[713]133
[684]134        (bit, EOF_mask) = bitutil.transpose_streams(u8data)
[686]135        (u8, Lex, Ctrl) = byteclass.classify_bytes(bit)
[713]136        ParityMask = parallel_prefix_parity(Lex.DQuote)
[690]137       
[684]138        bitutil.print_aligned_streams([('Input Data', u8data), 
[686]139                              ('Lex.DQuote', bitutil.bitstream2string(Lex.DQuote, lgth)),
[698]140                              ('Parity', bitutil.bitstream2string(ParityMask, lgth)),   
[684]141                              ('EOF_Mask', bitutil.bitstream2string(EOF_mask, lgth+1))])
142        return
143
[755]144def atom_starts(Lex, StringSpans):
[873]145        """
[698]146        This function returns multi-cursor start positions for each JSON value type.
[713]147       
148        Define JSON atoms as 'String', 'Number', 'true', 'false', and 'null' types.
[714]149       
150        Error cases are postponed for the post-processing stage as expect() cases in
151        a recursive descent parser.
[698]152        """
[713]153        global lgth
154               
[768]155        StringStarts = StringSpans &~ bitutil.Advance(StringSpans)     
[684]156
[768]157        UnmaskedAtomSpans = (Lex.True | Lex.False | Lex.Null | Lex.Number) &~ StringSpans
158        UnmaskedAtomStarts = UnmaskedAtomSpans &~ bitutil.Advance(UnmaskedAtomSpans)
159
160        NumberStarts = UnmaskedAtomStarts & (Lex.Minus|Lex.Digit0_9)
161        TrueStarts = UnmaskedAtomStarts & (Lex.t)
162        FalseStarts = UnmaskedAtomStarts & (Lex.f)
163        NullStarts = UnmaskedAtomStarts & (Lex.n)
164
[698]165        if debug:
166                bitutil.print_aligned_streams([('Input Data', u8data), 
[714]167                              ('AtomSpans', bitutil.bitstream2string(AtomSpans, lgth)),
168                              ('AtomStarts', bitutil.bitstream2string(AtomStarts, lgth)),
169                              ('StringSpans', bitutil.bitstream2string(StringSpans, lgth)),
170                              ('StringStarts', bitutil.bitstream2string(StringStarts, lgth)),
171                              ('Lex.True', bitutil.bitstream2string(Lex.True, lgth)),
172                              ('TrueStarts', bitutil.bitstream2string(TrueStarts, lgth)),
173                              ('Lex.False', bitutil.bitstream2string(Lex.False, lgth)),
174                              ('FalseStarts', bitutil.bitstream2string(FalseStarts, lgth)),
175                              ('Lex.Null', bitutil.bitstream2string(Lex.Null, lgth)),
176                              ('NullStarts', bitutil.bitstream2string(NullStarts, lgth)),
177                              ('Lex.Number', bitutil.bitstream2string(Lex.Number, lgth)),
178                              ('NumberStarts', bitutil.bitstream2string(NumberStarts, lgth))])
[698]179
[714]180        return (StringStarts, NumberStarts, TrueStarts, FalseStarts, NullStarts)
[698]181
[714]182
[713]183def demo_atom_starts(u8data):
184        global lgth     
[698]185        lgth = len(u8data)
186               
187        (bit, EOF_mask) = bitutil.transpose_streams(u8data)
188        (u8, Lex, Ctrl) = byteclass.classify_bytes(bit) 
189       
[755]190        Escape = parse_escape(Lex, EOF_mask)
[698]191       
[755]192        UnescapedDQuotes = (Lex.DQuote &~ Escape)
[698]193       
[739]194        ParityMask = parallel_prefix_parity(UnescapedDQuotes) # TODO - Solve EOF_mask problem, if final position is 1 then the final DQUOTE is not matched.
[698]195        StringMask = ParityMask & bitutil.Advance(ParityMask)   
[714]196        StringSpans = StringMask | UnescapedDQuotes
[698]197       
[714]198        (StringStarts, NumberStarts,TrueStarts, FalseStarts, NullStarts) = atom_starts(Lex, StringSpans)
[698]199        bitutil.print_aligned_streams([('Input Data', u8data), 
200                              ('StringStarts', bitutil.bitstream2string(StringStarts, lgth)),
201                              ('NumberStarts', bitutil.bitstream2string(NumberStarts, lgth)),
202                              ('TrueStarts', bitutil.bitstream2string(TrueStarts, lgth)),
203                              ('FalseStarts', bitutil.bitstream2string(FalseStarts, lgth)),
204                              ('NullStarts', bitutil.bitstream2string(NullStarts, lgth)),
205                              ('EOF_Mask', bitutil.bitstream2string(EOF_mask, lgth+1))])       
206        return
207
208
[766]209def validate_true(TrueStarts, Lex):
[873]210        """
[690]211        RFC 4627 - JavaScript Object Notation (JSON) 
[704]212       
213        true  = %x74.72.75.65      ; true
214       
[766]215        """     
[768]216        TrueScope1 = bitutil.Advance(TrueStarts)
217        TrueScope2 = bitutil.Advance(TrueScope1)
218        TrueScope3 = bitutil.Advance(TrueScope2)
[704]219       
[768]220        TrueErrors = TrueScope1 &~ Lex.r
221        TrueErrors |= TrueScope2 &~ Lex.u
222        TrueErrors |= TrueScope3 &~ Lex.e
[704]223       
[768]224        TrueFollows = bitutil.Advance(TrueScope3)
225        TrueSpans = TrueFollows - TrueStarts
[690]226
[766]227        return (TrueErrors, TrueSpans)
[704]228
[766]229def validate_false(FalseStarts, Lex):
[873]230        """
[704]231        RFC 4627 - JavaScript Object Notation (JSON) 
232       
[690]233        false = %x66.61.6c.73.65   ; false
[704]234       
[766]235        """     
[768]236        FalseScope1 = bitutil.Advance(FalseStarts)
237        FalseScope2 = bitutil.Advance(FalseScope1)
238        FalseScope3 = bitutil.Advance(FalseScope2)
239        FalseScope4 = bitutil.Advance(FalseScope3)
[704]240       
[768]241        FalseErrors = FalseScope1 &~ Lex.a
242        FalseErrors |= FalseScope2 &~ Lex.l
243        FalseErrors |= FalseScope3 &~ Lex.s
244        FalseErrors |= FalseScope4 &~ Lex.e
[704]245
[768]246        FalseFollows = bitutil.Advance(FalseScope4)
247        FalseSpans = FalseFollows - FalseStarts
[704]248       
[766]249        return (FalseErrors, FalseSpans)
[704]250
[766]251def validate_null(NullStarts, Lex):
[873]252        """
[704]253        RFC 4627 - JavaScript Object Notation (JSON) 
254       
[690]255        null  = %x6e.75.6c.6c      ; null
256       
257        """
[768]258        NullScope1 = bitutil.Advance(NullStarts)
259        NullScope2 = bitutil.Advance(NullScope1)
260        NullScope3 = bitutil.Advance(NullScope2)
[690]261       
[768]262        NullErrors = NullScope1 &~ Lex.u
263        NullErrors |= NullScope2 &~ Lex.l
264        NullErrors |= NullScope3 &~ Lex.l
[690]265       
[768]266        NullFollows = bitutil.Advance(NullScope3)
267        NullSpans = NullFollows - NullStarts
[690]268
[766]269        return (NullErrors, NullSpans)
[704]270
[766]271def validate_number(NumberStarts, Lex, EOF_mask):
[873]272        """     
[686]273        RFC 4627 - JavaScript Object Notation (JSON) 
274        RFC 5234 - Augmented BNF for Syntax Specifications: ABNF
275
276        number = [ minus ] int [ frac ] [ exp ]
277        decimal-point = %x2E       ; .
278        digit1-9 = %x31-39         ; 1-9
279        e = %x65 / %x45            ; e E
280        exp = e [ minus / plus ] 1*DIGIT
281        frac = decimal-point 1*DIGIT
282        int = zero / ( digit1-9 *DIGIT )
283        minus = %x2D               ; -
284        plus = %x2B                ; +
285        zero = %x30                ; 0 
[715]286
287        NOTE - Does not validate number follow characters. Hence leading zero errors are not reported.
288
[686]289        """     
[713]290        global lgth
291       
[768]292        M0 = NumberStarts                                       # Initialize marker stream     
[690]293       
[686]294        M1 = bitutil.ScanThru(M0, Lex.Minus & M0)               # ? Optional character class [-]
295        E1 = M1 &~(Lex.Zero|Lex.Digit1_9)
[714]296        M1 = M1 &~ E1                                           # Remove cursors at error positions so as to report a single error position
[672]297
[686]298        M1a = M1 & Lex.Zero                                     # Split
[714]299        M1b = M1 & Lex.Digit1_9                         
[672]300        M2a = bitutil.Advance(M1a)
301        M2b = bitutil.Advance(M1b)
[686]302        M3b = bitutil.ScanThru(M2b,Lex.Digit0_9)
[714]303        M4 =  M2a | M3b                                         # Join
[672]304       
[686]305        M4a = M4 &~(Lex.DecimalPoint)                           # Split
306        M4b = M4 & (Lex.DecimalPoint)
[672]307        M5b = bitutil.Advance(M4b)
[686]308        E5b = M5b &~(Lex.Digit0_9)                              # + [0-9]+
[714]309        M5b = M5b &~E5b                                         # Remove cursors at error positions so as to report a single error position
310       
[686]311        M6 = bitutil.ScanThru(M5b,Lex.Digit0_9)
[672]312        M7 = M4a | M6                                           # Join
313       
[686]314        M7a = M7 &~(Lex.Ee)                                     # Split
[768]315        M7b = M7 & (Lex.Ee)
[672]316        M8b = bitutil.Advance(M7b)
[686]317        M9b = bitutil.ScanThru(M8b, Lex.PlusMinus & M8b)        # ? Optional character class [+-]               
318        E9b  = M9b &~(Lex.Digit0_9)                             # + [0-9]+
[714]319        M9b = M9b &~ E9b                                        # Remove cursors at error positions so as to report a single error position
[686]320        M10b = bitutil.ScanThru(M9b,Lex.Digit0_9)
[700]321
[768]322        M11 = E1 | E5b | E9b | M7a | M10b                       # Record final marker positions,
323                                                                # At Advance(M11) this positions is either (1) a known error position or (2) an undetermined position to be checked
[672]324       
[766]325        NumberErrors = E1 | E5b | E9b
[672]326       
[715]327        NumberSpans = M11 - M0
[714]328       
[672]329        if debug:
[675]330                bitutil.print_aligned_streams([('Input Data', u8data), 
[672]331                              ('M0', bitutil.bitstream2string(M0, lgth)),
332                              ('M1', bitutil.bitstream2string(M1, lgth)),
[690]333                              ('E1', bitutil.bitstream2string(E1, lgth+1)),
[672]334                              ('M1a', bitutil.bitstream2string(M1a, lgth)),
335                              ('M2a', bitutil.bitstream2string(M2a, lgth)),
336                              ('M2b', bitutil.bitstream2string(M2b, lgth)),
337                              ('M3b', bitutil.bitstream2string(M3b, lgth)),
338                              ('M4', bitutil.bitstream2string(M4, lgth)),
339                              ('M4a', bitutil.bitstream2string(M4a, lgth)),
340                              ('M4b', bitutil.bitstream2string(M4b, lgth)),
341                              ('M5b', bitutil.bitstream2string(M5b, lgth)),
[690]342                              ('E5b', bitutil.bitstream2string(E5b, lgth+1)),         
[672]343                              ('M6', bitutil.bitstream2string(M6, lgth)),
344                              ('M7', bitutil.bitstream2string(M7, lgth)),
345                              ('M7a', bitutil.bitstream2string(M7a, lgth)),
[700]346                              #('E7a', bitutil.bitstream2string(E7a, lgth+1)),       
[672]347                              ('M7b', bitutil.bitstream2string(M7b, lgth)),
348                              ('M8b', bitutil.bitstream2string(M8b, lgth)),
349                              ('M9b', bitutil.bitstream2string(M9b, lgth)),           
[690]350                              ('E9b', bitutil.bitstream2string(E9b, lgth+1)),         
351                              ('M10b', bitutil.bitstream2string(M10b, lgth)),
352                              #('E10b', bitutil.bitstream2string(E10b, lgth)),
[714]353                              ('M11', bitutil.bitstream2string(M11, lgth+1)),
[766]354                              ('NumberStarts', bitutil.bitstream2string(NumberStarts, lgth)),
[700]355                              ('NumberSpans', bitutil.bitstream2string(NumberSpans, lgth)),
[766]356                              ('NumberErrors', bitutil.bitstream2string(NumberErrors, lgth+1))])               
[700]357       
[766]358        return (NumberErrors, NumberSpans)
[675]359
360def demo_validate_number(u8data):
[672]361        r"""
[690]362        >>> demo_validate_number('[-,--,-a,00,-00,-0.,-0.e,-0.E,00,-123.456-,0.456+,0e10+,0123456789]')
363        Input Data  : [-,--,-a,00,-00,-0.,-0.e,-0.E,00,-123.456-,0.456+,0e10+,0123456789]
[675]364        Minus       : _1_11_1_____1___1___1____1_______1_______1_________________________
365        Zero        : _________11__11__1___1____1___11___________1______1__1__1__________
366        Digit1_9    : __________________________________111_111____111____1____111111111_
367        Digit0_9    : _________11__11__1___1____1___11__111_111__1_111__1_11__1111111111_
368        DecimalPoint: __________________1___1____1_________1______1______________________
369        Ee          : _______________________1____1______________________1_______________
370        PlusMinus   : _1_11_1_____1___1___1____1_______1_______1______1_____1____________
[768]371        NumberSpans : _1_1__1__1__11__111_111__111__1__11111111__11111__1111__1__________
[766]372        NumberErrors: __1_1__1___________1___1____1_______________________________________
[675]373        EOF_Mask    : 1111111111111111111111111111111111111111111111111111111111111111111_
[672]374        <BLANKLINE>
[690]375
376        >>> demo_validate_number('[0.5,98.6,99.44,1066,1e1,0.1e1,1e-1,1e00,2e+00,2e-00,1234567890,-9876.543210,0.123456789e-12,1.234567890E+34,23456789012E66]')
377        Input Data  : [0.5,98.6,99.44,1066,1e1,0.1e1,1e-1,1e00,2e+00,2e-00,1234567890,-9876.543210,0.123456789e-12,1.234567890E+34,23456789012E66]
[675]378        Minus       : _________________________________1_______________1______________1________________________1__________________________________
379        Zero        : _1_______________1_______1____________11____11____11__________1____________1_1_________________________1_____________1______
380        Digit1_9    : ___1_11_1_11_11_1_11_1_1___1_1_1__1_1____1_____1_____111111111___1111_11111____111111111__11_1_11111111___11_11111111_11_11_
[690]381        Digit0_9    : _1_1_11_1_11_11_1111_1_1_1_1_1_1__1_1_11_1__11_1__11_1111111111__1111_111111_1_111111111__11_1_111111111__11_11111111111_11_
[675]382        DecimalPoint: __1____1____1_____________1__________________________________________1________1_______________1_____________________________
383        Ee          : ______________________1_____1___1____1____1_____1_______________________________________1_______________1_______________1___
384        PlusMinus   : _________________________________1_________1_____1______________1________________________1_______________1__________________
[700]385        NumberSpans : _111_1111_11111_1111_111_11111_1111_1111_11111_11111_1111111111_111111111111_111111111111111_111111111111111_11111111111111_
[766]386        NumberErrors: _____________________________________________________________________________________________________________________________
[675]387        EOF_Mask    : 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111_
[672]388        <BLANKLINE>
[690]389
390        >>> demo_validate_number('[012345,12345,0,00,-0,-00]')
391        Input Data  : [012345,12345,0,00,-0,-00]
392        Minus       : ___________________1__1___
393        Zero        : _1____________1_11__1__11_
394        Digit1_9    : __11111_11111_____________
395        Digit0_9    : _111111_11111_1_11__1__11_
396        DecimalPoint: __________________________
397        Ee          : __________________________
398        PlusMinus   : ___________________1__1___
[714]399        NumberSpans : _1______11111_1_1__11_11__
[766]400        NumberErrors: ___________________________
[690]401        EOF_Mask    : 11111111111111111111111111_
[672]402        <BLANKLINE>
403        """ 
404        global lgth
[675]405        lgth = len(u8data)
[713]406       
[675]407        (bit, EOF_mask) = bitutil.transpose_streams(u8data)
[686]408        (u8, Lex, Ctrl) = byteclass.classify_bytes(bit)
[700]409       
[755]410        Escape = parse_escape(Lex, EOF_mask)
411        UnescapedDQuotes = (Lex.DQuote &~ Escape)
[700]412       
[739]413        ParityMask = parallel_prefix_parity(UnescapedDQuotes)
[700]414        StringMask = ParityMask & bitutil.Advance(ParityMask)   
[714]415        StringSpans = StringMask | UnescapedDQuotes
[700]416       
[714]417        (StringStarts, NumberStarts, TrueStarts, FalseStarts, NullStarts) = atom_starts(Lex, StringSpans)       
[700]418       
[766]419        (NumberErrors, NumberSpans) = validate_number(NumberStarts, Lex, EOF_mask)
[672]420
[675]421        bitutil.print_aligned_streams([('Input Data', u8data), 
[686]422                              ('Minus', bitutil.bitstream2string(Lex.Minus, lgth)),
423                              ('Zero', bitutil.bitstream2string(Lex.Zero, lgth)),
424                              ('Digit1_9', bitutil.bitstream2string(Lex.Digit1_9, lgth)),
425                              ('Digit0_9', bitutil.bitstream2string(Lex.Digit0_9, lgth)),
426                              ('DecimalPoint', bitutil.bitstream2string(Lex.DecimalPoint, lgth)),
427                              ('Ee', bitutil.bitstream2string(Lex.Ee, lgth)),
428                              ('PlusMinus', bitutil.bitstream2string(Lex.PlusMinus, lgth)),
[700]429                              ('NumberSpans', bitutil.bitstream2string(NumberSpans, lgth)),
[766]430                              ('NumberErrors', bitutil.bitstream2string(NumberErrors, lgth+1)),
[700]431                              ('EOF_Mask', bitutil.bitstream2string(EOF_mask, lgth+1))])
[675]432
[755]433def validate_string(StringStarts, Lex, Ctrl, StringMask, Escape, UnescapedDQuotes):
[873]434        """
[684]435        RFC 4627 - JavaScript Object Notation (JSON) 
[675]436
[684]437        string = quotation-mark *char quotation-mark
438        char = unescaped /
439              escape (
440                  %x22 /          ; "    quotation mark  U+0022
441                  %x5C /          ; \    reverse solidus U+005C
442                  %x2F /          ; /    solidus         U+002F
443                  %x62 /          ; b    backspace       U+0008
444                  %x66 /          ; f    form feed       U+000C
445                  %x6E /          ; n    line feed       U+000A
446                  %x72 /          ; r    carriage return U+000D
447                  %x74 /          ; t    tab             U+0009
448                  %x75 4HEXDIG )  ; uXXXX                U+XXXX
[675]449
[684]450        escape = %x5C              ; \
451        quotation-mark = %x22      ; "
[686]452        unescaped = %x20-21 / %x23-5B / %x5D-10FFFF
[675]453
[686]454        JSON string validation requires both:
455        (1) validation of escape characters,
456        (2) validation of unescaped characters.
[675]457        """
[713]458        global lgth
[686]459       
460        # (1) Validate escape characters
[755]461        StringEscapeChars = Escape & StringMask
462        StringErrors = (StringEscapeChars &~ Lex.Escape)
[686]463       
464        u = StringEscapeChars & Lex.u
465       
466        uScope1 = bitutil.Advance(u)
467        uScope2 = bitutil.Advance(uScope1)
468        uScope3 = bitutil.Advance(uScope2)
469        uScope4 = bitutil.Advance(uScope3)
470       
[755]471        StringErrors |= uScope1 &~ Lex.HexDigit
472        StringErrors |= uScope2 &~ Lex.HexDigit
473        StringErrors |= uScope3 &~ Lex.HexDigit
474        StringErrors |= uScope4 &~ Lex.HexDigit
[686]475       
476        # (2) Validation of unescaped characters
477        # (2.1) StringMask construction ensures all '"' are escaped.
478        # (2.2) '\' are either correctly escaped or the character following an odd length run is escaped.
[675]479
[755]480        StringNotEscapedChars = (~(Escape | Lex.RSolidus)) & StringMask # TODO - Verify logic.
481        StringErrors |= (StringNotEscapedChars & Ctrl.x00_x1F)
[690]482       
483        # (3) Validate all strings are terminated with an unescaped "
[698]484        StringCursor = bitutil.Advance(StringStarts)
485        StringEnds = bitutil.ScanThru(StringCursor, StringMask)
[755]486        StringErrors |= StringEnds &~ UnescapedDQuotes
[700]487        StringSpans = (StringEnds - StringStarts) | StringEnds
[690]488       
[686]489        if debug:
490                bitutil.print_aligned_streams([('Input Data', u8data), 
[755]491                              ('Escape', bitutil.bitstream2string(Escape, lgth)),
[686]492                              ('StringEscapeChars', bitutil.bitstream2string(StringEscapeChars, lgth)),
493                              ('u', bitutil.bitstream2string(u, lgth)),
494                              ('uScope1', bitutil.bitstream2string(uScope1, lgth)),
495                              ('uScope2', bitutil.bitstream2string(uScope2, lgth)),
496                              ('uScope3', bitutil.bitstream2string(uScope3, lgth)),
497                              ('uScope4', bitutil.bitstream2string(uScope4, lgth)),
498                              ('StringNotEscapedChars', bitutil.bitstream2string(StringNotEscapedChars, lgth)),
[698]499                              ('StringStarts', bitutil.bitstream2string(StringStarts, lgth)),
500                              ('StringEnds', bitutil.bitstream2string(StringEnds, lgth)),
501                              ('StringSpans', bitutil.bitstream2string(StringSpans, lgth)),
[755]502                              ('StringErrors', bitutil.bitstream2string(StringErrors, lgth+1))])         
503        return (StringErrors, StringSpans)
[684]504
505def demo_validate_string(u8data):
[687]506        r"""
[704]507        >>> demo_validate_string('["\u abcd", "\u1___", "\u12__", "\u123_"]')
508        Input Data      : ["\u abcd", "\u1___", "\u12__", "\u123_"]
[755]509        Escape          : ___1__________1_________1_________1______
[698]510        UnescapedDQuotes: _1_______1__1______1__1______1__1______1_
511        ParityMask      : _11111111___1111111___1111111___1111111__
512        StringMask      : __1111111____111111____111111____111111__
513        StringStarts    : _1__________1_________1_________1________
[700]514        StringSpans     : _111111111__11111111__11111111__11111111_
[755]515        StringErrors    : ____1___________111________11_________1___
[698]516        EOF_Mask        : 11111111111111111111111111111111111111111_
[687]517        <BLANKLINE>
518        """ 
[675]519        global lgth
520        lgth = len(u8data)
[713]521       
[675]522        (bit, EOF_mask) = bitutil.transpose_streams(u8data)
[686]523        (u8, Lex, Ctrl) = byteclass.classify_bytes(bit)
[713]524
[700]525        # Construct string interiors mask (1),(2),(3)
[686]526        # (1) Mark all escaped characters
[755]527        Escape = parse_escape(Lex, EOF_mask)
[713]528
[686]529        # (2) Mark all unescaped "
[755]530        UnescapedDQuotes = (Lex.DQuote &~ Escape)
[713]531
[700]532        # (3) Construct string interiors mask
[739]533        ParityMask = parallel_prefix_parity(UnescapedDQuotes)
[686]534        StringMask = ParityMask & bitutil.Advance(ParityMask)
[714]535        StringSpans = StringMask | UnescapedDQuotes
[713]536
[714]537        (StringStarts, NumberStarts, TrueStarts, FalseStarts, NullStarts) = atom_starts(Lex, StringSpans)                               
[684]538       
[755]539        (StringErrors, StringSpans) = validate_string(StringStarts, Lex,Ctrl,StringMask,Escape,UnescapedDQuotes)
[713]540       
[675]541        bitutil.print_aligned_streams([('Input Data', u8data), 
[755]542                              ('Escape', bitutil.bitstream2string(Escape, lgth)),
[686]543                              ('UnescapedDQuotes', bitutil.bitstream2string(UnescapedDQuotes, lgth)),
[687]544                              ('ParityMask', bitutil.bitstream2string(ParityMask, lgth)),
545                              ('StringMask', bitutil.bitstream2string(StringMask, lgth)),
[698]546                              ('StringStarts', bitutil.bitstream2string(StringStarts, lgth)),
547                              ('StringSpans', bitutil.bitstream2string(StringSpans, lgth)),
[755]548                              ('StringErrors', bitutil.bitstream2string(StringErrors, lgth+1)),
[700]549                              ('EOF_Mask', bitutil.bitstream2string(EOF_mask, lgth+1))])       
[684]550                             
[675]551        return
552
[713]553def validate_json_atoms(u8data):
[698]554               
555        Errors = 0       
556               
[682]557        (bit, EOF_mask) = bitutil.transpose_streams(u8data)
[684]558       
[698]559        (u8, Lex, Ctrl) = byteclass.classify_bytes(bit) 
[684]560       
[755]561        Escape = parse_escape(Lex, EOF_mask)
562        UnescapedDQuotes = (Lex.DQuote &~ Escape)
[739]563        ParityMask = parallel_prefix_parity(UnescapedDQuotes)
[698]564        StringMask = ParityMask & bitutil.Advance(ParityMask)   
[714]565        StringSpans = StringMask | UnescapedDQuotes
[698]566       
[714]567        (StringStarts, NumberStarts, TrueStarts, FalseStarts, NullStarts) = atom_starts(Lex, StringSpans)
[698]568       
[755]569        (StringErrors, StringSpans) = validate_string(StringStarts, Lex, Ctrl, StringMask, Escape, UnescapedDQuotes)
[698]570       
[704]571        (NumberErrors, NumberSpans) = validate_number(NumberStarts, Lex, EOF_mask)
572       
573        (TrueErrors, TrueSpans) = validate_true(TrueStarts, Lex)
574       
575        (FalseErrors, FalseSpans) = validate_false(FalseStarts, Lex)
576       
577        (NullErrors, NullSpans) = validate_null(NullStarts, Lex)
[713]578
579        return (StringStarts, StringSpans, StringErrors, 
[704]580                NumberStarts, NumberSpans, NumberErrors, 
581                TrueStarts, TrueSpans, TrueErrors, 
582                FalseStarts, FalseSpans, FalseErrors, 
583                NullStarts, NullSpans, NullErrors)
[682]584 
[713]585def demo_validate_json_atoms(u8data):
[682]586        global lgth
587        lgth = len(u8data)
[713]588
[714]589        (StringStarts, StringSpans, StringErrors,
[704]590        NumberStarts, NumberSpans, NumberErrors, 
591        TrueStarts, TrueSpans, TrueErrors, 
592        FalseStarts, FalseSpans, FalseErrors, 
[713]593        NullStarts, NullSpans, NullErrors) = validate_json_atoms(u8data)
[698]594
[766]595        AtomSpans = (StringSpans|NumberSpans|TrueSpans|FalseSpans|NullSpans)
596
[698]597        bitutil.print_aligned_streams([('Input Data', u8data), 
598                              ('StringStarts', bitutil.bitstream2string(StringStarts, lgth)),
[704]599                              ('StringSpans', bitutil.bitstream2string(StringSpans, lgth)),
600                              ('StringErrors', bitutil.bitstream2string(StringErrors, lgth)),                         
[698]601                              ('NumberStarts', bitutil.bitstream2string(NumberStarts, lgth)),
[704]602                              ('NumberSpans', bitutil.bitstream2string(NumberSpans, lgth)),
603                              ('NumberErrors', bitutil.bitstream2string(NumberErrors, lgth)),                         
[698]604                              ('TrueStarts', bitutil.bitstream2string(TrueStarts, lgth)),
[704]605                              ('TrueSpans', bitutil.bitstream2string(TrueSpans, lgth)),
606                              ('TrueErrors', bitutil.bitstream2string(TrueErrors, lgth)),                             
[698]607                              ('FalseStarts', bitutil.bitstream2string(FalseStarts, lgth)),
[704]608                              ('FalseSpans', bitutil.bitstream2string(FalseSpans, lgth)),
609                              ('FalseErrors', bitutil.bitstream2string(FalseErrors, lgth)),                           
[698]610                              ('NullStarts', bitutil.bitstream2string(NullStarts, lgth)),
[704]611                              ('NullSpans', bitutil.bitstream2string(NullSpans, lgth)),
[766]612                              ('NullErrors', bitutil.bitstream2string(NullErrors, lgth)),
613                              ('AtomSpans', bitutil.bitstream2string(AtomSpans, lgth))])
[698]614
[682]615        return
616
[672]617if __name__ == "__main__":
618        import doctest
619        doctest.testmod()
620
[690]621        if len(sys.argv) < 2:
622                sys.stderr.write("Usage: python " + filename + " <filename>" "\n")
[672]623                sys.exit(2)
624
[675]625        u8data = bitutil.readfile(sys.argv[1]) 
[713]626
[746]627#       demo_parse_escape(u8data)
[744]628#       demo_parallel_prefix_parity(u8data)
[715]629#       demo_atom_starts(u8data)
[739]630#       demo_validate_number(u8data)
[766]631#       demo_validate_string(u8data)
632        demo_validate_json_atoms(u8data)
Note: See TracBrowser for help on using the repository browser.