@maksbotan
21 апреля 2020 г.
0, 1, 2, 42, ...
True, False
x, y, z, ...
x * 5, x == 0
x if y else z
lambda x: 2 * x
(lambda x: 2 * x)(21)
lambda f: lambda x: f(f(x))
Числа
Логика
Переменные
Арифметика
Условия
Функции
Вызов
Выражения
42, True
2 * 4
5 * True
int
2 * 4
int
int
int
2 * 4
int
int
?
2 * a
int
?
"Контекст"
\( v \) имеет тип \( t \) в контексте \( \Gamma \)
"отношение типизации"
2 * a
(a + b) * c
v1 if c else v2
bool
t
t
t
lambda x: 2 * x
v
Если \( x : \mathrm{int} \), то \( v : \mathrm{int} \)
Расширенный контекст
(lambda x: 2 * x)(21)
def typecheck(env, expr) -> typ:
...
if expr == a * b:
if typecheck(a) == int and typecheck(b) == int:
return int
else:
raise TypeCheckError("a * b")
>>> ast.dump(ast.parse('2*4'))
Module(body=[
Expr(value=
BinOp(
left=Num(n=2),
op=Mult(),
right=Num(n=4)
)
)])
expr
class PrimType(Enum):
ty_int = "int"
ty_bool = "bool"
Type = Union[PrimType, "FunctionType"]
class FunctionType(NamedTuple):
arg: Type
res: Type
Env = Dict[str, Type]
class TypeCheckError(Exception):
pass
def typecheck(expr, env: Env) -> Type:
if isinstance(expr, ast.Num):
return PrimType.ty_int
if (isinstance(expr, ast.NameConstant)
and expr.value in (False, True)):
return PrimType.ty_bool
if isinstance(expr, ast.Name):
ty = env.get(expr.id)
if ty is not None:
return ty
if isinstance(expr, ast.BinOp):
if (isinstance(expr.op,
(ast.Add, ast.Mult, ast.Sub, ast.Div))):
ty_left = typecheck(expr.left, env)
ty_right = tyecheck(expr.right, env)
if ty_left == ty_right == PrimType.ty_int:
return PrimType.ty_int
if isinstance(expr, ast.Compare):
ty_left = typecheck(expr.left, env)
ty_right = typecheck(expr.comparators[0], env)
if ty_left == ty_right:
return PrimType.ty_bool
if isinstance(expr, ast.IfExp):
ty_compare = typecheck(expr.test, env)
ty_then = typecheck(expr.body, env)
ty_else = typecheck(expr.orelse, env)
if (ty_compare == PrimType.ty_bool
and ty_then == ty_else):
return ty_then
if isinstance(expr, ast.Lambda):
arg = expr.args.args[0]
body = expr.body
new_env = copy(env)
new_env[arg.arg] = PrimType.ty_int
body_type = typecheck(body, new_env)
return FunctionType(PrimType.ty_int, body_type)
if isinstance(expr, ast.Call):
ty_func = typecheck(expr.func, env)
ty_arg = typecheck(expr.args[0], env)
if (isinstance(ty_func, FunctionType)
and ty_func.arg == ty_arg):
return ty_func.res
$ python3 typecheck.py '2 * 2' int
$ python3 typecheck.py '(2 * 2) + 4' int
$ python3 typecheck.py 'x * 5' __main__.TypeCheckError: Name(id='x', ctx=Load())
$ python3 typecheck.py 'lambda x: 2 * x' int -> int
$ python3 typecheck.py 'lambda x: x if x > 0 else 0' int -> int
$ python3 typecheck.py 'lambda x: lambda y: x if x > 0 else y' int -> int -> int
$ python3 typecheck.py '(lambda x: x if x > 0 else 0)(5)' int
lambda x: 2 * x
: ?
\( \Gamma = \{ \} \)
infer(lambda x: 2 * x)
infer(2 * x)
\( \Gamma = \{ x : \tau_1 \} \)
infer(2)
infer(x)
\( \tau_1 \)
int
2 * x : int \( \Gamma = \{ x : \tau_1 \} \) \( C = \{ \tau_1= \mathrm{int} \} \)
lambda x: 2 * x : int \( \to \) int
class ExpressionChecker(ExpressionVisitor[Type]):
def visit_call_expr(self, e: CallExpr) -> Type:
...
def visit_int_expr(self, e: IntExpr) -> Type:
...
def visit_op_expr(self, e: OpExpr) -> Type:
...
def visit_comparison_expr(self, e: ComparisonExpr) -> Type:
...
def visit_list_expr(self, e: ListExpr) -> Type:
...
match value with
| BooleanOperator { BooleanOperator.left; operator; right } ->
...
| Call
{ callee =
{ Node.value = Name (Name.Identifier "super"); _ } as callee;
arguments
} ->
...
| Integer literal ->
...
| List elements ->
...
[]
def foo(x):
if x == 5:
return True
return x * 2
n = 5
x = 10
if n > 0:
x = x + n
else:
x = x - n