diff options
Diffstat (limited to 'projects/hackc/syntax.py')
-rw-r--r-- | projects/hackc/syntax.py | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/projects/hackc/syntax.py b/projects/hackc/syntax.py new file mode 100644 index 0000000..c9be157 --- /dev/null +++ b/projects/hackc/syntax.py @@ -0,0 +1,126 @@ +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 <name> { + <zero or more class variable declarations> + <zero or more subroutines> + } + """ + 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] + ) + + variables = Variable.from_tokens(tokens[3:]) + variables.print_verbose() + 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): + """Construct variable declaration statement. + + You can declare multiple variables of one scope and type on the same line. + + Format: + <scope> <type> <one or more names, joined with a comma>; + + <scope> = static | field | var + <type> = int | char | boolean | <class name> + """ + if len(tokens) < 4 or tokens[0] not in SCOPES: + # not variable declaration + return None + + 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] + + names = [] # names of variables + expecting_identifier = True + + for token in tokens[2:]: + 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 identifier, got `,` instead", token + ) + elif token == ";": + if expecting_identifier: + raise JackSyntaxError( + f"Expected identifier, got `;` instead", token + ) + break + + return Variable(scope, type, names) + + def print_verbose(self): + print(f"Declare {len(self.names)} variables:") + 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__() |