summaryrefslogtreecommitdiff
path: root/python/codegen/scmexpr.py
blob: d08c517adbe58ddae05cec9d545ecac1d9d9a807 (plain)
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`