(* How to add a data type like the Variable->Value functions we need in semantics. Also demonstrates how to create a version of SML with a new set of fucntions etc in its library by using 'exportML;' at the end. Source: After Ullman page 148 *) structure Mapping = struct (* a new module *) exception NotFound; (* an error exit from some functions*) val create = nil; (* the empty map *) (* value(lhs, sto) = sto(lhs) = the rhs associated with lhs in sto*) fun value(v, nil) = raise NotFound | value(lhs, (v,e)::sto) = if lhs = v then e else (* Dick's mistake value(v, sto)*) value(lhs, sto)(*Larry Gately's correction*); (* insert(lhs,rhs,sto) returns sto[lhs+>rhs]*) fun insert(lhs,rhs,nil) = [(lhs,rhs)] | insert(lhs,rhs, (v,e)::sto) = if lhs = v then (lhs,rhs)::sto else (v,e)::insert(lhs,rhs,sto); (* ------------------------------------------------------------------*) (* the above were easy! A simple output function (below) ran into an unexpected problems... SML print is overloaded but SML won't run a function with print(v) unless it is sure that 'v' is a printable value. Second: SML needs to assign types to all arguments, even if they are not used, so adding to print functions still was not enough... *) (* dump(pv,pe,sto) prints out sto in mathematical format [v+>1,...] pv:Var->unit and pe:values->unit are print functions*) (* dump_inside help dump...lists pairs, no brackets*) fun dump_inside(pv:'a->unit,pe:'b->unit, v:'a,e:'b, nil:('a*'b)list ) = ( pv (v); print ("+>"); pe (e) ) | dump_inside(pv, pe, v1,e1, (v2,e2)::es) = ( pv (v1); print ("+>"); pe (e1); print (", "); dump_inside(pv,pe,v2,e2, es) ); fun dump(pv,pe,sto)= (* SML figures out argument types from dump_inside*) ( print ("["); if sto = nil then print ("Empty") else let val (v,e)::es = sto in dump_inside(pv, pe, v,e, es) end; print ("]\n\n") ) (* can not use dump(pv,pe,nil)=print("[]")!*) (* Exercise. Write a parser for strings like "[ x+>7, y+>5, z+>0]" *) end; exportML("smlMapping"); (* This saved a version of the compiler called "smlMapping" *) (* You run it like 'sml' but to use the Mapping operations above you can execute "open Mapping" inside the 'sml' the 'sml' run. Ullman pages 237 to 238, and 151-152 *) (* You can do a more solid job by using signatures and functors to define that the values and variables are both data types with a valid 'print' function. The user then has to define their own version of storage before using it... Rather than having to supply suitable output functions as parameters and enough hints to make dump(nil) work... dump(print, print, create:(string*int)list); or dump(print,print,create:(int*int)list); *) (* Sample customization... without using 'open'. type Store=(string*int)list; val empty = Mapping.create:Store; fun eval(v:string,L:Store)=Mapping.value(v,L); fin assign(v,e,L:Store)=Mapping.insert(v,e,L); fun d(L:Store)=Mapping.dump(print,print,L); *)