-- Some test code that tries to simulate important/interesting parts of mypy itself!
[case testSimulateMypy]
from mypy_extensions import trait
from typing import List, TypeVar, cast, Generic
from abc import abstractmethod
import other_strconv as strconv
#from other_visitor import ExpressionVisitor, StatementVisitor, NodeVisitor
import other_visitor as visitor

T = TypeVar('T')

############ nodes.py

class Context:
    def __init__(self) -> None:
        self.line = -1
    def set_line(self, line: int) -> None:
        self.line = line

class Node(Context):
    def accept(self, visitor: visitor.NodeVisitor[T]) -> T: return cast(T, None)
    def to_str(self) -> str:
        return self.accept(strconv.StrConv())

@trait
class Statement(Node):
    def accept(self, visitor: visitor.StatementVisitor[T]) -> T: return cast(T, None)

@trait
class Expression(Node):
    def accept(self, visitor: visitor.ExpressionVisitor[T]) -> T: return cast(T, None)

@trait
class SymbolNode(Node):
    """Nodes that can be stored in a symbol table."""
    @abstractmethod
    def name(self) -> str: return cast(str, None)

class FuncBase(Node):
    def __init__(self) -> None:
        super().__init__()
        self.is_static = False

class Block(Statement):
    def __init__(self, stmts: List[Statement]) -> None:
        self.stmts = stmts

    def accept(self, visitor: visitor.StatementVisitor[T]) -> T:
        return visitor.visit_block(self)

class ExprStmt(Statement):
    def __init__(self, expr: Expression) -> None:
        self.expr = expr

    def accept(self, visitor: visitor.StatementVisitor[T]) -> T:
        return visitor.visit_expr_stmt(self)

class FuncItem(FuncBase):
    def __init__(self, body: Block) -> None:
        self.body = body

class FuncDef(FuncItem, SymbolNode, Statement):
    def __init__(self, name: str, body: Block) -> None:
        super().__init__(body)
        self._name = name
    def accept(self, visitor: visitor.StatementVisitor[T]) -> T:
        return visitor.visit_func_def(self)
    def name(self) -> str:
        return self._name

class LambdaExpr(FuncItem, Expression):
    def accept(self, visitor: visitor.ExpressionVisitor[T]) -> T:
        return visitor.visit_lambda_expr(self)

def lol(x: Statement) -> int:
    return x.line

[file other_visitor.py]
from mypy_extensions import trait
from typing import TypeVar, cast, Generic
from abc import abstractmethod
import native as nodes

T = TypeVar('T')

@trait
class ExpressionVisitor(Generic[T]):
    @abstractmethod
    def visit_lambda_expr(self, o: 'nodes.LambdaExpr') -> T:
        return cast(T, None)

@trait
class StatementVisitor(Generic[T]):
    @abstractmethod
    def visit_block(self, o: 'nodes.Block') -> T:
        return cast(T, None)

    @abstractmethod
    def visit_func_def(self, o: 'nodes.FuncDef') -> T:
        return cast(T, None)

    @abstractmethod
    def visit_expr_stmt(self, o: 'nodes.ExprStmt') -> T:
        return cast(T, None)

@trait
class NodeVisitor(Generic[T], ExpressionVisitor[T], StatementVisitor[T]):
    pass

[file other_strconv.py]
from typing import List
import native as nodes
from other_visitor import NodeVisitor

class StrConv(NodeVisitor[str]):
    def visit_block(self, b: nodes.Block) -> str:
        # we really need comprehensions!
        # TODO: PartialType unsupported
        things = []  # type: List[str]
        for s in b.stmts:
            things.append(s.accept(self))
        return "{" + "; ".join(things) + "}"

    def visit_func_def(self, f: nodes.FuncDef) -> str:
        return "def " + f.name() + "(): " + f.body.accept(self)

    def visit_expr_stmt(self, e: nodes.ExprStmt) -> str:
        return e.expr.accept(self)

    def visit_lambda_expr(self, b: nodes.LambdaExpr) -> str:
        return "(fn: " + b.body.accept(self) + ")"

[file driver.py]
from native import *

block = Block([Block([]), ExprStmt(LambdaExpr(Block([])))])
fn = FuncDef('test', block)
assert fn.to_str() == "def test(): {{}; (fn: {})}"
