···11+# ego
22+33+Analyzes alpha equivalence in untyped lambda calculus terms. The OCaml
44+and Nim implementations keep the 4 binding representations independent:
55+66+- named variables
77+- de bruijn indices
88+- locally nameless terms
99+- explicit substitutions
1010+1111+each representation implements parsing conversion, pretty printing, free
1212+variable analysis, alpha equivalence, capture avoiding substitution and
1313+normalisation according to its own binding semantics
1414+1515+## Surface syntax
1616+1717+```text
1818+x variable
1919+\x. x abstraction
2020+\x. \y. x y nested abstraction
2121+(\x. x) y application
2222+[x := y] t explicit substitution
2323+```
2424+2525+Application is left associative and abstraction bodies extend to the right while names
2626+match `[a-zA-Z][a-zA-Z0-9_'-]*` accordingly
2727+2828+## Representation contracts
2929+3030+Named terms store textual binders and resolve references through explicit
3131+environments whereas substitution performs freshness generation and capture checks
3232+3333+De Bruijn terms encode bound references as lexical indices and retain free
3434+variables separately and shifting is required when terms cross binder boundaries.
3535+3636+Locally nameless terms encode bound references as indices and free references as
3737+names. Opening and closing enforce scope transitions; conversion and operations
3838+reject terms that are not locally closed.
3939+4040+Explicit substitution terms retain substitutions in the syntax tree and reduction
4141+propagates substitutions through variables, applications as well abstractions while
4242+preserving alpha equivalence and delayed capture avoidance
4343+4444+## interfaces
4545+4646+All 4 implementations expose the same semantic operations:
4747+4848+```text
4949+parse
5050+pretty_print
5151+free_variables
5252+alpha_equivalent
5353+substitute
5454+normalize
5555+convert_from_named
5656+convert_to_named
5757+```
5858+5959+The OCaml modules are `Named`, `Debruijn`, `Locally_nameless`, and
6060+`Explicit_subst`. The Nim modules use distinct variant types for each term
6161+representation and expose equivalent operations
6262+6363+## Automata renderer
6464+6565+This will convert automaton and transducer JSON into Graphviz
6666+DOT, SVG, PNG, or PDF. Input states may be plain identifiers or objects with
6767+labels and accepting state metadata. Transitions also accept explicit labels or
6868+`read`, `write` and `action`
···11+type t =
22+ | Var of int
33+ | Free of string
44+ | Lam of t
55+ | App of t * t
66+77+module SSet = Set.Make (String)
88+99+exception Unbound_index of int
1010+1111+let rec free_variables = function
1212+ | Var _ -> SSet.empty
1313+ | Free x -> SSet.singleton x
1414+ | Lam b -> free_variables b
1515+ | App (f, a) -> SSet.union (free_variables f) (free_variables a)
1616+1717+let free_variable_list t = SSet.elements (free_variables t)
1818+1919+let rec pretty = function
2020+ | Var i -> "#" ^ string_of_int i
2121+ | Free x -> x
2222+ | Lam b -> "\\. " ^ pretty b
2323+ | App (f, a) ->
2424+ let pf =
2525+ match f with Var _ | Free _ | App _ -> pretty f | _ -> "(" ^ pretty f ^ ")"
2626+ in
2727+ let pa =
2828+ match a with Var _ | Free _ -> pretty a | _ -> "(" ^ pretty a ^ ")"
2929+ in
3030+ pf ^ " " ^ pa
3131+3232+let rec shift cutoff amount t =
3333+ match t with
3434+ | Var i -> if i >= cutoff then Var (i + amount) else Var i
3535+ | Free x -> Free x
3636+ | Lam b -> Lam (shift (cutoff + 1) amount b)
3737+ | App (f, a) -> App (shift cutoff amount f, shift cutoff amount a)
3838+3939+let rec subst j s = function
4040+ | Var i ->
4141+ if i = j then s else if i > j then Var (i - 1) else Var i
4242+ | Free x -> Free x
4343+ | Lam b -> Lam (subst (j + 1) (shift 0 1 s) b)
4444+ | App (f, a) -> App (subst j s f, subst j s a)
4545+4646+let substitute j s body = subst j s body
4747+4848+let rec convert_from_named_depth env = function
4949+ | Named.Var x -> begin
5050+ match List.assoc_opt x env with
5151+ | Some i -> Var i
5252+ | None -> Free x
5353+ end
5454+ | Named.Lam (x, b) -> Lam (convert_from_named_depth ((x, 0) :: List.map (fun (v, i) -> (v, i + 1)) env) b)
5555+ | Named.App (f, a) ->
5656+ App (convert_from_named_depth env f, convert_from_named_depth env a)
5757+ | Named.Subst (x, s, b) ->
5858+ let body = Named.substitute x s b in
5959+ convert_from_named_depth env body
6060+6161+let convert_from_named t = convert_from_named_depth [] t
6262+6363+let convert_to_named t =
6464+ let fv = free_variables t in
6565+ let fresh_avoid avoid base =
6666+ if not (List.mem base avoid) then base
6767+ else
6868+ let rec go i =
6969+ let cand = base ^ string_of_int i in
7070+ if List.mem cand avoid then go (i + 1) else cand
7171+ in
7272+ go 0
7373+ in
7474+ let rec go env = function
7575+ | Var i -> begin
7676+ match List.nth_opt env i with
7777+ | Some x -> Named.Var x
7878+ | None -> raise (Unbound_index i)
7979+ end
8080+ | Free x -> Named.Var x
8181+ | Lam b ->
8282+ let avoid = SSet.elements fv @ env in
8383+ let x = fresh_avoid avoid "v" in
8484+ Named.Lam (x, go (x :: env) b)
8585+ | App (f, a) -> Named.App (go env f, go env a)
8686+ in
8787+ go [] t
8888+8989+let is_value = function Lam _ -> true | _ -> false
9090+9191+let rec step = function
9292+ | App (Lam b, a) -> Some (subst 0 a b)
9393+ | App (f, a) when not (is_value f) -> begin
9494+ match step f with
9595+ | Some f' -> Some (App (f', a))
9696+ | None -> begin
9797+ match step a with Some a' -> Some (App (f, a')) | None -> None
9898+ end
9999+ end
100100+ | App (f, a) -> begin
101101+ match step a with Some a' -> Some (App (f, a')) | None -> None
102102+ end
103103+ | Lam b -> begin
104104+ match step b with Some b' -> Some (Lam b') | None -> None
105105+ end
106106+ | _ -> None
107107+108108+let step_limit = 10000
109109+110110+let normalise t =
111111+ let rec go n t =
112112+ if n >= step_limit then t
113113+ else match step t with Some t' -> go (n + 1) t' | None -> t
114114+ in
115115+ go 0 t
116116+117117+let normalise_counted t =
118118+ let count = ref 0 in
119119+ let maxd = ref 0 in
120120+ let rec walk d t =
121121+ incr count;
122122+ if d > !maxd then maxd := d;
123123+ match t with
124124+ | Var _ | Free _ -> ()
125125+ | Lam b -> walk (d + 1) b
126126+ | App (f, a) -> walk (d + 1) f; walk (d + 1) a
127127+ in
128128+ walk 0 t;
129129+ let rec go n t =
130130+ if n >= step_limit then t
131131+ else
132132+ match step t with
133133+ | Some t' -> walk 0 t'; go (n + 1) t'
134134+ | None -> t
135135+ in
136136+ (go 0 t, !count, !maxd)
137137+138138+let alpha_equivalent t1 t2 = normalise t1 = normalise t2
139139+140140+let rec well_scoped depth = function
141141+ | Var i -> i < depth
142142+ | Free _ -> true
143143+ | Lam b -> well_scoped (depth + 1) b
144144+ | App (f, a) -> well_scoped depth f && well_scoped depth a
145145+146146+let alpha_equivalent_counted t1 t2 =
147147+ let count = ref 0 in
148148+ let maxd = ref 0 in
149149+ let rec go d a b =
150150+ incr count;
151151+ if d > !maxd then maxd := d;
152152+ match (a, b) with
153153+ | Var i, Var j -> i = j
154154+ | Free x, Free y -> x = y
155155+ | Lam b1, Lam b2 -> go (d + 1) b1 b2
156156+ | App (f1, a1), App (f2, a2) -> go (d + 1) f1 f2 && go (d + 1) a1 a2
157157+ | _ -> false
158158+ in
159159+ let r = go 0 (normalise t1) (normalise t2) in
160160+ (r, !count, !maxd)
···11+type t =
22+ | Var of int
33+ | Free of string
44+ | Lam of t
55+ | App of t * t
66+77+module SSet : Set.S with type elt = string
88+99+exception Unbound_index of int
1010+1111+val free_variables : t -> SSet.t
1212+val free_variable_list : t -> string list
1313+val pretty : t -> string
1414+val shift : int -> int -> t -> t
1515+val subst : int -> t -> t -> t
1616+val substitute : int -> t -> t -> t
1717+val convert_from_named : Named.t -> t
1818+val convert_to_named : t -> Named.t
1919+val alpha_equivalent : t -> t -> bool
2020+val alpha_equivalent_counted : t -> t -> bool * int * int
2121+val normalise : t -> t
2222+val normalise_counted : t -> t * int * int
2323+val well_scoped : int -> t -> bool
···11+type t =
22+ | Var of string
33+ | Lam of string * t
44+ | App of t * t
55+ | Subst of string * t * t
66+77+module SSet = Set.Make (String)
88+99+let rec free_variables = function
1010+ | Var x -> SSet.singleton x
1111+ | Lam (x, b) -> SSet.remove x (free_variables b)
1212+ | App (f, a) -> SSet.union (free_variables f) (free_variables a)
1313+ | Subst (x, s, b) ->
1414+ let fv_b = free_variables b in
1515+ let fv_b' = SSet.remove x fv_b in
1616+ if SSet.mem x fv_b then SSet.union fv_b' (free_variables s) else fv_b'
1717+1818+let free_variable_list t = SSet.elements (free_variables t)
1919+2020+let rec pretty = function
2121+ | Var x -> x
2222+ | Lam (x, b) -> "\\" ^ x ^ ". " ^ pretty b
2323+ | App (f, a) ->
2424+ let pf =
2525+ match f with Var _ | App _ -> pretty f | _ -> "(" ^ pretty f ^ ")"
2626+ in
2727+ let pa = match a with Var _ -> pretty a | _ -> "(" ^ pretty a ^ ")" in
2828+ pf ^ " " ^ pa
2929+ | Subst (x, s, b) -> "[" ^ x ^ " := " ^ pretty s ^ "] " ^ pretty b
3030+3131+let fresh avoid base =
3232+ if not (SSet.mem base avoid) then base
3333+ else
3434+ let rec go i =
3535+ let cand = base ^ string_of_int i in
3636+ if SSet.mem cand avoid then go (i + 1) else cand
3737+ in
3838+ go 0
3939+4040+let rec rename old nw = function
4141+ | Var z -> if z = old then Var nw else Var z
4242+ | App (f, a) -> App (rename old nw f, rename old nw a)
4343+ | Lam (z, b) -> if z = old then Lam (z, b) else Lam (z, rename old nw b)
4444+ | Subst (z, u, b) ->
4545+ if z = old then Subst (z, rename old nw u, b)
4646+ else Subst (z, rename old nw u, rename old nw b)
4747+4848+let step_limit = 10000
4949+5050+let push_subst x s body =
5151+ let fv_s = free_variables s in
5252+ let rec go = function
5353+ | Var y -> if y = x then s else Var y
5454+ | App (t, u) -> App (go t, go u)
5555+ | Lam (y, b) ->
5656+ if y = x then Lam (y, b)
5757+ else if not (SSet.mem y fv_s) then Lam (y, go b)
5858+ else
5959+ let avoid =
6060+ SSet.union fv_s
6161+ (SSet.union (free_variables b) (free_variables s))
6262+ in
6363+ let y' = fresh avoid y in
6464+ Lam (y', go (rename y y' b))
6565+ | Subst (y, u, b) ->
6666+ if y = x then Subst (y, go u, b)
6767+ else if not (SSet.mem y fv_s) then Subst (y, go u, go b)
6868+ else
6969+ let avoid =
7070+ SSet.union fv_s
7171+ (SSet.union (free_variables b) (free_variables s))
7272+ in
7373+ let y' = fresh avoid y in
7474+ Subst (y', go u, go (rename y y' b))
7575+ in
7676+ go body
7777+7878+let rec reduce = function
7979+ | Var x -> Var x
8080+ | Lam (x, b) -> Lam (x, reduce b)
8181+ | App (f, a) -> begin
8282+ let f' = reduce f in
8383+ let a' = reduce a in
8484+ match f' with Lam (x, b) -> reduce (push_subst x a' b) | _ -> App (f', a')
8585+ end
8686+ | Subst (x, s, b) -> reduce (push_subst x (reduce s) b)
8787+8888+let normalise t =
8989+ let rec go n t =
9090+ if n >= step_limit then t
9191+ else
9292+ let t' = reduce t in
9393+ if t' = t then t else go (n + 1) t'
9494+ in
9595+ go 0 t
9696+9797+let rec to_named = function
9898+ | Var x -> Named.Var x
9999+ | Lam (x, b) -> Named.Lam (x, to_named b)
100100+ | App (f, a) -> Named.App (to_named f, to_named a)
101101+ | Subst (x, s, b) -> Named.Subst (x, to_named s, to_named b)
102102+103103+let rec of_named = function
104104+ | Named.Var x -> Var x
105105+ | Named.Lam (x, b) -> Lam (x, of_named b)
106106+ | Named.App (f, a) -> App (of_named f, of_named a)
107107+ | Named.Subst (x, s, b) -> Subst (x, of_named s, of_named b)
108108+109109+let alpha_equivalent t1 t2 =
110110+ Named.alpha_equivalent (to_named (normalise t1)) (to_named (normalise t2))
111111+112112+let alpha_equivalent_susp t1 t2 =
113113+ let rec go d e1 e2 a b =
114114+ match (a, b) with
115115+ | Var x, Var y -> begin
116116+ match (List.assoc_opt x e1, List.assoc_opt y e2) with
117117+ | Some i, Some j -> i = j
118118+ | None, None -> x = y
119119+ | _ -> false
120120+ end
121121+ | Lam (x, b1), Lam (y, b2) -> go (d + 1) ((x, d) :: e1) ((y, d) :: e2) b1 b2
122122+ | App (f1, a1), App (f2, a2) -> go d e1 e2 f1 f2 && go d e1 e2 a1 a2
123123+ | Subst (x, s1, b1), Subst (y, s2, b2) ->
124124+ go d e1 e2 s1 s2
125125+ && go (d + 1) ((x, d) :: e1) ((y, d) :: e2) b1 b2
126126+ | Subst _, _ -> go d e1 e2 (normalise a) b
127127+ | _, Subst _ -> go d e1 e2 a (normalise b)
128128+ | _ -> false
129129+ in
130130+ go 0 [] [] t1 t2
131131+132132+let alpha_equivalent_counted t1 t2 =
133133+ let count = ref 0 in
134134+ let maxd = ref 0 in
135135+ let rec go d e1 e2 a b =
136136+ incr count;
137137+ if d > !maxd then maxd := d;
138138+ match (a, b) with
139139+ | Var x, Var y -> begin
140140+ match (List.assoc_opt x e1, List.assoc_opt y e2) with
141141+ | Some i, Some j -> i = j
142142+ | None, None -> x = y
143143+ | _ -> false
144144+ end
145145+ | Lam (x, b1), Lam (y, b2) -> go (d + 1) ((x, d) :: e1) ((y, d) :: e2) b1 b2
146146+ | App (f1, a1), App (f2, a2) -> go d e1 e2 f1 f2 && go d e1 e2 a1 a2
147147+ | Subst (x, s1, b1), Subst (y, s2, b2) ->
148148+ go d e1 e2 s1 s2
149149+ && go (d + 1) ((x, d) :: e1) ((y, d) :: e2) b1 b2
150150+ | Subst _, _ ->
151151+ let a' = normalise a in
152152+ if a' = a then false else go d e1 e2 a' b
153153+ | _, Subst _ ->
154154+ let b' = normalise b in
155155+ if b' = b then false else go d e1 e2 a b'
156156+ | _ -> false
157157+ in
158158+ let r = go 0 [] [] t1 t2 in
159159+ (r, !count, !maxd)
160160+161161+let normalise_counted t =
162162+ let count = ref 0 in
163163+ let maxd = ref 0 in
164164+ let rec walk d t =
165165+ incr count;
166166+ if d > !maxd then maxd := d;
167167+ match t with
168168+ | Var _ -> ()
169169+ | Lam (_, b) -> walk (d + 1) b
170170+ | App (f, a) -> walk (d + 1) f; walk (d + 1) a
171171+ | Subst (_, s, b) -> walk (d + 1) s; walk (d + 1) b
172172+ in
173173+ walk 0 t;
174174+ let rec go n t =
175175+ if n >= step_limit then t
176176+ else
177177+ let t' = reduce t in
178178+ walk 0 t';
179179+ if t' = t then t else go (n + 1) t'
180180+ in
181181+ (go 0 t, !count, !maxd)
182182+183183+let substitute x s body = Subst (x, s, body)
184184+185185+let convert_from_named t = of_named t
186186+let convert_to_named t = to_named (normalise t)
···11+type t =
22+ | Var of string
33+ | Lam of string * t
44+ | App of t * t
55+ | Subst of string * t * t
66+77+module SSet : Set.S with type elt = string
88+99+val free_variables : t -> SSet.t
1010+val free_variable_list : t -> string list
1111+val pretty : t -> string
1212+val push_subst : string -> t -> t -> t
1313+val normalise : t -> t
1414+val normalise_counted : t -> t * int * int
1515+val to_named : t -> Named.t
1616+val of_named : Named.t -> t
1717+val alpha_equivalent : t -> t -> bool
1818+val alpha_equivalent_susp : t -> t -> bool
1919+val alpha_equivalent_counted : t -> t -> bool * int * int
2020+val substitute : string -> t -> t -> t
2121+val convert_from_named : Named.t -> t
2222+val convert_to_named : t -> Named.t
···11+type t =
22+ | BVar of int
33+ | FVar of string
44+ | Lam of t
55+ | App of t * t
66+77+module SSet = Set.Make (String)
88+99+exception Not_locally_closed
1010+1111+let rec free_variables = function
1212+ | BVar _ -> SSet.empty
1313+ | FVar x -> SSet.singleton x
1414+ | Lam b -> free_variables b
1515+ | App (f, a) -> SSet.union (free_variables f) (free_variables a)
1616+1717+let free_variable_list t = SSet.elements (free_variables t)
1818+1919+let rec pretty = function
2020+ | BVar i -> "#" ^ string_of_int i
2121+ | FVar x -> x
2222+ | Lam b -> "\\. " ^ pretty b
2323+ | App (f, a) ->
2424+ let pf =
2525+ match f with
2626+ | BVar _ | FVar _ | App _ -> pretty f
2727+ | _ -> "(" ^ pretty f ^ ")"
2828+ in
2929+ let pa =
3030+ match a with BVar _ | FVar _ -> pretty a | _ -> "(" ^ pretty a ^ ")"
3131+ in
3232+ pf ^ " " ^ pa
3333+3434+let rec open_at k x = function
3535+ | BVar i -> if i = k then FVar x else BVar i
3636+ | FVar y -> FVar y
3737+ | Lam b -> Lam (open_at (k + 1) x b)
3838+ | App (f, a) -> App (open_at k x f, open_at k x a)
3939+4040+let open_top t x = open_at 0 x t
4141+4242+let rec close_at k x = function
4343+ | BVar i -> BVar i
4444+ | FVar y -> if y = x then BVar k else FVar y
4545+ | Lam b -> Lam (close_at (k + 1) x b)
4646+ | App (f, a) -> App (close_at k x f, close_at k x a)
4747+4848+let close_top t x = close_at 0 x t
4949+5050+let rec lc_at depth = function
5151+ | BVar i -> i < depth
5252+ | FVar _ -> true
5353+ | Lam b -> lc_at (depth + 1) b
5454+ | App (f, a) -> lc_at depth f && lc_at depth a
5555+5656+let lc t = lc_at 0 t
5757+5858+let require_lc t =
5959+ if not (lc t) then raise Not_locally_closed
6060+6161+let rec subst_bvar j s = function
6262+ | BVar i -> if i = j then s else if i > j then BVar (i - 1) else BVar i
6363+ | FVar x -> FVar x
6464+ | Lam b -> Lam (subst_bvar (j + 1) (shift s) b)
6565+ | App (f, a) -> App (subst_bvar j s f, subst_bvar j s a)
6666+6767+and shift t =
6868+ let rec go cutoff = function
6969+ | BVar i -> if i >= cutoff then BVar (i + 1) else BVar i
7070+ | FVar x -> FVar x
7171+ | Lam b -> Lam (go (cutoff + 1) b)
7272+ | App (f, a) -> App (go cutoff f, go cutoff a)
7373+ in
7474+ go 0 t
7575+7676+let rec subst_fvar x s = function
7777+ | BVar i -> BVar i
7878+ | FVar y -> if y = x then s else FVar y
7979+ | Lam b -> Lam (subst_fvar x s b)
8080+ | App (f, a) -> App (subst_fvar x s f, subst_fvar x s a)
8181+8282+let convert_from_named t =
8383+ let rec go env = function
8484+ | Named.Var x -> begin
8585+ match List.assoc_opt x env with
8686+ | Some i -> BVar i
8787+ | None -> FVar x
8888+ end
8989+ | Named.Lam (x, b) ->
9090+ Lam (go ((x, 0) :: List.map (fun (v, i) -> (v, i + 1)) env) b)
9191+ | Named.App (f, a) -> App (go env f, go env a)
9292+ | Named.Subst (x, s, b) ->
9393+ go env (Named.substitute x s b)
9494+ in
9595+ go [] t
9696+9797+let convert_to_named t =
9898+ require_lc t;
9999+ let fv = free_variables t in
100100+ let fresh_avoid avoid base =
101101+ if not (SSet.mem base avoid) then base
102102+ else
103103+ let rec go i =
104104+ let cand = base ^ string_of_int i in
105105+ if SSet.mem cand avoid then go (i + 1) else cand
106106+ in
107107+ go 0
108108+ in
109109+ let rec go env = function
110110+ | BVar i -> Named.Var (List.nth env i)
111111+ | FVar x -> Named.Var x
112112+ | Lam b ->
113113+ let avoid =
114114+ List.fold_left (fun a x -> SSet.add x a) fv env
115115+ in
116116+ let x = fresh_avoid avoid "v" in
117117+ Named.Lam (x, go (x :: env) b)
118118+ | App (f, a) -> Named.App (go env f, go env a)
119119+ in
120120+ go [] t
121121+122122+let is_value = function Lam _ -> true | _ -> false
123123+124124+let rec step = function
125125+ | App (Lam b, a) -> Some (subst_bvar 0 a b)
126126+ | App (f, a) when not (is_value f) -> begin
127127+ match step f with
128128+ | Some f' -> Some (App (f', a))
129129+ | None -> begin
130130+ match step a with Some a' -> Some (App (f, a')) | None -> None
131131+ end
132132+ end
133133+ | App (f, a) -> begin
134134+ match step a with Some a' -> Some (App (f, a')) | None -> None
135135+ end
136136+ | Lam b -> begin
137137+ match step b with Some b' -> Some (Lam b') | None -> None
138138+ end
139139+ | _ -> None
140140+141141+let step_limit = 10000
142142+143143+let normalise t =
144144+ require_lc t;
145145+ let rec go n t =
146146+ if n >= step_limit then t
147147+ else match step t with Some t' -> go (n + 1) t' | None -> t
148148+ in
149149+ go 0 t
150150+151151+let normalise_counted t =
152152+ require_lc t;
153153+ let count = ref 0 in
154154+ let maxd = ref 0 in
155155+ let rec walk d t =
156156+ incr count;
157157+ if d > !maxd then maxd := d;
158158+ match t with
159159+ | BVar _ | FVar _ -> ()
160160+ | Lam b -> walk (d + 1) b
161161+ | App (f, a) -> walk (d + 1) f; walk (d + 1) a
162162+ in
163163+ walk 0 t;
164164+ let rec go n t =
165165+ if n >= step_limit then t
166166+ else
167167+ match step t with
168168+ | Some t' -> walk 0 t'; go (n + 1) t'
169169+ | None -> t
170170+ in
171171+ (go 0 t, !count, !maxd)
172172+173173+let substitute x s body = subst_fvar x s body
174174+175175+let alpha_equivalent t1 t2 =
176176+ require_lc t1;
177177+ require_lc t2;
178178+ normalise t1 = normalise t2
179179+180180+let alpha_equivalent_counted t1 t2 =
181181+ require_lc t1;
182182+ require_lc t2;
183183+ let count = ref 0 in
184184+ let maxd = ref 0 in
185185+ let rec go d a b =
186186+ incr count;
187187+ if d > !maxd then maxd := d;
188188+ match (a, b) with
189189+ | BVar i, BVar j -> i = j
190190+ | FVar x, FVar y -> x = y
191191+ | Lam b1, Lam b2 -> go (d + 1) b1 b2
192192+ | App (f1, a1), App (f2, a2) -> go (d + 1) f1 f2 && go (d + 1) a1 a2
193193+ | _ -> false
194194+ in
195195+ let r = go 0 (normalise t1) (normalise t2) in
196196+ (r, !count, !maxd)
···11+type t =
22+ | BVar of int
33+ | FVar of string
44+ | Lam of t
55+ | App of t * t
66+77+module SSet : Set.S with type elt = string
88+99+exception Not_locally_closed
1010+1111+val free_variables : t -> SSet.t
1212+val free_variable_list : t -> string list
1313+val pretty : t -> string
1414+val open_at : int -> string -> t -> t
1515+val open_top : t -> string -> t
1616+val close_at : int -> string -> t -> t
1717+val close_top : t -> string -> t
1818+val lc : t -> bool
1919+val require_lc : t -> unit
2020+val subst_bvar : int -> t -> t -> t
2121+val subst_fvar : string -> t -> t -> t
2222+val substitute : string -> t -> t -> t
2323+val convert_from_named : Named.t -> t
2424+val convert_to_named : t -> Named.t
2525+val alpha_equivalent : t -> t -> bool
2626+val alpha_equivalent_counted : t -> t -> bool * int * int
2727+val normalise : t -> t
2828+val normalise_counted : t -> t * int * int
···11+type t =
22+ | Var of string
33+ | Lam of string * t
44+ | App of t * t
55+ | Subst of string * t * t
66+77+module SSet = Set.Make (String)
88+99+let rec free_variables = function
1010+ | Var x -> SSet.singleton x
1111+ | Lam (x, b) -> SSet.remove x (free_variables b)
1212+ | App (f, a) -> SSet.union (free_variables f) (free_variables a)
1313+ | Subst (x, s, b) ->
1414+ let fv_b = free_variables b in
1515+ let fv_b' = SSet.remove x fv_b in
1616+ if SSet.mem x fv_b then SSet.union fv_b' (free_variables s) else fv_b'
1717+1818+let free_variable_list t = SSet.elements (free_variables t)
1919+2020+let rec pretty = function
2121+ | Var x -> x
2222+ | Lam (x, b) -> "\\" ^ x ^ ". " ^ pretty b
2323+ | App (f, a) ->
2424+ let pf =
2525+ match f with Var _ | App _ -> pretty f | _ -> "(" ^ pretty f ^ ")"
2626+ in
2727+ let pa = match a with Var _ -> pretty a | _ -> "(" ^ pretty a ^ ")" in
2828+ pf ^ " " ^ pa
2929+ | Subst (x, s, b) -> "[" ^ x ^ " := " ^ pretty s ^ "] " ^ pretty b
3030+3131+let fresh_name avoid base =
3232+ if not (SSet.mem base avoid) then base
3333+ else
3434+ let rec go i =
3535+ let cand = base ^ string_of_int i in
3636+ if SSet.mem cand avoid then go (i + 1) else cand
3737+ in
3838+ go 0
3939+4040+let rename old nw t =
4141+ let rec ren = function
4242+ | Var z -> if z = old then Var nw else Var z
4343+ | App (f, a) -> App (ren f, ren a)
4444+ | Lam (z, b) -> if z = old then Lam (z, b) else Lam (z, ren b)
4545+ | Subst (z, u, b) ->
4646+ if z = old then Subst (z, ren u, b) else Subst (z, ren u, ren b)
4747+ in
4848+ ren t
4949+5050+let substitute x s body =
5151+ let fv_s = free_variables s in
5252+ let fv_b = free_variables body in
5353+ let avoid0 = SSet.union fv_s fv_b in
5454+ let rec sub avoid = function
5555+ | Var y -> if y = x then s else Var y
5656+ | App (f, a) -> App (sub avoid f, sub avoid a)
5757+ | Lam (y, b) ->
5858+ if y = x then Lam (y, b)
5959+ else if (not (SSet.mem y fv_s)) || not (SSet.mem x (free_variables b)) then
6060+ Lam (y, sub avoid b)
6161+ else
6262+ let y' = fresh_name avoid y in
6363+ Lam (y', sub (SSet.add y' avoid) (rename y y' b))
6464+ | Subst (y, u, b) ->
6565+ if y = x then Subst (y, sub avoid u, b)
6666+ else if (not (SSet.mem y fv_s)) || not (SSet.mem x (free_variables b)) then
6767+ Subst (y, sub avoid u, sub avoid b)
6868+ else
6969+ let y' = fresh_name avoid y in
7070+ Subst (y', sub avoid u, sub (SSet.add y' avoid) (rename y y' b))
7171+ in
7272+ sub avoid0 body
7373+7474+let step_limit = 10000
7575+7676+let rec elim_subst = function
7777+ | Var x -> Var x
7878+ | Lam (x, b) -> Lam (x, elim_subst b)
7979+ | App (f, a) -> App (elim_subst f, elim_subst a)
8080+ | Subst (x, s, b) -> substitute x (elim_subst s) (elim_subst b)
8181+8282+let is_value = function Lam _ -> true | _ -> false
8383+8484+let rec step = function
8585+ | App (Lam (x, b), a) -> Some (substitute x a b)
8686+ | App (f, a) when not (is_value f) -> begin
8787+ match step f with
8888+ | Some f' -> Some (App (f', a))
8989+ | None -> begin
9090+ match step a with Some a' -> Some (App (f, a')) | None -> None
9191+ end
9292+ end
9393+ | App (f, a) -> begin
9494+ match step a with Some a' -> Some (App (f, a')) | None -> None
9595+ end
9696+ | Lam (x, b) -> begin
9797+ match step b with Some b' -> Some (Lam (x, b')) | None -> None
9898+ end
9999+ | _ -> None
100100+101101+let normalise t =
102102+ let t = elim_subst t in
103103+ let rec go n t =
104104+ if n >= step_limit then t
105105+ else match step t with Some t' -> go (n + 1) t' | None -> t
106106+ in
107107+ go 0 t
108108+109109+let normalise_counted t =
110110+ let count = ref 0 in
111111+ let maxd = ref 0 in
112112+ let rec walk d t =
113113+ incr count;
114114+ if d > !maxd then maxd := d;
115115+ match t with
116116+ | Var _ -> ()
117117+ | Lam (_, b) -> walk (d + 1) b
118118+ | App (f, a) -> walk (d + 1) f; walk (d + 1) a
119119+ | Subst (_, s, b) -> walk (d + 1) s; walk (d + 1) b
120120+ in
121121+ let t = elim_subst t in
122122+ walk 0 t;
123123+ let rec go n t =
124124+ if n >= step_limit then t
125125+ else
126126+ match step t with
127127+ | Some t' -> walk 0 t'; go (n + 1) t'
128128+ | None -> t
129129+ in
130130+ (go 0 t, !count, !maxd)
131131+132132+let convert_from_named t = t
133133+let convert_to_named t = t
134134+135135+let alpha_equivalent t1 t2 =
136136+ let rec go d e1 e2 a b =
137137+ match (a, b) with
138138+ | Var x, Var y -> begin
139139+ match (List.assoc_opt x e1, List.assoc_opt y e2) with
140140+ | Some i, Some j -> i = j
141141+ | None, None -> x = y
142142+ | _ -> false
143143+ end
144144+ | Lam (x, b1), Lam (y, b2) -> go (d + 1) ((x, d) :: e1) ((y, d) :: e2) b1 b2
145145+ | App (f1, a1), App (f2, a2) -> go d e1 e2 f1 f2 && go d e1 e2 a1 a2
146146+ | _ -> false
147147+ in
148148+ go 0 [] [] (normalise t1) (normalise t2)
149149+150150+let alpha_equivalent_counted t1 t2 =
151151+ let count = ref 0 in
152152+ let maxd = ref 0 in
153153+ let rec go d e1 e2 a b =
154154+ incr count;
155155+ if d > !maxd then maxd := d;
156156+ match (a, b) with
157157+ | Var x, Var y -> begin
158158+ match (List.assoc_opt x e1, List.assoc_opt y e2) with
159159+ | Some i, Some j -> i = j
160160+ | None, None -> x = y
161161+ | _ -> false
162162+ end
163163+ | Lam (x, b1), Lam (y, b2) -> go (d + 1) ((x, d) :: e1) ((y, d) :: e2) b1 b2
164164+ | App (f1, a1), App (f2, a2) -> go d e1 e2 f1 f2 && go d e1 e2 a1 a2
165165+ | _ -> false
166166+ in
167167+ let r = go 0 [] [] (normalise t1) (normalise t2) in
168168+ (r, !count, !maxd)
···11+type t =
22+ | Var of string
33+ | Lam of string * t
44+ | App of t * t
55+ | Subst of string * t * t
66+77+module SSet : Set.S with type elt = string
88+99+val free_variables : t -> SSet.t
1010+val free_variable_list : t -> string list
1111+val pretty : t -> string
1212+val fresh_name : SSet.t -> string -> string
1313+val substitute : string -> t -> t -> t
1414+val alpha_equivalent : t -> t -> bool
1515+val alpha_equivalent_counted : t -> t -> bool * int * int
1616+val normalise : t -> t
1717+val normalise_counted : t -> t * int * int
1818+val elim_subst : t -> t
1919+val convert_from_named : t -> t
2020+val convert_to_named : t -> t
2121+val step_limit : int
···11+type token =
22+ | TBackslash
33+ | TDot
44+ | TLParen
55+ | TRParen
66+ | TLBrack
77+ | TRBrack
88+ | TColonEq
99+ | TVar of string
1010+1111+exception Parse_error of string
1212+1313+let is_var_start c = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
1414+1515+let is_var_char c =
1616+ is_var_start c || (c >= '0' && c <= '9') || c = '_' || c = '\'' || c = '-'
1717+1818+let lex s =
1919+ let n = String.length s in
2020+ let rec go i acc =
2121+ if i >= n then List.rev acc
2222+ else
2323+ match s.[i] with
2424+ | ' ' | '\t' | '\n' | '\r' -> go (i + 1) acc
2525+ | '\\' -> go (i + 1) (TBackslash :: acc)
2626+ | '.' -> go (i + 1) (TDot :: acc)
2727+ | '(' -> go (i + 1) (TLParen :: acc)
2828+ | ')' -> go (i + 1) (TRParen :: acc)
2929+ | '[' -> go (i + 1) (TLBrack :: acc)
3030+ | ']' -> go (i + 1) (TRBrack :: acc)
3131+ | ':' ->
3232+ if i + 1 < n && s.[i + 1] = '=' then go (i + 2) (TColonEq :: acc)
3333+ else raise (Parse_error ("unexpected ':' at position " ^ string_of_int i))
3434+ | c when is_var_start c ->
3535+ let j = ref (i + 1) in
3636+ while !j < n && is_var_char s.[!j] do
3737+ incr j
3838+ done;
3939+ go !j (TVar (String.sub s i (!j - i)) :: acc)
4040+ | c ->
4141+ raise
4242+ (Parse_error
4343+ (Printf.sprintf "unexpected character %C at position %d" c i))
4444+ in
4545+ go 0 []
4646+4747+let parse s =
4848+ let toks = Array.of_list (lex s) in
4949+ let n = Array.length toks in
5050+ let pos = ref 0 in
5151+ let peek () = if !pos < n then Some toks.(!pos) else None in
5252+ let next () =
5353+ let t = peek () in
5454+ incr pos;
5555+ t
5656+ in
5757+ let expect tok what =
5858+ match next () with
5959+ | Some t when t = tok -> ()
6060+ | _ -> raise (Parse_error ("expected " ^ what))
6161+ in
6262+ let starts_atom = function
6363+ | Some (TVar _) | Some TLParen | Some TBackslash | Some TLBrack -> true
6464+ | _ -> false
6565+ in
6666+ let rec term () =
6767+ match peek () with
6868+ | Some TBackslash -> lam ()
6969+ | Some TLBrack -> subst ()
7070+ | _ -> app ()
7171+ and lam () =
7272+ ignore (next ());
7373+ match next () with
7474+ | Some (TVar v) ->
7575+ expect TDot "'.'";
7676+ let body = term () in
7777+ Named.Lam (v, body)
7878+ | _ -> raise (Parse_error "expected variable after '\\'")
7979+ and subst () =
8080+ ignore (next ());
8181+ match next () with
8282+ | Some (TVar v) ->
8383+ expect TColonEq "':='";
8484+ let s = term () in
8585+ expect TRBrack "']'";
8686+ let body = term () in
8787+ Named.Subst (v, s, body)
8888+ | _ -> raise (Parse_error "expected variable after '['")
8989+ and app () =
9090+ let lhs = atom () in
9191+ let rec args lhs =
9292+ if starts_atom (peek ()) then args (Named.App (lhs, atom ())) else lhs
9393+ in
9494+ args lhs
9595+ and atom () =
9696+ match next () with
9797+ | Some (TVar v) -> Named.Var v
9898+ | Some TLParen ->
9999+ let t = term () in
100100+ expect TRParen "')'";
101101+ t
102102+ | Some TBackslash -> begin
103103+ match next () with
104104+ | Some (TVar v) ->
105105+ expect TDot "'.'";
106106+ Named.Lam (v, term ())
107107+ | _ -> raise (Parse_error "expected variable after '\\'")
108108+ end
109109+ | Some TLBrack -> begin
110110+ match next () with
111111+ | Some (TVar v) ->
112112+ expect TColonEq "':='";
113113+ let s = term () in
114114+ expect TRBrack "']'";
115115+ Named.Subst (v, s, term ())
116116+ | _ -> raise (Parse_error "expected variable after '['")
117117+ end
118118+ | _ -> raise (Parse_error "expected a term")
119119+ in
120120+ let t = term () in
121121+ if !pos <> n then raise (Parse_error "trailing input");
122122+ t
123123+124124+let parse_result s =
125125+ match parse s with t -> Ok t | exception Parse_error m -> Error m