1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
|
#!/usr/bin/env python
# -*- Mode: Python; py-indent-offset: 4 -*-
from __future__ import generators
import string
import types
from cStringIO import StringIO
class error(Exception):
def __init__(self, filename, lineno, msg):
Exception.__init__(self, msg)
self.filename = filename
self.lineno = lineno
self.msg = msg
def __str__(self):
return '%s:%d: error: %s' % (self.filename, self.lineno, self.msg)
trans = [' '] * 256
for i in range(256):
if chr(i) in string.letters + string.digits + '_':
trans[i] = chr(i)
else:
trans[i] = '_'
trans = string.join(trans, '')
def parse(filename):
if isinstance(filename, str):
fp = open(filename, 'r')
else: # if not string, assume it is some kind of iterator
fp = filename
filename = getattr(fp, 'name', '<unknown>')
whitespace = ' \t\n\r\x0b\x0c'
nonsymbol = whitespace + '();\'"'
stack = []
openlines = []
lineno = 0
for line in fp:
pos = 0
lineno += 1
while pos < len(line):
if line[pos] in whitespace: # ignore whitespace
pass
elif line[pos] == ';': # comment
break
elif line[pos:pos+2] == "'(":
pass # the open parenthesis will be handled next iteration
elif line[pos] == '(':
stack.append(())
openlines.append(lineno)
elif line[pos] == ')':
if len(stack) == 0:
raise error(filename, lineno, 'close parenthesis found when none open')
closed = stack[-1]
del stack[-1]
del openlines[-1]
if stack:
stack[-1] += (closed,)
else:
yield closed
elif line[pos] == '"': # quoted string
if not stack:
raise error(filename, lineno,
'string found outside of s-expression')
endpos = pos + 1
chars = []
while endpos < len(line):
if endpos+1 < len(line) and line[endpos] == '\\':
endpos += 1
if line[endpos] == 'n':
chars.append('\n')
elif line[endpos] == 'r':
chars.append('\r')
elif line[endpos] == 't':
chars.append('\t')
else:
chars.append('\\')
chars.append(line[endpos])
elif line[endpos] == '"':
break
else:
chars.append(line[endpos])
endpos += 1
if endpos >= len(line):
raise error(filename, lineno, "unclosed quoted string")
pos = endpos
stack[-1] += (''.join(chars),)
else: # symbol/number
if not stack:
raise error(filename, lineno,
'identifier found outside of s-expression')
endpos = pos
while endpos < len(line) and line[endpos] not in nonsymbol:
endpos += 1
symbol = line[pos:endpos]
pos = max(pos, endpos-1)
try: symbol = int(symbol)
except ValueError:
try: symbol = float(symbol)
except ValueError: pass
stack[-1] += (symbol,)
pos += 1
if len(stack) != 0:
msg = '%d unclosed parentheses found at end of ' \
'file (opened on line(s) %s)' % (len(stack),
', '.join(map(str, openlines)))
raise error(filename, lineno, msg)
class Parser:
def __init__(self, filename):
"""Argument is either a string, a parse tree, or file object"""
self.filename = filename
def startParsing(self, filename=None):
statements = parse(filename or self.filename)
for statement in statements:
self.handle(statement)
def handle(self, tup):
cmd = string.translate(tup[0], trans)
if hasattr(self, cmd):
getattr(self, cmd)(*tup[1:])
else:
self.unknown(tup)
def unknown(self, tup):
pass
_testString = """; a scheme file
(define-func gdk_font_load ; a comment at end of line
GdkFont
((string name)))
(define-boxed GdkEvent
gdk_event_copy
gdk_event_free
"sizeof(GdkEvent)")
"""
if __name__ == '__main__':
import sys
if sys.argv[1:]:
fp = open(sys.argv[1])
else:
fp = StringIO(_testString)
statements = parse(fp)
for s in statements:
print `s`
|