nand2tetris/compiler/engine.py

168 lines
4.2 KiB
Python
Raw Normal View History

2020-06-24 12:42:36 +00:00
from tokenizer import JackTokenizer
2020-07-06 09:06:41 +00:00
from keywords import *
from grammar import *
import sys
2020-06-24 12:42:36 +00:00
"""
New Compilation Engine
"""
class Engine:
def __init__(self, input_file):
self.i = 0
self.jt = JackTokenizer(input_file, False)
self.file = open(self.xml_file(input_file), "w")
2020-07-06 09:06:41 +00:00
def xml_file(self, input_file):
return input_file + ".xml"
2020-07-06 09:30:36 +00:00
""" Throughout the compilation engine, we work using atoms"""
def atom(self):
token = self.jt.tokenType()
return Atom(token.value)
2020-07-06 10:57:19 +00:00
def compileClass(self):
self.compile(CLASS)
2020-07-06 10:57:19 +00:00
def advance(self):
self.jt.advance()
def ZeroOrMany(self, grammarList, matchOnly):
# print("ZOM called")
ret = self.compile(grammarList[0], matchOnly)
if matchOnly:
return ret
elif ret:
2020-07-06 10:57:19 +00:00
# We now expect the whole of it
for e in grammarList:
self.compile(e)
# We try for another list after this
self.ZeroOrMany(grammarList, False)
return True
2020-07-06 10:57:19 +00:00
else:
return False
2020-07-06 10:57:19 +00:00
def write(self, line, end = "\n"):
self.file.write(self.i*" " + line + end)
2020-07-06 10:57:19 +00:00
def MatchDict(self, dictionary, matchOnly):
# Easy way out
xml_rows_for_lookup_terms = [self.jt.xml_row()]
lookup_keys = (self.atom(),)
2020-07-06 10:57:19 +00:00
# How much to lookahead
keys = list(dictionary.keys())
lookahead = len(keys[0])
# We don't have to move the cursor for LL0 grammar
if matchOnly:
assert(lookahead == 1)
return lookup_keys in dictionary
for _ in range(lookahead-1):
self.advance()
2020-07-06 10:57:19 +00:00
xml_rows_for_lookup_terms += [self.jt.xml_row()]
lookup_keys = lookup_keys + (self.atom(),)
if not lookup_keys in dictionary:
return False
grammar = el = dictionary[lookup_keys]
# We must open this before we compile the remainder
if isinstance(grammar, Element):
self.open(el)
grammar = grammar.grammar
# Now we put the first X terms from the conditional
2020-07-06 10:57:19 +00:00
for line in xml_rows_for_lookup_terms:
self.write(line, end="")
self.advance()
2020-07-06 10:57:19 +00:00
2020-07-06 21:53:06 +00:00
print(lookup_keys)
print("grammar inside matchDict ")
print(grammar)
# Grammar can be none
if grammar:
self.compile(grammar)
# TODO: Improve open and close for dicts
if isinstance(el, Element):
self.close(el)
2020-07-06 10:57:19 +00:00
return True
def ZeroOrOne(self, grammarTuple, matchOnly):
ret = self.compile(grammarTuple[0], True)
if matchOnly:
return ret
elif ret:
2020-07-06 10:57:19 +00:00
for e in grammarTuple:
self.compile(e)
return True
else:
return None
""" Has to MATCH """
def MatchAtom(self, atom, matchOnly):
2020-07-06 10:57:19 +00:00
expected = atom
current = self.atom()
# We use in here to accomodate for bitmasks
match = current in expected
if matchOnly:
return match
elif match:
self.write(self.jt.xml_row(), end="")
2020-07-06 10:57:19 +00:00
self.advance()
return True
2020-07-06 10:57:19 +00:00
else:
2020-07-06 21:53:06 +00:00
print("%s != %s" % (current, expected))
return False
def open(self, el):
self.write("<%s>" % el.name)
self.i+=2
def close(self, el):
self.i-=2
self.write("</%s>" % el.name)
"""
If you set matchOnly = true, the cursor will not move forward
If it is forced to move forward (LL1 grammar for eg,) it will raise an error instead
"""
2020-07-06 21:53:06 +00:00
def compile(self, grammar, matchOnly = False):
if callable(grammar):
ret = self.compile(grammar(), matchOnly)
elif isinstance(grammar, Element):
ret = self.compile(grammar.grammar, True)
if grammar.name == 'term':
print(ret)
print(self.atom())
if (matchOnly == False and ret) or grammar.empty:
self.open(grammar)
# Avoid useless compilation
if ret:
ret = self.compile(grammar.grammar)
self.close(grammar)
elif isinstance(grammar, Sequence):
if matchOnly:
ret = self.compile(grammar[0], True)
else:
for e in grammar:
ret = self.compile(e)
2020-07-06 21:53:06 +00:00
elif isinstance(grammar, list):
ret = self.ZeroOrMany(grammar, matchOnly)
elif isinstance(grammar,dict):
ret = self.MatchDict(grammar, matchOnly)
elif isinstance(grammar,tuple):
ret = self.ZeroOrOne(grammar, matchOnly)
elif isinstance(grammar,Atom):
ret = self.MatchAtom(grammar, matchOnly)
2020-07-06 10:57:19 +00:00
else:
2020-07-06 21:53:06 +00:00
raise Exception("Invalid Grammar")
return ret