feat(parser): add documentation to Midas parser
This commit is contained in:
@@ -16,6 +16,8 @@ from parser.errors import ParsingError
|
|||||||
|
|
||||||
|
|
||||||
class MidasParser(Parser):
|
class MidasParser(Parser):
|
||||||
|
"""A simple parser for midas type definitions"""
|
||||||
|
|
||||||
SYNC_BOUNDARY: set[TokenType] = {TokenType.TYPE, TokenType.OP, TokenType.CONSTRAINT}
|
SYNC_BOUNDARY: set[TokenType] = {TokenType.TYPE, TokenType.OP, TokenType.CONSTRAINT}
|
||||||
|
|
||||||
def parse(self) -> list[Stmt]:
|
def parse(self) -> list[Stmt]:
|
||||||
@@ -29,6 +31,11 @@ class MidasParser(Parser):
|
|||||||
return statements
|
return statements
|
||||||
|
|
||||||
def synchronize(self):
|
def synchronize(self):
|
||||||
|
"""Skip tokens until a synchronization boundary is found
|
||||||
|
|
||||||
|
This method allows gracefully recovering from a parse error
|
||||||
|
to a safe place and continue parsing
|
||||||
|
"""
|
||||||
self.advance()
|
self.advance()
|
||||||
while not self.is_at_end():
|
while not self.is_at_end():
|
||||||
if self.previous().type == TokenType.NEWLINE:
|
if self.previous().type == TokenType.NEWLINE:
|
||||||
@@ -38,6 +45,13 @@ class MidasParser(Parser):
|
|||||||
self.advance()
|
self.advance()
|
||||||
|
|
||||||
def declaration(self) -> Optional[Stmt]:
|
def declaration(self) -> Optional[Stmt]:
|
||||||
|
"""Try and parse a declaration
|
||||||
|
|
||||||
|
Any parsing error is caught and None is returned
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Optional[Stmt]: the parsed Midas statement, or None if a ParsingError was raised
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
if self.match(TokenType.TYPE):
|
if self.match(TokenType.TYPE):
|
||||||
return self.type_declaration()
|
return self.type_declaration()
|
||||||
@@ -50,6 +64,13 @@ class MidasParser(Parser):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def type_declaration(self) -> TypeStmt:
|
def type_declaration(self) -> TypeStmt:
|
||||||
|
"""Parse a type declaration
|
||||||
|
|
||||||
|
A type declaration is written `type Name<TypeExpr, ...>` optionally followed by a brace-wrapped body
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
TypeStmt: the parsed type declaration statement
|
||||||
|
"""
|
||||||
name: Token = self.consume(TokenType.IDENTIFIER, "Expected type name")
|
name: Token = self.consume(TokenType.IDENTIFIER, "Expected type name")
|
||||||
self.consume(TokenType.LESS, "Expected '<' after type name")
|
self.consume(TokenType.LESS, "Expected '<' after type name")
|
||||||
bases: list[TypeExpr] = []
|
bases: list[TypeExpr] = []
|
||||||
@@ -66,6 +87,11 @@ class MidasParser(Parser):
|
|||||||
return TypeStmt(name=name, bases=bases, body=body)
|
return TypeStmt(name=name, bases=bases, body=body)
|
||||||
|
|
||||||
def type_expr(self) -> TypeExpr:
|
def type_expr(self) -> TypeExpr:
|
||||||
|
"""Parse a type expression
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
TypeExpr: the parsed type expression
|
||||||
|
"""
|
||||||
name: Token = self.consume(TokenType.IDENTIFIER, "Expected type name")
|
name: Token = self.consume(TokenType.IDENTIFIER, "Expected type name")
|
||||||
constraints: list[ConstraintExpr] = []
|
constraints: list[ConstraintExpr] = []
|
||||||
|
|
||||||
@@ -75,10 +101,23 @@ class MidasParser(Parser):
|
|||||||
return TypeExpr(name=name, constraints=constraints)
|
return TypeExpr(name=name, constraints=constraints)
|
||||||
|
|
||||||
def constraint_expr(self) -> ConstraintExpr:
|
def constraint_expr(self) -> ConstraintExpr:
|
||||||
|
"""Parse a type constraint
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
ConstraintExpr: the parsed type constraint expression
|
||||||
|
"""
|
||||||
# TODO
|
# TODO
|
||||||
return ConstraintExpr()
|
return ConstraintExpr()
|
||||||
|
|
||||||
def type_body_expr(self) -> TypeBodyExpr:
|
def type_body_expr(self) -> TypeBodyExpr:
|
||||||
|
"""Parse a type definition body
|
||||||
|
|
||||||
|
A type definition body is a set of whitespace-separated
|
||||||
|
property statements enclosed in curly braces
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
TypeBodyExpr: the parsed type body expression
|
||||||
|
"""
|
||||||
self.consume(TokenType.LEFT_BRACE, "Expected '{' to start type body")
|
self.consume(TokenType.LEFT_BRACE, "Expected '{' to start type body")
|
||||||
properties: list[PropertyStmt] = []
|
properties: list[PropertyStmt] = []
|
||||||
while not self.check(TokenType.RIGHT_BRACE) and not self.is_at_end():
|
while not self.check(TokenType.RIGHT_BRACE) and not self.is_at_end():
|
||||||
@@ -87,12 +126,26 @@ class MidasParser(Parser):
|
|||||||
return TypeBodyExpr(properties=properties)
|
return TypeBodyExpr(properties=properties)
|
||||||
|
|
||||||
def property_stmt(self) -> PropertyStmt:
|
def property_stmt(self) -> PropertyStmt:
|
||||||
|
"""Parse a property statement
|
||||||
|
|
||||||
|
A type property statement is written `name: Type`
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
PropertyStmt: the parsed property statement
|
||||||
|
"""
|
||||||
name: Token = self.consume(TokenType.IDENTIFIER, "Expected property name")
|
name: Token = self.consume(TokenType.IDENTIFIER, "Expected property name")
|
||||||
self.consume(TokenType.COLON, "Expected ':' after property name")
|
self.consume(TokenType.COLON, "Expected ':' after property name")
|
||||||
type: TypeExpr = self.type_expr()
|
type: TypeExpr = self.type_expr()
|
||||||
return PropertyStmt(name=name, type=type)
|
return PropertyStmt(name=name, type=type)
|
||||||
|
|
||||||
def op_declaration(self) -> OpStmt:
|
def op_declaration(self) -> OpStmt:
|
||||||
|
"""Parse an operation definition
|
||||||
|
|
||||||
|
An operation is written `op <Type1> operator <Type2> = <Type3>` where `operator` can be any single token
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
OpStmt: the parsed operation statement
|
||||||
|
"""
|
||||||
self.consume(TokenType.LESS, "Expected '<' before first type")
|
self.consume(TokenType.LESS, "Expected '<' before first type")
|
||||||
left: TypeExpr = self.type_expr()
|
left: TypeExpr = self.type_expr()
|
||||||
self.consume(TokenType.GREATER, "Expected '>' after first type")
|
self.consume(TokenType.GREATER, "Expected '>' after first type")
|
||||||
@@ -112,6 +165,13 @@ class MidasParser(Parser):
|
|||||||
return OpStmt(left=left, op=op, right=right, result=result)
|
return OpStmt(left=left, op=op, right=right, result=result)
|
||||||
|
|
||||||
def constraint_declaration(self) -> ConstraintStmt:
|
def constraint_declaration(self) -> ConstraintStmt:
|
||||||
|
"""Parse a type constraint declaration
|
||||||
|
|
||||||
|
A constraint is written `constraint Name = constraint_expression`
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
ConstraintStmt: the parsed constraint declaration statement
|
||||||
|
"""
|
||||||
name: Token = self.consume(TokenType.IDENTIFIER, "Expected constraint name")
|
name: Token = self.consume(TokenType.IDENTIFIER, "Expected constraint name")
|
||||||
self.consume(TokenType.EQUAL, "Expected '=' after constraint name")
|
self.consume(TokenType.EQUAL, "Expected '=' after constraint name")
|
||||||
constraint: ConstraintExpr = self.constraint_expr()
|
constraint: ConstraintExpr = self.constraint_expr()
|
||||||
|
|||||||
Reference in New Issue
Block a user