Beispiel #1
0
    def __init__(self, rule, args, prevs=None, th=None):
        typecheck.checkinstance('ProofTerm', rule, str)

        self.rule = rule
        if prevs is None:
            prevs = []
        prev_ths = [prev.th for prev in prevs]
        if rule == 'atom':
            assert th is not None, "ProofTerm: must provide th for atom."
            self.th = th
        elif rule == 'sorry':
            assert th is not None, "ProofTerm: must provide th for sorry."
            self.th = th
        elif rule == 'variable':
            nm, T = args
            self.th = Thm.mk_VAR(Var(nm, T))
        elif rule == 'theorem':
            self.th = theory.get_theorem(args)
        elif rule in primitive_deriv:
            rule_fun, _ = primitive_deriv[rule]
            self.th = rule_fun(*prev_ths) if args is None else rule_fun(args, *prev_ths)
        else:
            macro = theory.get_macro(rule)
            if th is None:
                self.th = macro.eval(args, prev_ths)
            else:
                self.th = th
        self.args = args
        self.prevs = prevs
        if self.rule == 'sorry':
            self.gaps = [self.th]
        else:
            self.gaps = list(set(sum([prev.gaps for prev in self.prevs], [])))
Beispiel #2
0
def TFun(*args):
    """Returns the function type arg1 => arg2 => ... => argn."""
    typecheck.checkinstance('TFun', args, [Type])
    res = args[-1]
    for arg in reversed(args[:-1]):
        res = TConst("fun", arg, res)
    return res
Beispiel #3
0
def parse_init_state(prop):
    """Given data for a theorem statement, construct the initial partial proof.
    
    data['vars']: list of variables.
    data['prop']: proposition to be proved. In the form A1 --> ... --> An --> C.

    Construct initial partial proof for the given assumptions and
    conclusion.

    assums - assumptions A1, ... An.
    concl - conclusion C.
    
    Constructs:

    0: assume A1
    ...
    n-1: assume An
    n: C by sorry
    n+1: A1 --> ... --> An --> C by intros from 0, 1, ..., n.

    """
    typecheck.checkinstance('parse_init_state', prop, (str, list, Term))
    if isinstance(prop, (str, list)):
        prop = parser.parse_term(prop)
    assums, concl = prop.strip_implies()

    state = ProofState()
    for nm, T in context.ctxt.vars.items():
        state.vars.append(Var(nm, T))
    state.prf = Proof(*assums)
    n = len(assums)
    state.prf.add_item(n, "sorry", th=Thm(assums, concl))
    state.prf.add_item(n + 1, "intros", prevs=range(n + 1))
    state.check_proof(compute_only=True)
    return state
Beispiel #4
0
def get_ast_type(T):
    """Obtain the abstract syntax tree for a type."""
    typecheck.checkinstance('get_ast_type', T, Type)

    def helper(T):
        if T.is_stvar():
            return STVarName(T.name)
        elif T.is_tvar():
            return TVarName(T.name)
        elif T.is_tconst():
            if len(T.args) == 0:
                return TypeConstr(T.name, [])
            elif len(T.args) == 1:
                arg_ast = helper(T.args[0])
                # Insert parenthesis if the single argument is a function.
                if T.args[0].is_fun():
                    arg_ast = Bracket(arg_ast)
                return TypeConstr(T.name, [arg_ast])
            elif T.is_fun():
                # 'a => 'b => 'c associates to the right. So parenthesis is
                # needed to express ('a => 'b) => 'c.
                fun_op = " ⇒ " if settings.unicode else " => "
                arg1_ast = helper(T.args[0])
                if T.args[0].is_fun():
                    arg1_ast = Bracket(arg1_ast)
                arg2_ast = helper(T.args[1])
                return FunType(arg1_ast, fun_op, arg2_ast)
            else:
                return TypeConstr(T.name, [helper(arg) for arg in T.args])
        else:
            raise TypeError

    return helper(T)
Beispiel #5
0
def print_type(T):
    """Pretty-printing for types."""
    typecheck.checkinstance('print_type', T, Type)

    ast = pprint.get_ast_type(T)
    with global_setting(line_length=None):
        return pprint.print_ast(ast)
Beispiel #6
0
    def __init__(self, hyps, prop):
        """Create a theorem with the given list of hypotheses and
        proposition.

        """
        typecheck.checkinstance('Thm', hyps, [Term], prop, Term)
        self.hyps = tuple(term_ord.sorted_terms(hyps))
        self.prop = prop
Beispiel #7
0
def Implies(*args):
    """Construct the term s1 --> ... --> sn --> t."""
    typecheck.checkinstance('Implies', args, [Term])
    if not args:
        raise TermException("Implies: input must have at least one term.")
    res = args[-1]
    for s in reversed(args[:-1]):
        res = implies(s, res)
    return res
Beispiel #8
0
def Or(*args):
    """Return the disjunction of the arguments."""
    typecheck.checkinstance('Or', args, [Term])
    if not args:
        return false
    res = args[-1]
    for s in reversed(args[:-1]):
        res = disj(s, res)
    return res
Beispiel #9
0
def And(*args):
    """Return the conjunction of the arguments."""
    typecheck.checkinstance('And', args, [Term])
    if not args:
        return true
    res = args[-1]
    for s in reversed(args[:-1]):
        res = conj(s, res)
    return res
Beispiel #10
0
def Prod(T, ts):
    """Compute the product of a list of terms with type T."""
    ts = list(ts)  # Coerce generators to list
    typecheck.checkinstance('Prod', T, Type, ts, [Term])
    if len(ts) == 0:
        return Const('one', T)
    res = ts[0]
    for t in ts[1:]:
        res = res * t
    return res
Beispiel #11
0
def Sum(T, ts):
    """Compute the sum of a list of terms with type T."""
    ts = list(ts)  # Coerce generators to list
    typecheck.checkinstance('Sum', T, Type, ts, [Term])
    if len(ts) == 0:
        return Const('zero', T)
    res = ts[0]
    for t in ts[1:]:
        res = res + t
    return res
Beispiel #12
0
def print_thm(th):
    """Print the given theorem with highlight."""
    typecheck.checkinstance('print_thm', th, Thm)

    turnstile = pprint.N("⊢") if settings.unicode else pprint.N("|-")
    if th.hyps:
        str_hyps = commas_join(print_term(hyp) for hyp in th.hyps)
        return str_hyps + pprint.N(" ") + turnstile + pprint.N(
            " ") + print_term(th.prop)
    else:
        return turnstile + pprint.N(" ") + print_term(th.prop)
Beispiel #13
0
    def __init__(self, pt, *, sym=False, conds=None):
        if conds is None:
            conds = []
        typecheck.checkinstance('rewr_conv', pt, (ProofTerm, str), conds,
                                [ProofTerm])
        self.pt = pt
        self.sym = sym
        self.conds = conds

        # Computed after the first invocation
        self.eq_pt = None
        self.As = None
        self.C = None
Beispiel #14
0
def Binary(n):
    """Convert Python integer n to HOL binary form.
    
    This function does not apply of_nat.
    
    """
    typecheck.checkinstance('Binary', n, int)
    if n == 0:
        return nat_zero
    elif n == 1:
        return nat_one
    elif n % 2 == 0:
        return bit0(Binary(n // 2))
    else:
        return bit1(Binary(n // 2))
Beispiel #15
0
def print_extension(ext):
    typecheck.checkinstance('print_extension', ext, extension.Extension)
    if ext.is_tconst():
        return "Type " + ext.name + " " + str(ext.arity)
    elif ext.is_constant():
        ref_str = " (" + ext.ref_name + ")" if ext.ref_name != ext.name else ""
        return "Constant " + ext.name + " :: " + print_type(ext.T) + ref_str
    elif ext.is_theorem():
        return "Theorem " + ext.name + ": " + print_term(ext.th.prop)
    elif ext.is_attribute():
        return "Attribute " + ext.name + " [" + ext.attribute + "]"
    elif ext.is_overload():
        return "Overload " + ext.name
    else:
        raise TypeError
Beispiel #16
0
def Exists(*args):
    """Construct the term EX x. body.
    
    Here x must be a variable and body is a term possibly depending on x.
    
    """
    typecheck.checkinstance('Forall', args, [Term])
    if len(args) < 2:
        raise TermException("Forall: must provide two terms.")
    body = args[-1]
    for x in reversed(args[:-1]):
        if not (x.is_var() or x.is_svar()):
            raise TermException("Forall: x must be a variable. Got %s" %
                                str(x))
        body = exists(x.T)(Lambda(x, body))
    return body
Beispiel #17
0
 def subst_type_inplace(self, tyinst):
     """Perform substitution on type variables."""
     typecheck.checkinstance('subst_type_inplace', tyinst, TyInst)
     if hasattr(self, "_hash_val"):
         del self._hash_val
     if self.is_svar() or self.is_var() or self.is_const():
         self.T = self.T.subst(tyinst)
     elif self.is_comb():
         self.fun.subst_type_inplace(tyinst)
         self.arg.subst_type_inplace(tyinst)
     elif self.is_abs():
         self.var_T = self.var_T.subst(tyinst)
         self.body.subst_type_inplace(tyinst)
     elif self.is_bound():
         pass
     else:
         raise TypeError
Beispiel #18
0
def Forall(*args):
    """Construct the term !x_1 ... x_n. body.
    
    The arguments are x_1, ..., x_n, body.

    Here x_1, ..., x_n must be variables (or schematic variable) and
    body is a term possibly depending on x_1, ..., x_n.

    """
    typecheck.checkinstance('Forall', args, [Term])
    if len(args) < 2:
        raise TermException("Forall: must provide two terms.")
    body = args[-1]
    for x in reversed(args[:-1]):
        if not (x.is_var() or x.is_svar()):
            raise TermException("Forall: x must be a variable. Got %s" %
                                str(x))
        body = forall(x.T)(Lambda(x, body))
    return body
Beispiel #19
0
def apply_theorem(th_name, *pts, concl=None, inst=None):
    """Wrapper for apply_theorem and apply_theorem_for macros.

    The function takes optional arguments concl, inst. Matching
    always starts with inst. If conclusion is specified, it is
    matched next. Finally, the assumptions are matched.

    """
    typecheck.checkinstance('apply_theorem', pts, [ProofTerm])
    if concl is None and inst is None:
        # Normal case, can use apply_theorem
        return ProofTerm("apply_theorem", th_name, pts)
    else:
        pt = ProofTerm.theorem(th_name)
        if inst is None:
            inst = Inst()
        if concl is not None:
            inst = matcher.first_order_match(pt.concl, concl, inst)
        for i, prev in enumerate(pts):
            inst = matcher.first_order_match(pt.assums[i], prev.prop, inst)
        return ProofTerm("apply_theorem_for", (th_name, inst), pts)
Beispiel #20
0
def print_term(t):
    """Pretty-printing for terms."""
    typecheck.checkinstance('print_term', t, Term)

    ast = pprint.get_ast_term(t)
    return pprint.print_ast(ast)
Beispiel #21
0
 def __init__(self, cv):
     typecheck.checkinstance('top_sweep_conv', cv, Conv)
     self.cv = cv
Beispiel #22
0
 def __init__(self, cv):
     typecheck.checkinstance('bottom_conv', cv, Conv)
     self.cv = cv
Beispiel #23
0
def Not(t):
    """Return negation of boolean term t."""
    typecheck.checkinstance('Not', t, Term)
    return neg(t)
Beispiel #24
0
def get_ast_term(t):
    """Obtain the abstract syntax tree for a term."""
    if (t, settings.unicode) in term_ast:
        return term_ast[(t, settings.unicode)]

    typecheck.checkinstance('get_ast_term', t, term.Term)
    var_names = [v.name for v in t.get_vars()]

    # Import modules for custom parsed data
    from logic import logic
    from data import nat
    from data import list
    from data import set
    from data import function
    from data import interval
    from data import string

    def get_priority_pair(t):
        """Obtain the binding priority of the top-most operation of t."""
        if (t.is_number() and isinstance(t.dest_number(), int) and t.dest_number() >= 0) or \
           list.is_literal_list(t):
            return 100, ATOM  # Nat atom case
        elif t.is_comb():
            op_data = operator.get_info_for_fun(t.head)
            binder_data = operator.get_binder_info_for_fun(t.head)

            if op_data is not None:
                if op_data.arity == operator.UNARY:
                    return op_data.priority, UNARY
                else:
                    return op_data.priority, BINARY
            elif binder_data is not None or logic.is_if(t):
                return 10, BINDER
            else:
                return 95, FUN_APPL  # Function application
        elif t.is_abs():
            return 10, BINDER
        else:
            return 100, ATOM  # Atom case

    def get_priority(t):
        return get_priority_pair(t)[0]

    def helper(t, bd_vars):
        """Main recursive function. Here bd_vars is the list of bound
        variables (represented by a list of AST Bound objects).
        
        """
        # Some special cases:
        # Natural numbers:
        if t.is_zero() or t.is_one() or \
           (t.is_comb('of_nat', 1) and t.arg.is_binary() and t.arg.dest_binary() >= 2):
            # First find the number
            n = t.dest_number()
            res = Number(n, t.get_type())
            if (t.is_const() and hasattr(t, "print_type")) or \
               (t.is_comb() and hasattr(t.fun, "print_type")):
                res = Bracket(ShowType(res, get_ast_type(res.T)))
            return res

        # Lists
        elif list.is_literal_list(t):
            items = list.dest_literal_list(t)
            res = List([helper(item, bd_vars) for item in items], t.get_type())
            if hasattr(t, "print_type"):
                res = Bracket(ShowType(res, get_ast_type(res.T)))
            return res

        # Sets
        elif set.is_literal_set(t):
            items = set.dest_literal_set(t)
            if set.is_empty_set(t):
                res = Operator("∅", t.T,
                               "empty_set") if settings.unicode else Operator(
                                   "{}", t.T, "empty_set")
                if hasattr(t, "print_type"):
                    res = Bracket(ShowType(res, get_ast_type(res.T)))
                return res
            else:
                return Set([helper(item, bd_vars) for item in items],
                           t.get_type())

        # Chars and Strings
        elif string.is_char(t):
            return Char(string.dest_char(t))
        elif string.is_string(t):
            return String(string.dest_string(t))

        # Intervals
        elif interval.is_interval(t):
            return Interval(helper(t.arg1, bd_vars), helper(t.arg, bd_vars),
                            t.get_type())

        elif t.is_comb('collect', 1) and t.arg.is_abs():
            nm = name.get_variant_name(t.arg.var_name, var_names)
            var_names.append(nm)

            bind_var = Bound(nm, t.arg.var_T)
            body_ast = helper(t.arg.body, [bind_var] + bd_vars)
            var_names.remove(nm)

            if hasattr(t.arg, "print_type"):
                bind_var = ShowType(bind_var, get_ast_type(bind_var.T))

            return Collect(bind_var, body_ast, t.get_type())

        elif logic.is_if(t):
            P, x, y = t.args
            return ITE(helper(P, bd_vars), helper(x, bd_vars),
                       helper(y, bd_vars), t.get_type())

        elif t.is_svar():
            return SVarName(t.name, t.T)

        elif t.is_var():
            return VarName(t.name, t.T)

        elif t.is_const():
            link_name = theory.thy.get_overload_const_name(t.name, t.T)
            res = ConstName(t.name, t.T, link_name=link_name)
            if hasattr(t, "print_type"):
                res = Bracket(ShowType(res, get_ast_type(res.T)))
            return res

        elif t.is_comb():
            op_data = operator.get_info_for_fun(t.head)
            binder_data = operator.get_binder_info_for_fun(t.head)

            # First, we take care of the case of operators
            if op_data and op_data.arity == operator.BINARY and t.is_binop():
                arg1, arg2 = t.args

                # Obtain output for first argument, enclose in parenthesis
                # if necessary.
                arg1_ast = helper(arg1, bd_vars)
                if (op_data.assoc == operator.LEFT
                        and get_priority(arg1) < op_data.priority
                        or op_data.assoc == operator.RIGHT
                        and get_priority(arg1) <= op_data.priority):
                    arg1_ast = Bracket(arg1_ast)

                op_str = op_data.unicode_op if settings.unicode else op_data.ascii_op
                op_name = theory.thy.get_overload_const_name(
                    op_data.fun_name, t.head.get_type())
                op_ast = Operator(op_str, t.head.get_type(), op_name)

                # Obtain output for second argument, enclose in parenthesis
                # if necessary.
                arg2_ast = helper(arg2, bd_vars)
                if (op_data.assoc == operator.LEFT
                        and get_priority(arg2) <= op_data.priority
                        or op_data.assoc == operator.RIGHT
                        and get_priority(arg2) < op_data.priority):
                    arg2_ast = Bracket(arg2_ast)

                return BinaryOp(arg1_ast, op_ast, arg2_ast, t.get_type())

            # Unary case
            elif op_data and op_data.arity == operator.UNARY:
                op_str = op_data.unicode_op if settings.unicode else op_data.ascii_op
                op_name = theory.thy.get_overload_const_name(
                    op_data.fun_name, t.head.get_type())
                op_ast = Operator(op_str, t.head.get_type(), op_name)

                arg_ast = helper(t.arg, bd_vars)
                arg_prior, arg_type = get_priority_pair(t.arg)
                if arg_prior < op_data.priority or arg_type == FUN_APPL:
                    arg_ast = Bracket(arg_ast)

                return UnaryOp(op_ast, arg_ast, t.get_type())

            # Next, the case of binders
            elif binder_data and t.arg.is_abs():
                binder_str = binder_data.unicode_op if settings.unicode else binder_data.ascii_op
                op_ast = Binder(binder_str)

                nm = name.get_variant_name(t.arg.var_name, var_names)
                var_names.append(nm)

                bind_var = Bound(nm, t.arg.var_T)
                body_ast = helper(t.arg.body, [bind_var] + bd_vars)
                if hasattr(t.arg, "print_type"):
                    bind_var = ShowType(bind_var, get_ast_type(bind_var.T))
                var_names.remove(nm)

                return BinderAppl(op_ast, bind_var, body_ast)

            # Function update
            elif function.is_fun_upd(t):
                f, upds = function.strip_fun_upd(t)
                f_ast = helper(f, bd_vars)
                upds_ast = [(helper(a, bd_vars), helper(b, bd_vars))
                            for a, b in upds]
                return FunUpd(f_ast, upds_ast, t.get_type())

            # Finally, usual function application
            else:
                fun_ast = helper(t.fun, bd_vars)
                fun_prior, fun_type = get_priority_pair(t.fun)
                if fun_prior < 95 or fun_type == UNARY:
                    fun_ast = Bracket(fun_ast)

                arg_ast = helper(t.arg, bd_vars)
                if get_priority(t.arg) <= 95:
                    arg_ast = Bracket(arg_ast)

                return FunAppl(fun_ast, arg_ast)

        elif t.is_abs():
            op_ast = Binder("λ") if settings.unicode else Binder("%")

            nm = name.get_variant_name(t.var_name, var_names)
            var_names.append(nm)

            bind_var = Bound(nm, t.var_T)
            body_ast = helper(t.body, [bind_var] + bd_vars)
            if hasattr(t, "print_type"):
                bind_var = ShowType(bind_var, get_ast_type(bind_var.T))
            var_names.remove(nm)

            return BinderAppl(op_ast, bind_var, body_ast)

        elif t.is_bound():
            if t.n >= len(bd_vars):
                raise term.TermException("print_term: open term")
            else:
                return bd_vars[t.n]
        else:
            raise TypeError

    copy_t = copy(
        t)  # make copy here, because infer_printed_type may change t.
    infertype.infer_printed_type(copy_t)

    ast = helper(copy_t, [])
    term_ast[(t, settings.unicode)] = ast
    return ast
Beispiel #25
0
 def sorry(th):
     typecheck.checkinstance('sorry', th, Thm)
     return ProofTerm("sorry", None, [], th)
Beispiel #26
0
def print_extensions(exts):
    typecheck.checkinstance('print_extensions', exts, [extension.Extension])
    return "\n".join(print_extension(ext) for ext in exts)
Beispiel #27
0
 def __init__(self, cv1, cv2):
     typecheck.checkinstance('combination_conv', cv1, Conv, cv2, Conv)
     self.cv1 = cv1
     self.cv2 = cv2
Beispiel #28
0
 def __init__(self, cv1, cv2):
     typecheck.checkinstance('else_conv', cv1, Conv, cv2, Conv)
     self.cv1 = cv1
     self.cv2 = cv2
Beispiel #29
0
def first_order_match(pat, t, inst=None):
    """First-order matching of pat with t.

    inst : optional Inst
        Existing instantiation. Default to empty instantiation.
        
    Return the new instantiation or throws MatchException. The input
    instantiation is guaranteed to be not modified.

    """
    if inst is None:
        inst = Inst()
    else:
        inst = copy(inst)  # do not modify input

    typecheck.checkinstance('first_order_match', pat, Term, t, Term, inst,
                            Inst)
    assert len(t.get_svars()
               ) == 0, "first_order_match: t should not contain patterns."

    # Trace of pattern and term, for debugging
    trace = []

    # List of replacements for bound variables
    bd_vars = []

    def match(pat, t):
        trace.append((pat, t))
        if pat.head.is_svar():
            # Case where the head of the function is a variable.
            if pat.head.name not in inst:
                # If the head variable is not instantiated, check that the
                # arguments are distinct, and each argument is either a
                # bound variable or a matched variable. In addition, all bound
                # variables appearing in t also appear as an argument.
                # If all conditions hold, assign appropriately.

                heuristic_match = False
                # Check each argument is either a bound variable or is a
                # schematic variable that is already matched.
                for v in pat.args:
                    if not (v in bd_vars or (v.is_svar() and v.name in inst)):
                        heuristic_match = True

                # Check arguments of pat are distinct.
                if len(set(pat.args)) != len(pat.args):
                    heuristic_match = True

                # Check t does not contain any extra bound variables.
                t_vars = t.get_vars()
                if any(v in t_vars and v not in pat.args for v in bd_vars):
                    heuristic_match = True

                if heuristic_match:
                    # Heuristic matching: just assign pat.fun to t.fun.
                    if pat.is_svar():
                        # t contains bound variables, so match fails
                        raise MatchException(trace)
                    elif t.is_comb():
                        try:
                            pat.head.T.match_incr(t.fun.get_type(),
                                                  inst.tyinst)
                        except TypeMatchException:
                            raise MatchException(trace)
                        inst[pat.head.name] = t.fun
                        match(pat.arg, t.arg)
                    else:
                        raise MatchException(trace)
                else:
                    # First, obtain and match the expected type of pat_T.
                    Tlist = []
                    for v in pat.args:
                        if v in bd_vars:
                            Tlist.append(v.T)
                        else:
                            Tlist.append(inst[v.name].get_type())
                    Tlist.append(t.get_type())
                    try:
                        pat.head.T.match_incr(TFun(*Tlist), inst.tyinst)
                    except TypeMatchException:
                        raise MatchException(trace)

                    # The instantiation of the head variable is computed by starting
                    # with t, then abstract each of the arguments.
                    inst_t = t
                    for v in reversed(pat.args):
                        if v in bd_vars:
                            if inst_t.is_comb(
                            ) and inst_t.arg == v and v not in inst_t.fun.get_vars(
                            ):
                                op_data = operator.get_info_for_fun(
                                    inst_t.head)
                                if inst_t.is_comb("IF", 3):
                                    inst_t = Lambda(v, inst_t)
                                elif op_data is None:
                                    # inst_t is of the form f x, where x is the argument.
                                    # In this case, directly reduce to f.
                                    inst_t = inst_t.fun
                                elif op_data.arity == operator.BINARY and len(
                                        inst_t.args) == 2:
                                    inst_t = Lambda(v, inst_t)
                                else:
                                    inst_t = inst_t.fun
                            else:
                                # Otherwise, perform the abstraction.
                                inst_t = Lambda(v, inst_t)
                        else:
                            assert v.name in inst
                            inst_v = inst[v.name]
                            if inst_t.is_comb(
                            ) and inst_t.arg == inst_v and not find_term(
                                    inst_t.fun, inst_v):
                                inst_t = inst_t.fun
                            elif inst_v.is_var():
                                inst_t = Lambda(inst_v, inst_t)
                            else:
                                raise MatchException(trace)
                    inst[pat.head.name] = inst_t
            else:
                # If the head variable is already instantiated, apply the
                # instantiation onto the arguments, simplify using beta-conversion,
                # and match again.
                pat2 = inst[pat.head.name](*pat.args).beta_norm()
                match(pat2, t.beta_norm())
        elif pat.is_var() or pat.is_const():
            # The case where pat is a free variable, constant, or comes
            # from a bound variable.
            if pat.ty != t.ty or pat.name != t.name:
                raise MatchException(trace)
            else:
                try:
                    pat.T.match_incr(t.T, inst.tyinst)
                except TypeMatchException:
                    raise MatchException(trace)
        elif pat.is_comb():
            # In the combination case (where the head is not a variable),
            # match fun and arg.
            if pat.ty != t.ty:
                raise MatchException(trace)
            if is_pattern(pat.fun,
                          list(inst.keys()),
                          bd_vars=[v.name for v in bd_vars]):
                match(pat.fun, t.fun)
                match(pat.arg, t.arg)
            else:
                match(pat.arg, t.arg)
                match(pat.fun, t.fun)
        elif pat.is_abs():
            # When pat is a lambda term, t must also be a lambda term.
            # Replace bound variable by a variable, then match the body.
            if t.is_abs():
                try:
                    pat.var_T.match_incr(t.var_T, inst.tyinst)
                except TypeMatchException:
                    raise MatchException(trace)
                T = pat.var_T.subst(inst.tyinst)

                var_names = [
                    v.name for v in pat.body.get_vars() + t.body.get_vars()
                ]
                nm = name.get_variant_name(pat.var_name, var_names)
                v = Var(nm, T)
                pat_body = pat.subst_type(inst.tyinst).subst_bound(v)
                t_body = t.subst_bound(v)
                bd_vars.append(v)
                match(pat_body, t_body)
                bd_vars.pop()
            else:
                tT = t.get_type()
                if not tT.is_fun():
                    raise MatchException(trace)
                try:
                    pat.var_T.match_incr(tT.domain_type(), inst.tyinst)
                except TypeMatchException:
                    raise MatchException(trace)
                T = pat.var_T.subst(inst.tyinst)
                match(pat, Abs(pat.var_name, T, t(Bound(0))))
        elif pat.is_bound():
            raise MatchException(trace)
        else:
            raise TypeError

        trace.pop()

    match(pat, t)
    return inst