add tool/sexp.py
[gnuk/gnuk.git] / tool / sexp.py
1 # SEXP (S-expressions) Basic Transport Support
2 #
3 # See: http://people.csail.mit.edu/rivest/sexp.html
4 #
5
6 import re
7
8 WHITESPACE='[ \n\t\v\r\f]+'
9 re_ws = re.compile(WHITESPACE)
10 DIGITS='[0-9]+'
11 re_digit = re.compile(DIGITS)
12
13 def skip_whitespace(string, pos):
14     m = re_ws.match(string, pos)
15     if m:
16         return m.start()
17     else:
18         return pos
19
20 def sexp_match(string, ch, pos):
21     pos = skip_whitespace(string,pos)
22     if string[pos] == ch:
23         return pos+1
24     else:
25         raise ValueError("expect '%s'" % ch)
26
27 def sexp_parse_simple_string(string, pos):
28     pos = skip_whitespace(string,pos)
29     m = re_digit.match(string, pos)
30     if m:
31         length = int(string[m.start():m.end()],10)
32         pos = sexp_match(string, ':', m.end())
33         return (string[pos:pos+length], pos+length)
34     else:
35         raise ValueError('expect digit')
36
37 def sexp_parse_list(string,pos):
38     l = []
39     while True:
40         pos = skip_whitespace(string,pos)
41         if string[pos] == ')':
42             return (l, pos)
43         else:
44             (sexp, pos) = sexp_parse(string,pos)
45             l.append(sexp)
46
47 def sexp_parse(string, pos=0):
48     pos = skip_whitespace(string,pos)
49     if string[pos] == '(':
50         (l, pos) = sexp_parse_list(string,pos+1)
51         pos = sexp_match(string, ')', pos)
52         return (l, pos)
53     elif string[pos] == '[':
54         pos = skip_whitespace(string,pos)
55         (dsp, pos) = sexp_parse_simple_string(string,pos+1)
56         pos = sexp_match(string, ']', pos)
57         pos = skip_whitespace(string,pos)
58         (ss, pos) = sexp_parse_simple_string(string, pos)
59         return ((dsp, ss), pos)
60     else:
61         return sexp_parse_simple_string(string, pos)
62
63 def sexp(string):
64     (sexp, pos) = sexp_parse(string)
65     return sexp