(*
 Code from the 3rd lecture. *)

(* ************** References ******************* *)
val r = (ref 5) ;
!r ;
fun ! (ref n) = n;
r := 6 + 2 ;


val n = ref 4;
val w1 =
  while (!n > 0)
  do (print (Int.toString (!n) ^ "\n");
      n := (!n) - 1 );


fun fact3 n =
  let val ans = ref 1
      val count = ref n
  in while (!count > 0)
       do (ans := !ans * !count
          ; count := !count - 1);
     !ans
  end;

(************** Accumulating functions ***** *)

fun fact4 n =
    let fun loop 0 ans = ans
          | loop n ans = loop (n-1) (n*ans)
    in loop n 1 end;

datatype Tree = Tip | Node of Tree * int * Tree;

fun flat3 x =
  let fun help Tip ans = ans
        | help (Node(x,y,z)) ans =
                  help x (y::(help z ans))
  in help x [] end;


(* ************************************************** *)

datatype RE
  = Empty
  | Union of RE * RE
  | Concat of RE * RE
  | Star of RE
  | C of char;

val re1 = Concat(Union(C #"+",Union(C #"-",Empty))
                ,Concat(C #"D",Star (C #"D")));


datatype Label = Epsilon | Char of char;
type Start = int;
type Finish = int;
datatype Edge = Edge of Start * Label * Finish;

val next = ref 0;
fun new () = let val ref n = next
             in (next := n+1; n) end;

type Nfa = (int * int * Edge list)


fun nfa Empty =
        let val s = new()
            val f = new()
        in (s,f,[Edge(s,Epsilon,f)]):Nfa end
  | nfa (C x) =
        let val s = new()
            val f = new()
        in (s,f,[Edge(s,Char x,f)]) end
  | nfa (Union(x,y)) =
        let val (sx,fx,xes) = nfa x
            val (sy,fy,yes) = nfa y
            val s = new()
            val f = new()
            val newes = [Edge(s,Epsilon,sx),Edge(s,Epsilon,sy)
                        ,Edge(fx,Epsilon,f),Edge(fy,Epsilon,f)]
        in (s,f,newes @ xes @ yes) end
  | nfa (Concat(x,y)) =
        let val (sx,fx,xes) = nfa x
            val (sy,fy,yes) = nfa y
        in (sx,fy,(Edge(fx,Epsilon,sy))::(xes @ yes)) end
  | nfa (Star r) =
        let val (sr,fr,res) = nfa r
            val s = new()
            val f = new()
            val newes = [Edge(s,Epsilon,sr),Edge(fr,Epsilon,f)
                        ,Edge(s,Epsilon,f),Edge(f,Epsilon,s)]
        in (s,f,newes @ res) end

val ex6 = nfa re1;

(* 
*)