summaryrefslogtreecommitdiff
path: root/projects/hackc/syntax.py
diff options
context:
space:
mode:
Diffstat (limited to 'projects/hackc/syntax.py')
-rw-r--r--projects/hackc/syntax.py126
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__()