feat(parser): parse annotation type constraints

This commit is contained in:
2026-05-18 11:27:52 +02:00
parent be50a8db35
commit 8bc0918517
3 changed files with 90 additions and 7 deletions

View File

@@ -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
@@ -34,6 +34,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: ...
@@ -47,6 +53,22 @@ class Expr(ABC):
def visit_schema_element_expr(self, expr: SchemaElementExpr) -> T: ... def visit_schema_element_expr(self, expr: SchemaElementExpr) -> 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
@@ -58,6 +80,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)

View File

@@ -105,7 +105,18 @@ class AnnotationAstPrinter(AstPrinter, a.Expr.Visitor[None], a.Stmt.Visitor[None
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")
# TODO 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_schema_expr(self, expr: a.SchemaExpr): def visit_schema_expr(self, expr: a.SchemaExpr):
self._write_line("SchemaExpr") self._write_line("SchemaExpr")
@@ -123,6 +134,14 @@ class AnnotationAstPrinter(AstPrinter, a.Expr.Visitor[None], a.Stmt.Visitor[None
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:
self._write_line("WildcardExpr")
def visit_literal_expr(self, expr: a.LiteralExpr) -> None:
self._write_line("LiteralExpr")
with self._child_level():
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]):
def print(self, expr: a.Expr | a.Stmt): def print(self, expr: a.Expr | a.Stmt):
@@ -141,8 +160,12 @@ class AnnotationPrinter(a.Expr.Visitor[str], a.Stmt.Visitor[str]):
return " + ".join(parts) return " + ".join(parts)
def visit_constraint_expr(self, expr: a.ConstraintExpr) -> str: def visit_constraint_expr(self, expr: a.ConstraintExpr) -> str:
# TODO parts: list[str] = [
return "" expr.left.accept(self),
expr.op.lexeme,
expr.right.accept(self)
]
return " ".join(parts)
def visit_schema_expr(self, expr: a.SchemaExpr) -> str: def visit_schema_expr(self, expr: a.SchemaExpr) -> str:
res: str = expr.left.lexeme res: str = expr.left.lexeme
@@ -161,6 +184,12 @@ class AnnotationPrinter(a.Expr.Visitor[str], a.Stmt.Visitor[str]):
parts.append(expr.type.accept(self)) parts.append(expr.type.accept(self))
return ": ".join(parts) return ": ".join(parts)
def visit_wildcard_expr(self, expr: a.WildcardExpr) -> str:
return "_"
def visit_literal_expr(self, expr: a.LiteralExpr) -> str:
return str(expr.value)
class MidasAstPrinter(AstPrinter, m.Expr.Visitor[None], m.Stmt.Visitor[None]): class MidasAstPrinter(AstPrinter, m.Expr.Visitor[None], m.Stmt.Visitor[None]):
def visit_type_stmt(self, stmt: m.TypeStmt): def visit_type_stmt(self, stmt: m.TypeStmt):

View File

@@ -4,10 +4,12 @@ from core.ast.annotations import (
AnnotationStmt, AnnotationStmt,
ConstraintExpr, ConstraintExpr,
Expr, Expr,
LiteralExpr,
SchemaElementExpr, SchemaElementExpr,
SchemaExpr, SchemaExpr,
Stmt, Stmt,
TypeExpr, TypeExpr,
WildcardExpr,
) )
from lexer.token import Token, TokenType from lexer.token import Token, TokenType
from parser.base import Parser from parser.base import Parser
@@ -80,8 +82,34 @@ class AnnotationParser(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 schema(self) -> SchemaExpr: def schema(self) -> SchemaExpr:
"""Parse a schema definition """Parse a schema definition