Compare commits
3
Commits
efa5454776
...
c6ead886ec
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6ead886ec
|
||
|
|
9de03bf2b5
|
||
|
|
a26b9293be
|
+13
@@ -30,6 +30,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
T = TypeVar("T")
|
T = TypeVar("T")
|
||||||
|
|
||||||
|
{preamble}
|
||||||
{sections}
|
{sections}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -57,6 +58,11 @@ IMPORTS_REGEX = re.compile(
|
|||||||
re.MULTILINE | re.DOTALL,
|
re.MULTILINE | re.DOTALL,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
PREAMBLE_REGEX = re.compile(
|
||||||
|
r"^###>\s*Preamble\s*?\n(?P<body>.*?)\n###<$",
|
||||||
|
re.MULTILINE | re.DOTALL,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def snake_case(text: str) -> str:
|
def snake_case(text: str) -> str:
|
||||||
return re.sub(r"[A-Z]", lambda c: "_" + c.group().lower(), text).lower().strip("_")
|
return re.sub(r"[A-Z]", lambda c: "_" + c.group().lower(), text).lower().strip("_")
|
||||||
@@ -88,6 +94,7 @@ def make_banner(text: str) -> str:
|
|||||||
|
|
||||||
|
|
||||||
def make_section(full_name: str, base: str, param: str, body: str) -> str:
|
def make_section(full_name: str, base: str, param: str, body: str) -> str:
|
||||||
|
print(f" Generating {full_name}")
|
||||||
visitor_methods: list[str] = []
|
visitor_methods: list[str] = []
|
||||||
classes: list[str] = []
|
classes: list[str] = []
|
||||||
definitions: list[str] = body.strip("\n").split("\n\n\n")
|
definitions: list[str] = body.strip("\n").split("\n\n\n")
|
||||||
@@ -107,6 +114,7 @@ def make_section(full_name: str, base: str, param: str, body: str) -> str:
|
|||||||
|
|
||||||
|
|
||||||
def generate(definitions_path: Path, out_path: Path):
|
def generate(definitions_path: Path, out_path: Path):
|
||||||
|
print(f"Processing generating {out_path} from {definitions_path}")
|
||||||
root_dir: Path = Path(__file__).parent.parent
|
root_dir: Path = Path(__file__).parent.parent
|
||||||
rel_path: Path = definitions_path.relative_to(root_dir)
|
rel_path: Path = definitions_path.relative_to(root_dir)
|
||||||
src: str = definitions_path.read_text()
|
src: str = definitions_path.read_text()
|
||||||
@@ -116,6 +124,10 @@ def generate(definitions_path: Path, out_path: Path):
|
|||||||
if m := IMPORTS_REGEX.search(src):
|
if m := IMPORTS_REGEX.search(src):
|
||||||
imports = m.group("body").strip("\n")
|
imports = m.group("body").strip("\n")
|
||||||
|
|
||||||
|
preamble: str = ""
|
||||||
|
if m := PREAMBLE_REGEX.search(src):
|
||||||
|
preamble = m.group("body")
|
||||||
|
|
||||||
for section_m in SECTION_REGEX.finditer(src):
|
for section_m in SECTION_REGEX.finditer(src):
|
||||||
full_name: str = section_m.group("name")
|
full_name: str = section_m.group("name")
|
||||||
base: str = section_m.group("base")
|
base: str = section_m.group("base")
|
||||||
@@ -129,6 +141,7 @@ def generate(definitions_path: Path, out_path: Path):
|
|||||||
gen_path=Path(__file__).relative_to(root_dir),
|
gen_path=Path(__file__).relative_to(root_dir),
|
||||||
),
|
),
|
||||||
imports=imports,
|
imports=imports,
|
||||||
|
preamble=preamble,
|
||||||
sections="\n\n\n".join(sections),
|
sections="\n\n\n".join(sections),
|
||||||
)
|
)
|
||||||
out_path.write_text(result)
|
out_path.write_text(result)
|
||||||
|
|||||||
+27
-8
@@ -12,25 +12,31 @@ from midas.lexer.token import Token
|
|||||||
###<
|
###<
|
||||||
|
|
||||||
|
|
||||||
###> Stmt | Statements
|
###> Preamble
|
||||||
class TypeStmt:
|
|
||||||
name: Token
|
|
||||||
params: list[Param]
|
|
||||||
type: Type
|
|
||||||
|
|
||||||
@dataclass(frozen=True, kw_only=True)
|
@dataclass(frozen=True, kw_only=True)
|
||||||
class Param:
|
class TypeParam:
|
||||||
location: Location
|
location: Location
|
||||||
name: Token
|
name: Token
|
||||||
bound: Optional[Type]
|
bound: Optional[Type]
|
||||||
|
|
||||||
|
|
||||||
|
###<
|
||||||
|
|
||||||
|
|
||||||
|
###> Stmt | Statements
|
||||||
|
class TypeStmt:
|
||||||
|
name: Token
|
||||||
|
params: list[TypeParam]
|
||||||
|
type: Type
|
||||||
|
|
||||||
|
|
||||||
class PropertyStmt:
|
class PropertyStmt:
|
||||||
name: Token
|
name: Token
|
||||||
type: Type
|
type: Type
|
||||||
|
|
||||||
|
|
||||||
class ExtendStmt:
|
class ExtendStmt:
|
||||||
|
params: list[TypeParam]
|
||||||
type: Type
|
type: Type
|
||||||
operations: list[OpStmt]
|
operations: list[OpStmt]
|
||||||
|
|
||||||
@@ -103,7 +109,7 @@ class NamedType:
|
|||||||
|
|
||||||
class GenericType:
|
class GenericType:
|
||||||
type: Type
|
type: Type
|
||||||
params: list[Type]
|
args: list[Type]
|
||||||
|
|
||||||
|
|
||||||
class ConstraintType:
|
class ConstraintType:
|
||||||
@@ -115,4 +121,17 @@ class ComplexType:
|
|||||||
properties: list[PropertyStmt]
|
properties: list[PropertyStmt]
|
||||||
|
|
||||||
|
|
||||||
|
class FunctionType:
|
||||||
|
pos_args: list[Argument]
|
||||||
|
kw_args: list[Argument]
|
||||||
|
returns: Type
|
||||||
|
|
||||||
|
@dataclass(frozen=True, kw_only=True)
|
||||||
|
class Argument:
|
||||||
|
location: Optional[Location] = None
|
||||||
|
name: Optional[Token]
|
||||||
|
type: Type
|
||||||
|
required: bool
|
||||||
|
|
||||||
|
|
||||||
###<
|
###<
|
||||||
|
|||||||
+30
-8
@@ -14,6 +14,13 @@ from midas.lexer.token import Token
|
|||||||
|
|
||||||
T = TypeVar("T")
|
T = TypeVar("T")
|
||||||
|
|
||||||
|
@dataclass(frozen=True, kw_only=True)
|
||||||
|
class TypeParam:
|
||||||
|
location: Location
|
||||||
|
name: Token
|
||||||
|
bound: Optional[Type]
|
||||||
|
|
||||||
|
|
||||||
##############
|
##############
|
||||||
# Statements #
|
# Statements #
|
||||||
##############
|
##############
|
||||||
@@ -46,15 +53,9 @@ class Stmt(ABC):
|
|||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class TypeStmt(Stmt):
|
class TypeStmt(Stmt):
|
||||||
name: Token
|
name: Token
|
||||||
params: list[Param]
|
params: list[TypeParam]
|
||||||
type: Type
|
type: Type
|
||||||
|
|
||||||
@dataclass(frozen=True, kw_only=True)
|
|
||||||
class Param:
|
|
||||||
location: Location
|
|
||||||
name: Token
|
|
||||||
bound: Optional[Type]
|
|
||||||
|
|
||||||
def accept(self, visitor: Stmt.Visitor[T]) -> T:
|
def accept(self, visitor: Stmt.Visitor[T]) -> T:
|
||||||
return visitor.visit_type_stmt(self)
|
return visitor.visit_type_stmt(self)
|
||||||
|
|
||||||
@@ -70,6 +71,7 @@ class PropertyStmt(Stmt):
|
|||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class ExtendStmt(Stmt):
|
class ExtendStmt(Stmt):
|
||||||
|
params: list[TypeParam]
|
||||||
type: Type
|
type: Type
|
||||||
operations: list[OpStmt]
|
operations: list[OpStmt]
|
||||||
|
|
||||||
@@ -231,6 +233,9 @@ class Type(ABC):
|
|||||||
@abstractmethod
|
@abstractmethod
|
||||||
def visit_complex_type(self, type: ComplexType) -> T: ...
|
def visit_complex_type(self, type: ComplexType) -> T: ...
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def visit_function_type(self, type: FunctionType) -> T: ...
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class NamedType(Type):
|
class NamedType(Type):
|
||||||
@@ -243,7 +248,7 @@ class NamedType(Type):
|
|||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class GenericType(Type):
|
class GenericType(Type):
|
||||||
type: Type
|
type: Type
|
||||||
params: list[Type]
|
args: list[Type]
|
||||||
|
|
||||||
def accept(self, visitor: Type.Visitor[T]) -> T:
|
def accept(self, visitor: Type.Visitor[T]) -> T:
|
||||||
return visitor.visit_generic_type(self)
|
return visitor.visit_generic_type(self)
|
||||||
@@ -264,3 +269,20 @@ class ComplexType(Type):
|
|||||||
|
|
||||||
def accept(self, visitor: Type.Visitor[T]) -> T:
|
def accept(self, visitor: Type.Visitor[T]) -> T:
|
||||||
return visitor.visit_complex_type(self)
|
return visitor.visit_complex_type(self)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class FunctionType(Type):
|
||||||
|
pos_args: list[Argument]
|
||||||
|
kw_args: list[Argument]
|
||||||
|
returns: Type
|
||||||
|
|
||||||
|
@dataclass(frozen=True, kw_only=True)
|
||||||
|
class Argument:
|
||||||
|
location: Optional[Location] = None
|
||||||
|
name: Optional[Token]
|
||||||
|
type: Type
|
||||||
|
required: bool
|
||||||
|
|
||||||
|
def accept(self, visitor: Type.Visitor[T]) -> T:
|
||||||
|
return visitor.visit_function_type(self)
|
||||||
|
|||||||
+75
-12
@@ -100,12 +100,12 @@ class MidasAstPrinter(
|
|||||||
self._idx = i
|
self._idx = i
|
||||||
if i == len(stmt.params) - 1:
|
if i == len(stmt.params) - 1:
|
||||||
self._mark_last()
|
self._mark_last()
|
||||||
self._print_type_stmt_param(param)
|
self._print_type_param(param)
|
||||||
self._write_line("type", last=True)
|
self._write_line("type", last=True)
|
||||||
with self._child_level(single=True):
|
with self._child_level(single=True):
|
||||||
stmt.type.accept(self)
|
stmt.type.accept(self)
|
||||||
|
|
||||||
def _print_type_stmt_param(self, param: m.TypeStmt.Param) -> None:
|
def _print_type_param(self, param: m.TypeParam) -> None:
|
||||||
self._write_line("Param")
|
self._write_line("Param")
|
||||||
with self._child_level():
|
with self._child_level():
|
||||||
self._write_line(f'name: "{param.name.lexeme}"')
|
self._write_line(f'name: "{param.name.lexeme}"')
|
||||||
@@ -122,6 +122,13 @@ class MidasAstPrinter(
|
|||||||
def visit_extend_stmt(self, stmt: m.ExtendStmt) -> None:
|
def visit_extend_stmt(self, stmt: m.ExtendStmt) -> None:
|
||||||
self._write_line("ExtendStmt")
|
self._write_line("ExtendStmt")
|
||||||
with self._child_level():
|
with self._child_level():
|
||||||
|
self._write_line("params")
|
||||||
|
with self._child_level():
|
||||||
|
for i, param in enumerate(stmt.params):
|
||||||
|
self._idx = i
|
||||||
|
if i == len(stmt.params) - 1:
|
||||||
|
self._mark_last()
|
||||||
|
self._print_type_param(param)
|
||||||
self._write_line("type")
|
self._write_line("type")
|
||||||
with self._child_level(single=True):
|
with self._child_level(single=True):
|
||||||
stmt.type.accept(self)
|
stmt.type.accept(self)
|
||||||
@@ -234,11 +241,11 @@ class MidasAstPrinter(
|
|||||||
self._write_line("type")
|
self._write_line("type")
|
||||||
with self._child_level():
|
with self._child_level():
|
||||||
type.type.accept(self)
|
type.type.accept(self)
|
||||||
self._write_line("params", last=True)
|
self._write_line("args", last=True)
|
||||||
with self._child_level():
|
with self._child_level():
|
||||||
for i, param in enumerate(type.params):
|
for i, param in enumerate(type.args):
|
||||||
self._idx = i
|
self._idx = i
|
||||||
if i == len(type.params) - 1:
|
if i == len(type.args) - 1:
|
||||||
self._mark_last()
|
self._mark_last()
|
||||||
param.accept(self)
|
param.accept(self)
|
||||||
|
|
||||||
@@ -263,6 +270,41 @@ class MidasAstPrinter(
|
|||||||
self._mark_last()
|
self._mark_last()
|
||||||
prop.accept(self)
|
prop.accept(self)
|
||||||
|
|
||||||
|
def visit_function_type(self, type: m.FunctionType) -> None:
|
||||||
|
self._write_line("FunctionType")
|
||||||
|
with self._child_level():
|
||||||
|
self._write_line("pos_args")
|
||||||
|
with self._child_level():
|
||||||
|
for i, arg in enumerate(type.pos_args):
|
||||||
|
self._idx = i
|
||||||
|
if i == len(type.pos_args) - 1:
|
||||||
|
self._mark_last()
|
||||||
|
self._print_function_arg(arg)
|
||||||
|
|
||||||
|
self._write_line("kw_args")
|
||||||
|
with self._child_level():
|
||||||
|
for i, arg in enumerate(type.kw_args):
|
||||||
|
self._idx = i
|
||||||
|
if i == len(type.kw_args) - 1:
|
||||||
|
self._mark_last()
|
||||||
|
self._print_function_arg(arg)
|
||||||
|
|
||||||
|
self._write_line("returns", last=True)
|
||||||
|
with self._child_level(single=True):
|
||||||
|
type.returns.accept(self)
|
||||||
|
|
||||||
|
def _print_function_arg(self, arg: m.FunctionType.Argument) -> None:
|
||||||
|
self._write_line("Argument")
|
||||||
|
with self._child_level():
|
||||||
|
name: str = "None"
|
||||||
|
if arg.name is not None:
|
||||||
|
name = f'"{arg.name.lexeme}"'
|
||||||
|
self._write_line(f"name: {name}")
|
||||||
|
self._write_line("type")
|
||||||
|
with self._child_level(single=True):
|
||||||
|
arg.type.accept(self)
|
||||||
|
self._write_line(f"required: {arg.required}", last=True)
|
||||||
|
|
||||||
|
|
||||||
class MidasPrinter(m.Expr.Visitor[str], m.Stmt.Visitor[str], m.Type.Visitor[str]):
|
class MidasPrinter(m.Expr.Visitor[str], m.Stmt.Visitor[str], m.Type.Visitor[str]):
|
||||||
def __init__(self, indent: int = 4):
|
def __init__(self, indent: int = 4):
|
||||||
@@ -279,14 +321,12 @@ class MidasPrinter(m.Expr.Visitor[str], m.Stmt.Visitor[str], m.Type.Visitor[str]
|
|||||||
def visit_type_stmt(self, stmt: m.TypeStmt) -> str:
|
def visit_type_stmt(self, stmt: m.TypeStmt) -> str:
|
||||||
template: str = ""
|
template: str = ""
|
||||||
if len(stmt.params) != 0:
|
if len(stmt.params) != 0:
|
||||||
params: list[str] = [
|
params: list[str] = [self._print_type_param(param) for param in stmt.params]
|
||||||
self._print_type_template_param(param) for param in stmt.params
|
|
||||||
]
|
|
||||||
template = f"[{', '.join(params)}]"
|
template = f"[{', '.join(params)}]"
|
||||||
res: str = f"type {stmt.name.lexeme}{template} = {stmt.type.accept(self)}"
|
res: str = f"type {stmt.name.lexeme}{template} = {stmt.type.accept(self)}"
|
||||||
return self.indented(res)
|
return self.indented(res)
|
||||||
|
|
||||||
def _print_type_template_param(self, param: m.TypeStmt.Param) -> str:
|
def _print_type_param(self, param: m.TypeParam) -> str:
|
||||||
res: str = param.name.lexeme
|
res: str = param.name.lexeme
|
||||||
if param.bound is not None:
|
if param.bound is not None:
|
||||||
res += "<:" + param.bound.accept(self)
|
res += "<:" + param.bound.accept(self)
|
||||||
@@ -358,9 +398,9 @@ class MidasPrinter(m.Expr.Visitor[str], m.Stmt.Visitor[str], m.Type.Visitor[str]
|
|||||||
|
|
||||||
def visit_generic_type(self, type: m.GenericType) -> str:
|
def visit_generic_type(self, type: m.GenericType) -> str:
|
||||||
res: str = type.type.accept(self)
|
res: str = type.type.accept(self)
|
||||||
if len(type.params) != 0:
|
if len(type.args) != 0:
|
||||||
params: list[str] = [param.accept(self) for param in type.params]
|
args: list[str] = [param.accept(self) for param in type.args]
|
||||||
res += f"[{', '.join(params)}]"
|
res += f"[{', '.join(args)}]"
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def visit_constraint_type(self, type: m.ConstraintType) -> str:
|
def visit_constraint_type(self, type: m.ConstraintType) -> str:
|
||||||
@@ -378,6 +418,29 @@ class MidasPrinter(m.Expr.Visitor[str], m.Stmt.Visitor[str], m.Type.Visitor[str]
|
|||||||
res += self.indented("}")
|
res += self.indented("}")
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
def visit_function_type(self, type: m.FunctionType) -> str:
|
||||||
|
pos_args: list[str] = [self._print_arg(arg) for arg in type.pos_args]
|
||||||
|
kw_args: list[str] = [self._print_arg(arg) for arg in type.pos_args]
|
||||||
|
args: list[str] = pos_args
|
||||||
|
|
||||||
|
if len(pos_args) != 0:
|
||||||
|
args.append("/")
|
||||||
|
if len(kw_args) != 0:
|
||||||
|
args.append("*")
|
||||||
|
args += kw_args
|
||||||
|
|
||||||
|
return f"({', '.join(args)}) -> {type.returns.accept(self)}"
|
||||||
|
|
||||||
|
def _print_arg(self, arg: m.FunctionType.Argument) -> str:
|
||||||
|
res: str = ""
|
||||||
|
if arg.name is not None:
|
||||||
|
res += arg.name.lexeme
|
||||||
|
res += ": "
|
||||||
|
res += arg.type.accept(self)
|
||||||
|
if not arg.required:
|
||||||
|
res += "?"
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
class PythonAstPrinter(
|
class PythonAstPrinter(
|
||||||
AstPrinter,
|
AstPrinter,
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ from midas.ast.location import Location
|
|||||||
|
|
||||||
T = TypeVar("T")
|
T = TypeVar("T")
|
||||||
|
|
||||||
|
|
||||||
####################
|
####################
|
||||||
# Type annotations #
|
# Type annotations #
|
||||||
####################
|
####################
|
||||||
|
|||||||
+43
-11
@@ -8,6 +8,7 @@ from midas.checker.reporter import FileReporter, Reporter
|
|||||||
from midas.checker.types import (
|
from midas.checker.types import (
|
||||||
AliasType,
|
AliasType,
|
||||||
ComplexType,
|
ComplexType,
|
||||||
|
Function,
|
||||||
GenericType,
|
GenericType,
|
||||||
Type,
|
Type,
|
||||||
TypeVar,
|
TypeVar,
|
||||||
@@ -64,15 +65,8 @@ class MidasTyper(m.Stmt.Visitor[None], m.Expr.Visitor[None], m.Type.Visitor[Type
|
|||||||
stmt.accept(self)
|
stmt.accept(self)
|
||||||
|
|
||||||
def visit_type_stmt(self, stmt: m.TypeStmt) -> None:
|
def visit_type_stmt(self, stmt: m.TypeStmt) -> None:
|
||||||
params: list[TypeVar] = []
|
params: list[TypeVar] = self._resolve_type_params(stmt.params)
|
||||||
for param in stmt.params:
|
|
||||||
name: str = param.name.lexeme
|
|
||||||
bound: Optional[Type] = None
|
|
||||||
if param.bound is not None:
|
|
||||||
bound = param.bound.accept(self)
|
|
||||||
var = TypeVar(name=name, bound=bound)
|
|
||||||
self._local_variables[name] = var
|
|
||||||
params.append(var)
|
|
||||||
name: str = stmt.name.lexeme
|
name: str = stmt.name.lexeme
|
||||||
type: Type = stmt.type.accept(self)
|
type: Type = stmt.type.accept(self)
|
||||||
if len(params) != 0:
|
if len(params) != 0:
|
||||||
@@ -85,6 +79,7 @@ class MidasTyper(m.Stmt.Visitor[None], m.Expr.Visitor[None], m.Type.Visitor[Type
|
|||||||
def visit_property_stmt(self, stmt: m.PropertyStmt) -> None: ...
|
def visit_property_stmt(self, stmt: m.PropertyStmt) -> None: ...
|
||||||
|
|
||||||
def visit_extend_stmt(self, stmt: m.ExtendStmt) -> None:
|
def visit_extend_stmt(self, stmt: m.ExtendStmt) -> None:
|
||||||
|
self._resolve_type_params(stmt.params)
|
||||||
base: Type = stmt.type.accept(self)
|
base: Type = stmt.type.accept(self)
|
||||||
for op in stmt.operations:
|
for op in stmt.operations:
|
||||||
right: Type = op.operand.accept(self)
|
right: Type = op.operand.accept(self)
|
||||||
@@ -122,8 +117,8 @@ class MidasTyper(m.Stmt.Visitor[None], m.Expr.Visitor[None], m.Type.Visitor[Type
|
|||||||
|
|
||||||
def visit_generic_type(self, type: m.GenericType) -> Type:
|
def visit_generic_type(self, type: m.GenericType) -> Type:
|
||||||
type_: Type = type.type.accept(self)
|
type_: Type = type.type.accept(self)
|
||||||
params: list[Type] = [param.accept(self) for param in type.params]
|
args: list[Type] = [arg.accept(self) for arg in type.args]
|
||||||
return self.types.apply_generic(type_, params)
|
return self.types.apply_generic(type_, args)
|
||||||
|
|
||||||
def visit_constraint_type(self, type: m.ConstraintType) -> Type:
|
def visit_constraint_type(self, type: m.ConstraintType) -> Type:
|
||||||
type_: Type = type.type.accept(self)
|
type_: Type = type.type.accept(self)
|
||||||
@@ -137,3 +132,40 @@ class MidasTyper(m.Stmt.Visitor[None], m.Expr.Visitor[None], m.Type.Visitor[Type
|
|||||||
prop.name.lexeme: prop.type.accept(self) for prop in type.properties
|
prop.name.lexeme: prop.type.accept(self) for prop in type.properties
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def visit_function_type(self, type: m.FunctionType) -> Type:
|
||||||
|
return Function(
|
||||||
|
name="<anonymous>",
|
||||||
|
pos_args=[
|
||||||
|
Function.Argument(
|
||||||
|
pos=i,
|
||||||
|
name=arg.name.lexeme if arg.name is not None else str(i),
|
||||||
|
type=arg.type.accept(self),
|
||||||
|
required=arg.required,
|
||||||
|
)
|
||||||
|
for i, arg in enumerate(type.pos_args)
|
||||||
|
],
|
||||||
|
args=[],
|
||||||
|
kw_args=[
|
||||||
|
Function.Argument(
|
||||||
|
pos=i,
|
||||||
|
name=arg.name.lexeme if arg.name is not None else str(i),
|
||||||
|
type=arg.type.accept(self),
|
||||||
|
required=arg.required,
|
||||||
|
)
|
||||||
|
for i, arg in enumerate(type.kw_args, start=len(type.pos_args))
|
||||||
|
],
|
||||||
|
returns=type.returns.accept(self),
|
||||||
|
)
|
||||||
|
|
||||||
|
def _resolve_type_params(self, params: list[m.TypeParam]):
|
||||||
|
vars: list[TypeVar] = []
|
||||||
|
for param in params:
|
||||||
|
name: str = param.name.lexeme
|
||||||
|
bound: Optional[Type] = None
|
||||||
|
if param.bound is not None:
|
||||||
|
bound = param.bound.accept(self)
|
||||||
|
var = TypeVar(name=name, bound=bound)
|
||||||
|
self._local_variables[name] = var
|
||||||
|
vars.append(var)
|
||||||
|
return vars
|
||||||
|
|||||||
+12
-12
@@ -250,34 +250,34 @@ class TypesRegistry:
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def apply_generic(self, type: Type, params: list[Type]) -> Type:
|
def apply_generic(self, type: Type, args: list[Type]) -> Type:
|
||||||
match type:
|
match type:
|
||||||
case AliasType(name=name, type=base):
|
case AliasType(name=name, type=base):
|
||||||
return AliasType(name=name, type=self.apply_generic(base, params))
|
return AliasType(name=name, type=self.apply_generic(base, args))
|
||||||
|
|
||||||
case GenericType(name=name, params=type_vars, body=body):
|
case GenericType(name=name, params=type_vars, body=body):
|
||||||
n_params: int = len(params)
|
n_args: int = len(args)
|
||||||
n_type_vars: int = len(type_vars)
|
n_type_vars: int = len(type_vars)
|
||||||
if n_params < n_type_vars:
|
if n_args < n_type_vars:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Missing type parameters, expected {n_type_vars} but only {n_params} provided"
|
f"Missing type arguments, expected {n_type_vars} but only {n_args} provided"
|
||||||
)
|
)
|
||||||
if n_params > n_type_vars:
|
if n_args > n_type_vars:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Too many type parameters, expected {n_type_vars} but {n_params} provided"
|
f"Too many type arguments, expected {n_type_vars} but {n_args} provided"
|
||||||
)
|
)
|
||||||
substitutions: dict[str, Type] = {}
|
substitutions: dict[str, Type] = {}
|
||||||
for param, type_var in zip(params, type_vars):
|
for arg, type_var in zip(args, type_vars):
|
||||||
if type_var.bound is not None and not self.is_subtype(
|
if type_var.bound is not None and not self.is_subtype(
|
||||||
param, type_var.bound
|
arg, type_var.bound
|
||||||
):
|
):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Type parameter {param} is not a subtype of {type_var.bound}"
|
f"Type argument {arg} is not a subtype of {type_var.bound}"
|
||||||
)
|
)
|
||||||
substitutions[type_var.name] = param
|
substitutions[type_var.name] = arg
|
||||||
return AppliedType(
|
return AppliedType(
|
||||||
name=name,
|
name=name,
|
||||||
args=params,
|
args=args,
|
||||||
body=substitute_typevars(body, substitutions),
|
body=substitute_typevars(body, substitutions),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -288,8 +288,8 @@ class MidasHighlighter(
|
|||||||
def visit_generic_type(self, type: m.GenericType) -> None:
|
def visit_generic_type(self, type: m.GenericType) -> None:
|
||||||
self.wrap(type, "generic-type")
|
self.wrap(type, "generic-type")
|
||||||
type.type.accept(self)
|
type.type.accept(self)
|
||||||
for param in type.params:
|
for arg in type.args:
|
||||||
param.accept(self)
|
arg.accept(self)
|
||||||
|
|
||||||
def visit_constraint_type(self, type: m.ConstraintType) -> None:
|
def visit_constraint_type(self, type: m.ConstraintType) -> None:
|
||||||
self.wrap(type, "constraint-type")
|
self.wrap(type, "constraint-type")
|
||||||
@@ -301,6 +301,12 @@ class MidasHighlighter(
|
|||||||
for prop in type.properties:
|
for prop in type.properties:
|
||||||
prop.accept(self)
|
prop.accept(self)
|
||||||
|
|
||||||
|
def visit_function_type(self, type: m.FunctionType) -> None:
|
||||||
|
self.wrap(type, "function")
|
||||||
|
for arg in type.pos_args + type.kw_args:
|
||||||
|
arg.type.accept(self)
|
||||||
|
type.returns.accept(self)
|
||||||
|
|
||||||
|
|
||||||
class DiagnosticsHighlighter(Highlighter):
|
class DiagnosticsHighlighter(Highlighter):
|
||||||
EXTRA_CSS_PATH: Optional[Path] = Path(__file__).parent / "hl_diagnostic.css"
|
EXTRA_CSS_PATH: Optional[Path] = Path(__file__).parent / "hl_diagnostic.css"
|
||||||
|
|||||||
@@ -50,12 +50,14 @@ class MidasLexer(Lexer):
|
|||||||
# self.add_token(TokenType.PLUS)
|
# self.add_token(TokenType.PLUS)
|
||||||
case "-":
|
case "-":
|
||||||
self.add_token(TokenType.MINUS)
|
self.add_token(TokenType.MINUS)
|
||||||
# case "*":
|
case "*":
|
||||||
# self.add_token(TokenType.STAR)
|
self.add_token(TokenType.STAR)
|
||||||
case "/" if self.match("/"):
|
case "/" if self.match("/"):
|
||||||
self.scan_comment()
|
self.scan_comment()
|
||||||
case "/" if self.match("*"):
|
case "/" if self.match("*"):
|
||||||
self.scan_comment_multiline()
|
self.scan_comment_multiline()
|
||||||
|
case "/":
|
||||||
|
self.add_token(TokenType.SLASH)
|
||||||
case "\n":
|
case "\n":
|
||||||
self.add_token(TokenType.NEWLINE)
|
self.add_token(TokenType.NEWLINE)
|
||||||
case " " | "\r" | "\t":
|
case " " | "\r" | "\t":
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ class TokenType(Enum):
|
|||||||
# Operators
|
# Operators
|
||||||
# PLUS = auto()
|
# PLUS = auto()
|
||||||
MINUS = auto()
|
MINUS = auto()
|
||||||
# STAR = auto()
|
STAR = auto()
|
||||||
# SLASH = auto()
|
SLASH = auto()
|
||||||
GREATER = auto()
|
GREATER = auto()
|
||||||
GREATER_EQUAL = auto()
|
GREATER_EQUAL = auto()
|
||||||
LESS = auto()
|
LESS = auto()
|
||||||
|
|||||||
+99
-32
@@ -7,6 +7,7 @@ from midas.ast.midas import (
|
|||||||
ConstraintType,
|
ConstraintType,
|
||||||
Expr,
|
Expr,
|
||||||
ExtendStmt,
|
ExtendStmt,
|
||||||
|
FunctionType,
|
||||||
GenericType,
|
GenericType,
|
||||||
GetExpr,
|
GetExpr,
|
||||||
GroupingExpr,
|
GroupingExpr,
|
||||||
@@ -18,12 +19,13 @@ from midas.ast.midas import (
|
|||||||
PropertyStmt,
|
PropertyStmt,
|
||||||
Stmt,
|
Stmt,
|
||||||
Type,
|
Type,
|
||||||
|
TypeParam,
|
||||||
TypeStmt,
|
TypeStmt,
|
||||||
UnaryExpr,
|
UnaryExpr,
|
||||||
VariableExpr,
|
VariableExpr,
|
||||||
WildcardExpr,
|
WildcardExpr,
|
||||||
)
|
)
|
||||||
from midas.lexer.token import Token, TokenType
|
from midas.lexer.token import KEYWORDS, Token, TokenType
|
||||||
from midas.parser.base import Parser
|
from midas.parser.base import Parser
|
||||||
from midas.parser.errors import ParsingError
|
from midas.parser.errors import ParsingError
|
||||||
|
|
||||||
@@ -107,10 +109,8 @@ class MidasParser(Parser):
|
|||||||
TypeStmt: the parsed type declaration statement
|
TypeStmt: the parsed type declaration statement
|
||||||
"""
|
"""
|
||||||
keyword: Token = self.previous()
|
keyword: Token = self.previous()
|
||||||
name: Token = self.consume(TokenType.IDENTIFIER, "Expected type name")
|
name: Token = self.consume_identifier("Expected type name")
|
||||||
params: list[TypeStmt.Param] = []
|
params: list[TypeParam] = self.type_params()
|
||||||
if self.check(TokenType.LEFT_BRACKET):
|
|
||||||
params = self.type_stmt_params()
|
|
||||||
|
|
||||||
self.consume(TokenType.EQUAL, "Expected '=' before type definition")
|
self.consume(TokenType.EQUAL, "Expected '=' before type definition")
|
||||||
|
|
||||||
@@ -123,24 +123,27 @@ class MidasParser(Parser):
|
|||||||
type=type,
|
type=type,
|
||||||
)
|
)
|
||||||
|
|
||||||
def type_stmt_params(self) -> list[TypeStmt.Param]:
|
def type_params(self) -> list[TypeParam]:
|
||||||
"""Parse a generic template expression
|
"""Parse a list of type parameters
|
||||||
|
|
||||||
A template is written `[TypeExpr]`
|
Type parameters are a comma-separated list of type variables wrapped in brackets.
|
||||||
|
Each type variable is either a simple variable, or a bounded variable written `S <: T`
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
TemplateExpr: the parsed template expression
|
list[TypeParam]: the list of type parameters, if any, or an empty list
|
||||||
"""
|
"""
|
||||||
self.consume(TokenType.LEFT_BRACKET, "Missing '[' before template expression")
|
if not self.match(TokenType.LEFT_BRACKET):
|
||||||
params: list[TypeStmt.Param] = []
|
return []
|
||||||
|
|
||||||
|
params: list[TypeParam] = []
|
||||||
while not self.is_at_end() and not self.check(TokenType.RIGHT_BRACKET):
|
while not self.is_at_end() and not self.check(TokenType.RIGHT_BRACKET):
|
||||||
name: Token = self.consume(TokenType.IDENTIFIER, "Expected type variable")
|
name: Token = self.consume_identifier("Expected type variable")
|
||||||
bound: Optional[Type] = None
|
bound: Optional[Type] = None
|
||||||
if self.match(TokenType.LESS):
|
if self.match(TokenType.LESS):
|
||||||
self.consume(TokenType.COLON, "Expected ':' after '<'")
|
self.consume(TokenType.COLON, "Expected ':' after '<'")
|
||||||
bound = self.type_expr()
|
bound = self.type_expr()
|
||||||
params.append(
|
params.append(
|
||||||
TypeStmt.Param(
|
TypeParam(
|
||||||
location=name.location_to(self.previous()),
|
location=name.location_to(self.previous()),
|
||||||
name=name,
|
name=name,
|
||||||
bound=bound,
|
bound=bound,
|
||||||
@@ -148,7 +151,7 @@ class MidasParser(Parser):
|
|||||||
)
|
)
|
||||||
if not self.match(TokenType.COMMA):
|
if not self.match(TokenType.COMMA):
|
||||||
break
|
break
|
||||||
self.consume(TokenType.RIGHT_BRACKET, "Missing ']' after template expression")
|
self.consume(TokenType.RIGHT_BRACKET, "Missing ']' after type parameters")
|
||||||
return params
|
return params
|
||||||
|
|
||||||
def type_expr(self) -> Type:
|
def type_expr(self) -> Type:
|
||||||
@@ -187,26 +190,26 @@ class MidasParser(Parser):
|
|||||||
def generic_type(self) -> Type:
|
def generic_type(self) -> Type:
|
||||||
type: Type = self.named_type()
|
type: Type = self.named_type()
|
||||||
if self.check(TokenType.LEFT_BRACKET):
|
if self.check(TokenType.LEFT_BRACKET):
|
||||||
params: list[Type] = self.type_params()
|
args: list[Type] = self.type_args()
|
||||||
return GenericType(
|
return GenericType(
|
||||||
location=Location.span(type.location, self.previous().get_location()),
|
location=Location.span(type.location, self.previous().get_location()),
|
||||||
type=type,
|
type=type,
|
||||||
params=params,
|
args=args,
|
||||||
)
|
)
|
||||||
return type
|
return type
|
||||||
|
|
||||||
def type_params(self) -> list[Type]:
|
def type_args(self) -> list[Type]:
|
||||||
params: list[Type] = []
|
args: list[Type] = []
|
||||||
self.consume(TokenType.LEFT_BRACKET, "Missing '[' before generic parameters")
|
self.consume(TokenType.LEFT_BRACKET, "Missing '[' before generic arguments")
|
||||||
while not self.is_at_end() and not self.check(TokenType.RIGHT_BRACKET):
|
while not self.is_at_end() and not self.check(TokenType.RIGHT_BRACKET):
|
||||||
params.append(self.type_expr())
|
args.append(self.type_expr())
|
||||||
if not self.match(TokenType.COMMA):
|
if not self.match(TokenType.COMMA):
|
||||||
break
|
break
|
||||||
self.consume(TokenType.RIGHT_BRACKET, "Missing ']' after generic parameters")
|
self.consume(TokenType.RIGHT_BRACKET, "Missing ']' after generic arguments")
|
||||||
return params
|
return args
|
||||||
|
|
||||||
def named_type(self) -> Type:
|
def named_type(self) -> Type:
|
||||||
name: Token = self.consume(TokenType.IDENTIFIER, "Expected type name")
|
name: Token = self.consume_identifier("Expected type name")
|
||||||
return NamedType(
|
return NamedType(
|
||||||
location=name.get_location(),
|
location=name.get_location(),
|
||||||
name=name,
|
name=name,
|
||||||
@@ -322,9 +325,7 @@ class MidasParser(Parser):
|
|||||||
"""
|
"""
|
||||||
expr: Expr = self.primary()
|
expr: Expr = self.primary()
|
||||||
while self.match(TokenType.DOT):
|
while self.match(TokenType.DOT):
|
||||||
name: Token = self.consume(
|
name: Token = self.consume_identifier("Expected property name after '.'")
|
||||||
TokenType.IDENTIFIER, "Expected property name after '.'"
|
|
||||||
)
|
|
||||||
location: Location = Location.span(expr.location, name.get_location())
|
location: Location = Location.span(expr.location, name.get_location())
|
||||||
expr = GetExpr(location=location, expr=expr, name=name)
|
expr = GetExpr(location=location, expr=expr, name=name)
|
||||||
return expr
|
return expr
|
||||||
@@ -348,7 +349,7 @@ class MidasParser(Parser):
|
|||||||
if self.match(TokenType.NUMBER):
|
if self.match(TokenType.NUMBER):
|
||||||
return LiteralExpr(location=token.get_location(), value=token.value)
|
return LiteralExpr(location=token.get_location(), value=token.value)
|
||||||
|
|
||||||
if self.match(TokenType.IDENTIFIER):
|
if self.match_identifier():
|
||||||
return VariableExpr(location=token.get_location(), name=token)
|
return VariableExpr(location=token.get_location(), name=token)
|
||||||
|
|
||||||
if self.match(TokenType.UNDERSCORE):
|
if self.match(TokenType.UNDERSCORE):
|
||||||
@@ -361,6 +362,20 @@ class MidasParser(Parser):
|
|||||||
|
|
||||||
raise self.error(self.peek(), "Expected expression")
|
raise self.error(self.peek(), "Expected expression")
|
||||||
|
|
||||||
|
def consume_identifier(self, message: str = "Expected identifier") -> Token:
|
||||||
|
if not self.match_identifier():
|
||||||
|
raise self.error(self.peek(), message)
|
||||||
|
return self.previous()
|
||||||
|
|
||||||
|
def match_identifier(self) -> bool:
|
||||||
|
return self.match(TokenType.IDENTIFIER, *KEYWORDS.values())
|
||||||
|
|
||||||
|
def check_identifier(self) -> bool:
|
||||||
|
for tt in [TokenType.IDENTIFIER, *KEYWORDS.values()]:
|
||||||
|
if self.check(tt):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def property_stmt(self) -> PropertyStmt:
|
def property_stmt(self) -> PropertyStmt:
|
||||||
"""Parse a property statement
|
"""Parse a property statement
|
||||||
|
|
||||||
@@ -369,7 +384,7 @@ class MidasParser(Parser):
|
|||||||
Returns:
|
Returns:
|
||||||
PropertyStmt: the parsed property statement
|
PropertyStmt: the parsed property statement
|
||||||
"""
|
"""
|
||||||
name: Token = self.consume(TokenType.IDENTIFIER, "Expected property name")
|
name: Token = self.consume_identifier("Expected property name")
|
||||||
self.consume(TokenType.COLON, "Expected ':' after property name")
|
self.consume(TokenType.COLON, "Expected ':' after property name")
|
||||||
type: Type = self.type_expr()
|
type: Type = self.type_expr()
|
||||||
return PropertyStmt(
|
return PropertyStmt(
|
||||||
@@ -381,12 +396,14 @@ class MidasParser(Parser):
|
|||||||
def extend_declaration(self) -> ExtendStmt:
|
def extend_declaration(self) -> ExtendStmt:
|
||||||
"""Parse an extension definition
|
"""Parse an extension definition
|
||||||
|
|
||||||
An extension is written `extend Type { operations }`
|
An extension is written `extend Type { operations }` or `extend[S <: T, U] Type { operations }`
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
ExtendStmt: the parsed extension statement
|
ExtendStmt: the parsed extension statement
|
||||||
"""
|
"""
|
||||||
keyword: Token = self.previous()
|
keyword: Token = self.previous()
|
||||||
|
params: list[TypeParam] = self.type_params()
|
||||||
|
|
||||||
type: Type = self.type_expr()
|
type: Type = self.type_expr()
|
||||||
self.consume(TokenType.LEFT_BRACE, "Expected '{' to start extend body")
|
self.consume(TokenType.LEFT_BRACE, "Expected '{' to start extend body")
|
||||||
operations: list[OpStmt] = []
|
operations: list[OpStmt] = []
|
||||||
@@ -394,7 +411,12 @@ class MidasParser(Parser):
|
|||||||
operations.append(self.op_declaration())
|
operations.append(self.op_declaration())
|
||||||
self.consume(TokenType.RIGHT_BRACE, "Unclosed extend body")
|
self.consume(TokenType.RIGHT_BRACE, "Unclosed extend body")
|
||||||
location: Location = keyword.location_to(self.previous())
|
location: Location = keyword.location_to(self.previous())
|
||||||
return ExtendStmt(location=location, type=type, operations=operations)
|
return ExtendStmt(
|
||||||
|
location=location,
|
||||||
|
params=params,
|
||||||
|
type=type,
|
||||||
|
operations=operations,
|
||||||
|
)
|
||||||
|
|
||||||
def op_declaration(self) -> OpStmt:
|
def op_declaration(self) -> OpStmt:
|
||||||
"""Parse an operation definition
|
"""Parse an operation definition
|
||||||
@@ -430,9 +452,9 @@ class MidasParser(Parser):
|
|||||||
PredicateStmt: the parsed predicate declaration statement
|
PredicateStmt: the parsed predicate declaration statement
|
||||||
"""
|
"""
|
||||||
keyword: Token = self.previous()
|
keyword: Token = self.previous()
|
||||||
name: Token = self.consume(TokenType.IDENTIFIER, "Expected predicate name")
|
name: Token = self.consume_identifier("Expected predicate name")
|
||||||
self.consume(TokenType.LEFT_PAREN, "Expected '(' before predicate subject")
|
self.consume(TokenType.LEFT_PAREN, "Expected '(' before predicate subject")
|
||||||
subject: Token = self.consume(TokenType.IDENTIFIER, "Expected subject name")
|
subject: Token = self.consume_identifier("Expected subject name")
|
||||||
self.consume(TokenType.COLON, "Expected ':' after subject name")
|
self.consume(TokenType.COLON, "Expected ':' after subject name")
|
||||||
type: Type = self.type_expr()
|
type: Type = self.type_expr()
|
||||||
self.consume(TokenType.RIGHT_PAREN, "Expected ')' after predicate subject")
|
self.consume(TokenType.RIGHT_PAREN, "Expected ')' after predicate subject")
|
||||||
@@ -445,3 +467,48 @@ class MidasParser(Parser):
|
|||||||
type=type,
|
type=type,
|
||||||
condition=condition,
|
condition=condition,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def function(self) -> FunctionType:
|
||||||
|
l_paren: Token = self.consume(
|
||||||
|
TokenType.LEFT_PAREN, "Expected '(' before function parameters"
|
||||||
|
)
|
||||||
|
pos_args: list[FunctionType.Argument] = []
|
||||||
|
kw_args: list[FunctionType.Argument] = []
|
||||||
|
|
||||||
|
positional: bool = True
|
||||||
|
while not self.is_at_end() and not self.check(TokenType.RIGHT_PAREN):
|
||||||
|
if positional and (
|
||||||
|
self.match(TokenType.STAR) or self.match(TokenType.SLASH)
|
||||||
|
):
|
||||||
|
positional = False
|
||||||
|
else:
|
||||||
|
name: Optional[Token] = None
|
||||||
|
if self.check_identifier() and self.check_next(TokenType.COLON):
|
||||||
|
name = self.advance()
|
||||||
|
self.advance()
|
||||||
|
type: Type = self.type_expr()
|
||||||
|
required: bool = self.match(TokenType.QMARK)
|
||||||
|
arg = FunctionType.Argument(
|
||||||
|
location=None,
|
||||||
|
name=name,
|
||||||
|
type=type,
|
||||||
|
required=required,
|
||||||
|
)
|
||||||
|
if positional:
|
||||||
|
pos_args.append(arg)
|
||||||
|
else:
|
||||||
|
kw_args.append(arg)
|
||||||
|
|
||||||
|
if not self.match(TokenType.COMMA):
|
||||||
|
break
|
||||||
|
self.consume(TokenType.RIGHT_PAREN, "Expected ')' after function parameters")
|
||||||
|
|
||||||
|
self.consume(TokenType.ARROW, "Expected '->' before result type")
|
||||||
|
result: Type = self.type_expr()
|
||||||
|
|
||||||
|
return FunctionType(
|
||||||
|
location=l_paren.location_to(self.previous()),
|
||||||
|
pos_args=pos_args,
|
||||||
|
kw_args=kw_args,
|
||||||
|
returns=result,
|
||||||
|
)
|
||||||
|
|||||||
@@ -2385,7 +2385,7 @@
|
|||||||
"_type": "NamedType",
|
"_type": "NamedType",
|
||||||
"name": "Difference"
|
"name": "Difference"
|
||||||
},
|
},
|
||||||
"params": [
|
"args": [
|
||||||
{
|
{
|
||||||
"_type": "NamedType",
|
"_type": "NamedType",
|
||||||
"name": "GeoLocation"
|
"name": "GeoLocation"
|
||||||
@@ -2416,7 +2416,7 @@
|
|||||||
"_type": "NamedType",
|
"_type": "NamedType",
|
||||||
"name": "Difference"
|
"name": "Difference"
|
||||||
},
|
},
|
||||||
"params": [
|
"args": [
|
||||||
{
|
{
|
||||||
"_type": "NamedType",
|
"_type": "NamedType",
|
||||||
"name": "Latitude"
|
"name": "Latitude"
|
||||||
@@ -2433,7 +2433,7 @@
|
|||||||
"_type": "NamedType",
|
"_type": "NamedType",
|
||||||
"name": "Difference"
|
"name": "Difference"
|
||||||
},
|
},
|
||||||
"params": [
|
"args": [
|
||||||
{
|
{
|
||||||
"_type": "NamedType",
|
"_type": "NamedType",
|
||||||
"name": "Longitude"
|
"name": "Longitude"
|
||||||
@@ -2464,7 +2464,7 @@
|
|||||||
"_type": "NamedType",
|
"_type": "NamedType",
|
||||||
"name": "Difference"
|
"name": "Difference"
|
||||||
},
|
},
|
||||||
"params": [
|
"args": [
|
||||||
{
|
{
|
||||||
"_type": "NamedType",
|
"_type": "NamedType",
|
||||||
"name": "Latitude"
|
"name": "Latitude"
|
||||||
@@ -2494,7 +2494,7 @@
|
|||||||
"_type": "NamedType",
|
"_type": "NamedType",
|
||||||
"name": "Difference"
|
"name": "Difference"
|
||||||
},
|
},
|
||||||
"params": [
|
"args": [
|
||||||
{
|
{
|
||||||
"_type": "NamedType",
|
"_type": "NamedType",
|
||||||
"name": "Longitude"
|
"name": "Longitude"
|
||||||
@@ -2638,7 +2638,7 @@
|
|||||||
"_type": "NamedType",
|
"_type": "NamedType",
|
||||||
"name": "Optional"
|
"name": "Optional"
|
||||||
},
|
},
|
||||||
"params": [
|
"args": [
|
||||||
{
|
{
|
||||||
"_type": "ConstraintType",
|
"_type": "ConstraintType",
|
||||||
"type": {
|
"type": {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ from midas.ast.midas import (
|
|||||||
ConstraintType,
|
ConstraintType,
|
||||||
Expr,
|
Expr,
|
||||||
ExtendStmt,
|
ExtendStmt,
|
||||||
|
FunctionType,
|
||||||
GenericType,
|
GenericType,
|
||||||
GetExpr,
|
GetExpr,
|
||||||
GroupingExpr,
|
GroupingExpr,
|
||||||
@@ -17,6 +18,7 @@ from midas.ast.midas import (
|
|||||||
PropertyStmt,
|
PropertyStmt,
|
||||||
Stmt,
|
Stmt,
|
||||||
Type,
|
Type,
|
||||||
|
TypeParam,
|
||||||
TypeStmt,
|
TypeStmt,
|
||||||
UnaryExpr,
|
UnaryExpr,
|
||||||
VariableExpr,
|
VariableExpr,
|
||||||
@@ -46,13 +48,11 @@ class MidasAstJsonSerializer(
|
|||||||
return {
|
return {
|
||||||
"_type": "TypeStmt",
|
"_type": "TypeStmt",
|
||||||
"name": stmt.name.lexeme,
|
"name": stmt.name.lexeme,
|
||||||
"params": [
|
"params": [self._serialize_type_param(param) for param in stmt.params],
|
||||||
self._serialize_type_stmt_template_param(param) for param in stmt.params
|
|
||||||
],
|
|
||||||
"type": stmt.type.accept(self),
|
"type": stmt.type.accept(self),
|
||||||
}
|
}
|
||||||
|
|
||||||
def _serialize_type_stmt_template_param(self, param: TypeStmt.Param) -> dict:
|
def _serialize_type_param(self, param: TypeParam) -> dict:
|
||||||
return {
|
return {
|
||||||
"name": param.name.lexeme,
|
"name": param.name.lexeme,
|
||||||
"bound": self._serialize_optional(param.bound),
|
"bound": self._serialize_optional(param.bound),
|
||||||
@@ -150,7 +150,7 @@ class MidasAstJsonSerializer(
|
|||||||
return {
|
return {
|
||||||
"_type": "GenericType",
|
"_type": "GenericType",
|
||||||
"type": type.type.accept(self),
|
"type": type.type.accept(self),
|
||||||
"params": self._serialize_list(type.params),
|
"args": self._serialize_list(type.args),
|
||||||
}
|
}
|
||||||
|
|
||||||
def visit_constraint_type(self, type: ConstraintType) -> dict:
|
def visit_constraint_type(self, type: ConstraintType) -> dict:
|
||||||
@@ -165,3 +165,18 @@ class MidasAstJsonSerializer(
|
|||||||
"_type": "ComplexType",
|
"_type": "ComplexType",
|
||||||
"properties": self._serialize_list(type.properties),
|
"properties": self._serialize_list(type.properties),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def visit_function_type(self, type: FunctionType) -> dict:
|
||||||
|
return {
|
||||||
|
"_type": "FunctionType",
|
||||||
|
"pos_args": [self._serialize_func_arg(arg) for arg in type.pos_args],
|
||||||
|
"kw_args": [self._serialize_func_arg(arg) for arg in type.kw_args],
|
||||||
|
"returns": type.returns.accept(self),
|
||||||
|
}
|
||||||
|
|
||||||
|
def _serialize_func_arg(self, arg: FunctionType.Argument) -> dict:
|
||||||
|
return {
|
||||||
|
"name": arg.name,
|
||||||
|
"type": arg.type.accept(self),
|
||||||
|
"required": arg.required,
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user