Nand2Tetris — Capítulo 10
El analizador sintáctico de Jack
# Jack Analyzer
# Entrada: archivo .jack
# Salida: token XML/árbol de análisis
# Alcance: tokenizador + analizador de descenso recursivo
# movimiento:
# Después de cortar el código Jack en unidades simbólicas,
# Genere XML en el siguiente orden: clase -> classVarDec -> subrutinaDec -> declaraciones
import re
import sys
from html import escape
KEYWORDS = {
"class", "constructor", "function", "method", "field", "static",
"var", "int", "char", "boolean", "void", "true", "false", "null",
"this", "let", "do", "if", "else", "while", "return",
}
SYMBOLS = set("{}()[].,;+-*/&|<>=~")
def remove_comments(source):
source = re.sub(r"/\*.*?\*/", "", source, flags=re.S)
source = re.sub(r"//.*", "", source)
return source
def tokenize(source):
tokens = []
i = 0
while i < len(source):
c = source[i]
if c.isspace():
i += 1
continue
if c == '"':
j = i + 1
while source[j] != '"':
j += 1
tokens.append(("stringConstant", source[i + 1:j]))
i = j + 1
continue
if c in SYMBOLS:
tokens.append(("symbol", c))
i += 1
continue
j = i
while j < len(source) and not source[j].isspace() and source[j] not in SYMBOLS:
j += 1
word = source[i:j]
if word in KEYWORDS:
tokens.append(("keyword", word))
elif word.isdigit():
tokens.append(("integerConstant", word))
else:
tokens.append(("identifier", word))
i = j
return tokens
class CompilationEngine:
def __init__(self, tokens):
self.tokens = tokens
self.index = 0
self.lines = []
def current(self):
return self.tokens[self.index]
def write(self, text):
self.lines.append(text)
def eat(self):
token_type, value = self.current()
self.write(f"<{token_type}> {escape(value)} </{token_type}>")
self.index += 1
return value
def compile_class(self):
self.write("<class>")
self.eat() # class
self.eat() # className
self.eat() # {
while self.current()[1] in ("static", "field"):
self.compile_class_var_dec()
while self.current()[1] in ("constructor", "function", "method"):
self.compile_subroutine()
self.eat() # }
self.write("</class>")
def compile_class_var_dec(self):
self.write("<classVarDec>")
while self.current()[1] != ";":
self.eat()
self.eat()
self.write("</classVarDec>")
def compile_subroutine(self):
self.write("<subroutineDec>")
while self.current()[1] != "{":
self.eat()
self.compile_subroutine_body()
self.write("</subroutineDec>")
def compile_subroutine_body(self):
self.write("<subroutineBody>")
self.eat() # {
while self.current()[1] == "var":
self.compile_var_dec()
self.compile_statements()
self.eat() # }
self.write("</subroutineBody>")
def compile_var_dec(self):
self.write("<varDec>")
while self.current()[1] != ";":
self.eat()
self.eat()
self.write("</varDec>")
def compile_statements(self):
self.write("<statements>")
while self.current()[1] in ("let", "if", "while", "do", "return"):
self.write(f"<{self.current()[1]}Statement>")
while self.current()[1] not in (";", "}"):
self.eat()
if self.current()[1] == ";":
self.eat()
self.write(f"</{self.lines[-2][1:].split('Statement>')[0]}Statement>")
self.write("</statements>")