(*
*)
val empty = "empty";
fun emptyP x = x=empty;
datatype Grammar = Gram of
(string list) * (* Non Terminals *)
(string list) * (* Terminals *)
(string * (string list)) list * (* Productions *)
string; (* Start symbol *)
val g1 = Gram(["Expr","Term","Factor","Expr'","Term'"]
,["ident","+","-","*","div","(",")","num"]
,[("Expr",["Term","Expr'"])
,("Expr'",["+","Term","Expr'"])
,("Expr'",["-","Term","Expr'"])
,("Expr'",[])
,("Term",["Factor","Term'"])
,("Term'",["*","Factor","Term'"])
,("Term'",["div","Factor","Term'"])
,("Term'",[])
,("Factor",["(","Expr",")"])
,("Factor",["num"])
,("Factor",["ident"])
]
,"Expr");
(* ********* Operations on Grammars *********************** *)
fun termP (Gram(nts,ts,ps,s)) x = List.exists (fn y => y=x) ts;
fun nontermP (Gram(nts,ts,ps,s)) x = List.exists (fn y => y=x) nts;
fun rhssFor (Gram(nts,ts,ps,start)) symbol =
let fun test (rhs,lhs) = rhs=symbol
fun lhs (r,l) = l
in List.map lhs (List.filter test ps) end;
(* ******************* Operations on Tables ************************ *)
(* a table is (string * ref item) list *)
(* We can update the reference to indicate updating the table *)
(* Create a table with all slots initialized *)
fun init symbols initf =
let fun f x = (x,ref (initf x))
in List.map f symbols end;
(* Update the slot at "s" with the function "updatef" *)
(* returns true if it makes a change, false otherwise *)
fun update s updatef pairs =
case List.find (fn (y,r) => s=y) pairs of
SOME(_,r) => let val old = !r
val new = updatef old
in if new=old
then false
else (r := new; true) end
| NONE => false
(* ***** Create a Table for nullable symbols from a grammar **** *)
fun nullTable (gram as(Gram(nts,ts,ps,start))) =
let val allsymbols = nts @ ts
val table = init allsymbols (fn _ => false)
val changed = ref true
fun onePass [] = false (* returns true if it makes a change *)
| onePass (x::xs) =
let val rhss = rhssFor gram x
fun nullify b = List.exists (nullableRhs table) rhss
val b1 = update x nullify table
val b2 = onePass xs
in b1 orelse b2 end
in while (!changed) do
(changed := false; changed := onePass allsymbols);
table
end
and nullable table s =
case List.find (fn (y,r) => y=s) table of
NONE => false
| SOME(_,r) => !r
and nullableRhs table [] = true
| nullableRhs table (x::xs) =
(nullable table x) andalso (nullableRhs table xs);
val NullT = nullTable g1;
(* ************ normalizing lists of strings ************ *)
val filter = List.filter;
fun sort' comp [] ans = ans
| sort' comp [x] ans = x :: ans
| sort' comp (x::xs) ans =
let fun LE x y = case comp(x,y) of GREATER => false | _ => true
fun GT x y = case comp(x,y) of GREATER => true | _ => false
val small = filter (GT x) xs
val big = filter (LE x) xs
in sort' comp small (x::(sort' comp big ans)) end;
fun nub [] = []
| nub [x] = [x]
| nub (x::y::xs) = if x=y then nub (x::xs) else x::(nub (y::xs));
fun norm x = nub (sort' String.compare x [])
(* ********************************************************* *)
fun firstTable (gram as(Gram(nts,ts,ps,start))) =
let val termTable = init ts (fn x => [x])
val nontermTable = init nts (fn x => [])
val table = termTable @ nontermTable
val changed = ref true
fun onePass [] = false (* returns true if it makes a change *)
| onePass (x::xs) =
let val rhss = rhssFor gram x
fun first old =
let val listOflists = map (firstRhs gram table) rhss
val new = List.concat listOflists
in norm(old @ new) end
val b1 = update x first table
val b2 = onePass xs
in b1 orelse b2 end
in while (!changed) do
(changed := false; changed := onePass nts);
table
end
and first table s =
case List.find (fn (y,r) => y=s) table of
NONE => []
| SOME(_,r) => !r
and firstRhs gram table [] = [empty]
| firstRhs gram table [x] = first table x
| firstRhs gram table (x::xs) =
let val temp = first table x
in case List.find emptyP temp of
NONE => temp
| SOME _ => temp @ firstRhs gram table xs
end;
val FirstT = firstTable g1;
(* *)