home *** CD-ROM | disk | FTP | other *** search
Text File | 1996-07-03 | 15.1 KB | 495 lines | [TEXT/R*ch] |
- (* match.ml : expansion of pattern-matching as a cascade of tests. *)
-
- open List Fnlib Mixture Const Smlexc Prim Globals Types Asynt Asyntfn;
- open Instruct Lambda Location Tr_env;
-
- (* See Peyton-Jones, The Implementation of functional programming
- languages, chapter 5. *)
-
- (* A pattern-matching is represented as a disjunction of conjunctions:
-
- pat & pat & ... & pat -> action
- | pat & pat & ... & pat -> action
- | ...
- | pat & pat & ... & pat -> action
-
- exp exp ... exp
-
- A pattern "pat" applies to (i.e. must match) the expression below it. *)
-
- datatype ThreeValuedLogic = False | Maybe | True;
-
- datatype MatchRules =
- Rules of (Pat list * Lambda) list * Lambda list
- ;
-
- (* Simple pattern manipulations *)
-
- fun splitPath n (path::paths) =
- let fun loop i paths =
- if i < 0 then paths else
- loop (i-1) (Lprim(Pfield i, [path]) :: paths)
- in loop (n-1) paths end
- ;
-
- fun addToRules (Rules(rules, paths)) rule =
- Rules(rule :: rules, paths)
- ;
-
- fun makeRules_Const (path :: paths) rule = Rules([rule], paths)
- | makeRules_Const _ _ = fatalError "makeRules_Const"
- ;
-
- fun makeRules_Vec paths rule =
- Rules([rule], paths)
- ;
-
- fun makeRules_Tuple arity paths =
- Rules([], splitPath arity paths)
- ;
-
- fun makeRules_Constr (ci : ConInfo) (paths as path :: paths') rule =
- if #conArity(!ci) = 0 then
- Rules([rule], paths')
- else if #conSpan(!ci) = 1 then
- Rules([rule], paths)
- else if #conIsGreedy(!ci) then
- Rules([rule], paths)
- else
- Rules([rule], Lprim(Pfield 0, [path]) :: paths')
- ;
-
- fun makeRules_ExConstr (ei : ExConInfo) (paths as path :: paths') rule =
- if #exconArity(!ei) = 0 then
- Rules([rule], paths')
- else if #exconIsGreedy(!ei) then
- Rules([rule], paths)
- else
- Rules([rule], Lprim(Pfield 0, [path]) :: paths')
- ;
-
- fun makeRules_ExName (path :: paths) rule = Rules([rule], paths)
- | makeRules_ExName _ _ = fatalError "makeRules_ExName"
- ;
-
- (* Auxiliaries for factoring common tests *)
-
- fun addToDivision make_rules divlist key rule =
- let val rules_ref = lookup key divlist in
- rules_ref := addToRules (!rules_ref) rule;
- divlist
- end
- handle Subscript =>
- (key, ref (make_rules rule)) :: divlist
- ;
-
- (* Two different identifiers can be bound to the same exception name. *)
- (* Therefore, they can't be swapped in excases. *)
-
- fun fakeAssoc (key: IdInfo) (divlist: (IdInfo * MatchRules ref) list) =
- case divlist of
- [] => raise Subscript
- | (ii, rules_ref) :: _ =>
- if #qualid(!(#idKind(#info key))) = #qualid(!(#idKind(#info ii)))
- then rules_ref
- else raise Subscript
- ;
-
- fun addToExNameDivision make_rules divlist key rule =
- let val rules_ref = fakeAssoc key divlist in
- rules_ref := addToRules (!rules_ref) rule;
- divlist
- end
- handle Subscript =>
- (key, ref (make_rules rule)) :: divlist
- ;
-
- (* To skip type constraints and aliases. *)
-
- val smlExnEi =
- { qualid = { qual = "General", id = "Exception" },
- info = ref{ exconArity = 2,
- exconIsGreedy = true,
- exconTag = SOME exnTagName }
- };
-
- fun mkPairPat p1 p2 =
- let val loc = xxLR p1 p2 in
- (loc, RECpat(ref (TUPLErp [p1, p2])))
- end
- ;
-
- fun mkExnPat (ii : IdInfo) arg =
- let val {qualid, info} = ii
- val {idLoc, withOp, ...} = info
- val ii' = mkIdInfo (idLoc, qualid) withOp
- in
- #idKind(#info ii') :=
- { qualid= #qualid smlExnEi, info=EXCONik (#info smlExnEi) };
- EXCONSpat(ii', arg)
- end
- ;
-
- fun simplifyRules rules =
- case rules of
- ((loc, VARpat ii) :: pats, action) :: rest =>
- ((loc, WILDCARDpat)::pats, action) :: rest
- | ((loc, EXNILpat ii) :: pats, action) :: rest =>
- if isExConStatic(getExConInfo ii) then
- rules
- else
- let val arg = mkPairPat (loc, EXNAMEpat ii) (loc, WILDCARDpat) in
- ((loc, mkExnPat ii arg) :: pats, action) :: rest
- end
- | ((loc, EXCONSpat(ii, p)) :: pats, action) :: rest =>
- if isExConStatic(getExConInfo ii) then
- rules
- else
- let val arg = mkPairPat (loc, EXNAMEpat ii) p in
- ((loc, mkExnPat ii arg) :: pats, action) :: rest
- end
- | ((loc, REFpat pat) :: pats, action) :: rest =>
- ((loc, RECpat(ref (TUPLErp [pat]))) :: pats, action) :: rest
- | ((_, PARpat pat) :: pats, action) :: rest =>
- simplifyRules ((pat::pats, action) :: rest)
- | ((_, LAYEREDpat(_, pat)) :: pats, action) :: rest =>
- simplifyRules ((pat::pats, action) :: rest)
- | ((_, TYPEDpat(pat,_)) :: pats, action) :: rest =>
- simplifyRules ((pat::pats, action) :: rest)
- | _ =>
- rules
- ;
-
- (* Factoring pattern-matchings. *)
-
- fun divideRules_Const (Rules(rules, paths)) =
- let fun loop rules =
- case simplifyRules rules of
- ((_, SCONpat key) :: pats, action) :: rest =>
- let val (constant, others) = loop rest in
- (addToDivision
- (makeRules_Const paths) constant key (pats, action),
- others)
- end
- | rules =>
- ([], Rules(rules, paths))
- in loop rules end
- ;
-
- fun divideRules_Vec (Rules(rules, paths)) =
- let fun loop rules =
- case simplifyRules rules of
- ((loc, VECpat args) :: pats, action) :: rest =>
- let val (vecs, others) = loop rest in
- (addToDivision
- (makeRules_Vec paths) vecs (INTscon (List.length args))
- ((loc, RECpat(ref (TUPLErp args))) :: pats, action),
- others)
- end
- | rules =>
- ([], Rules(rules, paths))
- in loop rules end
- ;
-
- fun splitVarPat arity =
- let fun loop i =
- if i >= arity then []
- else (nilLocation, WILDCARDpat) :: loop (i+1)
- in loop 0 end
- ;
-
- fun divideRules_Tuple arity (Rules(rules, paths)) =
- let fun loop rules =
- case simplifyRules rules of
- ((_, RECpat(ref (TUPLErp args))) :: pats, action) :: rest =>
- addToRules (loop rest) (args @ pats, action)
- | ((_, WILDCARDpat) :: pats, action) :: rest =>
- addToRules (loop rest) (splitVarPat arity @ pats, action)
- | [] =>
- makeRules_Tuple arity paths
- | _ =>
- fatalError "divideRules_Tuple"
- in loop rules end
- ;
-
- fun divideRules_Constr (Rules(rules, paths)) =
- let fun loop rules =
- case simplifyRules rules of
- ((_, NILpat ii) :: pats, action) :: rest =>
- let val (constrs, others) = loop rest
- val ci = getConInfo ii
- in
- (addToDivision
- (makeRules_Constr ci paths) constrs
- (CONtag(#conTag(!ci), #conSpan(!ci))) (pats, action),
- others)
- end
- | ((_, CONSpat(ii, arg)) :: pats, action) :: rest =>
- let val (constrs, others) = loop rest
- val ci = getConInfo ii
- in
- (addToDivision
- (makeRules_Constr ci paths) constrs
- (CONtag(#conTag(!ci), #conSpan(!ci)))
- (arg :: pats, action),
- others)
- end
- | rules =>
- ([], Rules(rules, paths))
- in loop rules end
- ;
-
- fun getExConTag (ei : ExConInfo) =
- case #exconTag(!ei) of
- NONE => fatalError "getExConTag"
- | SOME tag => tag
- ;
-
- fun divideRules_ExConstr (Rules(rules, paths)) =
- let fun loop rules =
- case simplifyRules rules of
- ((_, EXNILpat ii) :: pats, action) :: rest =>
- let val (exs, others) = loop rest
- val ei = getExConInfo ii
- in
- (addToDivision
- (makeRules_ExConstr ei paths) exs
- (EXNtag (getExConTag ei)) (pats, action),
- others)
- end
- | ((_, EXCONSpat(ii, arg)) :: pats, action) :: rest =>
- let val (exs, others) = loop rest
- val ei = getExConInfo ii
- in
- (addToDivision
- (makeRules_ExConstr ei paths) exs
- (EXNtag (getExConTag ei)) (arg :: pats, action),
- others)
- end
- | rules =>
- ([], Rules(rules, paths))
- in loop rules end
- ;
-
- fun divideRules_ExName (Rules(rules, paths)) =
- let fun loop rules =
- case simplifyRules rules of
- ((_, EXNAMEpat ii) :: pats, action) :: rest =>
- let val (exs, others) = loop rest
- val ei = getExConInfo ii
- in
- (addToExNameDivision
- (makeRules_ExName paths) exs ii (pats, action),
- others)
- end
- | rules =>
- ([], Rules(rules, paths))
- in loop rules end
- ;
-
- fun divideRules_Var (Rules(rules, (paths as _ :: paths'))) =
- let fun loop rules =
- case simplifyRules rules of
- ((_, WILDCARDpat) :: pats, action) :: rest =>
- let val (vars, others) = loop rest in
- (addToRules vars (pats, action), others)
- end
- | rules =>
- (Rules([], paths'), Rules(rules, paths))
- in loop rules end
- ;
-
-
- (* Utilities on pattern-matchings *)
-
- fun numberOfRules (Rules(rules,_)) = List.length rules;
-
- fun simplifyPat (pat as (loc, pat')) =
- case pat' of
- VARpat _ => (loc, WILDCARDpat)
- | REFpat p => (loc, RECpat(ref (TUPLErp [p])))
- | PARpat p => simplifyPat p
- | TYPEDpat(p,_) => simplifyPat p
- | LAYEREDpat(_, p) => simplifyPat p
- | _ => pat
- ;
-
- fun upperLeftPattern (Rules((pat::_, _) :: _, _)) = simplifyPat pat
- | upperLeftPattern _ = fatalError "upperLeftPattern"
- ;
-
- fun getConInfoOfRules rules =
- case upperLeftPattern rules of
- (_, NILpat ii) => getConInfo ii
- | (_, CONSpat(ii,_)) => getConInfo ii
- | _ => fatalError "getConInfoOfRules"
- ;
-
- (* The three-valued booleans. *)
-
- fun threevalued_or (True, _ ) = True
- | threevalued_or (_, True ) = True
- | threevalued_or (False, False) = False
- | threevalued_or (_, _ ) = Maybe
- ;
-
- fun mkExCase arg clauses alt =
- foldR (fn (key, body) => fn alt =>
- Lstatichandle(
- Lif(Lprim(Ptest Peq_test, [arg, key]), body, Lstaticfail),
- alt))
- alt clauses
- ;
-
- (* The main compilation function.
- Input: matching rules,
- Output: a lambda term, a "partial" flag, a list of used cases. *)
-
- fun conquerRules (env : TranslEnv) =
- let
- fun conquerDividedRules [] =
- ([], False, [])
- | conquerDividedRules ((key, rules_ref) :: rest) =
- let val (lambda1, partial1, used1) = conquerRules env (!rules_ref)
- and (rest', partial2, used2) = conquerDividedRules rest
- in
- ((key, lambda1) :: rest',
- threevalued_or(partial1,partial2),
- used1 @ used2)
- end
- fun conquerConstrRules (rules as Rules(_, (path :: _))) =
- let val (constrs, vars) = divideRules_Constr rules
- val (switchlst, partial1, used1) = conquerDividedRules constrs
- and (lambda, partial2, used2) = conquerRules env vars
- val ci = getConInfoOfRules rules
- and num_of_clauses = List.length constrs
- val span = #conSpan(!ci) and arity = #conArity(!ci)
- in
- if arity = 1 andalso span = 1 then
- (case switchlst of
- [(_, lam1)] =>
- if partial1 = False then
- (lam1, False, used1)
- else
- (Lstatichandle(lam1, lambda),
- (if partial2 = False then False else Maybe),
- used1 @ used2)
- | _ => fatalError "conquerConstrRules")
- else if num_of_clauses = span andalso partial1 = False then
- (Lswitch(span, path, switchlst), False, used1)
- else
- (Lstatichandle(Lswitch(span, path, switchlst), lambda),
- (if partial2 = False then False
- else if num_of_clauses < span andalso partial2 = True then True
- else Maybe),
- used1 @ used2)
- end
- fun conquerExConstrRules (rules as Rules(_, (path :: _))) =
- let val (constrs, vars) = divideRules_ExConstr rules
- val (switchlst, partial1, used1) = conquerDividedRules constrs
- and (lambda, partial2, used2) = conquerRules env vars
- in
- (Lstatichandle(Lswitch(0, path, switchlst), lambda),
- (if partial2 = False then False
- else if partial2 = True then True
- else Maybe),
- used1 @ used2)
- end
- and conquerExNameRules (rules as Rules(_, (path :: _))) =
- let val (exs, vars) = divideRules_ExName rules
- val (divlist, partial1, used1) = conquerDividedRules exs
- and (lambda, partial2, used2) = conquerRules env vars
- val clauses = map (fn(ii, lam) => (translateExName env ii, lam))
- divlist
- in
- (mkExCase path clauses lambda,
- partial2,
- used1 @ used2)
- end
- in fn
- Rules([], _) =>
- (Lstaticfail, True, [])
- | Rules(([], action) :: rest, _) =>
- (action, False, [action])
- | rules as Rules(_, (path :: _)) =>
- (case upperLeftPattern rules of
- (_, SCONpat _) =>
- let val (constants, vars) = divideRules_Const rules
- val (caselist1, _, used1) = conquerDividedRules constants
- and (lambda2, partial2, used2) = conquerRules env vars
- in
- (Lstatichandle(Lcase(path, caselist1), lambda2),
- partial2,
- used1 @ used2)
- end
- | (_, VECpat _) =>
- let val (vecs, vars) = divideRules_Vec rules
- val (caselist1, _, used1) = conquerDividedRules vecs
- and (lambda2, partial2, used2) = conquerRules env vars
- in
- (Lstatichandle
- (Lcase(Lprim(Pvectlength, [path]), caselist1), lambda2),
- partial2,
- used1 @ used2)
- end
- | (_, WILDCARDpat) =>
- let val (vars, rest) = divideRules_Var rules
- val (lambda1, partial1, used1) = conquerRules env vars
- and (lambda2, partial2, used2) = conquerRules env rest
- in
- if partial1 = False then
- (lambda1, False, used1)
- else
- (Lstatichandle(lambda1, lambda2),
- (if partial2 = False then False else Maybe),
- used1 @ used2)
- end
- | (_, NILpat _) =>
- conquerConstrRules rules
- | (_, CONSpat _) =>
- conquerConstrRules rules
- | (_, EXNILpat _) =>
- conquerExConstrRules rules
- | (_, EXCONSpat _) =>
- conquerExConstrRules rules
- | (_, EXNAMEpat _) =>
- conquerExNameRules rules
- | (_, RECpat(ref (TUPLErp pats))) =>
- conquerRules env (divideRules_Tuple (List.length pats) rules)
- | (_, RECpat(ref (RECrp _))) =>
- fatalError "conquerRules"
- | _ =>
- fatalError "conquerRules 2")
- | _ => fatalError "conquerRules 1"
- end
- ;
-
- fun makeInitialRules [] =
- fatalError "makeInitialRules: empty"
- | makeInitialRules (rules as (pats, _) :: _) =
- let fun makePath n =
- if n <= 0 then [] else Lvar(n-1) :: makePath(n-1)
- in Rules(rules, makePath(List.length pats)) end
- ;
-
- (* The entry point *)
-
- fun translateMatch (env : TranslEnv) failure_code loc mrules =
- let val mrules' =
- map (fn (pats,l) => (pats, shared_lambda l)) mrules
- val (lambda, partial, used) =
- conquerRules env (makeInitialRules mrules')
- in
- if not(all (fn (_, act) => member act used) mrules') then
- (msgIBlock 0;
- errLocation loc;
- errPrompt "Warning: some cases are unused in this match.";
- msgEOL(); msgEOL();
- msgEBlock())
- else ();
- case partial of
- False => lambda
- | _ => Lstatichandle(lambda, failure_code partial)
- end
- ;
-