CC, SCC, CK, CEK Machines.

dvanhorn <[email protected]>
Newsgroups gmane.org.ballistichelmet.lambda
Message-ID <[email protected]>
I've added the CEK machine to the list and refactored things into modules.

Coming soon: SECD machine, parser.

-d




(*                                                              -*- OCaml -*-
   Copyright (c) 2003 David Van Horn
   Licensed under the Academic Free License version 2.0

   The CC, CK and CEK Machines -- ISWIM interpreters
   [email protected]

   The specification of the Cc.eval, Ck.eval and Cek.eval functions come from
   Felleisen & Flatt -- Programming Languages and Lambda Calculi.

   TODO: Make delta and base types parameterizable.
*)

module Term =
  struct
    open List
    type term =
      | Value of value
      | App of term * term
      | AppPrim of primop * term list
    and value =
      | Base of base
      | Var of var
      | Lambda of var * term
    and var = string
    and base = Int of int | False | True
    and primop = Add1 | Sub1 | Plus | Minus
    and result = Result of base | Function

(*
   span : ('a -> bool) -> 'a list -> 'a list * 'a list
   An old scheme friend

   Span splits the list into the longest initial prefix whose elements all
   satisfy the predicate f, and the remaining tail. Cf. Olin Shivers's SRFI-1.
*)
    let span f ls =
      let rec recur = function
	| [] -> [], []
	| x::rest as l ->
	    if f x then let (prefix, suffix) = recur rest in
	    x::prefix, suffix
	    else [], l
      in
      recur ls
	
(* delta : primop -> base list -> term *)
    let delta op args =
      match args with
	Base(Int x)::rest ->
	  let y =
	    match rest with
	    | [Base(Int y)] -> y
	    | [] -> 1
	  in
	  let result =
	    match op with
	    | Add1 | Plus -> x+y
	    | Sub1 | Minus ->
		let sum = x-y in
		if sum <= 0 then 0 else sum
	  in
	  Value(Base (Int result))
	
    let is_value = function
      | Value v -> true
      | _ -> false
	    	
    let is_base = function
      | Value (Base b) -> true
      | _ -> false
	
(*
   gen_var : unit -> string

   Returns a fresh variable name of the form "zn" where n is an integer
   (so don't use variable of this form!).
*)
    let gen_var =
      let i = ref 0 in
      fun () ->
	i := !i+1;
	"z" ^ (string_of_int !i)
		
(*
   substitute : term -> var -> value -> term

   Substitutes v for x in m.
*)
    let rec substitute m x v =
      match m with
      | Value(Base b) -> m
      | Value(Var y) ->
	  if x=y then Value v
	  else m
      | Value(Lambda(y,n)) ->
	  if x=y then m
	  else let z = gen_var () in
	  Value(Lambda(z, (substitute (substitute n y (Var z)) x v)))
      | App(n,p) ->
	  App(substitute n x v, substitute p x v)
      | AppPrim(o, tlist) ->
	  AppPrim(o, (map (fun t -> substitute t x v) tlist))
	
  end

module Cc =
  struct
    open Term
    open List
    type context =
      | CEmptyHole
      | CInHole of context * context
      | CAppV of value * context
      | CAppE of context * term
      | CAppPrim of primop * value list * context * term list

(*
   cc : term * context -> term * context

   The CC Machine state transition function.
*)
(*
    let cc = function
      | App(m,n), e
	when not (is_value m) ->
	  m, CInHole(e, CAppE(CEmptyHole, n))
      | App(Value(v), m), e
	when not (is_value m) ->
	  m, CInHole(e, CAppV(v, CEmptyHole))
      | AppPrim(o, args), e
	when for_all is_base args ->
	  let blist = (map (fun (Value v) -> v) args) in
	  delta o blist, e
      | AppPrim(o, args), e ->
	  let (vlist, m::tlist) = span is_value args in
	  let vlist = map (fun (Value v) -> v) vlist in
	  m, CInHole(e, CAppPrim(o, vlist, CEmptyHole, tlist))
      | App(Value(Lambda(x,m)), Value v), e ->
	  substitute m x v, e
      | Value v, CInHole(e, CAppV(u, CEmptyHole)) ->
	  App(Value u, Value v), e
      | Value v, CInHole(e, CAppE(CEmptyHole, n)) ->
	  App(Value v, n), e
      | Value v, CInHole(e, CAppPrim(o, vlist, CEmptyHole, tlist)) ->
	  let vlist = map (fun x -> (Value x)) vlist in
	  AppPrim(o, vlist@[(Value v)]@tlist), e
*)

(*
   Simplified version of CC state transitions.
*)
    let cc = function
      | App(m,n), e ->
	  m, CInHole(e, CAppE(CEmptyHole, n))
      | AppPrim(o, m::tlist), e ->
	  m, CInHole(e, CAppPrim(o, [], CEmptyHole, tlist))
      | Value v, CInHole(e, CAppV(Lambda (x,m), CEmptyHole)) ->
	  substitute m x v, e
      |  Value v, CInHole(e, CAppE(CEmptyHole, n)) ->
	  n, CInHole(e, CAppV(v, CEmptyHole))
      | (Value b) as v, CInHole(e, CAppPrim(o, blist, CEmptyHole, []))
	when is_base v ->
	  delta o (blist@[b]), e
      | Value v, CInHole(e, CAppPrim(o, vlist, CEmptyHole, n::tlist)) ->
	  n, CInHole(e, CAppPrim(o, vlist@[v], CEmptyHole, tlist))
	
    let eval term =
      let rec loop = function
	| Value(Base b), CEmptyHole -> (Result b)
	| Value(Lambda (x,m)), CEmptyHole -> Function
	| term, context -> loop (cc (term, context))
      in
      loop (term, CEmptyHole)
  end

(* For use in the CK Machine *)
module Ck =
  struct
    open Term
    open List
    type kont =
      | Mt
      | Fun of value * kont
      | Arg of term * kont
      | Opd of value list * primop * term list * kont

(*
   ck : term * kont -> term * kont

   The CK Machine state transition function.
 *)
    let ck = function
      | App(m,n), k ->
	  m, Arg(n, k)
      | AppPrim(o, m::tlist), k ->
	  m, Opd([], o, tlist, k)
      | Value v, Fun(Lambda (x,m), k) ->
	  substitute m x v, k
      | Value v, Arg(n, k) ->
	  n, Fun(v, k)
      | Value(Base b), Opd(vlist, o, [], k)
	when for_all is_base (map (fun v -> (Value v)) vlist) ->
	  delta o (vlist@[(Base b)]), k
      | Value v, Opd(vlist, o, n::tlist, k) ->
	  n, Opd([v]@vlist, o, tlist, k)
	
    let eval term =
      let rec loop = function
	| Value(Base b), Mt -> (Result b)
	| Value(Lambda (x,m)), Mt -> Function
	| term, k -> loop (ck (term, k))
      in
      loop (term, Mt)	
  end

module Cek =
  struct
    open Term
    open List
    exception UnboundVariable
    type k =
      | Mt
      | Fun of value * environment * k
      | Arg of term * environment * k
      | Opd of (value * environment) list * primop * (term * environment) list * k
    and environment = EmptyEnv | Env of (term -> term * environment)

(* In the CEK machine we use environment update rather than substitution *)

(* environment -> term -> term * environment *)
    let environment_lookup e t =
      match e with
      | Env(f) -> f t
      | EmptyEnv -> raise UnboundVariable
	
(*  environment -> term -> term -> environment -> environment *)
    let environment_update e x t e' =
      Env (fun term ->
	if (term = x) then (t,e')
	else environment_lookup e term)

(*
   cek : (term * environment) * k -> (term * environment) * k

   The CEK Machine state transition function.
*)
    let cek = function
      | (App(m,n), e), k ->
	  (m, e), Arg(n, e, k)
      | (AppPrim(o, m::tlist), e), k ->
	  (m, e), Opd([], o, (map (fun t -> (t, e)) tlist), k)
      | (Value(Var _) as x, e), k ->
	  environment_lookup e x, k
      | ((Value _) as v, e), Fun(Lambda(x,m), e', k) ->
	  (m, environment_update e' (Value (Var x)) v e), k
      | (Value v, e), Arg(n, e', k) ->
	  (n, e'), Fun(v, e, k)
      | (Value(Base b), e), Opd(vclosures, o, [], k) ->
	  let blist = (map (function (v,e) -> v) vclosures) in
	  (delta o (blist@[(Base b)]), EmptyEnv), k
      | (Value v, e), Opd(vclosures, o, (n,e')::tclosures, k) ->
	  (n, e'), Opd((v,e)::vclosures, o, tclosures, k)
	
    let eval term =
      let rec loop = function
	| (Value(Base b), e), Mt -> (Result b)
	| (Value(Lambda (x,m)), e), Mt -> Function
	| (t, e), k -> loop (cek ((t, e), k))
      in
      loop ((term, EmptyEnv), Mt)
  end

;;

open Term;;

Cc.eval (App (Value (Lambda ("x", Value (Var "x"))),
	      App (Value (Lambda ("x", Value (Var "x"))),
		   Value (Base (Int 5)))))
;;

Ck.eval (App (Value (Lambda ("x", Value (Var "x"))),
	      App (Value (Lambda ("x", Value (Var "x"))),
		   Value (Base (Int 5)))))
;;
Cek.eval(App (Value (Lambda ("x", Value (Var "x"))),
	      App (Value (Lambda ("x", Value (Var "x"))),
		   Value (Base (Int 5)))))
;;
(* - : result = Result (Int 5) *)

Cc.eval (App (Value (Lambda ("x", AppPrim(Add1, [Value (Var "x")]))),
	      App (Value (Lambda ("x", Value (Var "x"))),
		   Value (Base (Int 5)))))
;;
Ck.eval (App (Value (Lambda ("x", AppPrim(Add1, [Value (Var "x")]))),
	      App (Value (Lambda ("x", Value (Var "x"))),
		   Value (Base (Int 5)))))
;;
Cek.eval (App (Value (Lambda ("x", AppPrim(Add1, [Value (Var "x")]))),
	       App (Value (Lambda ("x", Value (Var "x"))),
		    Value (Base (Int 5)))))
;;
(* - : result = Result (Int 6) *)

(*
   Useful for seeing how evaluation proceeds.
   #trace cc;;
*)
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.