Skip to content

Instantly share code, notes, and snippets.

@jdp
Created February 21, 2011 10:31
Show Gist options
  • Save jdp/836905 to your computer and use it in GitHub Desktop.
Save jdp/836905 to your computer and use it in GitHub Desktop.
A simple compiler targeting 6502 family chips
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