feat(parser): add frame type to midas syntax
This commit is contained in:
10
gen/midas.py
10
gen/midas.py
@@ -157,4 +157,14 @@ class FunctionType:
|
||||
required: bool
|
||||
|
||||
|
||||
class FrameType:
|
||||
columns: list[Column]
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class Column:
|
||||
location: Optional[Location] = None
|
||||
name: Token
|
||||
type: Type
|
||||
|
||||
|
||||
###<
|
||||
|
||||
@@ -265,6 +265,9 @@ class Type(ABC):
|
||||
@abstractmethod
|
||||
def visit_function_type(self, type: FunctionType) -> T: ...
|
||||
|
||||
@abstractmethod
|
||||
def visit_frame_type(self, type: FrameType) -> T: ...
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class NamedType(Type):
|
||||
@@ -323,3 +326,17 @@ class FunctionType(Type):
|
||||
|
||||
def accept(self, visitor: Type.Visitor[T]) -> T:
|
||||
return visitor.visit_function_type(self)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class FrameType(Type):
|
||||
columns: list[Column]
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class Column:
|
||||
location: Optional[Location] = None
|
||||
name: Token
|
||||
type: Type
|
||||
|
||||
def accept(self, visitor: Type.Visitor[T]) -> T:
|
||||
return visitor.visit_frame_type(self)
|
||||
|
||||
@@ -358,6 +358,25 @@ class MidasAstPrinter(
|
||||
arg.type.accept(self)
|
||||
self._write_line(f"required: {arg.required}", last=True)
|
||||
|
||||
def visit_frame_type(self, type: m.FrameType) -> None:
|
||||
self._write_line("FrameType")
|
||||
with self._child_level(single=True):
|
||||
self._write_line("columns")
|
||||
with self._child_level():
|
||||
for i, column in enumerate(type.columns):
|
||||
self._idx = i
|
||||
if i == len(type.columns) - 1:
|
||||
self._mark_last()
|
||||
self._print_frame_column(column)
|
||||
|
||||
def _print_frame_column(self, column: m.FrameType.Column) -> None:
|
||||
self._write_line("Column")
|
||||
with self._child_level():
|
||||
self._write_line(f'name: "{column.name.lexeme}"')
|
||||
self._write_line("type")
|
||||
with self._child_level(single=True):
|
||||
column.type.accept(self)
|
||||
|
||||
|
||||
class MidasPrinter(m.Expr.Visitor[str], m.Stmt.Visitor[str], m.Type.Visitor[str]):
|
||||
def __init__(self, indent: int = 4):
|
||||
@@ -513,6 +532,23 @@ class MidasPrinter(m.Expr.Visitor[str], m.Stmt.Visitor[str], m.Type.Visitor[str]
|
||||
res += "?"
|
||||
return res
|
||||
|
||||
def visit_frame_type(self, type: m.FrameType) -> str:
|
||||
res: str = self.indented("Frame[")
|
||||
if len(type.columns) != 0:
|
||||
res += "\n"
|
||||
self.level += 1
|
||||
columns: list[str] = []
|
||||
for column in type.columns:
|
||||
columns.append(self.indented(self._print_frame_column(column)))
|
||||
res += ",\n".join(columns)
|
||||
self.level -= 1
|
||||
res += "\n"
|
||||
res += "]"
|
||||
return res
|
||||
|
||||
def _print_frame_column(self, column: m.FrameType.Column) -> str:
|
||||
return f"{column.name.lexeme}: {column.type.accept(self)}"
|
||||
|
||||
|
||||
class PythonAstPrinter(
|
||||
AstPrinter,
|
||||
|
||||
@@ -10,6 +10,7 @@ from midas.ast.midas import (
|
||||
Expr,
|
||||
ExtendStmt,
|
||||
ExtensionType,
|
||||
FrameType,
|
||||
FunctionType,
|
||||
GenericType,
|
||||
GetExpr,
|
||||
@@ -226,8 +227,10 @@ class MidasParser(Parser):
|
||||
return self.generic_type()
|
||||
|
||||
def generic_type(self) -> Type:
|
||||
type: Type = self.named_type()
|
||||
type: NamedType = self.named_type()
|
||||
if self.check(TokenType.LEFT_BRACKET):
|
||||
if type.name.lexeme == "Frame":
|
||||
return self.frame_type()
|
||||
args: list[Type] = self.type_args()
|
||||
return GenericType(
|
||||
location=Location.span(type.location, self.previous().get_location()),
|
||||
@@ -246,7 +249,7 @@ class MidasParser(Parser):
|
||||
self.consume(TokenType.RIGHT_BRACKET, "Missing ']' after generic arguments")
|
||||
return args
|
||||
|
||||
def named_type(self) -> Type:
|
||||
def named_type(self) -> NamedType:
|
||||
name: Token = self.consume_identifier("Expected type name")
|
||||
return NamedType(
|
||||
location=name.get_location(),
|
||||
@@ -281,6 +284,32 @@ class MidasParser(Parser):
|
||||
members=members,
|
||||
)
|
||||
|
||||
def frame_type(self) -> FrameType:
|
||||
keyword: Token = self.previous()
|
||||
self.consume(TokenType.LEFT_BRACKET, "Expected '[' to start frame schema")
|
||||
|
||||
columns: list[FrameType.Column] = []
|
||||
while not self.check(TokenType.RIGHT_BRACKET) and not self.is_at_end():
|
||||
name: Token = self.advance()
|
||||
self.consume(TokenType.COLON, "Expected ':' between column name and type")
|
||||
type: Type = self.type_expr()
|
||||
columns.append(
|
||||
FrameType.Column(
|
||||
location=name.location_to(self.previous()),
|
||||
name=name,
|
||||
type=type,
|
||||
)
|
||||
)
|
||||
if not self.match(TokenType.COMMA):
|
||||
break
|
||||
|
||||
self.consume(TokenType.RIGHT_BRACKET, "Unclosed frame schema")
|
||||
|
||||
return FrameType(
|
||||
location=keyword.location_to(self.previous()),
|
||||
columns=columns,
|
||||
)
|
||||
|
||||
def constraint(self) -> Expr:
|
||||
"""Parse a constraint
|
||||
|
||||
|
||||
Reference in New Issue
Block a user