3 Commits
Author SHA1 Message Date
HEL 4f5967a151 feat(checker): add top type (Any) 2026-06-13 12:45:40 +02:00
HEL 2a714a1021 fix: extend example of complex types 2026-06-13 12:40:26 +02:00
HEL dafe0b471a feat(checker): define members on builtin types 2026-06-13 12:39:46 +02:00
7 changed files with 205 additions and 66 deletions
@@ -1,11 +1,21 @@
type Meter = float
extend Meter {
op __add__(Meter) -> Meter
op __sub__(Meter) -> Meter
def __add__: fn(Meter) -> Meter
def __sub__: fn(Meter) -> Meter
}
type Coordinate = {
x: Meter
y: Meter
type Coordinate = object
extend Coordinate {
prop x: Meter
prop y: Meter
}
type Difference[T <: float] = T
type MeterDifference = Difference[Meter]
type CompDiff[T <: float] = {
prop d1: Difference[T]
prop d2: Difference[T]
}
@@ -1,5 +1,6 @@
# type: ignore
# ruff: disable [F821]
p1: Coordinate
p2: Coordinate
@@ -9,6 +10,21 @@ diff_y = p2.y - p1.y
dist = diff_x + diff_y
p2.x += cast(Meter, 1)
p2.y = True
p2.z = 3
p2.x.a = 3
p2.y = True # invalid, wrong type
p2.z = 3 # invalid, no property 'z' on Coordinate
p2.x.a = 3 # invalid, no properties on Meter
foo: list[float] = []
append = foo.append
foo.append("") # invalid, must be float
foo.append(2)
append(True) # invalid, must be float
append(2)
bar: list[list[Meter]]
bar.append([p2.x])
foo2 = foo + foo
+152
View File
@@ -0,0 +1,152 @@
extend float {
def hex: fn() -> str
def is_integer: fn() -> bool
prop real: float
prop imag: float
def conjugate: fn() -> float
def __add__: fn(value: float, /) -> float
def __sub__: fn(value: float, /) -> float
def __mul__: fn(value: float, /) -> float
def __floordiv__: fn(value: float, /) -> float
def __truediv__: fn(value: float, /) -> float
def __mod__: fn(value: float, /) -> float
// def __divmod__: fn(value: float, /) -> tuple[float, float]
def __pow__: fn(value: int, /) -> float
// positive __value -> float; negative __value -> complex
// return type must be Any as `float | complex` causes too many false-positive errors
def __pow__: fn(value: float, /) -> Any
def __radd__: fn(value: float, /) -> float
def __rsub__: fn(value: float, /) -> float
def __rmul__: fn(value: float, /) -> float
def __rfloordiv__: fn(value: float, /) -> float
def __rtruediv__: fn(value: float, /) -> float
def __rmod__: fn(value: float, /) -> float
// def __rdivmod__: fn(value: float, /) -> tuple[float, float]
// def __rpow__: fn(value: _PositiveInteger, mod: None = None, /) -> float
// def __rpow__: fn(value: _NegativeInteger, mod: None = None, /) -> complex
// Returning `complex` for the general case gives too many false-positive errors.
// def __rpow__: fn(value: float, mod: None = None, /) -> Any
// def __getnewargs__: fn() -> tuple[float]
def __trunc__: fn() -> int
def __ceil__: fn() -> int
def __floor__: fn() -> int
def __round__: fn(ndigits: None?, /) -> int
def __round__: fn(ndigits: int, /) -> float
def __eq__: fn(value: object, /) -> bool
def __ne__: fn(value: object, /) -> bool
def __lt__: fn(value: float, /) -> bool
def __le__: fn(value: float, /) -> bool
def __gt__: fn(value: float, /) -> bool
def __ge__: fn(value: float, /) -> bool
def __neg__: fn() -> float
def __pos__: fn() -> float
def __int__: fn() -> int
def __float__: fn() -> float
def __abs__: fn() -> float
def __hash__: fn() -> int
def __bool__: fn() -> bool
def __format__: fn(format_spec: str, /) -> str
}
extend int {
prop real: int
prop imag: int
prop numerator: int
prop denominator: int
def conjugate: fn() -> int
def bit_length: fn() -> int
def bit_count: fn() -> int
def to_bytes: fn(length: int?, byteorder: str?, *, signed: bool?) -> bytes
def __add__: fn(value: int, /) -> int
def __sub__: fn(value: int, /) -> int
def __mul__: fn(value: int, /) -> int
def __floordiv__: fn(value: int, /) -> int
def __truediv__: fn(value: int, /) -> float
def __mod__: fn(value: int, /) -> int
// def __divmod__: fn(value: int, /) -> tuple[int, int]
def __radd__: fn(value: int, /) -> int
def __rsub__: fn(value: int, /) -> int
def __rmul__: fn(value: int, /) -> int
def __rfloordiv__: fn(value: int, /) -> int
def __rtruediv__: fn(value: int, /) -> float
def __rmod__: fn(value: int, /) -> int
// def __rdivmod__: fn(value: int, /) -> tuple[int, int]
def __pow__: fn(value: int, /) -> int
// def __pow__: fn(value: _PositiveInteger, mod: None = None, /) -> int
// def __pow__: fn(value: _NegativeInteger, mod: None = None, /) -> float
// positive __value -> int; negative __value -> float
// return type must be Any as `int | float` causes too many false-positive errors
// def __pow__: fn(value: int, mod: None = None, /) -> Any
// def __pow__: fn(value: int, mod: int, /) -> int
def __rpow__: fn(value: int, /) -> Any
def __and__: fn(value: int, /) -> int
def __or__: fn(value: int, /) -> int
def __xor__: fn(value: int, /) -> int
def __lshift__: fn(value: int, /) -> int
def __rshift__: fn(value: int, /) -> int
def __rand__: fn(value: int, /) -> int
def __ror__: fn(value: int, /) -> int
def __rxor__: fn(value: int, /) -> int
def __rlshift__: fn(value: int, /) -> int
def __rrshift__: fn(value: int, /) -> int
def __neg__: fn() -> int
def __pos__: fn() -> int
def __invert__: fn() -> int
def __trunc__: fn() -> int
def __ceil__: fn() -> int
def __floor__: fn() -> int
def __round__: fn(ndigits: None?, /) -> int
def __round__: fn(ndigits: int, /) -> int
// def __getnewargs__: fn() -> tuple[int]
def __eq__: fn(value: object, /) -> bool
def __ne__: fn(value: object, /) -> bool
def __lt__: fn(value: int, /) -> bool
def __le__: fn(value: int, /) -> bool
def __gt__: fn(value: int, /) -> bool
def __ge__: fn(value: int, /) -> bool
def __float__: fn() -> float
def __int__: fn() -> int
def __abs__: fn() -> int
def __hash__: fn() -> int
def __bool__: fn() -> bool
def __index__: fn() -> int
def __format__: fn(format_spec: str, /) -> str
}
extend list[T] {
def copy: fn () -> list[T]
def append: fn (object: T, /) -> None
def extend: fn (iterable: list[T], /) -> None
def pop: fn (index: int?, /) -> T
def index: fn (value: T, start: int?, stop: int?, /) -> int
def count: fn (value: T, /) -> int
def insert: fn (index: int, object: T, /) -> None
def remove: fn (value: T, /) -> None
def sort: fn (*, reverse: bool?) -> None
def __len__: fn () -> int
// def __iter__: fn () -> Iterator[T]
def __getitem__: fn (i: int, /) -> T
//__getitem__: fn (s: slice, /) -> list[T]
def __setitem__: fn (key: int, value: T, /) -> None
//__setitem__: fn (key: slice, value: list[T], /) -> None
def __delitem__: fn (key: int, /) -> None
// def __delitem__: fn (key: slice, /) -> None
// def __add__: fn[S <: T] (value: list[S], /) -> list[T]
def __add__: fn (value: list[T], /) -> list[T]
def __iadd__: fn (value: list[T], /) -> list[T]
def __mul__: fn (value: int, /) -> list[T]
def __rmul__: fn (value: int, /) -> list[T]
def __imul__: fn (value: int, /) -> list[T]
def __contains__: fn (key: object, /) -> bool
// def __reversed__: fn (self) -> Iterator[_T]
def __gt__: fn (value: list[T], /) -> bool
def __ge__: fn (value: list[T], /) -> bool
def __lt__: fn (value: list[T], /) -> bool
def __le__: fn (value: list[T], /) -> bool
def __eq__: fn (value: object, /) -> bool
prop __doc__: str
}
+4 -57
View File
@@ -4,9 +4,8 @@ from typing import TYPE_CHECKING
from midas.checker.types import (
BaseType,
ComplexType,
Function,
GenericType,
TopType,
Type,
TypeVar,
UnitType,
@@ -42,71 +41,19 @@ def basic_op(reg: TypesRegistry, type: Type, op: str):
def define_builtins(reg: TypesRegistry):
"""Define builtin types and operations"""
any = reg.define_type("Any", TopType())
unit = reg.define_type("None", UnitType())
object = reg.define_type("object", BaseType(name="object"))
bool = reg.define_type("bool", BaseType(name="bool"))
int = reg.define_type("int", BaseType(name="int"))
float = reg.define_type("float", BaseType(name="float"))
str = reg.define_type("str", BaseType(name="str"))
basic_op(reg, int, "__add__") # int + int = int
basic_op(reg, int, "__sub__") # int - int = int
basic_op(reg, int, "__mul__") # int * int = int
basic_op(reg, int, "__pow__") # int ** int = int
basic_op(reg, int, "__mod__") # int % int = int
basic_op(reg, int, "__and__") # int & int = int
basic_op(reg, int, "__or__") # int | int = int
basic_op(reg, int, "__xor__") # int ^ int = int
op(reg, int, "__lt__", int, bool) # int < int = bool
op(reg, int, "__gt__", int, bool) # int > int = bool
op(reg, int, "__le__", int, bool) # int <= int = bool
op(reg, int, "__ge__", int, bool) # int >= int = bool
op(reg, int, "__eq__", int, bool) # int == int = bool
basic_op(reg, float, "__add__") # float + float = float
basic_op(reg, float, "__sub__") # float - float = float
basic_op(reg, float, "__mul__") # float * float = float
basic_op(reg, float, "__truediv__") # float / float = float
op(reg, float, "__lt__", float, bool) # float < float = bool
op(reg, float, "__gt__", float, bool) # float > float = bool
op(reg, float, "__le__", float, bool) # float <= float = bool
op(reg, float, "__ge__", float, bool) # float >= float = bool
op(reg, float, "__eq__", float, bool) # float == float = bool
basic_op(reg, str, "__add__") # str + str = str
op(reg, str, "__eq__", str, bool) # str == str = bool
op(reg, int, "__lt__", float, bool) # int < float = bool
op(reg, int, "__gt__", float, bool) # int > float = bool
op(reg, int, "__le__", float, bool) # int <= float = bool
op(reg, int, "__ge__", float, bool) # int >= float = bool
op(reg, int, "__eq__", float, bool) # int == float = bool
op(reg, float, "__lt__", int, bool) # float < int = bool
op(reg, float, "__gt__", int, bool) # float > int = bool
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(),
)
}
),
body=BaseType(name="list"),
),
)
+3
View File
@@ -1,4 +1,5 @@
import logging
from pathlib import Path
from typing import Optional
import midas.ast.midas as m
@@ -33,6 +34,8 @@ class MidasTyper(m.Stmt.Visitor[None], m.Expr.Visitor[None], m.Type.Visitor[Type
self._current_name: Optional[str] = None
define_builtins(self.types)
builtins_path: Path = (Path(__file__).parent / "builtins.midas").resolve()
self.process(builtins_path.read_text(), str(builtins_path))
def process(self, source: str, path: Optional[str]):
self.reporter = self.reporter.for_file(path)
+4
View File
@@ -12,6 +12,7 @@ from midas.checker.types import (
GenericType,
Operation,
OverloadedFunction,
TopType,
Type,
TypeVar,
UnknownType,
@@ -155,6 +156,9 @@ class TypesRegistry:
return True
match (type1, type2):
case (_, TopType()):
return True
case (AliasType(type=base1), _):
return self.is_subtype(base1, type2)
+8 -1
View File
@@ -4,6 +4,12 @@ from dataclasses import dataclass
from typing import Optional
@dataclass(frozen=True, kw_only=True)
class TopType:
def __str__(self) -> str:
return "Any"
@dataclass(frozen=True, kw_only=True)
class BaseType:
name: str
@@ -222,7 +228,8 @@ def unfold_type(type: Type) -> Type:
Type = (
BaseType
TopType
| BaseType
| AliasType
| UnknownType
| UnitType