188 lines · 4.7 KB · Standard ML
RawDownload
(* Parsercomb -- Hutton/Paulson-style parser combinators for Moscow ML.   Fritz Henglein, Ken Larsen, Peter Sestoft.   Documentation by sestoft@dina.kvl.dk.  Version 0.4 of 2000-04-30 *) structure Parsercomb :> Parsercomb =struct     datatype 'elm stream =	S of int * (int -> ('elm * 'elm stream) option)     type ('elm,'res) parser = 'elm stream -> ('res * 'elm stream) option     fun stream get src =	let fun next src n = 	        case get src of		    SOME(x, rest) => SOME(x, S(n+1, next rest))		  | NONE          => NONE	in S(0, next src) end     fun getItem (S(n, next)) = next n     exception Parse of string     infix 6 $-- --$ #-- --#    infix 5 --    infix 3 >> >>*    infix 2 >>=    infix 0 ||     fun commitChar expected par (strm as S(n, next)) = 	case par strm of 	    NONE => 		raise Parse (String.concat 			     ["Expected <", expected, "> but found <", 			      (case next n of				   NONE       => "eof>"				 | SOME(c, _) => str c ^ ">"),			      " at character number ", Int.toString n]) 	  | res as SOME _ => res     fun commitElem expected show par (strm as S(n, next)) = 	case par strm of 	    NONE => 		raise Parse (String.concat 			     ["Expected <", expected, "> but found <", 			      (case next n of				   NONE         => "eof>"				 | SOME(elm, _) => show elm ^ ">"),			      " at element number ", Int.toString n]) 	  | res as SOME _ => res		        fun scan (scanner : ('a, 'a stream) StringCvt.reader -> 'a stream -> 'b) =	scanner getItem      fun (par1 >>= parf2) strm =	case par1 strm of	    SOME(b, strm1) => parf2 b strm1	  | NONE           => NONE     fun success x strm = SOME(x, strm)     fun failure strm = NONE     fun eof r strm = 	case getItem strm of	    NONE       => SOME(r, strm)	  | SOME(c, _) => NONE     (* fun (par >> f) = par >>= (success o f) *)     fun (par >> f) strm = 	case par strm of	    SOME(x, strm1) => SOME(f x, strm1)	  | _              => NONE     fun (par >>* f) strm = 	case par strm of	    SOME(x, strm1) => 		(case f x of 		     SOME y => SOME(y, strm1)		   | NONE   => NONE)	  | _              => NONE     (* fun (par1 -- par2) = par1 >>= (fn r1 => par2 >> (fn r2 => (r1, r2))) *)     fun (par1 -- par2) strm =	case par1 strm of	    SOME(r1, strm1) => (case par2 strm1 of				    SOME(r2 , strm2) => SOME((r1,r2), strm2)				  | NONE => NONE)	  | NONE => NONE		    (* fun (par1 #-- par2) = (par1 -- par2) >> #2 *)     (* Define explicitly to make par2 a tail call; possible because no       backtracking over par1: *)     fun (par1 #-- par2) strm = 	case par1 strm of	    SOME (_, strm1) => par2 strm1	  | NONE            => NONE     fun (par1 --# par2) = (par1 -- par2) >> #1 	    fun (par1 || par2) strm =	case par1 strm of	    NONE => par2 strm	  | res  => res     fun skipWS par strm = par (StringCvt.skipWS getItem strm)     fun optional par strm0 =	case par strm0 of	    SOME(x, strm1) => SOME(SOME x, strm1)	  | NONE           => SOME(NONE,   strm0)		    fun repeat0 par strm =	let fun loop strm0 res =	        case par strm0 of		    SOME(x, strm1) => loop strm1 (x::res)		  | NONE           => SOME(List.rev res, strm0)	in loop strm [] end     fun repeat1 par = par -- repeat0 par     fun $ s strm0 =	let val len = size s	    val sub = String.sub	    infix sub	    fun loop n strm =		if n = len then SOME(s, strm)		else (case getItem strm of			  SOME(c, rest) => 			      if c = (s sub n) then loop (n+1) rest			      else NONE			| NONE => NONE)	in loop 0 strm0 end        fun (s $-- par) = $ s -- par >> #2     fun (par --$ s) = par -- $ s >> #1     fun getChar pred strm =	case getItem strm of	    res as SOME(c, src) => if pred c then res 				   else NONE	  | NONE => NONE     fun $# elm strm = 	case getItem strm of	    res as SOME(x, src) => if x = elm then res 				   else NONE	  | NONE => NONE     val getLit = $#     fun getChars0 pred strm = 	SOME(StringCvt.splitl pred getItem strm)     fun getChars1 pred strm =	case StringCvt.splitl pred getItem strm of	    ("", _) => NONE	  | res     => SOME res     fun getChars1 pred = 	repeat1 (getChar pred) >> op:: >> String.implode	    val getElem = getChar     fun getElems0 pred = repeat0 (getElem pred)      fun getElems1 pred = repeat1 (getElem pred)       fun compose(par1, par2) strm = 	let val par1stream = stream par1 strm	in par2 par1stream end     fun parse (par : ('a, 'b) parser) (strm : 'a stream) : 'b option = 	case par strm of	    NONE          => NONE	  | SOME (res, _) => SOME res     fun scanSubstr par sus = parse par (stream Substring.getc sus)     fun scanString par s = scanSubstr par (Substring.all s)     fun scanList par cs = parse par (stream List.getItem cs)end