summaryrefslogtreecommitdiffstats
path: root/codegen/scmexpr.py
diff options
context:
space:
mode:
Diffstat (limited to 'codegen/scmexpr.py')
-rw-r--r--codegen/scmexpr.py143
1 files changed, 143 insertions, 0 deletions
diff --git a/codegen/scmexpr.py b/codegen/scmexpr.py
new file mode 100644
index 0000000..02f2e4b
--- /dev/null
+++ b/codegen/scmexpr.py
@@ -0,0 +1,143 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+from __future__ import generators
+
+import string
+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`