from .tokens import Token from .utils import * SCOPES = ["static", "field", "var"] PRIMITIVE_TYPES = ["int", "char", "boolean"] class Class: def __init__(self, name: Token, variables: list, subroutines: list): self.name = name self.variables = variables self.subroutines = subroutines @classmethod def from_tokens(cls, tokens: list): """Construct a class from a list of tokens. In standard Jack, one file is exactly one class. Format: class { } """ if len(tokens) < 4: return None if tokens[0] != "class": raise JackSyntaxError( f"Expected `class`, got `{tokens[0]}` instead", tokens[0] ) if tokens[1].type != "identifier": raise JackSyntaxError( f"You cannot name a class `{tokens[1]}`", tokens[1] ) name = tokens[1] if tokens[2] != LEFT_BRACE: raise JackSyntaxError( f"Expected `{LEFT_BRACE}`, got `{tokens[2]}` instead", tokens[2] ) tokens_consumed = 3 while True: variables, token_cnt = Variable.from_tokens(tokens[tokens_consumed:]) if variables is None: break variables.print_verbose() tokens_consumed += token_cnt return Class(name, variables, []) class Variable: def __init__(self, scope: Token, type: Token, names: list[Token]): self.scope = scope self.type = type self.names = names @classmethod def from_tokens(cls, tokens: list) -> tuple: """Construct variable declaration statement. Return a tuple of a Variable instance and number of tokens consumed. Format: ; = static | field | var = int | char | boolean | """ if len(tokens) < 4 or tokens[0] not in SCOPES: # not variable declaration return (None, 0) scope = tokens[0] if tokens[1] not in PRIMITIVE_TYPES and tokens[1].type != "identifier": raise JackSyntaxError( f"Expected datatype, got `{tokens[1]}` instead", tokens[1] ) type = tokens[1] tokens_consumed = 2 names = [] # names of variables expecting_identifier = True for token in tokens[2:]: tokens_consumed += 1 if token.type == "identifier": if expecting_identifier: names.append(token) expecting_identifier = False else: raise JackSyntaxError( f"Expected `,`, got `{token}` instead", token ) elif token == ",": if not expecting_identifier: expecting_identifier = True else: raise JackSyntaxError( f"Expected variable name, got `,` instead", token ) elif token == ";": if expecting_identifier: raise JackSyntaxError( f"Expected variable name, got `;` instead", token ) break else: expected = "variable name" if expecting_identifier else "`,` or `;`" raise JackSyntaxError(f"Expected {expected}, got `{token}` instead", token) return (Variable(scope, type, names), tokens_consumed) def print_verbose(self): print(f"Declare {len(self.names)} variable(s):") for name in self.names: print(self.scope, self.type, name) class Subroutine: def __init__(self): pass class Statement: def __init__(self): super().__init__() class IfStatement(Statement): def __init__(self): super().__init__()