mirror of
				https://github.com/KevinMidboe/linguist.git
				synced 2025-10-29 17:50:22 +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;
 | 
						||
}; |