diff options
-rw-r--r-- | src/disjoint.sig | 38 | ||||
-rw-r--r-- | src/disjoint.sml | 294 | ||||
-rw-r--r-- | src/elab.sml | 3 | ||||
-rw-r--r-- | src/elab_print.sml | 3 | ||||
-rw-r--r-- | src/elab_util.sml | 4 | ||||
-rw-r--r-- | src/elaborate.sml | 12 | ||||
-rw-r--r-- | src/explify.sml | 4 | ||||
-rw-r--r-- | src/lacweb.grm | 4 | ||||
-rw-r--r-- | src/lacweb.lex | 2 | ||||
-rw-r--r-- | src/source.sml | 3 | ||||
-rw-r--r-- | src/source_print.sml | 4 | ||||
-rw-r--r-- | src/sources | 3 | ||||
-rw-r--r-- | tests/disjoint.lac | 1 |
13 files changed, 374 insertions, 1 deletions
diff --git a/src/disjoint.sig b/src/disjoint.sig new file mode 100644 index 00000000..16afa885 --- /dev/null +++ b/src/disjoint.sig @@ -0,0 +1,38 @@ +(* Copyright (c) 2008, Adam Chlipala + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - The names of contributors may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *) + +signature DISJOINT = sig + + type env + + val empty : env + val assert : ElabEnv.env -> env -> Elab.con * Elab.con -> env + val enter : env -> env + + val prove : ElabEnv.env -> env -> Elab.con * Elab.con * ErrorMsg.span -> (Elab.con * Elab.con) list + +end diff --git a/src/disjoint.sml b/src/disjoint.sml new file mode 100644 index 00000000..784bd635 --- /dev/null +++ b/src/disjoint.sml @@ -0,0 +1,294 @@ +(* Copyright (c) 2008, Adam Chlipala + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - The names of contributors may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *) + +structure Disjoint :> DISJOINT = struct + +open Elab +open ElabOps + +structure SS = BinarySetFn(struct + type ord_key = string + val compare = String.compare + end) + +structure IS = IntBinarySet +structure IM = IntBinaryMap + +type name_ineqs = { + namesC : SS.set, + namesR : IS.set, + namesN : IS.set +} + +val name_default = { + namesC = SS.empty, + namesR = IS.empty, + namesN = IS.empty +} + +type row_ineqs = { + namesC : SS.set, + namesR : IS.set, + namesN : IS.set, + rowsR : IS.set, + rowsN : IS.set +} + +val row_default = { + namesC = SS.empty, + namesR = IS.empty, + namesN = IS.empty, + rowsR = IS.empty, + rowsN = IS.empty +} + +fun nameToRow_ineqs {namesC, namesR, namesN} = + {namesC = namesC, + namesR = namesR, + namesN = namesN, + rowsR = IS.empty, + rowsN = IS.empty} + +type env = { + namesR : name_ineqs IM.map, + namesN : name_ineqs IM.map, + rowsR : row_ineqs IM.map, + rowsN : row_ineqs IM.map +} + +val empty = { + namesR = IM.empty, + namesN = IM.empty, + rowsR = IM.empty, + rowsN = IM.empty +} + +datatype piece = + NameC of string + | NameR of int + | NameN of int + | RowR of int + | RowN of int + | Unknown + +fun nameToRow (c, loc) = + (CRecord ((KUnit, loc), [((c, loc), (CUnit, loc))]), loc) + +fun pieceToRow (p, loc) = + case p of + NameC s => nameToRow (CName s, loc) + | NameR n => nameToRow (CRel n, loc) + | NameN n => nameToRow (CNamed n, loc) + | RowR n => (CRel n, loc) + | RowN n => (CRel n, loc) + | Unknown => raise Fail "Unknown to row" + +fun decomposeRow env c = + let + fun decomposeName (c, acc) = + case #1 (hnormCon env c) of + CName s => NameC s :: acc + | CRel n => NameR n :: acc + | CNamed n => NameN n :: acc + | _ => Unknown :: acc + + fun decomposeRow (c, acc) = + case #1 (hnormCon env c) of + CRecord (_, xcs) => foldl (fn ((x, _), acc) => decomposeName (x, acc)) acc xcs + | CConcat (c1, c2) => decomposeRow (c1, decomposeRow (c2, acc)) + | CRel n => RowR n :: acc + | CNamed n => RowN n :: acc + | _ => Unknown :: acc + in + decomposeRow (c, []) + end + +fun assertPiece_name (ps, ineqs : name_ineqs) = + {namesC = foldl (fn (p', namesC) => + case p' of + NameC s => SS.add (namesC, s) + | _ => namesC) (#namesC ineqs) ps, + namesR = foldl (fn (p', namesR) => + case p' of + NameR n => IS.add (namesR, n) + | _ => namesR) (#namesR ineqs) ps, + namesN = foldl (fn (p', namesN) => + case p' of + NameN n => IS.add (namesN, n) + | _ => namesN) (#namesN ineqs) ps} + +fun assertPiece_row (ps, ineqs : row_ineqs) = + {namesC = foldl (fn (p', namesC) => + case p' of + NameC s => SS.add (namesC, s) + | _ => namesC) (#namesC ineqs) ps, + namesR = foldl (fn (p', namesR) => + case p' of + NameR n => IS.add (namesR, n) + | _ => namesR) (#namesR ineqs) ps, + namesN = foldl (fn (p', namesN) => + case p' of + NameN n => IS.add (namesN, n) + | _ => namesN) (#namesN ineqs) ps, + rowsR = foldl (fn (p', rowsR) => + case p' of + RowR n => IS.add (rowsR, n) + | _ => rowsR) (#rowsR ineqs) ps, + rowsN = foldl (fn (p', rowsN) => + case p' of + RowN n => IS.add (rowsN, n) + | _ => rowsN) (#rowsN ineqs) ps} + +fun assertPiece ps (p, denv) = + case p of + Unknown => denv + | NameC _ => denv + + | NameR n => + let + val ineqs = Option.getOpt (IM.find (#namesR denv, n), name_default) + val ineqs = assertPiece_name (ps, ineqs) + in + {namesR = IM.insert (#namesR denv, n, ineqs), + namesN = #namesN denv, + rowsR = #rowsR denv, + rowsN = #rowsN denv} + end + + | NameN n => + let + val ineqs = Option.getOpt (IM.find (#namesN denv, n), name_default) + val ineqs = assertPiece_name (ps, ineqs) + in + {namesR = #namesR denv, + namesN = IM.insert (#namesN denv, n, ineqs), + rowsR = #rowsR denv, + rowsN = #rowsN denv} + end + + | RowR n => + let + val ineqs = Option.getOpt (IM.find (#rowsR denv, n), row_default) + val ineqs = assertPiece_row (ps, ineqs) + in + {namesR = #namesR denv, + namesN = #namesN denv, + rowsR = IM.insert (#rowsR denv, n, ineqs), + rowsN = #rowsN denv} + end + + | RowN n => + let + val ineqs = Option.getOpt (IM.find (#rowsN denv, n), row_default) + val ineqs = assertPiece_row (ps, ineqs) + in + {namesR = #namesR denv, + namesN = #namesN denv, + rowsR = #rowsR denv, + rowsN = IM.insert (#rowsN denv, n, ineqs)} + end + +fun assert env denv (c1, c2) = + let + val ps1 = decomposeRow env c1 + val ps2 = decomposeRow env c2 + + val denv = foldl (assertPiece ps2) denv ps1 + in + foldl (assertPiece ps1) denv ps2 + end + +fun nameEnter {namesC, namesR, namesN} = + {namesC = namesC, + namesR = IS.map (fn n => n + 1) namesR, + namesN = namesN} + +fun rowEnter {namesC, namesR, namesN, rowsR, rowsN} = + {namesC = namesC, + namesR = IS.map (fn n => n + 1) namesR, + namesN = namesN, + rowsR = IS.map (fn n => n + 1) rowsR, + rowsN = rowsN} + +fun enter {namesR, namesN, rowsR, rowsN} = + {namesR = IM.foldli (fn (n, ineqs, namesR) => IM.insert (namesR, n+1, nameEnter ineqs)) IM.empty namesR, + namesN = IM.map nameEnter namesN, + rowsR = IM.foldli (fn (n, ineqs, rowsR) => IM.insert (rowsR, n+1, rowEnter ineqs)) IM.empty rowsR, + rowsN = IM.map rowEnter rowsN} + +fun getIneqs (denv : env) p = + case p of + Unknown => raise Fail "getIneqs: Unknown" + | NameC _ => raise Fail "getIneqs: NameC" + | NameR n => nameToRow_ineqs (Option.getOpt (IM.find (#namesR denv, n), name_default)) + | NameN n => nameToRow_ineqs (Option.getOpt (IM.find (#namesN denv, n), name_default)) + | RowR n => Option.getOpt (IM.find (#rowsR denv, n), row_default) + | RowN n => Option.getOpt (IM.find (#rowsN denv, n), row_default) + +fun prove1' denv (p1, p2) = + let + val {namesC, namesR, namesN, rowsR, rowsN} = getIneqs denv p1 + in + case p2 of + Unknown => raise Fail "prove1': Unknown" + | NameC s => SS.member (namesC, s) + | NameR n => IS.member (namesR, n) + | NameN n => IS.member (namesN, n) + | RowR n => IS.member (rowsR, n) + | RowN n => IS.member (rowsN, n) + end + +fun prove1 denv (p1, p2) = + case (p1, p2) of + (NameC s1, NameC s2) => s1 <> s2 + | (_, RowR _) => prove1' denv (p2, p1) + | (_, RowN _) => prove1' denv (p2, p1) + | _ => prove1' denv (p1, p2) + +fun prove env denv (c1, c2, loc) = + let + val ps1 = decomposeRow env c1 + val ps2 = decomposeRow env c2 + + val hasUnknown = List.exists (fn p => p = Unknown) + in + if hasUnknown ps1 orelse hasUnknown ps2 then + (ErrorMsg.errorAt loc "Structure of row is too complicated to prove disjointness"; + Print.eprefaces' [("Row 1", ElabPrint.p_con env c1), + ("Row 2", ElabPrint.p_con env c2)]; + []) + else + foldl (fn (p1, rem) => + foldl (fn (p2, rem) => + if prove1 denv (p1, p2) then + rem + else + (pieceToRow (p1, loc), pieceToRow (p2, loc)) :: rem) rem ps2) + [] ps1 + end + +end diff --git a/src/elab.sml b/src/elab.sml index 1b0e3ea1..a7085fee 100644 --- a/src/elab.sml +++ b/src/elab.sml @@ -34,6 +34,7 @@ datatype kind' = | KArrow of kind * kind | KName | KRecord of kind + | KUnit | KError | KUnif of ErrorMsg.span * string * kind option ref @@ -61,6 +62,8 @@ datatype con' = | CConcat of con * con | CFold of kind * kind + | CUnit + | CError | CUnif of ErrorMsg.span * kind * string * con option ref diff --git a/src/elab_print.sml b/src/elab_print.sml index 8c51be11..01472ca7 100644 --- a/src/elab_print.sml +++ b/src/elab_print.sml @@ -48,6 +48,7 @@ fun p_kind' par (k, _) = p_kind k2]) | KName => string "Name" | KRecord k => box [string "{", p_kind k, string "}"] + | KUnit => string "Unit" | KError => string "<ERROR>" | KUnif (_, _, ref (SOME k)) => p_kind' par k @@ -155,6 +156,8 @@ fun p_con' par env (c, _) = p_con env c2]) | CFold _ => string "fold" + | CUnit => string "()" + | CError => string "<ERROR>" | CUnif (_, _, _, ref (SOME c)) => p_con' par env c | CUnif (_, k, s, _) => box [string ("<UNIF:" ^ s ^ "::"), diff --git a/src/elab_util.sml b/src/elab_util.sml index 45446a4e..e9b7ddd9 100644 --- a/src/elab_util.sml +++ b/src/elab_util.sml @@ -56,6 +56,8 @@ fun mapfold f = fn k' => (KRecord k', loc)) + | KUnit => S.return2 kAll + | KError => S.return2 kAll | KUnif (_, _, ref (SOME k)) => mfk' k @@ -150,6 +152,8 @@ fun mapfoldB {kind = fk, con = fc, bind} = fn k2' => (CFold (k1', k2'), loc))) + | CUnit => S.return2 cAll + | CError => S.return2 cAll | CUnif (_, _, _, ref (SOME c)) => mfc' ctx c | CUnif _ => S.return2 cAll diff --git a/src/elaborate.sml b/src/elaborate.sml index 4323677d..0e9696b9 100644 --- a/src/elaborate.sml +++ b/src/elaborate.sml @@ -32,6 +32,7 @@ structure L = Source structure L' = Elab structure E = ElabEnv structure U = ElabUtil +structure D = Disjoint open Print open ElabPrint @@ -73,6 +74,8 @@ fun unifyKinds' (k1All as (k1, _)) (k2All as (k2, _)) = in case (k1, k2) of (L'.KType, L'.KType) => () + | (L'.KUnit, L'.KUnit) => () + | (L'.KArrow (d1, r1), L'.KArrow (d2, r2)) => (unifyKinds' d1 d2; unifyKinds' r1 r2) @@ -199,6 +202,7 @@ fun elabKind (k, loc) = | L.KArrow (k1, k2) => (L'.KArrow (elabKind k1, elabKind k2), loc) | L.KName => (L'.KName, loc) | L.KRecord k => (L'.KRecord (elabKind k), loc) + | L.KUnit => (L'.KUnit, loc) | L.KWild => kunif loc fun foldKind (dom, ran, loc)= @@ -330,6 +334,10 @@ fun elabCon env (c, loc) = val ku = kunif loc val k = (L'.KRecord ku, loc) in + case D.prove env D.empty (c1', c2', loc) of + [] => () + | _ => raise Fail "Can't prove disjointness"; + checkKind env c1' k1 k; checkKind env c2' k2 k; ((L'.CConcat (c1', c2'), loc), k) @@ -343,6 +351,8 @@ fun elabCon env (c, loc) = foldKind (dom, ran, loc)) end + | L.CUnit => ((L'.CUnit, loc), (L'.KUnit, loc)) + | L.CWild k => let val k' = elabKind k @@ -478,6 +488,8 @@ fun kindof env (c, loc) = | L'.CConcat (c, _) => kindof env c | L'.CFold (dom, ran) => foldKind (dom, ran, loc) + | L'.CUnit => (L'.KUnit, loc) + | L'.CError => kerror | L'.CUnif (_, k, _, _) => k diff --git a/src/explify.sml b/src/explify.sml index 0f416067..af655eb4 100644 --- a/src/explify.sml +++ b/src/explify.sml @@ -38,6 +38,8 @@ fun explifyKind (k, loc) = | L.KName => (L'.KName, loc) | L.KRecord k => (L'.KRecord (explifyKind k), loc) + | L.KUnit => raise Fail "Explify KUnit" + | L.KError => raise Fail ("explifyKind: KError at " ^ EM.spanToString loc) | L.KUnif (_, _, ref (SOME k)) => explifyKind k | L.KUnif _ => raise Fail ("explifyKind: KUnif at " ^ EM.spanToString loc) @@ -61,6 +63,8 @@ fun explifyCon (c, loc) = | L.CConcat (c1, c2) => (L'.CConcat (explifyCon c1, explifyCon c2), loc) | L.CFold (dom, ran) => (L'.CFold (explifyKind dom, explifyKind ran), loc) + | L.CUnit => raise Fail "Explify CUnit" + | L.CError => raise Fail ("explifyCon: CError at " ^ EM.spanToString loc) | L.CUnif (_, _, _, ref (SOME c)) => explifyCon c | L.CUnif _ => raise Fail ("explifyCon: CUnif at " ^ EM.spanToString loc) diff --git a/src/lacweb.grm b/src/lacweb.grm index c42b5eb5..91285e4c 100644 --- a/src/lacweb.grm +++ b/src/lacweb.grm @@ -40,7 +40,7 @@ val s = ErrorMsg.spanOf | SYMBOL of string | CSYMBOL of string | LPAREN | RPAREN | LBRACK | RBRACK | LBRACE | RBRACE | EQ | COMMA | COLON | DCOLON | TCOLON | DOT | HASH | UNDER | UNDERUNDER - | CON | LTYPE | VAL | FOLD + | CON | LTYPE | VAL | FOLD | UNIT | KUNIT | TYPE | NAME | ARROW | LARROW | DARROW | FN | PLUSPLUS | DOLLAR @@ -179,6 +179,7 @@ kind : TYPE (KType, s (TYPEleft, TYPEright)) | LBRACE kind RBRACE (KRecord kind, s (LBRACEleft, RBRACEright)) | kind ARROW kind (KArrow (kind1, kind2), s (kind1left, kind2right)) | LPAREN kind RPAREN (#1 kind, s (LPARENleft, RPARENright)) + | KUNIT (KUnit, s (KUNITleft, KUNITright)) | UNDERUNDER (KWild, s (UNDERUNDERleft, UNDERUNDERright)) capps : cterm (cterm) @@ -216,6 +217,7 @@ cterm : LPAREN cexp RPAREN (#1 cexp, s (LPARENleft, RPARENright)) | path (CVar path, s (pathleft, pathright)) | UNDER (CWild (KWild, s (UNDERleft, UNDERright)), s (UNDERleft, UNDERright)) | FOLD (CFold, s (FOLDleft, FOLDright)) + | UNIT (CUnit, s (UNITleft, UNITright)) rcon : ([]) | ident EQ cexp ([(ident, cexp)]) diff --git a/src/lacweb.lex b/src/lacweb.lex index 038e591f..45e2f1f2 100644 --- a/src/lacweb.lex +++ b/src/lacweb.lex @@ -120,6 +120,7 @@ realconst = [0-9]+\.[0-9]*; str := #"\n" :: !str; continue()); <STRING> . => (str := String.sub (yytext, 0) :: !str; continue()); +<INITIAL> "()" => (Tokens.UNIT (pos yypos, pos yypos + size yytext)); <INITIAL> "(" => (Tokens.LPAREN (pos yypos, pos yypos + size yytext)); <INITIAL> ")" => (Tokens.RPAREN (pos yypos, pos yypos + size yytext)); <INITIAL> "[" => (Tokens.LBRACK (pos yypos, pos yypos + size yytext)); @@ -161,6 +162,7 @@ realconst = [0-9]+\.[0-9]*; <INITIAL> "Type" => (Tokens.TYPE (pos yypos, pos yypos + size yytext)); <INITIAL> "Name" => (Tokens.NAME (pos yypos, pos yypos + size yytext)); +<INITIAL> "Unit" => (Tokens.KUNIT (pos yypos, pos yypos + size yytext)); <INITIAL> {id} => (Tokens.SYMBOL (yytext, pos yypos, pos yypos + size yytext)); <INITIAL> {cid} => (Tokens.CSYMBOL (yytext, pos yypos, pos yypos + size yytext)); diff --git a/src/source.sml b/src/source.sml index d36e86ca..e55e5901 100644 --- a/src/source.sml +++ b/src/source.sml @@ -34,6 +34,7 @@ datatype kind' = | KArrow of kind * kind | KName | KRecord of kind + | KUnit | KWild withtype kind = kind' located @@ -59,6 +60,8 @@ datatype con' = | CConcat of con * con | CFold + | CUnit + | CWild of kind withtype con = con' located diff --git a/src/source_print.sml b/src/source_print.sml index aa880e08..80b077d0 100644 --- a/src/source_print.sml +++ b/src/source_print.sml @@ -44,6 +44,7 @@ fun p_kind' par (k, _) = p_kind k2]) | KName => string "Name" | KRecord k => box [string "{", p_kind k, string "}"] + | KUnit => string "Unit" | KWild => string "_" and p_kind k = p_kind' false k @@ -127,6 +128,9 @@ fun p_con' par (c, _) = space, p_con c2]) | CFold => string "fold" + + | CUnit => string "()" + | CWild k => box [string "(_", space, string "::", diff --git a/src/sources b/src/sources index 03b8f344..20bb2011 100644 --- a/src/sources +++ b/src/sources @@ -35,6 +35,9 @@ elab_print.sml elab_ops.sig elab_ops.sml +disjoint.sig +disjoint.sml + elaborate.sig elaborate.sml diff --git a/tests/disjoint.lac b/tests/disjoint.lac new file mode 100644 index 00000000..805b9674 --- /dev/null +++ b/tests/disjoint.lac @@ -0,0 +1 @@ +con c = [A = ()] ++ [A = ()] |