feat(parser): parse Midas type constraints
This commit is contained in:
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Generic, Optional, TypeVar
|
from typing import Any, Generic, Optional, TypeVar
|
||||||
|
|
||||||
from lexer.token import Token
|
from lexer.token import Token
|
||||||
|
|
||||||
@@ -79,6 +79,12 @@ class Expr(ABC):
|
|||||||
def accept(self, visitor: Visitor[T]) -> T: ...
|
def accept(self, visitor: Visitor[T]) -> T: ...
|
||||||
|
|
||||||
class Visitor(ABC, Generic[T]):
|
class Visitor(ABC, Generic[T]):
|
||||||
|
@abstractmethod
|
||||||
|
def visit_wildcard_expr(self, expr: WildcardExpr) -> T: ...
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def visit_literal_expr(self, expr: LiteralExpr) -> T: ...
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def visit_type_expr(self, expr: TypeExpr) -> T: ...
|
def visit_type_expr(self, expr: TypeExpr) -> T: ...
|
||||||
|
|
||||||
@@ -89,6 +95,22 @@ class Expr(ABC):
|
|||||||
def visit_type_body_expr(self, expr: TypeBodyExpr) -> T: ...
|
def visit_type_body_expr(self, expr: TypeBodyExpr) -> T: ...
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class WildcardExpr(Expr):
|
||||||
|
token: Token
|
||||||
|
|
||||||
|
def accept(self, visitor: Expr.Visitor[T]) -> T:
|
||||||
|
return visitor.visit_wildcard_expr(self)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class LiteralExpr(Expr):
|
||||||
|
value: Any
|
||||||
|
|
||||||
|
def accept(self, visitor: Expr.Visitor[T]) -> T:
|
||||||
|
return visitor.visit_literal_expr(self)
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class TypeExpr(Expr):
|
class TypeExpr(Expr):
|
||||||
name: Token
|
name: Token
|
||||||
@@ -100,6 +122,10 @@ class TypeExpr(Expr):
|
|||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class ConstraintExpr(Expr):
|
class ConstraintExpr(Expr):
|
||||||
|
left: Expr
|
||||||
|
op: Token
|
||||||
|
right: Expr
|
||||||
|
|
||||||
def accept(self, visitor: Expr.Visitor[T]) -> T:
|
def accept(self, visitor: Expr.Visitor[T]) -> T:
|
||||||
return visitor.visit_constraint_expr(self)
|
return visitor.visit_constraint_expr(self)
|
||||||
|
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ class AnnotationAstPrinter(AstPrinter, a.Expr.Visitor[None], a.Stmt.Visitor[None
|
|||||||
if i == len(expr.constraints) - 1:
|
if i == len(expr.constraints) - 1:
|
||||||
self._mark_last()
|
self._mark_last()
|
||||||
constraint.accept(self)
|
constraint.accept(self)
|
||||||
|
|
||||||
def visit_constraint_expr(self, expr: a.ConstraintExpr) -> None:
|
def visit_constraint_expr(self, expr: a.ConstraintExpr) -> None:
|
||||||
self._write_line("ConstraintExpr")
|
self._write_line("ConstraintExpr")
|
||||||
with self._child_level():
|
with self._child_level():
|
||||||
@@ -110,9 +110,9 @@ class AnnotationAstPrinter(AstPrinter, a.Expr.Visitor[None], a.Stmt.Visitor[None
|
|||||||
with self._child_level():
|
with self._child_level():
|
||||||
self._mark_last()
|
self._mark_last()
|
||||||
expr.left.accept(self)
|
expr.left.accept(self)
|
||||||
|
|
||||||
self._write_line(f"operator: {expr.op.lexeme}")
|
self._write_line(f"operator: {expr.op.lexeme}")
|
||||||
|
|
||||||
self._write_line("right", last=True)
|
self._write_line("right", last=True)
|
||||||
with self._child_level():
|
with self._child_level():
|
||||||
self._mark_last()
|
self._mark_last()
|
||||||
@@ -133,14 +133,14 @@ class AnnotationAstPrinter(AstPrinter, a.Expr.Visitor[None], a.Stmt.Visitor[None
|
|||||||
name_text: str = "None" if expr.name is None else f'"{expr.name.lexeme}"'
|
name_text: str = "None" if expr.name is None else f'"{expr.name.lexeme}"'
|
||||||
self._write_line(f"name: {name_text}")
|
self._write_line(f"name: {name_text}")
|
||||||
self._write_optional_child("type", expr.type, last=True)
|
self._write_optional_child("type", expr.type, last=True)
|
||||||
|
|
||||||
def visit_wildcard_expr(self, expr: a.WildcardExpr) -> None:
|
def visit_wildcard_expr(self, expr: a.WildcardExpr) -> None:
|
||||||
self._write_line("WildcardExpr")
|
self._write_line("WildcardExpr")
|
||||||
|
|
||||||
def visit_literal_expr(self, expr: a.LiteralExpr) -> None:
|
def visit_literal_expr(self, expr: a.LiteralExpr) -> None:
|
||||||
self._write_line("LiteralExpr")
|
self._write_line("LiteralExpr")
|
||||||
with self._child_level():
|
with self._child_level():
|
||||||
self._write_line(f'value: {expr.value}', last=True)
|
self._write_line(f"value: {expr.value}", last=True)
|
||||||
|
|
||||||
|
|
||||||
class AnnotationPrinter(a.Expr.Visitor[str], a.Stmt.Visitor[str]):
|
class AnnotationPrinter(a.Expr.Visitor[str], a.Stmt.Visitor[str]):
|
||||||
@@ -158,12 +158,12 @@ class AnnotationPrinter(a.Expr.Visitor[str], a.Stmt.Visitor[str]):
|
|||||||
for constraint in expr.constraints:
|
for constraint in expr.constraints:
|
||||||
parts.append("(" + constraint.accept(self) + ")")
|
parts.append("(" + constraint.accept(self) + ")")
|
||||||
return " + ".join(parts)
|
return " + ".join(parts)
|
||||||
|
|
||||||
def visit_constraint_expr(self, expr: a.ConstraintExpr) -> str:
|
def visit_constraint_expr(self, expr: a.ConstraintExpr) -> str:
|
||||||
parts: list[str] = [
|
parts: list[str] = [
|
||||||
expr.left.accept(self),
|
expr.left.accept(self),
|
||||||
expr.op.lexeme,
|
expr.op.lexeme,
|
||||||
expr.right.accept(self)
|
expr.right.accept(self),
|
||||||
]
|
]
|
||||||
return " ".join(parts)
|
return " ".join(parts)
|
||||||
|
|
||||||
@@ -257,6 +257,18 @@ class MidasAstPrinter(AstPrinter, m.Expr.Visitor[None], m.Stmt.Visitor[None]):
|
|||||||
|
|
||||||
def visit_constraint_expr(self, expr: m.ConstraintExpr):
|
def visit_constraint_expr(self, expr: m.ConstraintExpr):
|
||||||
self._write_line("ConstraintExpr")
|
self._write_line("ConstraintExpr")
|
||||||
|
with self._child_level():
|
||||||
|
self._write_line("left")
|
||||||
|
with self._child_level():
|
||||||
|
self._mark_last()
|
||||||
|
expr.left.accept(self)
|
||||||
|
|
||||||
|
self._write_line(f"operator: {expr.op.lexeme}")
|
||||||
|
|
||||||
|
self._write_line("right", last=True)
|
||||||
|
with self._child_level():
|
||||||
|
self._mark_last()
|
||||||
|
expr.right.accept(self)
|
||||||
|
|
||||||
def visit_type_body_expr(self, expr: m.TypeBodyExpr):
|
def visit_type_body_expr(self, expr: m.TypeBodyExpr):
|
||||||
self._write_line("TypeBodyExpr")
|
self._write_line("TypeBodyExpr")
|
||||||
@@ -268,3 +280,11 @@ class MidasAstPrinter(AstPrinter, m.Expr.Visitor[None], m.Stmt.Visitor[None]):
|
|||||||
if i == len(expr.properties) - 1:
|
if i == len(expr.properties) - 1:
|
||||||
self._mark_last()
|
self._mark_last()
|
||||||
property.accept(self)
|
property.accept(self)
|
||||||
|
|
||||||
|
def visit_wildcard_expr(self, expr: m.WildcardExpr) -> None:
|
||||||
|
self._write_line("WildcardExpr")
|
||||||
|
|
||||||
|
def visit_literal_expr(self, expr: m.LiteralExpr) -> None:
|
||||||
|
self._write_line("LiteralExpr")
|
||||||
|
with self._child_level():
|
||||||
|
self._write_line(f"value: {expr.value}", last=True)
|
||||||
|
|||||||
@@ -68,8 +68,6 @@ class AnnotationParser(Parser):
|
|||||||
constraints: list[ConstraintExpr] = []
|
constraints: list[ConstraintExpr] = []
|
||||||
|
|
||||||
while not self.is_at_end() and self.match(TokenType.PLUS):
|
while not self.is_at_end() and self.match(TokenType.PLUS):
|
||||||
print(self.peek())
|
|
||||||
print(self.tokens)
|
|
||||||
self.consume(TokenType.LEFT_PAREN, "Expected '(' before type constraint")
|
self.consume(TokenType.LEFT_PAREN, "Expected '(' before type constraint")
|
||||||
constraints.append(self.constraint_expr())
|
constraints.append(self.constraint_expr())
|
||||||
self.consume(TokenType.RIGHT_PAREN, "Expected ')' after type constraint")
|
self.consume(TokenType.RIGHT_PAREN, "Expected ')' after type constraint")
|
||||||
|
|||||||
@@ -3,12 +3,15 @@ from typing import Optional
|
|||||||
from core.ast.midas import (
|
from core.ast.midas import (
|
||||||
ConstraintExpr,
|
ConstraintExpr,
|
||||||
ConstraintStmt,
|
ConstraintStmt,
|
||||||
|
Expr,
|
||||||
|
LiteralExpr,
|
||||||
OpStmt,
|
OpStmt,
|
||||||
PropertyStmt,
|
PropertyStmt,
|
||||||
Stmt,
|
Stmt,
|
||||||
TypeBodyExpr,
|
TypeBodyExpr,
|
||||||
TypeExpr,
|
TypeExpr,
|
||||||
TypeStmt,
|
TypeStmt,
|
||||||
|
WildcardExpr,
|
||||||
)
|
)
|
||||||
from lexer.token import Token, TokenType
|
from lexer.token import Token, TokenType
|
||||||
from parser.base import Parser
|
from parser.base import Parser
|
||||||
@@ -96,7 +99,9 @@ class MidasParser(Parser):
|
|||||||
constraints: list[ConstraintExpr] = []
|
constraints: list[ConstraintExpr] = []
|
||||||
|
|
||||||
while not self.is_at_end() and self.match(TokenType.PLUS):
|
while not self.is_at_end() and self.match(TokenType.PLUS):
|
||||||
|
self.consume(TokenType.LEFT_PAREN, "Expected '(' before type constraint")
|
||||||
constraints.append(self.constraint_expr())
|
constraints.append(self.constraint_expr())
|
||||||
|
self.consume(TokenType.RIGHT_PAREN, "Expected ')' after type constraint")
|
||||||
|
|
||||||
return TypeExpr(name=name, constraints=constraints)
|
return TypeExpr(name=name, constraints=constraints)
|
||||||
|
|
||||||
@@ -106,8 +111,34 @@ class MidasParser(Parser):
|
|||||||
Returns:
|
Returns:
|
||||||
ConstraintExpr: the parsed type constraint expression
|
ConstraintExpr: the parsed type constraint expression
|
||||||
"""
|
"""
|
||||||
# TODO
|
|
||||||
return ConstraintExpr()
|
left: Expr = self.constraint_value()
|
||||||
|
op: Token = self.constraint_operator()
|
||||||
|
right: Expr = self.constraint_value()
|
||||||
|
return ConstraintExpr(left=left, op=op, right=right)
|
||||||
|
|
||||||
|
def constraint_value(self) -> Expr:
|
||||||
|
if self.match(TokenType.UNDERSCORE):
|
||||||
|
return WildcardExpr(self.previous())
|
||||||
|
return self.literal()
|
||||||
|
|
||||||
|
def literal(self) -> LiteralExpr:
|
||||||
|
if self.match(TokenType.FALSE):
|
||||||
|
return LiteralExpr(False)
|
||||||
|
if self.match(TokenType.TRUE):
|
||||||
|
return LiteralExpr(True)
|
||||||
|
if self.match(TokenType.NONE):
|
||||||
|
return LiteralExpr(None)
|
||||||
|
|
||||||
|
if self.match(TokenType.NUMBER):
|
||||||
|
return LiteralExpr(self.previous().value)
|
||||||
|
|
||||||
|
raise self.error(self.peek(), "Expected literal")
|
||||||
|
|
||||||
|
def constraint_operator(self) -> Token:
|
||||||
|
if self.match(TokenType.LESS, TokenType.LESS_EQUAL, TokenType.GREATER, TokenType.GREATER_EQUAL, TokenType.EQUAL_EQUAL, TokenType.BANG_EQUAL):
|
||||||
|
return self.previous()
|
||||||
|
raise self.error(self.peek(), "Expected constraint operator")
|
||||||
|
|
||||||
def type_body_expr(self) -> TypeBodyExpr:
|
def type_body_expr(self) -> TypeBodyExpr:
|
||||||
"""Parse a type definition body
|
"""Parse a type definition body
|
||||||
|
|||||||
Reference in New Issue
Block a user