// File: pfnet-research[P]pml/src/typechecker.cpp
// Function: typechecker::typecheck(ast::expr_t const&, environment_t<ast::expr_t> const&)
bool typecheck(ast::expr_t const& expr, env_t const& env) {
    using namespace ast;
    switch (expr.kind()) {
    case expr_kind_t::LetFun:
    case expr_kind_t::App:
        throw std::runtime_error{"unimplemented yet"};
    case expr_kind_t::Typed:
        return model_checking(
                *add_bindings(cast<typed_expr_t>(expr).expr, env),
                cast<typed_expr_t>(expr).type);
    case expr_kind_t::Let: {
        auto init = cast<let_expr_t>(expr).init;
        if (!typecheck(*init, env))
            return false;
        std::string name = cast<let_expr_t>(expr).name;
        auto new_env = env.append(name, init);
        auto body = cast<let_expr_t>(expr).body;
        return typecheck(*body, new_env);
        }
    case expr_kind_t::If:
        return
            typecheck(*cast<if_expr_t>(expr).cond_expr, env) &&
            typecheck(*cast<if_expr_t>(expr).true_expr, env) &&
            typecheck(*cast<if_expr_t>(expr).false_expr, env);
    case expr_kind_t::Neg:
        return typecheck(*cast<neg_expr_t>(expr).inner, env);
    case expr_kind_t::Add: case expr_kind_t::Sub:
    case expr_kind_t::Mul: case expr_kind_t::Div:
    case expr_kind_t::Eq: case expr_kind_t::Neq:
    case expr_kind_t::Leq: case expr_kind_t::Geq:
    case expr_kind_t::And: case expr_kind_t::Or:
        return
            typecheck(*cast<binop_expr_t>(expr).lhs, env) &&
            typecheck(*cast<binop_expr_t>(expr).rhs, env);
    case expr_kind_t::Int: case expr_kind_t::Bool: case expr_kind_t::Fun:
    case expr_kind_t::Rand: case expr_kind_t::Var:
        return true; // primitives
    }
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0x100, %rsp            # imm = 0x100
movq %rdi, -0x10(%rbp)
movq %rsi, -0x18(%rbp)
movq -0x10(%rbp), %rdi
movq (%rdi), %rax
movq (%rax), %rax
callq *%rax
movl %eax, %ecx
movq %rcx, -0xa0(%rbp)
subl $0x14, %eax
ja 0x9770c
movq -0xa0(%rbp), %rax
leaq 0xad1d(%rip), %rcx      # 0xa205c
movslq (%rcx,%rax,4), %rax
addq %rcx, %rax
jmpq *%rax
movl $0x10, %edi
callq 0xa1d0
movq %rax, %rdi
movq %rdi, %rax
movq %rax, -0xa8(%rbp)
leaq 0xae4b(%rip), %rsi      # 0xa21b1
callq 0xa160
jmp 0x9736d
movq -0xa8(%rbp), %rdi
movq 0x4fc75(%rip), %rsi     # 0xe6ff0
movq 0x4fbfe(%rip), %rdx     # 0xe6f80
callq 0xa610
movq -0xa8(%rbp), %rdi
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x20(%rbp)
movl %eax, -0x24(%rbp)
callq 0xa2c0
jmp 0x9771c
movq -0x10(%rbp), %rdi
callq 0x97730
movq %rax, %rsi
addq $0x8, %rsi
movq -0x18(%rbp), %rdx
leaq -0x38(%rbp), %rdi
movq %rdi, -0xc0(%rbp)
callq 0x97190
movq -0xc0(%rbp), %rdi
callq 0x13020
movq %rax, -0xb8(%rbp)
movq -0x10(%rbp), %rdi
callq 0x97730
movq %rax, -0xb0(%rbp)
jmp 0x973ed
movq -0xb8(%rbp), %rdi
movq -0xb0(%rbp), %rsi
addq $0x18, %rsi
callq 0x96f80
movb %al, -0xc1(%rbp)
jmp 0x9740c
movb -0xc1(%rbp), %al
andb $0x1, %al
movb %al, -0x1(%rbp)
leaq -0x38(%rbp), %rdi
callq 0x13080
jmp 0x9770e
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x20(%rbp)
movl %eax, -0x24(%rbp)
leaq -0x38(%rbp), %rdi
callq 0x13080
jmp 0x9771c
movq -0x10(%rbp), %rdi
callq 0x97770
movq %rax, %rsi
addq $0x28, %rsi
leaq -0x48(%rbp), %rdi
movq %rdi, -0xd0(%rbp)
callq 0x13050
movq -0xd0(%rbp), %rdi
callq 0x13020
movq %rax, %rdi
movq -0x18(%rbp), %rsi
callq 0x97300
movb %al, -0xc2(%rbp)
jmp 0x9747f
movb -0xc2(%rbp), %al
testb $0x1, %al
jne 0x974ac
jmp 0x9748b
movb $0x0, -0x1(%rbp)
movl $0x1, -0x4c(%rbp)
jmp 0x975c1
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x20(%rbp)
movl %eax, -0x24(%rbp)
jmp 0x975cf
movq -0x10(%rbp), %rdi
callq 0x97770
movq %rax, -0xd8(%rbp)
jmp 0x974be
movq -0xd8(%rbp), %rsi
addq $0x8, %rsi
leaq -0x70(%rbp), %rdi
callq 0xa190
jmp 0x974d4
movq -0x18(%rbp), %rsi
leaq -0x88(%rbp), %rdi
leaq -0x70(%rbp), %rdx
leaq -0x48(%rbp), %rcx
callq 0x130a0
jmp 0x974ee
movq -0x10(%rbp), %rdi
callq 0x97770
movq %rax, -0xe0(%rbp)
jmp 0x97500
movq -0xe0(%rbp), %rsi
addq $0x38, %rsi
leaq -0x98(%rbp), %rdi
movq %rdi, -0xf0(%rbp)
callq 0x13050
movq -0xf0(%rbp), %rdi
callq 0x13020
movq %rax, %rdi
leaq -0x88(%rbp), %rsi
callq 0x97300
movb %al, -0xe1(%rbp)
jmp 0x97541
movb -0xe1(%rbp), %al
andb $0x1, %al
movb %al, -0x1(%rbp)
movl $0x1, -0x4c(%rbp)
leaq -0x98(%rbp), %rdi
callq 0x13080
leaq -0x88(%rbp), %rdi
callq 0x13170
leaq -0x70(%rbp), %rdi
callq 0xa240
jmp 0x975c1
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x20(%rbp)
movl %eax, -0x24(%rbp)
jmp 0x975b6
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x20(%rbp)
movl %eax, -0x24(%rbp)
jmp 0x975aa
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x20(%rbp)
movl %eax, -0x24(%rbp)
leaq -0x98(%rbp), %rdi
callq 0x13080
leaq -0x88(%rbp), %rdi
callq 0x13170
leaq -0x70(%rbp), %rdi
callq 0xa240
jmp 0x975cf
leaq -0x48(%rbp), %rdi
callq 0x13080
jmp 0x9770e
leaq -0x48(%rbp), %rdi
callq 0x13080
jmp 0x9771c
movq -0x10(%rbp), %rdi
callq 0x977b0
movq %rax, %rdi
addq $0x8, %rdi
callq 0x13020
movq %rax, %rdi
movq -0x18(%rbp), %rsi
callq 0x97300
movb %al, %cl
xorl %eax, %eax
testb $0x1, %cl
movb %al, -0xf1(%rbp)
jne 0x9760f
jmp 0x97668
movq -0x10(%rbp), %rdi
callq 0x977b0
movq %rax, %rdi
addq $0x18, %rdi
callq 0x13020
movq %rax, %rdi
movq -0x18(%rbp), %rsi
callq 0x97300
movb %al, %cl
xorl %eax, %eax
testb $0x1, %cl
movb %al, -0xf1(%rbp)
jne 0x97641
jmp 0x97668
movq -0x10(%rbp), %rdi
callq 0x977b0
movq %rax, %rdi
addq $0x28, %rdi
callq 0x13020
movq %rax, %rdi
movq -0x18(%rbp), %rsi
callq 0x97300
movb %al, -0xf1(%rbp)
movb -0xf1(%rbp), %al
andb $0x1, %al
movb %al, -0x1(%rbp)
jmp 0x9770e
movq -0x10(%rbp), %rdi
callq 0x977f0
movq %rax, %rdi
addq $0x8, %rdi
callq 0x13020
movq %rax, %rdi
movq -0x18(%rbp), %rsi
callq 0x97300
andb $0x1, %al
movb %al, -0x1(%rbp)
jmp 0x9770e
movq -0x10(%rbp), %rdi
callq 0x97830
movq %rax, %rdi
addq $0x8, %rdi
callq 0x13020
movq %rax, %rdi
movq -0x18(%rbp), %rsi
callq 0x97300
movb %al, %cl
xorl %eax, %eax
testb $0x1, %cl
movb %al, -0xf2(%rbp)
jne 0x976d2
jmp 0x976f9
movq -0x10(%rbp), %rdi
callq 0x97830
movq %rax, %rdi
addq $0x18, %rdi
callq 0x13020
movq %rax, %rdi
movq -0x18(%rbp), %rsi
callq 0x97300
movb %al, -0xf2(%rbp)
movb -0xf2(%rbp), %al
andb $0x1, %al
movb %al, -0x1(%rbp)
jmp 0x9770e
movb $0x1, -0x1(%rbp)
jmp 0x9770e
ud2
movb -0x1(%rbp), %al
andb $0x1, %al
addq $0x100, %rsp            # imm = 0x100
popq %rbp
retq
movq -0x20(%rbp), %rdi
callq 0xa650
nopw %cs:(%rax,%rax)