3 Commits
Author SHA1 Message Date
HEL 0a748a36a3 feat(types): WIP add AppliedType 2026-06-08 18:26:11 +02:00
HEL 89fdd1b47e feat(checker): WIP add lists 2026-06-08 18:25:37 +02:00
HEL 0cde53ac6e feat(types): add name to generic type 2026-06-08 18:21:40 +02:00
12 changed files with 143 additions and 7 deletions
+4
View File
@@ -139,4 +139,8 @@ class TernaryExpr:
if_false: Expr
class ListExpr:
items: list[Expr]
###<
+11
View File
@@ -626,3 +626,14 @@ class PythonAstPrinter(
self._write_line("if_false", last=True)
with self._child_level(single=True):
expr.if_false.accept(self)
def visit_list_expr(self, expr: p.ListExpr) -> None:
self._write_line("ListExpr")
with self._child_level():
self._write_line("items", last=True)
with self._child_level():
for i, item in enumerate(expr.items):
self._idx = i
if i == len(expr.items) - 1:
self._mark_last()
item.accept(self)
+11
View File
@@ -220,6 +220,9 @@ class Expr(ABC):
@abstractmethod
def visit_ternary_expr(self, expr: TernaryExpr) -> T: ...
@abstractmethod
def visit_list_expr(self, expr: ListExpr) -> T: ...
@dataclass(frozen=True)
class BinaryExpr(Expr):
@@ -312,3 +315,11 @@ class TernaryExpr(Expr):
def accept(self, visitor: Expr.Visitor[T]) -> T:
return visitor.visit_ternary_expr(self)
@dataclass(frozen=True)
class ListExpr(Expr):
items: list[Expr]
def accept(self, visitor: Expr.Visitor[T]) -> T:
return visitor.visit_list_expr(self)
+35 -1
View File
@@ -2,7 +2,15 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from midas.checker.types import BaseType, Type, UnitType
from midas.checker.types import (
BaseType,
ComplexType,
Function,
GenericType,
Type,
TypeVar,
UnitType,
)
if TYPE_CHECKING:
from midas.checker.registry import TypesRegistry
@@ -76,3 +84,29 @@ def define_builtins(reg: TypesRegistry):
op(reg, float, "__le__", int, bool) # float <= int = bool
op(reg, float, "__ge__", int, bool) # float >= int = bool
op(reg, float, "__eq__", int, bool) # float == int = bool
list = reg.define_type(
"list",
GenericType(
name="list",
params=[TypeVar(name="T", bound=None)],
body=ComplexType(
properties={
"append": Function(
name="append",
pos_args=[
Function.Argument(
pos=0,
name="object",
type=TypeVar(name="T", bound=None),
required=True,
)
],
args=[],
kw_args=[],
returns=UnitType(),
)
}
),
),
)
+5 -3
View File
@@ -73,11 +73,13 @@ class MidasTyper(m.Stmt.Visitor[None], m.Expr.Visitor[None], m.Type.Visitor[Type
var = TypeVar(name=name, bound=bound)
self._local_variables[name] = var
params.append(var)
name: str = stmt.name.lexeme
type: Type = stmt.type.accept(self)
if len(params) != 0:
type = GenericType(params=params, body=type)
name: str = stmt.name.lexeme
self.types.define_type(name, AliasType(name=name, type=type))
type = GenericType(name=name, params=params, body=type)
else:
type = AliasType(name=name, type=type)
self.types.define_type(name, type)
self._local_variables.clear()
def visit_property_stmt(self, stmt: m.PropertyStmt) -> None: ...
+39 -1
View File
@@ -499,8 +499,46 @@ class PythonTyper(
)
return UnknownType()
def visit_list_expr(self, expr: p.ListExpr) -> Type:
list_type: Type = self.types.get_type("list")
item_types: list[Type] = [self.type_of(item) for item in expr.items]
# Try to reduce types with subsumption
reduced: bool = True
keep: list[int] = list(range(len(item_types)))
while reduced:
reduced = False
for i, i1 in enumerate(keep):
type1: Type = item_types[i1]
for i2 in keep[i + 1 :]:
type2 = item_types[i2]
if self.types.is_subtype(type1, type2):
keep.remove(i1)
elif self.types.is_subtype(type2, type1):
keep.remove(i2)
else:
continue
reduced = True
break
if len(keep) == 0:
return list_type
if len(keep) == 1:
item_type: Type = item_types[keep[0]]
return self.types.apply_generic(list_type, [item_type])
self.reporter.error(
expr.location,
f"Heterogeneous list items: {[item_types[i] for i in keep]}",
)
return self.types.apply_generic(list_type, [UnknownType()])
def visit_base_type(self, node: p.BaseType) -> Type:
return self.types.get_type(node.base)
base: Type = self.types.get_type(node.base)
if node.param is not None:
param: Type = node.param.accept(self)
return self.types.apply_generic(base, [param])
return base
def visit_constraint_type(self, node: p.ConstraintType) -> Type: ...
+7 -2
View File
@@ -3,6 +3,7 @@ from typing import Optional
from midas.checker.builtins import BUILTIN_SUBTYPES
from midas.checker.types import (
AliasType,
AppliedType,
BaseType,
ComplexType,
Function,
@@ -254,7 +255,7 @@ class TypesRegistry:
case AliasType(name=name, type=base):
return AliasType(name=name, type=self.apply_generic(base, params))
case GenericType(params=type_vars, body=body):
case GenericType(name=name, params=type_vars, body=body):
n_params: int = len(params)
n_type_vars: int = len(type_vars)
if n_params < n_type_vars:
@@ -274,7 +275,11 @@ class TypesRegistry:
f"Type parameter {param} is not a subtype of {type_var.bound}"
)
substitutions[type_var.name] = param
return substitute_typevars(body, substitutions)
return AppliedType(
name=name,
args=params,
body=substitute_typevars(body, substitutions),
)
case _:
raise ValueError(f"{type} is not a generic type")
+4
View File
@@ -180,3 +180,7 @@ class Resolver(p.Stmt.Visitor[None], p.Expr.Visitor[None]):
self.resolve(expr.test)
self.resolve(expr.if_true)
self.resolve(expr.if_false)
def visit_list_expr(self, expr: p.ListExpr) -> None:
for item in expr.items:
self.resolve(item)
+9
View File
@@ -66,10 +66,18 @@ class TypeVar:
@dataclass(frozen=True, kw_only=True)
class GenericType:
name: str
params: list[TypeVar]
body: Type
@dataclass(frozen=True, kw_only=True)
class AppliedType:
name: str
args: list[Type]
body: Type
def substitute_typevars(type: Type, substitutions: dict[str, Type]) -> Type:
def sub_argument(arg: Function.Argument):
return Function.Argument(
@@ -137,4 +145,5 @@ Type = (
| ComplexType
| TypeVar
| GenericType
| AppliedType
)
+4
View File
@@ -214,6 +214,10 @@ class PythonHighlighter(
def visit_ternary_expr(self, expr: p.TernaryExpr) -> None: ...
def visit_list_expr(self, expr: p.ListExpr) -> None:
for item in expr.items:
item.accept(self)
class MidasHighlighter(
Highlighter, m.Stmt.Visitor[None], m.Expr.Visitor[None], m.Type.Visitor[None]
+7
View File
@@ -17,6 +17,7 @@ from midas.ast.python import (
Function,
GetExpr,
IfStmt,
ListExpr,
LiteralExpr,
LogicalExpr,
MidasType,
@@ -416,6 +417,12 @@ class PythonParser:
case ast.Name(id=name):
return VariableExpr(location=location, name=name)
case ast.List(elts=items):
return ListExpr(
location=location,
items=[self.parse_expr(item) for item in items],
)
case _:
raise UnsupportedSyntaxError(node)
+7
View File
@@ -16,6 +16,7 @@ from midas.ast.python import (
Function,
GetExpr,
IfStmt,
ListExpr,
LiteralExpr,
LogicalExpr,
MidasType,
@@ -245,3 +246,9 @@ class PythonAstJsonSerializer(
"if_true": expr.if_true.accept(self),
"if_false": expr.if_false.accept(self),
}
def visit_list_expr(self, expr: ListExpr) -> dict:
return {
"_type": "ListExpr",
"items": [item.accept(self) for item in expr.items],
}