Created
February 21, 2011 10:31
-
-
Save jdp/836905 to your computer and use it in GitHub Desktop.
A simple compiler targeting 6502 family chips
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Scanner(): | |
def __init__(self, source): | |
self.source = source | |
self.position = 0 | |
self.buffer = '' | |
self.line = 1 | |
def current(self): | |
return self.source[self.position] | |
def next(self): | |
self.position += 1 | |
return self.current() | |
def reset(self): | |
self.buffer = '' | |
def save(self): | |
self.buffer = self.buffer + self.current() | |
def is_operand(self, token): | |
return ":;[].+-=".find(token) != -1 | |
def scan(self): | |
try: | |
while True: | |
# Eat EOL's but keep track of the line number | |
if self.current() == "\n" or self.current == "\r": | |
self.next() | |
self.line += 1 | |
# Regular whitespace consumption | |
elif self.current().isspace(): | |
while self.current().isspace(): | |
self.next() | |
# Return an operand as a parse token | |
elif self.is_operand(self.current()): | |
token = self.current() | |
self.next() | |
return (token, self.line) | |
# Return an integer value | |
elif self.current().isdigit(): | |
self.reset() | |
while self.current().isdigit(): | |
self.save() | |
self.next() | |
return (int(self.buffer), self.line) | |
else: | |
raise SyntaxError, "Unknown input `%s' on line %d" % (self.current(), self.line) | |
except IndexError: | |
return (False, self.line) | |
class Parser(): | |
def __init__(self, scanner): | |
self.scanner = scanner | |
self.reset() | |
def reset(self): | |
self.ast = [] | |
self.quote_stack = [self.ast] | |
def save(self, node): | |
self.quote_stack[-1].append(node) | |
def parse(self): | |
token, line = self.scanner.scan() | |
while token != False: | |
if token == '[': | |
self.quote_stack[-1].append([]) | |
self.quote_stack.append(self.quote_stack[-1][-1]) | |
elif token == ']': | |
if len(self.quote_stack) == 1: | |
raise SyntaxError, "quote underflow on line %d" % (line,) | |
self.quote_stack.pop() | |
else: | |
self.quote_stack[-1].append(token) | |
token, line = self.scanner.scan() | |
return self.ast | |
class Compiler(): | |
def __init__(self, ast): | |
self.ast = ast | |
self.base = 0 | |
self.offset = 0 | |
self.code = [] | |
def emit(self, bytes): | |
bytes = ["{0:02X}".format(byte) for byte in bytes] | |
for byte in bytes: | |
self.code.append(byte) | |
def compile(self, tree): | |
size = 0 | |
for node in tree: | |
if type(node) is int: | |
self.emit([0xa9, node, 0x48]) | |
self.offset += 3 | |
size += 3 | |
elif type(node) is str: | |
if node == '.': | |
self.emit([0x68, 0x20, 0xee, 0xff]) | |
self.offset += 4 | |
size += 4 | |
elif node == '+': | |
self.emit([0x68, 0x85, 0x00, 0x68, 0x65, 0x00, 0x48]) | |
self.offset += 7 | |
size += 7 | |
else: | |
raise SyntaxError, "unknown word" | |
elif type(node) is list: | |
# push high byte of offset | |
self.emit([0xa9, (self.offset & 0xff00) >> 8, 0x48]) | |
# push low byte of offset | |
self.emit([0xa9, self.offset & 0xff, 0x48]) | |
self.offset += 6 | |
size += 6 | |
self.compile(node) | |
else: | |
raise SyntaxError, "unknown node" | |
self.emit([0]) | |
print "".join(self.code) | |
return size | |
if __name__ == '__main__': | |
scanner = Scanner("33 32 + . ") | |
parser = Parser(scanner) | |
ast = parser.parse() | |
compiler = Compiler(ast) | |
compiler.compile(ast) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment