Files
jarvis-models/runtime/ast/parser.py
2024-03-21 16:59:35 +08:00

178 lines
4.9 KiB
Python

from runtime.ast.tokenizer import Tokenizer
class Parser:
def __init__(self) -> None:
self.script = ""
self.tokenizer = Tokenizer()
self.current_token = None
def parse(self, script: str):
self.script = script
self.tokenizer.init(script)
self.current_token = self.tokenizer.get_next_token()
return self.program()
def program(self):
return {
"type": 'Program',
"body": {
"type": "BlockStatement",
"body": self.statement_list()
},
}
def return_statement(self):
self.eat('return')
return {
"type": 'ReturnStatement',
"value": self.expression_statement(),
}
def statement_list(self):
statment_list = [self.statement()]
while self.current_token != None:
statment_list.append(self.statement())
return statment_list
def statement(self):
if self.token_type() == "let":
return self.variable_statement()
return self.expression_statement()
def block_statement(self):
if self.token_type() != "{":
self.eat('{')
body = self.statement_list()
if self.token_type() != "}":
self.eat('}')
return {
"type": 'BlockStatement',
"body": body,
}
def expression_statement(self):
if self.token_type() == "return":
return self.return_statement()
if self._is_literal():
return self.literal()
if self.token_type() == "IDENTIFIER":
identifier = self.identifier()
if self.token_type() == "(":
return self.call_expression(identifier)
if self.token_type() == "SIMPLE_ASSIGN":
return self.assignment_expression(identifier)
return identifier
raise Exception("Unexpected token: " + self.token_type())
def assignment_expression(self,identifier):
self.eat('SIMPLE_ASSIGN')
return {
"type": 'AssignmentExpression',
"identifier": identifier,
"value": self.statement(),
}
def call_expression(self, identifier):
self.eat('(')
arguments = self.argument_list()
self.eat(')')
return {
"type": 'CallExpression',
"arguments": arguments,
"callee": identifier,
}
def token_type(self):
if self.current_token == None:
return None
return self.current_token["type"]
def _is_literal(self):
return self.current_token["type"] in ["NUMBER", "STRING", "FLOAT", "true", "false"]
# variable
def variable_statement(self):
self.eat('let')
identifier = self.identifier()
self.eat('SIMPLE_ASSIGN')
return {
"type": 'VariableDeclaration',
"identifier": identifier,
"value": self.statement(),
}
def eat(self, tokenType):
token = self.current_token
if token == None:
raise Exception("Unexpected EOF")
if token["type"] != tokenType:
raise Exception("Unexpected token: " + token["type"])
self.current_token = self.tokenizer.get_next_token()
return token
def identifier(self):
name = self.eat('IDENTIFIER')
return {
"type": 'Identifier',
"name": name["value"],
}
def literal(self):
token_type = self.current_token["type"]
if token_type == "true" or token_type == "false":
return self.boolean_literal()
if token_type == "NUMBER":
return self.numberic_literal()
if token_type == "STRING":
return self.string_literal()
if token_type == "FLOAT":
return self.float_literal()
raise Exception("Unexpected token: " + token_type)
def boolean_literal(self):
if self.token_type() == "true":
self.eat('true')
value = "True"
else:
self.eat("false")
value = "False"
return {
"type": 'BooleanLiteral',
"value": value,
}
def numberic_literal(self):
token = self.eat('NUMBER')
return {
"type": 'NumericLiteral',
"value": token["value"],
}
def string_literal(self):
token = self.eat('STRING')
return {
"type": 'StringLiteral',
"value": token["value"][1:-1],
}
def float_literal(self):
token = self.eat('FLOAT')
return {
"type": 'FloatLiteral',
"value": token["value"],
}
def argument_list(self):
args = []
while self.token_type() != ")":
args.append(self.statement())
if self.token_type() == ",":
self.eat(',')
return args