mirror of
https://github.com/KevinMidboe/linguist.git
synced 2025-10-29 09:40:21 +00:00
344 lines
8.1 KiB
ReasonML
344 lines
8.1 KiB
ReasonML
open Format;
|
||
|
||
let module Endo = {
|
||
type t 'a = 'a => 'a;
|
||
};
|
||
|
||
let module Syntax = {
|
||
let module Var = {
|
||
type t = int;
|
||
};
|
||
let module Term = {
|
||
type t =
|
||
| App t t
|
||
| Lam t
|
||
| Var Var.t
|
||
;
|
||
};
|
||
let module Sub = {
|
||
type t 'a =
|
||
| Cmp (t 'a) (t 'a)
|
||
| Dot 'a (t 'a)
|
||
| Id
|
||
| Shift
|
||
;
|
||
|
||
let map f sgm => {
|
||
let rec go = fun
|
||
| Cmp sgm0 sgm1 => Cmp (go sgm0) (go sgm1)
|
||
| Dot a sgm => Dot (f a) (go sgm)
|
||
| Id => Id
|
||
| Shift => Shift
|
||
;
|
||
go sgm;
|
||
};
|
||
|
||
let rec apply sgm e =>
|
||
switch (sgm, e) {
|
||
| (sgm, Term.App e0 e1) => Term.App (apply sgm e0) (apply sgm e1)
|
||
| (sgm, Term.Lam e) => Term.Lam (apply (Dot (Term.Var 0) (Cmp sgm Shift)) e)
|
||
| (Dot e _, Term.Var 0) => e
|
||
| (Dot _ sgm, Term.Var i) => apply sgm (Term.Var (i - 1))
|
||
| (Id, Term.Var i) => Term.Var i
|
||
| (Shift, Term.Var i) => Term.Var (i + 1)
|
||
| (Cmp rho sgm, e) => apply sgm (apply rho e)
|
||
};
|
||
};
|
||
};
|
||
|
||
let module Zip = {
|
||
open Syntax;
|
||
type t 'a =
|
||
| App0 (t 'a) 'a
|
||
| App1 'a (t 'a)
|
||
| Halt
|
||
| Lam (t 'a)
|
||
;
|
||
|
||
let map f sgm => {
|
||
let rec go = fun
|
||
| App0 zip e1 => App0 (go zip) (f e1)
|
||
| App1 e0 zip => App1 (f e0) (go zip)
|
||
| Halt => Halt
|
||
| Lam zip => Lam (go zip)
|
||
;
|
||
go sgm;
|
||
};
|
||
|
||
let rec apply zip acc => switch zip {
|
||
| App0 zip e1 => apply zip (Term.App acc e1)
|
||
| App1 e0 zip => apply zip (Term.App e0 acc)
|
||
| Halt => acc
|
||
| Lam zip => apply zip (Term.Lam acc)
|
||
};
|
||
};
|
||
|
||
let module Clo = {
|
||
open Syntax;
|
||
type t =
|
||
| Clo Term.t (Sub.t t);
|
||
let rec from (Clo term sgm) => Sub.apply (Sub.map from sgm) term;
|
||
};
|
||
|
||
let module Pretty = {
|
||
let module Delim = {
|
||
type t = string;
|
||
let pp prev next fmt token => if (prev < next) { fprintf fmt "%s" token };
|
||
};
|
||
let module Prec = {
|
||
type t = int;
|
||
open Syntax.Term;
|
||
let calc = fun
|
||
| App _ _ => 1
|
||
| Lam _ => 2
|
||
| Var _ => 0
|
||
;
|
||
};
|
||
let module Name = {
|
||
type t = string;
|
||
|
||
let suffix = {
|
||
let script = fun
|
||
| 0 => "₀"
|
||
| 1 => "₁"
|
||
| 2 => "₂"
|
||
| 3 => "₃"
|
||
| 4 => "₄"
|
||
| 5 => "₅"
|
||
| 6 => "₆"
|
||
| 7 => "₇"
|
||
| 8 => "₈"
|
||
| 9 => "₉"
|
||
| _ => failwith "bad subscript";
|
||
let rec go acc => fun
|
||
| 0 => acc
|
||
| n => go (script (n mod 10) ^ acc) (n / 10);
|
||
go ""
|
||
};
|
||
|
||
let gen = {
|
||
let offset = 97;
|
||
let width = 26;
|
||
fun () i => {
|
||
let code = i mod width + offset;
|
||
let char = Char.chr code;
|
||
let prime = i / width;
|
||
let suffix = suffix prime;
|
||
let name = Char.escaped char ^ suffix;
|
||
Some name;
|
||
}
|
||
};
|
||
};
|
||
|
||
let module Env = {
|
||
type t = {
|
||
used: list Name.t,
|
||
rest: Stream.t Name.t,
|
||
};
|
||
let mk () => {
|
||
let used = [];
|
||
let rest = Stream.from @@ Name.gen ();
|
||
{ used, rest };
|
||
};
|
||
};
|
||
|
||
type printer 'a = Env.t => Prec.t => formatter => 'a => unit;
|
||
|
||
let module Term = {
|
||
open Syntax.Term;
|
||
let rec pp ({ Env.used: used, rest } as env) prev fmt e => {
|
||
let next = Prec.calc e;
|
||
switch e {
|
||
| App e0 e1 =>
|
||
fprintf fmt "@[%a%a@ %a%a@]"
|
||
(Delim.pp prev next) "("
|
||
(pp env 1) e0
|
||
(pp env 0) e1
|
||
(Delim.pp prev next) ")"
|
||
| Lam e =>
|
||
let name = Stream.next rest;
|
||
let env = { ...env, Env.used: [name, ...used] };
|
||
fprintf fmt "%aλ%a.%a%a"
|
||
(Delim.pp prev next) "("
|
||
(pp_print_string) name
|
||
(pp env next) e
|
||
(Delim.pp prev next) ")"
|
||
| Var index =>
|
||
fprintf fmt "%s" @@ try (List.nth used index) {
|
||
| _ => "#" ^ string_of_int index
|
||
}
|
||
}
|
||
};
|
||
};
|
||
|
||
let module Sub = {
|
||
open Syntax.Sub;
|
||
let rec pp pp_elem env prev fmt => fun
|
||
| Cmp sgm1 sgm0 =>
|
||
fprintf fmt "@[%a;@ %a@]"
|
||
(pp pp_elem env prev) sgm1
|
||
(pp pp_elem env prev) sgm0
|
||
| Dot e sgm =>
|
||
fprintf fmt "@[%a@ ·@ %a@]"
|
||
(pp_elem env prev) e
|
||
(pp pp_elem env prev) sgm
|
||
| Id =>
|
||
fprintf fmt "ι"
|
||
| Shift =>
|
||
fprintf fmt "↑"
|
||
;
|
||
};
|
||
|
||
let module Clo = {
|
||
let rec pp env prev fmt (Clo.Clo e sgm) => {
|
||
let next = Prec.calc e;
|
||
fprintf fmt "@[%a%a%a[%a]@]"
|
||
(Delim.pp prev next) "("
|
||
(Term.pp env next) e
|
||
(Delim.pp prev next) ")"
|
||
(Sub.pp pp env next) sgm
|
||
};
|
||
};
|
||
|
||
let module Zip = {
|
||
open Zip;
|
||
let rec pp pp_elem env prev fmt => fun
|
||
| App0 zip elem =>
|
||
fprintf fmt "inl@[<v -1>⟨@,%a@,%a⟩@]"
|
||
(pp pp_elem env prev) zip
|
||
(pp_elem env prev) elem
|
||
| App1 elem zip =>
|
||
fprintf fmt "inr@[<v -1>⟨@,%a@,%a⟩@]"
|
||
(pp_elem env prev) elem
|
||
(pp pp_elem env prev) zip
|
||
| Halt =>
|
||
fprintf fmt "halt"
|
||
| Lam zip =>
|
||
fprintf fmt "lam@[<v -1>⟨@,%a⟩@]"
|
||
(pp pp_elem env prev) zip
|
||
;
|
||
};
|
||
};
|
||
|
||
let module Machine = {
|
||
type t = {
|
||
clo: Clo.t,
|
||
ctx: Zip.t Clo.t,
|
||
};
|
||
|
||
let into e => {
|
||
open Clo;
|
||
open Syntax.Sub;
|
||
let clo = Clo e Id;
|
||
let ctx = Zip.Halt;
|
||
{ clo, ctx }
|
||
};
|
||
|
||
let from { clo, ctx } => Zip.apply (Zip.map Clo.from ctx) (Clo.from clo);
|
||
|
||
let pp fmt rule state => {
|
||
fprintf fmt "@[<v>ctx ::@[<v -5>@,%a@]@,clo ::@[<v -5>@,%a@]@,rule ::@[<v -5>@,%a@]@,term ::@[<v -5>@,%a@]@]@."
|
||
(Pretty.Zip.pp Pretty.Clo.pp (Pretty.Env.mk ()) 2) state.ctx
|
||
(Pretty.Clo.pp (Pretty.Env.mk ()) 2) state.clo
|
||
(pp_print_string) rule
|
||
(Pretty.Term.pp (Pretty.Env.mk ()) 2) (from state)
|
||
};
|
||
|
||
let halted state => {
|
||
open Clo;
|
||
open Syntax.Sub;
|
||
open Syntax.Term;
|
||
switch state {
|
||
| { clo: Clo (Var _) Id, _ } => true
|
||
| _ => false
|
||
} [@warning "-4"];
|
||
};
|
||
|
||
let step state => {
|
||
open Clo;
|
||
open Syntax.Sub;
|
||
open Syntax.Term;
|
||
let rule = ref "";
|
||
let state = switch state {
|
||
/* left */
|
||
| { clo: Clo (App e0 e1) sgm, ctx } =>
|
||
let clo = Clo e0 sgm;
|
||
let ctx = Zip.App0 ctx (Clo e1 sgm);
|
||
rule := "LEFT";
|
||
{ clo, ctx };
|
||
/* beta */
|
||
| { clo: Clo (Lam e) sgm, ctx: Zip.App0 ctx c0 } =>
|
||
let clo = Clo e (Cmp (Dot c0 sgm) Id);
|
||
rule := "BETA";
|
||
{ clo, ctx };
|
||
/* lambda */
|
||
| { clo: Clo (Lam e) sgm, ctx } =>
|
||
let clo = Clo e (Cmp (Dot (Clo (Var 0) Id) (Cmp sgm Shift)) Id);
|
||
let ctx = Zip.Lam ctx;
|
||
rule := "LAMBDA";
|
||
{ clo, ctx };
|
||
/* associate */
|
||
| { clo: Clo (Var n) (Cmp (Cmp pi rho) sgm), ctx } =>
|
||
let clo = Clo (Var n) (Cmp pi (Cmp rho sgm));
|
||
rule := "ASSOCIATE";
|
||
{ clo, ctx };
|
||
/* head */
|
||
| { clo: Clo (Var 0) (Cmp (Dot (Clo e pi) _) sgm), ctx } =>
|
||
let clo = Clo e (Cmp pi sgm);
|
||
rule := "HEAD";
|
||
{ clo, ctx };
|
||
/* tail */
|
||
| { clo: Clo (Var n) (Cmp (Dot (Clo _ _) rho) sgm), ctx } =>
|
||
let clo = Clo (Var (n - 1)) (Cmp rho sgm);
|
||
rule := "TAIL";
|
||
{ clo, ctx };
|
||
/* shift */
|
||
| { clo: Clo (Var n) (Cmp Shift sgm), ctx } =>
|
||
let clo = Clo (Var (n + 1)) sgm;
|
||
rule := "SHIFT";
|
||
{ clo, ctx };
|
||
/* id */
|
||
| { clo: Clo (Var n) (Cmp Id sgm), ctx } =>
|
||
let clo = Clo (Var n) sgm;
|
||
rule := "ID";
|
||
{ clo, ctx };
|
||
| _ =>
|
||
pp std_formatter !rule state;
|
||
failwith "bad state";
|
||
} [@warning "-4"];
|
||
pp std_formatter !rule state;
|
||
state;
|
||
};
|
||
|
||
let norm e => {
|
||
let count = ref 0;
|
||
let state = ref (into e);
|
||
while (not (halted !state)) {
|
||
fprintf std_formatter "@\n--- step[%d] ---@\n" !count;
|
||
incr count;
|
||
state := step !state;
|
||
};
|
||
from !state;
|
||
};
|
||
};
|
||
|
||
let module Test = {
|
||
open Syntax.Term;
|
||
let l e => Lam e;
|
||
let ( *@ ) e0 e1 => App e0 e1;
|
||
let ff = l (l (Var 1));
|
||
let tt = l (l (Var 0));
|
||
let zero = l (l (Var 1));
|
||
let succ = l (l (l (Var 0 *@ Var 2)));
|
||
let one = succ *@ zero;
|
||
let two = succ *@ one;
|
||
let three = succ *@ two;
|
||
let const = l (l (Var 1));
|
||
let fix = l (l (Var 1 *@ (Var 0 *@ Var 0)) *@ l (Var 1 *@ (Var 0 *@ Var 0)));
|
||
let add = fix *@ l (l (l (Var 1 *@ Var 0 *@ l (succ *@ Var 3 *@ Var 0 *@ Var 1))));
|
||
let init = l (l (Var 0) *@ l (l (Var 1)));
|
||
};
|
||
|
||
let module Run = {
|
||
let go () => Machine.norm Test.init;
|
||
}; |