@@ -293,72 +293,101 @@ def _SExpr_unescape_string(s):
293
293
return re .sub (r'\\(["\\])' , r'\1' , s )
294
294
295
295
296
- class _SExprParser (object ):
297
- def parse (self , s ):
298
- i = 0
299
- n = len (s )
300
- while i < n and s [i ].isspace ():
301
- i += 1
302
- if i == n :
303
- return SExprResult ([], '' )
304
- assert s [i ] == '('
305
- i += 1
306
- while i < n and s [i ].isspace ():
296
+ def _SExpr_parse (s : str ) -> SExprResult :
297
+ s = s .lstrip ()
298
+ data : _Cons = []
299
+ if not s :
300
+ return SExprResult (data , '' )
301
+ assert s .startswith ('(' )
302
+ i = 1
303
+ n = len (s )
304
+ stack : List [List [_SExpr ]] = []
305
+ vals : List [_SExpr ] = []
306
+ while i < n :
307
+ c = s [i ]
308
+ # numbers
309
+ if c .isdigit () or c == '-' and (i + 1 < n ) and s [i + 1 ].isdigit ():
310
+ num , i = _SExpr_parse_number (s , i )
311
+ vals .append (num )
312
+ # quoted strings
313
+ elif c == '"' :
314
+ string , i = _SExpr_parse_string (s , i )
315
+ vals .append (string )
316
+ # start new list
317
+ elif c == '(' :
318
+ stack .append (vals )
319
+ vals = []
307
320
i += 1
308
- stack = [[]]
309
- while i < n :
310
- c = s [i ]
311
- # numbers
312
- if c .isdigit () or c == '-' and s [i + 1 ].isdigit ():
313
- j = i + 1
314
- while s [j ].isdigit ():
315
- j += 1
316
- c = s [j ]
317
- if c in '.eE' : # float
318
- if c == '.' :
319
- j += 1
320
- while s [j ].isdigit ():
321
- j += 1
322
- if c in 'eE' :
323
- j += 1
324
- if s [j ] in '+=' :
325
- j += 1
326
- while s [j ].isdigit ():
327
- j += 1
328
- stack [- 1 ].append (float (s [i :j ]))
329
- else : # int
330
- stack [- 1 ].append (int (s [i :j ]))
331
- i = j
332
- elif c == '"' : # quoted strings
333
- j = i + 1
334
- while s [j ] != '"' :
335
- if s [j ] == '\\ ' :
336
- j += 2
337
- else :
338
- j += 1
339
- stack [- 1 ].append (
340
- _SExpr_unescape_string (s [i + 1 : j ])) # noqa: E203
341
- i = j + 1
342
- elif c == '(' :
343
- stack .append ([])
344
- i += 1
345
- elif c == ')' :
346
- xs = stack .pop ()
347
- if len (xs ) == 3 and xs [1 ] == '.' :
348
- xs = tuple (xs [::2 ])
349
- if len (stack ) == 0 :
350
- return SExprResult (xs , s [i + 1 :]) # noqa: E203
351
- else :
352
- stack [- 1 ].append (xs )
353
- i += 1
354
- elif c .isspace ():
355
- i += 1
321
+ # end list
322
+ elif c == ')' :
323
+ if len (vals ) == 3 and vals [1 ] == '.' :
324
+ data = (vals [0 ], vals [2 ]) # simplify dotted pair
356
325
else :
357
- m = _SExpr_symbol_re .match (s , pos = i )
358
- if m is None :
359
- raise ValueError ('Invalid S-Expression: ' + s )
360
- stack [- 1 ].append (_SExpr_unescape_symbol (m .group (0 )))
361
- i += len (m .group (0 ))
326
+ data = vals
327
+ if len (stack ) == 0 :
328
+ break
329
+ else :
330
+ stack [- 1 ].append (data )
331
+ vals = stack .pop ()
332
+ i += 1
333
+ # ignore whitespace
334
+ elif c .isspace ():
335
+ i += 1
336
+ # any other symbol
337
+ else :
338
+ sym , i = _SExpr_parse_symbol (s , i )
339
+ vals .append (sym )
340
+
341
+ return SExprResult (data , s [i + 1 :])
342
+
343
+
344
+ def _SExpr_parse_number (s : str , i : int ) -> Tuple [Union [int , float ], int ]:
345
+ j = i + 1 # start at next character
346
+ while s [j ].isdigit ():
347
+ j += 1
348
+ c = s [j ]
349
+
350
+ if c not in '.eE' : # int
351
+ return int (s [i :j ]), j
352
+
353
+ # float
354
+ if c == '.' :
355
+ j += 1
356
+ while s [j ].isdigit ():
357
+ j += 1
358
+ c = s [j ]
359
+
360
+ if c in 'eE' :
361
+ j += 1
362
+ if s [j ] in '+-' :
363
+ j += 1
364
+ while s [j ].isdigit ():
365
+ j += 1
366
+
367
+ return float (s [i :j ]), j
368
+
369
+
370
+ def _SExpr_parse_string (s : str , i : int ) -> Tuple [str , int ]:
371
+ j = i + 1
372
+ while s [j ] != '"' :
373
+ if s [j ] == '\\ ' :
374
+ j += 2
375
+ else :
376
+ j += 1
377
+ return _SExpr_unescape_string (s [i + 1 :j ]), j + 1
378
+
379
+
380
+ def _SExpr_parse_symbol (s : str , i : int ) -> Tuple [str , int ]:
381
+ m = _SExpr_symbol_re .match (s , pos = i )
382
+ if m is None :
383
+ raise ValueError ('Invalid S-Expression: ' + s )
384
+ return _SExpr_unescape_symbol (m .group (0 )), m .end ()
385
+
386
+
387
+ class _SExprParser (object ):
388
+
389
+ def parse (self , s : str ) -> SExprResult :
390
+ return _SExpr_parse (s .lstrip ())
362
391
363
392
def format (self , d ):
364
393
if isinstance (d , tuple ) and len (d ) == 2 :
0 commit comments