packages feed

swish 0.6.5.2 → 0.7.0.0

raw patch · 108 files changed

+19525/−19796 lines, 108 filesdep +semigroupsdep ~containersdep ~internPVP ok

version bump matches the API change (PVP)

Dependencies added: semigroups

Dependency ranges changed: containers, intern

API changes (from Hackage documentation)

- Swish.RDF.BuiltInDatatypes: allDatatypes :: [RDFDatatype]
- Swish.RDF.BuiltInDatatypes: findRDFDatatype :: ScopedName -> Maybe RDFDatatype
- Swish.RDF.BuiltInMap: allDatatypeRulesets :: [RDFRuleset]
- Swish.RDF.BuiltInMap: allRulesets :: [RDFRuleset]
- Swish.RDF.BuiltInMap: findRDFDatatype :: ScopedName -> Maybe RDFDatatype
- Swish.RDF.BuiltInMap: findRDFOpenVarBindingModifier :: ScopedName -> Maybe RDFOpenVarBindingModify
- Swish.RDF.BuiltInMap: rdfRulesetMap :: RDFRulesetMap
- Swish.RDF.BuiltInRules: allDatatypeRulesets :: [RDFRuleset]
- Swish.RDF.BuiltInRules: allRulesets :: [RDFRuleset]
- Swish.RDF.BuiltInRules: findRDFOpenVarBindingModifier :: ScopedName -> Maybe RDFOpenVarBindingModify
- Swish.RDF.BuiltInRules: rdfRulesetMap :: RDFRulesetMap
- Swish.RDF.Datatype: Datatype :: (DatatypeVal ex vt lb vn) -> Datatype ex lb vn
- Swish.RDF.Datatype: DatatypeMap :: (Text -> Maybe vt) -> (vt -> Maybe Text) -> DatatypeMap vt
- Swish.RDF.Datatype: DatatypeMod :: ScopedName -> [ModifierFn vt] -> ApplyModifier lb vn -> DatatypeMod vt lb vn
- Swish.RDF.Datatype: DatatypeRel :: ScopedName -> DatatypeRelFn vt -> DatatypeRel vt
- Swish.RDF.Datatype: DatatypeSub :: DatatypeVal ex supvt lb vn -> DatatypeVal ex subvt lb vn -> (subvt -> supvt) -> (supvt -> Maybe subvt) -> DatatypeSub ex lb vn supvt subvt
- Swish.RDF.Datatype: DatatypeVal :: ScopedName -> Ruleset ex -> (ex -> [Rule ex]) -> [OpenVarBindingModify lb vn] -> DatatypeMap vt -> [DatatypeRel vt] -> [DatatypeMod vt lb vn] -> DatatypeVal ex vt lb vn
- Swish.RDF.Datatype: altArgs :: Eq vt => DatatypeRelPr vt -> [(vt -> Bool, [b])] -> ((vt -> Bool) -> b -> [Maybe vt] -> Maybe [vt]) -> DatatypeRelFn vt
- Swish.RDF.Datatype: binMaybeFnApp :: BinMaybeFnApply a
- Swish.RDF.Datatype: binaryFnApp :: BinaryFnApply a
- Swish.RDF.Datatype: data Datatype ex lb vn
- Swish.RDF.Datatype: data DatatypeMap vt
- Swish.RDF.Datatype: data DatatypeMod vt lb vn
- Swish.RDF.Datatype: data DatatypeRel vt
- Swish.RDF.Datatype: data DatatypeSub ex lb vn supvt subvt
- Swish.RDF.Datatype: data DatatypeVal ex vt lb vn
- Swish.RDF.Datatype: dmAppf :: DatatypeMod vt lb vn -> ApplyModifier lb vn
- Swish.RDF.Datatype: dmModf :: DatatypeMod vt lb vn -> [ModifierFn vt]
- Swish.RDF.Datatype: dmName :: DatatypeMod vt lb vn -> ScopedName
- Swish.RDF.Datatype: dtRelFunc :: DatatypeRel vt -> DatatypeRelFn vt
- Swish.RDF.Datatype: dtRelName :: DatatypeRel vt -> ScopedName
- Swish.RDF.Datatype: getDTMod :: ScopedName -> DatatypeVal ex vt lb vn -> Maybe (DatatypeMod vt lb vn)
- Swish.RDF.Datatype: getDTRel :: ScopedName -> DatatypeVal ex vt lb vn -> Maybe (DatatypeRel vt)
- Swish.RDF.Datatype: getTypeAxiom :: ScopedName -> Datatype ex lb vn -> Maybe (Formula ex)
- Swish.RDF.Datatype: getTypeRule :: ScopedName -> Datatype ex lb vn -> Maybe (Rule ex)
- Swish.RDF.Datatype: instance LookupEntryClass (Datatype ex lb vn) ScopedName (Datatype ex lb vn)
- Swish.RDF.Datatype: instance LookupEntryClass (DatatypeMod vt lb vn) ScopedName (DatatypeMod vt lb vn)
- Swish.RDF.Datatype: instance LookupEntryClass (DatatypeRel vt) ScopedName (DatatypeRel vt)
- Swish.RDF.Datatype: listFnApp :: ListFnApply a
- Swish.RDF.Datatype: makeVmod11 :: (Eq lb, Show lb, Eq vn, Show vn) => ApplyModifier lb vn
- Swish.RDF.Datatype: makeVmod11inv :: (Eq lb, Show lb, Eq vn, Show vn) => ApplyModifier lb vn
- Swish.RDF.Datatype: makeVmod20 :: (Eq lb, Show lb, Eq vn, Show vn) => ApplyModifier lb vn
- Swish.RDF.Datatype: makeVmod21 :: (Eq lb, Show lb, Eq vn, Show vn) => ApplyModifier lb vn
- Swish.RDF.Datatype: makeVmod21inv :: (Eq lb, Show lb, Eq vn, Show vn) => ApplyModifier lb vn
- Swish.RDF.Datatype: makeVmod22 :: (Eq lb, Show lb, Eq vn, Show vn) => ApplyModifier lb vn
- Swish.RDF.Datatype: makeVmodN1 :: (Eq lb, Show lb, Eq vn, Show vn) => ApplyModifier lb vn
- Swish.RDF.Datatype: mapL2V :: DatatypeMap vt -> Text -> Maybe vt
- Swish.RDF.Datatype: mapV2L :: DatatypeMap vt -> vt -> Maybe Text
- Swish.RDF.Datatype: nullDatatypeMod :: DatatypeMod vt lb vn
- Swish.RDF.Datatype: trelSub :: DatatypeSub ex lb vn supvt subvt -> DatatypeVal ex subvt lb vn
- Swish.RDF.Datatype: trelSup :: DatatypeSub ex lb vn supvt subvt -> DatatypeVal ex supvt lb vn
- Swish.RDF.Datatype: trelToSub :: DatatypeSub ex lb vn supvt subvt -> supvt -> Maybe subvt
- Swish.RDF.Datatype: trelToSup :: DatatypeSub ex lb vn supvt subvt -> subvt -> supvt
- Swish.RDF.Datatype: tvalMap :: DatatypeVal ex vt lb vn -> DatatypeMap vt
- Swish.RDF.Datatype: tvalMkCanonicalForm :: DatatypeVal ex vt lb vn -> Text -> Maybe Text
- Swish.RDF.Datatype: tvalMkMods :: DatatypeVal ex vt lb vn -> [OpenVarBindingModify lb vn]
- Swish.RDF.Datatype: tvalMkRules :: DatatypeVal ex vt lb vn -> ex -> [Rule ex]
- Swish.RDF.Datatype: tvalMod :: DatatypeVal ex vt lb vn -> [DatatypeMod vt lb vn]
- Swish.RDF.Datatype: tvalName :: DatatypeVal ex vt lb vn -> ScopedName
- Swish.RDF.Datatype: tvalRel :: DatatypeVal ex vt lb vn -> [DatatypeRel vt]
- Swish.RDF.Datatype: tvalRules :: DatatypeVal ex vt lb vn -> Ruleset ex
- Swish.RDF.Datatype: type ApplyModifier lb vn = ScopedName -> [ModifierFn vn] -> OpenVarBindingModify lb vn
- Swish.RDF.Datatype: type BinMaybeFnApply a = (a -> Bool) -> BinMaybeFnDescr a -> [Maybe a] -> Maybe [a]
- Swish.RDF.Datatype: type BinMaybeFnDescr a = (a -> a -> Maybe [a], Int, Int)
- Swish.RDF.Datatype: type BinMaybeFnTable a = [(a -> Bool, [BinMaybeFnDescr a])]
- Swish.RDF.Datatype: type BinaryFnApply a = (a -> Bool) -> BinaryFnDescr a -> [Maybe a] -> Maybe [a]
- Swish.RDF.Datatype: type BinaryFnDescr a = (a -> a -> a, Int, Int)
- Swish.RDF.Datatype: type BinaryFnTable a = [(a -> Bool, [BinaryFnDescr a])]
- Swish.RDF.Datatype: type DatatypeRelFn vt = [Maybe vt] -> Maybe [[vt]]
- Swish.RDF.Datatype: type DatatypeRelPr vt = [vt] -> Bool
- Swish.RDF.Datatype: type ListFnApply a = (a -> Bool) -> ListFnDescr a -> [Maybe a] -> Maybe [a]
- Swish.RDF.Datatype: type ListFnDescr a = (a -> a -> a, a, a -> a -> a, Int)
- Swish.RDF.Datatype: type ListFnTable a = [(a -> Bool, [ListFnDescr a])]
- Swish.RDF.Datatype: type ModifierFn vn = [vn] -> [vn]
- Swish.RDF.Datatype: type UnaryFnApply a = (a -> Bool) -> UnaryFnDescr a -> [Maybe a] -> Maybe [a]
- Swish.RDF.Datatype: type UnaryFnDescr a = (a -> a, Int)
- Swish.RDF.Datatype: type UnaryFnTable a = [(a -> Bool, [UnaryFnDescr a])]
- Swish.RDF.Datatype: typeMkCanonicalForm :: Datatype ex lb vn -> Text -> Maybe Text
- Swish.RDF.Datatype: typeMkModifiers :: Datatype ex lb vn -> [OpenVarBindingModify lb vn]
- Swish.RDF.Datatype: typeMkRules :: Datatype ex lb vn -> ex -> [Rule ex]
- Swish.RDF.Datatype: typeName :: Datatype ex lb vn -> ScopedName
- Swish.RDF.Datatype: typeRules :: Datatype ex lb vn -> Ruleset ex
- Swish.RDF.Datatype: unaryFnApp :: UnaryFnApply a
- Swish.RDF.GraphClass: Arc :: lb -> lb -> lb -> Arc lb
- Swish.RDF.GraphClass: add :: LDGraph lg lb => lg lb -> lg lb -> lg lb
- Swish.RDF.GraphClass: aobj :: Arc lb -> lb
- Swish.RDF.GraphClass: apred :: Arc lb -> lb
- Swish.RDF.GraphClass: arc :: lb -> lb -> lb -> Arc lb
- Swish.RDF.GraphClass: arcFromTriple :: (lb, lb, lb) -> Arc lb
- Swish.RDF.GraphClass: arcLabels :: Arc lb -> [lb]
- Swish.RDF.GraphClass: arcObj :: Arc lb -> lb
- Swish.RDF.GraphClass: arcPred :: Arc lb -> lb
- Swish.RDF.GraphClass: arcSubj :: Arc lb -> lb
- Swish.RDF.GraphClass: arcToTriple :: Arc lb -> (lb, lb, lb)
- Swish.RDF.GraphClass: asubj :: Arc lb -> lb
- Swish.RDF.GraphClass: class (Eq (lg lb), Eq lb) => LDGraph lg lb where extract sel = update (filter sel) add addg = update (union (getArcs addg)) delete delg = update (\\ getArcs delg) labels g = foldl' union [] (map arcLabels (getArcs g)) nodes g = foldl' union [] (map arcNodes (getArcs g)) update f g = setArcs (f (getArcs g)) g
- Swish.RDF.GraphClass: class (Eq lb, Show lb, Ord lb) => Label lb
- Swish.RDF.GraphClass: containedIn :: LDGraph lg lb => lg lb -> lg lb -> Bool
- Swish.RDF.GraphClass: data Arc lb
- Swish.RDF.GraphClass: delete :: LDGraph lg lb => lg lb -> lg lb -> lg lb
- Swish.RDF.GraphClass: extract :: LDGraph lg lb => Selector lb -> lg lb -> lg lb
- Swish.RDF.GraphClass: getArcs :: LDGraph lg lb => lg lb -> [Arc lb]
- Swish.RDF.GraphClass: getLocal :: Label lb => lb -> String
- Swish.RDF.GraphClass: hasLabel :: Eq lb => lb -> Arc lb -> Bool
- Swish.RDF.GraphClass: instance Eq lb => Eq (Arc lb)
- Swish.RDF.GraphClass: instance Foldable Arc
- Swish.RDF.GraphClass: instance Functor Arc
- Swish.RDF.GraphClass: instance Hashable lb => Hashable (Arc lb)
- Swish.RDF.GraphClass: instance Ord lb => Ord (Arc lb)
- Swish.RDF.GraphClass: instance Show lb => Show (Arc lb)
- Swish.RDF.GraphClass: instance Traversable Arc
- Swish.RDF.GraphClass: labelHash :: Label lb => Int -> lb -> Int
- Swish.RDF.GraphClass: labelIsVar :: Label lb => lb -> Bool
- Swish.RDF.GraphClass: labels :: LDGraph lg lb => lg lb -> [lb]
- Swish.RDF.GraphClass: makeLabel :: Label lb => String -> lb
- Swish.RDF.GraphClass: nodes :: LDGraph lg lb => lg lb -> [lb]
- Swish.RDF.GraphClass: replaceArcs :: LDGraph lg lb => lg lb -> [Arc lb] -> lg lb
- Swish.RDF.GraphClass: setArcs :: LDGraph lg lb => [Arc lb] -> lg lb -> lg lb
- Swish.RDF.GraphClass: type Selector lb = Arc lb -> Bool
- Swish.RDF.GraphClass: update :: LDGraph lg lb => ([Arc lb] -> [Arc lb]) -> lg lb -> lg lb
- Swish.RDF.GraphMatch: LabelEntry :: lb -> lv -> GenLabelEntry lb lv
- Swish.RDF.GraphMatch: LabelMap :: Int -> (LookupMap (GenLabelEntry lb lv)) -> GenLabelMap lb lv
- Swish.RDF.GraphMatch: ScopedLabel :: Int -> lb -> ScopedLabel lb
- Swish.RDF.GraphMatch: assignLabelMap :: Label lb => [lb] -> LabelMap lb -> LabelMap lb
- Swish.RDF.GraphMatch: data Label lb => GenLabelEntry lb lv
- Swish.RDF.GraphMatch: data (Label lb, Eq lv, Show lv) => GenLabelMap lb lv
- Swish.RDF.GraphMatch: data Label lb => ScopedLabel lb
- Swish.RDF.GraphMatch: emptyMap :: Label lb => LabelMap lb
- Swish.RDF.GraphMatch: equivalenceClasses :: Label lb => LabelMap lb -> [lb] -> [EquivalenceClass lb]
- Swish.RDF.GraphMatch: graphLabels :: Label lb => [Arc lb] -> [lb]
- Swish.RDF.GraphMatch: graphMatch :: Label lb => (lb -> lb -> Bool) -> [Arc lb] -> [Arc lb] -> (Bool, LabelMap (ScopedLabel lb))
- Swish.RDF.GraphMatch: graphMatch1 :: Label lb => Bool -> (lb -> lb -> Bool) -> [Arc lb] -> [Arc lb] -> LabelMap lb -> [(EquivalenceClass lb, EquivalenceClass lb)] -> (Bool, LabelMap lb)
- Swish.RDF.GraphMatch: graphMatch2 :: Label lb => (lb -> lb -> Bool) -> [Arc lb] -> [Arc lb] -> LabelMap lb -> [(EquivalenceClass lb, EquivalenceClass lb)] -> (Bool, LabelMap lb)
- Swish.RDF.GraphMatch: instance (Label lb, Eq lb, Show lb, Eq lv, Show lv) => Eq (GenLabelEntry lb lv)
- Swish.RDF.GraphMatch: instance (Label lb, Eq lb, Show lb, Eq lv, Show lv) => LookupEntryClass (GenLabelEntry lb lv) lb lv
- Swish.RDF.GraphMatch: instance (Label lb, Eq lb, Show lb, Eq lv, Show lv) => Show (GenLabelEntry lb lv)
- Swish.RDF.GraphMatch: instance Label lb => Eq (LabelMap lb)
- Swish.RDF.GraphMatch: instance Label lb => Eq (ScopedLabel lb)
- Swish.RDF.GraphMatch: instance Label lb => Label (ScopedLabel lb)
- Swish.RDF.GraphMatch: instance Label lb => Ord (ScopedLabel lb)
- Swish.RDF.GraphMatch: instance Label lb => Show (LabelMap lb)
- Swish.RDF.GraphMatch: instance Label lb => Show (ScopedLabel lb)
- Swish.RDF.GraphMatch: labelHash :: Label lb => Int -> lb -> Int
- Swish.RDF.GraphMatch: labelIsVar :: Label lb => lb -> Bool
- Swish.RDF.GraphMatch: makeScopedArc :: Label lb => Int -> Arc lb -> Arc (ScopedLabel lb)
- Swish.RDF.GraphMatch: makeScopedLabel :: Label lb => Int -> lb -> ScopedLabel lb
- Swish.RDF.GraphMatch: mapLabelIndex :: Label lb => LabelMap lb -> lb -> LabelIndex
- Swish.RDF.GraphMatch: newGenerationMap :: Label lb => LabelMap lb -> LabelMap lb
- Swish.RDF.GraphMatch: newLabelMap :: Label lb => LabelMap lb -> [(lb, Int)] -> LabelMap lb
- Swish.RDF.GraphMatch: nullLabelVal :: LabelIndex
- Swish.RDF.GraphMatch: reclassify :: Label lb => [Arc lb] -> [Arc lb] -> LabelMap lb -> [(EquivalenceClass lb, EquivalenceClass lb)] -> (LabelMap lb, [(EquivalenceClass lb, EquivalenceClass lb)], Bool, Bool)
- Swish.RDF.GraphMatch: setLabelHash :: Label lb => LabelMap lb -> (lb, Int) -> LabelMap lb
- Swish.RDF.GraphMatch: type EquivalenceClass lb = (LabelIndex, [lb])
- Swish.RDF.GraphMatch: type LabelEntry lb = GenLabelEntry lb LabelIndex
- Swish.RDF.GraphMatch: type LabelIndex = (Int, Int)
- Swish.RDF.GraphMatch: type LabelMap lb = GenLabelMap lb LabelIndex
- Swish.RDF.GraphMem: GraphMem :: [Arc lb] -> GraphMem lb
- Swish.RDF.GraphMem: LF :: String -> LabelMem
- Swish.RDF.GraphMem: LV :: String -> LabelMem
- Swish.RDF.GraphMem: add :: LDGraph lg lb => lg lb -> lg lb -> lg lb
- Swish.RDF.GraphMem: arcs :: GraphMem lb -> [Arc lb]
- Swish.RDF.GraphMem: data GraphMem lb
- Swish.RDF.GraphMem: data LabelMem
- Swish.RDF.GraphMem: delete :: LDGraph lg lb => lg lb -> lg lb -> lg lb
- Swish.RDF.GraphMem: extract :: LDGraph lg lb => Selector lb -> lg lb -> lg lb
- Swish.RDF.GraphMem: getArcs :: LDGraph lg lb => lg lb -> [Arc lb]
- Swish.RDF.GraphMem: instance Eq LabelMem
- Swish.RDF.GraphMem: instance Foldable GraphMem
- Swish.RDF.GraphMem: instance Functor GraphMem
- Swish.RDF.GraphMem: instance Hashable LabelMem
- Swish.RDF.GraphMem: instance Label LabelMem
- Swish.RDF.GraphMem: instance Label lb => Eq (GraphMem lb)
- Swish.RDF.GraphMem: instance Label lb => LDGraph GraphMem lb
- Swish.RDF.GraphMem: instance Label lb => Show (GraphMem lb)
- Swish.RDF.GraphMem: instance Ord LabelMem
- Swish.RDF.GraphMem: instance Show LabelMem
- Swish.RDF.GraphMem: instance Traversable GraphMem
- Swish.RDF.GraphMem: labelHash :: Label lb => Int -> lb -> Int
- Swish.RDF.GraphMem: labelIsVar :: Label lb => lb -> Bool
- Swish.RDF.GraphMem: labels :: LDGraph lg lb => lg lb -> [lb]
- Swish.RDF.GraphMem: matchGraphMem :: Label lb => GraphMem lb -> GraphMem lb -> (Bool, LabelMap (ScopedLabel lb))
- Swish.RDF.GraphMem: setArcs :: LDGraph lg lb => [Arc lb] -> lg lb -> lg lb
- Swish.RDF.GraphPartition: PartObj :: lb -> GraphPartition lb
- Swish.RDF.GraphPartition: PartSub :: lb -> [(lb, GraphPartition lb)] -> GraphPartition lb
- Swish.RDF.GraphPartition: PartitionedGraph :: [GraphPartition lb] -> PartitionedGraph lb
- Swish.RDF.GraphPartition: comparePartitions :: Label lb => PartitionedGraph lb -> PartitionedGraph lb -> [(Maybe (GraphPartition lb), Maybe (GraphPartition lb))]
- Swish.RDF.GraphPartition: data GraphPartition lb
- Swish.RDF.GraphPartition: data PartitionedGraph lb
- Swish.RDF.GraphPartition: getArcs :: PartitionedGraph lb -> [Arc lb]
- Swish.RDF.GraphPartition: getPartitions :: PartitionedGraph lb -> [GraphPartition lb]
- Swish.RDF.GraphPartition: instance Label lb => Eq (GraphPartition lb)
- Swish.RDF.GraphPartition: instance Label lb => Eq (PartitionedGraph lb)
- Swish.RDF.GraphPartition: instance Label lb => Show (GraphPartition lb)
- Swish.RDF.GraphPartition: instance Label lb => Show (PartitionedGraph lb)
- Swish.RDF.GraphPartition: node :: GraphPartition lb -> lb
- Swish.RDF.GraphPartition: partitionGraph :: Label lb => [Arc lb] -> PartitionedGraph lb
- Swish.RDF.GraphPartition: partitionShowP :: Label lb => String -> GraphPartition lb -> String
- Swish.RDF.GraphPartition: toArcs :: GraphPartition lb -> [Arc lb]
- Swish.RDF.MapXsdDecimal: mapXsdDecimal :: DatatypeMap Double
- Swish.RDF.MapXsdInteger: mapXsdInteger :: DatatypeMap Integer
- Swish.RDF.N3Formatter: formatGraphAsBuilder :: RDFGraph -> Builder
- Swish.RDF.N3Formatter: formatGraphAsLazyText :: RDFGraph -> Text
- Swish.RDF.N3Formatter: formatGraphAsText :: RDFGraph -> Text
- Swish.RDF.N3Formatter: formatGraphDiag :: Builder -> Bool -> RDFGraph -> (Builder, NodeGenLookupMap, Int, [String])
- Swish.RDF.N3Formatter: formatGraphIndent :: Builder -> Bool -> RDFGraph -> Builder
- Swish.RDF.N3Formatter: instance Eq LabelContext
- Swish.RDF.N3Formatter: instance Show LabelContext
- Swish.RDF.N3Formatter: type NodeGenLookupMap = LookupMap (RDFLabel, Int)
- Swish.RDF.N3Parser: N3State :: RDFGraph -> RDFLabel -> NamespaceMap -> SpecialMap -> Int -> [Text] -> Bool -> N3State
- Swish.RDF.N3Parser: allowLocalNames :: N3State -> Bool
- Swish.RDF.N3Parser: data N3State
- Swish.RDF.N3Parser: document :: N3Parser RDFGraph
- Swish.RDF.N3Parser: getPrefix :: N3Parser ()
- Swish.RDF.N3Parser: graphState :: N3State -> RDFGraph
- Swish.RDF.N3Parser: keywordsList :: N3State -> [Text]
- Swish.RDF.N3Parser: lexUriRef :: N3Parser URI
- Swish.RDF.N3Parser: n3symbol :: N3Parser ScopedName
- Swish.RDF.N3Parser: newBlankNode :: N3Parser RDFLabel
- Swish.RDF.N3Parser: nodeGen :: N3State -> Int
- Swish.RDF.N3Parser: parseAbsURIrefFromText :: Text -> Either String URI
- Swish.RDF.N3Parser: parseAltFromText :: String -> String -> Text -> Either String String
- Swish.RDF.N3Parser: parseAnyfromText :: N3Parser a -> Maybe QName -> Text -> Either String a
- Swish.RDF.N3Parser: parseLexURIrefFromText :: Text -> Either String URI
- Swish.RDF.N3Parser: parseN3 :: Text -> Maybe QName -> ParseResult
- Swish.RDF.N3Parser: parseN3fromText :: Text -> ParseResult
- Swish.RDF.N3Parser: parseNameFromText :: Text -> Either String String
- Swish.RDF.N3Parser: parseTextFromText :: String -> Text -> Either String String
- Swish.RDF.N3Parser: parseURIref2FromText :: Text -> Either String ScopedName
- Swish.RDF.N3Parser: prefixUris :: N3State -> NamespaceMap
- Swish.RDF.N3Parser: quickVariable :: N3Parser RDFLabel
- Swish.RDF.N3Parser: subgraph :: RDFLabel -> N3Parser RDFGraph
- Swish.RDF.N3Parser: syntaxUris :: N3State -> SpecialMap
- Swish.RDF.N3Parser: thisNode :: N3State -> RDFLabel
- Swish.RDF.N3Parser: type N3Parser a = Parser N3State a
- Swish.RDF.N3Parser: type ParseResult = Either String RDFGraph
- Swish.RDF.N3Parser: type SpecialMap = LookupMap (String, ScopedName)
- Swish.RDF.NTFormatter: formatGraphAsBuilder :: RDFGraph -> Builder
- Swish.RDF.NTFormatter: formatGraphAsLazyText :: RDFGraph -> Text
- Swish.RDF.NTFormatter: formatGraphAsText :: RDFGraph -> Text
- Swish.RDF.NTFormatter: instance Show NTFormatterState
- Swish.RDF.NTFormatter: type NodeGenLookupMap = LookupMap (RDFLabel, Int)
- Swish.RDF.NTParser: parseNT :: Text -> ParseResult
- Swish.RDF.NTParser: parsefromString :: NTParser a -> String -> Either String a
- Swish.RDF.NTParser: type ParseResult = Either String RDFGraph
- Swish.RDF.Proof: Proof :: [Ruleset ex] -> Formula ex -> Formula ex -> [Step ex] -> Proof ex
- Swish.RDF.Proof: Step :: Rule ex -> [Formula ex] -> Formula ex -> Step ex
- Swish.RDF.Proof: checkProof :: Expression ex => Proof ex -> Bool
- Swish.RDF.Proof: checkStep :: Expression ex => [Rule ex] -> [ex] -> Step ex -> Bool
- Swish.RDF.Proof: data Proof ex
- Swish.RDF.Proof: data Step ex
- Swish.RDF.Proof: explainProof :: Expression ex => Proof ex -> Maybe String
- Swish.RDF.Proof: instance Show ex => Show (Step ex)
- Swish.RDF.Proof: proofChain :: Proof ex -> [Step ex]
- Swish.RDF.Proof: proofContext :: Proof ex -> [Ruleset ex]
- Swish.RDF.Proof: proofInput :: Proof ex -> Formula ex
- Swish.RDF.Proof: proofResult :: Proof ex -> Formula ex
- Swish.RDF.Proof: showProof :: ShowM ex => String -> Proof ex -> String
- Swish.RDF.Proof: showsFormula :: ShowM ex => String -> Formula ex -> ShowS
- Swish.RDF.Proof: showsProof :: ShowM ex => String -> Proof ex -> ShowS
- Swish.RDF.Proof: stepAnt :: Step ex -> [Formula ex]
- Swish.RDF.Proof: stepCon :: Step ex -> Formula ex
- Swish.RDF.Proof: stepRule :: Step ex -> Rule ex
- Swish.RDF.RDFDatatype: applyRDFDatatypeMod :: RDFDatatypeVal vt -> RDFDatatypeMod vt -> [RDFLabel] -> [RDFVarBinding] -> [RDFVarBinding]
- Swish.RDF.RDFDatatype: fromRDFLabel :: RDFDatatypeVal vt -> RDFLabel -> Maybe vt
- Swish.RDF.RDFDatatype: makeDatatypedLiteral :: ScopedName -> Text -> RDFLabel
- Swish.RDF.RDFDatatype: makeRdfDtOpenVarBindingModifiers :: RDFDatatypeVal vt -> [RDFOpenVarBindingModify]
- Swish.RDF.RDFDatatype: makeRdfDtOpenVarBindingModify :: RDFDatatypeVal vt -> RDFDatatypeMod vt -> RDFOpenVarBindingModify
- Swish.RDF.RDFDatatype: toRDFLabel :: RDFDatatypeVal vt -> vt -> Maybe RDFLabel
- Swish.RDF.RDFDatatype: type RDFApplyModifier = ApplyModifier RDFLabel RDFLabel
- Swish.RDF.RDFDatatype: type RDFDatatype = Datatype RDFGraph RDFLabel RDFLabel
- Swish.RDF.RDFDatatype: type RDFDatatypeMod vt = DatatypeMod vt RDFLabel RDFLabel
- Swish.RDF.RDFDatatype: type RDFDatatypeSub supvt subvt = DatatypeSub RDFGraph RDFLabel RDFLabel supvt subvt
- Swish.RDF.RDFDatatype: type RDFDatatypeVal vt = DatatypeVal RDFGraph vt RDFLabel RDFLabel
- Swish.RDF.RDFDatatype: type RDFModifierFn = ModifierFn RDFLabel
- Swish.RDF.RDFDatatypeXsdDecimal: axiomsXsdDecimal :: [RDFFormula]
- Swish.RDF.RDFDatatypeXsdDecimal: namespaceXsdDecimal :: Namespace
- Swish.RDF.RDFDatatypeXsdDecimal: prefixXsdDecimal :: Builder
- Swish.RDF.RDFDatatypeXsdDecimal: rdfDatatypeValXsdDecimal :: RDFDatatypeVal Double
- Swish.RDF.RDFDatatypeXsdDecimal: rdfDatatypeXsdDecimal :: RDFDatatype
- Swish.RDF.RDFDatatypeXsdDecimal: rulesXsdDecimal :: [RDFRule]
- Swish.RDF.RDFDatatypeXsdDecimal: typeNameXsdDecimal :: ScopedName
- Swish.RDF.RDFDatatypeXsdInteger: axiomsXsdInteger :: [RDFFormula]
- Swish.RDF.RDFDatatypeXsdInteger: namespaceXsdInteger :: Namespace
- Swish.RDF.RDFDatatypeXsdInteger: prefixXsdInteger :: Builder
- Swish.RDF.RDFDatatypeXsdInteger: rdfDatatypeValXsdInteger :: RDFDatatypeVal Integer
- Swish.RDF.RDFDatatypeXsdInteger: rdfDatatypeXsdInteger :: RDFDatatype
- Swish.RDF.RDFDatatypeXsdInteger: rulesXsdInteger :: [RDFRule]
- Swish.RDF.RDFDatatypeXsdInteger: typeNameXsdInteger :: ScopedName
- Swish.RDF.RDFDatatypeXsdString: axiomsXsdString :: [RDFFormula]
- Swish.RDF.RDFDatatypeXsdString: namespaceXsdString :: Namespace
- Swish.RDF.RDFDatatypeXsdString: prefixXsdString :: Builder
- Swish.RDF.RDFDatatypeXsdString: rdfDatatypeValXsdString :: RDFDatatypeVal Text
- Swish.RDF.RDFDatatypeXsdString: rdfDatatypeXsdString :: RDFDatatype
- Swish.RDF.RDFDatatypeXsdString: rulesXsdString :: [RDFRule]
- Swish.RDF.RDFDatatypeXsdString: typeNameXsdString :: ScopedName
- Swish.RDF.RDFGraph: Arc :: lb -> lb -> lb -> Arc lb
- Swish.RDF.RDFGraph: Blank :: String -> RDFLabel
- Swish.RDF.RDFGraph: Formula :: lb -> gr -> LookupFormula lb gr
- Swish.RDF.RDFGraph: Lit :: Text -> (Maybe ScopedName) -> RDFLabel
- Swish.RDF.RDFGraph: NSGraph :: NamespaceMap -> FormulaMap lb -> [Arc lb] -> NSGraph lb
- Swish.RDF.RDFGraph: NoNode :: RDFLabel
- Swish.RDF.RDFGraph: Res :: ScopedName -> RDFLabel
- Swish.RDF.RDFGraph: Var :: String -> RDFLabel
- Swish.RDF.RDFGraph: add :: LDGraph lg lb => lg lb -> lg lb -> lg lb
- Swish.RDF.RDFGraph: addArc :: Label lb => Arc lb -> NSGraph lb -> NSGraph lb
- Swish.RDF.RDFGraph: allLabels :: Label lb => (lb -> Bool) -> NSGraph lb -> [lb]
- Swish.RDF.RDFGraph: allNodes :: Label lb => (lb -> Bool) -> NSGraph lb -> [lb]
- Swish.RDF.RDFGraph: aobj :: Arc lb -> lb
- Swish.RDF.RDFGraph: apred :: Arc lb -> lb
- Swish.RDF.RDFGraph: arc :: lb -> lb -> lb -> Arc lb
- Swish.RDF.RDFGraph: arcObj :: Arc lb -> lb
- Swish.RDF.RDFGraph: arcPred :: Arc lb -> lb
- Swish.RDF.RDFGraph: arcSubj :: Arc lb -> lb
- Swish.RDF.RDFGraph: asubj :: Arc lb -> lb
- Swish.RDF.RDFGraph: class FromRDFLabel a
- Swish.RDF.RDFGraph: class (Eq (lg lb), Eq lb) => LDGraph lg lb where extract sel = update (filter sel) add addg = update (union (getArcs addg)) delete delg = update (\\ getArcs delg) labels g = foldl' union [] (map arcLabels (getArcs g)) nodes g = foldl' union [] (map arcNodes (getArcs g)) update f g = setArcs (f (getArcs g)) g
- Swish.RDF.RDFGraph: class (Eq lb, Show lb, Ord lb) => Label lb
- Swish.RDF.RDFGraph: class ToRDFLabel a
- Swish.RDF.RDFGraph: containedIn :: LDGraph lg lb => lg lb -> lg lb -> Bool
- Swish.RDF.RDFGraph: data Arc lb
- Swish.RDF.RDFGraph: data LookupFormula lb gr
- Swish.RDF.RDFGraph: data NSGraph lb
- Swish.RDF.RDFGraph: data RDFLabel
- Swish.RDF.RDFGraph: data RevNamespace
- Swish.RDF.RDFGraph: delete :: LDGraph lg lb => lg lb -> lg lb -> lg lb
- Swish.RDF.RDFGraph: emptyFormulaMap :: FormulaMap RDFLabel
- Swish.RDF.RDFGraph: emptyNamespaceMap :: NamespaceMap
- Swish.RDF.RDFGraph: emptyRDFGraph :: RDFGraph
- Swish.RDF.RDFGraph: extract :: LDGraph lg lb => Selector lb -> lg lb -> lg lb
- Swish.RDF.RDFGraph: formGraph :: LookupFormula lb gr -> gr
- Swish.RDF.RDFGraph: formLabel :: LookupFormula lb gr -> lb
- Swish.RDF.RDFGraph: formulae :: NSGraph lb -> FormulaMap lb
- Swish.RDF.RDFGraph: fromRDFLabel :: FromRDFLabel a => RDFLabel -> Maybe a
- Swish.RDF.RDFGraph: fromRDFTriple :: (FromRDFLabel s, FromRDFLabel p, FromRDFLabel o) => RDFTriple -> Maybe (s, p, o)
- Swish.RDF.RDFGraph: getArcs :: LDGraph lg lb => lg lb -> [Arc lb]
- Swish.RDF.RDFGraph: getFormula :: Label lb => NSGraph lb -> lb -> Maybe (NSGraph lb)
- Swish.RDF.RDFGraph: getFormulae :: NSGraph lb -> FormulaMap lb
- Swish.RDF.RDFGraph: getLiteralText :: RDFLabel -> Text
- Swish.RDF.RDFGraph: getLocal :: Label lb => lb -> String
- Swish.RDF.RDFGraph: getNamespaces :: NSGraph lb -> NamespaceMap
- Swish.RDF.RDFGraph: getScopedName :: RDFLabel -> ScopedName
- Swish.RDF.RDFGraph: grEq :: Label lb => NSGraph lb -> NSGraph lb -> Bool
- Swish.RDF.RDFGraph: grMatchMap :: Label lb => NSGraph lb -> NSGraph lb -> (Bool, LabelMap (ScopedLabel lb))
- Swish.RDF.RDFGraph: instance (Eq lb, Eq gr) => Eq (LookupFormula lb gr)
- Swish.RDF.RDFGraph: instance Eq RDFLabel
- Swish.RDF.RDFGraph: instance Foldable NSGraph
- Swish.RDF.RDFGraph: instance FromRDFLabel Bool
- Swish.RDF.RDFGraph: instance FromRDFLabel Char
- Swish.RDF.RDFGraph: instance FromRDFLabel Day
- Swish.RDF.RDFGraph: instance FromRDFLabel Double
- Swish.RDF.RDFGraph: instance FromRDFLabel Float
- Swish.RDF.RDFGraph: instance FromRDFLabel Int
- Swish.RDF.RDFGraph: instance FromRDFLabel Integer
- Swish.RDF.RDFGraph: instance FromRDFLabel QName
- Swish.RDF.RDFGraph: instance FromRDFLabel RDFLabel
- Swish.RDF.RDFGraph: instance FromRDFLabel ScopedName
- Swish.RDF.RDFGraph: instance FromRDFLabel String
- Swish.RDF.RDFGraph: instance FromRDFLabel URI
- Swish.RDF.RDFGraph: instance FromRDFLabel UTCTime
- Swish.RDF.RDFGraph: instance Functor NSGraph
- Swish.RDF.RDFGraph: instance IsString RDFLabel
- Swish.RDF.RDFGraph: instance Label RDFLabel
- Swish.RDF.RDFGraph: instance Label lb => Eq (NSGraph lb)
- Swish.RDF.RDFGraph: instance Label lb => LDGraph NSGraph lb
- Swish.RDF.RDFGraph: instance Label lb => LookupEntryClass (LookupFormula lb (NSGraph lb)) lb (NSGraph lb)
- Swish.RDF.RDFGraph: instance Label lb => Monoid (NSGraph lb)
- Swish.RDF.RDFGraph: instance Label lb => Show (LookupFormula lb (NSGraph lb))
- Swish.RDF.RDFGraph: instance Label lb => Show (NSGraph lb)
- Swish.RDF.RDFGraph: instance LookupEntryClass RevNamespace URI (Maybe Text)
- Swish.RDF.RDFGraph: instance Ord RDFLabel
- Swish.RDF.RDFGraph: instance Show RDFLabel
- Swish.RDF.RDFGraph: instance ToRDFLabel Bool
- Swish.RDF.RDFGraph: instance ToRDFLabel Char
- Swish.RDF.RDFGraph: instance ToRDFLabel Day
- Swish.RDF.RDFGraph: instance ToRDFLabel Double
- Swish.RDF.RDFGraph: instance ToRDFLabel Float
- Swish.RDF.RDFGraph: instance ToRDFLabel Int
- Swish.RDF.RDFGraph: instance ToRDFLabel Integer
- Swish.RDF.RDFGraph: instance ToRDFLabel QName
- Swish.RDF.RDFGraph: instance ToRDFLabel RDFLabel
- Swish.RDF.RDFGraph: instance ToRDFLabel ScopedName
- Swish.RDF.RDFGraph: instance ToRDFLabel String
- Swish.RDF.RDFGraph: instance ToRDFLabel URI
- Swish.RDF.RDFGraph: instance ToRDFLabel UTCTime
- Swish.RDF.RDFGraph: instance Traversable NSGraph
- Swish.RDF.RDFGraph: isBlank :: RDFLabel -> Bool
- Swish.RDF.RDFGraph: isDatatyped :: ScopedName -> RDFLabel -> Bool
- Swish.RDF.RDFGraph: isLiteral :: RDFLabel -> Bool
- Swish.RDF.RDFGraph: isMemberProp :: RDFLabel -> Bool
- Swish.RDF.RDFGraph: isQueryVar :: RDFLabel -> Bool
- Swish.RDF.RDFGraph: isTypedLiteral :: RDFLabel -> Bool
- Swish.RDF.RDFGraph: isUntypedLiteral :: RDFLabel -> Bool
- Swish.RDF.RDFGraph: isUri :: RDFLabel -> Bool
- Swish.RDF.RDFGraph: isXMLLiteral :: RDFLabel -> Bool
- Swish.RDF.RDFGraph: labelHash :: Label lb => Int -> lb -> Int
- Swish.RDF.RDFGraph: labelIsVar :: Label lb => lb -> Bool
- Swish.RDF.RDFGraph: labels :: LDGraph lg lb => lg lb -> [lb]
- Swish.RDF.RDFGraph: makeBlank :: RDFLabel -> RDFLabel
- Swish.RDF.RDFGraph: makeLabel :: Label lb => String -> lb
- Swish.RDF.RDFGraph: maplist :: Label lb => [lb] -> [lb] -> (lb -> lb) -> [(lb, lb)] -> [(lb, lb)]
- Swish.RDF.RDFGraph: mapnode :: Label lb => [lb] -> [lb] -> (lb -> lb) -> lb -> lb
- Swish.RDF.RDFGraph: merge :: Label lb => NSGraph lb -> NSGraph lb -> NSGraph lb
- Swish.RDF.RDFGraph: namespaces :: NSGraph lb -> NamespaceMap
- Swish.RDF.RDFGraph: newNode :: Label lb => lb -> [lb] -> lb
- Swish.RDF.RDFGraph: newNodes :: Label lb => lb -> [lb] -> [lb]
- Swish.RDF.RDFGraph: nodes :: LDGraph lg lb => lg lb -> [lb]
- Swish.RDF.RDFGraph: quote :: Bool -> String -> String
- Swish.RDF.RDFGraph: quoteT :: Bool -> Text -> Text
- Swish.RDF.RDFGraph: remapLabelList :: Label lb => [lb] -> [lb] -> [(lb, lb)]
- Swish.RDF.RDFGraph: remapLabels :: Label lb => [lb] -> [lb] -> (lb -> lb) -> NSGraph lb -> NSGraph lb
- Swish.RDF.RDFGraph: resLogImplies :: RDFLabel
- Swish.RDF.RDFGraph: resOwlSameAs :: RDFLabel
- Swish.RDF.RDFGraph: resRdf1 :: RDFLabel
- Swish.RDF.RDFGraph: resRdf2 :: RDFLabel
- Swish.RDF.RDFGraph: resRdfAbout :: RDFLabel
- Swish.RDF.RDFGraph: resRdfAlt :: RDFLabel
- Swish.RDF.RDFGraph: resRdfBag :: RDFLabel
- Swish.RDF.RDFGraph: resRdfDatatype :: RDFLabel
- Swish.RDF.RDFGraph: resRdfDescription :: RDFLabel
- Swish.RDF.RDFGraph: resRdfFirst :: RDFLabel
- Swish.RDF.RDFGraph: resRdfID :: RDFLabel
- Swish.RDF.RDFGraph: resRdfLi :: RDFLabel
- Swish.RDF.RDFGraph: resRdfList :: RDFLabel
- Swish.RDF.RDFGraph: resRdfNil :: RDFLabel
- Swish.RDF.RDFGraph: resRdfNodeID :: RDFLabel
- Swish.RDF.RDFGraph: resRdfObject :: RDFLabel
- Swish.RDF.RDFGraph: resRdfParseType :: RDFLabel
- Swish.RDF.RDFGraph: resRdfPredicate :: RDFLabel
- Swish.RDF.RDFGraph: resRdfProperty :: RDFLabel
- Swish.RDF.RDFGraph: resRdfRDF :: RDFLabel
- Swish.RDF.RDFGraph: resRdfResource :: RDFLabel
- Swish.RDF.RDFGraph: resRdfRest :: RDFLabel
- Swish.RDF.RDFGraph: resRdfSeq :: RDFLabel
- Swish.RDF.RDFGraph: resRdfStatement :: RDFLabel
- Swish.RDF.RDFGraph: resRdfSubject :: RDFLabel
- Swish.RDF.RDFGraph: resRdfType :: RDFLabel
- Swish.RDF.RDFGraph: resRdfValue :: RDFLabel
- Swish.RDF.RDFGraph: resRdfXMLLiteral :: RDFLabel
- Swish.RDF.RDFGraph: resRdfdConstraint :: RDFLabel
- Swish.RDF.RDFGraph: resRdfdGeneralRestriction :: RDFLabel
- Swish.RDF.RDFGraph: resRdfdMaxCardinality :: RDFLabel
- Swish.RDF.RDFGraph: resRdfdOnProperties :: RDFLabel
- Swish.RDF.RDFGraph: resRdfn :: Int -> RDFLabel
- Swish.RDF.RDFGraph: resRdfsClass :: RDFLabel
- Swish.RDF.RDFGraph: resRdfsComment :: RDFLabel
- Swish.RDF.RDFGraph: resRdfsContainer :: RDFLabel
- Swish.RDF.RDFGraph: resRdfsContainerMembershipProperty :: RDFLabel
- Swish.RDF.RDFGraph: resRdfsDatatype :: RDFLabel
- Swish.RDF.RDFGraph: resRdfsDomain :: RDFLabel
- Swish.RDF.RDFGraph: resRdfsIsDefinedBy :: RDFLabel
- Swish.RDF.RDFGraph: resRdfsLabel :: RDFLabel
- Swish.RDF.RDFGraph: resRdfsLiteral :: RDFLabel
- Swish.RDF.RDFGraph: resRdfsMember :: RDFLabel
- Swish.RDF.RDFGraph: resRdfsRange :: RDFLabel
- Swish.RDF.RDFGraph: resRdfsResource :: RDFLabel
- Swish.RDF.RDFGraph: resRdfsSeeAlso :: RDFLabel
- Swish.RDF.RDFGraph: resRdfsSubClassOf :: RDFLabel
- Swish.RDF.RDFGraph: resRdfsSubPropertyOf :: RDFLabel
- Swish.RDF.RDFGraph: setArcs :: LDGraph lg lb => [Arc lb] -> lg lb -> lg lb
- Swish.RDF.RDFGraph: setFormula :: Label lb => Formula lb -> NSGraph lb -> NSGraph lb
- Swish.RDF.RDFGraph: setFormulae :: FormulaMap lb -> NSGraph lb -> NSGraph lb
- Swish.RDF.RDFGraph: setNamespaces :: NamespaceMap -> NSGraph lb -> NSGraph lb
- Swish.RDF.RDFGraph: statements :: NSGraph lb -> [Arc lb]
- Swish.RDF.RDFGraph: toRDFGraph :: [RDFTriple] -> RDFGraph
- Swish.RDF.RDFGraph: toRDFLabel :: ToRDFLabel a => a -> RDFLabel
- Swish.RDF.RDFGraph: toRDFTriple :: (ToRDFLabel s, ToRDFLabel p, ToRDFLabel o) => s -> p -> o -> RDFTriple
- Swish.RDF.RDFGraph: type Formula lb = LookupFormula lb (NSGraph lb)
- Swish.RDF.RDFGraph: type FormulaMap lb = LookupMap (LookupFormula lb (NSGraph lb))
- Swish.RDF.RDFGraph: type NamespaceMap = LookupMap Namespace
- Swish.RDF.RDFGraph: type RDFGraph = NSGraph RDFLabel
- Swish.RDF.RDFGraph: type RDFTriple = Arc RDFLabel
- Swish.RDF.RDFGraph: type RevNamespaceMap = LookupMap RevNamespace
- Swish.RDF.RDFGraph: type Selector lb = Arc lb -> Bool
- Swish.RDF.RDFGraph: update :: LDGraph lg lb => ([Arc lb] -> [Arc lb]) -> lg lb -> lg lb
- Swish.RDF.RDFGraphShowM: instance ShowM RDFGraph
- Swish.RDF.RDFParser: appendURIs :: URI -> URI -> Either String URI
- Swish.RDF.RDFParser: char :: Char -> Parser s Char
- Swish.RDF.RDFParser: endBy :: Parser s a -> Parser s b -> Parser s [a]
- Swish.RDF.RDFParser: eoln :: Parser s ()
- Swish.RDF.RDFParser: fullStop :: Parser s ()
- Swish.RDF.RDFParser: hex4 :: Parser a Char
- Swish.RDF.RDFParser: hex8 :: Parser a Char
- Swish.RDF.RDFParser: ichar :: Char -> Parser s ()
- Swish.RDF.RDFParser: ignore :: Applicative f => f a -> f ()
- Swish.RDF.RDFParser: isymbol :: String -> Parser s ()
- Swish.RDF.RDFParser: lexeme :: Parser s a -> Parser s a
- Swish.RDF.RDFParser: manyTill :: Parser s a -> Parser s b -> Parser s [a]
- Swish.RDF.RDFParser: mkTypedLit :: ScopedName -> Text -> RDFLabel
- Swish.RDF.RDFParser: noneOf :: String -> Parser s Char
- Swish.RDF.RDFParser: notFollowedBy :: (Char -> Bool) -> Parser s ()
- Swish.RDF.RDFParser: prefixTable :: [Namespace]
- Swish.RDF.RDFParser: runParserWithError :: Parser a b -> a -> Text -> Either String b
- Swish.RDF.RDFParser: sepEndBy :: Parser s a -> Parser s b -> Parser s [a]
- Swish.RDF.RDFParser: sepEndBy1 :: Parser s a -> Parser s b -> Parser s [a]
- Swish.RDF.RDFParser: skipMany :: Parser s a -> Parser s ()
- Swish.RDF.RDFParser: skipMany1 :: Parser s a -> Parser s ()
- Swish.RDF.RDFParser: specialTable :: Maybe ScopedName -> [(String, ScopedName)]
- Swish.RDF.RDFParser: string :: String -> Parser s String
- Swish.RDF.RDFParser: stringT :: Text -> Parser s Text
- Swish.RDF.RDFParser: symbol :: String -> Parser s String
- Swish.RDF.RDFParser: type ParseResult = Either String RDFGraph
- Swish.RDF.RDFParser: type SpecialMap = LookupMap (String, ScopedName)
- Swish.RDF.RDFParser: whiteSpace :: Parser s ()
- Swish.RDF.RDFProof: instance (Label lb, LDGraph lg lb) => Expression (lg lb)
- Swish.RDF.RDFProof: makeRDFProof :: [RDFRuleset] -> RDFFormula -> RDFFormula -> [RDFProofStep] -> RDFProof
- Swish.RDF.RDFProof: makeRDFProofStep :: RDFRule -> [RDFFormula] -> RDFFormula -> RDFProofStep
- Swish.RDF.RDFProof: makeRdfInstanceEntailmentRule :: ScopedName -> [RDFLabel] -> RDFRule
- Swish.RDF.RDFProof: makeRdfSimpleEntailmentRule :: ScopedName -> RDFRule
- Swish.RDF.RDFProof: makeRdfSubgraphEntailmentRule :: ScopedName -> RDFRule
- Swish.RDF.RDFProof: type RDFProof = Proof RDFGraph
- Swish.RDF.RDFProof: type RDFProofStep = Step RDFGraph
- Swish.RDF.RDFProofContext: rulesetRDF :: RDFRuleset
- Swish.RDF.RDFProofContext: rulesetRDFD :: RDFRuleset
- Swish.RDF.RDFProofContext: rulesetRDFS :: RDFRuleset
- Swish.RDF.RDFQuery: rdfFindArcs :: (RDFTriple -> Bool) -> RDFGraph -> [RDFTriple]
- Swish.RDF.RDFQuery: rdfFindList :: RDFGraph -> RDFLabel -> [RDFLabel]
- Swish.RDF.RDFQuery: rdfFindPredInt :: RDFLabel -> RDFLabel -> RDFGraph -> [Integer]
- Swish.RDF.RDFQuery: rdfFindPredVal :: RDFLabel -> RDFLabel -> RDFGraph -> [RDFLabel]
- Swish.RDF.RDFQuery: rdfFindValSubj :: RDFLabel -> RDFLabel -> RDFGraph -> [RDFLabel]
- Swish.RDF.RDFQuery: rdfObjEq :: RDFLabel -> RDFTriple -> Bool
- Swish.RDF.RDFQuery: rdfPredEq :: RDFLabel -> RDFTriple -> Bool
- Swish.RDF.RDFQuery: rdfQueryBack :: RDFGraph -> RDFGraph -> [[RDFVarBinding]]
- Swish.RDF.RDFQuery: rdfQueryBackFilter :: RDFVarBindingFilter -> [[RDFVarBinding]] -> [[RDFVarBinding]]
- Swish.RDF.RDFQuery: rdfQueryBackModify :: VarBindingModify a b -> [[VarBinding a b]] -> [[VarBinding a b]]
- Swish.RDF.RDFQuery: rdfQueryBackSubs :: [[RDFVarBinding]] -> RDFGraph -> [[(RDFGraph, [RDFLabel])]]
- Swish.RDF.RDFQuery: rdfQueryBackSubsBlank :: [[RDFVarBinding]] -> RDFGraph -> [[RDFGraph]]
- Swish.RDF.RDFQuery: rdfQueryFilter :: RDFVarBindingFilter -> [RDFVarBinding] -> [RDFVarBinding]
- Swish.RDF.RDFQuery: rdfQueryFind :: RDFGraph -> RDFGraph -> [RDFVarBinding]
- Swish.RDF.RDFQuery: rdfQueryInstance :: RDFGraph -> RDFGraph -> [RDFVarBinding]
- Swish.RDF.RDFQuery: rdfQuerySubs :: [RDFVarBinding] -> RDFGraph -> [RDFGraph]
- Swish.RDF.RDFQuery: rdfQuerySubs2 :: RDFVarBinding -> RDFGraph -> (RDFGraph, [RDFLabel])
- Swish.RDF.RDFQuery: rdfQuerySubsAll :: [RDFVarBinding] -> RDFGraph -> [(RDFGraph, [RDFLabel])]
- Swish.RDF.RDFQuery: rdfQuerySubsBlank :: [RDFVarBinding] -> RDFGraph -> [RDFGraph]
- Swish.RDF.RDFQuery: rdfSubjEq :: RDFLabel -> RDFTriple -> Bool
- Swish.RDF.RDFRuleset: GraphClosure :: ScopedName -> [Arc lb] -> [Arc lb] -> VarBindingModify lb lb -> GraphClosure lb
- Swish.RDF.RDFRuleset: data GraphClosure lb
- Swish.RDF.RDFRuleset: graphClosureBwdApply :: GraphClosure RDFLabel -> RDFGraph -> [[RDFGraph]]
- Swish.RDF.RDFRuleset: graphClosureFwdApply :: GraphClosure RDFLabel -> [RDFGraph] -> [RDFGraph]
- Swish.RDF.RDFRuleset: instance Label lb => Eq (GraphClosure lb)
- Swish.RDF.RDFRuleset: instance Label lb => Show (GraphClosure lb)
- Swish.RDF.RDFRuleset: makeGraphClosureRule :: GraphClosure RDFLabel -> Rule RDFGraph
- Swish.RDF.RDFRuleset: makeN3ClosureAllocatorRule :: Namespace -> Text -> Builder -> Builder -> RDFVarBindingModify -> ([RDFLabel] -> RDFVarBindingModify) -> RDFRule
- Swish.RDF.RDFRuleset: makeN3ClosureModifyRule :: Namespace -> Text -> Builder -> Builder -> RDFVarBindingModify -> RDFVarBindingModify -> RDFRule
- Swish.RDF.RDFRuleset: makeN3ClosureRule :: Namespace -> Text -> Builder -> Builder -> RDFVarBindingModify -> RDFRule
- Swish.RDF.RDFRuleset: makeN3ClosureSimpleRule :: Namespace -> Text -> Builder -> Builder -> RDFRule
- Swish.RDF.RDFRuleset: makeNodeAllocTo :: RDFLabel -> RDFLabel -> [RDFLabel] -> RDFVarBindingModify
- Swish.RDF.RDFRuleset: makeRDFClosureRule :: ScopedName -> [RDFGraph] -> RDFGraph -> RDFVarBindingModify -> RDFRule
- Swish.RDF.RDFRuleset: makeRDFFormula :: Namespace -> Text -> Builder -> RDFFormula
- Swish.RDF.RDFRuleset: makeRDFGraphFromN3Builder :: Builder -> RDFGraph
- Swish.RDF.RDFRuleset: nameGraphRule :: GraphClosure lb -> ScopedName
- Swish.RDF.RDFRuleset: nullRDFFormula :: Formula RDFGraph
- Swish.RDF.RDFRuleset: ruleAnt :: GraphClosure lb -> [Arc lb]
- Swish.RDF.RDFRuleset: ruleCon :: GraphClosure lb -> [Arc lb]
- Swish.RDF.RDFRuleset: ruleModify :: GraphClosure lb -> VarBindingModify lb lb
- Swish.RDF.RDFRuleset: type RDFClosure = GraphClosure RDFLabel
- Swish.RDF.RDFRuleset: type RDFFormula = Formula RDFGraph
- Swish.RDF.RDFRuleset: type RDFRule = Rule RDFGraph
- Swish.RDF.RDFRuleset: type RDFRuleMap = RuleMap RDFGraph
- Swish.RDF.RDFRuleset: type RDFRuleset = Ruleset RDFGraph
- Swish.RDF.RDFRuleset: type RDFRulesetMap = RulesetMap RDFGraph
- Swish.RDF.RDFVarBinding: nullRDFVarBinding :: RDFVarBinding
- Swish.RDF.RDFVarBinding: rdfVarBindingBlank :: RDFLabel -> RDFVarBindingFilter
- Swish.RDF.RDFVarBinding: rdfVarBindingDatatyped :: RDFLabel -> RDFLabel -> RDFVarBindingFilter
- Swish.RDF.RDFVarBinding: rdfVarBindingLiteral :: RDFLabel -> RDFVarBindingFilter
- Swish.RDF.RDFVarBinding: rdfVarBindingMemberProp :: RDFLabel -> RDFVarBindingFilter
- Swish.RDF.RDFVarBinding: rdfVarBindingTypedLiteral :: RDFLabel -> RDFVarBindingFilter
- Swish.RDF.RDFVarBinding: rdfVarBindingUntypedLiteral :: RDFLabel -> RDFVarBindingFilter
- Swish.RDF.RDFVarBinding: rdfVarBindingUriRef :: RDFLabel -> RDFVarBindingFilter
- Swish.RDF.RDFVarBinding: rdfVarBindingXMLLiteral :: RDFLabel -> RDFVarBindingFilter
- Swish.RDF.RDFVarBinding: type RDFOpenVarBindingModify = OpenVarBindingModify RDFLabel RDFLabel
- Swish.RDF.RDFVarBinding: type RDFOpenVarBindingModifyMap = LookupMap RDFOpenVarBindingModify
- Swish.RDF.RDFVarBinding: type RDFVarBinding = VarBinding RDFLabel RDFLabel
- Swish.RDF.RDFVarBinding: type RDFVarBindingFilter = VarBindingFilter RDFLabel RDFLabel
- Swish.RDF.RDFVarBinding: type RDFVarBindingModify = VarBindingModify RDFLabel RDFLabel
- Swish.RDF.Rule: Formula :: ScopedName -> ex -> Formula ex
- Swish.RDF.Rule: Rule :: ScopedName -> ([ex] -> [ex]) -> (ex -> [[ex]]) -> ([ex] -> ex -> Bool) -> Rule ex
- Swish.RDF.Rule: bwdApply :: Rule ex -> ex -> [[ex]]
- Swish.RDF.Rule: bwdCheckInference :: Eq ex => Rule ex -> [ex] -> ex -> Bool
- Swish.RDF.Rule: checkInference :: Rule ex -> [ex] -> ex -> Bool
- Swish.RDF.Rule: class Eq ex => Expression ex
- Swish.RDF.Rule: data Formula ex
- Swish.RDF.Rule: data Rule ex
- Swish.RDF.Rule: formExpr :: Formula ex -> ex
- Swish.RDF.Rule: formName :: Formula ex -> ScopedName
- Swish.RDF.Rule: fwdApply :: Rule ex -> [ex] -> [ex]
- Swish.RDF.Rule: fwdCheckInference :: Eq ex => Rule ex -> [ex] -> ex -> Bool
- Swish.RDF.Rule: instance Eq (Formula ex)
- Swish.RDF.Rule: instance Eq (Rule ex)
- Swish.RDF.Rule: instance LookupEntryClass (Formula ex) ScopedName (Formula ex)
- Swish.RDF.Rule: instance LookupEntryClass (Rule ex) ScopedName (Rule ex)
- Swish.RDF.Rule: instance Ord (Formula ex)
- Swish.RDF.Rule: instance Ord (Rule ex)
- Swish.RDF.Rule: instance Show (Rule ex)
- Swish.RDF.Rule: instance Show ex => Show (Formula ex)
- Swish.RDF.Rule: isValid :: Expression ex => ex -> Bool
- Swish.RDF.Rule: nullFormula :: Formula ex
- Swish.RDF.Rule: nullRule :: Rule ex
- Swish.RDF.Rule: nullSN :: Text -> ScopedName
- Swish.RDF.Rule: nullScope :: Namespace
- Swish.RDF.Rule: ruleName :: Rule ex -> ScopedName
- Swish.RDF.Rule: showsFormula :: ShowM ex => String -> Formula ex -> ShowS
- Swish.RDF.Rule: showsFormulae :: ShowM ex => String -> [Formula ex] -> String -> ShowS
- Swish.RDF.Rule: showsWidth :: Int -> String -> ShowS
- Swish.RDF.Rule: type RuleMap ex = LookupMap (Rule ex)
- Swish.RDF.Ruleset: Ruleset :: Namespace -> [Formula ex] -> [Rule ex] -> Ruleset ex
- Swish.RDF.Ruleset: data Ruleset ex
- Swish.RDF.Ruleset: getContextAxiom :: ScopedName -> Formula ex -> [Ruleset ex] -> Formula ex
- Swish.RDF.Ruleset: getContextRule :: ScopedName -> Rule ex -> [Ruleset ex] -> Rule ex
- Swish.RDF.Ruleset: getMaybeContextAxiom :: ScopedName -> [Ruleset ex] -> Maybe (Formula ex)
- Swish.RDF.Ruleset: getMaybeContextRule :: ScopedName -> [Ruleset ex] -> Maybe (Rule ex)
- Swish.RDF.Ruleset: getRulesetAxiom :: ScopedName -> Ruleset ex -> Maybe (Formula ex)
- Swish.RDF.Ruleset: getRulesetAxioms :: Ruleset ex -> [Formula ex]
- Swish.RDF.Ruleset: getRulesetNamespace :: Ruleset ex -> Namespace
- Swish.RDF.Ruleset: getRulesetRule :: ScopedName -> Ruleset ex -> Maybe (Rule ex)
- Swish.RDF.Ruleset: getRulesetRules :: Ruleset ex -> [Rule ex]
- Swish.RDF.Ruleset: instance Eq (Ruleset ex)
- Swish.RDF.Ruleset: instance LookupEntryClass (Ruleset ex) Namespace (Ruleset ex)
- Swish.RDF.Ruleset: makeRuleset :: Namespace -> [Formula ex] -> [Rule ex] -> Ruleset ex
- Swish.RDF.Ruleset: rsAxioms :: Ruleset ex -> [Formula ex]
- Swish.RDF.Ruleset: rsNamespace :: Ruleset ex -> Namespace
- Swish.RDF.Ruleset: rsRules :: Ruleset ex -> [Rule ex]
- Swish.RDF.Ruleset: type RulesetMap ex = LookupMap (Ruleset ex)
- Swish.RDF.SwishCommands: swishBase :: Maybe QName -> Maybe String -> SwishStateIO ()
- Swish.RDF.SwishCommands: swishCompare :: Maybe String -> SwishStateIO ()
- Swish.RDF.SwishCommands: swishFormat :: SwishFormat -> Maybe String -> SwishStateIO ()
- Swish.RDF.SwishCommands: swishGraphDiff :: Maybe String -> SwishStateIO ()
- Swish.RDF.SwishCommands: swishInput :: Maybe String -> SwishStateIO ()
- Swish.RDF.SwishCommands: swishMerge :: Maybe String -> SwishStateIO ()
- Swish.RDF.SwishCommands: swishOutput :: Maybe String -> SwishStateIO ()
- Swish.RDF.SwishCommands: swishScript :: Maybe String -> SwishStateIO ()
- Swish.RDF.SwishMain: SwishArgumentError :: SwishStatus
- Swish.RDF.SwishMain: SwishDataAccessError :: SwishStatus
- Swish.RDF.SwishMain: SwishDataInputError :: SwishStatus
- Swish.RDF.SwishMain: SwishExecutionError :: SwishStatus
- Swish.RDF.SwishMain: SwishGraphCompareError :: SwishStatus
- Swish.RDF.SwishMain: SwishSuccess :: SwishStatus
- Swish.RDF.SwishMain: data SwishAction
- Swish.RDF.SwishMain: data SwishStatus
- Swish.RDF.SwishMain: displaySwishHelp :: IO ()
- Swish.RDF.SwishMain: instance Show SwishAction
- Swish.RDF.SwishMain: runSwish :: String -> IO ExitCode
- Swish.RDF.SwishMain: runSwishActions :: [SwishAction] -> IO SwishStatus
- Swish.RDF.SwishMain: splitArguments :: [String] -> ([String], [String])
- Swish.RDF.SwishMain: validateCommands :: [String] -> Either (String, SwishStatus) [SwishAction]
- Swish.RDF.SwishMonad: N3 :: SwishFormat
- Swish.RDF.SwishMonad: NT :: SwishFormat
- Swish.RDF.SwishMonad: NamedGraph :: ScopedName -> [RDFGraph] -> NamedGraph
- Swish.RDF.SwishMonad: SwishArgumentError :: SwishStatus
- Swish.RDF.SwishMonad: SwishDataAccessError :: SwishStatus
- Swish.RDF.SwishMonad: SwishDataInputError :: SwishStatus
- Swish.RDF.SwishMonad: SwishExecutionError :: SwishStatus
- Swish.RDF.SwishMonad: SwishGraphCompareError :: SwishStatus
- Swish.RDF.SwishMonad: SwishState :: SwishFormat -> Maybe QName -> RDFGraph -> NamedGraphMap -> RDFRuleMap -> RDFRulesetMap -> Maybe String -> Maybe String -> SwishStatus -> SwishState
- Swish.RDF.SwishMonad: SwishSuccess :: SwishStatus
- Swish.RDF.SwishMonad: Turtle :: SwishFormat
- Swish.RDF.SwishMonad: base :: SwishState -> Maybe QName
- Swish.RDF.SwishMonad: data NamedGraph
- Swish.RDF.SwishMonad: data SwishFormat
- Swish.RDF.SwishMonad: data SwishState
- Swish.RDF.SwishMonad: data SwishStatus
- Swish.RDF.SwishMonad: emptyState :: SwishState
- Swish.RDF.SwishMonad: errormsg :: SwishState -> Maybe String
- Swish.RDF.SwishMonad: exitcode :: SwishState -> SwishStatus
- Swish.RDF.SwishMonad: findDatatype :: ScopedName -> SwishState -> Maybe RDFDatatype
- Swish.RDF.SwishMonad: findFormula :: ScopedName -> SwishState -> Maybe RDFFormula
- Swish.RDF.SwishMonad: findGraph :: ScopedName -> SwishState -> Maybe [RDFGraph]
- Swish.RDF.SwishMonad: findOpenVarModify :: ScopedName -> SwishState -> Maybe RDFOpenVarBindingModify
- Swish.RDF.SwishMonad: findRule :: ScopedName -> SwishState -> Maybe RDFRule
- Swish.RDF.SwishMonad: findRuleset :: ScopedName -> SwishState -> Maybe RDFRuleset
- Swish.RDF.SwishMonad: format :: SwishState -> SwishFormat
- Swish.RDF.SwishMonad: graph :: SwishState -> RDFGraph
- Swish.RDF.SwishMonad: graphs :: SwishState -> NamedGraphMap
- Swish.RDF.SwishMonad: infomsg :: SwishState -> Maybe String
- Swish.RDF.SwishMonad: instance Enum SwishStatus
- Swish.RDF.SwishMonad: instance Eq SwishFormat
- Swish.RDF.SwishMonad: instance Eq SwishStatus
- Swish.RDF.SwishMonad: instance LookupEntryClass NamedGraph ScopedName [RDFGraph]
- Swish.RDF.SwishMonad: instance Show SwishFormat
- Swish.RDF.SwishMonad: instance Show SwishStatus
- Swish.RDF.SwishMonad: modGraphs :: (NamedGraphMap -> NamedGraphMap) -> SwishState -> SwishState
- Swish.RDF.SwishMonad: modRules :: (RDFRuleMap -> RDFRuleMap) -> SwishState -> SwishState
- Swish.RDF.SwishMonad: modRulesets :: (RDFRulesetMap -> RDFRulesetMap) -> SwishState -> SwishState
- Swish.RDF.SwishMonad: ngGraph :: NamedGraph -> [RDFGraph]
- Swish.RDF.SwishMonad: ngName :: NamedGraph -> ScopedName
- Swish.RDF.SwishMonad: reportLine :: String -> SwishStateIO ()
- Swish.RDF.SwishMonad: reportLines :: [String] -> SwishStateIO ()
- Swish.RDF.SwishMonad: resetError :: SwishState -> SwishState
- Swish.RDF.SwishMonad: resetInfo :: SwishState -> SwishState
- Swish.RDF.SwishMonad: rules :: SwishState -> RDFRuleMap
- Swish.RDF.SwishMonad: rulesets :: SwishState -> RDFRulesetMap
- Swish.RDF.SwishMonad: setBase :: Maybe QName -> SwishState -> SwishState
- Swish.RDF.SwishMonad: setError :: String -> SwishState -> SwishState
- Swish.RDF.SwishMonad: setFormat :: SwishFormat -> SwishState -> SwishState
- Swish.RDF.SwishMonad: setGraph :: RDFGraph -> SwishState -> SwishState
- Swish.RDF.SwishMonad: setInfo :: String -> SwishState -> SwishState
- Swish.RDF.SwishMonad: setStatus :: SwishStatus -> SwishState -> SwishState
- Swish.RDF.SwishMonad: swishError :: String -> SwishStatus -> SwishStateIO ()
- Swish.RDF.SwishMonad: type NamedGraphMap = LookupMap NamedGraph
- Swish.RDF.SwishMonad: type SwishStateIO a = StateT SwishState IO a
- Swish.RDF.SwishScript: parseScriptFromText :: Maybe QName -> Text -> Either String [SwishStateIO ()]
- Swish.RDF.TurtleFormatter: formatGraphAsBuilder :: RDFGraph -> Builder
- Swish.RDF.TurtleFormatter: formatGraphAsLazyText :: RDFGraph -> Text
- Swish.RDF.TurtleFormatter: formatGraphAsText :: RDFGraph -> Text
- Swish.RDF.TurtleFormatter: formatGraphDiag :: Builder -> Bool -> RDFGraph -> (Builder, NodeGenLookupMap, Int, [String])
- Swish.RDF.TurtleFormatter: formatGraphIndent :: Builder -> Bool -> RDFGraph -> Builder
- Swish.RDF.TurtleFormatter: instance Eq LabelContext
- Swish.RDF.TurtleFormatter: instance Show LabelContext
- Swish.RDF.TurtleFormatter: quoteText :: Text -> Builder
- Swish.RDF.TurtleFormatter: type NodeGenLookupMap = LookupMap (RDFLabel, Int)
- Swish.RDF.TurtleParser: instance Show TurtleState
- Swish.RDF.TurtleParser: parseTurtle :: Text -> Maybe URI -> ParseResult
- Swish.RDF.TurtleParser: parseTurtlefromText :: Text -> ParseResult
- Swish.RDF.TurtleParser: type ParseResult = Either String RDFGraph
- Swish.RDF.VarBinding: VarBinding :: (a -> Maybe b) -> [(a, b)] -> Bool -> VarBinding a b
- Swish.RDF.VarBinding: VarBindingFilter :: ScopedName -> [a] -> (VarBinding a b -> Bool) -> VarBindingFilter a b
- Swish.RDF.VarBinding: VarBindingModify :: ScopedName -> ([VarBinding a b] -> [VarBinding a b]) -> [a] -> [[a]] -> VarBindingModify a b
- Swish.RDF.VarBinding: addVarBinding :: (Eq a, Show a, Eq b, Show b) => a -> b -> VarBinding a b -> VarBinding a b
- Swish.RDF.VarBinding: applyVarBinding :: VarBinding a a -> a -> a
- Swish.RDF.VarBinding: boundVars :: VarBinding a b -> [a]
- Swish.RDF.VarBinding: composeSequence :: Eq a => [VarBindingModify a b] -> Maybe (VarBindingModify a b)
- Swish.RDF.VarBinding: data VarBinding a b
- Swish.RDF.VarBinding: data VarBindingFilter a b
- Swish.RDF.VarBinding: data VarBindingModify a b
- Swish.RDF.VarBinding: findComposition :: Eq a => [VarBindingModify a b] -> [a] -> Maybe (VarBindingModify a b)
- Swish.RDF.VarBinding: findCompositions :: Eq a => [VarBindingModify a b] -> [a] -> [VarBindingModify a b]
- Swish.RDF.VarBinding: instance (Eq a, Eq b) => Eq (VarBinding a b)
- Swish.RDF.VarBinding: instance (Show a, Show b) => Show (VarBinding a b)
- Swish.RDF.VarBinding: instance LookupEntryClass (OpenVarBindingModify a b) ScopedName (OpenVarBindingModify a b)
- Swish.RDF.VarBinding: instance LookupEntryClass (VarBindingModify a b) ScopedName (VarBindingModify a b)
- Swish.RDF.VarBinding: instance Show (OpenVarBindingModify a b)
- Swish.RDF.VarBinding: joinVarBindings :: Eq a => VarBinding a b -> VarBinding a b -> VarBinding a b
- Swish.RDF.VarBinding: makeVarBinding :: (Eq a, Show a, Eq b, Show b) => [(a, b)] -> VarBinding a b
- Swish.RDF.VarBinding: makeVarCompareFilter :: ScopedName -> (b -> b -> Bool) -> a -> a -> VarBindingFilter a b
- Swish.RDF.VarBinding: makeVarFilterModify :: VarBindingFilter a b -> VarBindingModify a b
- Swish.RDF.VarBinding: makeVarTestFilter :: ScopedName -> (b -> Bool) -> a -> VarBindingFilter a b
- Swish.RDF.VarBinding: nullVarBinding :: VarBinding a b
- Swish.RDF.VarBinding: nullVarBindingModify :: OpenVarBindingModify a b
- Swish.RDF.VarBinding: subBinding :: (Eq a, Eq b) => VarBinding a b -> VarBinding a b -> Bool
- Swish.RDF.VarBinding: type OpenVarBindingModify lb vn = [lb] -> VarBindingModify lb vn
- Swish.RDF.VarBinding: varBindingId :: VarBindingModify a b
- Swish.RDF.VarBinding: varFilterConjunction :: Eq a => [VarBindingFilter a b] -> VarBindingFilter a b
- Swish.RDF.VarBinding: varFilterDisjunction :: Eq a => [VarBindingFilter a b] -> VarBindingFilter a b
- Swish.RDF.VarBinding: varFilterEQ :: Eq b => a -> a -> VarBindingFilter a b
- Swish.RDF.VarBinding: varFilterNE :: Eq b => a -> a -> VarBindingFilter a b
- Swish.RDF.VarBinding: vbEnum :: VarBinding a b -> [(a, b)]
- Swish.RDF.VarBinding: vbMap :: VarBinding a b -> a -> Maybe b
- Swish.RDF.VarBinding: vbNull :: VarBinding a b -> Bool
- Swish.RDF.VarBinding: vbfName :: VarBindingFilter a b -> ScopedName
- Swish.RDF.VarBinding: vbfTest :: VarBindingFilter a b -> VarBinding a b -> Bool
- Swish.RDF.VarBinding: vbfVocab :: VarBindingFilter a b -> [a]
- Swish.RDF.VarBinding: vbmApply :: VarBindingModify a b -> [VarBinding a b] -> [VarBinding a b]
- Swish.RDF.VarBinding: vbmCompatibility :: Eq a => VarBindingModify a b -> [a] -> Maybe [a]
- Swish.RDF.VarBinding: vbmCompose :: Eq a => VarBindingModify a b -> VarBindingModify a b -> Maybe (VarBindingModify a b)
- Swish.RDF.VarBinding: vbmName :: VarBindingModify a b -> ScopedName
- Swish.RDF.VarBinding: vbmUsage :: VarBindingModify a b -> [[a]]
- Swish.RDF.VarBinding: vbmVocab :: VarBindingModify a b -> [a]
- Swish.RDF.Vocabulary: isLang :: ScopedName -> Bool
- Swish.RDF.Vocabulary: langName :: Text -> ScopedName
- Swish.RDF.Vocabulary: langTag :: ScopedName -> Text
- Swish.RDF.Vocabulary: namespaceLang :: Namespace
- Swish.Utils.ListHelpers: addSetElem :: Eq a => a -> [a] -> [a]
- Swish.Utils.ListHelpers: allp :: [a -> Bool] -> a -> Bool
- Swish.Utils.ListHelpers: anyp :: [a -> Bool] -> a -> Bool
- Swish.Utils.ListHelpers: breakAll :: (a -> Bool) -> [a] -> [[a]]
- Swish.Utils.ListHelpers: deleteIndex :: [a] -> Int -> [a]
- Swish.Utils.ListHelpers: headOrNothing :: [Maybe a] -> Maybe a
- Swish.Utils.ListHelpers: listProduct :: [[a]] -> [[a]]
- Swish.Utils.ListHelpers: pairGroup :: Ord a => [(a, b)] -> [(a, [b])]
- Swish.Utils.ListHelpers: pairSort :: Ord a => [(a, b)] -> [(a, b)]
- Swish.Utils.ListHelpers: pairUngroup :: (a, [b]) -> [(a, b)]
- Swish.Utils.ListHelpers: permutations :: [a] -> [[a]]
- Swish.Utils.ListHelpers: powerSequencesLen :: Int -> [a] -> [[a]]
- Swish.Utils.ListHelpers: powerSet :: [a] -> [[a]]
- Swish.Utils.ListHelpers: select :: (a -> Bool) -> [a] -> [b] -> [b]
- Swish.Utils.LookupMap: LookupMap :: [a] -> LookupMap a
- Swish.Utils.LookupMap: class (Eq k, Show k) => LookupEntryClass a k v | a -> k, a -> v where entryKey = fst . keyVal entryVal = snd . keyVal entryEq e1 e2 = keyVal e1 == keyVal e2 entryShow e = show k ++ ":" ++ show v where (k, v) = keyVal e kmap f = newEntry . first f . keyVal vmap f = newEntry . second f . keyVal
- Swish.Utils.LookupMap: data LookupMap a
- Swish.Utils.LookupMap: emptyLookupMap :: LookupEntryClass a k v => LookupMap a
- Swish.Utils.LookupMap: entryEq :: (LookupEntryClass a k v, Eq v) => a -> a -> Bool
- Swish.Utils.LookupMap: entryKey :: LookupEntryClass a k v => a -> k
- Swish.Utils.LookupMap: entryShow :: (LookupEntryClass a k v, Show v) => a -> String
- Swish.Utils.LookupMap: entryVal :: LookupEntryClass a k v => a -> v
- Swish.Utils.LookupMap: instance (Eq k, Show k) => LookupEntryClass (k, v) k v
- Swish.Utils.LookupMap: instance Eq a => Eq (LookupMap a)
- Swish.Utils.LookupMap: instance Foldable LookupMap
- Swish.Utils.LookupMap: instance Functor LookupMap
- Swish.Utils.LookupMap: instance Show a => Show (LookupMap a)
- Swish.Utils.LookupMap: instance Traversable LookupMap
- Swish.Utils.LookupMap: keyOrder :: (LookupEntryClass a k v, Ord k) => a -> a -> Ordering
- Swish.Utils.LookupMap: keyVal :: LookupEntryClass a k v => a -> (k, v)
- Swish.Utils.LookupMap: kmap :: (LookupEntryClass a k v, LookupEntryClass a2 k2 v) => (k -> k2) -> a -> a2
- Swish.Utils.LookupMap: listLookupMap :: LookupEntryClass a k v => LookupMap a -> [a]
- Swish.Utils.LookupMap: makeLookupMap :: LookupEntryClass a k v => [a] -> LookupMap a
- Swish.Utils.LookupMap: mapAdd :: LookupMap a -> a -> LookupMap a
- Swish.Utils.LookupMap: mapAddIfNew :: LookupEntryClass a k v => LookupMap a -> a -> LookupMap a
- Swish.Utils.LookupMap: mapApplyToAll :: LookupEntryClass a k v => LookupMap a -> (k -> w) -> [w]
- Swish.Utils.LookupMap: mapContains :: LookupEntryClass a k v => LookupMap a -> k -> Bool
- Swish.Utils.LookupMap: mapDelete :: LookupEntryClass a k v => LookupMap a -> k -> LookupMap a
- Swish.Utils.LookupMap: mapDeleteAll :: LookupEntryClass a k v => LookupMap a -> k -> LookupMap a
- Swish.Utils.LookupMap: mapEq :: (LookupEntryClass a k v, Eq v) => LookupMap a -> LookupMap a -> Bool
- Swish.Utils.LookupMap: mapFind :: LookupEntryClass a k v => v -> k -> LookupMap a -> v
- Swish.Utils.LookupMap: mapFindMaybe :: LookupEntryClass a k v => k -> LookupMap a -> Maybe v
- Swish.Utils.LookupMap: mapKeys :: LookupEntryClass a k v => LookupMap a -> [k]
- Swish.Utils.LookupMap: mapMerge :: (LookupEntryClass a k v, Eq a, Show a, Ord k) => LookupMap a -> LookupMap a -> LookupMap a
- Swish.Utils.LookupMap: mapReplace :: LookupEntryClass a k v => LookupMap a -> a -> LookupMap a
- Swish.Utils.LookupMap: mapReplaceAll :: LookupEntryClass a k v => LookupMap a -> a -> LookupMap a
- Swish.Utils.LookupMap: mapReplaceMap :: LookupEntryClass a k v => LookupMap a -> LookupMap a -> LookupMap a
- Swish.Utils.LookupMap: mapReplaceOrAdd :: LookupEntryClass a k v => a -> LookupMap a -> LookupMap a
- Swish.Utils.LookupMap: mapSelect :: LookupEntryClass a k v => LookupMap a -> [k] -> LookupMap a
- Swish.Utils.LookupMap: mapTranslate :: LookupEntryClass a k v => LookupMap a -> [w] -> k -> w -> w
- Swish.Utils.LookupMap: mapTranslateEntries :: (a1 -> a2) -> LookupMap a1 -> LookupMap a2
- Swish.Utils.LookupMap: mapTranslateEntriesM :: Monad m => (a1 -> m a2) -> LookupMap a1 -> m (LookupMap a2)
- Swish.Utils.LookupMap: mapTranslateKeys :: (LookupEntryClass a1 k1 v, LookupEntryClass a2 k2 v) => (k1 -> k2) -> LookupMap a1 -> LookupMap a2
- Swish.Utils.LookupMap: mapTranslateVals :: (LookupEntryClass a1 k v1, LookupEntryClass a2 k v2) => (v1 -> v2) -> LookupMap a1 -> LookupMap a2
- Swish.Utils.LookupMap: mapVals :: (Eq v, LookupEntryClass a k v) => LookupMap a -> [v]
- Swish.Utils.LookupMap: newEntry :: LookupEntryClass a k v => (k, v) -> a
- Swish.Utils.LookupMap: reverseLookupMap :: (LookupEntryClass a1 b c, LookupEntryClass a2 c b) => LookupMap a1 -> LookupMap a2
- Swish.Utils.LookupMap: vmap :: (LookupEntryClass a k v, LookupEntryClass a2 k v2) => (v -> v2) -> a -> a2
- Swish.Utils.MiscHelpers: hash :: Int -> String -> Int
- Swish.Utils.MiscHelpers: hashModulus :: Int
- Swish.Utils.Namespace: data Namespace
- Swish.Utils.Namespace: data ScopedName
- Swish.Utils.Namespace: getNamespacePrefix :: Namespace -> Maybe Text
- Swish.Utils.Namespace: getNamespaceTuple :: Namespace -> (Maybe Text, URI)
- Swish.Utils.Namespace: getNamespaceURI :: Namespace -> URI
- Swish.Utils.Namespace: getQName :: ScopedName -> QName
- Swish.Utils.Namespace: getScopeLocal :: ScopedName -> Text
- Swish.Utils.Namespace: getScopeNamespace :: ScopedName -> Namespace
- Swish.Utils.Namespace: getScopePrefix :: ScopedName -> Maybe Text
- Swish.Utils.Namespace: getScopeURI :: ScopedName -> URI
- Swish.Utils.Namespace: getScopedNameURI :: ScopedName -> URI
- Swish.Utils.Namespace: instance Eq Namespace
- Swish.Utils.Namespace: instance Eq ScopedName
- Swish.Utils.Namespace: instance IsString ScopedName
- Swish.Utils.Namespace: instance LookupEntryClass Namespace (Maybe Text) URI
- Swish.Utils.Namespace: instance Ord ScopedName
- Swish.Utils.Namespace: instance Show Namespace
- Swish.Utils.Namespace: instance Show ScopedName
- Swish.Utils.Namespace: makeNSScopedName :: Namespace -> Text -> ScopedName
- Swish.Utils.Namespace: makeNamespace :: Maybe Text -> URI -> Namespace
- Swish.Utils.Namespace: makeNamespaceQName :: Namespace -> Text -> QName
- Swish.Utils.Namespace: makeQNameScopedName :: Maybe Text -> QName -> ScopedName
- Swish.Utils.Namespace: makeScopedName :: Maybe Text -> URI -> Text -> ScopedName
- Swish.Utils.Namespace: makeURIScopedName :: URI -> ScopedName
- Swish.Utils.Namespace: matchName :: String -> ScopedName -> Bool
- Swish.Utils.Namespace: namespaceToBuilder :: Namespace -> Builder
- Swish.Utils.Namespace: nullScopedName :: ScopedName
- Swish.Utils.PartOrderedCollection: maxima :: PartCompare a -> [a] -> [a]
- Swish.Utils.PartOrderedCollection: minima :: PartCompare a -> [a] -> [a]
- Swish.Utils.PartOrderedCollection: partCompareEq :: Eq a => a -> a -> Maybe Ordering
- Swish.Utils.PartOrderedCollection: partCompareListMaybe :: Eq a => [Maybe a] -> [Maybe a] -> Maybe Ordering
- Swish.Utils.PartOrderedCollection: partCompareListOrd :: Ord a => [a] -> [a] -> Maybe Ordering
- Swish.Utils.PartOrderedCollection: partCompareListPartOrd :: PartCompare a -> [a] -> [a] -> Maybe Ordering
- Swish.Utils.PartOrderedCollection: partCompareListSubset :: Eq a => [a] -> [a] -> Maybe Ordering
- Swish.Utils.PartOrderedCollection: partCompareMaybe :: Eq a => Maybe a -> Maybe a -> Maybe Ordering
- Swish.Utils.PartOrderedCollection: partCompareOrd :: Ord a => a -> a -> Maybe Ordering
- Swish.Utils.PartOrderedCollection: partComparePair :: (a -> a -> Maybe Ordering) -> (b -> b -> Maybe Ordering) -> (a, b) -> (a, b) -> Maybe Ordering
- Swish.Utils.PartOrderedCollection: type PartCompare a = a -> a -> Maybe Ordering
- Swish.Utils.QName: data QName
- Swish.Utils.QName: getLocalName :: QName -> Text
- Swish.Utils.QName: getNamespace :: QName -> URI
- Swish.Utils.QName: getQNameURI :: QName -> URI
- Swish.Utils.QName: instance Eq QName
- Swish.Utils.QName: instance IsString QName
- Swish.Utils.QName: instance Ord QName
- Swish.Utils.QName: instance Show QName
- Swish.Utils.QName: newQName :: URI -> Text -> QName
- Swish.Utils.QName: qnameFromFilePath :: FilePath -> IO QName
- Swish.Utils.QName: qnameFromURI :: URI -> QName
- Swish.Utils.ShowM: class Show sh => ShowM sh where showms _ = shows
- Swish.Utils.ShowM: showms :: ShowM sh => String -> sh -> ShowS
+ Data.LookupMap: LookupMap :: [a] -> LookupMap a
+ Data.LookupMap: class (Eq k, Show k) => LookupEntryClass a k v | a -> k, a -> v where entryKey = fst . keyVal entryVal = snd . keyVal entryEq e1 e2 = keyVal e1 == keyVal e2 entryShow e = show k ++ ":" ++ show v where (k, v) = keyVal e kmap f = newEntry . first f . keyVal vmap f = newEntry . second f . keyVal
+ Data.LookupMap: data LookupMap a
+ Data.LookupMap: emptyLookupMap :: LookupEntryClass a k v => LookupMap a
+ Data.LookupMap: entryEq :: (LookupEntryClass a k v, Eq v) => a -> a -> Bool
+ Data.LookupMap: entryKey :: LookupEntryClass a k v => a -> k
+ Data.LookupMap: entryShow :: (LookupEntryClass a k v, Show v) => a -> String
+ Data.LookupMap: entryVal :: LookupEntryClass a k v => a -> v
+ Data.LookupMap: instance (Eq k, Show k) => LookupEntryClass (k, v) k v
+ Data.LookupMap: instance Eq a => Eq (LookupMap a)
+ Data.LookupMap: instance Foldable LookupMap
+ Data.LookupMap: instance Functor LookupMap
+ Data.LookupMap: instance Show a => Show (LookupMap a)
+ Data.LookupMap: instance Traversable LookupMap
+ Data.LookupMap: keyOrder :: (LookupEntryClass a k v, Ord k) => a -> a -> Ordering
+ Data.LookupMap: keyVal :: LookupEntryClass a k v => a -> (k, v)
+ Data.LookupMap: kmap :: (LookupEntryClass a k v, LookupEntryClass a2 k2 v) => (k -> k2) -> a -> a2
+ Data.LookupMap: listLookupMap :: LookupEntryClass a k v => LookupMap a -> [a]
+ Data.LookupMap: makeLookupMap :: LookupEntryClass a k v => [a] -> LookupMap a
+ Data.LookupMap: mapAdd :: LookupMap a -> a -> LookupMap a
+ Data.LookupMap: mapAddIfNew :: LookupEntryClass a k v => LookupMap a -> a -> LookupMap a
+ Data.LookupMap: mapContains :: LookupEntryClass a k v => LookupMap a -> k -> Bool
+ Data.LookupMap: mapDelete :: LookupEntryClass a k v => LookupMap a -> k -> LookupMap a
+ Data.LookupMap: mapDeleteAll :: LookupEntryClass a k v => LookupMap a -> k -> LookupMap a
+ Data.LookupMap: mapEq :: (LookupEntryClass a k v, Eq v) => LookupMap a -> LookupMap a -> Bool
+ Data.LookupMap: mapFind :: LookupEntryClass a k v => v -> k -> LookupMap a -> v
+ Data.LookupMap: mapFindMaybe :: LookupEntryClass a k v => k -> LookupMap a -> Maybe v
+ Data.LookupMap: mapKeys :: LookupEntryClass a k v => LookupMap a -> [k]
+ Data.LookupMap: mapMerge :: (LookupEntryClass a k v, Eq a, Show a, Ord k) => LookupMap a -> LookupMap a -> LookupMap a
+ Data.LookupMap: mapReplace :: LookupEntryClass a k v => LookupMap a -> a -> LookupMap a
+ Data.LookupMap: mapReplaceAll :: LookupEntryClass a k v => LookupMap a -> a -> LookupMap a
+ Data.LookupMap: mapReplaceMap :: LookupEntryClass a k v => LookupMap a -> LookupMap a -> LookupMap a
+ Data.LookupMap: mapVals :: (Eq v, LookupEntryClass a k v) => LookupMap a -> [v]
+ Data.LookupMap: newEntry :: LookupEntryClass a k v => (k, v) -> a
+ Data.LookupMap: reverseLookupMap :: (LookupEntryClass a1 b c, LookupEntryClass a2 c b) => LookupMap a1 -> LookupMap a2
+ Data.LookupMap: vmap :: (LookupEntryClass a k v, LookupEntryClass a2 k v2) => (v -> v2) -> a -> a2
+ Data.Ord.Partial: maxima :: PartCompare a -> [a] -> [a]
+ Data.Ord.Partial: minima :: PartCompare a -> [a] -> [a]
+ Data.Ord.Partial: partCompareEq :: Eq a => PartCompare a
+ Data.Ord.Partial: partCompareListMaybe :: Eq a => [Maybe a] -> [Maybe a] -> Maybe Ordering
+ Data.Ord.Partial: partCompareListSubset :: Eq a => [a] -> [a] -> Maybe Ordering
+ Data.Ord.Partial: partComparePair :: PartCompare a -> PartCompare b -> (a, b) -> (a, b) -> Maybe Ordering
+ Data.Ord.Partial: type PartCompare a = a -> a -> Maybe Ordering
+ Data.String.ShowLines: class Show sh => ShowLines sh where showls _ = shows
+ Data.String.ShowLines: showls :: ShowLines sh => String -> sh -> ShowS
+ Swish: SwishArgumentError :: SwishStatus
+ Swish: SwishDataAccessError :: SwishStatus
+ Swish: SwishDataInputError :: SwishStatus
+ Swish: SwishExecutionError :: SwishStatus
+ Swish: SwishGraphCompareError :: SwishStatus
+ Swish: SwishSuccess :: SwishStatus
+ Swish: data SwishAction
+ Swish: data SwishStatus
+ Swish: displaySwishHelp :: IO ()
+ Swish: instance Show SwishAction
+ Swish: runSwish :: String -> IO ExitCode
+ Swish: runSwishActions :: [SwishAction] -> IO SwishStatus
+ Swish: splitArguments :: [String] -> ([String], [String])
+ Swish: validateCommands :: [String] -> Either (String, SwishStatus) [SwishAction]
+ Swish.Commands: swishBase :: Maybe QName -> SwishStateIO ()
+ Swish.Commands: swishCompare :: Maybe String -> SwishStateIO ()
+ Swish.Commands: swishFormat :: SwishFormat -> SwishStateIO ()
+ Swish.Commands: swishGraphDiff :: Maybe String -> SwishStateIO ()
+ Swish.Commands: swishInput :: Maybe String -> SwishStateIO ()
+ Swish.Commands: swishMerge :: Maybe String -> SwishStateIO ()
+ Swish.Commands: swishOutput :: Maybe String -> SwishStateIO ()
+ Swish.Commands: swishScript :: Maybe String -> SwishStateIO ()
+ Swish.Datatype: Datatype :: (DatatypeVal ex vt lb vn) -> Datatype ex lb vn
+ Swish.Datatype: DatatypeMap :: (Text -> Maybe vt) -> (vt -> Maybe Text) -> DatatypeMap vt
+ Swish.Datatype: DatatypeMod :: ScopedName -> [ModifierFn vt] -> ApplyModifier lb vn -> DatatypeMod vt lb vn
+ Swish.Datatype: DatatypeRel :: ScopedName -> DatatypeRelFn vt -> DatatypeRel vt
+ Swish.Datatype: DatatypeSub :: DatatypeVal ex supvt lb vn -> DatatypeVal ex subvt lb vn -> (subvt -> supvt) -> (supvt -> Maybe subvt) -> DatatypeSub ex lb vn supvt subvt
+ Swish.Datatype: DatatypeVal :: ScopedName -> Ruleset ex -> (ex -> [Rule ex]) -> [OpenVarBindingModify lb vn] -> DatatypeMap vt -> [DatatypeRel vt] -> [DatatypeMod vt lb vn] -> DatatypeVal ex vt lb vn
+ Swish.Datatype: altArgs :: Eq vt => DatatypeRelPr vt -> [(vt -> Bool, [b])] -> ((vt -> Bool) -> b -> [Maybe vt] -> Maybe [vt]) -> DatatypeRelFn vt
+ Swish.Datatype: binMaybeFnApp :: BinMaybeFnApply a
+ Swish.Datatype: binaryFnApp :: BinaryFnApply a
+ Swish.Datatype: data Datatype ex lb vn
+ Swish.Datatype: data DatatypeMap vt
+ Swish.Datatype: data DatatypeMod vt lb vn
+ Swish.Datatype: data DatatypeRel vt
+ Swish.Datatype: data DatatypeSub ex lb vn supvt subvt
+ Swish.Datatype: data DatatypeVal ex vt lb vn
+ Swish.Datatype: dmAppf :: DatatypeMod vt lb vn -> ApplyModifier lb vn
+ Swish.Datatype: dmModf :: DatatypeMod vt lb vn -> [ModifierFn vt]
+ Swish.Datatype: dmName :: DatatypeMod vt lb vn -> ScopedName
+ Swish.Datatype: dtRelFunc :: DatatypeRel vt -> DatatypeRelFn vt
+ Swish.Datatype: dtRelName :: DatatypeRel vt -> ScopedName
+ Swish.Datatype: getDTMod :: ScopedName -> DatatypeVal ex vt lb vn -> Maybe (DatatypeMod vt lb vn)
+ Swish.Datatype: getDTRel :: ScopedName -> DatatypeVal ex vt lb vn -> Maybe (DatatypeRel vt)
+ Swish.Datatype: getTypeAxiom :: ScopedName -> Datatype ex lb vn -> Maybe (Formula ex)
+ Swish.Datatype: getTypeRule :: ScopedName -> Datatype ex lb vn -> Maybe (Rule ex)
+ Swish.Datatype: instance LookupEntryClass (Datatype ex lb vn) ScopedName (Datatype ex lb vn)
+ Swish.Datatype: instance LookupEntryClass (DatatypeMod vt lb vn) ScopedName (DatatypeMod vt lb vn)
+ Swish.Datatype: instance LookupEntryClass (DatatypeRel vt) ScopedName (DatatypeRel vt)
+ Swish.Datatype: listFnApp :: ListFnApply a
+ Swish.Datatype: makeVmod11 :: (Eq lb, Show lb, Eq vn, Show vn) => ApplyModifier lb vn
+ Swish.Datatype: makeVmod11inv :: (Eq lb, Show lb, Eq vn, Show vn) => ApplyModifier lb vn
+ Swish.Datatype: makeVmod20 :: (Eq lb, Show lb, Eq vn, Show vn) => ApplyModifier lb vn
+ Swish.Datatype: makeVmod21 :: (Eq lb, Show lb, Eq vn, Show vn) => ApplyModifier lb vn
+ Swish.Datatype: makeVmod21inv :: (Eq lb, Show lb, Eq vn, Show vn) => ApplyModifier lb vn
+ Swish.Datatype: makeVmod22 :: (Eq lb, Show lb, Eq vn, Show vn) => ApplyModifier lb vn
+ Swish.Datatype: makeVmodN1 :: (Eq lb, Show lb, Eq vn, Show vn) => ApplyModifier lb vn
+ Swish.Datatype: mapL2V :: DatatypeMap vt -> Text -> Maybe vt
+ Swish.Datatype: mapV2L :: DatatypeMap vt -> vt -> Maybe Text
+ Swish.Datatype: nullDatatypeMod :: DatatypeMod vt lb vn
+ Swish.Datatype: trelSub :: DatatypeSub ex lb vn supvt subvt -> DatatypeVal ex subvt lb vn
+ Swish.Datatype: trelSup :: DatatypeSub ex lb vn supvt subvt -> DatatypeVal ex supvt lb vn
+ Swish.Datatype: trelToSub :: DatatypeSub ex lb vn supvt subvt -> supvt -> Maybe subvt
+ Swish.Datatype: trelToSup :: DatatypeSub ex lb vn supvt subvt -> subvt -> supvt
+ Swish.Datatype: tvalMap :: DatatypeVal ex vt lb vn -> DatatypeMap vt
+ Swish.Datatype: tvalMkCanonicalForm :: DatatypeVal ex vt lb vn -> Text -> Maybe Text
+ Swish.Datatype: tvalMkMods :: DatatypeVal ex vt lb vn -> [OpenVarBindingModify lb vn]
+ Swish.Datatype: tvalMkRules :: DatatypeVal ex vt lb vn -> ex -> [Rule ex]
+ Swish.Datatype: tvalMod :: DatatypeVal ex vt lb vn -> [DatatypeMod vt lb vn]
+ Swish.Datatype: tvalName :: DatatypeVal ex vt lb vn -> ScopedName
+ Swish.Datatype: tvalRel :: DatatypeVal ex vt lb vn -> [DatatypeRel vt]
+ Swish.Datatype: tvalRules :: DatatypeVal ex vt lb vn -> Ruleset ex
+ Swish.Datatype: type ApplyModifier lb vn = ScopedName -> [ModifierFn vn] -> OpenVarBindingModify lb vn
+ Swish.Datatype: type BinMaybeFnApply a = (a -> Bool) -> BinMaybeFnDescr a -> [Maybe a] -> Maybe [a]
+ Swish.Datatype: type BinMaybeFnDescr a = (a -> a -> Maybe [a], Int, Int)
+ Swish.Datatype: type BinMaybeFnTable a = [(a -> Bool, [BinMaybeFnDescr a])]
+ Swish.Datatype: type BinaryFnApply a = (a -> Bool) -> BinaryFnDescr a -> [Maybe a] -> Maybe [a]
+ Swish.Datatype: type BinaryFnDescr a = (a -> a -> a, Int, Int)
+ Swish.Datatype: type BinaryFnTable a = [(a -> Bool, [BinaryFnDescr a])]
+ Swish.Datatype: type DatatypeRelFn vt = [Maybe vt] -> Maybe [[vt]]
+ Swish.Datatype: type DatatypeRelPr vt = [vt] -> Bool
+ Swish.Datatype: type ListFnApply a = (a -> Bool) -> ListFnDescr a -> [Maybe a] -> Maybe [a]
+ Swish.Datatype: type ListFnDescr a = (a -> a -> a, a, a -> a -> a, Int)
+ Swish.Datatype: type ListFnTable a = [(a -> Bool, [ListFnDescr a])]
+ Swish.Datatype: type ModifierFn vn = [vn] -> [vn]
+ Swish.Datatype: type UnaryFnApply a = (a -> Bool) -> UnaryFnDescr a -> [Maybe a] -> Maybe [a]
+ Swish.Datatype: type UnaryFnDescr a = (a -> a, Int)
+ Swish.Datatype: type UnaryFnTable a = [(a -> Bool, [UnaryFnDescr a])]
+ Swish.Datatype: typeMkCanonicalForm :: Datatype ex lb vn -> Text -> Maybe Text
+ Swish.Datatype: typeMkModifiers :: Datatype ex lb vn -> [OpenVarBindingModify lb vn]
+ Swish.Datatype: typeMkRules :: Datatype ex lb vn -> ex -> [Rule ex]
+ Swish.Datatype: typeName :: Datatype ex lb vn -> ScopedName
+ Swish.Datatype: typeRules :: Datatype ex lb vn -> Ruleset ex
+ Swish.Datatype: unaryFnApp :: UnaryFnApply a
+ Swish.GraphClass: Arc :: lb -> lb -> lb -> Arc lb
+ Swish.GraphClass: addGraphs :: LDGraph lg lb => lg lb -> lg lb -> lg lb
+ Swish.GraphClass: arc :: lb -> lb -> lb -> Arc lb
+ Swish.GraphClass: arcFromTriple :: (lb, lb, lb) -> Arc lb
+ Swish.GraphClass: arcLabels :: Arc lb -> [lb]
+ Swish.GraphClass: arcObj :: Arc lb -> lb
+ Swish.GraphClass: arcPred :: Arc lb -> lb
+ Swish.GraphClass: arcSubj :: Arc lb -> lb
+ Swish.GraphClass: arcToTriple :: Arc lb -> (lb, lb, lb)
+ Swish.GraphClass: class (Eq (lg lb), Eq lb) => LDGraph lg lb where extract sel = update (filter sel) addGraphs addg = update (union (getArcs addg)) delete delg = update (\\ getArcs delg) labels g = foldl' union [] (map arcLabels (getArcs g)) nodes g = foldl' union [] (map arcNodes (getArcs g)) update f g = setArcs g (f (getArcs g))
+ Swish.GraphClass: class (Eq lb, Show lb, Ord lb) => Label lb
+ Swish.GraphClass: data Arc lb
+ Swish.GraphClass: delete :: LDGraph lg lb => lg lb -> lg lb -> lg lb
+ Swish.GraphClass: emptyGraph :: LDGraph lg lb => lg lb
+ Swish.GraphClass: extract :: LDGraph lg lb => Selector lb -> lg lb -> lg lb
+ Swish.GraphClass: getArcs :: LDGraph lg lb => lg lb -> [Arc lb]
+ Swish.GraphClass: getLocal :: Label lb => lb -> String
+ Swish.GraphClass: hasLabel :: Eq lb => lb -> Arc lb -> Bool
+ Swish.GraphClass: instance Eq lb => Eq (Arc lb)
+ Swish.GraphClass: instance Foldable Arc
+ Swish.GraphClass: instance Functor Arc
+ Swish.GraphClass: instance Hashable lb => Hashable (Arc lb)
+ Swish.GraphClass: instance Ord lb => Ord (Arc lb)
+ Swish.GraphClass: instance Show lb => Show (Arc lb)
+ Swish.GraphClass: instance Traversable Arc
+ Swish.GraphClass: labelHash :: Label lb => Int -> lb -> Int
+ Swish.GraphClass: labelIsVar :: Label lb => lb -> Bool
+ Swish.GraphClass: labels :: LDGraph lg lb => lg lb -> [lb]
+ Swish.GraphClass: makeLabel :: Label lb => String -> lb
+ Swish.GraphClass: nodes :: LDGraph lg lb => lg lb -> [lb]
+ Swish.GraphClass: setArcs :: LDGraph lg lb => lg lb -> [Arc lb] -> lg lb
+ Swish.GraphClass: type Selector lb = Arc lb -> Bool
+ Swish.GraphClass: update :: LDGraph lg lb => ([Arc lb] -> [Arc lb]) -> lg lb -> lg lb
+ Swish.GraphMatch: LabelEntry :: lb -> lv -> GenLabelEntry lb lv
+ Swish.GraphMatch: LabelMap :: Word32 -> (LookupMap (GenLabelEntry lb lv)) -> GenLabelMap lb lv
+ Swish.GraphMatch: ScopedLabel :: Int -> lb -> ScopedLabel lb
+ Swish.GraphMatch: assignLabelMap :: Label lb => [lb] -> LabelMap lb -> LabelMap lb
+ Swish.GraphMatch: data Label lb => GenLabelEntry lb lv
+ Swish.GraphMatch: data (Label lb, Eq lv, Show lv) => GenLabelMap lb lv
+ Swish.GraphMatch: data Label lb => ScopedLabel lb
+ Swish.GraphMatch: emptyMap :: Label lb => LabelMap lb
+ Swish.GraphMatch: equivalenceClasses :: Label lb => LabelMap lb -> [lb] -> [EquivalenceClass lb]
+ Swish.GraphMatch: graphLabels :: Label lb => [Arc lb] -> [lb]
+ Swish.GraphMatch: graphMatch :: Label lb => (lb -> lb -> Bool) -> [Arc lb] -> [Arc lb] -> (Bool, LabelMap (ScopedLabel lb))
+ Swish.GraphMatch: graphMatch1 :: Label lb => Bool -> (lb -> lb -> Bool) -> [Arc lb] -> [Arc lb] -> LabelMap lb -> [(EquivalenceClass lb, EquivalenceClass lb)] -> (Bool, LabelMap lb)
+ Swish.GraphMatch: graphMatch2 :: Label lb => (lb -> lb -> Bool) -> [Arc lb] -> [Arc lb] -> LabelMap lb -> [(EquivalenceClass lb, EquivalenceClass lb)] -> (Bool, LabelMap lb)
+ Swish.GraphMatch: instance (Label lb, Eq lb, Show lb, Eq lv, Show lv) => Eq (GenLabelEntry lb lv)
+ Swish.GraphMatch: instance (Label lb, Eq lb, Show lb, Eq lv, Show lv) => LookupEntryClass (GenLabelEntry lb lv) lb lv
+ Swish.GraphMatch: instance (Label lb, Eq lb, Show lb, Eq lv, Show lv) => Show (GenLabelEntry lb lv)
+ Swish.GraphMatch: instance Label lb => Eq (LabelMap lb)
+ Swish.GraphMatch: instance Label lb => Eq (ScopedLabel lb)
+ Swish.GraphMatch: instance Label lb => Label (ScopedLabel lb)
+ Swish.GraphMatch: instance Label lb => Ord (ScopedLabel lb)
+ Swish.GraphMatch: instance Label lb => Show (LabelMap lb)
+ Swish.GraphMatch: instance Label lb => Show (ScopedLabel lb)
+ Swish.GraphMatch: labelHash :: Label lb => Int -> lb -> Int
+ Swish.GraphMatch: labelIsVar :: Label lb => lb -> Bool
+ Swish.GraphMatch: makeScopedArc :: Label lb => Int -> Arc lb -> Arc (ScopedLabel lb)
+ Swish.GraphMatch: makeScopedLabel :: Label lb => Int -> lb -> ScopedLabel lb
+ Swish.GraphMatch: mapLabelIndex :: Label lb => LabelMap lb -> lb -> LabelIndex
+ Swish.GraphMatch: newGenerationMap :: Label lb => LabelMap lb -> LabelMap lb
+ Swish.GraphMatch: newLabelMap :: Label lb => LabelMap lb -> [(lb, Word32)] -> LabelMap lb
+ Swish.GraphMatch: nullLabelVal :: LabelIndex
+ Swish.GraphMatch: reclassify :: Label lb => [Arc lb] -> [Arc lb] -> LabelMap lb -> [(EquivalenceClass lb, EquivalenceClass lb)] -> (LabelMap lb, [(EquivalenceClass lb, EquivalenceClass lb)], Bool, Bool)
+ Swish.GraphMatch: setLabelHash :: Label lb => LabelMap lb -> (lb, Word32) -> LabelMap lb
+ Swish.GraphMatch: type EquivalenceClass lb = (LabelIndex, [lb])
+ Swish.GraphMatch: type LabelEntry lb = GenLabelEntry lb LabelIndex
+ Swish.GraphMatch: type LabelIndex = (Word32, Word32)
+ Swish.GraphMatch: type LabelMap lb = GenLabelMap lb LabelIndex
+ Swish.GraphMem: GraphMem :: [Arc lb] -> GraphMem lb
+ Swish.GraphMem: LF :: String -> LabelMem
+ Swish.GraphMem: LV :: String -> LabelMem
+ Swish.GraphMem: addGraphs :: LDGraph lg lb => lg lb -> lg lb -> lg lb
+ Swish.GraphMem: arcs :: GraphMem lb -> [Arc lb]
+ Swish.GraphMem: data GraphMem lb
+ Swish.GraphMem: data LabelMem
+ Swish.GraphMem: delete :: LDGraph lg lb => lg lb -> lg lb -> lg lb
+ Swish.GraphMem: extract :: LDGraph lg lb => Selector lb -> lg lb -> lg lb
+ Swish.GraphMem: getArcs :: LDGraph lg lb => lg lb -> [Arc lb]
+ Swish.GraphMem: instance Eq LabelMem
+ Swish.GraphMem: instance Foldable GraphMem
+ Swish.GraphMem: instance Functor GraphMem
+ Swish.GraphMem: instance Hashable LabelMem
+ Swish.GraphMem: instance Label LabelMem
+ Swish.GraphMem: instance Label lb => Eq (GraphMem lb)
+ Swish.GraphMem: instance Label lb => LDGraph GraphMem lb
+ Swish.GraphMem: instance Label lb => Monoid (GraphMem lb)
+ Swish.GraphMem: instance Label lb => Show (GraphMem lb)
+ Swish.GraphMem: instance Ord LabelMem
+ Swish.GraphMem: instance Show LabelMem
+ Swish.GraphMem: instance Traversable GraphMem
+ Swish.GraphMem: labelHash :: Label lb => Int -> lb -> Int
+ Swish.GraphMem: labelIsVar :: Label lb => lb -> Bool
+ Swish.GraphMem: labels :: LDGraph lg lb => lg lb -> [lb]
+ Swish.GraphMem: matchGraphMem :: Label lb => GraphMem lb -> GraphMem lb -> (Bool, LabelMap (ScopedLabel lb))
+ Swish.GraphMem: setArcs :: LDGraph lg lb => lg lb -> [Arc lb] -> lg lb
+ Swish.GraphPartition: PartObj :: lb -> GraphPartition lb
+ Swish.GraphPartition: PartSub :: lb -> (NonEmpty (lb, GraphPartition lb)) -> GraphPartition lb
+ Swish.GraphPartition: PartitionedGraph :: [GraphPartition lb] -> PartitionedGraph lb
+ Swish.GraphPartition: comparePartitions :: Label lb => PartitionedGraph lb -> PartitionedGraph lb -> [(Maybe (GraphPartition lb), Maybe (GraphPartition lb))]
+ Swish.GraphPartition: data GraphPartition lb
+ Swish.GraphPartition: data PartitionedGraph lb
+ Swish.GraphPartition: getArcs :: PartitionedGraph lb -> [Arc lb]
+ Swish.GraphPartition: getPartitions :: PartitionedGraph lb -> [GraphPartition lb]
+ Swish.GraphPartition: instance Label lb => Eq (GraphPartition lb)
+ Swish.GraphPartition: instance Label lb => Eq (PartitionedGraph lb)
+ Swish.GraphPartition: instance Label lb => Show (GraphPartition lb)
+ Swish.GraphPartition: instance Label lb => Show (PartitionedGraph lb)
+ Swish.GraphPartition: node :: GraphPartition lb -> lb
+ Swish.GraphPartition: partitionGraph :: Label lb => [Arc lb] -> PartitionedGraph lb
+ Swish.GraphPartition: partitionShowP :: Label lb => String -> GraphPartition lb -> String
+ Swish.GraphPartition: toArcs :: GraphPartition lb -> [Arc lb]
+ Swish.Monad: N3 :: SwishFormat
+ Swish.Monad: NT :: SwishFormat
+ Swish.Monad: NamedGraph :: ScopedName -> [RDFGraph] -> NamedGraph
+ Swish.Monad: SwishArgumentError :: SwishStatus
+ Swish.Monad: SwishDataAccessError :: SwishStatus
+ Swish.Monad: SwishDataInputError :: SwishStatus
+ Swish.Monad: SwishExecutionError :: SwishStatus
+ Swish.Monad: SwishGraphCompareError :: SwishStatus
+ Swish.Monad: SwishState :: SwishFormat -> Maybe QName -> RDFGraph -> NamedGraphMap -> RDFRuleMap -> RDFRulesetMap -> Maybe String -> Maybe String -> SwishStatus -> SwishState
+ Swish.Monad: SwishSuccess :: SwishStatus
+ Swish.Monad: Turtle :: SwishFormat
+ Swish.Monad: base :: SwishState -> Maybe QName
+ Swish.Monad: data NamedGraph
+ Swish.Monad: data SwishFormat
+ Swish.Monad: data SwishState
+ Swish.Monad: data SwishStatus
+ Swish.Monad: emptyState :: SwishState
+ Swish.Monad: errormsg :: SwishState -> Maybe String
+ Swish.Monad: exitcode :: SwishState -> SwishStatus
+ Swish.Monad: findDatatype :: ScopedName -> SwishState -> Maybe RDFDatatype
+ Swish.Monad: findFormula :: ScopedName -> SwishState -> Maybe RDFFormula
+ Swish.Monad: findGraph :: ScopedName -> SwishState -> Maybe [RDFGraph]
+ Swish.Monad: findOpenVarModify :: ScopedName -> SwishState -> Maybe RDFOpenVarBindingModify
+ Swish.Monad: findRule :: ScopedName -> SwishState -> Maybe RDFRule
+ Swish.Monad: findRuleset :: ScopedName -> SwishState -> Maybe RDFRuleset
+ Swish.Monad: format :: SwishState -> SwishFormat
+ Swish.Monad: graph :: SwishState -> RDFGraph
+ Swish.Monad: graphs :: SwishState -> NamedGraphMap
+ Swish.Monad: infomsg :: SwishState -> Maybe String
+ Swish.Monad: instance Enum SwishStatus
+ Swish.Monad: instance Eq SwishFormat
+ Swish.Monad: instance Eq SwishStatus
+ Swish.Monad: instance LookupEntryClass NamedGraph ScopedName [RDFGraph]
+ Swish.Monad: instance Show SwishFormat
+ Swish.Monad: instance Show SwishStatus
+ Swish.Monad: modGraphs :: (NamedGraphMap -> NamedGraphMap) -> SwishState -> SwishState
+ Swish.Monad: modRules :: (RDFRuleMap -> RDFRuleMap) -> SwishState -> SwishState
+ Swish.Monad: modRulesets :: (RDFRulesetMap -> RDFRulesetMap) -> SwishState -> SwishState
+ Swish.Monad: ngGraph :: NamedGraph -> [RDFGraph]
+ Swish.Monad: ngName :: NamedGraph -> ScopedName
+ Swish.Monad: reportLine :: String -> SwishStateIO ()
+ Swish.Monad: resetError :: SwishState -> SwishState
+ Swish.Monad: resetInfo :: SwishState -> SwishState
+ Swish.Monad: rules :: SwishState -> RDFRuleMap
+ Swish.Monad: rulesets :: SwishState -> RDFRulesetMap
+ Swish.Monad: setBase :: Maybe QName -> SwishState -> SwishState
+ Swish.Monad: setError :: String -> SwishState -> SwishState
+ Swish.Monad: setFormat :: SwishFormat -> SwishState -> SwishState
+ Swish.Monad: setGraph :: RDFGraph -> SwishState -> SwishState
+ Swish.Monad: setInfo :: String -> SwishState -> SwishState
+ Swish.Monad: setStatus :: SwishStatus -> SwishState -> SwishState
+ Swish.Monad: swishError :: String -> SwishStatus -> SwishStateIO ()
+ Swish.Monad: type NamedGraphMap = LookupMap NamedGraph
+ Swish.Monad: type SwishStateIO a = StateT SwishState IO a
+ Swish.Namespace: data Namespace
+ Swish.Namespace: data ScopedName
+ Swish.Namespace: getNamespacePrefix :: Namespace -> Maybe Text
+ Swish.Namespace: getNamespaceTuple :: Namespace -> (Maybe Text, URI)
+ Swish.Namespace: getNamespaceURI :: Namespace -> URI
+ Swish.Namespace: getQName :: ScopedName -> QName
+ Swish.Namespace: getScopeLocal :: ScopedName -> LName
+ Swish.Namespace: getScopeNamespace :: ScopedName -> Namespace
+ Swish.Namespace: getScopePrefix :: ScopedName -> Maybe Text
+ Swish.Namespace: getScopeURI :: ScopedName -> URI
+ Swish.Namespace: getScopedNameURI :: ScopedName -> URI
+ Swish.Namespace: instance Eq Namespace
+ Swish.Namespace: instance Eq ScopedName
+ Swish.Namespace: instance IsString ScopedName
+ Swish.Namespace: instance LookupEntryClass Namespace (Maybe Text) URI
+ Swish.Namespace: instance Ord ScopedName
+ Swish.Namespace: instance Show Namespace
+ Swish.Namespace: instance Show ScopedName
+ Swish.Namespace: makeNSScopedName :: Namespace -> LName -> ScopedName
+ Swish.Namespace: makeNamespace :: Maybe Text -> URI -> Namespace
+ Swish.Namespace: makeNamespaceQName :: Namespace -> LName -> QName
+ Swish.Namespace: makeQNameScopedName :: Maybe Text -> QName -> ScopedName
+ Swish.Namespace: makeScopedName :: Maybe Text -> URI -> LName -> ScopedName
+ Swish.Namespace: makeURIScopedName :: URI -> ScopedName
+ Swish.Namespace: matchName :: String -> ScopedName -> Bool
+ Swish.Namespace: namespaceToBuilder :: Namespace -> Builder
+ Swish.Namespace: nullScopedName :: ScopedName
+ Swish.Proof: Proof :: [Ruleset ex] -> Formula ex -> Formula ex -> [Step ex] -> Proof ex
+ Swish.Proof: Step :: Rule ex -> [Formula ex] -> Formula ex -> Step ex
+ Swish.Proof: checkProof :: Expression ex => Proof ex -> Bool
+ Swish.Proof: checkStep :: Expression ex => [Rule ex] -> [ex] -> Step ex -> Bool
+ Swish.Proof: data Proof ex
+ Swish.Proof: data Step ex
+ Swish.Proof: explainProof :: Expression ex => Proof ex -> Maybe String
+ Swish.Proof: instance Show ex => Show (Step ex)
+ Swish.Proof: proofChain :: Proof ex -> [Step ex]
+ Swish.Proof: proofContext :: Proof ex -> [Ruleset ex]
+ Swish.Proof: proofInput :: Proof ex -> Formula ex
+ Swish.Proof: proofResult :: Proof ex -> Formula ex
+ Swish.Proof: showProof :: ShowLines ex => String -> Proof ex -> String
+ Swish.Proof: showsFormula :: ShowLines ex => String -> Formula ex -> ShowS
+ Swish.Proof: showsProof :: ShowLines ex => String -> Proof ex -> ShowS
+ Swish.Proof: stepAnt :: Step ex -> [Formula ex]
+ Swish.Proof: stepCon :: Step ex -> Formula ex
+ Swish.Proof: stepRule :: Step ex -> Rule ex
+ Swish.QName: data LName
+ Swish.QName: data QName
+ Swish.QName: emptyLName :: LName
+ Swish.QName: getLName :: LName -> Text
+ Swish.QName: getLocalName :: QName -> LName
+ Swish.QName: getNamespace :: QName -> URI
+ Swish.QName: getQNameURI :: QName -> URI
+ Swish.QName: instance Eq LName
+ Swish.QName: instance Eq QName
+ Swish.QName: instance IsString LName
+ Swish.QName: instance IsString QName
+ Swish.QName: instance Ord LName
+ Swish.QName: instance Ord QName
+ Swish.QName: instance Show LName
+ Swish.QName: instance Show QName
+ Swish.QName: newLName :: Text -> Maybe LName
+ Swish.QName: newQName :: URI -> LName -> QName
+ Swish.QName: qnameFromFilePath :: FilePath -> IO QName
+ Swish.QName: qnameFromURI :: URI -> Maybe QName
+ Swish.RDF.BuiltIn: allDatatypeRulesets :: [RDFRuleset]
+ Swish.RDF.BuiltIn: allRulesets :: [RDFRuleset]
+ Swish.RDF.BuiltIn: findRDFDatatype :: ScopedName -> Maybe RDFDatatype
+ Swish.RDF.BuiltIn: findRDFOpenVarBindingModifier :: ScopedName -> Maybe RDFOpenVarBindingModify
+ Swish.RDF.BuiltIn: rdfRulesetMap :: RDFRulesetMap
+ Swish.RDF.BuiltIn.Datatypes: allDatatypes :: [RDFDatatype]
+ Swish.RDF.BuiltIn.Datatypes: findRDFDatatype :: ScopedName -> Maybe RDFDatatype
+ Swish.RDF.BuiltIn.Rules: allDatatypeRulesets :: [RDFRuleset]
+ Swish.RDF.BuiltIn.Rules: allRulesets :: [RDFRuleset]
+ Swish.RDF.BuiltIn.Rules: findRDFOpenVarBindingModifier :: ScopedName -> Maybe RDFOpenVarBindingModify
+ Swish.RDF.BuiltIn.Rules: rdfRulesetMap :: RDFRulesetMap
+ Swish.RDF.Datatype: applyRDFDatatypeMod :: RDFDatatypeVal vt -> RDFDatatypeMod vt -> [RDFLabel] -> [RDFVarBinding] -> [RDFVarBinding]
+ Swish.RDF.Datatype: fromRDFLabel :: RDFDatatypeVal vt -> RDFLabel -> Maybe vt
+ Swish.RDF.Datatype: makeDatatypedLiteral :: ScopedName -> Text -> RDFLabel
+ Swish.RDF.Datatype: makeRdfDtOpenVarBindingModifiers :: RDFDatatypeVal vt -> [RDFOpenVarBindingModify]
+ Swish.RDF.Datatype: makeRdfDtOpenVarBindingModify :: RDFDatatypeVal vt -> RDFDatatypeMod vt -> RDFOpenVarBindingModify
+ Swish.RDF.Datatype: toRDFLabel :: RDFDatatypeVal vt -> vt -> Maybe RDFLabel
+ Swish.RDF.Datatype: type RDFApplyModifier = ApplyModifier RDFLabel RDFLabel
+ Swish.RDF.Datatype: type RDFDatatype = Datatype RDFGraph RDFLabel RDFLabel
+ Swish.RDF.Datatype: type RDFDatatypeMod vt = DatatypeMod vt RDFLabel RDFLabel
+ Swish.RDF.Datatype: type RDFDatatypeSub supvt subvt = DatatypeSub RDFGraph RDFLabel RDFLabel supvt subvt
+ Swish.RDF.Datatype: type RDFDatatypeVal vt = DatatypeVal RDFGraph vt RDFLabel RDFLabel
+ Swish.RDF.Datatype: type RDFModifierFn = ModifierFn RDFLabel
+ Swish.RDF.Datatype.XSD.Decimal: axiomsXsdDecimal :: [RDFFormula]
+ Swish.RDF.Datatype.XSD.Decimal: namespaceXsdDecimal :: Namespace
+ Swish.RDF.Datatype.XSD.Decimal: rdfDatatypeValXsdDecimal :: RDFDatatypeVal Double
+ Swish.RDF.Datatype.XSD.Decimal: rdfDatatypeXsdDecimal :: RDFDatatype
+ Swish.RDF.Datatype.XSD.Decimal: rulesXsdDecimal :: [RDFRule]
+ Swish.RDF.Datatype.XSD.Decimal: typeNameXsdDecimal :: ScopedName
+ Swish.RDF.Datatype.XSD.Integer: axiomsXsdInteger :: [RDFFormula]
+ Swish.RDF.Datatype.XSD.Integer: namespaceXsdInteger :: Namespace
+ Swish.RDF.Datatype.XSD.Integer: rdfDatatypeValXsdInteger :: RDFDatatypeVal Integer
+ Swish.RDF.Datatype.XSD.Integer: rdfDatatypeXsdInteger :: RDFDatatype
+ Swish.RDF.Datatype.XSD.Integer: rulesXsdInteger :: [RDFRule]
+ Swish.RDF.Datatype.XSD.Integer: typeNameXsdInteger :: ScopedName
+ Swish.RDF.Datatype.XSD.MapDecimal: mapXsdDecimal :: DatatypeMap Double
+ Swish.RDF.Datatype.XSD.MapInteger: mapXsdInteger :: DatatypeMap Integer
+ Swish.RDF.Datatype.XSD.String: axiomsXsdString :: [RDFFormula]
+ Swish.RDF.Datatype.XSD.String: namespaceXsdString :: Namespace
+ Swish.RDF.Datatype.XSD.String: rdfDatatypeValXsdString :: RDFDatatypeVal Text
+ Swish.RDF.Datatype.XSD.String: rdfDatatypeXsdString :: RDFDatatype
+ Swish.RDF.Datatype.XSD.String: rulesXsdString :: [RDFRule]
+ Swish.RDF.Datatype.XSD.String: typeNameXsdString :: ScopedName
+ Swish.RDF.Formatter.N3: formatGraphAsBuilder :: RDFGraph -> Builder
+ Swish.RDF.Formatter.N3: formatGraphAsLazyText :: RDFGraph -> Text
+ Swish.RDF.Formatter.N3: formatGraphAsText :: RDFGraph -> Text
+ Swish.RDF.Formatter.N3: formatGraphDiag :: Builder -> Bool -> RDFGraph -> (Builder, NodeGenLookupMap, Word32, [String])
+ Swish.RDF.Formatter.N3: formatGraphIndent :: Builder -> Bool -> RDFGraph -> Builder
+ Swish.RDF.Formatter.N3: instance Eq LabelContext
+ Swish.RDF.Formatter.N3: instance Show LabelContext
+ Swish.RDF.Formatter.N3: type NodeGenLookupMap = LookupMap (RDFLabel, Word32)
+ Swish.RDF.Formatter.NTriples: formatGraphAsBuilder :: RDFGraph -> Builder
+ Swish.RDF.Formatter.NTriples: formatGraphAsLazyText :: RDFGraph -> Text
+ Swish.RDF.Formatter.NTriples: formatGraphAsText :: RDFGraph -> Text
+ Swish.RDF.Formatter.NTriples: instance Show NTFormatterState
+ Swish.RDF.Formatter.Turtle: formatGraphAsBuilder :: RDFGraph -> Builder
+ Swish.RDF.Formatter.Turtle: formatGraphAsLazyText :: RDFGraph -> Text
+ Swish.RDF.Formatter.Turtle: formatGraphAsText :: RDFGraph -> Text
+ Swish.RDF.Formatter.Turtle: formatGraphDiag :: Builder -> Bool -> RDFGraph -> (Builder, NodeGenLookupMap, Word32, [String])
+ Swish.RDF.Formatter.Turtle: formatGraphIndent :: Builder -> Bool -> RDFGraph -> Builder
+ Swish.RDF.Formatter.Turtle: instance Eq LabelContext
+ Swish.RDF.Formatter.Turtle: instance Show LabelContext
+ Swish.RDF.Formatter.Turtle: type NodeGenLookupMap = LookupMap (RDFLabel, Word32)
+ Swish.RDF.Graph: Arc :: lb -> lb -> lb -> Arc lb
+ Swish.RDF.Graph: Blank :: String -> RDFLabel
+ Swish.RDF.Graph: Formula :: lb -> gr -> LookupFormula lb gr
+ Swish.RDF.Graph: LangLit :: Text -> LanguageTag -> RDFLabel
+ Swish.RDF.Graph: Lit :: Text -> RDFLabel
+ Swish.RDF.Graph: NSGraph :: NamespaceMap -> FormulaMap lb -> [Arc lb] -> NSGraph lb
+ Swish.RDF.Graph: NoNode :: RDFLabel
+ Swish.RDF.Graph: Res :: ScopedName -> RDFLabel
+ Swish.RDF.Graph: TypedLit :: Text -> ScopedName -> RDFLabel
+ Swish.RDF.Graph: Var :: String -> RDFLabel
+ Swish.RDF.Graph: addArc :: Label lb => Arc lb -> NSGraph lb -> NSGraph lb
+ Swish.RDF.Graph: addGraphs :: LDGraph lg lb => lg lb -> lg lb -> lg lb
+ Swish.RDF.Graph: allLabels :: Label lb => (lb -> Bool) -> NSGraph lb -> [lb]
+ Swish.RDF.Graph: allNodes :: Label lb => (lb -> Bool) -> NSGraph lb -> [lb]
+ Swish.RDF.Graph: arc :: lb -> lb -> lb -> Arc lb
+ Swish.RDF.Graph: arcObj :: Arc lb -> lb
+ Swish.RDF.Graph: arcPred :: Arc lb -> lb
+ Swish.RDF.Graph: arcSubj :: Arc lb -> lb
+ Swish.RDF.Graph: class FromRDFLabel a
+ Swish.RDF.Graph: class (Eq (lg lb), Eq lb) => LDGraph lg lb where extract sel = update (filter sel) addGraphs addg = update (union (getArcs addg)) delete delg = update (\\ getArcs delg) labels g = foldl' union [] (map arcLabels (getArcs g)) nodes g = foldl' union [] (map arcNodes (getArcs g)) update f g = setArcs g (f (getArcs g))
+ Swish.RDF.Graph: class (Eq lb, Show lb, Ord lb) => Label lb
+ Swish.RDF.Graph: class ToRDFLabel a
+ Swish.RDF.Graph: data Arc lb
+ Swish.RDF.Graph: data LookupFormula lb gr
+ Swish.RDF.Graph: data NSGraph lb
+ Swish.RDF.Graph: data RDFLabel
+ Swish.RDF.Graph: data RevNamespace
+ Swish.RDF.Graph: delete :: LDGraph lg lb => lg lb -> lg lb -> lg lb
+ Swish.RDF.Graph: emptyFormulaMap :: FormulaMap RDFLabel
+ Swish.RDF.Graph: emptyGraph :: LDGraph lg lb => lg lb
+ Swish.RDF.Graph: emptyNamespaceMap :: NamespaceMap
+ Swish.RDF.Graph: emptyRDFGraph :: RDFGraph
+ Swish.RDF.Graph: extract :: LDGraph lg lb => Selector lb -> lg lb -> lg lb
+ Swish.RDF.Graph: formGraph :: LookupFormula lb gr -> gr
+ Swish.RDF.Graph: formLabel :: LookupFormula lb gr -> lb
+ Swish.RDF.Graph: formulae :: NSGraph lb -> FormulaMap lb
+ Swish.RDF.Graph: fromRDFLabel :: FromRDFLabel a => RDFLabel -> Maybe a
+ Swish.RDF.Graph: fromRDFTriple :: (FromRDFLabel s, FromRDFLabel p, FromRDFLabel o) => RDFTriple -> Maybe (s, p, o)
+ Swish.RDF.Graph: getArcs :: LDGraph lg lb => lg lb -> [Arc lb]
+ Swish.RDF.Graph: getFormula :: Label lb => NSGraph lb -> lb -> Maybe (NSGraph lb)
+ Swish.RDF.Graph: getFormulae :: NSGraph lb -> FormulaMap lb
+ Swish.RDF.Graph: getLiteralText :: RDFLabel -> Text
+ Swish.RDF.Graph: getLocal :: Label lb => lb -> String
+ Swish.RDF.Graph: getNamespaces :: NSGraph lb -> NamespaceMap
+ Swish.RDF.Graph: getScopedName :: RDFLabel -> ScopedName
+ Swish.RDF.Graph: grEq :: Label lb => NSGraph lb -> NSGraph lb -> Bool
+ Swish.RDF.Graph: grMatchMap :: Label lb => NSGraph lb -> NSGraph lb -> (Bool, LabelMap (ScopedLabel lb))
+ Swish.RDF.Graph: instance (Eq lb, Eq gr) => Eq (LookupFormula lb gr)
+ Swish.RDF.Graph: instance Eq RDFLabel
+ Swish.RDF.Graph: instance Foldable NSGraph
+ Swish.RDF.Graph: instance FromRDFLabel Bool
+ Swish.RDF.Graph: instance FromRDFLabel Char
+ Swish.RDF.Graph: instance FromRDFLabel Day
+ Swish.RDF.Graph: instance FromRDFLabel Double
+ Swish.RDF.Graph: instance FromRDFLabel Float
+ Swish.RDF.Graph: instance FromRDFLabel Int
+ Swish.RDF.Graph: instance FromRDFLabel Integer
+ Swish.RDF.Graph: instance FromRDFLabel QName
+ Swish.RDF.Graph: instance FromRDFLabel RDFLabel
+ Swish.RDF.Graph: instance FromRDFLabel ScopedName
+ Swish.RDF.Graph: instance FromRDFLabel String
+ Swish.RDF.Graph: instance FromRDFLabel URI
+ Swish.RDF.Graph: instance FromRDFLabel UTCTime
+ Swish.RDF.Graph: instance Functor NSGraph
+ Swish.RDF.Graph: instance IsString RDFLabel
+ Swish.RDF.Graph: instance Label RDFLabel
+ Swish.RDF.Graph: instance Label lb => Eq (NSGraph lb)
+ Swish.RDF.Graph: instance Label lb => LDGraph NSGraph lb
+ Swish.RDF.Graph: instance Label lb => LookupEntryClass (LookupFormula lb (NSGraph lb)) lb (NSGraph lb)
+ Swish.RDF.Graph: instance Label lb => Monoid (NSGraph lb)
+ Swish.RDF.Graph: instance Label lb => Show (LookupFormula lb (NSGraph lb))
+ Swish.RDF.Graph: instance Label lb => Show (NSGraph lb)
+ Swish.RDF.Graph: instance LookupEntryClass RevNamespace URI (Maybe Text)
+ Swish.RDF.Graph: instance Ord RDFLabel
+ Swish.RDF.Graph: instance Show RDFLabel
+ Swish.RDF.Graph: instance ToRDFLabel Bool
+ Swish.RDF.Graph: instance ToRDFLabel Char
+ Swish.RDF.Graph: instance ToRDFLabel Day
+ Swish.RDF.Graph: instance ToRDFLabel Double
+ Swish.RDF.Graph: instance ToRDFLabel Float
+ Swish.RDF.Graph: instance ToRDFLabel Int
+ Swish.RDF.Graph: instance ToRDFLabel Integer
+ Swish.RDF.Graph: instance ToRDFLabel QName
+ Swish.RDF.Graph: instance ToRDFLabel RDFLabel
+ Swish.RDF.Graph: instance ToRDFLabel ScopedName
+ Swish.RDF.Graph: instance ToRDFLabel String
+ Swish.RDF.Graph: instance ToRDFLabel URI
+ Swish.RDF.Graph: instance ToRDFLabel UTCTime
+ Swish.RDF.Graph: instance Traversable NSGraph
+ Swish.RDF.Graph: isBlank :: RDFLabel -> Bool
+ Swish.RDF.Graph: isDatatyped :: ScopedName -> RDFLabel -> Bool
+ Swish.RDF.Graph: isLiteral :: RDFLabel -> Bool
+ Swish.RDF.Graph: isMemberProp :: RDFLabel -> Bool
+ Swish.RDF.Graph: isQueryVar :: RDFLabel -> Bool
+ Swish.RDF.Graph: isTypedLiteral :: RDFLabel -> Bool
+ Swish.RDF.Graph: isUntypedLiteral :: RDFLabel -> Bool
+ Swish.RDF.Graph: isUri :: RDFLabel -> Bool
+ Swish.RDF.Graph: isXMLLiteral :: RDFLabel -> Bool
+ Swish.RDF.Graph: labelHash :: Label lb => Int -> lb -> Int
+ Swish.RDF.Graph: labelIsVar :: Label lb => lb -> Bool
+ Swish.RDF.Graph: labels :: LDGraph lg lb => lg lb -> [lb]
+ Swish.RDF.Graph: makeBlank :: RDFLabel -> RDFLabel
+ Swish.RDF.Graph: makeLabel :: Label lb => String -> lb
+ Swish.RDF.Graph: maplist :: Label lb => [lb] -> [lb] -> (lb -> lb) -> [(lb, lb)] -> [(lb, lb)]
+ Swish.RDF.Graph: mapnode :: Label lb => [lb] -> [lb] -> (lb -> lb) -> lb -> lb
+ Swish.RDF.Graph: merge :: Label lb => NSGraph lb -> NSGraph lb -> NSGraph lb
+ Swish.RDF.Graph: namespaces :: NSGraph lb -> NamespaceMap
+ Swish.RDF.Graph: newNode :: Label lb => lb -> [lb] -> lb
+ Swish.RDF.Graph: newNodes :: Label lb => lb -> [lb] -> [lb]
+ Swish.RDF.Graph: nodes :: LDGraph lg lb => lg lb -> [lb]
+ Swish.RDF.Graph: quote :: Bool -> String -> String
+ Swish.RDF.Graph: quoteT :: Bool -> Text -> Text
+ Swish.RDF.Graph: remapLabelList :: Label lb => [lb] -> [lb] -> [(lb, lb)]
+ Swish.RDF.Graph: remapLabels :: Label lb => [lb] -> [lb] -> (lb -> lb) -> NSGraph lb -> NSGraph lb
+ Swish.RDF.Graph: resLogImplies :: RDFLabel
+ Swish.RDF.Graph: resOwlSameAs :: RDFLabel
+ Swish.RDF.Graph: resRdf1 :: RDFLabel
+ Swish.RDF.Graph: resRdf2 :: RDFLabel
+ Swish.RDF.Graph: resRdfAbout :: RDFLabel
+ Swish.RDF.Graph: resRdfAlt :: RDFLabel
+ Swish.RDF.Graph: resRdfBag :: RDFLabel
+ Swish.RDF.Graph: resRdfDatatype :: RDFLabel
+ Swish.RDF.Graph: resRdfDescription :: RDFLabel
+ Swish.RDF.Graph: resRdfFirst :: RDFLabel
+ Swish.RDF.Graph: resRdfID :: RDFLabel
+ Swish.RDF.Graph: resRdfLi :: RDFLabel
+ Swish.RDF.Graph: resRdfList :: RDFLabel
+ Swish.RDF.Graph: resRdfNil :: RDFLabel
+ Swish.RDF.Graph: resRdfNodeID :: RDFLabel
+ Swish.RDF.Graph: resRdfObject :: RDFLabel
+ Swish.RDF.Graph: resRdfParseType :: RDFLabel
+ Swish.RDF.Graph: resRdfPredicate :: RDFLabel
+ Swish.RDF.Graph: resRdfProperty :: RDFLabel
+ Swish.RDF.Graph: resRdfRDF :: RDFLabel
+ Swish.RDF.Graph: resRdfResource :: RDFLabel
+ Swish.RDF.Graph: resRdfRest :: RDFLabel
+ Swish.RDF.Graph: resRdfSeq :: RDFLabel
+ Swish.RDF.Graph: resRdfStatement :: RDFLabel
+ Swish.RDF.Graph: resRdfSubject :: RDFLabel
+ Swish.RDF.Graph: resRdfType :: RDFLabel
+ Swish.RDF.Graph: resRdfValue :: RDFLabel
+ Swish.RDF.Graph: resRdfXMLLiteral :: RDFLabel
+ Swish.RDF.Graph: resRdfdConstraint :: RDFLabel
+ Swish.RDF.Graph: resRdfdGeneralRestriction :: RDFLabel
+ Swish.RDF.Graph: resRdfdMaxCardinality :: RDFLabel
+ Swish.RDF.Graph: resRdfdOnProperties :: RDFLabel
+ Swish.RDF.Graph: resRdfn :: Word32 -> RDFLabel
+ Swish.RDF.Graph: resRdfsClass :: RDFLabel
+ Swish.RDF.Graph: resRdfsComment :: RDFLabel
+ Swish.RDF.Graph: resRdfsContainer :: RDFLabel
+ Swish.RDF.Graph: resRdfsContainerMembershipProperty :: RDFLabel
+ Swish.RDF.Graph: resRdfsDatatype :: RDFLabel
+ Swish.RDF.Graph: resRdfsDomain :: RDFLabel
+ Swish.RDF.Graph: resRdfsIsDefinedBy :: RDFLabel
+ Swish.RDF.Graph: resRdfsLabel :: RDFLabel
+ Swish.RDF.Graph: resRdfsLiteral :: RDFLabel
+ Swish.RDF.Graph: resRdfsMember :: RDFLabel
+ Swish.RDF.Graph: resRdfsRange :: RDFLabel
+ Swish.RDF.Graph: resRdfsResource :: RDFLabel
+ Swish.RDF.Graph: resRdfsSeeAlso :: RDFLabel
+ Swish.RDF.Graph: resRdfsSubClassOf :: RDFLabel
+ Swish.RDF.Graph: resRdfsSubPropertyOf :: RDFLabel
+ Swish.RDF.Graph: setArcs :: LDGraph lg lb => lg lb -> [Arc lb] -> lg lb
+ Swish.RDF.Graph: setFormula :: Label lb => Formula lb -> NSGraph lb -> NSGraph lb
+ Swish.RDF.Graph: setFormulae :: FormulaMap lb -> NSGraph lb -> NSGraph lb
+ Swish.RDF.Graph: setNamespaces :: NamespaceMap -> NSGraph lb -> NSGraph lb
+ Swish.RDF.Graph: statements :: NSGraph lb -> [Arc lb]
+ Swish.RDF.Graph: toRDFGraph :: [RDFTriple] -> RDFGraph
+ Swish.RDF.Graph: toRDFLabel :: ToRDFLabel a => a -> RDFLabel
+ Swish.RDF.Graph: toRDFTriple :: (ToRDFLabel s, ToRDFLabel p, ToRDFLabel o) => s -> p -> o -> RDFTriple
+ Swish.RDF.Graph: type Formula lb = LookupFormula lb (NSGraph lb)
+ Swish.RDF.Graph: type FormulaMap lb = LookupMap (LookupFormula lb (NSGraph lb))
+ Swish.RDF.Graph: type NamespaceMap = LookupMap Namespace
+ Swish.RDF.Graph: type RDFGraph = NSGraph RDFLabel
+ Swish.RDF.Graph: type RDFTriple = Arc RDFLabel
+ Swish.RDF.Graph: type RevNamespaceMap = LookupMap RevNamespace
+ Swish.RDF.Graph: type Selector lb = Arc lb -> Bool
+ Swish.RDF.Graph: update :: LDGraph lg lb => ([Arc lb] -> [Arc lb]) -> lg lb -> lg lb
+ Swish.RDF.GraphShowLines: instance ShowLines RDFGraph
+ Swish.RDF.Parser.N3: N3State :: RDFGraph -> RDFLabel -> NamespaceMap -> SpecialMap -> Word32 -> [Text] -> Bool -> N3State
+ Swish.RDF.Parser.N3: allowLocalNames :: N3State -> Bool
+ Swish.RDF.Parser.N3: data N3State
+ Swish.RDF.Parser.N3: document :: N3Parser RDFGraph
+ Swish.RDF.Parser.N3: getPrefix :: N3Parser ()
+ Swish.RDF.Parser.N3: graphState :: N3State -> RDFGraph
+ Swish.RDF.Parser.N3: keywordsList :: N3State -> [Text]
+ Swish.RDF.Parser.N3: lexUriRef :: N3Parser URI
+ Swish.RDF.Parser.N3: n3symbol :: N3Parser ScopedName
+ Swish.RDF.Parser.N3: newBlankNode :: N3Parser RDFLabel
+ Swish.RDF.Parser.N3: nodeGen :: N3State -> Word32
+ Swish.RDF.Parser.N3: parseAbsURIrefFromText :: Text -> Either String URI
+ Swish.RDF.Parser.N3: parseAltFromText :: String -> String -> Text -> Either String String
+ Swish.RDF.Parser.N3: parseAnyfromText :: N3Parser a -> Maybe QName -> Text -> Either String a
+ Swish.RDF.Parser.N3: parseLexURIrefFromText :: Text -> Either String URI
+ Swish.RDF.Parser.N3: parseN3 :: Text -> Maybe QName -> ParseResult
+ Swish.RDF.Parser.N3: parseN3fromText :: Text -> ParseResult
+ Swish.RDF.Parser.N3: parseNameFromText :: Text -> Either String String
+ Swish.RDF.Parser.N3: parseTextFromText :: String -> Text -> Either String String
+ Swish.RDF.Parser.N3: parseURIref2FromText :: Text -> Either String ScopedName
+ Swish.RDF.Parser.N3: prefixUris :: N3State -> NamespaceMap
+ Swish.RDF.Parser.N3: quickVariable :: N3Parser RDFLabel
+ Swish.RDF.Parser.N3: subgraph :: RDFLabel -> N3Parser RDFGraph
+ Swish.RDF.Parser.N3: syntaxUris :: N3State -> SpecialMap
+ Swish.RDF.Parser.N3: thisNode :: N3State -> RDFLabel
+ Swish.RDF.Parser.N3: type N3Parser a = Parser N3State a
+ Swish.RDF.Parser.N3: type ParseResult = Either String RDFGraph
+ Swish.RDF.Parser.N3: type SpecialMap = LookupMap (String, ScopedName)
+ Swish.RDF.Parser.NTriples: parseNT :: Text -> ParseResult
+ Swish.RDF.Parser.NTriples: type ParseResult = Either String RDFGraph
+ Swish.RDF.Parser.Turtle: instance Show TurtleState
+ Swish.RDF.Parser.Turtle: parseTurtle :: Text -> Maybe URI -> ParseResult
+ Swish.RDF.Parser.Turtle: parseTurtlefromText :: Text -> ParseResult
+ Swish.RDF.Parser.Turtle: type ParseResult = Either String RDFGraph
+ Swish.RDF.Parser.Utils: appendURIs :: URI -> URI -> Either String URI
+ Swish.RDF.Parser.Utils: char :: Char -> Parser s Char
+ Swish.RDF.Parser.Utils: endBy :: Parser s a -> Parser s b -> Parser s [a]
+ Swish.RDF.Parser.Utils: eoln :: Parser s ()
+ Swish.RDF.Parser.Utils: fullStop :: Parser s ()
+ Swish.RDF.Parser.Utils: hex4 :: Parser a Char
+ Swish.RDF.Parser.Utils: hex8 :: Parser a Char
+ Swish.RDF.Parser.Utils: ichar :: Char -> Parser s ()
+ Swish.RDF.Parser.Utils: ignore :: Applicative f => f a -> f ()
+ Swish.RDF.Parser.Utils: isymbol :: String -> Parser s ()
+ Swish.RDF.Parser.Utils: lexeme :: Parser s a -> Parser s a
+ Swish.RDF.Parser.Utils: manyTill :: Parser s a -> Parser s b -> Parser s [a]
+ Swish.RDF.Parser.Utils: noneOf :: String -> Parser s Char
+ Swish.RDF.Parser.Utils: notFollowedBy :: (Char -> Bool) -> Parser s ()
+ Swish.RDF.Parser.Utils: prefixTable :: [Namespace]
+ Swish.RDF.Parser.Utils: runParserWithError :: Parser a b -> a -> Text -> Either String b
+ Swish.RDF.Parser.Utils: sepEndBy :: Parser s a -> Parser s b -> Parser s [a]
+ Swish.RDF.Parser.Utils: sepEndBy1 :: Parser s a -> Parser s b -> Parser s [a]
+ Swish.RDF.Parser.Utils: skipMany :: Parser s a -> Parser s ()
+ Swish.RDF.Parser.Utils: skipMany1 :: Parser s a -> Parser s ()
+ Swish.RDF.Parser.Utils: specialTable :: Maybe ScopedName -> [(String, ScopedName)]
+ Swish.RDF.Parser.Utils: string :: String -> Parser s String
+ Swish.RDF.Parser.Utils: stringT :: Text -> Parser s Text
+ Swish.RDF.Parser.Utils: symbol :: String -> Parser s String
+ Swish.RDF.Parser.Utils: type ParseResult = Either String RDFGraph
+ Swish.RDF.Parser.Utils: type SpecialMap = LookupMap (String, ScopedName)
+ Swish.RDF.Parser.Utils: whiteSpace :: Parser s ()
+ Swish.RDF.Proof: instance (Label lb, LDGraph lg lb) => Expression (lg lb)
+ Swish.RDF.Proof: makeRDFProof :: [RDFRuleset] -> RDFFormula -> RDFFormula -> [RDFProofStep] -> RDFProof
+ Swish.RDF.Proof: makeRDFProofStep :: RDFRule -> [RDFFormula] -> RDFFormula -> RDFProofStep
+ Swish.RDF.Proof: makeRdfInstanceEntailmentRule :: ScopedName -> [RDFLabel] -> RDFRule
+ Swish.RDF.Proof: makeRdfSimpleEntailmentRule :: ScopedName -> RDFRule
+ Swish.RDF.Proof: makeRdfSubgraphEntailmentRule :: ScopedName -> RDFRule
+ Swish.RDF.Proof: type RDFProof = Proof RDFGraph
+ Swish.RDF.Proof: type RDFProofStep = Step RDFGraph
+ Swish.RDF.ProofContext: rulesetRDF :: RDFRuleset
+ Swish.RDF.ProofContext: rulesetRDFD :: RDFRuleset
+ Swish.RDF.ProofContext: rulesetRDFS :: RDFRuleset
+ Swish.RDF.Query: allp :: [a -> Bool] -> a -> Bool
+ Swish.RDF.Query: anyp :: [a -> Bool] -> a -> Bool
+ Swish.RDF.Query: rdfFindArcs :: (RDFTriple -> Bool) -> RDFGraph -> [RDFTriple]
+ Swish.RDF.Query: rdfFindList :: RDFGraph -> RDFLabel -> [RDFLabel]
+ Swish.RDF.Query: rdfFindPredInt :: RDFLabel -> RDFLabel -> RDFGraph -> [Integer]
+ Swish.RDF.Query: rdfFindPredVal :: RDFLabel -> RDFLabel -> RDFGraph -> [RDFLabel]
+ Swish.RDF.Query: rdfFindValSubj :: RDFLabel -> RDFLabel -> RDFGraph -> [RDFLabel]
+ Swish.RDF.Query: rdfObjEq :: RDFLabel -> RDFTriple -> Bool
+ Swish.RDF.Query: rdfPredEq :: RDFLabel -> RDFTriple -> Bool
+ Swish.RDF.Query: rdfQueryBack :: RDFGraph -> RDFGraph -> [[RDFVarBinding]]
+ Swish.RDF.Query: rdfQueryBackFilter :: RDFVarBindingFilter -> [[RDFVarBinding]] -> [[RDFVarBinding]]
+ Swish.RDF.Query: rdfQueryBackModify :: VarBindingModify a b -> [[VarBinding a b]] -> [[VarBinding a b]]
+ Swish.RDF.Query: rdfQueryBackSubs :: [[RDFVarBinding]] -> RDFGraph -> [[(RDFGraph, [RDFLabel])]]
+ Swish.RDF.Query: rdfQueryBackSubsBlank :: [[RDFVarBinding]] -> RDFGraph -> [[RDFGraph]]
+ Swish.RDF.Query: rdfQueryFilter :: RDFVarBindingFilter -> [RDFVarBinding] -> [RDFVarBinding]
+ Swish.RDF.Query: rdfQueryFind :: RDFGraph -> RDFGraph -> [RDFVarBinding]
+ Swish.RDF.Query: rdfQueryInstance :: RDFGraph -> RDFGraph -> [RDFVarBinding]
+ Swish.RDF.Query: rdfQuerySubs :: [RDFVarBinding] -> RDFGraph -> [RDFGraph]
+ Swish.RDF.Query: rdfQuerySubs2 :: RDFVarBinding -> RDFGraph -> (RDFGraph, [RDFLabel])
+ Swish.RDF.Query: rdfQuerySubsAll :: [RDFVarBinding] -> RDFGraph -> [(RDFGraph, [RDFLabel])]
+ Swish.RDF.Query: rdfQuerySubsBlank :: [RDFVarBinding] -> RDFGraph -> [RDFGraph]
+ Swish.RDF.Query: rdfSubjEq :: RDFLabel -> RDFTriple -> Bool
+ Swish.RDF.Ruleset: GraphClosure :: ScopedName -> [Arc lb] -> [Arc lb] -> VarBindingModify lb lb -> GraphClosure lb
+ Swish.RDF.Ruleset: data GraphClosure lb
+ Swish.RDF.Ruleset: graphClosureBwdApply :: GraphClosure RDFLabel -> RDFGraph -> [[RDFGraph]]
+ Swish.RDF.Ruleset: graphClosureFwdApply :: GraphClosure RDFLabel -> [RDFGraph] -> [RDFGraph]
+ Swish.RDF.Ruleset: instance Label lb => Eq (GraphClosure lb)
+ Swish.RDF.Ruleset: instance Label lb => Show (GraphClosure lb)
+ Swish.RDF.Ruleset: makeGraphClosureRule :: GraphClosure RDFLabel -> Rule RDFGraph
+ Swish.RDF.Ruleset: makeN3ClosureAllocatorRule :: Namespace -> LName -> Builder -> Builder -> RDFVarBindingModify -> ([RDFLabel] -> RDFVarBindingModify) -> RDFRule
+ Swish.RDF.Ruleset: makeN3ClosureModifyRule :: Namespace -> LName -> Builder -> Builder -> RDFVarBindingModify -> RDFVarBindingModify -> RDFRule
+ Swish.RDF.Ruleset: makeN3ClosureRule :: Namespace -> LName -> Builder -> Builder -> RDFVarBindingModify -> RDFRule
+ Swish.RDF.Ruleset: makeN3ClosureSimpleRule :: Namespace -> LName -> Builder -> Builder -> RDFRule
+ Swish.RDF.Ruleset: makeNodeAllocTo :: RDFLabel -> RDFLabel -> [RDFLabel] -> RDFVarBindingModify
+ Swish.RDF.Ruleset: makeRDFClosureRule :: ScopedName -> [RDFGraph] -> RDFGraph -> RDFVarBindingModify -> RDFRule
+ Swish.RDF.Ruleset: makeRDFFormula :: Namespace -> LName -> Builder -> RDFFormula
+ Swish.RDF.Ruleset: makeRDFGraphFromN3Builder :: Builder -> RDFGraph
+ Swish.RDF.Ruleset: nameGraphRule :: GraphClosure lb -> ScopedName
+ Swish.RDF.Ruleset: nullRDFFormula :: Formula RDFGraph
+ Swish.RDF.Ruleset: ruleAnt :: GraphClosure lb -> [Arc lb]
+ Swish.RDF.Ruleset: ruleCon :: GraphClosure lb -> [Arc lb]
+ Swish.RDF.Ruleset: ruleModify :: GraphClosure lb -> VarBindingModify lb lb
+ Swish.RDF.Ruleset: type RDFClosure = GraphClosure RDFLabel
+ Swish.RDF.Ruleset: type RDFFormula = Formula RDFGraph
+ Swish.RDF.Ruleset: type RDFRule = Rule RDFGraph
+ Swish.RDF.Ruleset: type RDFRuleMap = RuleMap RDFGraph
+ Swish.RDF.Ruleset: type RDFRuleset = Ruleset RDFGraph
+ Swish.RDF.Ruleset: type RDFRulesetMap = RulesetMap RDFGraph
+ Swish.RDF.VarBinding: nullRDFVarBinding :: RDFVarBinding
+ Swish.RDF.VarBinding: rdfVarBindingBlank :: RDFLabel -> RDFVarBindingFilter
+ Swish.RDF.VarBinding: rdfVarBindingDatatyped :: RDFLabel -> RDFLabel -> RDFVarBindingFilter
+ Swish.RDF.VarBinding: rdfVarBindingLiteral :: RDFLabel -> RDFVarBindingFilter
+ Swish.RDF.VarBinding: rdfVarBindingMemberProp :: RDFLabel -> RDFVarBindingFilter
+ Swish.RDF.VarBinding: rdfVarBindingTypedLiteral :: RDFLabel -> RDFVarBindingFilter
+ Swish.RDF.VarBinding: rdfVarBindingUntypedLiteral :: RDFLabel -> RDFVarBindingFilter
+ Swish.RDF.VarBinding: rdfVarBindingUriRef :: RDFLabel -> RDFVarBindingFilter
+ Swish.RDF.VarBinding: rdfVarBindingXMLLiteral :: RDFLabel -> RDFVarBindingFilter
+ Swish.RDF.VarBinding: type RDFOpenVarBindingModify = OpenVarBindingModify RDFLabel RDFLabel
+ Swish.RDF.VarBinding: type RDFOpenVarBindingModifyMap = LookupMap RDFOpenVarBindingModify
+ Swish.RDF.VarBinding: type RDFVarBinding = VarBinding RDFLabel RDFLabel
+ Swish.RDF.VarBinding: type RDFVarBindingFilter = VarBindingFilter RDFLabel RDFLabel
+ Swish.RDF.VarBinding: type RDFVarBindingModify = VarBindingModify RDFLabel RDFLabel
+ Swish.RDF.Vocabulary: data LanguageTag
+ Swish.RDF.Vocabulary: fromLangTag :: LanguageTag -> Text
+ Swish.RDF.Vocabulary: instance Eq LanguageTag
+ Swish.RDF.Vocabulary: instance IsString LanguageTag
+ Swish.RDF.Vocabulary: instance Show LanguageTag
+ Swish.RDF.Vocabulary: isBaseLang :: LanguageTag -> LanguageTag -> Bool
+ Swish.RDF.Vocabulary: toLangTag :: Text -> Maybe LanguageTag
+ Swish.Rule: Formula :: ScopedName -> ex -> Formula ex
+ Swish.Rule: Rule :: ScopedName -> ([ex] -> [ex]) -> (ex -> [[ex]]) -> ([ex] -> ex -> Bool) -> Rule ex
+ Swish.Rule: bwdApply :: Rule ex -> ex -> [[ex]]
+ Swish.Rule: bwdCheckInference :: Eq ex => Rule ex -> [ex] -> ex -> Bool
+ Swish.Rule: checkInference :: Rule ex -> [ex] -> ex -> Bool
+ Swish.Rule: class Eq ex => Expression ex
+ Swish.Rule: data Formula ex
+ Swish.Rule: data Rule ex
+ Swish.Rule: formExpr :: Formula ex -> ex
+ Swish.Rule: formName :: Formula ex -> ScopedName
+ Swish.Rule: fwdApply :: Rule ex -> [ex] -> [ex]
+ Swish.Rule: fwdCheckInference :: Eq ex => Rule ex -> [ex] -> ex -> Bool
+ Swish.Rule: instance Eq (Formula ex)
+ Swish.Rule: instance Eq (Rule ex)
+ Swish.Rule: instance LookupEntryClass (Formula ex) ScopedName (Formula ex)
+ Swish.Rule: instance LookupEntryClass (Rule ex) ScopedName (Rule ex)
+ Swish.Rule: instance Ord (Formula ex)
+ Swish.Rule: instance Ord (Rule ex)
+ Swish.Rule: instance Show (Rule ex)
+ Swish.Rule: instance Show ex => Show (Formula ex)
+ Swish.Rule: isValid :: Expression ex => ex -> Bool
+ Swish.Rule: nullFormula :: Formula ex
+ Swish.Rule: nullRule :: Rule ex
+ Swish.Rule: nullSN :: LName -> ScopedName
+ Swish.Rule: nullScope :: Namespace
+ Swish.Rule: ruleName :: Rule ex -> ScopedName
+ Swish.Rule: showsFormula :: ShowLines ex => String -> Formula ex -> ShowS
+ Swish.Rule: showsFormulae :: ShowLines ex => String -> [Formula ex] -> String -> ShowS
+ Swish.Rule: showsWidth :: Int -> String -> ShowS
+ Swish.Rule: type RuleMap ex = LookupMap (Rule ex)
+ Swish.Ruleset: Ruleset :: Namespace -> [Formula ex] -> [Rule ex] -> Ruleset ex
+ Swish.Ruleset: data Ruleset ex
+ Swish.Ruleset: getContextAxiom :: ScopedName -> Formula ex -> [Ruleset ex] -> Formula ex
+ Swish.Ruleset: getContextRule :: ScopedName -> Rule ex -> [Ruleset ex] -> Rule ex
+ Swish.Ruleset: getMaybeContextAxiom :: ScopedName -> [Ruleset ex] -> Maybe (Formula ex)
+ Swish.Ruleset: getMaybeContextRule :: ScopedName -> [Ruleset ex] -> Maybe (Rule ex)
+ Swish.Ruleset: getRulesetAxiom :: ScopedName -> Ruleset ex -> Maybe (Formula ex)
+ Swish.Ruleset: getRulesetAxioms :: Ruleset ex -> [Formula ex]
+ Swish.Ruleset: getRulesetNamespace :: Ruleset ex -> Namespace
+ Swish.Ruleset: getRulesetRule :: ScopedName -> Ruleset ex -> Maybe (Rule ex)
+ Swish.Ruleset: getRulesetRules :: Ruleset ex -> [Rule ex]
+ Swish.Ruleset: instance Eq (Ruleset ex)
+ Swish.Ruleset: instance LookupEntryClass (Ruleset ex) Namespace (Ruleset ex)
+ Swish.Ruleset: makeRuleset :: Namespace -> [Formula ex] -> [Rule ex] -> Ruleset ex
+ Swish.Ruleset: rsAxioms :: Ruleset ex -> [Formula ex]
+ Swish.Ruleset: rsNamespace :: Ruleset ex -> Namespace
+ Swish.Ruleset: rsRules :: Ruleset ex -> [Rule ex]
+ Swish.Ruleset: type RulesetMap ex = LookupMap (Ruleset ex)
+ Swish.Script: parseScriptFromText :: Maybe QName -> Text -> Either String [SwishStateIO ()]
+ Swish.VarBinding: VarBinding :: (a -> Maybe b) -> [(a, b)] -> Bool -> VarBinding a b
+ Swish.VarBinding: VarBindingFilter :: ScopedName -> [a] -> (VarBinding a b -> Bool) -> VarBindingFilter a b
+ Swish.VarBinding: VarBindingModify :: ScopedName -> ([VarBinding a b] -> [VarBinding a b]) -> [a] -> [[a]] -> VarBindingModify a b
+ Swish.VarBinding: addVarBinding :: (Eq a, Show a, Eq b, Show b) => a -> b -> VarBinding a b -> VarBinding a b
+ Swish.VarBinding: applyVarBinding :: VarBinding a a -> a -> a
+ Swish.VarBinding: boundVars :: VarBinding a b -> [a]
+ Swish.VarBinding: composeSequence :: Eq a => [VarBindingModify a b] -> Maybe (VarBindingModify a b)
+ Swish.VarBinding: data VarBinding a b
+ Swish.VarBinding: data VarBindingFilter a b
+ Swish.VarBinding: data VarBindingModify a b
+ Swish.VarBinding: findComposition :: Eq a => [VarBindingModify a b] -> [a] -> Maybe (VarBindingModify a b)
+ Swish.VarBinding: findCompositions :: Eq a => [VarBindingModify a b] -> [a] -> [VarBindingModify a b]
+ Swish.VarBinding: instance (Eq a, Eq b) => Eq (VarBinding a b)
+ Swish.VarBinding: instance (Show a, Show b) => Show (VarBinding a b)
+ Swish.VarBinding: instance LookupEntryClass (OpenVarBindingModify a b) ScopedName (OpenVarBindingModify a b)
+ Swish.VarBinding: instance LookupEntryClass (VarBindingModify a b) ScopedName (VarBindingModify a b)
+ Swish.VarBinding: instance Show (OpenVarBindingModify a b)
+ Swish.VarBinding: joinVarBindings :: Eq a => VarBinding a b -> VarBinding a b -> VarBinding a b
+ Swish.VarBinding: makeVarBinding :: (Eq a, Show a, Eq b, Show b) => [(a, b)] -> VarBinding a b
+ Swish.VarBinding: makeVarCompareFilter :: ScopedName -> (b -> b -> Bool) -> a -> a -> VarBindingFilter a b
+ Swish.VarBinding: makeVarFilterModify :: VarBindingFilter a b -> VarBindingModify a b
+ Swish.VarBinding: makeVarTestFilter :: ScopedName -> (b -> Bool) -> a -> VarBindingFilter a b
+ Swish.VarBinding: nullVarBinding :: VarBinding a b
+ Swish.VarBinding: nullVarBindingModify :: OpenVarBindingModify a b
+ Swish.VarBinding: subBinding :: (Eq a, Eq b) => VarBinding a b -> VarBinding a b -> Bool
+ Swish.VarBinding: type OpenVarBindingModify lb vn = [lb] -> VarBindingModify lb vn
+ Swish.VarBinding: varBindingId :: VarBindingModify a b
+ Swish.VarBinding: varFilterConjunction :: Eq a => [VarBindingFilter a b] -> VarBindingFilter a b
+ Swish.VarBinding: varFilterDisjunction :: Eq a => [VarBindingFilter a b] -> VarBindingFilter a b
+ Swish.VarBinding: varFilterEQ :: Eq b => a -> a -> VarBindingFilter a b
+ Swish.VarBinding: varFilterNE :: Eq b => a -> a -> VarBindingFilter a b
+ Swish.VarBinding: vbEnum :: VarBinding a b -> [(a, b)]
+ Swish.VarBinding: vbMap :: VarBinding a b -> a -> Maybe b
+ Swish.VarBinding: vbNull :: VarBinding a b -> Bool
+ Swish.VarBinding: vbfName :: VarBindingFilter a b -> ScopedName
+ Swish.VarBinding: vbfTest :: VarBindingFilter a b -> VarBinding a b -> Bool
+ Swish.VarBinding: vbfVocab :: VarBindingFilter a b -> [a]
+ Swish.VarBinding: vbmApply :: VarBindingModify a b -> [VarBinding a b] -> [VarBinding a b]
+ Swish.VarBinding: vbmCompatibility :: Eq a => VarBindingModify a b -> [a] -> Maybe [a]
+ Swish.VarBinding: vbmCompose :: Eq a => VarBindingModify a b -> VarBindingModify a b -> Maybe (VarBindingModify a b)
+ Swish.VarBinding: vbmName :: VarBindingModify a b -> ScopedName
+ Swish.VarBinding: vbmUsage :: VarBindingModify a b -> [[a]]
+ Swish.VarBinding: vbmVocab :: VarBindingModify a b -> [a]
- Swish.RDF.Vocabulary: namespaceXsdType :: Text -> Namespace
+ Swish.RDF.Vocabulary: namespaceXsdType :: LName -> Namespace
- Swish.RDF.Vocabulary: swishName :: Text -> ScopedName
+ Swish.RDF.Vocabulary: swishName :: LName -> ScopedName
- Swish.RDF.Vocabulary.RDF: rdfn :: Int -> ScopedName
+ Swish.RDF.Vocabulary.RDF: rdfn :: Word32 -> ScopedName
- Swish.RDF.Vocabulary.XSD: xsdType :: Text -> ScopedName
+ Swish.RDF.Vocabulary.XSD: xsdType :: LName -> ScopedName

Files

src/Data/Interned/URI.hs view
@@ -1,16 +1,18 @@-{-# LANGUAGE TypeFamilies, FlexibleInstances #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE FlexibleInstances #-}  -------------------------------------------------------------------------------- --  See end of this file for licence information. -------------------------------------------------------------------------------- -- | --  Module      :  URI---  Copyright   :  (c) 2011 Douglas Burke+--  Copyright   :  (c) 2011, 2012 Douglas Burke --  License     :  GPL V2 -- --  Maintainer  :  Douglas Burke --  Stability   :  experimental---  Portability :  TypeFamilies, FlexibleInstances+--  Portability :  CPP, TypeFamilies, FlexibleInstances -- --  Support interning URIs. --@@ -27,7 +29,10 @@  import Network.URI --- Could look at adding UNPACK statements before each component+-- Could look at adding UNPACK statements before the Int component++-- | An interned URI. The hashing is based on the+-- reversed URI (as a string). data InternedURI = InternedURI !Int !URI  instance IsString InternedURI where@@ -50,7 +55,10 @@   data Description InternedURI = DU !URI deriving (Eq) -- DU {-# UNPACK #-} !URI deriving (Eq)    describe = DU   identify = InternedURI+#if MIN_VERSION_intern(0,9,0)+#else   identity (InternedURI i _) = i+#endif   cache = iuCache  instance Uninternable InternedURI where@@ -68,7 +76,8 @@  -------------------------------------------------------------------------------- -----  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke --  All rights reserved. -- --  This file is part of Swish.
+ src/Data/LookupMap.hs view
@@ -0,0 +1,368 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveFoldable #-}+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE DeriveTraversable #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE OverloadedStrings #-}++--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  LookupMap+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke+--  License     :  GPL V2+--+--  Maintainer  :  Douglas Burke+--  Stability   :  experimental+--  Portability :  A lot of LANGUAGE extensions...+--+--  This module defines a lookup table format and associated functions+--  used by the graph matching code.+--+--------------------------------------------------------------------------------++------------------------------------------------------------+--  Generic list-of-pairs lookup functions+------------------------------------------------------------++module Data.LookupMap+    ( LookupEntryClass(..), LookupMap(..)+    , emptyLookupMap, makeLookupMap, listLookupMap+    , reverseLookupMap+    , keyOrder+    , mapFind, mapFindMaybe, mapContains+    , mapReplace, mapReplaceAll, mapReplaceMap+    , mapAdd, mapAddIfNew+    , mapDelete, mapDeleteAll+    , mapEq, mapKeys, mapVals+    , mapMerge+    )+    where++import Control.Arrow (first, second)++import Data.Maybe (fromMaybe)+import Data.Function (on)+import Data.Ord (comparing)++import Swish.Utils.ListHelpers (equiv)++import qualified Data.Foldable as F+import qualified Data.Traversable as T+import qualified Data.List as L++#if defined(__GLASGOW_HASKELL__) && (__GLASGOW_HASKELL__ >= 701)+import Data.Tuple (swap)+#else+swap :: (a,b) -> (b,a)+swap (a,b) = (b,a)+#endif++------------------------------------------------------------+--  Class for lookup map entries+------------------------------------------------------------++-- |@LookupEntryClass@ defines essential functions of any datatype+--  that can be used to make a 'LookupMap'.+--+--  Minimal definition: @newEntry@ and @keyVal@+--+class (Eq k, Show k) => LookupEntryClass a k v | a -> k, a -> v+    where+        newEntry    :: (k,v) -> a+        keyVal      :: a -> (k,v)+        +        entryKey    :: a -> k+        entryKey = fst . keyVal+        +        entryVal    :: a -> v+        entryVal = snd . keyVal+                             +        entryEq     :: (Eq v) => a -> a -> Bool+        entryEq e1 e2 = keyVal e1 == keyVal e2+        +        entryShow   :: (Show v) => a -> String+        entryShow e = show k ++ ":" ++ show v where (k,v) = keyVal e+                                                    +        kmap :: (LookupEntryClass a2 k2 v) => (k -> k2) -> a -> a2+        kmap f = newEntry . first f . keyVal+        +        vmap :: (LookupEntryClass a2 k v2) => (v -> v2) -> a -> a2+        vmap f = newEntry . second f . keyVal++-- |Predefine a pair of appropriate values as a valid lookup table entry+--  (i.e. an instance of LookupEntryClass).+--+instance (Eq k, Show k) => LookupEntryClass (k,v) k v where+    newEntry = id+    keyVal   = id++--  Note:  the class constraint that a is an instance of 'LookupEntryClass'+--  is not defined here, for good reasons (which I forget right now, but+--  something to do with the method dictionary being superfluous on+--  an algebraic data type).++-- |Define a lookup map based on a list of values.+--+data LookupMap a = LookupMap [a]+  deriving (Functor, F.Foldable, T.Traversable)++{-++To allow this Monoid instance, we would need UndecidableInstances.+Also, mapMerge can error out which is not what we would want.++instance (LookupEntryClass a k v, Eq a, Show a, Ord k) => Monoid (LookupMap a) where+    mempty = LookupMap []+    mappend = mapMerge++We could use the following (perhaps with a L.nub on the result before sticling back+into LookupMap) but it is unclear what the semantics are for repeated keys; it is+likely to be left-biased but would leave duplicate keys in the list which could+cause confusion at a later time (e.g. key removal). Many of the routines assume a single+key (or single key,value) pair.++instance (Eq a) => Monoid (LookupMap a) where+    mempty = LookupMap []+    (LookupMap a) `mappend` (LookupMap b) =+        LookupMap (a `mappend` b))+-}++gLM :: LookupMap a -> [a]+gLM (LookupMap es) = es++-- |Define equality of 'LookupMap' values based on equality of entries.+--+--  (This is possibly a poor definition, as it is dependent on ordering+--  of list members.  But it passes all current test cases, and is used+--  only for testing.)+--+instance (Eq a) => Eq (LookupMap a) where+    (==) = (==) `on` gLM++-- |Define Show instance for LookupMap based on Showing the+-- list of entries.+--+instance (Show a ) => Show (LookupMap a) where+    show (LookupMap es) = "LookupMap " ++ show es++-- |Empty lookup map of arbitrary (i.e. polymorphic) type.+--+emptyLookupMap :: (LookupEntryClass a k v) => LookupMap a+emptyLookupMap = LookupMap []++-- |Function to create a `LookupMap` from a list of entries.+--+makeLookupMap :: +    (LookupEntryClass a k v) +    => [a]  -- ^ This list is not checked for duplicate entries, or+            -- entries with the same key but different values.+    -> LookupMap a+makeLookupMap = LookupMap++-- |Returns a list of lookup map entries.+--+listLookupMap :: (LookupEntryClass a k v) => LookupMap a -> [a]+listLookupMap = gLM++-- |Given a lookup map entry, return a new entry that can be used+--  in the reverse direction of lookup.  This is used to construct+--  a reverse LookupMap.+--+reverseEntry :: (LookupEntryClass a1 k v, LookupEntryClass a2 v k)+    => a1 -> a2+reverseEntry = newEntry . swap . keyVal++-- |Given a lookup map, return a new map that can be used+--  in the opposite direction of lookup.+--+reverseLookupMap :: (LookupEntryClass a1 b c, LookupEntryClass a2 c b)+    => LookupMap a1 -> LookupMap a2+reverseLookupMap = fmap reverseEntry++-- |Given a pair of lookup entry values, return the ordering of their+--  key values.+--+keyOrder :: (LookupEntryClass a k v, Ord k)+    =>  a -> a -> Ordering+keyOrder = comparing entryKey++-- |Find key in lookup map and return corresponding value,+--  otherwise return default supplied.+--+mapFind :: +    (LookupEntryClass a k v) +    => v    -- ^ The default value.+    -> k +    -> LookupMap a -> v+mapFind def key = fromMaybe def . mapFindMaybe key++-- |Find key in lookup map and return Just the corresponding value,+--  otherwise return Nothing.+--+mapFindMaybe :: (LookupEntryClass a k v) => k -> LookupMap a -> Maybe v+mapFindMaybe key (LookupMap es) = foldr match Nothing es where+    match ent alt+        | key ==  entryKey ent  = Just (entryVal ent)+        | otherwise             = alt++-- |Test to see if key is present in the supplied map+--+mapContains :: (LookupEntryClass a k v) =>+    LookupMap a -> k -> Bool+mapContains (LookupMap es) key  = any match es where+    match ent = key == entryKey ent++-- |Replace the first occurrence of a key a with a new key-value pair,+--  or add a new key-value pair if the supplied key is not already present.+--+mapReplace :: (LookupEntryClass a k v) =>+    LookupMap a -> a -> LookupMap a+mapReplace (LookupMap []) newe      = LookupMap [newe]+mapReplace (LookupMap (e:es)) newe+    | entryKey e == entryKey newe   = LookupMap (newe:es)+    | otherwise                     = mapAdd more e where+        more = mapReplace (LookupMap es) newe++-- |Replace all occurrence of a key a with a new key-value pair.+--+--  The resulting lookup map has the same form as the original in all+--  other respects.+--+mapReplaceAll :: (LookupEntryClass a k v) =>+    LookupMap a -> a -> LookupMap a+mapReplaceAll l@(LookupMap []) _        = l+mapReplaceAll (LookupMap (e:es)) newe   = mapAdd more e' where+    more = mapReplaceAll (LookupMap es) newe+    e'   = if entryKey e == entryKey newe then newe else e++-- |Replace any occurrence of a key in the first argument with a+--  corresponding key-value pair from the second argument, if present.+--+--  This could be implemented by multiple applications of 'mapReplaceAll',+--  but is arranged differently so that only one new @LookupMap@ value is+--  created.+--+--  Note:  keys in the new map that are not present in the old map+--  are not included in the result map+--+mapReplaceMap :: (LookupEntryClass a k v) =>+    LookupMap a -> LookupMap a -> LookupMap a+mapReplaceMap l@(LookupMap []) _ = l+mapReplaceMap (LookupMap (e:es)) newmap = mapAdd more e' where+    more  = mapReplaceMap (LookupMap es) newmap+    e'    = newEntry (k, mapFind v k newmap)+    (k,v) = keyVal e++-- |Add supplied key-value pair to the lookup map.+--+--  This is effectively an optimized case of 'mapReplace' or 'mapAddIfNew',+--  where the caller guarantees to avoid duplicate key values.+--+mapAdd :: LookupMap a -> a -> LookupMap a+mapAdd (LookupMap es) e = LookupMap (e:es)++-- |Add supplied key-value pair to the lookup map,+--  only if the key value is not already present.+--+mapAddIfNew :: (LookupEntryClass a k v) =>+    LookupMap a -> a -> LookupMap a+mapAddIfNew emap e = if mapContains emap (entryKey e)+                        then emap+                        else mapAdd emap e++-- |Delete the first occurrence of the key from the lookup map.+--+--  If the key does not exist in the map then no change is made.+--+mapDelete :: (LookupEntryClass a k v) =>+    LookupMap a -> k -> LookupMap a+mapDelete l@(LookupMap []) _ = l+mapDelete (LookupMap (e:es)) k+    | k == entryKey e   = LookupMap es+    | otherwise         = mapAdd more e where+        more = mapDelete (LookupMap es) k++-- |Delete all occurrences of the key from the lookup map.+--+mapDeleteAll :: (LookupEntryClass a k v) =>+    LookupMap a -> k -> LookupMap a+mapDeleteAll l@(LookupMap []) _ = l+mapDeleteAll (LookupMap (e:es)) k =+    let more = mapDeleteAll (LookupMap es) k+    in if entryKey e == k then more else mapAdd more e+        +-- |Compare two lookup maps for equality.+--+--  Two maps are equal if they have the same set of keys, and if+--  each key maps to an equivalent value. This is only guaranteed+--  if the maps do not contain duplicate entries.+--+mapEq :: (LookupEntryClass a k v, Eq v) =>+    LookupMap a -> LookupMap a -> Bool+mapEq es1 es2 =+    ks1 `equiv` ks2 &&+    and [ mapFindMaybe k es1 == mapFindMaybe k es2 | k <- ks1 ]+    where+        ks1 = mapKeys es1+        ks2 = mapKeys es2++-- |Return the list of distinct keys in a supplied LookupMap+--+mapKeys :: (LookupEntryClass a k v) =>+    LookupMap a -> [k]+mapKeys = L.nub . gLM . fmap entryKey++-- |Return list of distinct values in a supplied LookupMap+--+mapVals :: (Eq v, LookupEntryClass a k v) =>+    LookupMap a -> [v]+mapVals = L.nub . gLM . fmap entryVal++-- |Merge two lookup maps, ensuring that if the same key appears+--  in both maps it is associated with the same value.+--+mapMerge :: (LookupEntryClass a k v, Eq a, Show a, Ord k) =>+    LookupMap a -> LookupMap a -> LookupMap a+mapMerge a b = LookupMap $ on merge (L.sortBy keyOrder . gLM) a b+    where+        merge es1 [] = es1+        merge [] es2 = es2+        merge es1@(e1:et1) es2@(e2:et2) =+            case keyOrder e1 e2 of+                LT -> e1 : merge et1 es2+                GT -> e2 : merge es1 et2+                EQ -> if e1 /= e2+                        then error ("mapMerge key conflict: " ++ show e1+                                    ++ " with " ++ show e2)+                        else e1 : merge et1 et2++--------------------------------------------------------------------------------+--+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke+--  All rights reserved.+--+--  This file is part of Swish.+--+--  Swish is free software; you can redistribute it and/or modify+--  it under the terms of the GNU General Public License as published by+--  the Free Software Foundation; either version 2 of the License, or+--  (at your option) any later version.+--+--  Swish is distributed in the hope that it will be useful,+--  but WITHOUT ANY WARRANTY; without even the implied warranty of+--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+--  GNU General Public License for more details.+--+--  You should have received a copy of the GNU General Public License+--  along with Swish; if not, write to:+--    The Free Software Foundation, Inc.,+--    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+--+--------------------------------------------------------------------------------
+ src/Data/Ord/Partial.hs view
@@ -0,0 +1,367 @@+--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  Partial+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke+--  License     :  GPL V2+--+--  Maintainer  :  Douglas Burke+--  Stability   :  experimental+--  Portability :  H98+--+--  This module provides methods to support operations on partially ordered+--  collections.  The partial ordering relationship is represented by+--  'Maybe' 'Ordering'.+--+--  Thanks to members of the haskell-cafe mailing list -+--    Robert (rvollmert-lists\@gmx.net) and+--    Tom Pledger (Tom.Pledger\@peace.com) -+--  who suggested key ideas on which some of the code in this module is based.+--+--------------------------------------------------------------------------------++-- at present the only user of this module is Swish.RDF.ClassRestrictionRule++module Data.Ord.Partial+    ( PartCompare+      -- * Finding the range of a part-ordered list+    , minima+    , maxima+      -- * Comparing part-ordered containers+    , partCompareEq+    , partComparePair+    , partCompareListMaybe+    , partCompareListSubset+    )+    where++import Data.List (foldl')++------------------------------------------------------------+--  Type of partial compare function+------------------------------------------------------------++-- | Partial comparison function.+type PartCompare a = a -> a -> Maybe Ordering++------------------------------------------------------------+--  Functions for minima and maxima of a part-ordered list+------------------------------------------------------------++-- |This function finds the maxima in a list of partially+--  ordered values, preserving the sequence of retained+--  values from the supplied list.+--+--  It returns all those values in the supplied list+--  for which there is no larger element in the list.+--+maxima :: PartCompare a -> [a] -> [a]+maxima cmp = foldl' add [] +    where+        add []     e = [e]+        add ms@(m:mr) e = case cmp m e of+            Nothing -> m : add mr e+            Just GT -> ms+            Just EQ -> ms+            Just LT -> add mr e++-- |This function finds the minima in a list of partially+--  ordered values, preserving the sequence of retained+--  values from the supplied list.+--+--  It returns all those values in the supplied list+--  for which there is no smaller element in the list.+--+minima :: PartCompare a -> [a] -> [a]+minima cmp = maxima (flip cmp)++------------------------------------------------------------+--  Partial ordering comparison functions+------------------------------------------------------------++-- |Partial ordering for Eq values+partCompareEq :: (Eq a) => PartCompare a+partCompareEq a1 a2 = if a1 == a2 then Just EQ else Nothing++-- |Part-ordering comparison on pairs of values,+--  where each has a part-ordering relationship+partComparePair ::+    PartCompare a+    -> PartCompare b+    -> (a,b) +    -> (a,b)+    -> Maybe Ordering+partComparePair cmpa cmpb (a1,b1) (a2,b2) = case (cmpa a1 a2,cmpb b1 b2) of+    (_,Nothing)       -> Nothing+    (jc1,Just EQ)     -> jc1+    (Nothing,_)       -> Nothing+    (Just EQ,jc2)     -> jc2+    (Just c1,Just c2) -> if c1 == c2 then Just c1 else Nothing++-- |Part-ordering comparison on lists of partially ordered values, where:+--+--  [@as==bs@]  if members of as are all equal to corresponding members of bs+--    +--  [@as<=bs@]  if members of as are all less than or equal to corresponding+--          members of bs+--    +--  [@as>=bs@]  if members of as are all greater than or equal to corresponding+--          members of bs+--    +--  [otherwise] as and bs are unrelated+--+--  The comparison is restricted to the common elements in the two lists.+--+partCompareListPartOrd :: PartCompare a -> [a] -> [a] -> Maybe Ordering+partCompareListPartOrd cmp a1s b1s = pcomp a1s b1s EQ+    where+        pcomp (a:as) (b:bs) ordp = case cmp a b of+            Just rel  -> pcomp1 as bs rel ordp+            _         -> Nothing+        pcomp _      _      ordp = Just ordp+        -- pcomp []     []     ordp = Just ordp+        +        pcomp1 as bs ordn EQ   = pcomp as bs ordn+        pcomp1 as bs EQ   ordp = pcomp as bs ordp+        pcomp1 as bs ordn ordp =+            if ordn == ordp then pcomp as bs ordp else Nothing+                                                       +-- |Part-ordering comparison for Maybe values.+partCompareMaybe :: (Eq a) => Maybe a -> Maybe a -> Maybe Ordering+partCompareMaybe Nothing  Nothing  = Just EQ+partCompareMaybe (Just _) Nothing  = Just GT+partCompareMaybe Nothing  (Just _) = Just LT+partCompareMaybe (Just a) (Just b) = if a == b then Just EQ else Nothing++-- |Part-ordering comparison on lists of Maybe values.+partCompareListMaybe :: (Eq a) => [Maybe a] -> [Maybe a] -> Maybe Ordering+partCompareListMaybe = partCompareListPartOrd partCompareMaybe++-- |Part-ordering comparison on lists based on subset relationship+partCompareListSubset :: (Eq a) => [a] -> [a] -> Maybe Ordering+partCompareListSubset a b+    | aeqvb     = Just EQ+    | asubb     = Just LT+    | bsuba     = Just GT+    | otherwise = Nothing+    where+        asubb = a `subset` b+        bsuba = b `subset` a+        aeqvb = asubb && bsuba+        x `subset` y = and [ ma `elem` y | ma <- x ]++------------------------------------------------------------+--  Test cases+------------------------------------------------------------++{-++notTrueFalse  = Nothing :: Maybe Bool++-- partCompareListOrd+test01 = partCompareListOrd [1,2,3] [1,2,3] == Just EQ+test02 = partCompareListOrd [1,2,3] [2,3,4] == Just LT+test03 = partCompareListOrd [1,2,4] [1,2,3] == Just GT+test04 = partCompareListOrd [1,2,3] [2,1,3] == Nothing++-- partCompareMaybe+test11 = partCompareMaybe (Just True)  (Just True)  == Just EQ+test12 = partCompareMaybe (Just True)  (Just False) == Nothing+test13 = partCompareMaybe notTrueFalse (Just False) == Just LT+test14 = partCompareMaybe (Just True)  notTrueFalse == Just GT+test15 = partCompareMaybe notTrueFalse notTrueFalse == Just EQ++-- partCompareListMaybe+test21 = partCompareListMaybe [Just True,Just False]+                              [Just True,Just False]+            == Just EQ+test22 = partCompareListMaybe [Just True,Just False]+                              [Just True,Just True]+            == Nothing+test23 = partCompareListMaybe [Just False,Just True]+                              [Just False,Just True]+            == Just EQ+test24 = partCompareListMaybe [Nothing,   Just True]+                              [Just False,Just True]+            == Just LT+test25 = partCompareListMaybe [Just False,Just True]+                              [Just False,Nothing]+            == Just GT+test26 = partCompareListMaybe [Nothing,   Just True]+                              [Just False,Nothing]+            == Nothing+test27 = partCompareListMaybe [Nothing,Just True]+                              [Nothing,Nothing]+            == Just GT+test28 = partCompareListMaybe [notTrueFalse,notTrueFalse]+                              [notTrueFalse,notTrueFalse]+            == Just EQ++--  minima, maxima+test31a = maxima partCompareListMaybe ds1a == ds1b+test31b = minima partCompareListMaybe ds1a == ds1c+ds1a =+    [ [Just 'a',Just 'b',Just 'c']+    , [Just 'a',Just 'b',Nothing ]+    , [Just 'a',Nothing ,Just 'c']+    , [Just 'a',Nothing ,Nothing ]+    , [Nothing ,Just 'b',Just 'c']+    , [Nothing ,Just 'b',Nothing ]+    , [Nothing ,Nothing ,Just 'c']+    , [Nothing ,Nothing ,Nothing ]+    ]+ds1b =+    [ [Just 'a',Just 'b',Just 'c']+    ]+ds1c =+    [ [Nothing ,Nothing ,Nothing ]+    ]++test32a = maxima partCompareListMaybe ds2a == ds2b+test32b = minima partCompareListMaybe ds2a == ds2c+ds2a =+    [ [Just 'a',Just 'b',Nothing ]+    , [Just 'a',Nothing ,Just 'c']+    , [Just 'a',Nothing ,Nothing ]+    , [Nothing ,Just 'b',Just 'c']+    , [Nothing ,Just 'b',Nothing ]+    , [Nothing ,Nothing ,Just 'c']+    ]+ds2b =+    [ [Just 'a',Just 'b',Nothing ]+    , [Just 'a',Nothing ,Just 'c']+    , [Nothing ,Just 'b',Just 'c']+    ]+ds2c =+    [ [Just 'a',Nothing ,Nothing ]+    , [Nothing ,Just 'b',Nothing ]+    , [Nothing ,Nothing ,Just 'c']+    ]++test33a = maxima partCompareListMaybe ds3a == ds3b+test33b = minima partCompareListMaybe ds3a == ds3c+ds3a =+    [ [Just "a1",Just "b1",Just "c1"]+    , [Just "a2",Just "b2",Nothing  ]+    , [Just "a3",Nothing  ,Just "c3"]+    , [Just "a4",Nothing  ,Nothing  ]+    , [Nothing  ,Just "b5",Just "c5"]+    , [Nothing  ,Just "b6",Nothing  ]+    , [Nothing  ,Nothing  ,Just "c7"]+    ]+ds3b =+    [ [Just "a1",Just "b1",Just "c1"]+    , [Just "a2",Just "b2",Nothing  ]+    , [Just "a3",Nothing  ,Just "c3"]+    , [Just "a4",Nothing  ,Nothing  ]+    , [Nothing  ,Just "b5",Just "c5"]+    , [Nothing  ,Just "b6",Nothing  ]+    , [Nothing  ,Nothing  ,Just "c7"]+    ]+ds3c =+    [ [Just "a1",Just "b1",Just "c1"]+    , [Just "a2",Just "b2",Nothing  ]+    , [Just "a3",Nothing  ,Just "c3"]+    , [Just "a4",Nothing  ,Nothing  ]+    , [Nothing  ,Just "b5",Just "c5"]+    , [Nothing  ,Just "b6",Nothing  ]+    , [Nothing  ,Nothing  ,Just "c7"]+    ]+++test34a = maxima partCompareListMaybe ds4a == ds4b+test34b = minima partCompareListMaybe ds4a == ds4c+ds4a =+    [ [Just 1, Just 1 ]+    , [Just 2, Nothing]+    , [Nothing,Just 3 ]+    , [Nothing,Nothing]+    ]+ds4b =+    [ [Just 1, Just 1 ]+    , [Just 2, Nothing]+    , [Nothing,Just 3 ]+    ]+ds4c =+    [ [Nothing,Nothing]+    ]++-- Check handling of equal values+test35a = maxima partCompareListMaybe ds5a == ds5b+test35b = minima partCompareListMaybe ds5a == ds5c+ds5a =+    [ [Just 1, Just 1 ]+    , [Just 2, Nothing]+    , [Nothing,Just 3 ]+    , [Nothing,Nothing]+    , [Just 1, Just 1 ]+    , [Just 2, Nothing]+    , [Nothing,Just 3 ]+    , [Nothing,Nothing]+    ]+ds5b =+    [ [Just 1, Just 1 ]+    , [Just 2, Nothing]+    , [Nothing,Just 3 ]+    ]+ds5c =+    [ [Nothing,Nothing]+    ]++-- test case 32 with different ordering of values+test36a = maxima partCompareListMaybe ds6a == ds6b+test36b = minima partCompareListMaybe ds6a == ds6c+ds6a =+    [ [Just 'a',Just 'b',Nothing ]+    , [Nothing ,Nothing ,Just 'c']+    , [Nothing ,Just 'b',Nothing ]+    , [Nothing ,Just 'b',Just 'c']+    , [Just 'a',Nothing ,Nothing ]+    , [Just 'a',Nothing ,Just 'c']+    ]+ds6b =+    [ [Just 'a',Just 'b',Nothing ]+    , [Nothing ,Just 'b',Just 'c']+    , [Just 'a',Nothing ,Just 'c']+    ]+ds6c =+    [ [Nothing ,Nothing ,Just 'c']+    , [Nothing ,Just 'b',Nothing ]+    , [Just 'a',Nothing ,Nothing ]+    ]++test = and+    [ test01, test02, test03, test04+    , test11, test12, test13, test14, test15+    , test21, test22, test23, test24, test25, test26, test27, test28+    , test31a, test31b, test32a, test32b, test33a, test33b+    , test34a, test34b, test35a, test35b, test36a, test36b+    ]++-}++--------------------------------------------------------------------------------+--+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke+--  All rights reserved.+--+--  This file is part of Swish.+--+--  Swish is free software; you can redistribute it and/or modify+--  it under the terms of the GNU General Public License as published by+--  the Free Software Foundation; either version 2 of the License, or+--  (at your option) any later version.+--+--  Swish is distributed in the hope that it will be useful,+--  but WITHOUT ANY WARRANTY; without even the implied warranty of+--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+--  GNU General Public License for more details.+--+--  You should have received a copy of the GNU General Public License+--  along with Swish; if not, write to:+--    The Free Software Foundation, Inc.,+--    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+--+--------------------------------------------------------------------------------
+ src/Data/String/ShowLines.hs view
@@ -0,0 +1,73 @@+--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  ShowLines+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke+--  License     :  GPL V2+--+--  Maintainer  :  Douglas Burke+--  Stability   :  experimental+--  Portability :  H98+--+--  This module defines an extension of the 'Show' class for displaying+--  multi-line values.  It serves the following purposes:+--+--  (1) provides a method with greater layout control of multiline values,+--+--  (2) provides a possibility to override the default 'Show' behaviour+--      for programs that use the extended 'ShowLines' interface, and+--+--  (3) uses a 'ShowS' intermediate value to avoid unnecessary+--      concatenation of long strings.+--+--------------------------------------------------------------------------------++module Data.String.ShowLines (ShowLines(..)) where++-- |ShowLines is a type class for values that may be formatted in+--  multi-line displays.+class (Show sh) => ShowLines sh where+    -- |Multi-line value display method+    --+    --  Create a multiline displayable form of a value, returned+    --  as a 'ShowS' value.  The default implementation behaves just+    --  like a normal instance of 'Show'.+    --+    --  This function is intended to allow the calling function some control+    --  of multiline displays by providing:+    --+    --  (1) the first line of the value is not preceded by any text, so+    --      it may be appended to some preceding text on the same line,+    --+    --  (2) the supplied line break string is used to separate lines of the+    --      formatted text, and may include any desired indentation, and+    --+    --  (3) no newline is output following the final line of text.+    showls :: String -> sh -> ShowS+    showls _ = shows++--------------------------------------------------------------------------------+--+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke+--  All rights reserved.+--+--  This file is part of Swish.+--+--  Swish is free software; you can redistribute it and/or modify+--  it under the terms of the GNU General Public License as published by+--  the Free Software Foundation; either version 2 of the License, or+--  (at your option) any later version.+--+--  Swish is distributed in the hope that it will be useful,+--  but WITHOUT ANY WARRANTY; without even the implied warranty of+--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+--  GNU General Public License for more details.+--+--  You should have received a copy of the GNU General Public License+--  along with Swish; if not, write to:+--    The Free Software Foundation, Inc.,+--    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+--+--------------------------------------------------------------------------------
src/Swish.hs view
@@ -3,7 +3,7 @@ -------------------------------------------------------------------------------- -- | --  Module      :  Swish---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke --  License     :  GPL V2 -- --  Maintainer  :  Douglas Burke@@ -25,12 +25,13 @@ --  the lines described in [3]. -- --  The script format used by Swish is described in---  "Swish.RDF.SwishScript".+--  "Swish.Script". -- --  Users wishing to process RDF data directly may prefer to look at---  the following modules; "Swish.RDF", "Swish.RDF.TurtleParser",---  "Swish.RDF.N3Parser", "Swish.RDF.N3Formatter", "Swish.RDF.NTParser"---  and "Swish.RDF.NTFormatter".+--  the following modules; "Swish.RDF", "Swish.RDF.Parser.Turtle",+--  "Swish.RDF.Parser.N3", "Swish.RDF.Parser.NTriples",+--  "Swish.RDF.Formatter.Turtle", "Swish.RDF.Formatter.N3"+--  and "Swish.RDF.Formatter.NTriples". -- --  (1) Semantic web: <http://www.w3.org/2001/sw/> --@@ -44,19 +45,269 @@ -- --  (6) RDF:          <http://www.w3.org/RDF/> --+--  Notes+--+--  I anticipate that this module may be used as a starting point for+--  creating new programs rather then as a complete program in its own+--  right.  The functionality built into this code is selected with a+--  view to testing the Haskell modules for handling RDF rather than+--  for performing any particular application processing (though+--  development as a tool with some broader utility is not ruled out).+--+--  With the following in ghci:+--+-- >>> :m + Swish+-- >>> :set prompt "swish> "+--+-- then we can run a Swish script (format described in "Swish.Script")+-- by saying:+--+-- >>> runSwish "-s=script.ss"+-- ExitSuccess+--+-- or convert a file from Turtle to NTriples format with:+--+-- >>> runSwish "-ttl -i=foo.ttl -nt -o=foo.nt"+-- ExitSuccess+--+-- You can also use `validateCommands` by giving it the individual commands,+-- such as+--+-- >>> let Right cs = validateCommands ["-ttl", "-i=file1.ttl", "-c=file2.ttl"]+-- >>> cs+-- [SwishAction: -ttl,SwishAction: -i=file1.ttl,SwishAction: -c=file2.ttl]+-- >>> st <- runSwishActions cs+-- >>> st+-- The graphs do not compare as equal.+-- -------------------------------------------------------------------------------- -module Swish-       (-         module Swish.RDF.SwishMain-       ) where+module Swish ( SwishStatus(..)+             , SwishAction+             , runSwish+             , runSwishActions+             , displaySwishHelp+             , splitArguments+             , validateCommands+             ) where -import Swish.RDF.SwishMain--- should this re-export Swish.RDF.SwishMain or other components?-       +import Swish.Commands+    ( swishFormat+    , swishBase+    , swishInput+    , swishOutput+    , swishMerge+    , swishCompare+    , swishGraphDiff+    , swishScript+    )++import Swish.Monad (SwishStateIO, SwishState(..), SwishStatus(..)+                   , SwishFormat(..)+                   , emptyState)+import Swish.QName (qnameFromURI)+++import Control.Monad.State (execStateT)+import Control.Monad (liftM)++import Network.URI (parseURI)++import Data.Char (isSpace)+import Data.Either (partitionEithers)++import System.Exit (ExitCode(ExitSuccess, ExitFailure))++------------------------------------------------------------+--  Command line description+------------------------------------------------------------++-- we do not display the version in the help file to avoid having+-- to include the Paths_swish module (so that we can use this from+-- an interactive environment).+--++usageText :: [String]+usageText =+    [ "Swish: Read, merge, write, compare and process RDF graphs."+    , ""+    , "Usage: swish option option ..."+    , ""+    , "where the options are processed from left to right, and may be"+    , "any of the following:"+    , "-h        display this message."+    , "-?        display this message."+    , "-v        display Swish version and quit."+    , "-q        do not display Swish version on start up."+    , "-nt       use Ntriples format for subsequent input and output."+    , "-ttl      use Turtle format for subsequent input and output."+    , "-n3       use Notation3 format for subsequent input and output (default)"+    , "-i[=file] read file in selected format into the graph workspace,"+    , "          replacing any existing graph."+    , "-m[=file] merge file in selected format with the graph workspace."+    , "-c[=file] compare file in selected format with the graph workspace."+    , "-d[=file] show graph differences between the file in selected"+    , "          format and the graph workspace.  Differences are displayed"+    , "          to the standard output stream."+    , "-o[=file] write the graph workspace to a file in the selected format."+    , "-s[=file] read and execute Swish script commands from the named file."+    , "-b[=base] set or clear the base URI. The semantics of this are not"+    , "          fully defined yet."+    , ""+    , "    If an optional filename value is omitted, the standard input"+    , "    or output stream is used, as appropriate."+    , ""+    , "Exit status codes:"+    , "Success - operation completed successfully/graphs compare equal"+    , "1 - graphs compare different"+    , "2 - input data format error"+    , "3 - file access problem"+    , "4 - command line error"+    , "5 - script file execution error"+    , ""+    , "Examples:"+    , ""+    , "swish -i=file"+    , "    read file as Notation3, and report any syntax errors."+    , "swish -i=file1 -o=file2"+    , "    read file1 as Notation3, report any syntax errors, and output the"+    , "    resulting graph as reformatted Notation3 (the output format"+    , "    is not perfect but may be improved)."+    , "swish -nt -i=file -n3 -o"+    , "    read file as NTriples and output as Notation3 to the screen."+    , "swich -i=file1 -c=file2"+    , "    read file1 and file2 as notation3, report any syntax errors, and"+    , "    if both are OK, compare the resulting graphs to indicate whether"+    , "    or not they are equivalent."+    ]++-- | Write out the help for Swish+displaySwishHelp :: IO ()+displaySwishHelp = mapM_ putStrLn usageText++------------------------------------------------------------+--  Swish command line interpreter+------------------------------------------------------------+--+--  This is a composite monad combining some state with an IO+--  Monad.  lift allows a pure IO monad to be used as a step+--  of the computation.+--+        +-- | Return any arguments that need processing immediately, namely                     +-- the \"help\", \"quiet\" and \"version\" options.+--+splitArguments :: [String] -> ([String], [String])+splitArguments = partitionEithers . map splitArgument++splitArgument :: String -> Either String String+splitArgument "-?" = Left "-h"+splitArgument "-h" = Left "-h"+splitArgument "-v" = Left "-v"+splitArgument "-q" = Left "-q"+splitArgument x    = Right x++-- | Represent a Swish action. At present there is no way to create these+-- actions other than 'validateCommands'.+-- +newtype SwishAction = SA (String, SwishStateIO ())++instance Show SwishAction where+  show (SA (lbl,_)) = "SwishAction: " ++ lbl++-- | Given a list of command-line arguments create the list of actions+-- to perform or a string and status value indicating an input error.+validateCommands :: [String] -> Either (String, SwishStatus) [SwishAction]+validateCommands args = +  let (ls, rs) = partitionEithers (map validateCommand args)+  in case ls of+    (e:_) -> Left e+    []    -> Right rs+  +-- This allows you to say "-nt=foo" and currently ignores the values+-- passed through. This may change+--    +validateCommand :: String -> Either (String, SwishStatus) SwishAction+validateCommand cmd =+  let (nam,more) = break (=='=') cmd+      arg        = drop 1 more+      marg       = if null arg then Nothing else Just arg+      +      wrap f = Right $ SA (cmd, f marg)+      wrap1 f = Right $ SA (cmd, f)++  in case nam of+    "-ttl"  -> wrap1 $ swishFormat Turtle+    "-nt"   -> wrap1 $ swishFormat NT+    "-n3"   -> wrap1 $ swishFormat N3+    "-i"    -> wrap swishInput+    "-m"    -> wrap swishMerge+    "-c"    -> wrap swishCompare+    "-d"    -> wrap swishGraphDiff+    "-o"    -> wrap swishOutput+    "-b"    -> validateBase cmd marg+    "-s"    -> wrap swishScript+    _       -> Left ("Invalid command line argument: "++cmd, SwishArgumentError)++-- | Execute the given set of actions.+swishCommands :: [SwishAction] -> SwishStateIO ()+swishCommands = mapM_ swishCommand++-- | Execute an action.+swishCommand :: SwishAction -> SwishStateIO ()+swishCommand (SA (_,act)) = act++validateBase :: String -> Maybe String -> Either (String, SwishStatus) SwishAction+validateBase arg Nothing  = Right $ SA (arg, swishBase Nothing)+validateBase arg (Just b) =+  case parseURI b >>= qnameFromURI of+    j@(Just _) -> Right $ SA (arg, swishBase j)+    _      -> Left ("Invalid base URI <" ++ b ++ ">", SwishArgumentError)+  +------------------------------------------------------------+--  Interactive test function (e.g. for use in ghci)+------------------------------------------------------------++-- this ignores the "flags" options, namely+--    -q / -h / -? / -v++-- | Parse and run the given string as if given at the command+-- line. The \"quiet\", \"version\" and \"help\" options are+-- ignored.+--+runSwish :: String -> IO ExitCode+runSwish cmdline = do+  let args = breakAll isSpace cmdline+      (_, cmds) = splitArguments args+      +  case validateCommands cmds of+    Left (emsg, ecode) -> do+      putStrLn $ "Swish exit: " ++ emsg+      return $ ExitFailure $ fromEnum ecode+      +    Right acts -> do+      ec <- runSwishActions acts+      case ec of+        SwishSuccess -> return ExitSuccess+        _  -> do+          putStrLn $ "Swish exit: " ++ show ec+          return $ ExitFailure $ fromEnum ec++-- |Break list into a list of sublists, separated by element+--  satisfying supplied condition.+breakAll :: (a -> Bool) -> [a] -> [[a]]+breakAll _ [] = []+breakAll p s  = let (h,s') = break p s+                    in h : breakAll p (drop 1 s')++-- | Execute the given set of actions.+runSwishActions :: [SwishAction] -> IO SwishStatus+runSwishActions acts = exitcode `liftM` execStateT (swishCommands acts) emptyState+ -------------------------------------------------------------------------------- -----  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke --  All rights reserved. -- --  This file is part of Swish.
+ src/Swish/Commands.hs view
@@ -0,0 +1,406 @@+{-# LANGUAGE OverloadedStrings #-}++--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  Commands+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke+--  License     :  GPL V2+--+--  Maintainer  :  Douglas Burke+--  Stability   :  experimental+--  Portability :  OverloadedStrings+--+--  Functions to deal with indivudual Swish command options.+--+--------------------------------------------------------------------------------++module Swish.Commands+    ( swishFormat+    , swishBase+    -- , swishVerbose+    , swishInput+    , swishOutput+    , swishMerge+    , swishCompare+    , swishGraphDiff+    , swishScript+    )+where++import Swish.GraphClass (LDGraph(..), Label(..))+import Swish.GraphPartition (GraphPartition(..))+import Swish.GraphPartition (partitionGraph, comparePartitions, partitionShowP)+import Swish.Monad (SwishStateIO, SwishState(..)+                   , SwishStatus(..), SwishFormat(..)+                   , setFormat, setBase, setGraph, resetInfo+                   , resetError, setStatus, swishError, reportLine)+import Swish.QName (QName, qnameFromURI, qnameFromFilePath, getQNameURI)+import Swish.Script (parseScriptFromText)++import Swish.RDF.Graph (RDFGraph, merge)++import qualified Swish.RDF.Formatter.Turtle as TTLF+import qualified Swish.RDF.Formatter.N3 as N3F+import qualified Swish.RDF.Formatter.NTriples as NTF++import Swish.RDF.Parser.Turtle (parseTurtle)+import Swish.RDF.Parser.N3 (parseN3)+import Swish.RDF.Parser.NTriples (parseNT)+import Swish.RDF.Parser.Utils (appendURIs)++import System.IO+    ( Handle, IOMode(..)+    , hPutStr, hPutStrLn, hClose+    , hIsReadable, hIsWritable+    , openFile, stdin, stdout+    )++import Network.URI (parseURIReference)++import Control.Monad.Trans (MonadTrans(..))+import Control.Monad.State (modify, gets)+import Control.Monad (liftM, when)++import qualified Data.Text.Lazy as T+import qualified Data.Text.Lazy.IO as IO++import Data.Maybe (isJust, fromMaybe)++import Control.Exception as CE++-- | Set the file format.+--+swishFormat :: SwishFormat -> SwishStateIO ()+swishFormat = modify . setFormat++-- | Set (or clear) the base URI.+swishBase :: Maybe QName -> SwishStateIO ()+swishBase = modify . setBase++-- | Read in a graph and make it the current graph.+swishInput :: +    Maybe String       -- ^ A filename or, if 'Nothing', then use standard input.+    -> SwishStateIO ()+swishInput fnam =+  swishReadGraph fnam >>= maybe (return ()) (modify . setGraph)+  +-- | Read in a graph and merge it with the current graph.+swishMerge :: +    Maybe String       -- ^ A filename or, if 'Nothing', then use standard input.+    -> SwishStateIO ()+swishMerge fnam =+  swishReadGraph fnam >>= maybe (return ()) (modify . mergeGraph)+    +mergeGraph :: RDFGraph -> SwishState -> SwishState+mergeGraph gr state = state { graph = newgr }+    where+        newgr = merge gr (graph state)++-- | Read in a graph and compare it with the current graph.+swishCompare ::+    Maybe String       -- ^ A filename or, if 'Nothing', then use standard input.+    -> SwishStateIO ()+swishCompare fnam =+  swishReadGraph fnam >>= maybe (return ()) compareGraph+    +compareGraph :: RDFGraph -> SwishStateIO ()+compareGraph gr = do+  oldGr <- gets graph+  let exitCode = if gr == oldGr then SwishSuccess else SwishGraphCompareError+  modify $ setStatus exitCode+  +------------------------------------------------------------+--  Display graph differences from named file+------------------------------------------------------------++-- | Read in a graph and display the differences to the current+-- graph to standard output.+swishGraphDiff ::+    Maybe String       -- ^ A filename or, if 'Nothing', then use standard input.+    -> SwishStateIO ()+swishGraphDiff fnam =+  swishReadGraph fnam >>= maybe (return ()) diffGraph++diffGraph :: RDFGraph -> SwishStateIO ()+diffGraph gr = do+  oldGr <- gets graph+  let p1 = partitionGraph (getArcs oldGr)+      p2 = partitionGraph (getArcs gr)+      diffs = comparePartitions p1 p2+      +  swishWriteFile (swishOutputDiffs diffs) Nothing+  +swishOutputDiffs :: (Label lb) =>+    [(Maybe (GraphPartition lb),Maybe (GraphPartition lb))]+    -> Maybe String +    -> Handle+    -> SwishStateIO ()+swishOutputDiffs diffs fnam hnd = do+  lift $ hPutStrLn hnd ("Graph differences: "++show (length diffs))+  mapM_ (swishOutputDiff fnam hnd) (zip [1..] diffs)++swishOutputDiff :: (Label lb) =>+    Maybe String +    -> Handle+    -> (Int,(Maybe (GraphPartition lb),Maybe (GraphPartition lb)))+    -> SwishStateIO ()+swishOutputDiff fnam hnd (diffnum,(part1,part2)) = do+  lift $ hPutStrLn hnd ("---- Difference "++show diffnum++" ----")+  lift $ hPutStr hnd "Graph 1:"+  swishOutputPart fnam hnd part1+  lift $ hPutStr hnd "Graph 2:"+  swishOutputPart fnam hnd part2++swishOutputPart :: (Label lb) =>+    Maybe String +    -> Handle +    -> Maybe (GraphPartition lb) +    -> SwishStateIO ()+swishOutputPart _ hnd part = +  let out = maybe "\n(No arcs)" (partitionShowP "\n") part+  in lift $ hPutStrLn hnd out++------------------------------------------------------------+--  Execute script from named file+------------------------------------------------------------++-- | Read in a script and execute it.+swishScript ::+    Maybe String       -- ^ A filename or, if 'Nothing', then use standard input.+    -> SwishStateIO ()+swishScript fnam = swishReadScript fnam >>= mapM_ swishCheckResult++swishReadScript :: Maybe String -> SwishStateIO [SwishStateIO ()]+swishReadScript = swishReadFile swishParseScript []++{-|+Calculate the base URI to use; it combines the file name+with any user-supplied base.++If both the file name and user-supplied base are Nothing+then the value ++   http://id.ninebynine.org/2003/Swish/++is used.++Needs some work.+-}++defURI :: QName+defURI = "http://id.ninebynine.org/2003/Swish/"++calculateBaseURI ::+  Maybe FilePath -- ^ file name+  -> SwishStateIO QName -- ^ base URI+calculateBaseURI Nothing = fromMaybe defURI `liftM` gets base+calculateBaseURI (Just fnam) =+  case parseURIReference fnam of+    Just furi -> do+      mbase <- gets base+      case mbase of+        Just buri -> case appendURIs (getQNameURI buri) furi of+          Left emsg -> fail emsg -- TODO: think about this ...+          Right res -> return $ fromMaybe defURI (qnameFromURI res)+        Nothing -> lift $ qnameFromFilePath fnam+        +    Nothing -> fail $ "Unable to convert to URI: filepath=" ++ fnam++swishParseScript ::+  Maybe String -- file name (or "stdin" if Nothing)+  -> T.Text    -- script contents+  -> SwishStateIO [SwishStateIO ()]+swishParseScript mfpath inp = do+  buri <- calculateBaseURI mfpath+  case parseScriptFromText (Just buri) inp of+    Left err -> do+      let inName = maybe "standard input" ("file " ++) mfpath+      swishError ("Script syntax error in " ++ inName ++ ": "++err) SwishDataInputError+      return []+              +    Right scs -> return scs++swishCheckResult :: SwishStateIO () -> SwishStateIO ()+swishCheckResult swishcommand = do+  swishcommand+  er <- gets errormsg+  case er of  +    Just x -> swishError x SwishExecutionError >> modify resetError+    _      -> return ()+    +  ms <- gets infomsg+  case ms of+    Just x -> reportLine x >> modify resetInfo+    _      -> return ()++-- | Write out the current graph.+swishOutput :: +    Maybe String       -- ^ A filename or, if 'Nothing', then use standard output.+    -> SwishStateIO ()+swishOutput = swishWriteFile swishOutputGraph+   +swishOutputGraph :: Maybe String -> Handle -> SwishStateIO ()+swishOutputGraph _ hnd = do+  fmt <- gets format+  +  let writeOut formatter = do+        out <- gets $ formatter . graph+        lift $ IO.hPutStrLn hnd out+        +  case fmt of+    N3 -> writeOut N3F.formatGraphAsLazyText+    NT -> writeOut NTF.formatGraphAsLazyText+    Turtle -> writeOut TTLF.formatGraphAsLazyText+    -- _  -> swishError ("Unsupported file format: "++show fmt) SwishArgumentError++------------------------------------------------------------+--  Common input functions+------------------------------------------------------------+--+--  Keep the logic separate for reading file data and+--  parsing it to an RDF graph value.++swishReadGraph :: Maybe String -> SwishStateIO (Maybe RDFGraph)+swishReadGraph = swishReadFile swishParse Nothing++-- | Open a file (or stdin), read its contents, and process them.+--+swishReadFile :: +  (Maybe String -> T.Text -> SwishStateIO a) -- ^ Convert filename and contents into desired value+  -> a -- ^ the value to use if the file can not be read in+  -> Maybe String -- ^ the file name or @stdin@ if @Nothing@+  -> SwishStateIO a+swishReadFile conv errVal fnam = +  let reader (h,f,i) = do+        res <- conv fnam i+        when f $ lift $ hClose h -- given that we use IO.hGetContents not sure the close is needed+        return res+  +  in swishOpenFile fnam >>= maybe (return errVal) reader++-- open a file in the SwishStateIO monad, catching+-- any errors+--+sOpen :: FilePath -> IOMode -> SwishStateIO (Either IOError Handle)+sOpen fp fm = lift . CE.try $ openFile fp fm++-- | Open and read file, returning its handle and content, or Nothing+-- WARNING:  the handle must not be closed until input is fully evaluated+--+swishOpenFile :: Maybe String -> SwishStateIO (Maybe (Handle, Bool, T.Text))+swishOpenFile Nothing     = readFromHandle stdin Nothing+swishOpenFile (Just fnam) = do+  o <- sOpen fnam ReadMode+  case o of+    Left _    -> do+      swishError ("Cannot open file: "++fnam) SwishDataAccessError+      return Nothing+      +    Right hnd -> readFromHandle hnd $ Just ("file: " ++ fnam)++readFromHandle :: Handle -> Maybe String -> SwishStateIO (Maybe (Handle, Bool, T.Text))+readFromHandle hdl mlbl = do+  hrd <- lift $ hIsReadable hdl+  if hrd+    then do+      fc <- lift $ IO.hGetContents hdl+      return $ Just (hdl, isJust mlbl, fc)+  +    else do+      lbl <- case mlbl of+        Just l  -> lift (hClose hdl) >> return l+        Nothing -> return "standard input"+      swishError ("Cannot read from " ++ lbl) SwishDataAccessError+      return Nothing++swishParse :: +  Maybe String -- ^ filename (if not stdin)+  -> T.Text    -- ^ contents of file+  -> SwishStateIO (Maybe RDFGraph)+swishParse mfpath inp = do+  fmt <- gets format+  buri <- calculateBaseURI mfpath+  +  let toError eMsg =+        swishError (show fmt ++ " syntax error in " ++ inName ++ ": " ++ eMsg) SwishDataInputError +        >> return Nothing+        +      inName = maybe "standard input" ("file " ++) mfpath+  +      readIn reader = case reader inp of+        Left eMsg -> toError eMsg+        Right res -> return $ Just res+             +  case fmt of+    Turtle -> readIn (`parseTurtle` Just (getQNameURI buri))+    N3 -> readIn (`parseN3` Just buri)+    NT -> readIn parseNT+    {-+    _  -> swishError ("Unsupported file format: "++show fmt) SwishArgumentError >>+          return Nothing+    -}+    +swishWriteFile :: +  (Maybe String -> Handle -> SwishStateIO ()) -- ^ given a file name and a handle, write to it+  -> Maybe String+  -> SwishStateIO ()+swishWriteFile conv fnam =  +  let hdlr (h, c) = conv fnam h >> when c (lift $ hClose h)+  in swishCreateWriteableFile fnam >>= maybe (return ()) hdlr+   +-- | Open file for writing, returning its handle, or Nothing+--  Also returned is a flag indicating whether or not the+--  handled should be closed when writing is done (if writing+--  to standard output, the handle should not be closed as the+--  run-time system should deal with that).+swishCreateWriteableFile :: Maybe String -> SwishStateIO (Maybe (Handle,Bool))+swishCreateWriteableFile Nothing = do+  hwt <- lift $ hIsWritable stdout+  if hwt+    then return $ Just (stdout, False)+    else do+      swishError "Cannot write to standard output" SwishDataAccessError+      return Nothing+  +swishCreateWriteableFile (Just fnam) = do+  o <- sOpen fnam WriteMode+  case o of+    Left _ -> do+      swishError ("Cannot open file for writing: " ++ fnam) SwishDataAccessError+      return Nothing+      +    Right hnd -> do+      hwt <- lift $ hIsWritable hnd+      if hwt+        then return $ Just (hnd, True)+        else do+          lift $ hClose hnd+          swishError ("Cannot write to file: "++fnam) SwishDataAccessError+          return Nothing+  +--------------------------------------------------------------------------------+--+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke  +--  All rights reserved.+--+--  This file is part of Swish.+--+--  Swish is free software; you can redistribute it and/or modify+--  it under the terms of the GNU General Public License as published by+--  the Free Software Foundation; either version 2 of the License, or+--  (at your option) any later version.+--+--  Swish is distributed in the hope that it will be useful,+--  but WITHOUT ANY WARRANTY; without even the implied warranty of+--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+--  GNU General Public License for more details.+--+--  You should have received a copy of the GNU General Public License+--  along with Swish; if not, write to:+--    The Free Software Foundation, Inc.,+--    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+--+--------------------------------------------------------------------------------
+ src/Swish/Datatype.hs view
@@ -0,0 +1,1074 @@+{-# LANGUAGE ExistentialQuantification #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}++--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  Datatype+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke+--  License     :  GPL V2+--+--  Maintainer  :  Douglas Burke+--  Stability   :  experimental+--  Portability :  ExistentialQuantification, MultiParamTypeClasses, OverloadedStrings+--+--  This module defines the structures used to represent and+--  manipulate datatypes.  It is designed as a basis for handling datatyped+--  RDF literals, but the functions in this module are more generic.+--+--------------------------------------------------------------------------------++--  Testing note:  this module supports a number of specific datatypes.+--  It is intended that functionality in this module will be tested via+--  modules "Swish.RDF.RDFDatatype", +--  "Swish.RDF.ClassRestrictionRule" and+--  "Swish.RDF.RDFDatatypeXsdInteger".+--  See also module ClassRestrictionRuleTest for test cases.++module Swish.Datatype+    ( Datatype(..)+    , typeName, typeRules, typeMkRules, typeMkModifiers, typeMkCanonicalForm+    , getTypeAxiom, getTypeRule+    , DatatypeVal(..)+    , getDTMod+    , getDTRel+    , tvalMkCanonicalForm+    , DatatypeMap(..)+    , DatatypeRel(..), DatatypeRelFn, DatatypeRelPr+    , altArgs+    , UnaryFnDescr,    UnaryFnTable,    UnaryFnApply,    unaryFnApp+    , BinaryFnDescr,   BinaryFnTable,   BinaryFnApply,   binaryFnApp+    , BinMaybeFnDescr, BinMaybeFnTable, BinMaybeFnApply, binMaybeFnApp+    , ListFnDescr,     ListFnTable,     ListFnApply,     listFnApp+    , DatatypeMod(..), ModifierFn+    , ApplyModifier+    , nullDatatypeMod+    -- , applyDatatypeMod+    , makeVmod11inv, makeVmod11+    , makeVmod21inv, makeVmod21+    , makeVmod20+    , makeVmod22+    , makeVmodN1+    , DatatypeSub(..)+    )+where++import Swish.Namespace (ScopedName)+import Swish.Rule (Formula(..), Rule(..))+import Swish.Ruleset (Ruleset(..))+import Swish.Ruleset (getRulesetAxiom, getRulesetRule)+import Swish.VarBinding (VarBinding(..), VarBindingModify(..), OpenVarBindingModify)+import Swish.VarBinding (addVarBinding, nullVarBindingModify)++import Swish.RDF.Vocabulary (swishName)++import Swish.Utils.ListHelpers (flist)++-- used to add Show instances for structures during debugging+-- but backed out again.+--+-- import Swish.Utils.ShowM (ShowM(..))++import Control.Monad (join, liftM)++import Data.LookupMap (LookupEntryClass(..), LookupMap(..), mapFindMaybe)+import Data.Maybe (isJust, catMaybes)++import qualified Data.Text as T++------------------------------------------------------------+--  Datatype framework+------------------------------------------------------------++-- |Datatype wraps a 'DatatypeVal' value, hiding the value type that+--  is used only in implementations of the datatype.+--  Users see just the datatype name and associated ruleset.+--+data Datatype ex lb vn = forall vt . Datatype (DatatypeVal ex vt lb vn)++instance LookupEntryClass+        (Datatype ex lb vn) ScopedName (Datatype ex lb vn)+    where+    newEntry (_,dt) = dt+    keyVal dt       = (typeName dt, dt)++-- |Get type name from Datatype value+typeName :: Datatype ex lb vn -> ScopedName+typeName (Datatype dtv) = tvalName  dtv++-- |Get static rules from Datatype value+typeRules :: Datatype ex lb vn -> Ruleset ex+typeRules (Datatype dtv) = tvalRules dtv++-- |Make rules for Datatype value based on supplied expression+typeMkRules :: Datatype ex lb vn -> ex -> [Rule ex]+typeMkRules (Datatype dtv) = tvalMkRules dtv++-- |Make variable binding modifiers based on values supplied+typeMkModifiers :: Datatype ex lb vn -> [OpenVarBindingModify lb vn]+typeMkModifiers (Datatype dtv) = tvalMkMods dtv++-- |Get the named axiom from a Datatype value.+getTypeAxiom :: ScopedName -> Datatype ex lb vn -> Maybe (Formula ex)+getTypeAxiom nam dt = getRulesetAxiom nam (typeRules dt)++-- |Get the named rule from a Datatype value.+getTypeRule :: ScopedName -> Datatype ex lb vn -> Maybe (Rule ex)+getTypeRule nam dt = getRulesetRule nam (typeRules dt)++-- |Get the canonical form of a datatype value.+typeMkCanonicalForm :: Datatype ex lb vn -> T.Text -> Maybe T.Text+typeMkCanonicalForm (Datatype dtv) = tvalMkCanonicalForm dtv++------------------------------------------------------------+--  DatatypeVal+------------------------------------------------------------++-- |DatatypeVal is a structure that defines a number of functions+--  and values that characterize the behaviour of a datatype.+--+--  A datatype is specified with respect to (polymophic in) a given+--  type of (syntactic) expression with which it may be used, and+--  a value type (whose existence is hidden as an existential type+--  within `DatatypeMap`).+--+--  (I tried hiding the value type with an internal existential+--  declaration, but that wouldn't wash.  Hence this two-part+--  structure with `Datatype` in which the internal detail+--  of the value type is hidden from users of the `Datatype` class.)+--+--  The datatype characteristic functions have two goals:+--+--  (1) to support the general datatype entailment rules defined by+--      the RDF semantics specification, and+--+--  (2) to define additional datatype-specific inference patterns by+--      means of which provide additional base functionality to+--      applications based on RDF inference.+--+--  Datatype-specific inferences are provided using the `DatatypeRel`+--  structure for a datatype, which allows a number of named relations+--  to be defined on datatype values, and provides mechanisms to+--  calculate missing values in a partially-specified member of+--  a relation.+--+--  Note that rules and variable binding modifiers that deal with+--  combined values of more than one datatype may be defined+--  separately.  Definitions in this module are generally applicable+--  only when using a single datatype.+--+--  An alternative model for datatype value calculations is inspired+--  by that introduced by CWM for arithmetic operations, e.g.+--+--  >     (1 2 3) math:sum ?x => ?x rdf:value 6+--+--  (where the bare integer @n@ here is shorthand for @\"n\"^^xsd:integer@).+--+--  Datatype-specific inference patterns are provided in two ways:+--+--  * by variable binding modifiers that can be combined with the+--    query results during forward- for backward-chaining of+--    inference rules, and+--+--  * by the definition of inference rulesets that involve+--    datatype values.+--+--  I believe the first method to be more flexible than the second,+--  in that it more readily supports forward and backward chaining,+--  but can be used only through the definition of new rules.+--+--  Type parameters:+--+--  [@ex@] is the type of expression with which the datatype may be used.+--+--  [@vt@] is the internal value type with which the labels are associated.+--+--  [@lb@] is the type of label that may be used as a variable in an+--         expression or rule.+--+--  [@vn@] is the type of node that may be used to carry a value in an+--         expression or rule.+--+data DatatypeVal ex vt lb vn = DatatypeVal+    { tvalName      :: ScopedName+                                -- ^Identifies the datatype, and also+                                --  its value space class.+    , tvalRules     :: Ruleset ex+                                -- ^A set of named expressions and rules+                                --  that are valid in in any theory that+                                --  recognizes the current datatype.+    , tvalMkRules   :: ex -> [Rule ex]+                                -- ^A function that accepts an expression+                                --  and devives some datatype-dependent+                                --  rules from it.  This is provided as a+                                --  hook for creating datatyped class+                                --  restriction rules.+    , tvalMkMods    :: [OpenVarBindingModify lb vn]+                                -- ^Constructs a list of open variable+                                --  binding modifiers based on tvalMod,+                                --  but hiding the actual value type.+    , tvalMap       :: DatatypeMap vt+                                -- ^Lexical to value mapping, where @vt@ is+                                --  a datatype used within a Haskell program+                                --  to represent and manipulate values in+                                --  the datatype's value space+    , tvalRel       :: [DatatypeRel vt]+                                -- ^A set of named relations on datatype+                                --  values.  Each relation accepts a list+                                --  of @Maybe vt@, and computes any+                                --  unspecified values that are in the+                                --  relation with values supplied.+    , tvalMod       :: [DatatypeMod vt lb vn]+                                -- ^A list of named values that are used to+                                --  construct variable binding modifiers, which+                                --  in turn may be used by a rule definition.+                                --+                                --  TODO: In due course, this value may be+                                --  calculated automatically from the supplied+                                --  value for @tvalRel@.+    }++{-+instance ShowM ex => Show (DatatypeVal ex vt lb vn) where+  show dv = "DatatypeVal: " ++ show (tvalName dv) ++ "\n -> rules:\n" ++ show (tvalRules dv)+-}++--  Other accessor functions++-- | Return the named datatype relation, if it exists.+getDTRel ::+    ScopedName -> DatatypeVal ex vt lb vn -> Maybe (DatatypeRel vt)+getDTRel nam dtv =+    mapFindMaybe nam (LookupMap (tvalRel dtv))++-- | Return the named datatype value modifier, if it exists.+getDTMod ::+    ScopedName -> DatatypeVal ex vt lb vn -> Maybe (DatatypeMod vt lb vn)+getDTMod nam dtv =+    mapFindMaybe nam (LookupMap (tvalMod dtv))++-- |Get the canonical form of a datatype value, or @Nothing@.+--+tvalMkCanonicalForm :: DatatypeVal ex vt lb vn -> T.Text -> Maybe T.Text+tvalMkCanonicalForm dtv str = can+    where+      dtmap = tvalMap dtv+      val   = mapL2V dtmap str+      can   = join $ liftM (mapV2L dtmap) val++-- |DatatypeMap consists of methods that perform lexical-to-value+--  and value-to-canonical-lexical mappings for a datatype.+--+--  The datatype mappings apply to string lexical forms which+--  are stored as `Data.Text`.+--+data DatatypeMap vt = DatatypeMap+    { mapL2V  :: T.Text -> Maybe vt+                            -- ^ Function to map a lexical string to+                            --   the datatype value.  This effectively+                            --   defines the lexical space of the+                            --   datatype to be all strings for which+                            --   yield a value other than @Nothing@.+    , mapV2L  :: vt -> Maybe T.Text+                            -- ^ Function to map a value to its canonical+                            --   lexical form, if it has such.+    }++-- |Type for a datatype relation inference function.+--+--  A datatype relation defines tuples of values that satisfy some+--  relation.  A datatype relation inference function calculates+--  values that complete a relation with values supplied.+--+--  The function accepts a list of @Maybe vt@, where vt is the+--  datatype value type.  It returns one of:+--+--  * Just a list of lists, where each inner list returned is a+--      complete set of values, including the values supplied, that+--      are in the relation.+--+--  * Just an empty list is returned if the supplied values are+--      insufficient to compute any complete sets of values in the+--      relation.+--+--  * Nothing if the supplied values are not consistent with+--      the relation.+--+type DatatypeRelFn vt = [Maybe vt] -> Maybe [[vt]]++-- |Type for datatype relation predicate:  accepts a list of values+--  and determines whether or not they satisfy the relation.+--+type DatatypeRelPr vt = [vt] -> Bool++-- |Datatype for a named relation on values of a datatype.+--+data DatatypeRel vt = DatatypeRel+    { dtRelName :: ScopedName+    , dtRelFunc :: DatatypeRelFn vt+    }++instance LookupEntryClass (DatatypeRel vt) ScopedName (DatatypeRel vt)+    where+    newEntry (_,relf) = relf+    keyVal dtrel = (dtRelName dtrel, dtrel)++-- |Datatype value modifier functions type+--+--  Each function accepts a list of values and returns a list of values.+--  The exact significance of the different values supplied and returned+--  depends on the variable binding pattern used (cf. 'ApplyModifier'),+--  but in all cases an empty list returned means that the corresponding+--  inputs are not consistent with the function and cannot be used.+--+type ModifierFn vn = [vn] -> [vn]++-- |Type of function used to apply a data value modifier to specified+--  variables in a supplied variable binding.  It also accepts the+--  name of the datatype modifier and carries it into the resulting+--  variable binding modifier.+--+--  (Note that @vn@ is not necessarily the same as @vt@, the datatype value+--  type:  the modifier functions may be lifted or otherwise adapted+--  to operate on some other type from which the raw data values are+--  extracted.)+--+type ApplyModifier lb vn =+    ScopedName -> [ModifierFn vn] -> OpenVarBindingModify lb vn++-- |Wrapper for data type variable binding modifier included in+--  a datatype value.+--+data DatatypeMod vt lb vn = DatatypeMod+    { dmName :: ScopedName+    , dmModf :: [ModifierFn vt]+    , dmAppf :: ApplyModifier lb vn+    }++instance LookupEntryClass+        (DatatypeMod vt lb vn) ScopedName (DatatypeMod vt lb vn)+    where+    newEntry (_,dmod) = dmod+    keyVal dmod = (dmName dmod, dmod)++-- |Null datatype value modifier+nullDatatypeMod :: DatatypeMod vt lb vn+nullDatatypeMod = DatatypeMod+    { dmName = swishName "nullDatatypeMod"+    , dmModf = []+    , dmAppf = nullAppf+    }+    where+        -- nullAppf :: ScopedName -> [ModifierFn vn] -> OpenVarBindingModify lb vn+        nullAppf nam _ lbs = (nullVarBindingModify lbs) { vbmName = nam }++{-+-- |Apply datatype variable binding modifier value to list of labels and+--  a variable binding.+applyDatatypeMod :: (Eq lb, Show lb, Eq vn, Show vn)+    => DatatypeMod vt lb vn -> OpenVarBindingModify lb vn+applyDatatypeMod dtmod = dmAppf dtmod (dmName dtmod) (dmModf dtmod)+-}++{-+dmName dtmod :: ScopedName+dmModf dtmod :: [ModifierFn vt]+             :: [[vt] -> [vt]]+dmAppf dtmod :: ApplyModifier lb vn+             :: ScopedName -> [ModifierFn vn] -> OpenVarBindingModify lb vn+             :: ScopedName -> [[vn] -> [vn]] -> OpenVarBindingModify lb vn+dmAppf dtmod (dmName dtmod)+             :: [[vn] -> [vn]] -> OpenVarBindingModify lb vn+-}++--------------------------------------------------------------+--  Functions for creating datatype variable binding modifiers+--------------------------------------------------------------++-- |'ApplyModifier' function for use with 'DatatypeMod' in cases+--  when the value mapping is a @1->1@ function and inverse, such+--  as negate.+--+--  [@nam@]     is the name from the 'DatatypeMod' value that is carried into+--          the resulting variable binding modifier.+--        +--  [@fns@]     are functions used to implement details of the variable+--          binding modifier:+--+--          (0) is @[x,y] -> [?]@, used as a filter (i.e. not creating any+--              new variable bindings), returning a non-empty list if @x@ and @y@+--              are in the appropriate relationship.+--+--          (1) is @[y] -> [x]@, used to perform the calculation in a forward+--              direction.+--+--          (2) is @[x] -> [y]@, used to perform the calculation in a backward+--              direction.  This may be the same as (2) (e.g. for negation)+--              or may be different (e.g. increment).+--+--  [@lbs@]     is a list of specific label values for which a variable binding+--          modifier will be generated.  (The intent is that a variable-free+--          value can be generated as a Curried function, and instantiated+--          for particular variables as required.)+--+--  Note: an irrefutable pattern match for @lbs@ is used so that a name+--  for the 'VarBindingModify' value can be extracted using an undefined+--  label value.+--+makeVmod11inv :: (Eq lb, Show lb, Eq vn, Show vn) => ApplyModifier lb vn+makeVmod11inv nam [f0,f1,f2] lbs@(~[lb1,lb2]) = VarBindingModify+    { vbmName   = nam+    , vbmApply  = concatMap app1+    , vbmVocab  = lbs+    , vbmUsage  = [[],[lb1],[lb2]]+    }+    where+        app1 vbind = app2 (map (vbMap vbind) lbs) vbind+        app2 [Just v1,Just v2] vbind = selv     (f0 [v1,v2]) vbind+        app2 [Nothing,Just v2] vbind = addv lb1 (f1 [v2])    vbind+        app2 [Just v1,Nothing] vbind = addv lb2 (f2 [v1])    vbind+        app2 _                     _     = []+makeVmod11inv _ _ _ =+    error "makeVmod11inv: requires 3 functions and 2 labels"++-- |'ApplyModifier' function for use with 'DatatypeMod' in cases when+--  the value mapping is a non-invertable @1->1@ injection, such as+--  absolute value.+--+--  [@nam@] is the name from the 'DatatypeMod' value that is carried into+--          the resulting variable binding modifier.+--+--  [@fns@] are functions used to implement details of the variable+--          binding modifier:+--+--          (0) is @[x,y] -> [?]@, used as a filter (i.e. not creating any+--              new variable bindings), returning a non-empty list if @x@ and @y@+--              are in the appropriate relationship.+--+--          (1) is @[x]@ -> @[y]@, used to perform the calculation.+--+--  [@lbs@] is a list of specific label values for which a variable binding+--          modifier will be generated.+--+--  Note: an irrefutable pattern match for @lbs@ is used so that a name+--  for the 'VarBindingModify' value can be extracted using an undefined+--  label value.+--+makeVmod11 :: (Eq lb, Show lb, Eq vn, Show vn) => ApplyModifier lb vn+makeVmod11 nam [f0,f1] lbs@(~[lb1,_]) = VarBindingModify+    { vbmName   = nam+    , vbmApply  = concatMap app1+    , vbmVocab  = lbs+    , vbmUsage  = [[],[lb1]]+    }+    where+        app1 vbind = app2 (map (vbMap vbind) lbs) vbind+        app2 [Just v1,Just v2] vbind = selv (f0 [v1,v2])  vbind+        app2 [Nothing,Just v2] vbind = addv lb1 (f1 [v2]) vbind+        app2 _                     _     = []+makeVmod11 _ _ _ =+    error "makeVmod11: requires 2 functions and 2 labels"++-- |'ApplyModifier' function for use with 'DatatypeMod' in cases+--  when the value mapping is a @2->1@ invertable function, such as+--  addition or subtraction.+--+--  [@nam@]     is the name from the 'DatatypeMod' value that is carried into+--          the resulting variable binding modifier.+--+--  [@fns@]     are functions used to implement details of the variable+--          binding modifier:+--+--          (1) is @[x,y,z] -> [?]@, used as a filter (i.e. not creating any+--              new variable bindings), returning a non-empty list if+--              @x@, @y@ and @z@ are in the appropriate relationship.+--+--          (2) is @[y,z] -> [x]@, used to perform the calculation in a+--              forward direction.+--+--          (3) is @[x,z] -> [y]@, used to run the calculation backwards to+--              determine the first input argument+--+--          (4) is @[x,y] -> [z]@, used to run the calculation backwards to+--              determine the second input argument+--+--  [@lbs@]     is a list of specific label values for which a variable binding+--          modifier will be generated.+--+--  Note: an irrefutable pattern match for @lbs@ is used so that a name+--  for the 'VarBindingModify' value can be extracted using an undefined+--  label value.+--+makeVmod21inv :: (Eq lb, Show lb, Eq vn, Show vn) => ApplyModifier lb vn+makeVmod21inv nam [f0,f1,f2,f3] lbs@(~[lb1,lb2,lb3]) = VarBindingModify+    { vbmName   = nam+    , vbmApply  = concatMap app1+    , vbmVocab  = lbs+    , vbmUsage  = [[],[lb1],[lb2],[lb3]]+    }+    where+        app1 vbind = app2 (map (vbMap vbind) lbs) vbind+        app2 [Just v1,Just v2,Just v3] vbind = selv (f0 [v1,v2,v3]) vbind+        app2 [Nothing,Just v2,Just v3] vbind = addv lb1 (f1 [v2,v3]) vbind+        app2 [Just v1,Nothing,Just v3] vbind = addv lb2 (f2 [v1,v3]) vbind+        app2 [Just v1,Just v2,Nothing] vbind = addv lb3 (f3 [v1,v2]) vbind+        app2 _                               _     = []+makeVmod21inv _ _ _ =+    error "makeVmod21inv: requires 4 functions and 3 labels"++-- |'ApplyModifier' function for use with 'DatatypeMod' in cases+--  when the value mapping is a @2->1@ non-invertable function, such as+--  logical @AND@ or @OR@.+--+--  [@nam@]     is the name from the 'DatatypeMod' value that is carried into+--          the resulting variable binding modifier.+--+--  [@fns@]     are functions used to implement details of the variable+--          binding modifier:+--+--          (1) is @[x,y,z] -> [?]@, used as a filter (i.e. not creating any+--              new variable bindings), returning a non-empty list if+--              @x@, @y@ and @z@ are in the appropriate relationship.+--+--          (2) is @[y,z] -> [x]@, used to perform the calculation in a+--              forward direction.+--+--  [@lbs@]     is a list of specific label values for which a variable binding+--          modifier will be generated.+--+--  Note: an irrefutable pattern match for @lbs@ is used so that a name+--  for the 'VarBindingModify' value can be extracted using an undefined+--  label value.+--+makeVmod21 :: (Eq lb, Show lb, Eq vn, Show vn) => ApplyModifier lb vn+makeVmod21 nam [f0,f1] lbs@(~[lb1,_,_]) = VarBindingModify+    { vbmName   = nam+    , vbmApply  = concatMap app1+    , vbmVocab  = lbs+    , vbmUsage  = [[],[lb1]]+    }+    where+        app1 vbind = app2 (map (vbMap vbind) lbs) vbind+        app2 [Just v1,Just v2,Just v3] vbind = selv (f0 [v1,v2,v3]) vbind+        app2 [Nothing,Just v2,Just v3] vbind = addv lb1 (f1 [v2,v3]) vbind+        app2 _                               _     = []+makeVmod21 _ _ _ =+    error "makeVmod21: requires 2 functions and 3 labels"++-- |'ApplyModifier' function for use with 'DatatypeMod' in cases+--  when the value mapping is a simple comparson of two values.+--+--  [@nam@]     is the name from the 'DatatypeMod' value that is carried into+--          the resulting variable binding modifier.+--+--  [@fns@]     are functions used to implement details of the variable+--          binding modifier:+--+--          (1) is @[x,y] -> [?]@, used as a filter (i.e. not creating any+--              new variable bindings), returning a non-empty list if+--              @x@ and @y@ are in the appropriate relationship.+--+--  [@lbs@]     is a list of specific label values for which a variable binding+--          modifier will be generated.+--+--  Note: an irrefutable pattern match for @lbs@ is used so that a name+--  for the 'VarBindingModify' value can be extracted using an undefined+--  label value.+--+makeVmod20 :: (Eq lb, Show lb, Eq vn, Show vn) => ApplyModifier lb vn+makeVmod20 nam [f0] lbs@(~[_,_]) = VarBindingModify+    { vbmName   = nam+    , vbmApply  = concatMap app1+    , vbmVocab  = lbs+    , vbmUsage  = [[]]+    }+    where+        app1 vbind = app2 (map (vbMap vbind) lbs) vbind+        app2 [Just v1,Just v2] vbind = selv (f0 [v1,v2]) vbind+        app2 _                     _     = []+makeVmod20 _ _ _ =+    error "makeVmod20: requires 1 function and 2 labels"++-- |'ApplyModifier' function for use with 'DatatypeMod' in cases+--  when the value mapping is a @2->2@ non-invertable function, such as+--  quotient/remainder+--+--  [@nam@]     is the name from the 'DatatypeMod' value that is carried into+--          the resulting variable binding modifier.+--+--  [@fns@]     are functions used to implement details of the variable+--          binding modifier:+--+--          (1) is @[w,x,y,z] -> [?]@, used as a filter (i.e. not creating+--              any new variable bindings), returning a non-empty list if+--              @w@, @x@, @y@ and @z@ are in the appropriate relationship.+--+--          (2) is @[y,z] -> [w,x]@, used to perform the calculation given+--              two input values.+--+--  [@lbs@]     is a list of specific label values for which a variable binding+--          modifier will be generated.+--+--  Note: an irrefutable pattern match for @lbs@ is used so that a name+--  for the 'VarBindingModify' value can be extracted using an undefined+--  label value.+--+--  NOTE: this might be generalized to allow one of @w@ or @x@ to be+--  specified, and return null if it doesn't match the calculated value.+--+makeVmod22 :: (Eq lb, Show lb, Eq vn, Show vn) => ApplyModifier lb vn+makeVmod22 nam [f0,f1] lbs@(~[lb1,lb2,_,_]) = VarBindingModify+    { vbmName   = nam+    , vbmApply  = concatMap app1+    , vbmVocab  = lbs+    , vbmUsage  = [[],[lb1,lb2]]+    }+    where+        app1 vbind = app2 (map (vbMap vbind) lbs) vbind+        app2 [Just v1,Just v2,Just v3,Just v4] vbind =+            selv (f0 [v1,v2,v3,v4]) vbind+        app2 [Nothing,Nothing,Just v3,Just v4] vbind =+            addv2 lb1 lb2 (f1 [v3,v4]) vbind+        app2 _                               _     = []+makeVmod22 _ _ _ =+    error "makeVmod22: requires 2 functions and 4 labels"++-- |'ApplyModifier' function for use with 'DatatypeMod' in cases+--  when the value mapping is a @N->1@ function,+--  such as Sigma (sum) of a vector.+--+--  [@nam@]     is the name from the 'DatatypeMod' value that is carried into+--          the resulting variable binding modifier.+--+--  [@fns@]     are functions used to implement details of the variable+--          binding modifier:+--+--          (1) is @[x,y...] -> [?]@, used as a filter (i.e. not creating+--              any new variable bindings), returning a non-empty list if+--              @x@ and @y...@ are in the appropriate relationship.+--+--          (2) is @[y...] -> [x]@, used to perform the calculation.+--+--  [@lbs@]     is a list of specific label values for which a variable binding+--          modifier will be generated.+--+--  Note: an irrefutable pattern match for @lbs@ is used so that a name+--  for the 'VarBindingModify' value can be extracted using an undefined+--  label value.+--+makeVmodN1 :: (Eq lb, Show lb, Eq vn, Show vn) => ApplyModifier lb vn+makeVmodN1 nam [f0,f1] lbs@(~(lb1:_)) = VarBindingModify+    { vbmName   = nam+    , vbmApply  = concatMap app1+    , vbmVocab  = lbs+    , vbmUsage  = [[],[lb1]]+    }+    where+        app1 vbind = app2 (map (vbMap vbind) lbs) vbind+        app2 vs@(v1:_) vbind+            | isJust v1 && isJustvs = selv (f0 jvs) vbind+            | isJustvs              = addv lb1 (f1 jvs) vbind+            | otherwise             = []+            where+                isJustvs = all isJust vs+                jvs      = catMaybes vs+        app2 _ _ = error "app2 sent empty list" -- -Wall++makeVmodN1 _ _ _ =+    error "makeVmodN1: requires 2 functions and at 1 or more labels"++--------------------------------------------------------+--  Local helper functions for makeVmodXXX variants+--------------------------------------------------------++--  Add value to variable variable binding, if value is singleton list,+--  otherwise return empty list.+addv :: (Eq lb, Show lb, Eq vt, Show vt)+    => lb -> [vt] -> VarBinding lb vt+    -> [VarBinding lb vt]+addv lb [val] vbind = [addVarBinding lb val vbind]+addv _  _     _     = []++--  Add two entries to variable variable binding, if value supplied is+--  a doubleton list, otherwise return empty list.+addv2 :: (Eq lb, Show lb, Eq vt, Show vt)+    => lb -> lb -> [vt] -> VarBinding lb vt+    -> [VarBinding lb vt]+addv2 lb1 lb2 [val1,val2] vbind = [addVarBinding lb1 val1 $+                                   addVarBinding lb2 val2 vbind]+addv2 _   _   _           _     = []++--  If supplied value is non-empty list return supplied variable binding,+--  otherwise return empty list.+selv :: [vt] -> varBinding lb vt -> [varBinding lb vt]+selv [] _     = []+selv _  vbind = [vbind]++--------------------------------------------------------------+--  Functions for evaluating arguments in a datatype relation+--------------------------------------------------------------+--+--  altArgs is a generic function for evaluating datatype relation+--          values, based on suppied functions and argument values+--+--  UnaryFnDescr, UnaryFnApply and unaryFnApp:+--          are support types and function for using altArgs to+--          evaluate relations on unary functions (binary relations).+--+--  BinaryFnDescr, BinaryFnApply and binaryFnApp:+--          are support types and function for using altArgs to+--          evaluate relations on binary functions (3-way relations).+--+--  ListFnDescr, ListFnApply and listFnApp:+--          are support types and function for using altArgs to+--          evaluate relations on list functions (n-way relations),+--          where the first member of the list is the value of a+--          fold of a function over the rest of the list.+--+--  See experimental module spike-altargs.hs for test cases and+--  development steps for this function.++-- |Given a list of argument values and a list of functions for+--  calculating new values from supplied values, return a list+--  of argument values, or @Nothing@ if the supplied values are+--  inconsistent with the calculations specified.+--+--  Each list of values returned corresponds to a set of values that+--  satisfy the relation, consistent with the values supplied.+--+--  Functions are described as tuple consisting of:+--+--    (a) a predicate that the argument is required to satisfy+--+--    (b) a function to apply,+--+--    (c) a function to apply function (b) to a list of arguments+--+--    (d) argument list index values to which the function is applied.+--+--  Each supplied argument is of the form @Maybe a@, where the argument+--  has value type a.  @Nothing@ indicates arguments of unknown value.+--+--  The basic idea is that, for each argument position in the relation,+--  a function may be supplied to calculate that argument's possible values+--  from some combination of the other arguments.  The results calculated+--  in this way are compared with the original arguments provided:+--  if the values conflict then the relation is presumed to be+--  unsatisfiable with the supplied values, and @Nothing@ is returned;+--  if there are any calculated values for arguments supplied without+--  any values, then tbe calculated values are used.+--  If there are any arguments for which no values are supplied or+--  calculated, then the relation is presumed to be underdetermined,+--  and @Just []@ is returned.+--+altArgs :: +  (Eq vt)+  => DatatypeRelPr vt +  -> [(vt->Bool,[b])]+  -- ^ a list of argument value predicates and+  --   function descriptors.  The predicate indicates any+  --   additional constraints on argument values (e.g. the result+  --   of abs must be positive).  Use @(const True)@ for the predicate+  --   associated with unconstrained relation arguments.+  --   For each argument, a list of function descriptors is+  --   supplied corresponding to alternative values (e.g. a square+  --   relation would offer two alternative values for the root.)++  -> ((vt->Bool)->b->[Maybe vt]->Maybe [vt])+  -- ^ a function that takes an argument value predicate,+  --   a function descriptor and applies it to a supplied argument+  --   list to return:+  --   @Just a@ calculated list of one or more possible argument values,+  --   @Just []@ indicating insufficient information provided, or+  --   @Nothing@ indicating inconsistent information provided.+  --   May be one of 'unaryFnApp', 'binaryFnApp', 'listFnApp' or+  --   some other caller-supplied value.++  -> DatatypeRelFn vt+  -- ^ The return value can be used as the+  -- 'dtRelFunc' component of a 'DatatypeRel' value.++altArgs pr fnss apfn args = cvals4 cvals3+    where+        --  Calculate new value(s) for each argument from supplied values, and+        --  lift inconsistency indicator (Just/Nothing) to outermost Monad.+        --    cvals1 :: [Maybe [vt]]+        cvals1 = flist (map (applyFdescToTuple apfn) fnss) args+        --  Merge calculated values with supplied arguments, and again+        --  lift inconsistency indicator (Just/Nothing) to outermost Monad.+        --    cvals2 :: Maybe [[vt]]+        cvals2 = sequence $ mergeTupleVals (map fst fnss) args cvals1+        --  Map list of alternative values for each tuple member to+        --  a list of alternative tuples.+        cvals3 = liftM sequence cvals2+        --  Check each tuple against the supplied predicate.+        --  If any of the alternative tuples does not match the predicate+        --  then signal an inconsistency.+        cvals4 Nothing       = Nothing+        cvals4 cvs@(Just ts) = if all pr ts then cvs else Nothing++--  Perform alternative calculations for single result value+--  Each result value is a list of zero or more alternatives+--  that can be calculated from available parameters, or+--  Nothing if the available parameters are inconsistent.+--+--  apfn    is the function that actually applies an element of+--          the function descriptor to a tuple of Maybe arguments+--          (where Nothing is used to indicate an unknown value)+--  (p,fns) is a pair consisting of a value-checking predicate+--          for the corresponding tuple member, and a list of+--          function descriptors that each return one or more+--          values the tuple member, calculated from other values+--          that are present.  Just [] means no values are+--          calculated for this member, and Nothing means the+--          calculation has detected tuple values supplied that+--          are inconsistent with the datatype relation concerned.+--  args    is a tuple of Maybe tuple elements, (where Nothing+--          indicates an unknown value).+--+--  Returns Maybe a list of alternative values for the member,+--  Just [] to indicate insufficient information to calculate+--  any new values, and Nothing to indicate an inconsistency.+--+applyFdescToTuple ::+    ((vt->Bool)->b->[Maybe vt]->Maybe [vt]) -> (vt->Bool,[b]) -> [Maybe vt]+    -> Maybe [vt]+applyFdescToTuple apfn (p,fns) args =+    liftM concat $ sequence cvals+    where+        -- cvals :: [Maybe [vt]]+        cvals = flist (map (apfn p) fns) args++--  Merge calculated tuple values with supplied tuple, checking for consistency.+--+--  ps      predicates used for isolated validation of each tuple member+--  args    supplied tuple values, with Nothing for unknown values+--  cvals   list of alternative calculated values for each tuple member,+--          or Nothing if an inconsistency has been detected by the+--          tuple-calculation functions.  Note that this list may contain+--          more entries than args; the surplus entries are ignored+--          (see list functions for how this is used).+--+--  Returns a tuple of Maybe lists of values for each tuple member,+--  containing Nothing if an inconsistency has been detected in the+--  supplied values.+--+mergeTupleVals  :: (Eq a) => [a->Bool] -> [Maybe a] -> [Maybe [a]] -> [Maybe [a]]+mergeTupleVals _ _  (Nothing:_) = [Nothing]+mergeTupleVals (_:ps) (Nothing:a1s) (Just a2s:a2ss)+                             = Just a2s:mergeTupleVals ps a1s a2ss+mergeTupleVals (p:ps) (Just a1:a1s) (Just []:a2ss)+    | p a1                   = Just [a1]:mergeTupleVals ps a1s a2ss+    | otherwise              = [Nothing]+mergeTupleVals (p:ps) (Just a1:a1s) (Just a2s:a2ss)+    | p a1 && elem a1 a2s    = Just [a1]:mergeTupleVals ps a1s a2ss+    | otherwise              = [Nothing]+mergeTupleVals _ [] _        = []+mergeTupleVals _ _  _        = [Nothing]++-- |'altArgs' support for unary functions: function descriptor type+type UnaryFnDescr a = (a->a,Int)++-- |'altArgs' support for unary functions: function descriptor table type+type UnaryFnTable a = [(a->Bool,[UnaryFnDescr a])]++-- |'altArgs' support for unary functions: function applicator type+type UnaryFnApply a = (a->Bool) -> UnaryFnDescr a -> [Maybe a] -> Maybe [a]++-- |'altArgs' support for unary functions: function applicator+unaryFnApp :: UnaryFnApply a+unaryFnApp p (f1,n) args = apf (args!!n)+    where+        apf (Just a) = if p r then Just [r] else Nothing where r = f1 a+        apf Nothing  = Just []++-- |'altArgs' support for binary functions: function descriptor type+type BinaryFnDescr a = (a->a->a,Int,Int)++-- |'altArgs' support for binary functions: function descriptor table type+type BinaryFnTable a = [(a->Bool,[BinaryFnDescr a])]++-- |'altArgs' support for binary functions: function applicator type+type BinaryFnApply a =+    (a->Bool) -> BinaryFnDescr a -> [Maybe a] -> Maybe [a]++-- |'altArgs' support for binary functions: function applicator+binaryFnApp :: BinaryFnApply a+binaryFnApp p (f,n1,n2) args = apf (args!!n1) (args!!n2)+    where+        apf (Just a1) (Just a2) = if p r then Just [r] else Nothing+            where r = f a1 a2+        apf _ _  = Just []++-- |'altArgs' support for binary function with provision for indicating+--  inconsistent supplied values:  function descriptor type+type BinMaybeFnDescr a = (a->a->Maybe [a],Int,Int)++-- |'altArgs' support for binary function with provision for indicating+--  inconsistent supplied values:  function descriptor table type+type BinMaybeFnTable a = [(a->Bool,[BinMaybeFnDescr a])]++-- |'altArgs' support for binary function with provision for indicating+--  inconsistent supplied values:  function applicator type+type BinMaybeFnApply a =+    (a->Bool) -> BinMaybeFnDescr a -> [Maybe a] -> Maybe [a]++-- |'altArgs' support for binary function with provision for indicating+--  inconsistent supplied values:  function applicator+binMaybeFnApp :: BinMaybeFnApply a+binMaybeFnApp p (f,n1,n2) args = apf (args!!n1) (args!!n2)+    where+        apf (Just a1) (Just a2) = if pm r then r else Nothing+            where+                r = f a1 a2+                pm Nothing  = False+                pm (Just x) = all p x+        apf _ _  = Just []++-- |'altArgs' support for list functions (e.g. sum over list of args),+--  where first element of list is a fold over the rest of the list,+--  and remaining elements of list can be calculated in terms+--  of the result of the fold and the remaining elements+--+--  List function descriptor is+--+--  (a) list-fold function, f  (e.g. (+)+--        +--  (b) list-fold identity, z  (e.g. 0)+--        +--  (c) list-fold-function inverse, g (e.g. (-))+--        +--  (d) index of element to evaluate+--        +--  such that:+--        +--  >    (a `f` z) == (z `f` a) == a+--  >    (a `g` c) == b <=> a == b `f` c+--  >    (a `g` z) == a+--  >    (a `g` a) == z+--+--  and the result of the folded function does not depend on+--  the order that the list elements are processed.+--+--  NOTE:  the list of 'ListFnDescr' values supplied to 'altArgs' must+--  be at least as long as the argument list.  In many cases, Haskell+--  lazy evaluation can be used to supply an arbitrarily long list.+--  See test cases in spike-altargs.hs for an example.+--+--  Function descriptor type+type ListFnDescr a = (a->a->a,a,a->a->a,Int)++-- |Function table type+type ListFnTable a = [(a->Bool,[ListFnDescr a])]++-- |'altArgs' support for list functions:  function applicator type+type ListFnApply a = (a->Bool) -> ListFnDescr a -> [Maybe a] -> Maybe [a]++-- |'altArgs' support for list functions:  function applicator+listFnApp :: ListFnApply a+listFnApp p (f,z,g,n) (a0:args)+    | n == 0    =+        app $ foldr (apf f) (Just [z]) args+    | otherwise =+        app $ apf g a0 (foldr (apf f) (Just [z]) (args `deleteIndex` (n-1)))+    where+        apf :: (a->a->a) -> Maybe a -> Maybe [a] -> Maybe [a]+        apf fn (Just a1) (Just [a2]) = Just [fn a1 a2]+        apf _  _         _           = Just []+        +        -- app :: Maybe [a] -> Maybe [a]+        app Nothing      = Nothing+        app r@(Just [a]) = if p a then r else Nothing+        app _            = Just []++listFnApp _ _ [] = error "listFnApp called with an empty list" -- -Wall++-- |Delete the n'th element of a list, returning the result+--+--  If the list doesn't have an n'th element, return the list unchanged.+--+deleteIndex :: [a] -> Int -> [a]+deleteIndex [] _ = []+deleteIndex xxs@(x:xs) n+    | n <  0    = xxs+    | n == 0    = xs+    | otherwise = x:deleteIndex xs (n-1)++{-+testdi1 = deleteIndex [1,2,3,4] 0    == [2,3,4]+testdi2 = deleteIndex [1,2,3,4] 1    == [1,3,4]+testdi3 = deleteIndex [1,2,3,4] 2    == [1,2,4]+testdi4 = deleteIndex [1,2,3,4] 3    == [1,2,3]+testdi5 = deleteIndex [1,2,3,4] 4    == [1,2,3,4]+testdi6 = deleteIndex [1,2,3,4] (-1) == [1,2,3,4]+testdi = and+    [ testdi1, testdi2, testdi3, testdi4, testdi5, testdi6 ]+-}++--------------------------------------------------------+--  Datatype sub/supertype description+--------------------------------------------------------++-- |Describe a subtype/supertype relationship between a pair of datatypes.+--+--  Originally, I had this as a supertype field of the DatatypeVal structure,+--  but that suffered from some problems:+--+--  * supertypes may be introduced retrospectively,+--+--  * the relationship expressed with respect to a single datatype+--      cannot indicate how to do injections/restrictions between the+--      underlying value types.+--+--  [@ex@]      is the type of expression with which the datatype may be used.+--+--  [@lb@]      is the type of the variable labels used.+--+--  [@vn@]      is the type of value node used to contain a datatyped value+--+--  [@supvt@]   is the internal value type of the super-datatype+--+--  [@subvt@]   is the internal value type of the sub-datatype+--+data DatatypeSub ex lb vn supvt subvt = DatatypeSub+    { trelSup   :: DatatypeVal ex supvt lb vn+                                -- ^ Datatype that is a supertype of @trelSub@,+                                --   having value space @supvt@.+    , trelSub   :: DatatypeVal ex subvt lb vn+                                -- ^ Datatype that is a subtype of @trelSup@,+                                --   having value space @supvt@.+    , trelToSup :: subvt -> supvt+                                -- ^ Function that maps subtype value to+                                --   corresponding supertype value.+    , trelToSub :: supvt -> Maybe subvt+                                -- ^ Function that maps supertype value to+                                --   corresponding subtype value, if there+                                --   is such a value.+    }++--------------------------------------------------------------------------------+--+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke+--  All rights reserved.+--+--  This file is part of Swish.+--+--  Swish is free software; you can redistribute it and/or modify+--  it under the terms of the GNU General Public License as published by+--  the Free Software Foundation; either version 2 of the License, or+--  (at your option) any later version.+--+--  Swish is distributed in the hope that it will be useful,+--  but WITHOUT ANY WARRANTY; without even the implied warranty of+--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+--  GNU General Public License for more details.+--+--  You should have received a copy of the GNU General Public License+--  along with Swish; if not, write to:+--    The Free Software Foundation, Inc.,+--    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+--+--------------------------------------------------------------------------------
+ src/Swish/GraphClass.hs view
@@ -0,0 +1,220 @@+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE DeriveFoldable #-}+{-# LANGUAGE DeriveTraversable #-}++--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  GraphClass+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke+--  License     :  GPL V2+--+--  Maintainer  :  Douglas Burke+--  Stability   :  experimental+--  Portability :  DeriveFunctor, DeriveFoldable, DeriveTraversable, MultiParamTypeClasses+--+--  This module defines a Labelled Directed Graph and Label classes,+--  and the Arc datatype.+--+--------------------------------------------------------------------------------++------------------------------------------------------------+-- Define LDGraph, arc and related classes and types+------------------------------------------------------------++module Swish.GraphClass+    ( LDGraph(..)+    , Label(..)+    , Arc(..)+    , Selector+    , arc, arcToTriple, arcFromTriple+    , hasLabel, arcLabels -- , arcNodes+    )+where++import Data.Hashable (Hashable(..))+import Data.List (foldl', union, (\\))++import qualified Data.Foldable as F+import qualified Data.Traversable as T++--  NOTE:  I wanted to declare this as a subclass of Functor, but+--  the constraint on the label type seems to prevent that.+--  So I've just declared specific instances to be Functors.+--++{-|+Labelled Directed Graph class.++Minimum required implementation: +'emptyGraph', 'setArcs', and 'getArcs'.+-}+class (Eq (lg lb), Eq lb ) => LDGraph lg lb where+    -- | Create the empty graph.+    emptyGraph  :: lg lb+      +    -- | Replace the existing arcs in the graph.+    setArcs     :: lg lb -> [Arc lb] -> lg lb+    +    -- | Extract all the arcs from a graph+    getArcs     :: lg lb -> [Arc lb]+    +    -- | Extract those arcs that match the given `Selector`.+    extract     :: Selector lb -> lg lb -> lg lb+    extract sel = update (filter sel)+    +    -- | Add the two graphs+    addGraphs         :: lg lb -> lg lb -> lg lb+    addGraphs    addg = update (union (getArcs addg))+    +    -- | Remove those arcs in the first graph from the second+    -- graph+    delete :: lg lb  -- ^ g1+              -> lg lb -- ^ g2+              -> lg lb -- ^ g2 - g1 -> g3+    delete delg = update (\\ getArcs delg)+    +    -- | Enumerate the distinct labels contained in a graph;+    -- that is, any label that appears in the subject,+    -- predicate or object position of an `Arc`.+    labels      :: lg lb -> [lb]+    labels g    = foldl' union [] (map arcLabels (getArcs g))+    +    -- | Enumerate the distinct nodes contained in a graph;+    -- that is, any label that appears in the subject+    -- or object position of an `Arc`.+    nodes       :: lg lb -> [lb]+    nodes g     = foldl' union [] (map arcNodes (getArcs g))+    +    -- | Update the arcs in a graph using a supplied function.+    update      :: ([Arc lb] -> [Arc lb]) -> lg lb -> lg lb+    update f g  = setArcs g ( f (getArcs g) )++-- | Label class+--+--  A label may have a fixed binding, which means that the label identifies (is) a+--  particular graph node, and different such labels are always distinct nodes.+--  Alternatively, a label may be unbound (variable), which means that it is a+--  placeholder for an unknown node label.  Unbound node labels are used as+--  graph-local identifiers for indicating when the same node appears in+--  several arcs.+--+--  For the purposes of graph-isomorphism testing, fixed labels are matched when they+--  are the same.  Variable labels may be matched with any other variable label.+--  Our definition of isomorphism (for RDF graphs) does not match variable labels+--  with fixed labels.++class (Eq lb, Show lb, Ord lb) => Label lb where+  +  -- | Does this node have a variable binding?+  labelIsVar  :: lb -> Bool           +    +  -- | Calculate the hash of the label using the supplied seed.+  labelHash   :: Int -> lb -> Int     +  +  -- could provide a default of +  --   labelHash = hashWithSalt+  -- but this would then force a Hashable constraint+    +  -- | Extract the local id from a variable node.                 +  getLocal    :: lb -> String+    +  -- | Make a label value from a local id.  +  makeLabel   :: String -> lb+    +  -- compare     :: lb -> lb -> Ordering+  -- compare l1 l2 = compare (show l1) (show l2)++-- | Arc type.+--+-- Prior to @0.7.0.0@ you could also use @asubj@, @apred@ and @aobj@+-- to access the elements of the arc.+--+data Arc lb = Arc +              { arcSubj :: lb  -- ^ The subject of the arc.+              , arcPred :: lb  -- ^ The predicate (property) of the arc.+              , arcObj :: lb   -- ^ The object of the arc.+              }+            deriving (Eq, Functor, F.Foldable, T.Traversable)++instance (Hashable lb) => Hashable (Arc lb) where+  hash (Arc s p o) = hash s `hashWithSalt` p `hashWithSalt` o+  hashWithSalt salt (Arc s p o) = salt `hashWithSalt` s `hashWithSalt` p `hashWithSalt` o++-- | Create an arc.+arc :: lb      -- ^ The subject of the arc.+       -> lb   -- ^ The predicate of the arc.+       -> lb   -- ^ The object of the arc.+       -> Arc lb+arc = Arc++-- | Convert an Arc into a tuple.+arcToTriple :: Arc lb -> (lb,lb,lb)+arcToTriple (Arc s p o) = (s, p, o)++-- | Create an Arc from a tuple.+arcFromTriple :: (lb,lb,lb) -> Arc lb+arcFromTriple (s,p,o) = Arc s p o++instance Ord lb => Ord (Arc lb) where+  compare (Arc s1 p1 o1) (Arc s2 p2 o2)+    | cs /= EQ = cs+    | cp /= EQ = cp+    | otherwise = co+    where+      cs = compare s1 s2+      cp = compare p1 p2+      co = compare o1 o2++  {- not needed+  (Arc s1 p1 o1) <= (Arc s2 p2 o2)+    | s1 /= s2 = s1 <= s2+    | p1 /= p2 = p1 <= p2+    | otherwise = o1 <= o2+  -}++instance (Show lb) => Show (Arc lb) where+    show (Arc lb1 lb2 lb3) =+        "("++ show lb1 ++","++ show lb2 ++","++ show lb3 ++")"++-- | Identify arcs.+type Selector lb = Arc lb -> Bool++-- | Does the arc contain the label in any position (subject, predicate, or object)?+hasLabel :: (Eq lb) => lb -> Arc lb -> Bool+hasLabel lbv lb = lbv `elem` arcLabels lb++-- | Return all the labels in an arc.+arcLabels :: Arc lb -> [lb]+arcLabels (Arc lb1 lb2 lb3) = [lb1,lb2,lb3]++-- | Return just the subject and object labels in the arc.+arcNodes :: Arc lb -> [lb]+arcNodes (Arc lb1 _ lb3) = [lb1,lb3]++--------------------------------------------------------------------------------+--+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke+--  All rights reserved.+--+--  This file is part of Swish.+--+--  Swish is free software; you can redistribute it and/or modify+--  it under the terms of the GNU General Public License as published by+--  the Free Software Foundation; either version 2 of the License, or+--  (at your option) any later version.+--+--  Swish is distributed in the hope that it will be useful,+--  but WITHOUT ANY WARRANTY; without even the implied warranty of+--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+--  GNU General Public License for more details.+--+--  You should have received a copy of the GNU General Public License+--  along with Swish; if not, write to:+--    The Free Software Foundation, Inc.,+--    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+--+--------------------------------------------------------------------------------
+ src/Swish/GraphMatch.hs view
@@ -0,0 +1,675 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}++--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  GraphMatch+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke+--  License     :  GPL V2+--+--  Maintainer  :  Douglas Burke+--  Stability   :  experimental+--  Portability :  FlexibleInstances, TypeSynonymInstances, MultiParamTypeClasses+--+--  This module contains graph-matching logic.+--+--  The algorithm used is derived from a paper on RDF graph matching+--  by Jeremy Carroll <http://www.hpl.hp.com/techreports/2001/HPL-2001-293.html>.+--+--------------------------------------------------------------------------------++module Swish.GraphMatch+      ( graphMatch,+        -- * Exported for testing+        LabelMap, GenLabelMap(..), LabelEntry, GenLabelEntry(..),+        ScopedLabel(..), makeScopedLabel, makeScopedArc,+        LabelIndex, EquivalenceClass, nullLabelVal, emptyMap,+        labelIsVar, labelHash,+        mapLabelIndex, setLabelHash, newLabelMap,+        graphLabels, assignLabelMap, newGenerationMap,+        graphMatch1, graphMatch2, equivalenceClasses, reclassify+      ) where++import Swish.GraphClass (Arc(..), Label(..))+import Swish.GraphClass (arcLabels, hasLabel, arcToTriple)++import Swish.Utils.ListHelpers (equiv)++import Control.Exception.Base (assert)+import Control.Arrow (second)++import Data.Ord (comparing)+import Data.List (foldl', nub, sortBy, groupBy, partition)+import Data.LookupMap (LookupEntryClass(..), LookupMap(..))+import Data.LookupMap (makeLookupMap, listLookupMap, mapFind, mapReplaceAll,+                       mapAddIfNew, mapReplaceMap, mapMerge)+import Data.Function (on)  +import Data.Hashable (combine)+import Data.Word++import qualified Data.List as L++--------------------------+--  Label index value type+--------------------------+--++-- | LabelIndex is a unique value assigned to each label, such that+--  labels with different values are definitely different values+--  in the graph;  e.g. do not map to each other in the graph+--  bijection.  The first member is a generation counter that+--  ensures new values are distinct from earlier passes.++type LabelIndex = (Word32, Word32)++-- | The null, or empty, index value.+nullLabelVal :: LabelIndex+nullLabelVal = (0, 0)++-----------------------+--  Label mapping types+-----------------------++-- | A Mapping between a label and a value (e.g. an index+-- value).+data (Label lb) => GenLabelEntry lb lv = LabelEntry lb lv++-- | A label associated with a 'LabelIndex'+type LabelEntry lb = GenLabelEntry lb LabelIndex++instance (Label lb, Eq lb, Show lb, Eq lv, Show lv)+    => LookupEntryClass (GenLabelEntry lb lv) lb lv where+    keyVal   (LabelEntry k v) = (k,v)+    newEntry (k,v)            = LabelEntry k v++instance (Label lb, Eq lb, Show lb, Eq lv, Show lv)+    => Show (GenLabelEntry lb lv) where+    show = entryShow++instance (Label lb, Eq lb, Show lb, Eq lv, Show lv)+    => Eq (GenLabelEntry lb lv) where+    (==) = entryEq++-- | Type for label->index lookup table+data (Label lb, Eq lv, Show lv) => GenLabelMap lb lv =+    LabelMap Word32 (LookupMap (GenLabelEntry lb lv))++-- | A label lookup table specialized to 'LabelIndex' indices.+type LabelMap lb = GenLabelMap lb LabelIndex++instance (Label lb) => Show (LabelMap lb) where+    show = showLabelMap++instance (Label lb) => Eq (LabelMap lb) where+    LabelMap gen1 lmap1 == LabelMap gen2 lmap2 =+        gen1 == gen2 && es1 `equiv` es2+        where+            es1 = listLookupMap lmap1+            es2 = listLookupMap lmap2++-- | The empty label map table.+emptyMap :: (Label lb) => LabelMap lb+emptyMap = LabelMap 1 $ makeLookupMap []++--------------------------+--  Equivalence class type+--------------------------+--++-- | Type for equivalence class description+--  (An equivalence class is a collection of labels with+--  the same 'LabelIndex' value.)++type EquivalenceClass lb = (LabelIndex, [lb])++{-+ecIndex :: EquivalenceClass lb -> LabelIndex+ecIndex = fst+-}++ecLabels :: EquivalenceClass lb -> [lb]+ecLabels = snd++{-+ecSize :: EquivalenceClass lb -> Int+ecSize = length . ecLabels+-}++ecRemoveLabel :: (Label lb) => EquivalenceClass lb -> lb -> EquivalenceClass lb+ecRemoveLabel xs l = second (L.delete l) xs++------------------------------------------------------------+--  Filter, ungroup, sort and group pairs by first member+------------------------------------------------------------++{-+pairSelect :: ((a,b) -> Bool) -> ((a,b) -> c) -> [(a,b)] -> [c]+pairSelect p f as = map f (filter p as)+-}++-- | Ungroup the pairs.+pairUngroup :: +    (a,[b])    -- ^ Given (a,bs)+    -> [(a,b)] -- ^ Returns (a,b) for all b in bs+pairUngroup (a,bs) = [ (a,b) | b <- bs ]++-- | Order the pairs based on the first argument.+pairSort :: (Ord a) => [(a,b)] -> [(a,b)]+pairSort = sortBy (comparing fst)++-- | Group the pairs based on the first argument.+pairGroup :: (Ord a) => [(a,b)] -> [(a,[b])]+pairGroup = map (factor . unzip) . groupBy eqFirst . pairSort +    where+      -- as is not [] by construction, but would be nice to have+      -- this enforced by the types+      factor (as, bs) = (head as, bs)+      eqFirst = (==) `on` fst++------------------------------------------------------------+--  Augmented graph label value - for graph matching+------------------------------------------------------------+--+-- | This instance of class label adds a graph identifier to+--  each variable label, so that variable labels from+--  different graphs are always seen as distinct values.+--+--  The essential logic added by this class instance is embodied+--  in the eq and hash functions.  Note that variable label hashes+--  depend only on the graph in which they appear, and non-variable+--  label hashes depend only on the variable.  Label hash values are+--  used when initializing a label equivalence-class map (and, for+--  non-variable labels, also for resolving hash collisions).++data (Label lb) => ScopedLabel lb = ScopedLabel Int lb++-- | Create a scoped label given an identifier and label.+makeScopedLabel :: (Label lb) => Int -> lb -> ScopedLabel lb+makeScopedLabel = ScopedLabel ++-- | Create an arc containining a scoped label with the given identifier.+makeScopedArc :: (Label lb) => Int -> Arc lb -> Arc (ScopedLabel lb)+makeScopedArc scope = fmap (ScopedLabel scope)++instance (Label lb) => Label (ScopedLabel lb) where+    getLocal  lab    = error $ "getLocal for ScopedLabel: "++show lab+    makeLabel locnam = error $ "makeLabel for ScopedLabel: "++locnam+    labelIsVar (ScopedLabel _ lab)   = labelIsVar lab+    labelHash seed (ScopedLabel scope lab)+        | labelIsVar lab    = seed `combine` scope -- MH.hash seed $ show scope ++ "???"+        | otherwise         = labelHash seed lab++instance (Label lb) => Eq (ScopedLabel lb) where+    (ScopedLabel s1 l1) == (ScopedLabel s2 l2)+        = l1 == l2 && s1 == s2++instance (Label lb) => Show (ScopedLabel lb) where+    show (ScopedLabel s1 l1) = show s1 ++ ":" ++ show l1++instance (Label lb) => Ord (ScopedLabel lb) where+    compare (ScopedLabel s1 l1) (ScopedLabel s2 l2) =+        case compare s1 s2 of+            LT -> LT+            EQ -> compare l1 l2+            GT -> GT++-- QUS: why doesn't this return Maybe (LabelMap (ScopedLabel lb)) ?++-- | Graph matching function accepting two lists of arcs and+--  returning a node map if successful+--+graphMatch :: (Label lb) =>+    (lb -> lb -> Bool)+    -- ^ a function that tests for additional constraints+    --   that may prevent the matching of a supplied pair+    --   of nodes.  Returns `True` if the supplied nodes may be+    --   matched.  (Used in RDF graph matching for checking+    --   that formula assignments are compatible.)+    -> [Arc lb] -- ^ the first graph to be compared, as a list of arcs+    -> [Arc lb] -- ^ the second graph to be compared, as a list of arcs+    -> (Bool, LabelMap (ScopedLabel lb))+    -- ^ If the first element is `True` then the second element maps each label+    --   to an equivalence class identifier, otherwise it is just+    --   `emptyMap`.+    --+graphMatch matchable gs1 gs2 =+    let+        sgs1    = {- trace "sgs1 " $ -} map (makeScopedArc 1) gs1+        sgs2    = {- trace "sgs2 " $ -} map (makeScopedArc 2) gs2+        ls1     = {- traceShow "ls1 " $ -} graphLabels sgs1+        ls2     = {- traceShow "ls2 " $ -} graphLabels sgs2+        lmap    = {- traceShow "lmap " $ -}+                  newGenerationMap $+                  assignLabelMap ls1 $+                  assignLabelMap ls2 emptyMap+        ec1     = {- traceShow "ec1 " $ -} equivalenceClasses lmap ls1+        ec2     = {- traceShow "ec2 " $ -} equivalenceClasses lmap ls2+        ecpairs = zip (pairSort ec1) (pairSort ec2)+        matchableScoped (ScopedLabel _ l1) (ScopedLabel _ l2) = matchable l1 l2+        match   = graphMatch1 False matchableScoped sgs1 sgs2 lmap ecpairs+    in+        if length ec1 /= length ec2 then (False,emptyMap) else match++--  TODO:+--+--    * replace Equivalence class pair by @(index,[lb],[lb])@ ?+--+--    * possible optimization:  the @graphMapEq@ test should be+--      needed only if `graphMatch2` has been used to guess a+--      mapping;  either: +--          a) supply flag saying guess has been used, or+--          b) move test to `graphMatch2` and use different+--             test to prevent rechecking for each guess used.+--++-- | Recursive graph matching function+--+--  This function assumes that no variable label appears in both graphs.+--  (Function `graphMatch`, which calls this, ensures that all variable+--  labels are distinct.)+--++graphMatch1 :: +  (Label lb) +  => Bool+  -- ^ `True` if a guess has been used before trying this comparison,+  --   `False` if nodes are being matched without any guesswork+  -> (lb -> lb -> Bool)+  -- ^ Test for additional constraints that may prevent the matching+  --  of a supplied pair of nodes.  Returns `True` if the supplied+  --  nodes may be matched.+  -> [Arc lb] +  -- ^ (@gs1@ argument)+  --   first of two lists of arcs (triples) to be compared+  -> [Arc lb]+  -- ^ (@gs2@ argument)+  --   secind of two lists of arcs (triples) to be compared+  -> LabelMap lb+  -- ^ the map so far used to map label values to equivalence class+  --   values+  -> [(EquivalenceClass lb,EquivalenceClass lb)]+  -- ^ (the @ecpairs@ argument) list of pairs of corresponding+  --   equivalence classes of nodes from @gs1@ and @gs2@ that have not+  --   been confirmed in 1:1 correspondence with each other.  Each+  --   pair of equivalence classes contains nodes that must be placed+  --   in 1:1 correspondence with each other.+  --+  -> (Bool,LabelMap lb)+  -- ^ the pair @(match, map)@ where @match@ is @True@ if the supplied+  --   sets of arcs can be matched, in which case @map@ is a+  --   corresponding map from labels to equivalence class identifiers.+  --   When @match@ is @False@, @map@ is the most detailed equivalence+  --   class map obtained before a mismatch was detected or a guess+  --   was required -- this is intended to help identify where the+  --   graph mismatch may be.+graphMatch1 guessed matchable gs1 gs2 lmap ecpairs =+    let+        (secs,mecs) = partition uniqueEc ecpairs+        uniqueEc ( (_,[_])  , (_,[_])  ) = True+        uniqueEc (  _       ,  _       ) = False+        +        doMatch  ( (_,[l1]) , (_,[l2]) ) = labelMatch matchable lmap l1 l2+        doMatch  x = error $ "doMatch failue: " ++ show x -- keep -Wall happy++        ecEqSize ( (_,ls1)  , (_,ls2)  ) = length ls1 == length ls2+        eSize    ( (_,ls1)  , _        ) = length ls1+        ecCompareSize = comparing eSize+        (lmap',mecs',newEc,matchEc) = reclassify gs1 gs2 lmap mecs+        match2 = graphMatch2 matchable gs1 gs2 lmap $ sortBy ecCompareSize mecs+    in+        -- trace ("graphMatch1\nsingle ECs:\n"++show secs+++        --                   "\nmultiple ECs:\n"++show mecs+++        --                   "\n\n") $+        --  if mismatch in singleton equivalence classes, fail+        if not $ all doMatch secs then (False,lmap)+        else+        --  if no multi-member equivalence classes,+        --  check and return label map supplied+        -- trace ("graphMatch1\ngraphMapEq: "++show (graphMapEq lmap gs1 gs2)) $+        if null mecs then (graphMapEq lmap gs1 gs2,lmap)+        else+        --  if size mismatch in equivalence classes, fail+        -- trace ("graphMatch1\nall ecEqSize mecs: "++show (all ecEqSize mecs)) $+        +          --  invoke reclassification, and deal with result+          if not (all ecEqSize mecs) || not matchEc+            then (False, lmap)+            else if newEc+                   then graphMatch1 guessed matchable gs1 gs2 lmap' mecs'+                        --  if guess does not result in a match, return supplied label map+                   else if fst match2 then match2 else (False, lmap)++{-+          if not $ all ecEqSize mecs then (False,lmap)+        else+        if not matchEc then (False,lmap)+        else+        if newEc then graphMatch1 guessed matchable gs1 gs2 lmap' mecs'+        else+        if fst match2 then match2 else (False,lmap)+-}++-- | Auxiliary graph matching function+--+--  This function is called when deterministic decomposition of node+--  mapping equivalence classes has run its course.+--+--  It picks a pair of equivalence classes in ecpairs, and arbitrarily matches+--  pairs of nodes in those equivalence classes, recursively calling the+--  graph matching function until a suitable node mapping is discovered+--  (success), or until all such pairs have been tried (failure).+--+--  This function represents a point to which arbitrary choices are backtracked.+--  The list comprehension 'glp' represents the alternative choices at the+--  point of backtracking+--+--  The selected pair of nodes are placed in a new equivalence class based on their+--  original equivalence class value, but with a new NodeVal generation number.++graphMatch2 :: (Label lb) => (lb -> lb -> Bool)+    -> [Arc lb] -> [Arc lb]+    -> LabelMap lb -> [(EquivalenceClass lb,EquivalenceClass lb)]+    -> (Bool,LabelMap lb)+graphMatch2 _         _   _   _    [] = error "graphMatch2 sent an empty list" -- To keep -Wall happy+graphMatch2 matchable gs1 gs2 lmap ((ec1@(ev1,ls1),ec2@(ev2,ls2)):ecpairs) =+    let+        v1 = snd ev1+        --  Return any equivalence-mapping obtained by matching a pair+        --  of labels in the supplied list, or Nothing.+        try []            = (False,lmap)+        try ((l1,l2):lps) = if isEquiv try1 l1 l2 then try1 else try lps+            where+                try1     = graphMatch1 True matchable gs1 gs2 lmap' ecpairs'+                lmap'    = newLabelMap lmap [(l1,v1),(l2,v1)]+                ecpairs' = ((ev',[l1]),(ev',[l2])):ec':ecpairs+                ev'      = mapLabelIndex lmap' l1+                ec'      = (ecRemoveLabel ec1 l1, ecRemoveLabel ec2 l2)+                -- [[[TODO: replace this: if isJust try ?]]]+                isEquiv (False,_)   _  _  = False+                isEquiv (True,lm) x1 x2 =+                    mapLabelIndex m1 x1 == mapLabelIndex m2 x2+                    where+                        m1 = remapLabels gs1 lm [x1]+                        m2 = remapLabels gs2 lm [x2]+        --  glp is a list of label-pair candidates for matching,+        --  selected from the first label-equivalence class.+        --  NOTE:  final test is call of external matchable function+        glp = [ (l1,l2) | l1 <- ls1 , l2 <- ls2 , matchable l1 l2 ]+    in+        assert (ev1==ev2) -- "GraphMatch2: Equivalence class value mismatch" $+        $ try glp++-- this was in Swish.Utils.MiscHelpers along with a simple hash-based function+-- based on Sedgewick, Algorithms in C, p233. As we have now moved to using+-- Data.Hashable it is not clear whether this is still necessary or sensible.+--+hashModulus :: Int+hashModulus = 16000001++-- | Returns a string representation  of a LabelMap value+--+showLabelMap :: (Label lb) => LabelMap lb -> String+showLabelMap (LabelMap gn lmap) =+    "LabelMap gen="++ Prelude.show gn ++", map="+++    foldl' (++) "" (map (("\n    "++) . Prelude.show) es)+    where+        es = listLookupMap lmap++-- | Map a label to its corresponding label index value in the supplied LabelMap+--+mapLabelIndex :: (Label lb) => LabelMap lb -> lb -> LabelIndex+mapLabelIndex (LabelMap _ lxms) lb = mapFind nullLabelVal lb lxms++-- | Confirm that a given pair of labels are matchable, and are+--  mapped to the same value by the supplied label map+--+labelMatch :: (Label lb)+    =>  (lb -> lb -> Bool) -> LabelMap lb -> lb -> lb -> Bool+labelMatch matchable lmap l1 l2 =+    matchable l1 l2 && (mapLabelIndex lmap l1 == mapLabelIndex lmap l1)++-- | Replace selected values in a label map with new values from the supplied+--  list of labels and new label index values.  The generation number is+--  supplied from the current label map.  The generation number in the+--  resulting label map is incremented.+--+newLabelMap :: (Label lb) => LabelMap lb -> [(lb, Word32)] -> LabelMap lb+newLabelMap lmap []       = newGenerationMap lmap+newLabelMap lmap (lv:lvs) = setLabelHash (newLabelMap lmap lvs) lv++-- | Replace a label and its associated value in a label map+--  with a new value using the supplied hash value and the current+--  `LabelMap` generation number.  If the key is not found, then no change+--  is made to the label map.++setLabelHash :: (Label lb)+    => LabelMap lb -> (lb, Word32) -> LabelMap lb+setLabelHash  (LabelMap g lmap) (lb,lh) =+    LabelMap g ( mapReplaceAll lmap $ newEntry (lb,(g,lh)) )++-- | Increment the generation of the label map.+--+--  Returns a new label map identical to the supplied value+--  but with an incremented generation number.+--+newGenerationMap :: (Label lb) => LabelMap lb -> LabelMap lb+newGenerationMap (LabelMap g lvs) = LabelMap (g+1) lvs++-- | Scan label list, assigning initial label map values,+--  adding new values to the label map supplied.+--+--  Label map values are assigned on the basis of the+--  label alone, without regard for it's connectivity in+--  the graph.  (cf. `reclassify`).+--+--  All variable node labels are assigned the same initial+--  value, as they may be matched with each other.+--+assignLabelMap :: (Label lb) => [lb] -> LabelMap lb -> LabelMap lb+assignLabelMap ns lmap = foldl' (flip assignLabelMap1) lmap ns++assignLabelMap1 :: (Label lb) => lb -> LabelMap lb -> LabelMap lb+assignLabelMap1 lab (LabelMap g lvs) = LabelMap g lvs'+    where+        lvs' = mapAddIfNew lvs $ newEntry (lab,(g,initVal lab))++--  Calculate initial value for a node++initVal :: (Label lb) => lb -> Word32+initVal = fromIntegral . hashVal 0++hashVal :: (Label lb) => Int -> lb -> Int+hashVal seed lab =+  if labelIsVar lab then seed `combine` 23 else labelHash seed lab+  -- if labelIsVar lab then hash seed "???" else labelHash seed lab++-- | Return the equivalence classes of the supplied nodes +-- using the label map.+equivalenceClasses :: +  (Label lb) +  => LabelMap lb -- ^ label map+  -> [lb]        -- ^ list of nodes to be reclassified+  -> [EquivalenceClass lb]+equivalenceClasses lmap ls =+    pairGroup $ map labelPair ls+    where+        labelPair l = (mapLabelIndex lmap l,l)++-- | Reclassify labels+--+--  Examines the supplied label equivalence classes (based on the supplied+--  label map), and evaluates new equivalence subclasses based on node+--  values and adjacency (for variable nodes) and rehashing+--  (for non-variable nodes).+--+--  Note, assumes that all all equivalence classes supplied are+--  non-singletons;  i.e. contain more than one label.+--+reclassify :: +  (Label lb) +  => [Arc lb] +  -- ^ (the @gs1@ argument) the first of two lists of arcs (triples) to perform a+  --   basis for reclassifying the labels in the first equivalence+  --   class in each pair of @ecpairs@.+  -> [Arc lb]+  -- ^ (the @gs2@ argument) the second of two lists of arcs (triples) to perform a+  --   basis for reclassifying the labels in the second equivalence+  --   class in each pair of the @ecpairs@ argument+  -> LabelMap lb +  -- ^ the label map used for classification of the labels in+  --   the supplied equivalence classes+  -> [(EquivalenceClass lb,EquivalenceClass lb)]+  -- ^ (the @ecpairs@ argument) a list of pairs of corresponding equivalence classes of+  --   nodes from @gs1@ and @gs2@ that have not been confirmed+  --   in 1:1 correspondence with each other.+  -> (LabelMap lb,[(EquivalenceClass lb,EquivalenceClass lb)],Bool,Bool)+  -- ^ The output tuple consists of:+  --+  --  1) a revised label map reflecting the reclassification+  --+  --  2) a new list of equivalence class pairs based on the+  --   new node map+  --+  --  3) if the reclassification partitions any of the+  --     supplied equivalence classes then `True`, else `False`+  --+  --  4) if reclassification results in each equivalence class+  --     being split same-sized equivalence classes in the two graphs,+  --     then `True`, otherwise `False`.++reclassify gs1 gs2 lmap@(LabelMap _ lm) ecpairs =+    assert (gen1==gen2) -- "Label map generation mismatch"+      (LabelMap gen1 lm',ecpairs',newPart,matchPart)+    where+        LabelMap gen1 lm1 =+            remapLabels gs1 lmap $ foldl1 (++) $ map (ecLabels . fst) ecpairs+        LabelMap gen2 lm2 =+            remapLabels gs2 lmap $ foldl1 (++) $ map (ecLabels . snd) ecpairs+        lm' = mapReplaceMap lm $ mapMerge lm1 lm2+        +        tmap f (a,b) = (f a, f b)+        +        -- ecGroups :: [([EquivalenceClass lb],[EquivalenceClass lb])]+        ecGroups  = map (tmap remapEc) ecpairs+        ecpairs'  = concatMap (uncurry zip) ecGroups+        newPart   = any pairG1 lenGroups+        matchPart = all pairEq lenGroups+        lenGroups = map (tmap length) ecGroups+        pairEq = uncurry (==)+        pairG1 (p1,p2) = p1 > 1 || p2 > 1+        remapEc = pairGroup . map (newIndex lm') . pairUngroup +        newIndex x (_,lab) = (mapFind nullLabelVal lab x,lab)++-- | Calculate a new index value for a supplied list of labels based on the+--  supplied label map and adjacency calculations in the supplied graph+--+remapLabels :: +  (Label lb) +  => [Arc lb] -- ^ arcs used for adjacency calculations when remapping+  -> LabelMap lb -- ^ the current label index values+  -> [lb] -- ^ the graph labels for which new mappings are to be created+  -> LabelMap lb+  -- ^ the updated label map containing recalculated label index values+  -- for the given graph labels. The label map generation number is+  -- incremented by 1.+remapLabels gs lmap@(LabelMap gen _) ls =+    LabelMap gen' (LookupMap newEntries)+    where+        gen'                = gen+1+        newEntries          = [ newEntry (l, (gen', fromIntegral (newIndex l))) | l <- ls ]+        newIndex l+            | labelIsVar l  = mapAdjacent l                 -- adjacency classifies variable labels+            | otherwise     = hashVal (fromIntegral gen) l  -- otherwise rehash (to disentangle collisions)+        -- mapAdjacent l       = sum (sigsOver l) `rem` hashModulus+        mapAdjacent l       = sum (sigsOver l) `combine` hashModulus -- is this a sensible replacement for `rem` MH.hashModulus        +        sigsOver l          = select (hasLabel l) gs (arcSignatures lmap gs)++-- |Select is like filter, except that it tests one list to select+--  elements from a second list.+select :: ( a -> Bool ) -> [a] -> [b] -> [b]+select _ [] []           = []+select f (e1:l1) (e2:l2)+    | f e1      = e2 : select f l1 l2+    | otherwise = select f l1 l2+select _ _ _    = error "select supplied with different length lists"+++-- | Return list of distinct labels used in a graph++graphLabels :: (Label lb) => [Arc lb] -> [lb]+graphLabels = nub . concatMap arcLabels++-- TODO: worry about overflow?++-- | Calculate a signature value for each arc that can be used in constructing an+--   adjacency based value for a node.  The adjacancy value for a label is obtained+--   by summing the signatures of all statements containing that label.+--+arcSignatures :: +  (Label lb) +  => LabelMap lb -- ^ the current label index values+  -> [Arc lb] -- ^ calculate signatures for these arcs+  -> [Int] -- ^ the signatures of the arcs+arcSignatures lmap =+    map (sigCalc . arcToTriple) +    where+        sigCalc (s,p,o)  =+            fromIntegral ( labelVal2 s ++                           labelVal2 p * 3 ++                           labelVal2 o * 5 )+            `combine` hashModulus+            -- `rem` hashModulus+          +        labelVal         = mapLabelIndex lmap+        labelVal2        = uncurry (*) . labelVal++-- | Return a new graph that is supplied graph with every node/arc+--  mapped to a new value according to the supplied function.+--+--  Used for testing for graph equivalence under a supplied+--  label mapping;  e.g.+--+--  >  if ( graphMap nodeMap gs1 ) `equiv` ( graphMap nodeMap gs2 ) then (same)+--+graphMap :: (Label lb) => LabelMap lb -> [Arc lb] -> [Arc LabelIndex]+graphMap = map . fmap . mapLabelIndex  -- graphMapStmt++-- | Compare a pair of graphs for equivalence under a given mapping+--   function.+--+--  This is used to perform the ultimate test that two graphs are+--  indeed equivalent:  guesswork in `graphMatch2` means that it is+--  occasionally possible to construct a node mapping that generates+--  the required singleton equivalence classes, but does not fully+--  reflect the topology of the graphs.++graphMapEq :: (Label lb) => LabelMap lb -> [Arc lb] -> [Arc lb] -> Bool+graphMapEq lmap gs1 gs2 = graphMap lmap gs1 `equiv` graphMap lmap gs2++--------------------------------------------------------------------------------+--+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke+--  All rights reserved.+--+--  This file is part of Swish.+--+--  Swish is free software; you can redistribute it and/or modify+--  it under the terms of the GNU General Public License as published by+--  the Free Software Foundation; either version 2 of the License, or+--  (at your option) any later version.+--+--  Swish is distributed in the hope that it will be useful,+--  but WITHOUT ANY WARRANTY; without even the implied warranty of+--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+--  GNU General Public License for more details.+--+--  You should have received a copy of the GNU General Public License+--  along with Swish; if not, write to:+--    The Free Software Foundation, Inc.,+--    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+--+--------------------------------------------------------------------------------
+ src/Swish/GraphMem.hs view
@@ -0,0 +1,172 @@+{-# LANGUAGE DeriveFoldable #-}+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE DeriveTraversable #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}++--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  GraphMem+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke+--  License     :  GPL V2+--+--  Maintainer  :  Douglas Burke+--  Stability   :  experimental+--  Portability :  DeriveFoldable, DeriveFunctor, DeriveTraversable, FlexibleInstances, MultiParamTypeClasses+--+--  This module defines a simple memory-based graph instance.+--+--------------------------------------------------------------------------------++------------------------------------------------------------+-- Simple labelled directed graph value+------------------------------------------------------------++module Swish.GraphMem+    ( GraphMem(..)+    , LabelMem(..)+    , setArcs, getArcs, addGraphs, delete, extract, labels+    , labelIsVar, labelHash+      -- For debug/test:+    , matchGraphMem+    ) where++import Swish.GraphClass+import Swish.GraphMatch++import Data.Hashable (Hashable(..), combine)+import Data.Monoid (Monoid(..))+import Data.Ord (comparing)++import qualified Data.Foldable as F+import qualified Data.Traversable as T++-- | Simple memory-based graph type. ++data GraphMem lb = GraphMem { arcs :: [Arc lb] }+                   deriving (Functor, F.Foldable, T.Traversable)+                            +instance (Label lb) => LDGraph GraphMem lb where+    emptyGraph   = GraphMem []+    getArcs      = arcs+    setArcs g as = g { arcs=as }++instance (Label lb) => Eq (GraphMem lb) where+    (==) = graphEq++instance (Label lb) => Show (GraphMem lb) where+    show = graphShow++instance (Label lb) => Monoid (GraphMem lb) where+    mempty  = emptyGraph+    mappend = addGraphs++graphShow   :: (Label lb) => GraphMem lb -> String+graphShow g = "Graph:" ++ foldr ((++) . ("\n    " ++) . show) "" (arcs g)++{-+toGraph :: (Label lb) => [Arc lb] -> GraphMem lb+toGraph as = GraphMem { arcs=nub as }+-}++-- |  Return Boolean graph equality++graphEq :: (Label lb) => GraphMem lb -> GraphMem lb -> Bool+graphEq g1 g2 = fst ( matchGraphMem g1 g2 )++-- | GraphMem matching function accepting GraphMem value and returning+--  node map if successful+--+matchGraphMem ::+  (Label lb)+  => GraphMem lb +  -> GraphMem lb+  -> (Bool,LabelMap (ScopedLabel lb))+  -- ^ if the first element is @True@ then the second value is a label+  --   map that maps each label to an equivalence-class identifier,+  --   otherwise `emptyMap`.+  --+matchGraphMem g1 g2 =+    let+        gs1     = arcs g1+        gs2     = arcs g2+        matchable l1 l2+            | labelIsVar l1 && labelIsVar l2 = True+            | labelIsVar l1 || labelIsVar l2 = False+            | otherwise                      = l1 == l2+    in+        graphMatch matchable gs1 gs2++{-+-- |  Return bijection between two graphs, or empty list+graphBiject :: (Label lb) => GraphMem lb -> GraphMem lb -> [(lb,lb)]+graphBiject g1 g2 = if null lmap then [] else zip (sortedls g1) (sortedls g2)+    where+        lmap        = graphMatch g1 g2+        sortedls g  = map snd $+                      (sortBy indexComp) $+                      equivalenceClasses (graphLabels $ arcs g) lmap+        classComp ec1 ec2 = indexComp (classIndexVal ec1) (classIndexVal ec2)+        indexComp (g1,v1) (g2,v2)+            | g1 == g2  = compare v1 v2+            | otherwise = compare g1 g2+-}++-- |  Minimal graph label value - for testing++data LabelMem+    = LF String+    | LV String++instance Hashable LabelMem where+  hash (LF l) = 1 `hashWithSalt` l+  hash (LV l) = 2 `hashWithSalt` l+  hashWithSalt salt (LF l) = salt `combine` 1 `hashWithSalt` l+  hashWithSalt salt (LV l) = salt `combine` 2 `hashWithSalt` l++instance Label LabelMem where+    labelIsVar (LV _)   = True+    labelIsVar _        = False+    getLocal   (LV loc) = loc+    getLocal   lab      = error "getLocal of non-variable label: " ++ show lab+    makeLabel           = LV +    labelHash = hashWithSalt++instance Eq LabelMem where+    (LF l1) == (LF l2)  = l1 == l2+    (LV l1) == (LV l2)  = l1 == l2+    _ == _              = False++instance Show LabelMem where+    show (LF l1)        = '!' : l1+    show (LV l2)        = '?' : l2++instance Ord LabelMem where+    compare = comparing show ++--------------------------------------------------------------------------------+--+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke+--  All rights reserved.+--+--  This file is part of Swish.+--+--  Swish is free software; you can redistribute it and/or modify+--  it under the terms of the GNU General Public License as published by+--  the Free Software Foundation; either version 2 of the License, or+--  (at your option) any later version.+--+--  Swish is distributed in the hope that it will be useful,+--  but WITHOUT ANY WARRANTY; without even the implied warranty of+--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+--  GNU General Public License for more details.+--+--  You should have received a copy of the GNU General Public License+--  along with Swish; if not, write to:+--    The Free Software Foundation, Inc.,+--    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+--+--------------------------------------------------------------------------------
+ src/Swish/GraphPartition.hs view
@@ -0,0 +1,591 @@+--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  GraphPartition+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke+--  License     :  GPL V2+--+--  Maintainer  :  Douglas Burke+--  Stability   :  experimental+--  Portability :  H98+--+--  This module contains functions for partitioning a graph into subgraphs+--  that rooted from different subject nodes.+--+--------------------------------------------------------------------------------++module Swish.GraphPartition+    ( PartitionedGraph(..), getArcs, getPartitions+    , GraphPartition(..), node, toArcs+    , partitionGraph, comparePartitions+    , partitionShowP+    )+where++import Swish.GraphClass (Label(..), Arc(..))++import Control.Monad.State (MonadState(..), State)+import Control.Monad.State (evalState)++import Data.List (foldl', partition)+import Data.List.NonEmpty (NonEmpty(..), (<|))+import Data.Maybe (mapMaybe)++import qualified Data.List.NonEmpty as NE++------------------------------------------------------------+--  Data type for a partitioned graph+------------------------------------------------------------++-- |Representation of a graph as a collection of (possibly nested)+--  partitions.  Each node in the graph appears at least once as the+--  root value of a 'GraphPartition' value:+--+--  * Nodes that are the subject of at least one statement appear as+--    the first value of exactly one 'PartSub' constructor, and may+--    also appear in any number of 'PartObj' constructors.+--+--  * Nodes appearing only as objects of statements appear only in+--    'PartObj' constructors.++data PartitionedGraph lb = PartitionedGraph [GraphPartition lb]+    deriving (Eq,Show)++-- | Returns all the arcs in the partitioned graph.+getArcs :: PartitionedGraph lb -> [Arc lb]+getArcs (PartitionedGraph ps) = concatMap toArcs ps++-- | Returns a list of partitions.+getPartitions :: PartitionedGraph lb -> [GraphPartition lb]+getPartitions (PartitionedGraph ps) = ps++-- Note: do not use the LabelledPartition local type here since we do+-- not want it to appear in the documentation.++-- | Represent a partition of a graph by a node and (optional) contents.+data GraphPartition lb+    = PartObj lb+    | PartSub lb (NonEmpty (lb,GraphPartition lb))++-- | Returns the node for the partition.+node :: GraphPartition lb -> lb+node (PartObj ob)   = ob+node (PartSub sb _) = sb++-- | Creates a list of arcs from the partition. The empty+-- list is returned for `PartObj`.+toArcs :: GraphPartition lb -> [Arc lb]+toArcs (PartObj _)      = []+toArcs (PartSub sb prs) = concatMap toArcs1 $ NE.toList prs+    where+        toArcs1 (pr,ob) = Arc sb pr (node ob) : toArcs ob++-- | Equality is based on total structural equivalence+-- rather than graph equality.+instance (Label lb) => Eq (GraphPartition lb) where+    (PartObj o1)    == (PartObj o2)    = o1 == o2+    (PartSub s1 p1) == (PartSub s2 p2) = s1 == s2 && p1 == p2+    _               == _               = False++instance (Label lb) => Show (GraphPartition lb) where+    show = partitionShow++-- can we just say +--  partitionShow = partitionShowP ""+-- ?+partitionShow :: (Label lb) => GraphPartition lb -> String+partitionShow (PartObj ob)             = show ob+partitionShow (PartSub sb (pr :| prs)) =+    "("++ show sb ++ " " ++ showpr pr ++ concatMap ((" ; "++).showpr) prs ++ ")"+    where+        showpr (a,b) = show a ++ " " ++ show b++-- only used in Swish.Commands  ++-- | Convert a partition into a string with a leading separator string.+partitionShowP :: +    (Label lb) => +    String +    -> GraphPartition lb +    -> String+partitionShowP _    (PartObj ob)             = show ob+partitionShowP pref (PartSub sb (pr :| prs)) =+    pref++"("++ show sb ++ " " ++ showpr pr ++ concatMap (((pref++"  ; ")++).showpr) prs ++ ")"+    where+        showpr (a,b) = show a ++ " " ++ partitionShowP (pref++"  ") b++------------------------------------------------------------+--  Creating partitioned graphs+------------------------------------------------------------+--+-- |Turning a partitioned graph into a flat graph is easy.+--  The interesting challenge is to turn a flat graph into a+--  partitioned graph that is more useful for certain purposes.+--  Currently, I'm interested in:+--        +--  (1) isolating differences between graphs+--        +--  (2) pretty-printing graphs+--+--  For (1), the goal is to separate subgraphs that are known+--  to be equivalent from subgraphs that are known to be different,+--  such that: +--+--  * different sub-graphs are minimized,+--+--  * different+--  sub-graphs are placed into 1:1 correspondence (possibly with null+--  subgraphs), and+--+--  * only deterministic matching decisions are made.+--+--  For (2), the goal is to decide when a subgraph is to be treated+--  as nested in another partition, or treated as a new top-level partition.+--  If a subgraph is referenced by exactly one graph partition, it should+--  be nested in that partition, otherwise it should be a new top-level+--  partition.+--+--  Strategy.  Examining just subject and object nodes:+--+--  * all non-blank subject nodes are the root of a top-level partition+--+--  * blank subject nodes that are not the object of exactly one statement+--     are the root of a top-level partition.+--+--  * blank nodes referenced as the object of exactly 1 statement+--     of an existing partition are the root of a sub-partition of the+--     refering partition.+--+--  * what remain are circular chains of blank nodes not referenced+--     elsewhere:  for each such chain, pick a root node arbitrarily.+--+partitionGraph :: (Label lb) => [Arc lb] -> PartitionedGraph lb+partitionGraph [] = PartitionedGraph []+partitionGraph arcs =+    makePartitions fixs topv1 intv1+    where+        (fixs,vars)  = partition isNonVar $ collect arcSubj arcs+        vars1        = collectMore arcObj arcs vars+        (intv,topv)  = partition objOnce vars1+        intv1        = map stripObj intv+        topv1        = map stripObj topv+        isNonVar     = not . labelIsVar . fst+        objOnce      = isSingle . snd . snd+        isSingle [_] = True+        isSingle _   = False+        stripObj (k,(s,_)) = (k,s)++-- Local state type for partitioning function+type LabelledArcs lb = (lb, NonEmpty (Arc lb))+type LabelledPartition lb = (lb, GraphPartition lb)+type MakePartitionState lb = ([LabelledArcs lb], [LabelledArcs lb], [LabelledArcs lb])+type PState lb = State (MakePartitionState lb)++makePartitions :: +    (Eq lb) =>+    [LabelledArcs lb]+    -> [LabelledArcs lb]+    -> [LabelledArcs lb]+    -> PartitionedGraph lb+makePartitions fixs topv intv =+    PartitionedGraph $ evalState (makePartitions1 []) (fixs,topv,intv)++-- Use a state monad to keep track of arcs that have been incorporated into+-- the resulting list of graph partitions.  The collections of arcs used to+-- generate the list of partitions are supplied as the initial state of the+-- monad (see call of evalState above).+--+makePartitions1 :: +    (Eq lb) =>+    [LabelledArcs lb] +    -> PState lb [GraphPartition lb]+makePartitions1 [] = do+    s <- pickNextSubject+    if null s then return [] else makePartitions1 s+makePartitions1 (sub:subs) = do+    ph <- makePartitions2 sub+    pt <- makePartitions1 subs+    return $ ph++pt++makePartitions2 :: +    (Eq lb) =>+    LabelledArcs lb+    -> PState lb [GraphPartition lb]+makePartitions2 subs = do+    (part,moresubs) <- makeStatements subs+    moreparts <- if null moresubs+                 then return []+                 else makePartitions1 moresubs+    return $ part:moreparts++makeStatements :: +    (Eq lb) =>+    LabelledArcs lb+    -> PState lb (GraphPartition lb, [LabelledArcs lb])+makeStatements (sub,stmts) = do+    propmore <- mapM makeStatement (NE.toList stmts)+    let (props,moresubs) = unzip propmore+    return (PartSub sub (NE.fromList props), concat moresubs)+    -- return (PartSub sub props, concat moresubs)++makeStatement :: +    (Eq lb) =>+    Arc lb+    -> PState lb (LabelledPartition lb, [LabelledArcs lb])+makeStatement (Arc _ prop obj) = do+    intobj <- pickIntSubject obj+    (gpobj, moresubs) <- if null intobj+                         then do+                             ms <- pickVarSubject obj+                             return (PartObj obj,ms)+                         else makeStatements (head intobj)+    return ((prop,gpobj), moresubs)++pickNextSubject :: PState lb [LabelledArcs lb]+pickNextSubject = do+    (a1,a2,a3) <- get+    let (s,st) = case (a1,a2,a3) of+                   (s1h:s1t,s2,s3) -> ([s1h],(s1t,s2,s3))+                   ([],s2h:s2t,s3) -> ([s2h],([],s2t,s3))+                   ([],[],s3h:s3t) -> ([s3h],([],[],s3t))+                   ([],[],[])      -> ([]   ,([],[],[] ))+    put st+    return s++pickIntSubject :: (Eq lb) =>+    lb +    -> PState lb [LabelledArcs lb]+pickIntSubject sub = do+    (s1,s2,s3) <- get+    let varsub = removeBy (\x->(x==).fst) sub s3+    case varsub of+        Just (vs, s3new) -> put (s1,s2,s3new) >> return [vs]+        Nothing          -> return []++pickVarSubject :: +    (Eq lb) =>+    lb -> +    PState lb [LabelledArcs lb]+pickVarSubject sub = do+    (s1,s2,s3) <- get+    let varsub = removeBy (\x->(x==).fst) sub s2+    case varsub of+        Just (vs, s2new) -> put (s1,s2new,s3) >> return [vs]+        _                -> return []++------------------------------------------------------------+--  Other useful functions+------------------------------------------------------------++-- | Create a list of pairs of corresponding Partitions that+--  are unequal.+comparePartitions :: (Label lb) =>+    PartitionedGraph lb +    -> PartitionedGraph lb+    -> [(Maybe (GraphPartition lb), Maybe (GraphPartition lb))]+comparePartitions (PartitionedGraph gp1) (PartitionedGraph gp2) =+    comparePartitions1 (reverse gp1) (reverse gp2)++comparePartitions1 :: (Label lb) =>+    [GraphPartition lb] +    -> [GraphPartition lb]+    -> [(Maybe (GraphPartition lb),Maybe (GraphPartition lb))]+comparePartitions1 pg1 pg2 =+        ds ++ [ (Just r1p,Nothing) | r1p<-r1 ]+           ++ [ (Nothing,Just r2p) | r2p<-r2 ]+    where+        (ds,r1,r2) = listDifferences comparePartitions2 pg1 pg2++--  Compare two graph partitions, with three possible outcomes:+--    Nothing    -> no match+--    Just []    -> total match+--    Just [...] -> partial match, with mismatched sub-partitions listed.+--+--  A partial match occurs when the leading nodes are non-variable and+--  equal, but something else in the partition does not match.+--+--  A complete match can be achieved with variable nodes that have+--  different labels+--+comparePartitions2 :: (Label lb) =>+    GraphPartition lb +    -> GraphPartition lb+    -> Maybe [(Maybe (GraphPartition lb), Maybe (GraphPartition lb))]+comparePartitions2 (PartObj l1) (PartObj l2) =+    if matchNodes l1 l2 then Just [] else Nothing+comparePartitions2 pg1@(PartSub l1 p1s) pg2@(PartSub l2 p2s) =+    if match then comp1 else Nothing+    where+        comp1  = case comparePartitions3 l1 l2 p1s p2s of+                    Nothing -> if matchVar then Nothing+                                           else Just [(Just pg1,Just pg2)]+                    Just [] -> Just []+                    Just ps -> {- if matchVar then Nothing else -} Just ps+        matchVar = labelIsVar l1 && labelIsVar l2+        match    = matchVar || l1 == l2+comparePartitions2 pg1 pg2 =+    if not (labelIsVar l1) && l1 == l2+        then Just [(Just pg1,Just pg2)]+        else Nothing+    where+        l1 = node pg1+        l2 = node pg2++comparePartitions3 :: (Label lb) =>+    lb +    -> lb +    -> NonEmpty (LabelledPartition lb)+    -> NonEmpty (LabelledPartition lb)+    -> Maybe [(Maybe (GraphPartition lb),Maybe (GraphPartition lb))]+comparePartitions3 l1 l2 s1s s2s = Just $+        ds ++ [ (Just (PartSub l1 (r1p :| [])),Nothing) | r1p<-r1 ]+           ++ [ (Nothing,Just (PartSub l2 (r2p :| []))) | r2p<-r2 ]+    where+        (ds,r1,r2) = listDifferences +                     (comparePartitions4 l1 l2) +                     (NE.toList s1s)+                     (NE.toList s2s)++comparePartitions4 :: (Label lb) =>+    lb +    -> lb +    -> LabelledPartition lb +    -> LabelledPartition lb+    -> Maybe [(Maybe (GraphPartition lb),Maybe (GraphPartition lb))]+comparePartitions4 _ _ (p1,o1) (p2,o2) =+    if matchNodes p1 p2 then comp1 else Nothing+    where+        comp1   = case comparePartitions2 o1 o2 of+                    Nothing -> Just [(Just o1,Just o2)]+                    ds      -> ds++matchNodes :: (Label lb) => lb -> lb -> Bool+matchNodes l1 l2+    | labelIsVar l1 = labelIsVar l2+    | otherwise     = l1 == l2+++------------------------------------------------------------+--  Helpers+------------------------------------------------------------++-- |Collect a list of items by some comparison of a selected component+--  or other derived value.+--+--  cmp     a comparison function that determines if a pair of values+--          should be grouped together+--  sel     a function that selects a value from any item+--+--  Example:    collect fst [(1,'a'),(2,'b'),(1,'c')] =+--                  [(1,[(1,'a'),(1,'c')]),(2,[(2,'b')])]+--+collect :: (Eq b) => (a->b) -> [a] -> [(b, NonEmpty a)]+collect = collectBy (==)++collectBy :: (b->b->Bool) -> (a->b) -> [a] -> [(b, NonEmpty a)]+collectBy cmp sel = map reverseCollection . collectBy1 cmp sel []++collectBy1 :: (b->b->Bool) -> (a->b) -> [(b, NonEmpty a)] -> [a] -> [(b, NonEmpty a)]+collectBy1 _   _   sofar []     = sofar+collectBy1 cmp sel sofar (a:as) =+    collectBy1 cmp sel (collectBy2 cmp sel a sofar) as++collectBy2 :: (b->b->Bool) -> (a->b) -> a -> [(b, NonEmpty a)] -> [(b, NonEmpty a)]+collectBy2 _   sel a [] = [(sel a, a :| [])]+collectBy2 cmp sel a (col@(k,as) : cols)+    | cmp ka k  = (k, a <| as) : cols+    | otherwise = col : collectBy2 cmp sel a cols+    where+        ka = sel a++reverseCollection :: (b, NonEmpty a) -> (b, NonEmpty a)+reverseCollection (k,as) = (k, NE.reverse as)++{-+-- Example/test:+testCollect1 :: [(Int, [(Int, Char)])]+testCollect1 = collect fst [(1,'a'),(2,'b'),(1,'c'),(1,'d'),(2,'d'),(3,'d')]++testCollect2 :: Bool+testCollect2 = testCollect1+                == [ (1,[(1,'a'),(1,'c'),(1,'d')])+                   , (2,[(2,'b'),(2,'d')])+                   , (3,[(3,'d')])+                   ]+-}++-- |Add new values to an existing list of collections.+--  The list of collections is not extended, but each collection is+--  augmented with a further list of values from the supplied list,+--  each of which are related to the existing collection in some way.+--+--  NOTE: the basic pattern of @collect@ and @collectMore@ is similar,+--  and might be generalized into a common set of core functions.+--+collectMore :: (Eq b) => (a->b) -> [a] -> [(b,c)] -> [(b,(c,[a]))]+collectMore = collectMoreBy (==)++collectMoreBy ::+    (b->b->Bool) -> (a->b) -> [a] -> [(b,c)] -> [(b,(c,[a]))]+collectMoreBy cmp sel as cols =+    map reverseMoreCollection $+    collectMoreBy1 cmp sel as (map (\ (b,cs) -> (b,(cs,[])) ) cols)++collectMoreBy1 ::+    (b->b->Bool) -> (a->b) -> [a] -> [(b,(c,[a]))] -> [(b,(c,[a]))]+collectMoreBy1 _   _   []     cols = cols+collectMoreBy1 cmp sel (a:as) cols =+    collectMoreBy1 cmp sel as (collectMoreBy2 cmp sel a cols)++collectMoreBy2 ::+    (b->b->Bool) -> (a->b) -> a -> [(b,(c,[a]))] -> [(b,(c,[a]))]+collectMoreBy2 _   _   _ [] = []+collectMoreBy2 cmp sel a (col@(k,(b,as)):cols)+    | cmp (sel a) k = (k,(b, a:as)):cols+    | otherwise     = col:collectMoreBy2 cmp sel a cols++reverseMoreCollection :: (b,(c,[a])) -> (b,(c,[a]))+reverseMoreCollection (k,(c,as)) = (k,(c,reverse as))++{-+-- Example/test:+testCollectMore1 =+    collectMore snd [(111,1),(112,1),(211,2),(311,3),(411,4)] testCollect1++testCollectMore2 :: Bool+testCollectMore2 = testCollectMore1+                == [ (1,([(1,'a'),(1,'c'),(1,'d')],[(111,1),(112,1)]))+                   , (2,([(2,'b'),(2,'d')],[(211,2)]))+                   , (3,([(3,'d')],[(311,3)]))+                   ]+-}++-- |Remove supplied element from a list using the supplied test+--  function, and return Just the element removed and the+--  remaining list, or Nothing if no element was matched for removal.+--+{-+remove :: (Eq a) => a -> [a] -> Maybe (a,[a])+remove = removeBy (==)++testRemove1  = remove 3 [1,2,3,4,5]+testRemove2  = testRemove1 == Just (3,[1,2,4,5])+testRemove3  = remove 3 [1,2,4,5]+testRemove4  = testRemove3 == Nothing+testRemove5  = remove 5 [1,2,4,5]+testRemove6  = testRemove5 == Just (5,[1,2,4])+testRemove7  = remove 1 [1,2,4]+testRemove8  = testRemove7 == Just (1,[2,4])+testRemove9  = remove 2 [2]+testRemove10 = testRemove9 == Just (2,[])++-}++removeBy :: (b->a->Bool) -> b -> [a] -> Maybe (a,[a])+removeBy cmp a0 as = removeBy1 cmp a0 as []++removeBy1 :: (b->a->Bool) -> b -> [a] -> [a] -> Maybe (a,[a])+removeBy1 _   _  []     _     = Nothing+removeBy1 cmp a0 (a:as) sofar+    | cmp a0 a  = Just (a,reverseTo sofar as)+    | otherwise = removeBy1 cmp a0 as (a:sofar)++-- |Reverse first argument, prepending the result to the second argument+--+reverseTo :: [a] -> [a] -> [a]+reverseTo front back = foldl' (flip (:)) back front++-- |Remove each element from a list, returning a list of pairs,+--  each of which is the element removed and the list remaining.+--+removeEach :: [a] -> [(a,[a])]+removeEach [] = []+removeEach (a:as) = (a,as):[ (a1,a:a1s) | (a1,a1s) <- removeEach as ]++{-+testRemoveEach1 = removeEach [1,2,3,4,5]+testRemoveEach2 = testRemoveEach1 ==+    [ (1,[2,3,4,5])+    , (2,[1,3,4,5])+    , (3,[1,2,4,5])+    , (4,[1,2,3,5])+    , (5,[1,2,3,4])+    ]+-}++-- |List differences between the members of two lists, where corresponding+--  elements may appear at arbitrary locations in the corresponding lists.+--+--  Elements are compared using the function 'cmp', which returns:+--  * Nothing  if the elements are completely unrelated+--  * Just []  if the elements are identical+--  * Just ds  if the elements are related but not identical, in which case+--             ds is a list of values describing differences between them.+--+--  Returns (ds,u1,u2), where:+--  ds is null if the related elements from each list are identical,+--  otherwise is a list of differences between the related elements.+--  u1 is a list of elements in a1 not related to elements in a2.+--  u2 is a list of elements in a2 not related to elements in a1.+--+listDifferences :: (a->a->Maybe [d]) -> [a] -> [a] -> ([d],[a],[a])+listDifferences _   []       a2s = ([],[],a2s)+listDifferences cmp (a1:a1t) a2s =+    case mcomp of+        Nothing       -> morediffs [] [a1] a1t a2s+        Just (ds,a2t) -> morediffs ds []   a1t a2t+    where+        -- mcomp finds identical match, if there is one, or+        -- the first element in a2s related to a1, or Nothing+        -- [choose was listToMaybe,+        --  but that didn't handle repeated properties well]+        mcomp = choose $ mapMaybe maybeResult comps+        comps = [ (cmp a1 a2,a2t) | (a2,a2t) <- removeEach a2s ]+        maybeResult (Nothing,_)   = Nothing+        maybeResult (Just ds,a2t) = Just (ds,a2t)+        morediffs xds xa1h xa1t xa2t  = (xds++xds1,xa1h++xa1r,xa2r)+            where+                (xds1,xa1r,xa2r) = listDifferences cmp xa1t xa2t+        choose  []       = Nothing+        choose  ds@(d:_) = choose1 d ds+        choose1 _ (d@([],_):_)  = Just d+        choose1 d []            = Just d+        choose1 d (_:ds)        = choose1 d ds++{-+testcmp (l1,h1) (l2,h2)+    | (l1 >= h2) || (l2 >= h1) = Nothing+    | (l1 == l2) && (h1 == h2) = Just []+    | otherwise                = Just [((l1,h1),(l2,h2))]++testdiff1 = listDifferences testcmp+                [(12,15),(1,2),(3,4),(5,8),(10,11)]+                [(10,11),(0,1),(3,4),(6,9),(13,15)]+testdiff2 = testdiff1 == ([((12,15),(13,15)),((5,8),(6,9))],[(1,2)],[(0,1)])+-}++--------------------------------------------------------------------------------+--+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke+--  All rights reserved.+--+--  This file is part of Swish.+--+--  Swish is free software; you can redistribute it and/or modify+--  it under the terms of the GNU General Public License as published by+--  the Free Software Foundation; either version 2 of the License, or+--  (at your option) any later version.+--+--  Swish is distributed in the hope that it will be useful,+--  but WITHOUT ANY WARRANTY; without even the implied warranty of+--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+--  GNU General Public License for more details.+--+--  You should have received a copy of the GNU General Public License+--  along with Swish; if not, write to:+--    The Free Software Foundation, Inc.,+--    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+--+--------------------------------------------------------------------------------
+ src/Swish/Monad.hs view
@@ -0,0 +1,262 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}++--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  Monad+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke+--  License     :  GPL V2+--+--  Maintainer  :  Douglas Burke+--  Stability   :  experimental+--  Portability :  FlexibleInstances, MultiParamTypeClasses+--+--  Composed state and IO monad for Swish+--+--------------------------------------------------------------------------------++module Swish.Monad+    ( SwishStateIO, SwishState(..), SwishStatus(..)+    , SwishFormat(..)+    , NamedGraph(..)+    , NamedGraphMap+    -- * Create and modify the Swish state+    , emptyState+    , setFormat, setBase, setGraph+    , modGraphs, findGraph, findFormula+    , modRules, findRule+    , modRulesets, findRuleset+    , findOpenVarModify, findDatatype+    , setInfo, resetInfo, setError, resetError+    , setStatus+    -- , setVerbose+    -- * Error handling+    , swishError+    , reportLine+    )+where++import Swish.Namespace (ScopedName, getScopeNamespace)+import Swish.QName (QName)+import Swish.Ruleset (getMaybeContextAxiom, getMaybeContextRule)+import Swish.Rule(Formula(..))++import Swish.RDF.Datatype (RDFDatatype)+import Swish.RDF.Graph (RDFGraph, emptyRDFGraph)+import Swish.RDF.Ruleset (RDFFormula, RDFRule, RDFRuleMap, RDFRuleset, RDFRulesetMap)+import Swish.RDF.VarBinding (RDFOpenVarBindingModify)++import Swish.RDF.BuiltIn (findRDFOpenVarBindingModifier, findRDFDatatype, rdfRulesetMap)++import Control.Monad.Trans (MonadTrans(..))+import Control.Monad.State (StateT(..), modify)++import Data.LookupMap (LookupEntryClass(..), LookupMap(..))+import Data.LookupMap (emptyLookupMap, mapFindMaybe, mapVals)++import System.IO (hPutStrLn, stderr)++{-|+The supported input and output formats.+-}+data SwishFormat = +  Turtle  -- ^ Turtle format+  | N3    -- ^ N3 format+  | NT    -- ^ NTriples format+    deriving Eq++instance Show SwishFormat where+  show N3  = "N3"+  show NT  = "Ntriples"+  show Turtle = "Turtle"+  -- show RDF = "RDF/XML"++-- | The State for a Swish \"program\".+  +data SwishState = SwishState+    { format    :: SwishFormat      -- ^ format to use for I/O+    , base      :: Maybe QName      -- ^ base to use rather than file name+    , graph     :: RDFGraph         -- ^ current graph+    , graphs    :: NamedGraphMap    -- ^ script processor named graphs+    , rules     :: RDFRuleMap       -- ^ script processor named rules+    , rulesets  :: RDFRulesetMap    -- ^ script processor rulesets+    , infomsg   :: Maybe String     -- ^ information message, or Nothing+    , errormsg  :: Maybe String     -- ^ error message, or Nothing+    , exitcode  :: SwishStatus      -- ^ current status+    }++-- | Status of the processor+--+data SwishStatus =+  SwishSuccess               -- ^ successful run+  | SwishGraphCompareError   -- ^ graphs do not compare+  | SwishDataInputError      -- ^ input data problem (ie format/syntax)+  | SwishDataAccessError     -- ^ data access error+  | SwishArgumentError       -- ^ command-line argument error+  | SwishExecutionError      -- ^ error executing a Swish script+    deriving (Eq, Enum)++instance Show SwishStatus where+  show SwishSuccess           = "Success."+  show SwishGraphCompareError = "The graphs do not compare as equal."+  show SwishDataInputError    = "There was a format or syntax error in the input data."+  show SwishDataAccessError   = "There was a problem accessing data."+  show SwishArgumentError     = "Argument error: use -h or -? for help."+  show SwishExecutionError    = "There was a problem executing a Swish script."++-- | The state monad used in executing Swish programs.+type SwishStateIO a = StateT SwishState IO a++-- | The default state for Swish: no loaded graphs or rules, and format+-- set to 'N3'.+emptyState :: SwishState+emptyState = SwishState+    { format    = N3+    , base      = Nothing+    , graph     = emptyRDFGraph+    , graphs    = emptyLookupMap+    , rules     = emptyLookupMap+    , rulesets  = rdfRulesetMap+    , infomsg   = Nothing+    , errormsg  = Nothing+    , exitcode  = SwishSuccess+    }++-- | Change the format.+setFormat :: SwishFormat -> SwishState -> SwishState+setFormat   fm state = state { format = fm }++-- | Change (or remove) the base URI.+setBase :: Maybe QName -> SwishState -> SwishState+setBase bs state = state { base = bs }++-- | Change the current graph.+setGraph :: RDFGraph -> SwishState -> SwishState+setGraph    gr state = state { graph = gr }++-- | Modify the named graphs.+modGraphs ::+    ( NamedGraphMap -> NamedGraphMap ) -> SwishState -> SwishState+modGraphs grmod state = state { graphs = grmod (graphs state) }++-- | Find a named graph.+findGraph :: ScopedName -> SwishState -> Maybe [RDFGraph]+findGraph nam state = mapFindMaybe nam (graphs state)++-- | Find a formula. The search is first made in the named graphs+-- and then, if not found, the rulesets.+findFormula :: ScopedName -> SwishState -> Maybe RDFFormula+findFormula nam state = case findGraph nam state of+        Nothing  -> getMaybeContextAxiom nam (mapVals $ rulesets state)+        Just []  -> Just $ Formula nam emptyRDFGraph+        Just grs -> Just $ Formula nam (head grs)++-- | Modify the named rules.+modRules ::+    ( RDFRuleMap -> RDFRuleMap ) -> SwishState -> SwishState+modRules rlmod state = state { rules = rlmod (rules state) }++-- | Find a named rule.+findRule :: ScopedName -> SwishState -> Maybe RDFRule+findRule nam state =+    case mapFindMaybe nam (rules state) of+      Nothing -> getMaybeContextRule nam $ mapVals $ rulesets state+      justlr  -> justlr++-- | Modify the rule sets.+modRulesets ::+    ( RDFRulesetMap -> RDFRulesetMap ) -> SwishState -> SwishState+modRulesets rsmod state = state { rulesets = rsmod (rulesets state) }++-- | Find a rule set.+findRuleset ::+    ScopedName -> SwishState -> Maybe RDFRuleset+findRuleset nam state = mapFindMaybe (getScopeNamespace nam) (rulesets state)++-- | Find a modify rule.+findOpenVarModify :: ScopedName -> SwishState -> Maybe RDFOpenVarBindingModify+findOpenVarModify nam _ = findRDFOpenVarBindingModifier nam++-- | Find a data type declaration.+findDatatype :: ScopedName -> SwishState -> Maybe RDFDatatype+findDatatype nam _ = findRDFDatatype nam++-- | Set the information message.+setInfo :: String -> SwishState -> SwishState+setInfo msg state = state { infomsg = Just msg }++-- | Clear the information message.+resetInfo :: SwishState -> SwishState+resetInfo state = state { infomsg = Nothing }++-- | Set the error message.+setError :: String -> SwishState -> SwishState+setError msg state = state { errormsg = Just msg }++-- | Clear the error message.+resetError :: SwishState -> SwishState+resetError state = state { errormsg = Nothing }++-- | Set the status.+setStatus :: SwishStatus -> SwishState -> SwishState+setStatus ec state = state { exitcode = ec }++{-+setVerbose :: Bool -> SwishState -> SwishState+setVerbose f state = state { banner = f }+-}++-- | The graphs dictionary contains named graphs and/or lists+--  of graphs that are created and used by script statements.++data NamedGraph = NamedGraph+    { ngName    :: ScopedName+    , ngGraph   :: [RDFGraph]+    }++instance LookupEntryClass NamedGraph ScopedName [RDFGraph]+    where+        keyVal   (NamedGraph k v) = (k,v)+        newEntry (k,v)            = NamedGraph k v++-- | A LookupMap for the graphs dictionary.+type NamedGraphMap = LookupMap NamedGraph++-- | Report error and set exit status code++swishError :: String -> SwishStatus -> SwishStateIO ()+swishError msg sts = do+  mapM_ reportLine [msg, show sts ++ "\n"]+  modify $ setStatus sts++-- | Output the text to the standard error stream (a new line is+-- added to the output).+reportLine  :: String -> SwishStateIO ()+reportLine = lift . hPutStrLn stderr++--------------------------------------------------------------------------------+--+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke +--  All rights reserved.+--+--  This file is part of Swish.+--+--  Swish is free software; you can redistribute it and/or modify+--  it under the terms of the GNU General Public License as published by+--  the Free Software Foundation; either version 2 of the License, or+--  (at your option) any later version.+--+--  Swish is distributed in the hope that it will be useful,+--  but WITHOUT ANY WARRANTY; without even the implied warranty of+--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+--  GNU General Public License for more details.+--+--  You should have received a copy of the GNU General Public License+--  along with Swish; if not, write to:+--    The Free Software Foundation, Inc.,+--    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+--+--------------------------------------------------------------------------------
+ src/Swish/Namespace.hs view
@@ -0,0 +1,244 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeSynonymInstances #-}++--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  Namespace+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke+--  License     :  GPL V2+--+--  Maintainer  :  Douglas Burke+--  Stability   :  experimental+--  Portability :  FlexibleInstances, MultiParamTypeClasses, OverloadedStrings, TypeSynonymInstances+--+--  This module defines algebraic datatypes for namespaces and scoped names.+--+--  For these purposes, a namespace is a prefix and URI used to identify+--  a namespace (cf. XML namespaces), and a scoped name is a name that+--  is scoped by a specified namespace.+--+--------------------------------------------------------------------------------++module Swish.Namespace+    ( Namespace+    , makeNamespace, makeNamespaceQName+      , getNamespacePrefix, getNamespaceURI, getNamespaceTuple+    -- , nullNamespace+    , ScopedName+    , getScopeNamespace, getScopeLocal+    , getScopePrefix, getScopeURI+    , getQName, getScopedNameURI+    , matchName+    , makeScopedName+    , makeQNameScopedName+    , makeURIScopedName+    , makeNSScopedName+    , nullScopedName+    , namespaceToBuilder+    )+    where++import Swish.QName (QName, LName, newQName, getLName, emptyLName, getQNameURI, getNamespace, getLocalName)++import Data.LookupMap (LookupEntryClass(..))+import Data.Maybe (fromMaybe)+import Data.Monoid (Monoid(..))+import Data.String (IsString(..))++import Network.URI (URI(..), parseURIReference, nullURI)++import qualified Data.Text as T+import qualified Data.Text.Lazy.Builder as B++------------------------------------------------------------+--  Namespace, having a prefix and a URI+------------------------------------------------------------++-- |A NameSpace value consists of an optional prefix and a corresponding URI.+--++data Namespace = Namespace (Maybe T.Text) URI+-- data Namespace = Namespace (Maybe T.Text) !URI+-- TODO: look at interning the URI+                 +-- | Returns the prefix stored in the name space.                 +getNamespacePrefix :: Namespace -> Maybe T.Text+getNamespacePrefix (Namespace p _) = p++-- | Returns the URI stored in the name space.+getNamespaceURI :: Namespace -> URI+getNamespaceURI (Namespace _ u) = u++-- | Convert the name space to a (prefix, URI) tuple.+getNamespaceTuple :: Namespace -> (Maybe T.Text, URI)+getNamespaceTuple (Namespace p u) = (p, u)++-- | Equality is defined by the URI, not by the prefix+-- (so the same URI with different prefixes will be+-- considered to be equal).+instance Eq Namespace where+  (Namespace _ u1) == (Namespace _ u2) = u1 == u2++instance Show Namespace where+    show (Namespace (Just p) u) = show p ++ ":<" ++ show u ++ ">"+    show (Namespace _ u)        = "<" ++ show u ++ ">"++instance LookupEntryClass Namespace (Maybe T.Text) URI where+    keyVal   (Namespace pre uri) = (pre,uri)+    newEntry (pre,uri)           = Namespace pre uri++-- | Create a name space from a URI and an optional prefix label.+makeNamespace :: +    Maybe T.Text  -- ^ optional prefix.+    -> URI        -- ^ URI.+    -> Namespace+makeNamespace = Namespace++-- | Create a qualified name by combining the URI from+-- the name space with a local component.+makeNamespaceQName :: +    Namespace   -- ^ The name space URI is used in the qualified name+    -> LName    -- ^ local component of the qualified name (can be 'emptyLName')+    -> QName+makeNamespaceQName (Namespace _ uri) = newQName uri++{-+nullNamespace :: Namespace+nullNamespace = Namespace Nothing ""+-}++-- | Utility routine to create a \@prefix line (matching N3/Turtle)+--   grammar for this namespace.+--+namespaceToBuilder :: Namespace -> B.Builder+namespaceToBuilder (Namespace pre uri) =+  mconcat $ map B.fromText +  [ "@prefix ", fromMaybe "" pre, ": <", T.pack (show uri), "> .\n"]++------------------------------------------------------------+--  ScopedName, made from a namespace and a local name+------------------------------------------------------------++-- | A full ScopedName value has a QName prefix, namespace URI+--  and a local part.  ScopedName values may omit the prefix+--  (see 'Namespace') or the local part.+--+--  Some applications may handle null namespace URIs as meaning+--  the local part is relative to some base URI.+--+data ScopedName = ScopedName !QName Namespace LName++-- | Returns the local part.+getScopeLocal :: ScopedName -> LName+getScopeLocal (ScopedName _ _ l) = l++-- | Returns the namespace.+getScopeNamespace :: ScopedName -> Namespace+getScopeNamespace (ScopedName _ ns _) = ns++-- | Returns the prefix of the namespace, if set.+getScopePrefix :: ScopedName -> Maybe T.Text+getScopePrefix = getNamespacePrefix . getScopeNamespace++-- | Returns the URI of the namespace.+getScopeURI :: ScopedName -> URI+getScopeURI = getNamespaceURI . getScopeNamespace++-- | This is not total since it will fail if the input string is not a valid URI.+instance IsString ScopedName where+  fromString s =+    maybe (error ("Unable to convert " ++ s ++ " into a ScopedName"))+          makeURIScopedName (parseURIReference s)+    +-- | Scoped names are equal if their corresponding QNames are equal+instance Eq ScopedName where+  (ScopedName qn1 _ _) == (ScopedName qn2 _ _) = qn1 == qn2++-- | Scoped names are ordered by their QNames+instance Ord ScopedName where+  (ScopedName qn1 _ _) <= (ScopedName qn2 _ _) = qn1 <= qn2++-- | If there is a namespace associated then the Show instance+-- uses @prefix:local@, otherwise @<url>@.+instance Show ScopedName where+    show (ScopedName qn n l) = case getNamespacePrefix n of+      Just pre -> T.unpack $ mconcat [pre, ":", getLName l]+      _        -> show qn -- "<" ++ show (getNamespaceURI n) ++ T.unpack l ++ ">"++-- |Get the QName corresponding to a scoped name.+getQName :: ScopedName -> QName+getQName (ScopedName qn _ _) = qn++-- |Get URI corresponding to a scoped name (using RDF conventions).+getScopedNameURI :: ScopedName -> URI+getScopedNameURI = getQNameURI . getQName++-- |Test if supplied string matches the display form of a+--  scoped name.+matchName :: String -> ScopedName -> Bool+matchName str nam = str == show nam++-- |Construct a ScopedName.+makeScopedName :: +    Maybe T.Text  -- ^ prefix for the namespace+    -> URI        -- ^ namespace+    -> LName      -- ^ local name+    -> ScopedName+makeScopedName pre nsuri local = +    ScopedName (newQName nsuri local)+               (Namespace pre nsuri)+               local++-- |Construct a ScopedName from a QName.+makeQNameScopedName :: +    Maybe T.Text   -- ^ prefix+    -> QName +    -> ScopedName+makeQNameScopedName pre qn = ScopedName qn (Namespace pre (getNamespace qn)) (getLocalName qn)++-- could use qnameFromURI to find a local name if there is one.++-- | Construct a ScopedName for a bare URI (the label is set to \"\").+makeURIScopedName :: URI -> ScopedName+makeURIScopedName uri = makeScopedName Nothing uri emptyLName++-- | Construct a ScopedName.+makeNSScopedName :: +    Namespace     -- ^ namespace+    -> LName      -- ^ local component+    -> ScopedName+makeNSScopedName ns local = +    ScopedName (newQName (getNamespaceURI ns) local) ns local++-- | This should never appear as a valid name+nullScopedName :: ScopedName+nullScopedName = makeURIScopedName nullURI++--------------------------------------------------------------------------------+--+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke+--  All rights reserved.+--+--  This file is part of Swish.+--+--  Swish is free software; you can redistribute it and/or modify+--  it under the terms of the GNU General Public License as published by+--  the Free Software Foundation; either version 2 of the License, or+--  (at your option) any later version.+--+--  Swish is distributed in the hope that it will be useful,+--  but WITHOUT ANY WARRANTY; without even the implied warranty of+--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+--  GNU General Public License for more details.+--+--  You should have received a copy of the GNU General Public License+--  along with Swish; if not, write to:+--    The Free Software Foundation, Inc.,+--    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+--+--------------------------------------------------------------------------------
+ src/Swish/Proof.hs view
@@ -0,0 +1,293 @@+--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  Proof+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke+--  License     :  GPL V2+--+--  Maintainer  :  Douglas Burke+--  Stability   :  experimental+--  Portability :  H98+--+--  This module defines a framework for constructing proofs+--  over some expression form.  It is intended to be used+--  with RDF graphs, but the structures aim to be quite+--  generic with respect to the expression forms allowed.+--+--  It does not define any proof-finding strategy.+--+--------------------------------------------------------------------------------++module Swish.Proof+    ( Proof(..), Step(..)+    , checkProof, explainProof, checkStep, showProof, showsProof, showsFormula )+where++import Swish.Rule (Expression(..), Formula(..), Rule(..))+import Swish.Rule (showsFormula, showsFormulae)+import Swish.Ruleset (Ruleset(..))++import Swish.Utils.ListHelpers (subset)++import Data.List (union, intersect, intercalate, foldl')+import Data.Maybe (catMaybes, isNothing)+import Data.String.ShowLines (ShowLines(..))++------------------------------------------------------------+--  Proof framework+------------------------------------------------------------++-- |Step in proof chain+--+--  The display name for a proof step comes from the display name of its+--  consequence formula.+data Step ex = Step+    { stepRule :: Rule ex           -- ^ Inference rule used+    , stepAnt  :: [Formula ex]      -- ^ Antecedents of inference rule+    , stepCon  :: Formula ex        -- ^ Named consequence of inference rule+    } deriving Show++-- |Proof is a structure that presents a chain of rule applications+--  that yield a result expression from a given expression+data Proof ex = Proof+    { proofContext :: [Ruleset ex]  -- ^ Proof context:  list of rulesets,+                                    --   each of which provides a number of+                                    --   axioms and rules.+    , proofInput   :: Formula ex    -- ^ Given expression+    , proofResult  :: Formula ex    -- ^ Result expression+    , proofChain   :: [Step ex]     -- ^ Chain of inference rule applications+                                    --   progressing from input to result+    }++-- |Return a list of axioms from all the rulesets in a proof+proofAxioms :: Proof a -> [Formula a]+proofAxioms = concatMap rsAxioms . proofContext++-- |Return a list of rules from all the rulesets in a proof+proofRules :: Proof a -> [Rule a]+proofRules = concatMap rsRules . proofContext++-- |Return list of axioms actually referenced by a proof+proofAxiomsUsed :: Proof ex -> [Formula ex]+proofAxiomsUsed proof = foldl' union [] $ map stepAxioms (proofChain proof)+    where+        stepAxioms st = stepAnt st `intersect` proofAxioms proof++-- |Check consistency of given proof.+--  The supplied rules and axioms are assumed to be correct.+checkProof :: (Expression ex) => Proof ex -> Bool+checkProof pr =+    checkProof1 (proofRules pr) initExpr (proofChain pr) goalExpr+    where+        initExpr = formExpr (proofInput pr) : map formExpr (proofAxioms pr)+        goalExpr = formExpr $ proofResult pr++checkProof1 :: (Expression ex) => [Rule ex] -> [ex] -> [Step ex] -> ex -> Bool+checkProof1 _     prev []       res = res `elem` prev+checkProof1 rules prev (st:steps) res =+    checkStep rules prev st &&+    checkProof1 rules (formExpr (stepCon st):prev) steps res++-- | A proof step is valid if rule is in list of rules+--  and the antecedents are sufficient to obtain the conclusion+--  and the antecedents are in the list of formulae already proven.+--+--  Note:  this function depends on the ruleName of any rule being+--  unique among all rules.  In particular the name of the step rule+--  being in correspondence with the name of one of the indicated+--  valid rules of inference.+checkStep :: +  (Expression ex) +  => [Rule ex]   -- ^ rules+  -> [ex]        -- ^ antecedants+  -> Step ex     -- ^ the step to validate+  -> Bool        -- ^ @True@ if the step is valid++checkStep rules prev step = isNothing $ explainStep rules prev step++{-++Is the following an optimisation of the above?++checkStep rules prev step =+    -- Rule name is one of supplied rules, and+    (ruleName srul `elem` map ruleName rules) &&+    -- Antecedent expressions are all previously accepted expressions+    (sant `subset` prev)   &&+    -- Inference rule yields concequence from antecendents+    checkInference srul sant scon+    where+        --  Rule from proof step:+        srul = stepRule step+        --  Antecedent expressions from proof step:+        sant = map formExpr $ stepAnt step+        --  Consequentent expression from proof step:+        scon = formExpr $ stepCon step+-}+++{-+    (formExpr (stepCon step) `elem` sfwd)+    -- (or $ map (`subset` sant) sbwd)+    where+        --  Rule from proof step:+        srul = stepRule step+        --  Antecedent expressions from proof step:+        sant = map formExpr $ stepAnt step+        --  Forward chaining from antecedents of proof step+        scon = map formExpr $ stepCon step+        --  Forward chaining from antecedents of proof step++        sfwd = fwdApply srul sant+        --  Backward chaining from consequent of proof step+        --  (Does not work because of introduction of existentials)+        sbwd = bwdApply srul (formExpr $ stepCon step)+-}++-- |Check proof. If there is an error then return information+-- about the failing step.+explainProof ::+    (Expression ex) => Proof ex -> Maybe String+explainProof pr =+    explainProof1 (proofRules pr) initExpr (proofChain pr) goalExpr+    where+        initExpr = formExpr (proofInput pr) : map formExpr (proofAxioms pr)+        goalExpr = formExpr $ proofResult pr++explainProof1 ::+    (Expression ex) => [Rule ex] -> [ex] -> [Step ex] -> ex -> Maybe String+explainProof1 _     prev []       res   =+    if res `elem` prev then Nothing else Just "Result not demonstrated"+explainProof1 rules prev (st:steps) res =+    case explainStep rules prev st  of+        Nothing -> explainProof1 rules (formExpr (stepCon st):prev) steps res+        Just ex -> Just ("Invalid step: "++show (formName $ stepCon st)++": "++ex)++-- | A proof step is valid if rule is in list of rules+--  and the antecedents are sufficient to obtain the conclusion+--  and the antecedents are in the list of formulae already proven.+--+--  Note:  this function depends on the ruleName of any rule being+--  unique among all rules.  In particular the name of the step rule+--  being in correspondence with the name of one of the indicated+--  valid rules of inference.+--+explainStep :: +  (Expression ex) +  => [Rule ex]  -- ^ rules+  -> [ex]       -- ^ previous+  -> Step ex    -- ^ step+  -> Maybe String -- ^ @Nothing@ if step is okay, otherwise a string indicating the error+explainStep rules prev step =+        if null errors then Nothing else Just $ intercalate ", " errors+    where+        --  Rule from proof step:+        srul = stepRule step+        --  Antecedent expressions from proof step:+        sant = map formExpr $ stepAnt step+        --  Consequentent expression from proof step:+        scon = formExpr $ stepCon step+        --  Tests for step to be valid+        errors = catMaybes+            -- Rule name is one of supplied rules, and+            [ require (ruleName srul `elem` map ruleName rules)+                      ("rule "++show (ruleName srul)++" not present")+            -- Antecedent expressions are all previously accepted expressions+            , require (sant `subset` prev)+                      "antecedent not axiom or previous result"+            -- Inference rule yields consequence from antecedents+            , require (checkInference srul sant scon)+                      "rule does not deduce consequence from antecedents"+            ]+        require b s = if b then Nothing else Just s++-- |Create a displayable form of a proof, returned as a `ShowS` value.+--+--  This function is intended to allow the calling function some control+--  of multiline displays by providing:+--+--  (1) the first line of the proof is not preceded by any text, so+--      it may be appended to some preceding text on the same line,+--+--  (2) the supplied newline string is used to separate lines of the+--      formatted text, and may include any desired indentation, and+--+--  (3) no newline is output following the final line of text.+showsProof :: +  (ShowLines ex) +  => String    -- ^ newline string+  -> Proof ex +  -> ShowS+showsProof newline proof =+    if null axioms then shProof else shAxioms . shProof+    where+        axioms = proofAxiomsUsed proof+        shAxioms =+            showString    ("Axioms:" ++ newline) .+            showsFormulae newline (proofAxiomsUsed proof) newline+        shProof =+            showString    ("Input:" ++ newline) .+            showsFormula  newline (proofInput  proof) .+            showString    (newline ++ "Proof:" ++ newline) .+            showsSteps    newline (proofChain  proof)++-- |Returns a simple string representation of a proof.+showProof :: +  (ShowLines ex) +  => String    -- ^ newline string+  -> Proof ex +  -> String+showProof newline proof = showsProof newline proof ""++-- |Create a displayable form of a list of labelled proof steps+showsSteps :: (ShowLines ex) => String -> [Step ex] -> ShowS+showsSteps _       []     = id+showsSteps newline [s]    = showsStep  newline s+showsSteps newline (s:ss) = showsStep  newline s .+                            showString newline .+                            showsSteps newline ss++-- |Create a displayable form of a labelled proof step.+showsStep :: (ShowLines ex) => String -> Step ex -> ShowS+showsStep newline s = showsFormula newline (stepCon s) .+                      showString newline .+                      showString ("  (by ["++rulename++"] from "++antnames++")")+    where+        rulename = show . ruleName $ stepRule s+        antnames = showNames $ map (show . formName) (stepAnt s)++-- |Return a string containing a list of names.+showNames :: [String] -> String+showNames []      = "<nothing>"+showNames [n]     = showName n+showNames [n1,n2] = showName n1 ++ " and " ++ showName n2+showNames (n1:ns) = showName n1 ++ ", " ++ showNames ns++-- |Return a string representing a single name.+showName :: String -> String+showName n = "["++n++"]"++--------------------------------------------------------------------------------+--+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke+--  All rights reserved.+--+--  This file is part of Swish.+--+--  Swish is free software; you can redistribute it and/or modify+--  it under the terms of the GNU General Public License as published by+--  the Free Software Foundation; either version 2 of the License, or+--  (at your option) any later version.+--+--  Swish is distributed in the hope that it will be useful,+--  but WITHOUT ANY WARRANTY; without even the implied warranty of+--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+--  GNU General Public License for more details.+--+--  You should have received a copy of the GNU General Public License+--  along with Swish; if not, write to:+--    The Free Software Foundation, Inc.,+--    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+--+--------------------------------------------------------------------------------
+ src/Swish/QName.hs view
@@ -0,0 +1,328 @@+{-# LANGUAGE OverloadedStrings #-}++--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  QName+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke+--  License     :  GPL V2+--+--  Maintainer  :  Douglas Burke+--  Stability   :  experimental+--  Portability :  OverloadedStrings+--+--  This module defines an algebraic datatype for qualified names (QNames).+--+--------------------------------------------------------------------------------++-- At present we support using URI references rather than forcing an absolute+-- URI. This is partly to support the existing tests (too lazy to resolve whether+-- the tests really should be using relative URIs in this case).++module Swish.QName+    ( QName+    , LName+    , emptyLName+    , newLName+    , getLName+    , newQName+    , qnameFromURI+    , getNamespace+    , getLocalName+    , getQNameURI+    , qnameFromFilePath+    )+    where++import Control.Monad (liftM)++import Data.String (IsString(..))+import Data.Maybe (fromMaybe)+import Data.Interned (intern, unintern)+import Data.Interned.URI (InternedURI)++import Network.URI (URI(..), URIAuth(..), parseURIReference)++import System.Directory (canonicalizePath)+import System.FilePath (splitFileName)++import qualified Data.Text as T++------------------------------------------------------------+--  Qualified name+------------------------------------------------------------+--+--  These are RDF QNames rather than XML ones (as much as+--  RDF can claim to have them).+--+++{-| A local name, which can be empty.++At present, the local name can not +contain spaces or the \'#\', \':\', or \'/\' characters. This restriction is+experimental.+-}+newtype LName = LName T.Text+    deriving (Eq, Ord)++instance Show LName where+    show (LName t) = show t++-- | This is not total since attempting to convert a string+--   containing invalid characters will cause an error.+instance IsString LName where+    fromString s = +        fromMaybe (error ("Invalid local name: " ++ s)) $+                  newLName (T.pack s)++-- | The empty local name.+emptyLName :: LName+emptyLName = LName ""++-- | Create a local name.+newLName :: T.Text -> Maybe LName+newLName l = if T.any (`elem` " #:/") l then Nothing else Just (LName l)++-- | Extract the local name.+getLName :: LName -> T.Text+getLName (LName l) = l++{-| ++A qualified name, consisting of a namespace URI+and the local part of the identifier, which can be empty.+The serialisation of a QName is formed by concatanating the+two components.++> Prelude> :set prompt "swish> "+> swish> :set -XOverloadedStrings+> swish> :m + Swish.QName+> swish> let qn1 = "http://example.com/" :: QName+> swish> let qn2 = "http://example.com/bob" :: QName+> swish> let qn3 = "http://example.com/bob/fred" :: QName+> swish> let qn4 = "http://example.com/bob/fred#x" :: QName+> swish> map getLocalName [qn1, qn2, qn3, qn4]+> ["","bob","fred","x"]+> swish> getNamespace qn1+> http://example.com+> swish> getNamespace qn2+> http://example.com+> swish> getNamespace qn3+> http://example.com/bob/+> swish> getNamespace qn4+> http://example.com/bob/fred#++-}++{-+For now I have added in storing the actual URI+as well as the namespace component. This may or+may not be a good idea (space vs time saving).+-}++data QName = QName !InternedURI URI LName++-- | This is not total since it will fail if the input string is not a valid URI.+instance IsString QName where+  fromString s = +      fromMaybe (error ("QName conversion given an invalid URI: " ++ s))+      (parseURIReference s >>= qnameFromURI)++-- | Equality is determined by a case sensitive comparison of the               +-- URI.+instance Eq QName where+  -- see qnEq comments below+  (QName u1 _ _) == (QName u2 _ _) = u1 == u2++-- ugly, use show instance OR switch to the ordering of InternedURI++-- | At present the ordering is based on a comparison of the @Show@+-- instance of the URI.+instance Ord QName where+  {-+    (QName u1 l1) <= (QName u2 l2) =+        if up1 /= up2 then up1 <= up2 else (ur1++l1) <= (ur2++l2)+        where+            n   = min (length u1) (length u2)+            (up1,ur1) = splitAt n u1+            (up2,ur2) = splitAt n u2+  -}+  +  -- TODO: which is faster?+  -- Now we have changed to InternedURI, we could use the+  -- Ord instance of it, but it is unclear to me what the+  -- ordering means in that case, and whether the semantics+  -- matter here?+  (QName u1 _ _) <= (QName u2 _ _) = show u1 <= show u2+  +  {-+  (QName _ uri1 l1) <= (QName _ uri2 l2) =+    if up1 /= up2 then up1 <= up2 else (ur1 ++ T.unpack l1) <= (ur2 ++ T.unpack l2)+      where+        u1 = show uri1+        u2 = show uri2+        +        n   = min (length u1) (length u2)+        (up1,ur1) = splitAt n u1+        (up2,ur2) = splitAt n u2+  -}+  +-- | The format used to display the URI is @\<uri\>@.+instance Show QName where+    show (QName u _ _) = "<" ++ show u ++ ">"++{-+The assumption in QName is that the validation done in creating+the local name is sufficient to ensure that the combined +URI is syntactically valid. Is this in fact true?+-}++-- | Create a new qualified name with an explicit local component.+--+newQName ::+    URI            -- ^ Namespace+    -> LName       -- ^ Local component+    -> QName+newQName ns l@(LName local) = +  -- Until profiling shows that this is a time/space issue, we use+  -- the following code rather than trying to deconstruct the URI+  -- directly+  let lstr   = T.unpack local+      uristr = show ns ++ lstr+  in case parseURIReference uristr of+       Just uri -> QName (intern uri) ns l+       _ -> error $ "Unable to combine " ++ show ns ++ " with " ++ lstr+  +{-++old behavior++ splitQname "http://example.org/aaa#bbb" = ("http://example.org/aaa#","bbb")+ splitQname "http://example.org/aaa/bbb" = ("http://example.org/aaa/","bbb")+ splitQname "http://example.org/aaa/"    = ("http://example.org/aaa/","")++Should "urn:foo:bar" have a local name of "" or "foo:bar"? For now go+with the first option.++-}++-- | Create a new qualified name.+qnameFromURI :: +    URI      -- ^ The URI will be deconstructed to find if it contains a local component.+    -> Maybe QName -- ^ The failure case may be removed.+qnameFromURI uri =+  let uf = uriFragment uri+      up = uriPath uri+      q0 = Just $ QName iuri uri emptyLName+      start = QName iuri+      iuri = intern uri+  in case uf of+       "#"    -> q0+       '#':xs -> start (uri {uriFragment = "#"}) `liftM` newLName (T.pack xs)+       ""     -> case break (=='/') (reverse up) of+                   ("",_) -> q0 -- path ends in / or is empty+                   (_,"") -> q0 -- path contains no /+                   (rlname,rpath) -> +                       start (uri {uriPath = reverse rpath}) `liftM` +                       newLName (T.pack (reverse rlname))++       -- e -> error $ "Unexpected: uri=" ++ show uri ++ " has fragment='" ++ show e ++ "'" +       _ -> Nothing++-- | Return the URI of the namespace stored in the QName.+-- This does not contain the local component.+--+getNamespace :: QName -> URI+getNamespace (QName _ ns _) = ns++-- | Return the local component of the QName.+getLocalName :: QName -> LName+getLocalName (QName _ _ l) = l++-- | Returns the full URI of the QName (ie the combination of the+-- namespace and local components).+getQNameURI :: QName -> URI+getQNameURI (QName u _ _) = unintern u++{-+Original used comparison of concatenated strings,+but that was very inefficient.  The longer version below+does the comparison without constructing new values but is+no longer valid with the namespace being stored as a URI,+so for now just compare the overall URIs and we can+optimize this at a later date if needed.+qnEq :: QName -> QName -> Bool+qnEq (QName u1 _ _) (QName u2 _ _) = u1 == u2++qnEq (QName _ n1 l1) (QName _ n2 l2) = qnEq1 n1 n2 l1 l2+  where+    qnEq1 (c1:ns1) (c2:ns2)  ln1 ln2   = c1==c2 && qnEq1 ns1 ns2 ln1 ln2+    qnEq1 []  ns2  ln1@(_:_) ln2       = qnEq1 ln1 ns2 []  ln2+    qnEq1 ns1 []   ln1       ln2@(_:_) = qnEq1 ns1 ln2 ln1 []+    qnEq1 []  []   []        []        = True+    qnEq1 _   _    _         _         = False+-}++{-|+Convert a filepath to a file: URI stored in a QName. If the+input file path is relative then the current working directory is used+to convert it into an absolute path.++If the input represents a directory then it *must* end in +the directory separator - so for Posix systems use +@\"\/foo\/bar\/\"@ rather than +@\"\/foo\/bar\"@.++This has not been tested on Windows.+-}++{-+NOTE: not sure why I say directories should end in the path+seperator since++ghci> System.Directory.canonicalizePath "/Users/dburke/haskell/swish-text"+"/Users/dburke/haskell/swish-text"+ghci> System.Directory.canonicalizePath "/Users/dburke/haskell/swish-text/"+"/Users/dburke/haskell/swish-text"++-}++qnameFromFilePath :: FilePath -> IO QName+qnameFromFilePath fname = do+  ipath <- canonicalizePath fname+  let (dname, lname) = splitFileName ipath+      nsuri = URI "file:" emptyAuth dname "" ""+      uri = URI "file:" emptyAuth ipath "" ""+  case lname of+    "" -> return $ QName (intern nsuri) nsuri emptyLName+    _  -> return $ QName (intern uri) nsuri (LName (T.pack lname))++emptyAuth :: Maybe URIAuth+emptyAuth = Just $ URIAuth "" "" ""++--------------------------------------------------------------------------------+--+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke+--  All rights reserved.+--+--  This file is part of Swish.+--+--  Swish is free software; you can redistribute it and/or modify+--  it under the terms of the GNU General Public License as published by+--  the Free Software Foundation; either version 2 of the License, or+--  (at your option) any later version.+--+--  Swish is distributed in the hope that it will be useful,+--  but WITHOUT ANY WARRANTY; without even the implied warranty of+--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+--  GNU General Public License for more details.+--+--  You should have received a copy of the GNU General Public License+--  along with Swish; if not, write to:+--    The Free Software Foundation, Inc.,+--    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+--+--------------------------------------------------------------------------------
src/Swish/RDF.hs view
@@ -3,7 +3,7 @@ -------------------------------------------------------------------------------- -- | --  Module      :  RDF---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke --  License     :  GPL V2 -- --  Maintainer  :  Douglas Burke@@ -11,17 +11,18 @@ --  Portability :  FlexibleInstances, MultiParamTypeClasses, TypeSynonymInstances -- --  This module provides an in-memory RDF Graph (it re-exports---  "Swish.RDF.RDFGraph").+--  "Swish.RDF.Graph"). -- -------------------------------------------------------------------------------- -module Swish.RDF (module Swish.RDF.RDFGraph) where+module Swish.RDF (module Swish.RDF.Graph) where -import Swish.RDF.RDFGraph+import Swish.RDF.Graph  -------------------------------------------------------------------------------- -----  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke --  All rights reserved. -- --  This file is part of Swish.
+ src/Swish/RDF/BuiltIn.hs view
@@ -0,0 +1,53 @@+--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  BuiltIn+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke+--  License     :  GPL V2+--+--  Maintainer  :  Douglas Burke+--  Stability   :  experimental+--  Portability :  H98+--+--  This module collects references and provides access to all of the+--  datatypes, variable binding modifiers and variable binding filters+--  built in to Swish.+--+--------------------------------------------------------------------------------++module Swish.RDF.BuiltIn+    ( findRDFOpenVarBindingModifier+    , findRDFDatatype+    , rdfRulesetMap+    , allRulesets, allDatatypeRulesets+    )+where++import Swish.RDF.BuiltIn.Datatypes+import Swish.RDF.BuiltIn.Rules++--------------------------------------------------------------------------------+--+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke+--  All rights reserved.+--+--  This file is part of Swish.+--+--  Swish is free software; you can redistribute it and/or modify+--  it under the terms of the GNU General Public License as published by+--  the Free Software Foundation; either version 2 of the License, or+--  (at your option) any later version.+--+--  Swish is distributed in the hope that it will be useful,+--  but WITHOUT ANY WARRANTY; without even the implied warranty of+--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+--  GNU General Public License for more details.+--+--  You should have received a copy of the GNU General Public License+--  along with Swish; if not, write to:+--    The Free Software Foundation, Inc.,+--    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+--+--------------------------------------------------------------------------------
+ src/Swish/RDF/BuiltIn/Datatypes.hs view
@@ -0,0 +1,81 @@+--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  Datatypes+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke+--  License     :  GPL V2+--+--  Maintainer  :  Douglas Burke+--  Stability   :  experimental+--  Portability :  H98+--+--  This module collects references and provides access to all of the+--  datatypes built in to Swish.+--+--------------------------------------------------------------------------------++module Swish.RDF.BuiltIn.Datatypes+       ( allDatatypes, findRDFDatatype )+    where++import Swish.Namespace (ScopedName)++import Swish.RDF.Datatype (RDFDatatype)++import Swish.RDF.Datatype.XSD.String (rdfDatatypeXsdString)+import Swish.RDF.Datatype.XSD.Integer (rdfDatatypeXsdInteger)+import Swish.RDF.Datatype.XSD.Decimal (rdfDatatypeXsdDecimal)++import Data.LookupMap (LookupMap(..), mapFindMaybe)++------------------------------------------------------------+--  Declare datatype map+------------------------------------------------------------++-- | Al the data type declarations built into Swish.+allDatatypes :: [RDFDatatype]+allDatatypes =+    [ rdfDatatypeXsdString+    , rdfDatatypeXsdInteger+    , rdfDatatypeXsdDecimal+    ]++-- | Look up a data type declaration.+findRDFDatatype :: ScopedName -> Maybe RDFDatatype+findRDFDatatype nam = mapFindMaybe nam (LookupMap allDatatypes)++------------------------------------------------------------+--  Declare datatype subtypes map+------------------------------------------------------------++{-+allDatatypeSubtypes :: [xxx]+allDatatypeSubtypes = []+--  [[[details TBD]]]+-}++--------------------------------------------------------------------------------+--+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke+--  All rights reserved.+--+--  This file is part of Swish.+--+--  Swish is free software; you can redistribute it and/or modify+--  it under the terms of the GNU General Public License as published by+--  the Free Software Foundation; either version 2 of the License, or+--  (at your option) any later version.+--+--  Swish is distributed in the hope that it will be useful,+--  but WITHOUT ANY WARRANTY; without even the implied warranty of+--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+--  GNU General Public License for more details.+--+--  You should have received a copy of the GNU General Public License+--  along with Swish; if not, write to:+--    The Free Software Foundation, Inc.,+--    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+--+--------------------------------------------------------------------------------
+ src/Swish/RDF/BuiltIn/Rules.hs view
@@ -0,0 +1,153 @@+--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  Rules+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke+--  License     :  GPL V2+--+--  Maintainer  :  Douglas Burke+--  Stability   :  experimental+--  Portability :  H98+--+--  This module collects references and provides access to all of the+--  rulesets, variable binding modifiers and variable binding filters+--  built in to Swish.+--+--------------------------------------------------------------------------------++module Swish.RDF.BuiltIn.Rules+    ( findRDFOpenVarBindingModifier+    , rdfRulesetMap+    , allRulesets, allDatatypeRulesets+    )+where++import Swish.Datatype (typeRules, typeMkModifiers)+import Swish.Namespace (ScopedName)+import Swish.VarBinding (nullVarBindingModify, makeVarFilterModify, varFilterEQ, varFilterNE)++import Swish.RDF.BuiltIn.Datatypes (allDatatypes)+import Swish.RDF.Ruleset (RDFRuleset, RDFRulesetMap)+import Swish.RDF.ProofContext (rulesetRDF, rulesetRDFS, rulesetRDFD)++import Swish.RDF.VarBinding+    ( RDFOpenVarBindingModify+    , rdfVarBindingUriRef, rdfVarBindingBlank+    , rdfVarBindingLiteral+    , rdfVarBindingUntypedLiteral, rdfVarBindingTypedLiteral+    , rdfVarBindingXMLLiteral, rdfVarBindingDatatyped+    , rdfVarBindingMemberProp+    )++import Data.LookupMap (LookupMap(..), mapFindMaybe)++------------------------------------------------------------+--  Declare variable binding filters list+------------------------------------------------------------++-- |List of rdfOpenVarBindingModify values for predefined filters+--+rdfVarBindingFilters :: [RDFOpenVarBindingModify]+rdfVarBindingFilters =+    [ filter1 rdfVarBindingUriRef+    , filter1 rdfVarBindingBlank+    , filter1 rdfVarBindingLiteral+    , filter1 rdfVarBindingUntypedLiteral+    , filter1 rdfVarBindingTypedLiteral+    , filter1 rdfVarBindingXMLLiteral+    , filter1 rdfVarBindingMemberProp+    , filter2 rdfVarBindingDatatyped+    -- , filterN nullVarBindingModify+    , filter2 varFilterEQ+    , filter2 varFilterNE+    ]+    where+      -- Swish.RDF.VarBinding.openVbmName seems to require that the label+      -- list not be evaluated which means that we can not replace these+      -- statements by ones like+      --+      --    filter1 f (lb:_) = makeVarFilterModift $ f lb+      --+      filter1 f lbs = makeVarFilterModify $ f (head lbs)+      filter2 f lbs = makeVarFilterModify $ f (head lbs) (lbs!!1)+      -- filterN f lbs = makeVarFilterModify $ f ...++------------------------------------------------------------+--  Declare variable binding modifiers map+------------------------------------------------------------++rdfVarBindingModifiers :: [RDFOpenVarBindingModify]+rdfVarBindingModifiers =+    [ nullVarBindingModify+    ]++------------------------------------------------------------+--  Find a named built-in OpenVarBindingModifier+------------------------------------------------------------++allOpenVarBindingModify :: [RDFOpenVarBindingModify]+allOpenVarBindingModify =+    rdfVarBindingFilters    +++    rdfVarBindingModifiers  +++    dtsVarBindingModifiers++dtsVarBindingModifiers :: [RDFOpenVarBindingModify]+-- dtsVarBindingModifiers = concatMap dtVarBindingModifiers allDatatypes+dtsVarBindingModifiers = concatMap typeMkModifiers allDatatypes++{-+dtVarBindingModifiers dtval =+    map (makeRdfDtOpenVarBindingModify dtval) (tvalMod dtval)+-}++-- | Find the named open variable binding modifier.+findRDFOpenVarBindingModifier :: ScopedName -> Maybe RDFOpenVarBindingModify+findRDFOpenVarBindingModifier nam =+    mapFindMaybe nam (LookupMap allOpenVarBindingModify)++------------------------------------------------------------+--  Lookup map for built-in rulesets+------------------------------------------------------------++-- | A 'LookupMap' of 'allRulesets'.+rdfRulesetMap :: RDFRulesetMap+rdfRulesetMap = LookupMap allRulesets++-- | All the rule sets known to Swish.+allRulesets :: [RDFRuleset]+allRulesets =+    [ rulesetRDF+    , rulesetRDFS+    , rulesetRDFD+    ]+    ++ allDatatypeRulesets++-- | The data type rule sets known to Swish.+allDatatypeRulesets :: [RDFRuleset]+allDatatypeRulesets = map typeRules allDatatypes++--------------------------------------------------------------------------------+--+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke+--  All rights reserved.+--+--  This file is part of Swish.+--+--  Swish is free software; you can redistribute it and/or modify+--  it under the terms of the GNU General Public License as published by+--  the Free Software Foundation; either version 2 of the License, or+--  (at your option) any later version.+--+--  Swish is distributed in the hope that it will be useful,+--  but WITHOUT ANY WARRANTY; without even the implied warranty of+--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+--  GNU General Public License for more details.+--+--  You should have received a copy of the GNU General Public License+--  along with Swish; if not, write to:+--    The Free Software Foundation, Inc.,+--    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+--+--------------------------------------------------------------------------------
− src/Swish/RDF/BuiltInDatatypes.hs
@@ -1,76 +0,0 @@------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  BuiltInDatatypes---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  License     :  GPL V2------  Maintainer  :  Douglas Burke---  Stability   :  experimental---  Portability :  H98------  This module collects references and provides access to all of the---  datatypes built in to Swish.--------------------------------------------------------------------------------------module Swish.RDF.BuiltInDatatypes-       ( allDatatypes, findRDFDatatype )-    where--import Swish.RDF.RDFDatatype (RDFDatatype)--import Swish.Utils.LookupMap (LookupMap(..), mapFindMaybe)-import Swish.Utils.Namespace (ScopedName)-import Swish.RDF.RDFDatatypeXsdString (rdfDatatypeXsdString)-import Swish.RDF.RDFDatatypeXsdInteger (rdfDatatypeXsdInteger)-import Swish.RDF.RDFDatatypeXsdDecimal (rdfDatatypeXsdDecimal)-----------------------------------------------------------------  Declare datatype map---------------------------------------------------------------allDatatypes :: [RDFDatatype]-allDatatypes =-    [ rdfDatatypeXsdString-    , rdfDatatypeXsdInteger-    , rdfDatatypeXsdDecimal-    ]--findRDFDatatype :: ScopedName -> Maybe RDFDatatype-findRDFDatatype nam = mapFindMaybe nam (LookupMap allDatatypes)-----------------------------------------------------------------  Declare datatype subtypes map---------------------------------------------------------------{--allDatatypeSubtypes :: [xxx]-allDatatypeSubtypes = []---  [[[details TBD]]]--}----------------------------------------------------------------------------------------  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  All rights reserved.------  This file is part of Swish.------  Swish is free software; you can redistribute it and/or modify---  it under the terms of the GNU General Public License as published by---  the Free Software Foundation; either version 2 of the License, or---  (at your option) any later version.------  Swish is distributed in the hope that it will be useful,---  but WITHOUT ANY WARRANTY; without even the implied warranty of---  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the---  GNU General Public License for more details.------  You should have received a copy of the GNU General Public License---  along with Swish; if not, write to:---    The Free Software Foundation, Inc.,---    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA------------------------------------------------------------------------------------
− src/Swish/RDF/BuiltInMap.hs
@@ -1,52 +0,0 @@------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  BuiltInMap---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  License     :  GPL V2------  Maintainer  :  Douglas Burke---  Stability   :  experimental---  Portability :  H98------  This module collects references and provides access to all of the---  datatypes, variable binding modifiers and variable binding filters---  built in to Swish.--------------------------------------------------------------------------------------module Swish.RDF.BuiltInMap-    ( findRDFOpenVarBindingModifier-    , findRDFDatatype-    , rdfRulesetMap-    , allRulesets, allDatatypeRulesets-    )-where--import Swish.RDF.BuiltInDatatypes-import Swish.RDF.BuiltInRules----------------------------------------------------------------------------------------  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  All rights reserved.------  This file is part of Swish.------  Swish is free software; you can redistribute it and/or modify---  it under the terms of the GNU General Public License as published by---  the Free Software Foundation; either version 2 of the License, or---  (at your option) any later version.------  Swish is distributed in the hope that it will be useful,---  but WITHOUT ANY WARRANTY; without even the implied warranty of---  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the---  GNU General Public License for more details.------  You should have received a copy of the GNU General Public License---  along with Swish; if not, write to:---    The Free Software Foundation, Inc.,---    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA------------------------------------------------------------------------------------
− src/Swish/RDF/BuiltInRules.hs
@@ -1,157 +0,0 @@------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  BuiltInRules---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  License     :  GPL V2------  Maintainer  :  Douglas Burke---  Stability   :  experimental---  Portability :  H98------  This module collects references and provides access to all of the---  rulesets, variable binding modifiers and variable binding filters---  built in to Swish.--------------------------------------------------------------------------------------module Swish.RDF.BuiltInRules-    ( findRDFOpenVarBindingModifier-    , rdfRulesetMap-    , allRulesets, allDatatypeRulesets-    )-where--import Swish.RDF.BuiltInDatatypes (allDatatypes)--import Swish.RDF.RDFVarBinding-    ( RDFOpenVarBindingModify-    , rdfVarBindingUriRef, rdfVarBindingBlank-    , rdfVarBindingLiteral-    , rdfVarBindingUntypedLiteral, rdfVarBindingTypedLiteral-    , rdfVarBindingXMLLiteral, rdfVarBindingDatatyped-    , rdfVarBindingMemberProp-    )--import Swish.RDF.RDFRuleset (RDFRuleset, RDFRulesetMap)--import Swish.RDF.RDFProofContext-    ( rulesetRDF-    , rulesetRDFS-    , rulesetRDFD )--import Swish.RDF.VarBinding-    ( nullVarBindingModify-    , makeVarFilterModify-    , varFilterEQ, varFilterNE-    )--import Swish.RDF.Datatype (typeRules, typeMkModifiers)-import Swish.Utils.LookupMap (LookupMap(..), mapFindMaybe)-import Swish.Utils.Namespace (ScopedName)-----------------------------------------------------------------  Declare variable binding filters list----------------------------------------------------------------- |List of rdfOpenVarBindingModify values for predefined filters----rdfVarBindingFilters :: [RDFOpenVarBindingModify]-rdfVarBindingFilters =-    [ filter1 rdfVarBindingUriRef-    , filter1 rdfVarBindingBlank-    , filter1 rdfVarBindingLiteral-    , filter1 rdfVarBindingUntypedLiteral-    , filter1 rdfVarBindingTypedLiteral-    , filter1 rdfVarBindingXMLLiteral-    , filter1 rdfVarBindingMemberProp-    , filter2 rdfVarBindingDatatyped-    -- , filterN nullVarBindingModify-    , filter2 varFilterEQ-    , filter2 varFilterNE-    ]-    where-      -- Swish.RDF.VarBinding.openVbmName seems to require that the label-      -- list not be evaluated which means that we can not replace these-      -- statements by ones like-      ---      --    filter1 f (lb:_) = makeVarFilterModift $ f lb-      ---      filter1 f lbs = makeVarFilterModify $ f (head lbs)-      filter2 f lbs = makeVarFilterModify $ f (head lbs) (lbs!!1)-      -- filterN f lbs = makeVarFilterModify $ f ...-----------------------------------------------------------------  Declare variable binding modifiers map---------------------------------------------------------------rdfVarBindingModifiers :: [RDFOpenVarBindingModify]-rdfVarBindingModifiers =-    [ nullVarBindingModify-    ]-----------------------------------------------------------------  Find a named built-in OpenVarBindingModifier---------------------------------------------------------------allOpenVarBindingModify :: [RDFOpenVarBindingModify]-allOpenVarBindingModify =-    rdfVarBindingFilters    ++-    rdfVarBindingModifiers  ++-    dtsVarBindingModifiers--dtsVarBindingModifiers :: [RDFOpenVarBindingModify]--- dtsVarBindingModifiers = concatMap dtVarBindingModifiers allDatatypes-dtsVarBindingModifiers = concatMap typeMkModifiers allDatatypes--{--dtVarBindingModifiers dtval =-    map (makeRdfDtOpenVarBindingModify dtval) (tvalMod dtval)--}--findRDFOpenVarBindingModifier :: ScopedName -> Maybe RDFOpenVarBindingModify-findRDFOpenVarBindingModifier nam =-    mapFindMaybe nam (LookupMap allOpenVarBindingModify)-----------------------------------------------------------------  Lookup map for built-in rulesets---------------------------------------------------------------rdfRulesetMap :: RDFRulesetMap-rdfRulesetMap = LookupMap allRulesets--allRulesets :: [RDFRuleset]-allRulesets =-    [ rulesetRDF-    , rulesetRDFS-    , rulesetRDFD-    ]-    ++ allDatatypeRulesets--allDatatypeRulesets :: [RDFRuleset]-allDatatypeRulesets = map typeRules allDatatypes----------------------------------------------------------------------------------------  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  All rights reserved.------  This file is part of Swish.------  Swish is free software; you can redistribute it and/or modify---  it under the terms of the GNU General Public License as published by---  the Free Software Foundation; either version 2 of the License, or---  (at your option) any later version.------  Swish is distributed in the hope that it will be useful,---  but WITHOUT ANY WARRANTY; without even the implied warranty of---  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the---  GNU General Public License for more details.------  You should have received a copy of the GNU General Public License---  along with Swish; if not, write to:---    The Free Software Foundation, Inc.,---    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA------------------------------------------------------------------------------------
src/Swish/RDF/ClassRestrictionRule.hs view
@@ -1,11 +1,12 @@-{-# LANGUAGE MultiParamTypeClasses, OverloadedStrings #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}  -------------------------------------------------------------------------------- --  See end of this file for licence information. -------------------------------------------------------------------------------- -- | --  Module      :  ClassRestrictionRule---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke --  License     :  GPL V2 -- --  Maintainer  :  Douglas Burke@@ -26,7 +27,12 @@        )        where -import Swish.RDF.RDFGraph+import Swish.Datatype (DatatypeVal(..), DatatypeRel(..), DatatypeRelFn)+import Swish.Namespace (Namespace, ScopedName, namespaceToBuilder)+import Swish.Rule (Rule(..), bwdCheckInference)+import Swish.VarBinding (VarBinding(..))++import Swish.RDF.Graph     ( RDFLabel(..)     , getScopedName     , RDFGraph@@ -38,37 +44,25 @@     , resRdfdMaxCardinality     ) -import Swish.RDF.RDFRuleset (RDFRule, makeRDFGraphFromN3Builder)-import Swish.RDF.RDFDatatype (RDFDatatypeVal, fromRDFLabel, toRDFLabel)+import Swish.RDF.Datatype (RDFDatatypeVal, fromRDFLabel, toRDFLabel)+import Swish.RDF.Ruleset (RDFRule, makeRDFGraphFromN3Builder) -import Swish.RDF.RDFQuery+import Swish.RDF.Query     ( rdfQueryFind     , rdfFindValSubj, rdfFindPredVal, rdfFindPredInt     , rdfFindList     ) -import Swish.RDF.RDFVarBinding (RDFVarBinding)-import Swish.RDF.Datatype (DatatypeVal(..), DatatypeRel(..), DatatypeRelFn)-import Swish.RDF.Rule ( Rule(..), bwdCheckInference)-import Swish.RDF.VarBinding (VarBinding(..))+import Swish.RDF.VarBinding (RDFVarBinding) import Swish.RDF.Vocabulary (namespaceRDFD) -import Swish.Utils.Namespace (Namespace, ScopedName, namespaceToBuilder)-import Swish.Utils.PartOrderedCollection-    ( minima, maxima-    , partCompareEq, partComparePair-    , partCompareListMaybe-    , partCompareListSubset-    )--import Swish.Utils.LookupMap (LookupEntryClass(..), LookupMap(..),mapFindMaybe)-import Swish.Utils.ListHelpers (powerSet)- import Control.Monad (liftM) -import Data.Monoid (Monoid (..))+import Data.List (delete, nub, (\\), subsequences)+import Data.LookupMap (LookupEntryClass(..), LookupMap(..),mapFindMaybe) import Data.Maybe (fromJust, fromMaybe, mapMaybe)-import Data.List (delete, nub, (\\))+import Data.Monoid (Monoid (..))+import Data.Ord.Partial (minima, maxima, partCompareEq, partComparePair, partCompareListMaybe, partCompareListSubset)  import qualified Data.Text.Lazy.Builder as B @@ -86,6 +80,7 @@     , crFunc    :: ClassRestrictionFn     } +-- | Equality of class restrictions is based on the name of the restriction. instance Eq ClassRestriction where     cr1 == cr2  =  crName cr1 == crName cr2 @@ -146,11 +141,16 @@             , "    rdfd:constraint   ?r . "             ]             ---  Placeholder false graph for now.+-- | The graph+--+-- > _:a <http://id.ninebynine.org/2003/rdfext/rdfd#false> _:b .+--+-- Exported for testing. falseGraph :: RDFGraph falseGraph = makeRDFGraphFromN3Builder $              mkPrefix namespaceRDFD `mappend` falseGraphStr +-- | Exported for testing. falseGraphStr :: B.Builder falseGraphStr = "_:a rdfd:false _:b . " @@ -471,7 +471,7 @@ coverSets pvs dts =     minima partCompareListSubset $ map (map fst) ctss     where-        ctss = filter coverspvs $ powerSet cts+        ctss = filter coverspvs $ tail $ subsequences cts         cts  = minima (partComparePair partCompareListMaybe partCompareEq) dts         coverspvs cs = coversVals delete (map snd cs) pvs @@ -511,9 +511,7 @@ deleteMaybe Nothing  as = as deleteMaybe (Just a) as = delete a as ----------------------------------------------------------------  Make restriction rules from supplied datatype and graph-------------------------------------------------------------+-- | Make restriction rules from the supplied datatype and graph.  makeRDFDatatypeRestrictionRules :: RDFDatatypeVal vt -> RDFGraph -> [RDFRule] makeRDFDatatypeRestrictionRules dtval =@@ -523,7 +521,8 @@  -------------------------------------------------------------------------------- -----  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke --  All rights reserved. -- --  This file is part of Swish.
src/Swish/RDF/Datatype.hs view
@@ -1,1047 +1,187 @@-{-# LANGUAGE ExistentialQuantification, MultiParamTypeClasses, OverloadedStrings #-}-------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  Datatype---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  License     :  GPL V2------  Maintainer  :  Douglas Burke---  Stability   :  experimental---  Portability :  ExistentialQuantification, MultiParamTypeClasses, OverloadedStrings------  This module defines the structures used by Swish to represent and---  manipulate datatypes.  It is designed as a basis for handling datatyped---  RDF literals, but the functions in this module are more generic.----------------------------------------------------------------------------------------  Testing note:  this module supports a number of specific datatypes.---  It is intended that functionality in this module will be tested via---  modules "Swish.RDF.RDFDatatype", ---  "Swish.RDF.ClassRestrictionRule" and---  "Swish.RDF.RDFDatatypeXsdInteger".---  See also module ClassRestrictionRuleTest for test cases.--module Swish.RDF.Datatype-    ( Datatype(..)-    , typeName, typeRules, typeMkRules, typeMkModifiers, typeMkCanonicalForm-    , getTypeAxiom, getTypeRule-    , DatatypeVal(..)-    , getDTMod-    , getDTRel-    , tvalMkCanonicalForm-    , DatatypeMap(..)-    , DatatypeRel(..), DatatypeRelFn, DatatypeRelPr-    , altArgs-    , UnaryFnDescr,    UnaryFnTable,    UnaryFnApply,    unaryFnApp-    , BinaryFnDescr,   BinaryFnTable,   BinaryFnApply,   binaryFnApp-    , BinMaybeFnDescr, BinMaybeFnTable, BinMaybeFnApply, binMaybeFnApp-    , ListFnDescr,     ListFnTable,     ListFnApply,     listFnApp-    , DatatypeMod(..), ModifierFn-    , ApplyModifier-    , nullDatatypeMod-    -- , applyDatatypeMod-    , makeVmod11inv, makeVmod11-    , makeVmod21inv, makeVmod21-    , makeVmod20-    , makeVmod22-    , makeVmodN1-    , DatatypeSub(..)-    )-where--import Swish.RDF.Ruleset-    ( Ruleset(..)-    , getRulesetAxiom, getRulesetRule-    )--import Swish.RDF.Rule-    ( Formula(..)-    , Rule(..)-    )--import Swish.RDF.Vocabulary (swishName)-import Swish.Utils.Namespace (ScopedName)--import Swish.RDF.VarBinding-    ( VarBinding(..)-    , addVarBinding-    , VarBindingModify(..), OpenVarBindingModify, nullVarBindingModify-    )--import Swish.Utils.LookupMap-    ( LookupEntryClass(..), LookupMap(..)-    , mapFindMaybe-    )--import Swish.Utils.ListHelpers-    ( flist-    , deleteIndex-    )---- used to add Show instances for structures during debugging--- but backed out again.------ import Swish.Utils.ShowM (ShowM(..))--import Data.Maybe( isJust, catMaybes )--import Control.Monad( join, liftM )--import qualified Data.Text as T-----------------------------------------------------------------  Datatype framework----------------------------------------------------------------- |Datatype wraps a 'DatatypeVal' value, hiding the value type that---  is used only in implementations of the datatype.---  Users see just the datatype name and associated ruleset.----data Datatype ex lb vn = forall vt . Datatype (DatatypeVal ex vt lb vn)--instance LookupEntryClass-        (Datatype ex lb vn) ScopedName (Datatype ex lb vn)-    where-    newEntry (_,dt) = dt-    keyVal dt       = (typeName dt, dt)---- |Get type name from Datatype value-typeName :: Datatype ex lb vn -> ScopedName-typeName (Datatype dtv) = tvalName  dtv---- |Get static rules from Datatype value-typeRules :: Datatype ex lb vn -> Ruleset ex-typeRules (Datatype dtv) = tvalRules dtv---- |Make rules for Datatype value based on supplied expression-typeMkRules :: Datatype ex lb vn -> ex -> [Rule ex]-typeMkRules (Datatype dtv) = tvalMkRules dtv---- |Make variable binding modifiers based on values supplied-typeMkModifiers :: Datatype ex lb vn -> [OpenVarBindingModify lb vn]-typeMkModifiers (Datatype dtv) = tvalMkMods dtv---- |Get the named axiom from a Datatype value.-getTypeAxiom :: ScopedName -> Datatype ex lb vn -> Maybe (Formula ex)-getTypeAxiom nam dt = getRulesetAxiom nam (typeRules dt)---- |Get the named rule from a Datatype value.-getTypeRule :: ScopedName -> Datatype ex lb vn -> Maybe (Rule ex)-getTypeRule nam dt = getRulesetRule nam (typeRules dt)---- |Get the canonical form of a datatype value.-typeMkCanonicalForm :: Datatype ex lb vn -> T.Text -> Maybe T.Text-typeMkCanonicalForm (Datatype dtv) = tvalMkCanonicalForm dtv-----------------------------------------------------------------  DatatypeVal----------------------------------------------------------------- |DatatypeVal is a structure that defines a number of functions---  and values that characterize the behaviour of a datatype.------  A datatype is specified with respect to (polymophic in) a given---  type of (syntactic) expression with which it may be used, and---  a value type (whose existence is hidden as an existential type---  within `DatatypeMap`).------  (I tried hiding the value type with an internal existential---  declaration, but that wouldn't wash.  Hence this two-part---  structure with `Datatype` in which the internal detail---  of the value type is hidden from users of the `Datatype` class.)------  The datatype characteristic functions have two goals:------  (1) to support the general datatype entailment rules defined by---      the RDF semantics specification, and------  (2) to define additional datatype-specific inference patterns by---      means of which provide additional base functionality to---      applications based on RDF inference.------  Datatype-specific inferences are provided using the `DatatypeRel`---  structure for a datatype, which allows a number of named relations---  to be defined on datatype values, and provides mechanisms to---  calculate missing values in a partially-specified member of---  a relation.------  Note that rules and variable binding modifiers that deal with---  combined values of more than one datatype may be defined---  separately.  Definitions in this module are generally applicable---  only when using a single datatype.------  An alternative model for datatype value calculations is inspired---  by that introduced by CWM for arithmetic operations, e.g.------  >     (1 2 3) math:sum ?x => ?x rdf:value 6------  (where the bare integer @n@ here is shorthand for @\"n\"^^xsd:integer@).------  Datatype-specific inference patterns are provided in two ways:------  * by variable binding modifiers that can be combined with the---    query results during forward- for backward-chaining of---    inference rules, and------  * by the definition of inference rulesets that involve---    datatype values.------  I believe the first method to be more flexible than the second,---  in that it more readily supports forward and backward chaining,---  but can be used only through the definition of new rules.------  Type parameters:------  [@ex@] is the type of expression with which the datatype may be used.------  [@vt@] is the internal value type with which the labels are associated.------  [@lb@] is the type of label that may be used as a variable in an---         expression or rule.------  [@vn@] is the type of node that may be used to carry a value in an---         expression or rule.----data DatatypeVal ex vt lb vn = DatatypeVal-    { tvalName      :: ScopedName-                                -- ^Identifies the datatype, and also-                                --  its value space class.-    , tvalRules     :: Ruleset ex-                                -- ^A set of named expressions and rules-                                --  that are valid in in any theory that-                                --  recognizes the current datatype.-    , tvalMkRules   :: ex -> [Rule ex]-                                -- ^A function that accepts an expression-                                --  and devives some datatype-dependent-                                --  rules from it.  This is provided as a-                                --  hook for creating datatyped class-                                --  restriction rules.-    , tvalMkMods    :: [OpenVarBindingModify lb vn]-                                -- ^Constructs a list of open variable-                                --  binding modifiers based on tvalMod,-                                --  but hiding the actual value type.-    , tvalMap       :: DatatypeMap vt-                                -- ^Lexical to value mapping, where @vt@ is-                                --  a datatype used within a Haskell program-                                --  to represent and manipulate values in-                                --  the datatype's value space-    , tvalRel       :: [DatatypeRel vt]-                                -- ^A set of named relations on datatype-                                --  values.  Each relation accepts a list-                                --  of @Maybe vt@, and computes any-                                --  unspecified values that are in the-                                --  relation with values supplied.-    , tvalMod       :: [DatatypeMod vt lb vn]-                                -- ^A list of named values that are used to-                                --  construct variable binding modifiers, which-                                --  in turn may be used by a rule definition.-                                ---                                --  TODO: In due course, this value may be-                                --  calculated automatically from the supplied-                                --  value for @tvalRel@.-    }--{--instance ShowM ex => Show (DatatypeVal ex vt lb vn) where-  show dv = "DatatypeVal: " ++ show (tvalName dv) ++ "\n -> rules:\n" ++ show (tvalRules dv)--}----  Other accessor functions--getDTRel ::-    ScopedName -> DatatypeVal ex vt lb vn -> Maybe (DatatypeRel vt)-getDTRel nam dtv =-    mapFindMaybe nam (LookupMap (tvalRel dtv))--getDTMod ::-    ScopedName -> DatatypeVal ex vt lb vn -> Maybe (DatatypeMod vt lb vn)-getDTMod nam dtv =-    mapFindMaybe nam (LookupMap (tvalMod dtv))---- |Get the canonical form of a datatype value, or @Nothing@.----tvalMkCanonicalForm :: DatatypeVal ex vt lb vn -> T.Text -> Maybe T.Text-tvalMkCanonicalForm dtv str = can-    where-      dtmap = tvalMap dtv-      val   = mapL2V dtmap str-      can   = join $ liftM (mapV2L dtmap) val---- |DatatypeMap consists of methods that perform lexical-to-value---  and value-to-canonical-lexical mappings for a datatype.------  The datatype mappings apply to string lexical forms which---  are stored as `Data.Text`.----data DatatypeMap vt = DatatypeMap-    { mapL2V  :: T.Text -> Maybe vt-                            -- ^ Function to map a lexical string to-                            --   the datatype value.  This effectively-                            --   defines the lexical space of the-                            --   datatype to be all strings for which-                            --   yield a value other than @Nothing@.-    , mapV2L  :: vt -> Maybe T.Text-                            -- ^ Function to map a value to its canonical-                            --   lexical form, if it has such.-    }---- |Type for a datatype relation inference function.------  A datatype relation defines tuples of values that satisfy some---  relation.  A datatype relation inference function calculates---  values that complete a relation with values supplied.------  The function accepts a list of @Maybe vt@, where vt is the---  datatype value type.  It returns one of:------  * Just a list of lists, where each inner list returned is a---      complete set of values, including the values supplied, that---      are in the relation.------  * Just an empty list is returned if the supplied values are---      insufficient to compute any complete sets of values in the---      relation.------  * Nothing if the supplied values are not consistent with---      the relation.----type DatatypeRelFn vt = [Maybe vt] -> Maybe [[vt]]---- |Type for datatype relation predicate:  accepts a list of values---  and determines whether or not they satisfy the relation.----type DatatypeRelPr vt = [vt] -> Bool---- |Datatype for a named relation on values of a datatype.----data DatatypeRel vt = DatatypeRel-    { dtRelName :: ScopedName-    , dtRelFunc :: DatatypeRelFn vt-    }--instance LookupEntryClass (DatatypeRel vt) ScopedName (DatatypeRel vt)-    where-    newEntry (_,relf) = relf-    keyVal dtrel = (dtRelName dtrel, dtrel)---- |Datatype value modifier functions type------  Each function accepts a list of values and returns a list of values.---  The exact significance of the different values supplied and returned---  depends on the variable binding pattern used (cf. 'ApplyModifier'),---  but in all cases an empty list returned means that the corresponding---  inputs are not consistent with the function and cannot be used.----type ModifierFn vn = [vn] -> [vn]---- |Type of function used to apply a data value modifier to specified---  variables in a supplied variable binding.  It also accepts the---  name of the datatype modifier and carries it into the resulting---  variable binding modifier.------  (Note that @vn@ is not necessarily the same as @vt@, the datatype value---  type:  the modifier functions may be lifted or otherwise adapted---  to operate on some other type from which the raw data values are---  extracted.)----type ApplyModifier lb vn =-    ScopedName -> [ModifierFn vn] -> OpenVarBindingModify lb vn---- |Wrapper for data type variable binding modifier included in---  a datatype value.----data DatatypeMod vt lb vn = DatatypeMod-    { dmName :: ScopedName-    , dmModf :: [ModifierFn vt]-    , dmAppf :: ApplyModifier lb vn-    }--instance LookupEntryClass-        (DatatypeMod vt lb vn) ScopedName (DatatypeMod vt lb vn)-    where-    newEntry (_,dmod) = dmod-    keyVal dmod = (dmName dmod, dmod)---- |Null datatype value modifier-nullDatatypeMod :: DatatypeMod vt lb vn-nullDatatypeMod = DatatypeMod-    { dmName = swishName "nullDatatypeMod"-    , dmModf = []-    , dmAppf = nullAppf-    }-    where-        -- nullAppf :: ScopedName -> [ModifierFn vn] -> OpenVarBindingModify lb vn-        nullAppf nam _ lbs = (nullVarBindingModify lbs) { vbmName = nam }--{---- |Apply datatype variable binding modifier value to list of labels and---  a variable binding.-applyDatatypeMod :: (Eq lb, Show lb, Eq vn, Show vn)-    => DatatypeMod vt lb vn -> OpenVarBindingModify lb vn-applyDatatypeMod dtmod = dmAppf dtmod (dmName dtmod) (dmModf dtmod)--}--{--dmName dtmod :: ScopedName-dmModf dtmod :: [ModifierFn vt]-             :: [[vt] -> [vt]]-dmAppf dtmod :: ApplyModifier lb vn-             :: ScopedName -> [ModifierFn vn] -> OpenVarBindingModify lb vn-             :: ScopedName -> [[vn] -> [vn]] -> OpenVarBindingModify lb vn-dmAppf dtmod (dmName dtmod)-             :: [[vn] -> [vn]] -> OpenVarBindingModify lb vn--}-------------------------------------------------------------------  Functions for creating datatype variable binding modifiers------------------------------------------------------------------- |'ApplyModifier' function for use with 'DatatypeMod' in cases---  when the value mapping is a @1->1@ function and inverse, such---  as negate.------  [@nam@]     is the name from the 'DatatypeMod' value that is carried into---          the resulting variable binding modifier.---        ---  [@fns@]     are functions used to implement details of the variable---          binding modifier:------          (0) is @[x,y] -> [?]@, used as a filter (i.e. not creating any---              new variable bindings), returning a non-empty list if @x@ and @y@---              are in the appropriate relationship.------          (1) is @[y] -> [x]@, used to perform the calculation in a forward---              direction.------          (2) is @[x] -> [y]@, used to perform the calculation in a backward---              direction.  This may be the same as (2) (e.g. for negation)---              or may be different (e.g. increment).------  [@lbs@]     is a list of specific label values for which a variable binding---          modifier will be generated.  (The intent is that a variable-free---          value can be generated as a Curried function, and instantiated---          for particular variables as required.)------  Note: an irrefutable pattern match for @lbs@ is used so that a name---  for the 'VarBindingModify' value can be extracted using an undefined---  label value.----makeVmod11inv :: (Eq lb, Show lb, Eq vn, Show vn) => ApplyModifier lb vn-makeVmod11inv nam [f0,f1,f2] lbs@(~[lb1,lb2]) = VarBindingModify-    { vbmName   = nam-    , vbmApply  = concatMap app1-    , vbmVocab  = lbs-    , vbmUsage  = [[],[lb1],[lb2]]-    }-    where-        app1 vbind = app2 (map (vbMap vbind) lbs) vbind-        app2 [Just v1,Just v2] vbind = selv     (f0 [v1,v2]) vbind-        app2 [Nothing,Just v2] vbind = addv lb1 (f1 [v2])    vbind-        app2 [Just v1,Nothing] vbind = addv lb2 (f2 [v1])    vbind-        app2 _                     _     = []-makeVmod11inv _ _ _ =-    error "makeVmod11inv: requires 3 functions and 2 labels"---- |'ApplyModifier' function for use with 'DatatypeMod' in cases when---  the value mapping is a non-invertable @1->1@ injection, such as---  absolute value.------  [@nam@] is the name from the 'DatatypeMod' value that is carried into---          the resulting variable binding modifier.------  [@fns@] are functions used to implement details of the variable---          binding modifier:------          (0) is @[x,y] -> [?]@, used as a filter (i.e. not creating any---              new variable bindings), returning a non-empty list if @x@ and @y@---              are in the appropriate relationship.------          (1) is @[x]@ -> @[y]@, used to perform the calculation.------  [@lbs@] is a list of specific label values for which a variable binding---          modifier will be generated.------  Note: an irrefutable pattern match for @lbs@ is used so that a name---  for the 'VarBindingModify' value can be extracted using an undefined---  label value.----makeVmod11 :: (Eq lb, Show lb, Eq vn, Show vn) => ApplyModifier lb vn-makeVmod11 nam [f0,f1] lbs@(~[lb1,_]) = VarBindingModify-    { vbmName   = nam-    , vbmApply  = concatMap app1-    , vbmVocab  = lbs-    , vbmUsage  = [[],[lb1]]-    }-    where-        app1 vbind = app2 (map (vbMap vbind) lbs) vbind-        app2 [Just v1,Just v2] vbind = selv (f0 [v1,v2])  vbind-        app2 [Nothing,Just v2] vbind = addv lb1 (f1 [v2]) vbind-        app2 _                     _     = []-makeVmod11 _ _ _ =-    error "makeVmod11: requires 2 functions and 2 labels"---- |'ApplyModifier' function for use with 'DatatypeMod' in cases---  when the value mapping is a @2->1@ invertable function, such as---  addition or subtraction.------  [@nam@]     is the name from the 'DatatypeMod' value that is carried into---          the resulting variable binding modifier.------  [@fns@]     are functions used to implement details of the variable---          binding modifier:------          (1) is @[x,y,z] -> [?]@, used as a filter (i.e. not creating any---              new variable bindings), returning a non-empty list if---              @x@, @y@ and @z@ are in the appropriate relationship.------          (2) is @[y,z] -> [x]@, used to perform the calculation in a---              forward direction.------          (3) is @[x,z] -> [y]@, used to run the calculation backwards to---              determine the first input argument------          (4) is @[x,y] -> [z]@, used to run the calculation backwards to---              determine the second input argument------  [@lbs@]     is a list of specific label values for which a variable binding---          modifier will be generated.------  Note: an irrefutable pattern match for @lbs@ is used so that a name---  for the 'VarBindingModify' value can be extracted using an undefined---  label value.----makeVmod21inv :: (Eq lb, Show lb, Eq vn, Show vn) => ApplyModifier lb vn-makeVmod21inv nam [f0,f1,f2,f3] lbs@(~[lb1,lb2,lb3]) = VarBindingModify-    { vbmName   = nam-    , vbmApply  = concatMap app1-    , vbmVocab  = lbs-    , vbmUsage  = [[],[lb1],[lb2],[lb3]]-    }-    where-        app1 vbind = app2 (map (vbMap vbind) lbs) vbind-        app2 [Just v1,Just v2,Just v3] vbind = selv (f0 [v1,v2,v3]) vbind-        app2 [Nothing,Just v2,Just v3] vbind = addv lb1 (f1 [v2,v3]) vbind-        app2 [Just v1,Nothing,Just v3] vbind = addv lb2 (f2 [v1,v3]) vbind-        app2 [Just v1,Just v2,Nothing] vbind = addv lb3 (f3 [v1,v2]) vbind-        app2 _                               _     = []-makeVmod21inv _ _ _ =-    error "makeVmod21inv: requires 4 functions and 3 labels"---- |'ApplyModifier' function for use with 'DatatypeMod' in cases---  when the value mapping is a @2->1@ non-invertable function, such as---  logical @AND@ or @OR@.------  [@nam@]     is the name from the 'DatatypeMod' value that is carried into---          the resulting variable binding modifier.------  [@fns@]     are functions used to implement details of the variable---          binding modifier:------          (1) is @[x,y,z] -> [?]@, used as a filter (i.e. not creating any---              new variable bindings), returning a non-empty list if---              @x@, @y@ and @z@ are in the appropriate relationship.------          (2) is @[y,z] -> [x]@, used to perform the calculation in a---              forward direction.------  [@lbs@]     is a list of specific label values for which a variable binding---          modifier will be generated.------  Note: an irrefutable pattern match for @lbs@ is used so that a name---  for the 'VarBindingModify' value can be extracted using an undefined---  label value.----makeVmod21 :: (Eq lb, Show lb, Eq vn, Show vn) => ApplyModifier lb vn-makeVmod21 nam [f0,f1] lbs@(~[lb1,_,_]) = VarBindingModify-    { vbmName   = nam-    , vbmApply  = concatMap app1-    , vbmVocab  = lbs-    , vbmUsage  = [[],[lb1]]-    }-    where-        app1 vbind = app2 (map (vbMap vbind) lbs) vbind-        app2 [Just v1,Just v2,Just v3] vbind = selv (f0 [v1,v2,v3]) vbind-        app2 [Nothing,Just v2,Just v3] vbind = addv lb1 (f1 [v2,v3]) vbind-        app2 _                               _     = []-makeVmod21 _ _ _ =-    error "makeVmod21: requires 2 functions and 3 labels"---- |'ApplyModifier' function for use with 'DatatypeMod' in cases---  when the value mapping is a simple comparson of two values.------  [@nam@]     is the name from the 'DatatypeMod' value that is carried into---          the resulting variable binding modifier.------  [@fns@]     are functions used to implement details of the variable---          binding modifier:------          (1) is @[x,y] -> [?]@, used as a filter (i.e. not creating any---              new variable bindings), returning a non-empty list if---              @x@ and @y@ are in the appropriate relationship.------  [@lbs@]     is a list of specific label values for which a variable binding---          modifier will be generated.------  Note: an irrefutable pattern match for @lbs@ is used so that a name---  for the 'VarBindingModify' value can be extracted using an undefined---  label value.----makeVmod20 :: (Eq lb, Show lb, Eq vn, Show vn) => ApplyModifier lb vn-makeVmod20 nam [f0] lbs@(~[_,_]) = VarBindingModify-    { vbmName   = nam-    , vbmApply  = concatMap app1-    , vbmVocab  = lbs-    , vbmUsage  = [[]]-    }-    where-        app1 vbind = app2 (map (vbMap vbind) lbs) vbind-        app2 [Just v1,Just v2] vbind = selv (f0 [v1,v2]) vbind-        app2 _                     _     = []-makeVmod20 _ _ _ =-    error "makeVmod20: requires 1 function and 2 labels"---- |'ApplyModifier' function for use with 'DatatypeMod' in cases---  when the value mapping is a @2->2@ non-invertable function, such as---  quotient/remainder------  [@nam@]     is the name from the 'DatatypeMod' value that is carried into---          the resulting variable binding modifier.------  [@fns@]     are functions used to implement details of the variable---          binding modifier:------          (1) is @[w,x,y,z] -> [?]@, used as a filter (i.e. not creating---              any new variable bindings), returning a non-empty list if---              @w@, @x@, @y@ and @z@ are in the appropriate relationship.------          (2) is @[y,z] -> [w,x]@, used to perform the calculation given---              two input values.------  [@lbs@]     is a list of specific label values for which a variable binding---          modifier will be generated.------  Note: an irrefutable pattern match for @lbs@ is used so that a name---  for the 'VarBindingModify' value can be extracted using an undefined---  label value.------  NOTE: this might be generalized to allow one of @w@ or @x@ to be---  specified, and return null if it doesn't match the calculated value.----makeVmod22 :: (Eq lb, Show lb, Eq vn, Show vn) => ApplyModifier lb vn-makeVmod22 nam [f0,f1] lbs@(~[lb1,lb2,_,_]) = VarBindingModify-    { vbmName   = nam-    , vbmApply  = concatMap app1-    , vbmVocab  = lbs-    , vbmUsage  = [[],[lb1,lb2]]-    }-    where-        app1 vbind = app2 (map (vbMap vbind) lbs) vbind-        app2 [Just v1,Just v2,Just v3,Just v4] vbind =-            selv (f0 [v1,v2,v3,v4]) vbind-        app2 [Nothing,Nothing,Just v3,Just v4] vbind =-            addv2 lb1 lb2 (f1 [v3,v4]) vbind-        app2 _                               _     = []-makeVmod22 _ _ _ =-    error "makeVmod22: requires 2 functions and 4 labels"---- |'ApplyModifier' function for use with 'DatatypeMod' in cases---  when the value mapping is a @N->1@ function,---  such as Sigma (sum) of a vector.------  [@nam@]     is the name from the 'DatatypeMod' value that is carried into---          the resulting variable binding modifier.------  [@fns@]     are functions used to implement details of the variable---          binding modifier:------          (1) is @[x,y...] -> [?]@, used as a filter (i.e. not creating---              any new variable bindings), returning a non-empty list if---              @x@ and @y...@ are in the appropriate relationship.------          (2) is @[y...] -> [x]@, used to perform the calculation.------  [@lbs@]     is a list of specific label values for which a variable binding---          modifier will be generated.------  Note: an irrefutable pattern match for @lbs@ is used so that a name---  for the 'VarBindingModify' value can be extracted using an undefined---  label value.----makeVmodN1 :: (Eq lb, Show lb, Eq vn, Show vn) => ApplyModifier lb vn-makeVmodN1 nam [f0,f1] lbs@(~(lb1:_)) = VarBindingModify-    { vbmName   = nam-    , vbmApply  = concatMap app1-    , vbmVocab  = lbs-    , vbmUsage  = [[],[lb1]]-    }-    where-        app1 vbind = app2 (map (vbMap vbind) lbs) vbind-        app2 vs@(v1:_) vbind-            | isJust v1 && isJustvs = selv (f0 jvs) vbind-            | isJustvs              = addv lb1 (f1 jvs) vbind-            | otherwise             = []-            where-                isJustvs = all isJust vs-                jvs      = catMaybes vs-        app2 _ _ = error "app2 sent empty list" -- -Wall--makeVmodN1 _ _ _ =-    error "makeVmodN1: requires 2 functions and at 1 or more labels"-------------------------------------------------------------  Local helper functions for makeVmodXXX variants-------------------------------------------------------------  Add value to variable variable binding, if value is singleton list,---  otherwise return empty list.-addv :: (Eq lb, Show lb, Eq vt, Show vt)-    => lb -> [vt] -> VarBinding lb vt-    -> [VarBinding lb vt]-addv lb [val] vbind = [addVarBinding lb val vbind]-addv _  _     _     = []----  Add two entries to variable variable binding, if value supplied is---  a doubleton list, otherwise return empty list.-addv2 :: (Eq lb, Show lb, Eq vt, Show vt)-    => lb -> lb -> [vt] -> VarBinding lb vt-    -> [VarBinding lb vt]-addv2 lb1 lb2 [val1,val2] vbind = [addVarBinding lb1 val1 $-                                   addVarBinding lb2 val2 vbind]-addv2 _   _   _           _     = []----  If supplied value is non-empty list return supplied variable binding,---  otherwise return empty list.-selv :: [vt] -> varBinding lb vt -> [varBinding lb vt]-selv [] _     = []-selv _  vbind = [vbind]-------------------------------------------------------------------  Functions for evaluating arguments in a datatype relation---------------------------------------------------------------------  altArgs is a generic function for evaluating datatype relation---          values, based on suppied functions and argument values------  UnaryFnDescr, UnaryFnApply and unaryFnApp:---          are support types and function for using altArgs to---          evaluate relations on unary functions (binary relations).------  BinaryFnDescr, BinaryFnApply and binaryFnApp:---          are support types and function for using altArgs to---          evaluate relations on binary functions (3-way relations).------  ListFnDescr, ListFnApply and listFnApp:---          are support types and function for using altArgs to---          evaluate relations on list functions (n-way relations),---          where the first member of the list is the value of a---          fold of a function over the rest of the list.------  See experimental module spike-altargs.hs for test cases and---  development steps for this function.---- |Given a list of argument values and a list of functions for---  calculating new values from supplied values, return a list---  of argument values, or @Nothing@ if the supplied values are---  inconsistent with the calculations specified.------  Each list of values returned corresponds to a set of values that---  satisfy the relation, consistent with the values supplied.------  Functions are described as tuple consisting of:------    (a) a predicate that the argument is required to satisfy------    (b) a function to apply,------    (c) a function to apply function (b) to a list of arguments------    (d) argument list index values to which the function is applied.------  Each supplied argument is of the form @Maybe a@, where the argument---  has value type a.  @Nothing@ indicates arguments of unknown value.------  The basic idea is that, for each argument position in the relation,---  a function may be supplied to calculate that argument's possible values---  from some combination of the other arguments.  The results calculated---  in this way are compared with the original arguments provided:---  if the values conflict then the relation is presumed to be---  unsatisfiable with the supplied values, and @Nothing@ is returned;---  if there are any calculated values for arguments supplied without---  any values, then tbe calculated values are used.---  If there are any arguments for which no values are supplied or---  calculated, then the relation is presumed to be underdetermined,---  and @Just []@ is returned.----altArgs :: -  (Eq vt)-  => DatatypeRelPr vt -  -> [(vt->Bool,[b])]-  -- ^ a list of argument value predicates and-  --   function descriptors.  The predicate indicates any-  --   additional constraints on argument values (e.g. the result-  --   of abs must be positive).  Use @(const True)@ for the predicate-  --   associated with unconstrained relation arguments.-  --   For each argument, a list of function descriptors is-  --   supplied corresponding to alternative values (e.g. a square-  --   relation would offer two alternative values for the root.)--  -> ((vt->Bool)->b->[Maybe vt]->Maybe [vt])-  -- ^ a function that takes an argument value predicate,-  --   a function descriptor and applies it to a supplied argument-  --   list to return:-  --   @Just a@ calculated list of one or more possible argument values,-  --   @Just []@ indicating insufficient information provided, or-  --   @Nothing@ indicating inconsistent information provided.-  --   May be one of 'unaryFnApp', 'binaryFnApp', 'listFnApp' or-  --   some other caller-supplied value.--  -> DatatypeRelFn vt-  -- ^ The return value can be used as the-  -- 'dtRelFunc' component of a 'DatatypeRel' value.--altArgs pr fnss apfn args = cvals4 cvals3-    where-        --  Calculate new value(s) for each argument from supplied values, and-        --  lift inconsistency indicator (Just/Nothing) to outermost Monad.-        --    cvals1 :: [Maybe [vt]]-        cvals1 = flist (map (applyFdescToTuple apfn) fnss) args-        --  Merge calculated values with supplied arguments, and again-        --  lift inconsistency indicator (Just/Nothing) to outermost Monad.-        --    cvals2 :: Maybe [[vt]]-        cvals2 = sequence $ mergeTupleVals (map fst fnss) args cvals1-        --  Map list of alternative values for each tuple member to-        --  a list of alternative tuples.-        cvals3 = liftM sequence cvals2-        --  Check each tuple against the supplied predicate.-        --  If any of the alternative tuples does not match the predicate-        --  then signal an inconsistency.-        cvals4 Nothing       = Nothing-        cvals4 cvs@(Just ts) = if all pr ts then cvs else Nothing----  Perform alternative calculations for single result value---  Each result value is a list of zero or more alternatives---  that can be calculated from available parameters, or---  Nothing if the available parameters are inconsistent.------  apfn    is the function that actually applies an element of---          the function descriptor to a tuple of Maybe arguments---          (where Nothing is used to indicate an unknown value)---  (p,fns) is a pair consisting of a value-checking predicate---          for the corresponding tuple member, and a list of---          function descriptors that each return one or more---          values the tuple member, calculated from other values---          that are present.  Just [] means no values are---          calculated for this member, and Nothing means the---          calculation has detected tuple values supplied that---          are inconsistent with the datatype relation concerned.---  args    is a tuple of Maybe tuple elements, (where Nothing---          indicates an unknown value).------  Returns Maybe a list of alternative values for the member,---  Just [] to indicate insufficient information to calculate---  any new values, and Nothing to indicate an inconsistency.----applyFdescToTuple ::-    ((vt->Bool)->b->[Maybe vt]->Maybe [vt]) -> (vt->Bool,[b]) -> [Maybe vt]-    -> Maybe [vt]-applyFdescToTuple apfn (p,fns) args =-    liftM concat $ sequence cvals-    where-        -- cvals :: [Maybe [vt]]-        cvals = flist (map (apfn p) fns) args----  Merge calculated tuple values with supplied tuple, checking for consistency.------  ps      predicates used for isolated validation of each tuple member---  args    supplied tuple values, with Nothing for unknown values---  cvals   list of alternative calculated values for each tuple member,---          or Nothing if an inconsistency has been detected by the---          tuple-calculation functions.  Note that this list may contain---          more entries than args; the surplus entries are ignored---          (see list functions for how this is used).------  Returns a tuple of Maybe lists of values for each tuple member,---  containing Nothing if an inconsistency has been detected in the---  supplied values.----mergeTupleVals  :: (Eq a) => [a->Bool] -> [Maybe a] -> [Maybe [a]] -> [Maybe [a]]-mergeTupleVals _ _  (Nothing:_) = [Nothing]-mergeTupleVals (_:ps) (Nothing:a1s) (Just a2s:a2ss)-                             = Just a2s:mergeTupleVals ps a1s a2ss-mergeTupleVals (p:ps) (Just a1:a1s) (Just []:a2ss)-    | p a1                   = Just [a1]:mergeTupleVals ps a1s a2ss-    | otherwise              = [Nothing]-mergeTupleVals (p:ps) (Just a1:a1s) (Just a2s:a2ss)-    | p a1 && elem a1 a2s    = Just [a1]:mergeTupleVals ps a1s a2ss-    | otherwise              = [Nothing]-mergeTupleVals _ [] _        = []-mergeTupleVals _ _  _        = [Nothing]---- |'altArgs' support for unary functions: function descriptor type-type UnaryFnDescr a = (a->a,Int)---- |'altArgs' support for unary functions: function descriptor table type-type UnaryFnTable a = [(a->Bool,[UnaryFnDescr a])]---- |'altArgs' support for unary functions: function applicator type-type UnaryFnApply a = (a->Bool) -> UnaryFnDescr a -> [Maybe a] -> Maybe [a]---- |'altArgs' support for unary functions: function applicator-unaryFnApp :: UnaryFnApply a-unaryFnApp p (f1,n) args = apf (args!!n)-    where-        apf (Just a) = if p r then Just [r] else Nothing where r = f1 a-        apf Nothing  = Just []---- |'altArgs' support for binary functions: function descriptor type-type BinaryFnDescr a = (a->a->a,Int,Int)---- |'altArgs' support for binary functions: function descriptor table type-type BinaryFnTable a = [(a->Bool,[BinaryFnDescr a])]---- |'altArgs' support for binary functions: function applicator type-type BinaryFnApply a =-    (a->Bool) -> BinaryFnDescr a -> [Maybe a] -> Maybe [a]---- |'altArgs' support for binary functions: function applicator-binaryFnApp :: BinaryFnApply a-binaryFnApp p (f,n1,n2) args = apf (args!!n1) (args!!n2)-    where-        apf (Just a1) (Just a2) = if p r then Just [r] else Nothing-            where r = f a1 a2-        apf _ _  = Just []---- |'altArgs' support for binary function with provision for indicating---  inconsistent supplied values:  function descriptor type-type BinMaybeFnDescr a = (a->a->Maybe [a],Int,Int)---- |'altArgs' support for binary function with provision for indicating---  inconsistent supplied values:  function descriptor table type-type BinMaybeFnTable a = [(a->Bool,[BinMaybeFnDescr a])]---- |'altArgs' support for binary function with provision for indicating---  inconsistent supplied values:  function applicator type-type BinMaybeFnApply a =-    (a->Bool) -> BinMaybeFnDescr a -> [Maybe a] -> Maybe [a]---- |'altArgs' support for binary function with provision for indicating---  inconsistent supplied values:  function applicator-binMaybeFnApp :: BinMaybeFnApply a-binMaybeFnApp p (f,n1,n2) args = apf (args!!n1) (args!!n2)-    where-        apf (Just a1) (Just a2) = if pm r then r else Nothing-            where-                r = f a1 a2-                pm Nothing  = False-                pm (Just x) = all p x-        apf _ _  = Just []---- |'altArgs' support for list functions (e.g. sum over list of args),---  where first element of list is a fold over the rest of the list,---  and remaining elements of list can be calculated in terms---  of the result of the fold and the remaining elements------  List function descriptor is------  (a) list-fold function, f  (e.g. (+)---        ---  (b) list-fold identity, z  (e.g. 0)---        ---  (c) list-fold-function inverse, g (e.g. (-))---        ---  (d) index of element to evaluate---        ---  such that:---        ---  >    (a `f` z) == (z `f` a) == a---  >    (a `g` c) == b <=> a == b `f` c---  >    (a `g` z) == a---  >    (a `g` a) == z------  and the result of the folded function does not depend on---  the order that the list elements are processed.------  NOTE:  the list of 'ListFnDescr' values supplied to 'altArgs' must---  be at least as long as the argument list.  In many cases, Haskell---  lazy evaluation can be used to supply an arbitrarily long list.---  See test cases in spike-altargs.hs for an example.------  Function descriptor type-type ListFnDescr a = (a->a->a,a,a->a->a,Int)---- |Function table type-type ListFnTable a = [(a->Bool,[ListFnDescr a])]---- |'altArgs' support for list functions:  function applicator type-type ListFnApply a = (a->Bool) -> ListFnDescr a -> [Maybe a] -> Maybe [a]---- |'altArgs' support for list functions:  function applicator-listFnApp :: ListFnApply a-listFnApp p (f,z,g,n) (a0:args)-    | n == 0    =-        app $ foldr (apf f) (Just [z]) args-    | otherwise =-        app $ apf g a0 (foldr (apf f) (Just [z]) (args `deleteIndex` (n-1)))-    where-        apf :: (a->a->a) -> Maybe a -> Maybe [a] -> Maybe [a]-        apf fn (Just a1) (Just [a2]) = Just [fn a1 a2]-        apf _  _         _           = Just []-        -        -- app :: Maybe [a] -> Maybe [a]-        app Nothing      = Nothing-        app r@(Just [a]) = if p a then r else Nothing-        app _            = Just []--listFnApp _ _ [] = error "listFnApp called with an empty list" -- -Wall-------------------------------------------------------------  Datatype sub/supertype description------------------------------------------------------------- |Describe a subtype/supertype relationship between a pair of datatypes.------  Originally, I had this as a supertype field of the DatatypeVal structure,---  but that suffered from some problems:------  * supertypes may be introduced retrospectively,------  * the relationship expressed with respect to a single datatype---      cannot indicate how to do injections/restrictions between the---      underlying value types.------  [@ex@]      is the type of expression with which the datatype may be used.------  [@lb@]      is the type of the variable labels used.------  [@vn@]      is the type of value node used to contain a datatyped value------  [@supvt@]   is the internal value type of the super-datatype------  [@subvt@]   is the internal value type of the sub-datatype----data DatatypeSub ex lb vn supvt subvt = DatatypeSub-    { trelSup   :: DatatypeVal ex supvt lb vn-                                -- ^ Datatype that is a supertype of @trelSub@,-                                --   having value space @supvt@.-    , trelSub   :: DatatypeVal ex subvt lb vn-                                -- ^ Datatype that is a subtype of @trelSup@,-                                --   having value space @supvt@.-    , trelToSup :: subvt -> supvt-                                -- ^ Function that maps subtype value to-                                --   corresponding supertype value.-    , trelToSub :: supvt -> Maybe subvt-                                -- ^ Function that maps supertype value to-                                --   corresponding subtype value, if there-                                --   is such a value.-    }----------------------------------------------------------------------------------------  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke+--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  Datatype+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke+--  License     :  GPL V2+--+--  Maintainer  :  Douglas Burke+--  Stability   :  experimental+--  Portability :  H98+--+--  This module defines the structures used by Swish to represent and+--  manipulate RDF datatypes.+--+--------------------------------------------------------------------------------++module Swish.RDF.Datatype+    ( RDFDatatype+    , RDFDatatypeVal+    , RDFDatatypeMod+    , RDFModifierFn, RDFApplyModifier+    , makeRdfDtOpenVarBindingModify, makeRdfDtOpenVarBindingModifiers+    , applyRDFDatatypeMod+    , RDFDatatypeSub+    , fromRDFLabel, toRDFLabel, makeDatatypedLiteral+    )+where++import Swish.Datatype+    ( Datatype+    , DatatypeVal(..)+    , DatatypeMap(..)+    , DatatypeMod(..), ModifierFn+    , ApplyModifier+    , DatatypeSub(..)+    )+import Swish.Namespace (ScopedName)+import Swish.VarBinding (VarBindingModify(..))++import Swish.RDF.Graph+    ( RDFLabel(..)+    , isDatatyped+    , getLiteralText+    , RDFGraph+    )++import Swish.RDF.VarBinding (RDFVarBinding, RDFOpenVarBindingModify)++import Control.Monad (liftM)+import Data.Maybe (fromMaybe, isJust, fromJust)++import qualified Data.Text as T++------------------------------------------------------------+--  Specialize datatype framework types for use with RDF+------------------------------------------------------------++-- |RDF datatype wrapper used with RDF graph values+--+type RDFDatatype = Datatype RDFGraph RDFLabel RDFLabel++-- |RDF datatype value used with RDF graph values+--+type RDFDatatypeVal vt = DatatypeVal RDFGraph vt RDFLabel RDFLabel++-- |RDF datatype modifier used with RDF graph values+--+type RDFDatatypeMod vt = DatatypeMod vt RDFLabel RDFLabel++-- |Describe a subtype/supertype relationship between a pair+--  of RDF datatypes.+--+type RDFDatatypeSub supvt subvt = DatatypeSub RDFGraph RDFLabel RDFLabel supvt subvt++-- |RDF value modifier function type+--+--  This indicates a modifier function that operates on 'RDFLabel' values.+--+type RDFModifierFn = ModifierFn RDFLabel++-- |RDF value modifier application function type+--+--  This indicates a function that applies RDFModifierFn functions.+--+type RDFApplyModifier = ApplyModifier RDFLabel RDFLabel++--------------------------------------------------------------+--  Functions for creating datatype variable binding modifiers+--------------------------------------------------------------++-- |Create an 'RDFOpenVarBindingModify' value.+--+--  The key purpose of this function is to lift the supplied+--  variable constraint functions from operating on data values directly+--  to a corresponding list of functions that operate on values contained+--  in RDF graph labels (i.e. RDF literal nodes).  It also applies+--  node type checking, such that if the actual RDF nodes supplied do+--  not contain appropriate values then the variable binding is not+--  accepted.+--+makeRdfDtOpenVarBindingModify ::+    RDFDatatypeVal vt+    -- ^ is an 'RDFDatatype' value containing details of the datatype+    --   for which a variable binding modifier is created.+    -> RDFDatatypeMod vt +    -- ^ is the data value modifier value that defines the calculations+    --   that are used to implement a variable binding modifier.+    -> RDFOpenVarBindingModify+makeRdfDtOpenVarBindingModify dtval dtmod =+    dmAppf dtmod (dmName dtmod) $ map (makeRDFModifierFn dtval) (dmModf dtmod)++-- |Create all RDFOpenVarBindingModify values for a given datatype value.+--  See 'makeRdfDtOpenVarBindingModify'.+--+makeRdfDtOpenVarBindingModifiers ::+    RDFDatatypeVal vt +    -- ^  is an 'RDFDatatype' value containing details of the datatype+    --    for which variable binding modifiers are created.+    -> [RDFOpenVarBindingModify]+makeRdfDtOpenVarBindingModifiers dtval =+    map (makeRdfDtOpenVarBindingModify dtval) (tvalMod dtval)++-- |Apply a datatype modifier using supplied RDF labels to a supplied+--  RDF variable binding.+--+applyRDFDatatypeMod ::+    RDFDatatypeVal vt -> RDFDatatypeMod vt -> [RDFLabel] -> [RDFVarBinding]+    -> [RDFVarBinding]+applyRDFDatatypeMod dtval dtmod lbs =+    vbmApply (makeRdfDtOpenVarBindingModify dtval dtmod lbs)++-- |Given details of a datatype and a single value constraint function,+--  return a new constraint function that operates on 'RDFLabel' values.+--+--  The returned constraint function incorporates checks for appropriately+--  typed literal nodes, and returns similarly typed literal nodes.+--+makeRDFModifierFn ::+    RDFDatatypeVal vt -> ModifierFn vt -> RDFModifierFn+makeRDFModifierFn dtval fn ivs =+    let+        ivals = mapM (fromRDFLabel dtval) ivs+        ovals | isJust ivals = fn (fromJust ivals)+              | otherwise    = []+    in+        fromMaybe [] $ mapM (toRDFLabel dtval) ovals++------------------------------------------------------------+--  Helpers to map between datatype values and RDFLabels+------------------------------------------------------------++-- | Convert from a typed literal to a Haskell value,+-- with the possibility of failure.+fromRDFLabel ::+    RDFDatatypeVal vt -> RDFLabel -> Maybe vt+fromRDFLabel dtv lab+    | isDatatyped dtnam lab = mapL2V dtmap $ getLiteralText lab+    | otherwise             = Nothing+    where+        dtnam = tvalName dtv+        dtmap = tvalMap dtv++-- | Convert a Haskell value to a typed literal (label),+-- with the possibility of failure.+toRDFLabel :: RDFDatatypeVal vt -> vt -> Maybe RDFLabel+toRDFLabel dtv =+    liftM (makeDatatypedLiteral dtnam) . mapV2L dtmap+    where+        dtnam = tvalName dtv+        dtmap = tvalMap dtv++-- | Create a typed literal. No conversion is made to the+-- string representation.+makeDatatypedLiteral :: +    ScopedName   -- ^ data type+    -> T.Text    -- ^ string form of the value+    -> RDFLabel+makeDatatypedLiteral = flip TypedLit++--------------------------------------------------------------------------------+--+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke --  All rights reserved. -- --  This file is part of Swish.
+ src/Swish/RDF/Datatype/XSD/Decimal.hs view
@@ -0,0 +1,486 @@+{-# LANGUAGE OverloadedStrings #-}++--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  Decimal+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--                     2011 William Waites, 2011, 2012 Douglas Burke+--  License     :  GPL V2+--+--  Maintainer  :  Douglas Burke+--  Stability   :  experimental+--  Portability :  OverloadedStrings+--+--  This module defines the structures used to represent and+--  manipulate RDF @xsd:decimal@ datatyped literals.+--+--  Note that in versions @0.6.4@ and @0.6.5@, this module was a mixture+--  of support for @xsd:decimal@ and @xsd:double@. In @0.7.0@ the module+--  has been changed to @xsd:decimal@, but this may change.+--+--------------------------------------------------------------------------------++-- NOTE: William's code is half about xsd:decimal and half xsd:double.+-- I have changed it all to xsd:decimal since the rules do not handle some+-- of the xsd:double specific conditions (e.g. NaN/Inf values). However,+-- the values are mapped to Haskell Double values, which is not a good match+-- for xsd:decimal.++module Swish.RDF.Datatype.XSD.Decimal+    ( rdfDatatypeXsdDecimal+    , rdfDatatypeValXsdDecimal+    , typeNameXsdDecimal, namespaceXsdDecimal+    , axiomsXsdDecimal, rulesXsdDecimal+    )+where++import Swish.Datatype+    ( Datatype(..)+    , DatatypeVal(..)+    , DatatypeRel(..), DatatypeRelPr+    , altArgs+    , UnaryFnTable,    unaryFnApp+    , BinaryFnTable,   binaryFnApp+    , DatatypeMod(..) +    , makeVmod11inv, makeVmod11+    , makeVmod21inv, makeVmod21+    , makeVmod20+    )+import Swish.Namespace (Namespace, ScopedName)+import Swish.Namespace (namespaceToBuilder, makeNSScopedName)+import Swish.QName (LName)+import Swish.Ruleset (makeRuleset)++import Swish.RDF.Datatype (RDFDatatype, RDFDatatypeVal, RDFDatatypeMod)+import Swish.RDF.Datatype (makeRdfDtOpenVarBindingModifiers)+import Swish.RDF.Datatype.XSD.MapDecimal (mapXsdDecimal)++import Swish.RDF.Ruleset (RDFFormula, RDFRule, RDFRuleset)+import Swish.RDF.Ruleset (makeRDFGraphFromN3Builder, makeRDFFormula)++import Swish.RDF.ClassRestrictionRule (makeRDFDatatypeRestrictionRules)++import Swish.RDF.Vocabulary+    ( namespaceRDF+    , namespaceRDFS+    , namespaceRDFD+    , namespaceXSD+    , namespaceXsdType+    )++import Data.Monoid(Monoid(..))++import qualified Data.Text.Lazy.Builder as B++------------------------------------------------------------+--  Misc values+------------------------------------------------------------++nameXsdDecimal :: LName+nameXsdDecimal = "decimal"++-- |Type name for @xsd:decimal@ datatype.+typeNameXsdDecimal :: ScopedName+typeNameXsdDecimal  = makeNSScopedName namespaceXSD nameXsdDecimal++-- | Namespace for @xsd:decimal@ datatype functions.+namespaceXsdDecimal :: Namespace+namespaceXsdDecimal = namespaceXsdType nameXsdDecimal++-- | The RDFDatatype value for @xsd:decimal@.+rdfDatatypeXsdDecimal :: RDFDatatype+rdfDatatypeXsdDecimal = Datatype rdfDatatypeValXsdDecimal++-- |Define Datatype value for @xsd:decimal@.+--+--  Members of this datatype decimal values.+--+--  The lexical form consists of an optional @+@ or @-@+--  followed by a sequence of decimal digits, an optional+--  decimal point and a sequence of decimal digits.+--+--  The canonical lexical form has leading zeros and @+@ sign removed.+--+rdfDatatypeValXsdDecimal :: RDFDatatypeVal Double+rdfDatatypeValXsdDecimal = DatatypeVal+    { tvalName      = typeNameXsdDecimal+    , tvalRules     = rdfRulesetXsdDecimal  -- Ruleset RDFGraph+    , tvalMkRules   = makeRDFDatatypeRestrictionRules rdfDatatypeValXsdDecimal+                                            -- RDFGraph -> [RDFRules]+    , tvalMkMods    = makeRdfDtOpenVarBindingModifiers rdfDatatypeValXsdDecimal+    , tvalMap       = mapXsdDecimal         -- DatatypeMap Double+    , tvalRel       = relXsdDecimal         -- [DatatypeRel Double]+    , tvalMod       = modXsdDecimal         -- [DatatypeMod Double]+    }++-- |relXsdDecimal contains arithmetic and other relations for xsd:decimal values.+--+--  The functions are inspired by those defined by CWM as math: properties+--  (<http://www.w3.org/2000/10/swap/doc/CwmBuiltins.html>).+--+relXsdDecimal :: [DatatypeRel Double]+relXsdDecimal =+    [ relXsdDecimalAbs+    , relXsdDecimalNeg+    , relXsdDecimalSum+    , relXsdDecimalDiff+    , relXsdDecimalProd+    , relXsdDecimalPower+    , relXsdDecimalEq+    , relXsdDecimalNe+    , relXsdDecimalLt+    , relXsdDecimalLe+    , relXsdDecimalGt+    , relXsdDecimalGe+    ]++mkDecRel2 ::+    LName -> DatatypeRelPr Double -> UnaryFnTable Double+    -> DatatypeRel Double+mkDecRel2 nam pr fns = DatatypeRel+    { dtRelName = makeNSScopedName namespaceXsdDecimal nam+    , dtRelFunc = altArgs pr fns unaryFnApp+    }++mkDecRel3 ::+    LName -> DatatypeRelPr Double -> BinaryFnTable Double+    -> DatatypeRel Double+mkDecRel3 nam pr fns = DatatypeRel+    { dtRelName = makeNSScopedName namespaceXsdDecimal nam+    , dtRelFunc = altArgs pr fns binaryFnApp+    }++relXsdDecimalAbs :: DatatypeRel Double+relXsdDecimalAbs = mkDecRel2 "abs" (const True)+    [ ( (>=0),      [ (abs,1) ] )+    , ( const True, [ (id,0), (negate,0) ] )+    ]++relXsdDecimalNeg :: DatatypeRel Double+relXsdDecimalNeg = mkDecRel2 "neg" (const True)+    [ ( const True, [ (negate,1) ] )+    , ( const True, [ (negate,0) ] )+    ]++relXsdDecimalSum :: DatatypeRel Double+relXsdDecimalSum = mkDecRel3 "sum" (const True)+    [ ( const True, [ ((+),1,2) ] )+    , ( const True, [ ((-),0,2) ] )+    , ( const True, [ ((-),0,1) ] )+    ]++relXsdDecimalDiff :: DatatypeRel Double+relXsdDecimalDiff = mkDecRel3 "diff" (const True)+    [ ( const True, [ ((-),1,2) ] )+    , ( const True, [ ((+),0,2) ] )+    , ( const True, [ ((-),1,0) ] )+    ]++relXsdDecimalProd :: DatatypeRel Double+relXsdDecimalProd = mkDecRel3 "prod" (const True)+    [ ( const True, [ ((*),1,2) ] )+    , ( const True, [ ((/),0,2) ] )+    , ( const True, [ ((/),0,1) ] )+    ]++relXsdDecimalPower :: DatatypeRel Double+relXsdDecimalPower = mkDecRel3 "power" (const True)+    [ ( const True, [ ((**),1,2) ] )+    , ( const True, [ ] )+    , ( (>=0),      [ ] )+    ]++liftL2 :: (a->a->Bool) -> ([a]->a) -> ([a]->a) -> [a] -> Bool+liftL2 p i1 i2 as = p (i1 as) (i2 as)++lcomp :: (a->a->Bool) -> [a] -> Bool+lcomp p = liftL2 p head (head . tail)++-- eq++relXsdDecimalEq :: DatatypeRel Double+relXsdDecimalEq = mkDecRel2 "eq" (lcomp (==))+    ( repeat (const True, []) )++-- ne++relXsdDecimalNe :: DatatypeRel Double+relXsdDecimalNe = mkDecRel2 "ne" (lcomp (/=))+    ( repeat (const True, []) )++-- lt++relXsdDecimalLt :: DatatypeRel Double+relXsdDecimalLt = mkDecRel2 "lt" (lcomp (<))+    ( repeat (const True, []) )++-- le++relXsdDecimalLe :: DatatypeRel Double+relXsdDecimalLe = mkDecRel2 "le" (lcomp (<=))+    ( repeat (const True, []) )++-- gt++relXsdDecimalGt :: DatatypeRel Double+relXsdDecimalGt = mkDecRel2 "gt" (lcomp (>))+    ( repeat (const True, []) )++-- ge++relXsdDecimalGe :: DatatypeRel Double+relXsdDecimalGe = mkDecRel2 "ge" (lcomp (>=))+    ( repeat (const True, []) )++-- |modXsdDecimal contains variable binding modifiers for xsd:decimal values.+--+--  The functions are selected from those defined by CWM as math:+--  properties+--  (<http://www.w3.org/2000/10/swap/doc/CwmBuiltins.html>).+--+modXsdDecimal :: [RDFDatatypeMod Double]+modXsdDecimal =+    [ modXsdDecimalAbs+    , modXsdDecimalNeg+    , modXsdDecimalSum+    , modXsdDecimalDiff+    , modXsdDecimalProd+    , modXsdDecimalPower+    , modXsdDecimalEq+    , modXsdDecimalNe+    , modXsdDecimalLt+    , modXsdDecimalLe+    , modXsdDecimalGt+    , modXsdDecimalGe+    ]++modXsdDecimalAbs :: RDFDatatypeMod Double+modXsdDecimalAbs = DatatypeMod+    { dmName = makeNSScopedName namespaceXsdDecimal "abs"+    , dmModf = [ f0, f1 ]+    , dmAppf = makeVmod11+    }+    where+        f0 vs@[v1,v2] = if v1 == abs v2 then vs else []+        f0 _          = []+        f1 [v2]       = [abs v2]+        f1 _          = []++modXsdDecimalNeg :: RDFDatatypeMod Double+modXsdDecimalNeg = DatatypeMod+    { dmName = makeNSScopedName namespaceXsdDecimal "neg"+    , dmModf = [ f0, f1, f1 ]+    , dmAppf = makeVmod11inv+    }+    where+        f0 vs@[v1,v2] = if v1 == negate v2 then vs else []+        f0 _          = []+        f1 [vi]       = [-vi]+        f1 _          = []++modXsdDecimalSum :: RDFDatatypeMod Double+modXsdDecimalSum = DatatypeMod+    { dmName = makeNSScopedName namespaceXsdDecimal "sum"+    , dmModf = [ f0, f1, f2, f2 ]+    , dmAppf = makeVmod21inv+    }+    where+        f0 vs@[v1,v2,v3] = if v1 == v2+v3 then vs else []+        f0 _             = []+        f1 [v2,v3]       = [v2+v3]+        f1 _             = []+        f2 [v1,vi]       = [v1-vi]+        f2 _             = []++modXsdDecimalDiff :: RDFDatatypeMod Double+modXsdDecimalDiff = DatatypeMod+    { dmName = makeNSScopedName namespaceXsdDecimal "diff"+    , dmModf = [ f0, f1, f2, f3 ]+    , dmAppf = makeVmod21inv+    }+    where+        f0 vs@[v1,v2,v3] = if v1 == v2-v3 then vs else []+        f0 _             = []+        f1 [v2,v3]       = [v2-v3]+        f1 _             = []+        f2 [v1,v3]       = [v1+v3]+        f2 _             = []+        f3 [v1,v2]       = [v2-v1]+        f3 _             = []++modXsdDecimalProd :: RDFDatatypeMod Double+modXsdDecimalProd = DatatypeMod+    { dmName = makeNSScopedName namespaceXsdDecimal "prod"+    , dmModf = [ f0, f1, f2, f2 ]+    , dmAppf = makeVmod21inv+    }+    where+        f0 vs@[v1,v2,v3] = if v1 == v2*v3 then vs else []+        f0 _             = []+        f1 [v2,v3]       = [v2*v3]+        f1 _             = []+        f2 [v1,vi]       = [v1/vi]+        f2 _             = []++modXsdDecimalPower :: RDFDatatypeMod Double+modXsdDecimalPower = DatatypeMod+    { dmName = makeNSScopedName namespaceXsdDecimal "power"+    , dmModf = [ f0, f1 ]+    , dmAppf = makeVmod21+    }+    where+        f0 vs@[v1,v2,v3] = if v1 == (v2**v3 :: Double) then vs else []+        f0 _             = []+        f1 [v2,v3]       = [v2**v3 :: Double]+        f1 _             = []++modXsdDecimalEq, modXsdDecimalNe, modXsdDecimalLt, modXsdDecimalLe, modXsdDecimalGt, modXsdDecimalGe :: RDFDatatypeMod Double +modXsdDecimalEq = modXsdDecimalCompare "eq" (==)+modXsdDecimalNe = modXsdDecimalCompare "ne" (/=)+modXsdDecimalLt = modXsdDecimalCompare "lt" (<)+modXsdDecimalLe = modXsdDecimalCompare "le" (<=)+modXsdDecimalGt = modXsdDecimalCompare "gt" (>)+modXsdDecimalGe = modXsdDecimalCompare "ge" (>=)++modXsdDecimalCompare ::+    LName -> (Double->Double->Bool) -> RDFDatatypeMod Double+modXsdDecimalCompare nam rel = DatatypeMod+    { dmName = makeNSScopedName namespaceXsdDecimal nam+    , dmModf = [ f0 ]+    , dmAppf = makeVmod20+    }+    where+        f0 vs@[v1,v2] = if rel v1 v2 then vs else []+        f0 _          = []++-- |rulesetXsdDecimal contains rules and axioms that allow additional+--  deductions when xsd:decimal values appear in a graph.+--+--  The rules defined here are concerned with basic decimal arithmetic+--  operations: +, -, *, /, **+--+--  makeRuleset :: Namespace -> [Formula ex] -> [Rule ex] -> Ruleset ex+--+rdfRulesetXsdDecimal :: RDFRuleset+rdfRulesetXsdDecimal =+    makeRuleset namespaceXsdDecimal axiomsXsdDecimal rulesXsdDecimal++prefixXsdDecimal :: B.Builder+prefixXsdDecimal = +  mconcat $ map namespaceToBuilder+              [ namespaceRDF+              , namespaceRDFS+              , namespaceRDFD+              , namespaceXSD+              , namespaceXsdDecimal+              ]++mkAxiom :: LName -> B.Builder -> RDFFormula+mkAxiom local gr =+    makeRDFFormula namespaceXsdDecimal local (prefixXsdDecimal `mappend` gr)++-- | The axioms for @xsd:decimal@, which are+--+-- > xsd:decimal a rdfs:Datatype .+--+axiomsXsdDecimal :: [RDFFormula]+axiomsXsdDecimal =+    [ mkAxiom "dt"+                  "xsd:decimal rdf:type rdfs:Datatype ."+                  -- "xsd:double rdf:type rdfs:Datatype ."+    ]++-- | The rules for @xsd:decimal@.+--+rulesXsdDecimal :: [RDFRule]+rulesXsdDecimal = makeRDFDatatypeRestrictionRules rdfDatatypeValXsdDecimal gr+    where+        gr = makeRDFGraphFromN3Builder rulesXsdDecimalBuilder++--- I have removed the newline which was added between each line+--- to improve the clarity of parser errors.+---+rulesXsdDecimalBuilder :: B.Builder+rulesXsdDecimalBuilder = +  mconcat+  [ prefixXsdDecimal+    , "xsd_decimal:Abs a rdfd:GeneralRestriction ; "+    , "  rdfd:onProperties (rdf:_1 rdf:_2) ; "+    , "  rdfd:constraint xsd_decimal:abs ; "+    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "+    , "xsd_decimal:Neg a rdfd:GeneralRestriction ; "+    , "  rdfd:onProperties (rdf:_1 rdf:_2) ; "+    , "  rdfd:constraint xsd_decimal:neg ; "+    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "+    , "xsd_decimal:Sum a rdfd:GeneralRestriction ; "+    , "  rdfd:onProperties (rdf:_1 rdf:_2 rdf:_3) ; "+    , "  rdfd:constraint xsd_decimal:sum ; "+    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "+    , "xsd_decimal:Diff a rdfd:GeneralRestriction ; "+    , "  rdfd:onProperties (rdf:_1 rdf:_2 rdf:_3) ; "+    , "  rdfd:constraint xsd_decimal:diff ; "+    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "+    , "xsd_decimal:Prod a rdfd:GeneralRestriction ; "+    , "  rdfd:onProperties (rdf:_1 rdf:_2 rdf:_3) ; "+    , "  rdfd:constraint xsd_decimal:prod ; "+    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "+    , "xsd_decimal:DivMod a rdfd:GeneralRestriction ; "+    , "  rdfd:onProperties (rdf:_1 rdf:_2 rdf:_3 rdf:_4) ; "+    , "  rdfd:constraint xsd_decimal:divmod ; "+    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "+    , "xsd_decimal:Power a rdfd:GeneralRestriction ; "+    , "  rdfd:onProperties (rdf:_1 rdf:_2 rdf:_3) ; "+    , "  rdfd:constraint xsd_decimal:power ; "+    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "+    , "xsd_decimal:Eq a rdfd:GeneralRestriction ; "+    , "  rdfd:onProperties (rdf:_1 rdf:_2) ; "+    , "  rdfd:constraint xsd_decimal:eq ; "+    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "+    , "xsd_decimal:Ne a rdfd:GeneralRestriction ; "+    , "  rdfd:onProperties (rdf:_1 rdf:_2) ; "+    , "  rdfd:constraint xsd_decimal:ne ; "+    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "+    , "xsd_decimal:Lt a rdfd:GeneralRestriction ; "+    , "  rdfd:onProperties (rdf:_1 rdf:_2) ; "+    , "  rdfd:constraint xsd_decimal:lt ; "+    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "+    , "xsd_decimal:Le a rdfd:GeneralRestriction ; "+    , "  rdfd:onProperties (rdf:_1 rdf:_2) ; "+    , "  rdfd:constraint xsd_decimal:le ; "+    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "+    , "xsd_decimal:Gt a rdfd:GeneralRestriction ; "+    , "  rdfd:onProperties (rdf:_1 rdf:_2) ; "+    , "  rdfd:constraint xsd_decimal:gt ; "+    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "+    , "xsd_decimal:Ge a rdfd:GeneralRestriction ; "+    , "  rdfd:onProperties (rdf:_1 rdf:_2) ; "+    , "  rdfd:constraint xsd_decimal:ge ; "+    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "+    ]+  +--------------------------------------------------------------------------------+--+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--                2011 William Waites, 2011, 2012 Douglas Burke, +--  All rights reserved.+--+--  This file is part of Swish.+--+--  Swish is free software; you can redistribute it and/or modify+--  it under the terms of the GNU General Public License as published by+--  the Free Software Foundation; either version 2 of the License, or+--  (at your option) any later version.+--+--  Swish is distributed in the hope that it will be useful,+--  but WITHOUT ANY WARRANTY; without even the implied warranty of+--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+--  GNU General Public License for more details.+--+--  You should have received a copy of the GNU General Public License+--  along with Swish; if not, write to:+--    The Free Software Foundation, Inc.,+--    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+--+--------------------------------------------------------------------------------
+ src/Swish/RDF/Datatype/XSD/Integer.hs view
@@ -0,0 +1,530 @@+{-# LANGUAGE OverloadedStrings #-}++--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  Integer+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke+--  License     :  GPL V2+--+--  Maintainer  :  Douglas Burke+--  Stability   :  experimental+--  Portability :  OverloadedStrings+--+--  This module defines the structures used to represent and+--  manipulate RDF @xsd:integer@ datatyped literals.+--+--------------------------------------------------------------------------------++module Swish.RDF.Datatype.XSD.Integer+    ( rdfDatatypeXsdInteger+    , rdfDatatypeValXsdInteger+    , typeNameXsdInteger, namespaceXsdInteger+    , axiomsXsdInteger, rulesXsdInteger+    )+where++import Swish.Datatype+    ( Datatype(..)+    , DatatypeVal(..)+    , DatatypeRel(..), DatatypeRelPr+    , altArgs+    , UnaryFnTable,    unaryFnApp+    , BinaryFnTable,   binaryFnApp+    , BinMaybeFnTable, binMaybeFnApp+    , DatatypeMod(..) +    , makeVmod11inv, makeVmod11+    , makeVmod21inv, makeVmod21+    , makeVmod20+    , makeVmod22+    )+import Swish.Namespace (Namespace, ScopedName)+import Swish.Namespace (namespaceToBuilder, makeNSScopedName)+import Swish.QName (LName)+import Swish.Ruleset (makeRuleset)++import Swish.RDF.Datatype (RDFDatatype, RDFDatatypeVal, RDFDatatypeMod)+import Swish.RDF.Datatype (makeRdfDtOpenVarBindingModifiers)+import Swish.RDF.Datatype.XSD.MapInteger (mapXsdInteger)++import Swish.RDF.Ruleset (RDFFormula, RDFRule, RDFRuleset)+import Swish.RDF.Ruleset (makeRDFGraphFromN3Builder, makeRDFFormula)++import Swish.RDF.ClassRestrictionRule (makeRDFDatatypeRestrictionRules)++import Swish.RDF.Vocabulary+    ( namespaceRDF+    , namespaceRDFS+    , namespaceRDFD+    , namespaceXSD+    , namespaceXsdType+    )++import Data.Monoid(Monoid(..))+import Control.Monad (liftM)+import Data.Maybe (maybeToList)++import qualified Data.Text.Lazy.Builder as B++------------------------------------------------------------+--  Misc values+------------------------------------------------------------++--  Local name for Integer datatype+nameXsdInteger :: LName+nameXsdInteger = "integer"++-- |Type name for @xsd:integer@ datatype.+typeNameXsdInteger :: ScopedName+typeNameXsdInteger  = makeNSScopedName namespaceXSD nameXsdInteger++-- |Namespace for @xsd:integer@ datatype functions.+namespaceXsdInteger :: Namespace+namespaceXsdInteger = namespaceXsdType nameXsdInteger++-- | The RDFDatatype value for @xsd:integer@.+rdfDatatypeXsdInteger :: RDFDatatype+rdfDatatypeXsdInteger = Datatype rdfDatatypeValXsdInteger++--  Integer power (exponentiation) function+--  returns Nothing if exponent is negative.+--+intPower :: Integer -> Integer -> Maybe Integer+intPower a b = if b < 0 then Nothing else Just (intPower1 a b)+    where+        intPower1 x y+            | q == 1           = atopsq*x+            | p == 0           = 1+            | otherwise        = atopsq+            where+                (p,q)  = y `divMod` 2+                atop   = intPower1 x p+                atopsq = atop*atop++------------------------------------------------------------+--  Implmentation of RDFDatatypeVal for xsd:integer+------------------------------------------------------------++-- |Define Datatype value for @xsd:integer@.+--+--  Members of this datatype are positive or negative integer values.+--+--  The lexical form consists of an optional @+@ or @-@+--  followed by a sequence of decimal digits.+--+--  The canonical lexical form has leading zeros and @+@ sign removed.+--+rdfDatatypeValXsdInteger :: RDFDatatypeVal Integer+rdfDatatypeValXsdInteger = DatatypeVal+    { tvalName      = typeNameXsdInteger+    , tvalRules     = rdfRulesetXsdInteger  -- Ruleset RDFGraph+    , tvalMkRules   = makeRDFDatatypeRestrictionRules rdfDatatypeValXsdInteger+                                            -- RDFGraph -> [RDFRules]+    , tvalMkMods    = makeRdfDtOpenVarBindingModifiers rdfDatatypeValXsdInteger+    , tvalMap       = mapXsdInteger         -- DatatypeMap Integer+    , tvalRel       = relXsdInteger         -- [DatatypeRel Integer]+    , tvalMod       = modXsdInteger         -- [DatatypeMod Integer]+    }++-- |relXsdInteger contains arithmetic and other relations for xsd:Integer values.+--+--  The functions are inspired by those defined by CWM as math: properties+--  (<http://www.w3.org/2000/10/swap/doc/CwmBuiltins.html>).+--+relXsdInteger :: [DatatypeRel Integer]+relXsdInteger =+    [ relXsdIntegerAbs+    , relXsdIntegerNeg+    , relXsdIntegerSum+    , relXsdIntegerDiff+    , relXsdIntegerProd+    , relXsdIntegerDivMod+    , relXsdIntegerPower+    , relXsdIntegerEq+    , relXsdIntegerNe+    , relXsdIntegerLt+    , relXsdIntegerLe+    , relXsdIntegerGt+    , relXsdIntegerGe+    ]++mkIntRel2 ::+    LName -> DatatypeRelPr Integer -> UnaryFnTable Integer+    -> DatatypeRel Integer+mkIntRel2 nam pr fns = DatatypeRel+    { dtRelName = makeNSScopedName namespaceXsdInteger nam+    , dtRelFunc = altArgs pr fns unaryFnApp+    }++mkIntRel3 ::+    LName -> DatatypeRelPr Integer -> BinaryFnTable Integer+    -> DatatypeRel Integer+mkIntRel3 nam pr fns = DatatypeRel+    { dtRelName = makeNSScopedName namespaceXsdInteger nam+    , dtRelFunc = altArgs pr fns binaryFnApp+    }++mkIntRel3maybe ::+    LName -> DatatypeRelPr Integer -> BinMaybeFnTable Integer+    -> DatatypeRel Integer+mkIntRel3maybe nam pr fns = DatatypeRel+    { dtRelName = makeNSScopedName namespaceXsdInteger nam+    , dtRelFunc = altArgs pr fns binMaybeFnApp+    }++relXsdIntegerAbs :: DatatypeRel Integer+relXsdIntegerAbs = mkIntRel2 "abs" (const True)+    [ ( (>=0),      [ (abs,1) ] )+    , ( const True, [ (id,0), (negate,0) ] )+    ]++relXsdIntegerNeg :: DatatypeRel Integer+relXsdIntegerNeg = mkIntRel2 "neg" (const True)+    [ ( const True, [ (negate,1) ] )+    , ( const True, [ (negate,0) ] )+    ]++relXsdIntegerSum :: DatatypeRel Integer+relXsdIntegerSum = mkIntRel3 "sum" (const True)+    [ ( const True, [ ((+),1,2) ] )+    , ( const True, [ ((-),0,2) ] )+    , ( const True, [ ((-),0,1) ] )+    ]++relXsdIntegerDiff :: DatatypeRel Integer+relXsdIntegerDiff = mkIntRel3 "diff" (const True)+    [ ( const True, [ ((-),1,2) ] )+    , ( const True, [ ((+),0,2) ] )+    , ( const True, [ ((-),1,0) ] )+    ]++relXsdIntegerProd :: DatatypeRel Integer+relXsdIntegerProd = mkIntRel3 "prod" (const True)+    [ ( const True, [ ((*),1,2) ] )+    , ( const True, [ (div,0,2) ] )+    , ( const True, [ (div,0,1) ] )+    ]++relXsdIntegerDivMod :: DatatypeRel Integer+relXsdIntegerDivMod = mkIntRel3 "divmod" (const True)+    [ ( const True, [ (div,2,3) ] )+    , ( const True, [ (mod,2,3) ] )+    , ( const True, [ ] )+    , ( const True, [ ] )+    ]++--  Compose with function of two arguments+c2 :: (b -> c) -> (a -> d -> b) -> a -> d -> c+c2 = (.) . (.)++relXsdIntegerPower :: DatatypeRel Integer+relXsdIntegerPower = mkIntRel3maybe "power" (const True)+    [ ( const True, [ (liftM (:[]) `c2` intPower,1,2) ] )+    , ( const True, [ ] )+    , ( (>=0),      [ ] )+    ]++liftL2 :: (a->a->Bool) -> ([a]->a) -> ([a]->a) -> [a] -> Bool+liftL2 p i1 i2 as = p (i1 as) (i2 as)++lcomp :: (a->a->Bool) -> [a] -> Bool+lcomp p = liftL2 p head (head . tail)++-- eq++relXsdIntegerEq :: DatatypeRel Integer+relXsdIntegerEq = mkIntRel2 "eq" (lcomp (==))+    ( repeat (const True, []) )++-- ne++relXsdIntegerNe :: DatatypeRel Integer+relXsdIntegerNe = mkIntRel2 "ne" (lcomp (/=))+    ( repeat (const True, []) )++-- lt++relXsdIntegerLt :: DatatypeRel Integer+relXsdIntegerLt = mkIntRel2 "lt" (lcomp (<))+    ( repeat (const True, []) )++-- le++relXsdIntegerLe :: DatatypeRel Integer+relXsdIntegerLe = mkIntRel2 "le" (lcomp (<=))+    ( repeat (const True, []) )++-- gt++relXsdIntegerGt :: DatatypeRel Integer+relXsdIntegerGt = mkIntRel2 "gt" (lcomp (>))+    ( repeat (const True, []) )++-- ge++relXsdIntegerGe :: DatatypeRel Integer+relXsdIntegerGe = mkIntRel2 "ge" (lcomp (>=))+    ( repeat (const True, []) )++-- |modXsdInteger contains variable binding modifiers for xsd:Integer values.+--+--  The functions are selected from those defined by CWM as math:+--  properties+--  (<http://www.w3.org/2000/10/swap/doc/CwmBuiltins.html>).+--+modXsdInteger :: [RDFDatatypeMod Integer]+modXsdInteger =+    [ modXsdIntegerAbs+    , modXsdIntegerNeg+    , modXsdIntegerSum+    , modXsdIntegerDiff+    , modXsdIntegerProd+    , modXsdIntegerDivMod+    , modXsdIntegerPower+    , modXsdIntegerEq+    , modXsdIntegerNe+    , modXsdIntegerLt+    , modXsdIntegerLe+    , modXsdIntegerGt+    , modXsdIntegerGe+    ]++modXsdIntegerAbs :: RDFDatatypeMod Integer+modXsdIntegerAbs = DatatypeMod+    { dmName = makeNSScopedName namespaceXsdInteger "abs"+    , dmModf = [ f0, f1 ]+    , dmAppf = makeVmod11+    }+    where+        f0 vs@[v1,v2] = if v1 == abs v2 then vs else []+        f0 _          = []+        f1 [v2]       = [abs v2]+        f1 _          = []++modXsdIntegerNeg :: RDFDatatypeMod Integer+modXsdIntegerNeg = DatatypeMod+    { dmName = makeNSScopedName namespaceXsdInteger "neg"+    , dmModf = [ f0, f1, f1 ]+    , dmAppf = makeVmod11inv+    }+    where+        f0 vs@[v1,v2] = if v1 == negate v2 then vs else []+        f0 _          = []+        f1 [vi]       = [-vi]+        f1 _          = []++modXsdIntegerSum :: RDFDatatypeMod Integer+modXsdIntegerSum = DatatypeMod+    { dmName = makeNSScopedName namespaceXsdInteger "sum"+    , dmModf = [ f0, f1, f2, f2 ]+    , dmAppf = makeVmod21inv+    }+    where+        f0 vs@[v1,v2,v3] = if v1 == v2+v3 then vs else []+        f0 _             = []+        f1 [v2,v3]       = [v2+v3]+        f1 _             = []+        f2 [v1,vi]       = [v1-vi]+        f2 _             = []++modXsdIntegerDiff :: RDFDatatypeMod Integer+modXsdIntegerDiff = DatatypeMod+    { dmName = makeNSScopedName namespaceXsdInteger "diff"+    , dmModf = [ f0, f1, f2, f3 ]+    , dmAppf = makeVmod21inv+    }+    where+        f0 vs@[v1,v2,v3] = if v1 == v2-v3 then vs else []+        f0 _             = []+        f1 [v2,v3]       = [v2-v3]+        f1 _             = []+        f2 [v1,v3]       = [v1+v3]+        f2 _             = []+        f3 [v1,v2]       = [v2-v1]+        f3 _             = []++modXsdIntegerProd :: RDFDatatypeMod Integer+modXsdIntegerProd = DatatypeMod+    { dmName = makeNSScopedName namespaceXsdInteger "prod"+    , dmModf = [ f0, f1, f2, f2 ]+    , dmAppf = makeVmod21inv+    }+    where+        f0 vs@[v1,v2,v3] = if v1 == v2*v3 then vs else []+        f0 _             = []+        f1 [v2,v3]       = [v2*v3]+        f1 _             = []+        f2 [v1,vi]       = if r == 0 then [q] else []+            where (q,r)  = quotRem v1 vi+        f2 _             = []++modXsdIntegerDivMod :: RDFDatatypeMod Integer+modXsdIntegerDivMod = DatatypeMod+    { dmName = makeNSScopedName namespaceXsdInteger "divmod"+    , dmModf = [ f0, f1 ]+    , dmAppf = makeVmod22+    }+    where+        f0 vs@[v1,v2,v3,v4] = if (v1,v2) == divMod v3 v4 then vs else []+        f0 _                = []+        f1 [v3,v4]          = [v1,v2] where (v1,v2) = divMod v3 v4+        f1 _                = []++modXsdIntegerPower :: RDFDatatypeMod Integer+modXsdIntegerPower = DatatypeMod+    { dmName = makeNSScopedName namespaceXsdInteger "power"+    , dmModf = [ f0, f1 ]+    , dmAppf = makeVmod21+    }+    where+        f0 vs@[v1,v2,v3] = if Just v1 == intPower v2 v3 then vs else []+        f0 _             = []+        f1 [v2,v3]       = maybeToList (intPower v2 v3)+        f1 _             = []++modXsdIntegerEq, modXsdIntegerNe, modXsdIntegerLt, modXsdIntegerLe, modXsdIntegerGt, modXsdIntegerGe :: RDFDatatypeMod Integer +modXsdIntegerEq = modXsdIntegerCompare "eq" (==)+modXsdIntegerNe = modXsdIntegerCompare "ne" (/=)+modXsdIntegerLt = modXsdIntegerCompare "lt" (<)+modXsdIntegerLe = modXsdIntegerCompare "le" (<=)+modXsdIntegerGt = modXsdIntegerCompare "gt" (>)+modXsdIntegerGe = modXsdIntegerCompare "ge" (>=)++modXsdIntegerCompare ::+    LName -> (Integer->Integer->Bool) -> RDFDatatypeMod Integer+modXsdIntegerCompare nam rel = DatatypeMod+    { dmName = makeNSScopedName namespaceXsdInteger nam+    , dmModf = [ f0 ]+    , dmAppf = makeVmod20+    }+    where+        f0 vs@[v1,v2] = if rel v1 v2 then vs else []+        f0 _          = []++-- |rulesetXsdInteger contains rules and axioms that allow additional+--  deductions when xsd:integer values appear in a graph.+--+--  The rules defined here are concerned with basic integer arithmetic+--  operations: +, -, *, div, rem+--+--  makeRuleset :: Namespace -> [Formula ex] -> [Rule ex] -> Ruleset ex+--+rdfRulesetXsdInteger :: RDFRuleset+rdfRulesetXsdInteger =+    makeRuleset namespaceXsdInteger axiomsXsdInteger rulesXsdInteger++prefixXsdInteger :: B.Builder+prefixXsdInteger = +  mconcat $ map namespaceToBuilder+              [ namespaceRDF+              , namespaceRDFS+              , namespaceRDFD+              , namespaceXSD+              , namespaceXsdInteger+              ]++mkAxiom :: LName -> B.Builder -> RDFFormula+mkAxiom local gr =+    makeRDFFormula namespaceXsdInteger local (prefixXsdInteger `mappend` gr)++-- | The axioms for @xsd:integer@, which are+--+-- > xsd:integer a rdfs:Datatype .+--+axiomsXsdInteger :: [RDFFormula]+axiomsXsdInteger =+    [ mkAxiom "dt"      "xsd:integer rdf:type rdfs:Datatype ."+    ]++-- | The rules for @xsd:integer@.+rulesXsdInteger :: [RDFRule]+rulesXsdInteger = makeRDFDatatypeRestrictionRules rdfDatatypeValXsdInteger gr+    where+        gr = makeRDFGraphFromN3Builder rulesXsdIntegerBuilder++--- I have removed the newline which was added between each line+--- to improve the clarity of parser errors.+---+rulesXsdIntegerBuilder :: B.Builder+rulesXsdIntegerBuilder = +  mconcat+  [ prefixXsdInteger+    , "xsd_integer:Abs a rdfd:GeneralRestriction ; "+    , "  rdfd:onProperties (rdf:_1 rdf:_2) ; "+    , "  rdfd:constraint xsd_integer:abs ; "+    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "+    , "xsd_integer:Neg a rdfd:GeneralRestriction ; "+    , "  rdfd:onProperties (rdf:_1 rdf:_2) ; "+    , "  rdfd:constraint xsd_integer:neg ; "+    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "+    , "xsd_integer:Sum a rdfd:GeneralRestriction ; "+    , "  rdfd:onProperties (rdf:_1 rdf:_2 rdf:_3) ; "+    , "  rdfd:constraint xsd_integer:sum ; "+    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "+    , "xsd_integer:Diff a rdfd:GeneralRestriction ; "+    , "  rdfd:onProperties (rdf:_1 rdf:_2 rdf:_3) ; "+    , "  rdfd:constraint xsd_integer:diff ; "+    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "+    , "xsd_integer:Prod a rdfd:GeneralRestriction ; "+    , "  rdfd:onProperties (rdf:_1 rdf:_2 rdf:_3) ; "+    , "  rdfd:constraint xsd_integer:prod ; "+    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "+    , "xsd_integer:DivMod a rdfd:GeneralRestriction ; "+    , "  rdfd:onProperties (rdf:_1 rdf:_2 rdf:_3 rdf:_4) ; "+    , "  rdfd:constraint xsd_integer:divmod ; "+    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "+    , "xsd_integer:Power a rdfd:GeneralRestriction ; "+    , "  rdfd:onProperties (rdf:_1 rdf:_2 rdf:_3) ; "+    , "  rdfd:constraint xsd_integer:power ; "+    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "+    , "xsd_integer:Eq a rdfd:GeneralRestriction ; "+    , "  rdfd:onProperties (rdf:_1 rdf:_2) ; "+    , "  rdfd:constraint xsd_integer:eq ; "+    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "+    , "xsd_integer:Ne a rdfd:GeneralRestriction ; "+    , "  rdfd:onProperties (rdf:_1 rdf:_2) ; "+    , "  rdfd:constraint xsd_integer:ne ; "+    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "+    , "xsd_integer:Lt a rdfd:GeneralRestriction ; "+    , "  rdfd:onProperties (rdf:_1 rdf:_2) ; "+    , "  rdfd:constraint xsd_integer:lt ; "+    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "+    , "xsd_integer:Le a rdfd:GeneralRestriction ; "+    , "  rdfd:onProperties (rdf:_1 rdf:_2) ; "+    , "  rdfd:constraint xsd_integer:le ; "+    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "+    , "xsd_integer:Gt a rdfd:GeneralRestriction ; "+    , "  rdfd:onProperties (rdf:_1 rdf:_2) ; "+    , "  rdfd:constraint xsd_integer:gt ; "+    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "+    , "xsd_integer:Ge a rdfd:GeneralRestriction ; "+    , "  rdfd:onProperties (rdf:_1 rdf:_2) ; "+    , "  rdfd:constraint xsd_integer:ge ; "+    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "+    ]+  +--------------------------------------------------------------------------------+--+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke+--  All rights reserved.+--+--  This file is part of Swish.+--+--  Swish is free software; you can redistribute it and/or modify+--  it under the terms of the GNU General Public License as published by+--  the Free Software Foundation; either version 2 of the License, or+--  (at your option) any later version.+--+--  Swish is distributed in the hope that it will be useful,+--  but WITHOUT ANY WARRANTY; without even the implied warranty of+--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+--  GNU General Public License for more details.+--+--  You should have received a copy of the GNU General Public License+--  along with Swish; if not, write to:+--    The Free Software Foundation, Inc.,+--    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+--+--------------------------------------------------------------------------------
+ src/Swish/RDF/Datatype/XSD/MapDecimal.hs view
@@ -0,0 +1,72 @@+{-# LANGUAGE OverloadedStrings #-}++--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  MapDecimal+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--                     2011 William Waites, 2011, 2012 Douglas Burke+--  License     :  GPL V2+--+--  Maintainer  :  Douglas Burke+--  Stability   :  experimental+--  Portability :  OverloadedStrings+--+--  This module defines the datatytpe mapping and relation values+--  used for RDF dataype @xsd:decimal@.+--+--------------------------------------------------------------------------------++module Swish.RDF.Datatype.XSD.MapDecimal (mapXsdDecimal) where++import Swish.Datatype (DatatypeMap(..))++import qualified Data.Text as T+import qualified Data.Text.Read as T++------------------------------------------------------------+--  Implementation of DatatypeMap for xsd:decimal+------------------------------------------------------------++-- | Functions that perform lexical-to-value+--  and value-to-canonical-lexical mappings for @xsd:decimal@ values.+--+mapXsdDecimal :: DatatypeMap Double+mapXsdDecimal = DatatypeMap+    { -- mapL2V :: T.Text -> Maybe Double+      mapL2V = \txt -> case T.double txt of+      	 Right (val, "") -> Just val+         _ -> Nothing+         +      -- mapV2L :: Double -> Maybe T.Text+      -- TODO: for now convert via String as issues with text-format+      --       (inability to use with ghci)   +    , mapV2L = Just . T.pack . show+    }++--------------------------------------------------------------------------------+--+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--                2011 William Waites, 2011, 2012 Douglas Burke+--+--  All rights reserved.+--+--  This file is part of Swish.+--+--  Swish is free software; you can redistribute it and/or modify+--  it under the terms of the GNU General Public License as published by+--  the Free Software Foundation; either version 2 of the License, or+--  (at your option) any later version.+--+--  Swish is distributed in the hope that it will be useful,+--  but WITHOUT ANY WARRANTY; without even the implied warranty of+--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+--  GNU General Public License for more details.+--+--  You should have received a copy of the GNU General Public License+--  along with Swish; if not, write to:+--    The Free Software Foundation, Inc.,+--    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+--+--------------------------------------------------------------------------------
+ src/Swish/RDF/Datatype/XSD/MapInteger.hs view
@@ -0,0 +1,70 @@+{-# LANGUAGE OverloadedStrings #-}++--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  MapInteger+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke+--  License     :  GPL V2+--+--  Maintainer  :  Douglas Burke+--  Stability   :  experimental+--  Portability :  OverloadedStrings+--+--  This module defines the datatytpe mapping and relation values+--  used for RDF dataype @xsd:integer@.+--+--------------------------------------------------------------------------------++module Swish.RDF.Datatype.XSD.MapInteger (mapXsdInteger) where++import Swish.Datatype (DatatypeMap(..))++import qualified Data.Text as T+import qualified Data.Text.Read as T++------------------------------------------------------------+--  Implementation of DatatypeMap for xsd:integer+------------------------------------------------------------++-- | Functions that perform lexical-to-value+--  and value-to-canonical-lexical mappings for @xsd:integer@ values.+--+mapXsdInteger :: DatatypeMap Integer+mapXsdInteger = DatatypeMap+    { -- mapL2V :: T.Text -> Maybe Integer+      mapL2V = \txt -> case T.signed T.decimal txt of+         Right (val, "") -> Just val+         _ -> Nothing+         +      -- mapV2L :: Integer -> Maybe T.Text+      -- TODO: for now convert via String as issues with text-format+      --       (inability to use with ghci)   +    , mapV2L = Just . T.pack . show+    }++--------------------------------------------------------------------------------+--+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke+--  All rights reserved.+--+--  This file is part of Swish.+--+--  Swish is free software; you can redistribute it and/or modify+--  it under the terms of the GNU General Public License as published by+--  the Free Software Foundation; either version 2 of the License, or+--  (at your option) any later version.+--+--  Swish is distributed in the hope that it will be useful,+--  but WITHOUT ANY WARRANTY; without even the implied warranty of+--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+--  GNU General Public License for more details.+--+--  You should have received a copy of the GNU General Public License+--  along with Swish; if not, write to:+--    The Free Software Foundation, Inc.,+--    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+--+--------------------------------------------------------------------------------
+ src/Swish/RDF/Datatype/XSD/String.hs view
@@ -0,0 +1,327 @@+{-# LANGUAGE OverloadedStrings #-}++--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  String+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke+--  License     :  GPL V2+--+--  Maintainer  :  Douglas Burke+--  Stability   :  experimental+--  Portability :  OverloadedStrings+--+--  This module defines the structures used to represent and+--  manipulate RDF @xsd:string@ datatyped literals.+--+--------------------------------------------------------------------------------++module Swish.RDF.Datatype.XSD.String+    ( rdfDatatypeXsdString+    , rdfDatatypeValXsdString+    , typeNameXsdString, namespaceXsdString+    , axiomsXsdString, rulesXsdString+    )+    where++import Swish.Datatype+    ( Datatype(..)+    , DatatypeVal(..)+    , DatatypeMap(..)+    , DatatypeRel(..), DatatypeRelPr+    , altArgs+    , UnaryFnTable,  unaryFnApp+    , DatatypeMod(..) +    , makeVmod20+    )++import Swish.Namespace (Namespace, ScopedName)+import Swish.Namespace (namespaceToBuilder, makeNSScopedName)+import Swish.QName (LName)+import Swish.Ruleset (makeRuleset)+import Swish.VarBinding (VarBinding(..), VarBindingModify(..))+import Swish.VarBinding (addVarBinding)++import Swish.RDF.ClassRestrictionRule (makeRDFDatatypeRestrictionRules)+import Swish.RDF.Datatype (RDFDatatype, RDFDatatypeVal, RDFDatatypeMod)+import Swish.RDF.Datatype (makeRdfDtOpenVarBindingModifiers )+import Swish.RDF.Graph (RDFLabel(..))+import Swish.RDF.Ruleset (RDFFormula, RDFRule, RDFRuleset)+import Swish.RDF.Ruleset (makeRDFGraphFromN3Builder, makeRDFFormula, makeN3ClosureRule)+import Swish.RDF.VarBinding (RDFVarBindingModify)++import Swish.RDF.Vocabulary+    ( namespaceRDF+    , namespaceRDFS+    , namespaceRDFD+    , namespaceXSD+    , namespaceXsdType+    )++import Data.Monoid(Monoid(..))++import qualified Data.Text as T+import qualified Data.Text.Lazy.Builder as B++------------------------------------------------------------+--  Misc values+------------------------------------------------------------++--  Local name for Integer datatype+nameXsdString :: LName+nameXsdString = "string"++-- | Type name for @xsd:string@ datatype+typeNameXsdString :: ScopedName+typeNameXsdString  = makeNSScopedName namespaceXSD nameXsdString++-- | Namespace for @xsd:string@ datatype functions+namespaceXsdString :: Namespace+namespaceXsdString = namespaceXsdType nameXsdString++-- | The RDFDatatype value for @xsd:string@.+rdfDatatypeXsdString :: RDFDatatype+rdfDatatypeXsdString = Datatype rdfDatatypeValXsdString++------------------------------------------------------------+--  Implmentation of RDFDatatypeVal for xsd:integer+------------------------------------------------------------++-- |Define Datatype value for @xsd:string@.+--+rdfDatatypeValXsdString :: RDFDatatypeVal T.Text+rdfDatatypeValXsdString = DatatypeVal+    { tvalName      = typeNameXsdString+    , tvalRules     = rdfRulesetXsdString+    , tvalMkRules   = makeRDFDatatypeRestrictionRules rdfDatatypeValXsdString+    , tvalMkMods    = makeRdfDtOpenVarBindingModifiers rdfDatatypeValXsdString+    , tvalMap       = mapXsdString+    , tvalRel       = relXsdString+    , tvalMod       = modXsdString+    }++-- |mapXsdString contains functions that perform lexical-to-value+--  and value-to-canonical-lexical mappings for @xsd:string@ values+--+--  These are identity mappings.+--+mapXsdString :: DatatypeMap T.Text+mapXsdString = DatatypeMap+    { mapL2V = Just+    , mapV2L = Just+    }++-- |relXsdString contains useful relations for @xsd:string@ values.+--+relXsdString :: [DatatypeRel T.Text]+relXsdString =+    [ relXsdStringEq+    , relXsdStringNe+    ]++mkStrRel2 ::+    LName -> DatatypeRelPr T.Text -> UnaryFnTable T.Text+    -> DatatypeRel T.Text+mkStrRel2 nam pr fns = +  DatatypeRel+    { dtRelName = makeNSScopedName namespaceXsdString nam+    , dtRelFunc = altArgs pr fns unaryFnApp+    }++{-+mkStrRel3 ::+    String -> DatatypeRelPr String -> BinaryFnTable String+    -> DatatypeRel String+mkStrRel3 nam pr fns = DatatypeRel+    { dtRelName = ScopedName namespaceXsdString nam+    , dtRelFunc = altArgs pr fns binaryFnApp+    }++mkStrRel3maybe ::+    String -> DatatypeRelPr String -> BinMaybeFnTable String+    -> DatatypeRel String+mkStrRel3maybe nam pr fns = DatatypeRel+    { dtRelName = ScopedName namespaceXsdString nam+    , dtRelFunc = altArgs pr fns binMaybeFnApp+    }+-}++liftL2 :: (a->a->Bool) -> ([a]->a) -> ([a]->a) -> [a] -> Bool+liftL2 p i1 i2 as = p (i1 as) (i2 as)++lcomp :: (a->a->Bool) -> [a] -> Bool+lcomp p = liftL2 p head (head . tail)++-- eq++relXsdStringEq :: DatatypeRel T.Text+relXsdStringEq = mkStrRel2 "eq" (lcomp (==))+    ( repeat (const True, []) )++-- ne++relXsdStringNe :: DatatypeRel T.Text+relXsdStringNe = mkStrRel2 "ne" (lcomp (/=))+    ( repeat (const True, []) )++-- |modXsdString contains variable binding modifiers for @xsd:string@ values.+--+modXsdString :: [RDFDatatypeMod T.Text]+modXsdString =+    [ modXsdStringEq+    , modXsdStringNe+    ]++modXsdStringEq, modXsdStringNe :: RDFDatatypeMod T.Text+modXsdStringEq = modXsdStringCompare "eq" (==)+modXsdStringNe = modXsdStringCompare "ne" (/=)++modXsdStringCompare ::+    LName -> (T.Text->T.Text->Bool) -> RDFDatatypeMod T.Text+modXsdStringCompare nam rel = DatatypeMod+    { dmName = makeNSScopedName namespaceXsdString nam+    , dmModf = [ f0 ]+    , dmAppf = makeVmod20+    }+    where+        f0 vs@[v1,v2] = if rel v1 v2 then vs else []+        f0 _          = []++-- |rulesetXsdString contains rules and axioms that allow additional+--  deductions when xsd:string values appear in a graph.+--+--  makeRuleset :: Namespace -> [Formula ex] -> [Rule ex] -> Ruleset ex+--+rdfRulesetXsdString :: RDFRuleset+rdfRulesetXsdString =+    makeRuleset namespaceXsdString axiomsXsdString rulesXsdString++mkPrefix :: Namespace -> B.Builder+mkPrefix = namespaceToBuilder++prefixXsdString :: B.Builder+prefixXsdString = +  mconcat+  [ mkPrefix namespaceRDF+  , mkPrefix namespaceRDFS+  , mkPrefix namespaceRDFD+  , mkPrefix namespaceXSD+  , mkPrefix namespaceXsdString+  ]+  +mkAxiom :: LName -> B.Builder -> RDFFormula+mkAxiom local gr =+    makeRDFFormula namespaceXsdString local (prefixXsdString `mappend` gr)++-- | The axioms for @xsd:string@, which are+--+-- > xsd:string a rdfs:Datatype .+--+axiomsXsdString :: [RDFFormula]+axiomsXsdString =+    [ mkAxiom "dt"      "xsd:string rdf:type rdfs:Datatype ."+    ]++-- | The rules for @xsd:string@.+rulesXsdString :: [RDFRule]+rulesXsdString = rulesXsdStringClosure ++ rulesXsdStringRestriction++rulesXsdStringRestriction :: [RDFRule]+rulesXsdStringRestriction =+    makeRDFDatatypeRestrictionRules rdfDatatypeValXsdString gr+    where+        gr = makeRDFGraphFromN3Builder rulesXsdStringBuilder++rulesXsdStringBuilder :: B.Builder+rulesXsdStringBuilder = +  mconcat+  [ prefixXsdString+    , "xsd_string:Eq a rdfd:GeneralRestriction ; "+    , "  rdfd:onProperties (rdf:_1 rdf:_2) ; "+    , "  rdfd:constraint xsd_string:eq ; "+    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "+    , "xsd_string:Ne a rdfd:GeneralRestriction ; "+    , "  rdfd:onProperties (rdf:_1 rdf:_2) ; "+    , "  rdfd:constraint xsd_string:ne ; "+    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "+    ]+  +rulesXsdStringClosure :: [RDFRule]+rulesXsdStringClosure =+    [ xsdstrls+    , xsdstrsl+    ]++--  Infer string from plain literal+xsdstrls :: RDFRule+xsdstrls = makeN3ClosureRule namespaceXsdString "ls"+            "?a ?p ?l ."+            "?a ?p ?s ."+            (stringPlain "s" "l")++--  Infer plain literal from string+xsdstrsl :: RDFRule+xsdstrsl = makeN3ClosureRule namespaceXsdString "sl"+            "?a ?p ?s ."+            "?a ?p ?l ."+            (stringPlain "s" "l")++--  Map between string and plain literal values+stringPlain :: String -> String -> RDFVarBindingModify+stringPlain svar lvar = stringPlainValue (Var svar) (Var lvar)++--  Variable binding modifier to create new binding to a canonical+--  form of a datatyped literal.+stringPlainValue ::+    RDFLabel -> RDFLabel -> RDFVarBindingModify+stringPlainValue svar lvar = VarBindingModify+        { vbmName   = makeNSScopedName namespaceRDFD "stringPlain"+        , vbmApply  = concatMap app1+        , vbmVocab  = [svar,lvar]+        , vbmUsage  = [[svar],[lvar],[]]+        }+    where+        app1 vbind = app2 (vbMap vbind svar) (vbMap vbind lvar) vbind++        -- Going to assume can only get TypedLit here, and assume LangLit+        -- can be ignored.+        app2 (Just (TypedLit s _))+             (Just (Lit l))+             vbind+             | s == l+             = [vbind]+        app2 (Just (TypedLit s _))+             Nothing+             vbind+             = [addVarBinding lvar (Lit s) vbind]+        app2 Nothing+             (Just (Lit l))+             vbind+             = [addVarBinding svar (TypedLit l typeNameXsdString) vbind]+        app2 _ _ _ = []++--------------------------------------------------------------------------------+--+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke+--  All rights reserved.+--+--  This file is part of Swish.+--+--  Swish is free software; you can redistribute it and/or modify+--  it under the terms of the GNU General Public License as published by+--  the Free Software Foundation; either version 2 of the License, or+--  (at your option) any later version.+--+--  Swish is distributed in the hope that it will be useful,+--  but WITHOUT ANY WARRANTY; without even the implied warranty of+--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+--  GNU General Public License for more details.+--+--  You should have received a copy of the GNU General Public License+--  along with Swish; if not, write to:+--    The Free Software Foundation, Inc.,+--    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+--+--------------------------------------------------------------------------------
+ src/Swish/RDF/Formatter/N3.hs view
@@ -0,0 +1,943 @@+{-# LANGUAGE OverloadedStrings #-}++--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  N3+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke+--  License     :  GPL V2+--+--  Maintainer  :  Douglas Burke+--  Stability   :  experimental+--  Portability :  OverloadedStrings+--+--  This Module implements a Notation 3 formatter+--  for an 'RDFGraph' value.+--+-- REFERENCES:+--+--  - \"Notation3 (N3): A readable RDF syntax\",+--     W3C Team Submission 14 January 2008,+--     <http://www.w3.org/TeamSubmission/2008/SUBM-n3-20080114/>+--+--  - Tim Berners-Lee's design issues series notes and description,+--     <http://www.w3.org/DesignIssues/Notation3.html>+--+--  - Notation 3 Primer by Sean Palmer,+--      <http://www.w3.org/2000/10/swap/Primer.html>+--+--  TODO:+--+--   * Initial prefix list to include nested formulae;+--      then don't need to update prefix list for these.+--+--   * correct output of strings containing unsupported escape+--     characters (such as @\\q@)+--+--   * more flexible terminator generation for formatted formulae+--     (for inline blank nodes.)+--+--------------------------------------------------------------------------------++{-+TODO:++The code used to determine whether a blank node can be written+using the "[]" short form could probably take advantage of the+GraphPartition module.++-}++module Swish.RDF.Formatter.N3+    ( NodeGenLookupMap+    , formatGraphAsText+    , formatGraphAsLazyText+    , formatGraphAsBuilder+    , formatGraphIndent  +    , formatGraphDiag+    )+where++import Swish.GraphClass (Arc(..))+import Swish.Namespace (ScopedName, getScopeLocal, getScopeURI)+import Swish.QName (getLName)++import Swish.RDF.Graph (+  RDFGraph, RDFLabel(..),+  NamespaceMap, RevNamespaceMap,+  emptyNamespaceMap,+  FormulaMap, emptyFormulaMap,+  getArcs, labels,+  setNamespaces, getNamespaces,+  getFormulae,+  emptyRDFGraph+  , quote+  , quoteT+  , resRdfFirst, resRdfRest, resRdfNil+  )++import Swish.RDF.Vocabulary (+  fromLangTag, +  rdfType,+  rdfNil,+  owlSameAs, logImplies+  , xsdBoolean, xsdDecimal, xsdInteger, xsdDouble +  )++import Control.Monad (liftM, when, void)+import Control.Monad.State (State, modify, get, put, runState)++import Data.Char (isDigit)+import Data.List (foldl', delete, groupBy, partition, sort, intersperse)+import Data.LookupMap (LookupEntryClass(..), LookupMap)+import Data.LookupMap (emptyLookupMap, reverseLookupMap, listLookupMap+                      , mapFind, mapFindMaybe, mapAdd, mapDelete, mapMerge)+import Data.Monoid (Monoid(..))+import Data.Word (Word32)++-- it strikes me that using Lazy Text here is likely to be+-- wrong; however I have done no profiling to back this+-- assumption up!++import qualified Data.Text as T+import qualified Data.Text.Lazy as L+import qualified Data.Text.Lazy.Builder as B++-- temporary conversion+quoteB :: Bool -> String -> B.Builder+quoteB f v = B.fromString $ quote f v++----------------------------------------------------------------------+--  Graph formatting state monad+----------------------------------------------------------------------+--+--  The graph to be formatted is carried as part of the formatting+--  state, so that decisions about what needs to be formatted can+--  themselves be based upon and reflected in the state (e.g. if a+--  decision is made to include a blank node inline, it can be removed+--  from the graph state that remains to be formatted).++type SubjTree lb = [(lb,PredTree lb)]+type PredTree lb = [(lb,[lb])]++data N3FormatterState = N3FS+    { indent    :: B.Builder+    , lineBreak :: Bool+    , graph     :: RDFGraph+    , subjs     :: SubjTree RDFLabel+    , props     :: PredTree RDFLabel   -- for last subject selected+    , objs      :: [RDFLabel]          -- for last property selected+    , formAvail :: FormulaMap RDFLabel+    , formQueue :: [(RDFLabel,RDFGraph)]+    , nodeGenSt :: NodeGenState+    , bNodesCheck   :: [RDFLabel]      -- these bNodes are not to be converted to '[..]' format+    , traceBuf  :: [String]+    }+             +type Formatter a = State N3FormatterState a++emptyN3FS :: NodeGenState -> N3FormatterState+emptyN3FS ngs = N3FS+    { indent    = "\n"+    , lineBreak = False+    , graph     = emptyRDFGraph+    , subjs     = []+    , props     = []+    , objs      = []+    , formAvail = emptyFormulaMap+    , formQueue = []+    , nodeGenSt = ngs+    , bNodesCheck   = []+    , traceBuf  = []+    }++-- | Node name generation state information that carries through+-- and is updated by nested formulae.+type NodeGenLookupMap = LookupMap (RDFLabel, Word32)++data NodeGenState = Ngs+    { prefixes  :: NamespaceMap+    , nodeMap   :: NodeGenLookupMap+    , nodeGen   :: Word32+    }++emptyNgs :: NodeGenState+emptyNgs = Ngs+    { prefixes  = emptyLookupMap+    , nodeMap   = emptyLookupMap+    , nodeGen   = 0+    }++-- simple context for label creation+-- (may be a temporary solution to the problem+--  of label creation)+--+data LabelContext = SubjContext | PredContext | ObjContext+                    deriving (Eq, Show)++getIndent :: Formatter B.Builder+getIndent = indent `liftM` get++setIndent :: B.Builder -> Formatter ()+setIndent ind = modify $ \st -> st { indent = ind }++getLineBreak :: Formatter Bool+getLineBreak = lineBreak `liftM` get++setLineBreak :: Bool -> Formatter ()+setLineBreak brk = modify $ \st -> st { lineBreak = brk }++getNgs :: Formatter NodeGenState+getNgs = nodeGenSt `liftM` get++setNgs :: NodeGenState -> Formatter ()+setNgs ngs = modify $ \st -> st { nodeGenSt = ngs }++getPrefixes :: Formatter NamespaceMap+getPrefixes = prefixes `liftM` getNgs++getSubjs :: Formatter (SubjTree RDFLabel)+getSubjs = subjs `liftM` get++setSubjs :: SubjTree RDFLabel -> Formatter ()+setSubjs sl = modify $ \st -> st { subjs = sl }++getProps :: Formatter (PredTree RDFLabel)+getProps = props `liftM` get++setProps :: PredTree RDFLabel -> Formatter ()+setProps ps = modify $ \st -> st { props = ps }++{-+getObjs :: Formatter ([RDFLabel])+getObjs = objs `liftM` get++setObjs :: [RDFLabel] -> Formatter ()+setObjs os = do+  st <- get+  put $ st { objs = os }+-}++getBnodesCheck :: Formatter [RDFLabel]+getBnodesCheck = bNodesCheck `liftM` get++{-+addTrace :: String -> Formatter ()+addTrace tr = do+  st <- get+  put $ st { traceBuf = tr : traceBuf st }+-}+  +queueFormula :: RDFLabel -> Formatter ()+queueFormula fn = do+  st <- get+  let fa = formAvail st+      newState fv = st {+                      formAvail = mapDelete fa fn,+                      formQueue = (fn,fv) : formQueue st+                    }+  case mapFindMaybe fn fa of+    Nothing -> return ()+    Just v -> void $ put $ newState v++{-+Return the graph associated with the label and delete it+from the store, if there is an association, otherwise+return Nothing.+-}+extractFormula :: RDFLabel -> Formatter (Maybe RDFGraph)+extractFormula fn = do+  st <- get+  let fa = formAvail st+      newState = st { formAvail=mapDelete fa fn }+  case mapFindMaybe fn fa of+    Nothing -> return Nothing+    Just fv -> put newState >> return (Just fv)++{-+moreFormulae :: Formatter Bool+moreFormulae =  do+  st <- get+  return $ not $ null (formQueue st)++nextFormula :: Formatter (RDFLabel,RDFGraph)+nextFormula = do+  st <- get+  let (nf : fq) = formQueue st+  put $ st { formQueue = fq }+  return nf++-}++-- list has a length of 1+len1 :: [a] -> Bool+len1 (_:[]) = True+len1 _ = False++{-|+Given a set of statements and a label, return the details of the+RDF collection referred to by label, or Nothing.++For label to be considered as representing a collection we require the+following conditions to hold (this is only to support the+serialisation using the '(..)' syntax and does not make any statement+about semantics of the statements with regard to RDF Collections):++  - there must be one rdf_first and one rdfRest statement+  - there must be no other predicates for the label++-} +getCollection ::          +  SubjTree RDFLabel -- ^ statements organized by subject+  -> RDFLabel -- ^ does this label represent a list?+  -> Maybe (SubjTree RDFLabel, [RDFLabel], [RDFLabel])+     -- ^ the statements with the elements removed; the+     -- content elements of the collection (the objects of the rdf:first+     -- predicate) and the nodes that represent the spine of the+     -- collection (in reverse order, unlike the actual contents which are in+     -- order).+getCollection subjList lbl = go subjList lbl ([],[]) +    where+      go sl l (cs,ss) | l == resRdfNil = Just (sl, reverse cs, ss)+                      | otherwise = do+        (pList1, sl') <- removeItem sl l+        (pFirst, pList2) <- removeItem pList1 resRdfFirst+        (pNext, pList3) <- removeItem pList2 resRdfRest++        -- QUS: could I include these checks implicitly in the pattern matches above?+        -- ie instrad of (pFirst, pos1) <- ..+        -- have ([content], pos1) <- ...+        -- ?+        if and [len1 pFirst, len1 pNext, null pList3]+          then go sl' (head pNext) (head pFirst : cs, l : ss)+          else Nothing++{-+TODO:++Should we change the preds/objs entries as well?++-}+extractList :: LabelContext -> RDFLabel -> Formatter (Maybe [RDFLabel])+extractList lctxt ln = do+  osubjs <- getSubjs+  oprops <- getProps+  let mlst = getCollection osubjs' ln++      -- we only want to send in rdf:first/rdf:rest here+      fprops = filter ((`elem` [resRdfFirst, resRdfRest]) . fst) oprops++      osubjs' =+          case lctxt of+            SubjContext -> (ln, fprops) : osubjs+            _ -> osubjs ++      -- tr = "extractList " ++ show ln ++ " (" ++ show lctxt ++ ")\n -> osubjs= " ++ show osubjs ++ "\n -> opreds= " ++ show oprops ++ "\n -> mlst= " ++ show mlst ++ "\n"+  -- addTrace tr+  case mlst of+    -- sl is guaranteed to be free of (ln,fprops) here if lctxt is SubjContext+    Just (sl,ls,_) -> do+              setSubjs sl+              when (lctxt == SubjContext) $ setProps $ filter ((`notElem` [resRdfFirst, resRdfRest]) . fst) oprops+              return (Just ls)++    Nothing -> return Nothing+  +{-|+Removes the first occurrence of the item from the+association list, returning it's contents and the rest+of the list, if it exists.+-}+removeItem :: (Eq a) => [(a,b)] -> a -> Maybe (b, [(a,b)])+removeItem os x =+  let (as, bs) = break (\a -> fst a == x) os+  in case bs of+    ((_,b):bbs) -> Just (b, as ++ bbs)+    [] -> Nothing++----------------------------------------------------------------------+--  Define a top-level formatter function:+----------------------------------------------------------------------++-- | Convert the graph to text.+formatGraphAsText :: RDFGraph -> T.Text+formatGraphAsText = L.toStrict . formatGraphAsLazyText++-- | Convert the graph to text.+formatGraphAsLazyText :: RDFGraph -> L.Text+formatGraphAsLazyText = B.toLazyText . formatGraphAsBuilder+  +-- | Convert the graph to a Builder.+formatGraphAsBuilder :: RDFGraph -> B.Builder+formatGraphAsBuilder = formatGraphIndent "\n" True+  +-- | Convert the graph to a builder using the given indentation text.+formatGraphIndent :: +    B.Builder     -- ^ indentation text+    -> Bool       -- ^ are prefixes to be generated?+    -> RDFGraph   -- ^ graph+    -> B.Builder+formatGraphIndent indnt flag gr = +  let (res, _, _, _) = formatGraphDiag indnt flag gr+  in res+  +-- | Format graph and return additional information+formatGraphDiag :: +  B.Builder  -- ^ indentation+  -> Bool    -- ^ are prefixes to be generated?+  -> RDFGraph +  -> (B.Builder, NodeGenLookupMap, Word32, [String])+formatGraphDiag indnt flag gr = +  let fg  = formatGraph indnt " .\n" False flag gr+      ngs = emptyNgs {+        prefixes = emptyLookupMap,+        nodeGen  = findMaxBnode gr+        }+             +      (out, fgs) = runState fg (emptyN3FS ngs)+      ogs        = nodeGenSt fgs+  +  in (out, nodeMap ogs, nodeGen ogs, traceBuf fgs)++----------------------------------------------------------------------+--  Formatting as a monad-based computation+----------------------------------------------------------------------++formatGraph :: +  B.Builder     -- indentation string+  -> B.Builder  -- text to be placed after final statement+  -> Bool       -- True if a line break is to be inserted at the start+  -> Bool       -- True if prefix strings are to be generated+  -> RDFGraph   -- graph to convert+  -> Formatter B.Builder+formatGraph ind end dobreak dopref gr = do+  setIndent ind+  setLineBreak dobreak+  setGraph gr+  +  fp <- if dopref+        then formatPrefixes (getNamespaces gr)+        else return mempty+  more <- moreSubjects+  if more+    then do+      fr <- formatSubjects+      return $ mconcat [fp, fr, end]+    else return fp++formatPrefixes :: NamespaceMap -> Formatter B.Builder+formatPrefixes pmap = do+  let mls = map (pref . keyVal) (listLookupMap pmap)+  ls <- sequence mls+  return $ mconcat ls+    where+      pref (Just p,u) = nextLine $ mconcat ["@prefix ", B.fromText p, ": <", quoteB True (show u), "> ."]+      pref (_,u)      = nextLine $ mconcat ["@prefix : <", quoteB True (show u), "> ."]++{-+NOTE:+I expect there to be confusion below where I need to+convert from Text to Builder+-}++formatSubjects :: Formatter B.Builder+formatSubjects = do+  sb    <- nextSubject+  sbstr <- formatLabel SubjContext sb+  +  flagP <- moreProperties+  if flagP+    then do+      prstr <- formatProperties sb sbstr+      flagS <- moreSubjects+      if flagS+        then do+          fr <- formatSubjects+          return $ mconcat [prstr, " .", fr]+        else return prstr+           +    else do+      txt <- nextLine sbstr+    +      flagS <- moreSubjects+      if flagS+        then do+          fr <- formatSubjects+          return $ mconcat [txt, " .", fr]+        else return txt++{-+TODO: now we are throwing a Builder around it is awkward to+get the length of the text to calculate the indentation++So++  a) change the indentation scheme+  b) pass around text instead of builder++mkIndent :: L.Text -> L.Text+mkIndent inVal = L.replicate (L.length inVal) " "+-}++hackIndent :: B.Builder+hackIndent = "    "++formatProperties :: RDFLabel -> B.Builder -> Formatter B.Builder+formatProperties sb sbstr = do+  pr <- nextProperty sb+  prstr <- formatLabel PredContext pr+  obstr <- formatObjects sb pr $ mconcat [sbstr, " ", prstr]+  more  <- moreProperties+  let sbindent = hackIndent -- mkIndent sbstr+  if more+    then do+      fr <- formatProperties sb sbindent+      nl <- nextLine $ obstr `mappend` " ;"+      return $ nl `mappend` fr+    else nextLine obstr++formatObjects :: RDFLabel -> RDFLabel -> B.Builder -> Formatter B.Builder+formatObjects sb pr prstr = do+  ob    <- nextObject sb pr+  obstr <- formatLabel ObjContext ob+  more  <- moreObjects+  if more+    then do+      let prindent = hackIndent -- mkIndent prstr+      fr <- formatObjects sb pr prindent+      nl <- nextLine $ mconcat [prstr, " ", obstr, ","]+      return $ nl `mappend` fr+    else return $ mconcat [prstr, " ", obstr]++insertFormula :: RDFGraph -> Formatter B.Builder+insertFormula gr = do+  ngs0  <- getNgs+  ind   <- getIndent+  let grm = formatGraph (ind `mappend` "    ") "" True False+            (setNamespaces emptyNamespaceMap gr)++      (f3str, fgs') = runState grm (emptyN3FS ngs0)++  setNgs (nodeGenSt fgs')+  f4str <- nextLine " } "+  return $ mconcat [" { ",f3str, f4str]++{-+Add a list inline. We are given the labels that constitute+the list, in order, so just need to display them surrounded+by ().+-}+insertList :: [RDFLabel] -> Formatter B.Builder+insertList [] = return "()" -- not convinced this can happen+insertList xs = do+  ls <- mapM (formatLabel ObjContext) xs+  return $ mconcat ("( " : intersperse " " ls) `mappend` " )"+    +{-+Add a blank node inline.+-}++insertBnode :: LabelContext -> RDFLabel -> Formatter B.Builder+insertBnode SubjContext lbl = do+  flag <- moreProperties+  txt <- if flag+         then (`mappend` "\n") `liftM` formatProperties lbl ""+         else return ""++  -- TODO: handle indentation?+  return $ mconcat ["[", txt, "]"]++insertBnode _ lbl = do+  ost <- get+  let osubjs = subjs ost+      oprops = props ost+      oobjs  = objs  ost++      (bsubj, rsubjs) = partition ((== lbl) . fst) osubjs++      rprops = case bsubj of+                 [(_,rs)] -> rs+                 _ -> []++      -- we essentially want to create a new subgraph+      -- for this node but it's not as simple as that since+      -- we could have something like+      --     :a :b [ :foo [ :bar "xx" ] ]+      -- so we still need to carry around the whole graph+      --+      nst = ost { subjs = rsubjs,+                  props = rprops,+                  objs  = []+                }++  put nst+  flag <- moreProperties+  txt <- if flag+         then (`mappend` "\n") `liftM` formatProperties lbl ""+         else return ""++  -- TODO: how do we restore the original set up?+  --       I can't believe the following is sufficient+  --+  nst' <- get+  let slist  = map fst $ subjs nst'+      nsubjs = filter (\(l,_) -> l `elem` slist) osubjs++  put $ nst' { subjs = nsubjs,+                       props = oprops, +                       objs  = oobjs+             }++  -- TODO: handle indentation?+  return $ mconcat ["[", txt, "]"]+  +----------------------------------------------------------------------+--  Formatting helpers+----------------------------------------------------------------------++setGraph :: RDFGraph -> Formatter ()+setGraph gr = do+  st <- get++  let ngs0 = nodeGenSt st+      pre' = mapMerge (prefixes ngs0) (getNamespaces gr)+      ngs' = ngs0 { prefixes = pre' }+      arcs = sortArcs $ getArcs gr+      nst  = st  { graph     = gr+                 , subjs     = arcTree arcs+                 , props     = []+                 , objs      = []+                 , formAvail = getFormulae gr+                 , nodeGenSt = ngs'+                 , bNodesCheck   = countBnodes arcs+                 }++  put nst++hasMore :: (N3FormatterState -> [b]) -> Formatter Bool+hasMore lens = (not . null . lens) `liftM` get++moreSubjects :: Formatter Bool+moreSubjects = hasMore subjs+-- moreSubjects = (not . null . subjs) `liftM` get++moreProperties :: Formatter Bool+moreProperties = hasMore props+-- moreProperties = (not . null . props) `liftM` get++moreObjects :: Formatter Bool+moreObjects = hasMore objs+-- moreObjects = (not . null . objs) `liftM` get++nextSubject :: Formatter RDFLabel+nextSubject = do+  st <- get++  let sb:sbs = subjs st+      nst = st  { subjs = sbs+                , props = snd sb+                , objs  = []+                }++  put nst+  return $ fst sb++nextProperty :: RDFLabel -> Formatter RDFLabel+nextProperty _ = do+  st <- get++  let pr:prs = props st+      nst = st  { props = prs+                 , objs  = snd pr+                 }++  put nst+  return $ fst pr++nextObject :: RDFLabel -> RDFLabel -> Formatter RDFLabel+nextObject _ _ = do+  st <- get++  let ob:obs = objs st+      nst = st { objs = obs }++  put nst+  return ob++nextLine :: B.Builder -> Formatter B.Builder+nextLine str = do+  ind <- getIndent+  brk <- getLineBreak+  if brk+    then return $ ind `mappend` str+    else do+      --  After first line, always insert line break+      setLineBreak True+      return str++--  Format a label+--  Most labels are simply displayed as provided, but there are a+--  number of wrinkles to take care of here:+--  (a) blank nodes automatically allocated on input, with node+--      identifiers of the form of a digit string nnn.  These are+--      not syntactically valid, and are reassigned node identifiers+--      of the form _nnn, where nnn is chosen so that is does not+--      clash with any other identifier in the graph.+--  (b) URI nodes:  if possible, replace URI with qname,+--      else display as <uri>+--  (c) formula nodes (containing graphs).+--  (d) use the "special-case" formats for integer/float/double+--      literals.      +--      +--  [[[TODO:]]]+--  (d) generate multi-line literals when appropriate+--+-- This is being updated to produce inline formula, lists and     +-- blank nodes. The code is not efficient.+--++specialTable :: [(ScopedName, String)]+specialTable = +  [ (rdfType, "a")+  , (owlSameAs, "=")+  , (logImplies, "=>")+  , (rdfNil, "()")+  ]++formatLabel :: LabelContext -> RDFLabel -> Formatter B.Builder+{-+formatLabel lab@(Blank (_:_)) = do+  name <- formatNodeId lab+  queueFormula lab+  return name+-}++{-+The "[..]" conversion is done last, after "()" and "{}" checks.+-}+formatLabel lctxt lab@(Blank (_:_)) = do+  mlst <- extractList lctxt lab+  case mlst of+    Just lst -> insertList lst+    Nothing -> do+              mfml <- extractFormula lab+              case mfml of+                Just fml -> insertFormula fml+                Nothing -> do+                          nb1 <- getBnodesCheck+                          if lctxt /= PredContext && lab `notElem` nb1+                            then insertBnode lctxt lab+                            else formatNodeId lab++formatLabel _ lab@(Res sn) = +  case lookup sn specialTable of+    Just txt -> return $ quoteB True txt -- TODO: do we need to quote?+    Nothing -> do+      pr <- getPrefixes+      let nsuri  = getScopeURI sn+          local  = getLName $ getScopeLocal sn+          premap = reverseLookupMap pr :: RevNamespaceMap+          prefix = mapFindMaybe nsuri premap+          +          name   = case prefix of+                     Just (Just p) -> B.fromText $ quoteT True $ mconcat [p, ":", local] -- TODO: what are quoting rules for QNames+                     _ -> mconcat ["<", quoteB True (show nsuri ++ T.unpack local), ">"]+      +      {-+          name   = case prefix of+                     Just p -> quoteB True (p ++ ":" ++ local) -- TODO: what are quoting rules for QNames+                     _ -> mconcat ["<", quoteB True (nsuri++local), ">"]+      -}+          +      queueFormula lab+      return name++-- The canonical notation for xsd:double in XSD, with an upper-case E,+-- does not match the syntax used in N3, so we need to convert here.     +-- Rather than converting back to a Double and then displaying that       +-- we just convert E to e for now.      +--+formatLabel _ (TypedLit lit dtype)+    | dtype == xsdDouble = return $ B.fromText $ T.toLower lit+    | dtype `elem` [xsdBoolean, xsdDecimal, xsdInteger] = return $ B.fromText lit+    | otherwise = return $ quoteText lit `mappend` "^^" `mappend` showScopedName dtype+formatLabel _ (LangLit lit lcode) =+    return $ quoteText lit `mappend` "@" `mappend` B.fromText (fromLangTag lcode)+formatLabel _ (Lit lit) = return $ quoteText lit++formatLabel _ lab = return $ B.fromString $ show lab++{-+We have to decide whether to use " or """ to quote+the string.++There is also no need to restrict the string to the+ASCII character set; this could be an option but we+can also leave Unicode as is (or at least convert to UTF-8).++If we use """ to surround the string then we protect the+last character if it is a " (assuming it isn't protected).+-}++quoteText :: T.Text -> B.Builder+quoteText txt = +  let st = T.unpack txt -- TODO: fix+      qst = quoteB (n==1) st+      n = if '\n' `elem` st || '"' `elem` st then 3 else 1+      qch = B.fromString (replicate n '"')+  in mconcat [qch, qst, qch]++formatNodeId :: RDFLabel -> Formatter B.Builder+formatNodeId lab@(Blank (lnc:_)) =+    if isDigit lnc then mapBlankNode lab else return $ B.fromString $ show lab+formatNodeId other = error $ "formatNodeId not expecting a " ++ show other -- to shut up -Wall++mapBlankNode :: RDFLabel -> Formatter B.Builder+mapBlankNode lab = do+  ngs <- getNgs+  let cmap = nodeMap ngs+      cval = nodeGen ngs+  nv <- case mapFind 0 lab cmap of+    0 -> do +      let nval = succ cval+          nmap = mapAdd cmap (lab, nval)+      setNgs $ ngs { nodeGen = nval, nodeMap = nmap }+      return nval+      +    n -> return n+  +  -- TODO: is this what we want?+  return $ "_:swish" `mappend` B.fromString (show nv)++-- TODO: need to be a bit more clever with this than we did in NTriples+--       not sure the following counts as clever enough ...+--  +showScopedName :: ScopedName -> B.Builder+{-+showScopedName (ScopedName n l) = +  let uri = nsURI n ++ l+  in quote uri+-}+showScopedName = quoteB True . show++----------------------------------------------------------------------+--  Graph-related helper functions+----------------------------------------------------------------------++newtype SortedArcs lb = SA [Arc lb]++sortArcs :: (Ord lb) => [Arc lb] -> SortedArcs lb+sortArcs = SA . sort++--  Rearrange a list of arcs into a tree of pairs which group together+--  all statements for a single subject, and similarly for multiple+--  objects of a common predicate.+--+arcTree :: (Eq lb) => SortedArcs lb -> SubjTree lb+arcTree (SA as) = commonFstEq (commonFstEq id) $ map spopair as+    where+        spopair (Arc s p o) = (s,(p,o))++{-+arcTree as = map spopair $ sort as+    where+        spopair (Arc s p o) = (s,[(p,[o])])+-}++--  Rearrange a list of pairs so that multiple occurrences of the first+--  are commoned up, and the supplied function is applied to each sublist+--  with common first elements to obtain the corresponding second value+commonFstEq :: (Eq a) => ( [b] -> c ) -> [(a,b)] -> [(a,c)]+commonFstEq f ps =+    [ (fst $ head sps,f $ map snd sps) | sps <- groupBy fstEq ps ]+    where+        fstEq (f1,_) (f2,_) = f1 == f2++{-+-- Diagnostic code for checking arcTree logic:+testArcTree = (arcTree testArcTree1) == testArcTree2+testArcTree1 =+    [Arc "s1" "p11" "o111", Arc "s1" "p11" "o112"+    ,Arc "s1" "p12" "o121", Arc "s1" "p12" "o122"+    ,Arc "s2" "p21" "o211", Arc "s2" "p21" "o212"+    ,Arc "s2" "p22" "o221", Arc "s2" "p22" "o222"+    ]+testArcTree2 =+    [("s1",[("p11",["o111","o112"]),("p12",["o121","o122"])])+    ,("s2",[("p21",["o211","o212"]),("p22",["o221","o222"])])+    ]+-}+++findMaxBnode :: RDFGraph -> Word32+findMaxBnode = maximum . map getAutoBnodeIndex . labels++getAutoBnodeIndex   :: RDFLabel -> Word32+getAutoBnodeIndex (Blank ('_':lns)) = res where+    -- cf. prelude definition of read s ...+    res = case [x | (x,t) <- reads lns, ("","") <- lex t] of+            [x] -> x+            _   -> 0+getAutoBnodeIndex _                   = 0++{-+Find all blank nodes that occur+  - any number of times as a subject+  - 0 or 1 times as an object++Such nodes can be output using the "[..]" syntax. To make it simpler+to check we actually store those nodes that can not be expanded.++Note that we do not try and expand any bNode that is used in+a predicate position.++Should probably be using the SubjTree RDFLabel structure but this+is easier for now.++-}++countBnodes :: SortedArcs RDFLabel -> [RDFLabel]+countBnodes (SA as) = snd (foldl' ctr ([],[]) as)+    where+      -- first element of tuple are those blank nodes only seen once,+      -- second element those blank nodes seen multiple times+      --+      inc b@(b1s,bms) l@(Blank _) | l `elem` bms = b+                                  | l `elem` b1s = (delete l b1s, l:bms)+                                  | otherwise    = (l:b1s, bms)+      inc b _ = b++      -- if the bNode appears as a predicate we instantly add it to the+      -- list of nodes not to expand, even if only used once+      incP b@(b1s,bms) l@(Blank _) | l `elem` bms = b+                                   | l `elem` b1s = (delete l b1s, l:bms)+           			   | otherwise    = (b1s, l:bms)+      incP b _ = b++      ctr orig (Arc _ p o) = inc (incP orig p) o++--------------------------------------------------------------------------------+--+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke+--  All rights reserved.+--+--  This file is part of Swish.+--+--  Swish is free software; you can redistribute it and/or modify+--  it under the terms of the GNU General Public License as published by+--  the Free Software Foundation; either version 2 of the License, or+--  (at your option) any later version.+--+--  Swish is distributed in the hope that it will be useful,+--  but WITHOUT ANY WARRANTY; without even the implied warranty of+--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+--  GNU General Public License for more details.+--+--  You should have received a copy of the GNU General Public License+--  along with Swish; if not, write to:+--    The Free Software Foundation, Inc.,+--    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+--+--------------------------------------------------------------------------------
+ src/Swish/RDF/Formatter/NTriples.hs view
@@ -0,0 +1,233 @@+{-# LANGUAGE OverloadedStrings #-}+--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  NTriples+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke+--  License     :  GPL V2+--+--  Maintainer  :  Douglas Burke+--  Stability   :  experimental+--  Portability :  OverloadedStrings+--+--  This Module implements a NTriples formatter for an 'RDFGraph'.+--+--  REFERENCES:+--+--  - \"RDF Test Cases\",+--     W3C Recommendation 10 February 2004,+--     <http://www.w3.org/TR/rdf-testcases/#ntriples>+--+--------------------------------------------------------------------------------++module Swish.RDF.Formatter.NTriples+    ( formatGraphAsText+    , formatGraphAsLazyText+    , formatGraphAsBuilder+    )+where++import Swish.GraphClass (Arc(..))+import Swish.Namespace (ScopedName, getQName)++import Swish.RDF.Graph (RDFGraph, RDFLabel(..))+import Swish.RDF.Graph (getArcs)++import Swish.RDF.Vocabulary (fromLangTag)++import Control.Monad.State+import Control.Applicative ((<$>))++import Data.Char (ord, intToDigit, toUpper)+import Data.LookupMap (LookupMap, emptyLookupMap, mapFind, mapAdd)+import Data.Monoid++-- it strikes me that using Lazy Text here is likely to be+-- wrong; however I have done no profiling to back this+-- assumption up!++import qualified Data.Text as T+import qualified Data.Text.Lazy as L+import qualified Data.Text.Lazy.Builder as B++----------------------------------------------------------------------+--  Graph formatting state monad+----------------------------------------------------------------------+--+--  This is a lot simpler than other formatters.++--  | Node name generation state information that carries through+--  and is updated by nested formulae+type NodeGenLookupMap = LookupMap (RDFLabel,Int)++data NTFormatterState = NTFS { +      ntfsNodeMap :: NodeGenLookupMap,+      ntfsNodeGen :: Int+    } deriving Show++emptyNTFS :: NTFormatterState+emptyNTFS = NTFS {+              ntfsNodeMap = emptyLookupMap,+              ntfsNodeGen = 0+              }++type Formatter a = State NTFormatterState a++-- | Convert a RDF graph to NTriples format.+formatGraphAsText :: RDFGraph -> T.Text+formatGraphAsText = L.toStrict . formatGraphAsLazyText++-- | Convert a RDF graph to NTriples format.+formatGraphAsLazyText :: RDFGraph -> L.Text+formatGraphAsLazyText = B.toLazyText . formatGraphAsBuilder++-- | Convert a RDF graph to NTriples format.+formatGraphAsBuilder :: RDFGraph -> B.Builder+formatGraphAsBuilder gr = fst $ runState (formatGraph gr) emptyNTFS++----------------------------------------------------------------------+--  Formatting as a monad-based computation+----------------------------------------------------------------------++formatGraph :: RDFGraph -> Formatter B.Builder+formatGraph gr = mconcat <$> mapM formatArc (getArcs gr)++-- TODO: this reverses the contents but may be faster?+--       that is if I've got the order right in the mappend call+-- formatGraphBuilder gr = foldl' (\a b -> b `mappend` (formatArcBuilder a)) B.empty (getArcs gr)++space, nl :: B.Builder+space = B.singleton ' '+nl    = " .\n"++formatArc :: Arc RDFLabel -> Formatter B.Builder+formatArc (Arc s p o) = do+  sl <- formatLabel s+  pl <- formatLabel p+  ol <- formatLabel o+  return $ mconcat [sl, space, pl, space, ol, nl]+  -- return $ sl `mappend` $ space `mappend` $ pl `mappend` $ space `mappend` $ ol `mappend` nl+  +{-+If we have a blank node then can++  - use the label it contains+  - generate a new one on output++For now we create new labels whatever the input was since this+simplifies things, but it may be changed.++formatLabel :: RDFLabel -> Formatter String+formatLabel lab@(Blank (lnc:_)) = +  if isDigit lnc then mapBlankNode lab else return $ show lab+formatLabel lab = return $ show lab+-}++squote, at, carets  :: B.Builder+squote = "\""+at     = "@"+carets = "^^"++formatLabel :: RDFLabel -> Formatter B.Builder+formatLabel lab@(Blank _) = mapBlankNode lab+formatLabel (Res sn) = return $ showScopedName sn+formatLabel (Lit lit) = return $ quoteText lit+formatLabel (LangLit lit lang) = return $ mconcat [quoteText lit, at, B.fromText (fromLangTag lang)]+formatLabel (TypedLit lit dt)  = return $ mconcat [quoteText lit, carets, showScopedName dt]++-- do not expect to get the following, but include+-- just in case rather than failing+formatLabel lab = return $ B.fromString $ show lab++mapBlankNode :: RDFLabel -> Formatter B.Builder+mapBlankNode lab = do+  st <- get+  let cmap = ntfsNodeMap st+      cval = ntfsNodeGen st++  nv <- case mapFind 0 lab cmap of+            0 -> do+              let nval = succ cval+                  nmap = mapAdd cmap (lab, nval)++              put $ st { ntfsNodeMap = nmap, ntfsNodeGen = nval }+              return nval++            n -> return n++  return $ "_:swish" `mappend` B.fromString (show nv)++-- TODO: can we use Network.URI to protect the URI?+showScopedName :: ScopedName -> B.Builder+{-+showScopedName (ScopedName n l) = +  let uri = T.pack (show (nsURI n)) `mappend` l+  in mconcat ["<", B.fromText (quote uri), ">"]+-}+-- showScopedName s = mconcat ["<", B.fromText (quote (T.pack (show (getQName s)))), ">"]+showScopedName s = B.fromText (quote (T.pack (show (getQName s)))) -- looks like qname already adds the <> around this++quoteText :: T.Text -> B.Builder+quoteText  st = mconcat [squote, B.fromText (quote st), squote]++{-+QUS: should we be operating on Text like this?+-}++quote :: T.Text -> T.Text+quote = T.concatMap quoteT++quoteT :: Char -> T.Text+quoteT '\\' = "\\\\"+quoteT '"'  = "\\\""+quoteT '\n' = "\\n"+quoteT '\t' = "\\t"+quoteT '\r' = "\\r"+quoteT c    = +  let nc = ord c+      +  in if nc > 0xffff +     then T.pack ('\\':'U': numToHex 8 nc)+     else if nc > 0x7e || nc < 0x20+          then T.pack ('\\':'u': numToHex 4 nc)+          else T.singleton c+                      +-- we assume c > 0, n >= 0 and that the input value fits+-- into the requested number of digits+numToHex :: Int -> Int -> String+numToHex c = go []+  where+    go s 0 = replicate (c - length s) '0' ++ s+    go s n = +      let (m,x) = divMod n 16+      in go (iToD x:s) m++    -- Data.Char.intToDigit uses lower-case Hex+    iToD x | x < 10    = intToDigit x+           | otherwise = toUpper $ intToDigit x+      +--------------------------------------------------------------------------------+--+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke+--  All rights reserved.+--+--  This file is part of Swish.+--+--  Swish is free software; you can redistribute it and/or modify+--  it under the terms of the GNU General Public License as published by+--  the Free Software Foundation; either version 2 of the License, or+--  (at your option) any later version.+--+--  Swish is distributed in the hope that it will be useful,+--  but WITHOUT ANY WARRANTY; without even the implied warranty of+--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+--  GNU General Public License for more details.+--+--  You should have received a copy of the GNU General Public License+--  along with Swish; if not, write to:+--    The Free Software Foundation, Inc.,+--    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+--+--------------------------------------------------------------------------------
+ src/Swish/RDF/Formatter/Turtle.hs view
@@ -0,0 +1,877 @@+{-# LANGUAGE OverloadedStrings #-}++--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  Turtle+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke+--  License     :  GPL V2+--+--  Maintainer  :  Douglas Burke+--  Stability   :  experimental+--  Portability :  OverloadedStrings+--+--  This Module implements a Turtle formatter +--  for an 'RDFGraph' value.+--+--  REFERENCES:+--+--  - \"Turtle, Terse RDF Triple Language\",+--    W3C Working Draft 09 August 2011 (<http://www.w3.org/TR/2011/WD-turtle-20110809/>)+--    <http://www.w3.org/TR/turtle/>+--+--------------------------------------------------------------------------------++{-+TODO:++The code used to determine whether a blank node can be written+using the "[]" short form could probably take advantage of the+GraphPartition module.++-}++module Swish.RDF.Formatter.Turtle+    ( NodeGenLookupMap+    , formatGraphAsText+    , formatGraphAsLazyText+    , formatGraphAsBuilder+    , formatGraphIndent  +    , formatGraphDiag+      +    --  -- * Auxillary routines+    -- , quoteText+    )+where++import Swish.GraphClass (Arc(..))+import Swish.Namespace (ScopedName, getScopeLocal, getScopeURI)+import Swish.QName (getLName)++import Swish.RDF.Graph (+  RDFGraph, RDFLabel(..)+  , NamespaceMap, RevNamespaceMap+  -- emptyNamespaceMap,+  -- FormulaMap, emptyFormulaMap,+  , getArcs+  , labels+  -- , setNamespaces+  , getNamespaces,+  -- getFormulae,+  emptyRDFGraph+  , quote+  , quoteT+  , resRdfFirst, resRdfRest, resRdfNil+  )++import Swish.RDF.Vocabulary ( fromLangTag +                            , rdfType+                            , rdfNil+                            , xsdBoolean, xsdDecimal, xsdInteger, xsdDouble +                            )++import Control.Monad (liftM, when)+import Control.Monad.State (State, modify, get, put, runState)++import Data.Char (isDigit)+import Data.List (foldl', delete, groupBy, partition, sort, intersperse)+import Data.LookupMap (LookupEntryClass(..), LookupMap)+import Data.LookupMap (emptyLookupMap, reverseLookupMap, listLookupMap, mapFind, mapFindMaybe, mapAdd, mapMerge)+import Data.Monoid (Monoid(..))+import Data.Word (Word32)++-- it strikes me that using Lazy Text here is likely to be+-- wrong; however I have done no profiling to back this+-- assumption up!++import qualified Data.Text as T+import qualified Data.Text.Lazy as L+import qualified Data.Text.Lazy.Builder as B++-- temporary conversion+quoteB :: Bool -> String -> B.Builder+quoteB f v = B.fromString $ quote f v++----------------------------------------------------------------------+--  Graph formatting state monad+----------------------------------------------------------------------+--+--  The graph to be formatted is carried as part of the formatting+--  state, so that decisions about what needs to be formatted can+--  themselves be based upon and reflected in the state (e.g. if a+--  decision is made to include a blank node inline, it can be removed+--  from the graph state that remains to be formatted).++type SubjTree lb = [(lb,PredTree lb)]+type PredTree lb = [(lb,[lb])]++data TurtleFormatterState = TFS+    { indent    :: B.Builder+    , lineBreak :: Bool+    , graph     :: RDFGraph+    , subjs     :: SubjTree RDFLabel+    , props     :: PredTree RDFLabel   -- for last subject selected+    , objs      :: [RDFLabel]          -- for last property selected+    -- , formAvail :: FormulaMap RDFLabel+    -- , formQueue :: [(RDFLabel,RDFGraph)]+    , nodeGenSt :: NodeGenState+    , bNodesCheck   :: [RDFLabel]      -- these bNodes are not to be converted to '[..]' format+    , traceBuf  :: [String]+    }+             +type Formatter a = State TurtleFormatterState a++emptyTFS :: NodeGenState -> TurtleFormatterState+emptyTFS ngs = TFS+    { indent    = "\n"+    , lineBreak = False+    , graph     = emptyRDFGraph+    , subjs     = []+    , props     = []+    , objs      = []+    -- , formAvail = emptyFormulaMap+    -- , formQueue = []+    , nodeGenSt = ngs+    , bNodesCheck   = []+    , traceBuf  = []+    }++-- | Node name generation state information.+type NodeGenLookupMap = LookupMap (RDFLabel, Word32)++data NodeGenState = Ngs+    { prefixes  :: NamespaceMap+    , nodeMap   :: NodeGenLookupMap+    , nodeGen   :: Word32+    }++emptyNgs :: NodeGenState+emptyNgs = Ngs+    { prefixes  = emptyLookupMap+    , nodeMap   = emptyLookupMap+    , nodeGen   = 0+    }++-- simple context for label creation+-- (may be a temporary solution to the problem+--  of label creation)+--+data LabelContext = SubjContext | PredContext | ObjContext+                    deriving (Eq, Show)++getIndent :: Formatter B.Builder+getIndent = indent `liftM` get++setIndent :: B.Builder -> Formatter ()+setIndent ind = modify $ \st -> st { indent = ind }++getLineBreak :: Formatter Bool+getLineBreak = lineBreak `liftM` get++setLineBreak :: Bool -> Formatter ()+setLineBreak brk = modify $ \st -> st { lineBreak = brk }++getNgs :: Formatter NodeGenState+getNgs = nodeGenSt `liftM` get++setNgs :: NodeGenState -> Formatter ()+setNgs ngs = modify $ \st -> st { nodeGenSt = ngs }++getPrefixes :: Formatter NamespaceMap+getPrefixes = prefixes `liftM` getNgs++getSubjs :: Formatter (SubjTree RDFLabel)+getSubjs = subjs `liftM` get++setSubjs :: SubjTree RDFLabel -> Formatter ()+setSubjs sl = modify $ \st -> st { subjs = sl }++getProps :: Formatter (PredTree RDFLabel)+getProps = props `liftM` get++setProps :: PredTree RDFLabel -> Formatter ()+setProps ps = modify $ \st -> st { props = ps }++{-+getObjs :: Formatter ([RDFLabel])+getObjs = objs `liftM` get++setObjs :: [RDFLabel] -> Formatter ()+setObjs os = do+  st <- get+  put $ st { objs = os }+-}++getBnodesCheck :: Formatter [RDFLabel]+getBnodesCheck = bNodesCheck `liftM` get++{-+addTrace :: String -> Formatter ()+addTrace tr = do+  st <- get+  put $ st { traceBuf = tr : traceBuf st }+-}+  +{-+queueFormula :: RDFLabel -> Formatter ()+queueFormula fn = do+  st <- get+  let fa = formAvail st+      newState fv = st {+                      formAvail = mapDelete fa fn,+                      formQueue = (fn,fv) : formQueue st+                    }+  case mapFindMaybe fn fa of+    Nothing -> return ()+    Just v -> put (newState v) >> return ()+-}++{-+Return the graph associated with the label and delete it+from the store, if there is an association, otherwise+return Nothing.++extractFormula :: RDFLabel -> Formatter (Maybe RDFGraph)+extractFormula fn = do+  st <- get+  let fa = formAvail st+      newState = st { formAvail=mapDelete fa fn }+  case mapFindMaybe fn fa of+    Nothing -> return Nothing+    Just fv -> put newState >> return (Just fv)++-}++{-+moreFormulae :: Formatter Bool+moreFormulae =  do+  st <- get+  return $ not $ null (formQueue st)++nextFormula :: Formatter (RDFLabel,RDFGraph)+nextFormula = do+  st <- get+  let (nf : fq) = formQueue st+  put $ st { formQueue = fq }+  return nf++-}++-- list has a length of 1+len1 :: [a] -> Bool+len1 (_:[]) = True+len1 _ = False++{-|+Given a set of statements and a label, return the details of the+RDF collection referred to by label, or Nothing.++For label to be considered as representing a collection we require the+following conditions to hold (this is only to support the+serialisation using the '(..)' syntax and does not make any statement+about semantics of the statements with regard to RDF Collections):++  - there must be one rdf_first and one rdfRest statement+  - there must be no other predicates for the label++-} +getCollection ::          +  SubjTree RDFLabel -- ^ statements organized by subject+  -> RDFLabel -- ^ does this label represent a list?+  -> Maybe (SubjTree RDFLabel, [RDFLabel], [RDFLabel])+     -- ^ the statements with the elements removed; the+     -- content elements of the collection (the objects of the rdf:first+     -- predicate) and the nodes that represent the spine of the+     -- collection (in reverse order, unlike the actual contents which are in+     -- order).+getCollection subjList lbl = go subjList lbl ([],[]) +    where+      go sl l (cs,ss) | l == resRdfNil = Just (sl, reverse cs, ss)+                      | otherwise = do+        (pList1, sl') <- removeItem sl l+        (pFirst, pList2) <- removeItem pList1 resRdfFirst+        (pNext, pList3) <- removeItem pList2 resRdfRest++        -- QUS: could I include these checks implicitly in the pattern matches above?+        -- ie instrad of (pFirst, pos1) <- ..+        -- have ([content], pos1) <- ...+        -- ?+        if and [len1 pFirst, len1 pNext, null pList3]+          then go sl' (head pNext) (head pFirst : cs, l : ss)+          else Nothing++{-+TODO:++Should we change the preds/objs entries as well?++-}+extractList :: LabelContext -> RDFLabel -> Formatter (Maybe [RDFLabel])+extractList lctxt ln = do+  osubjs <- getSubjs+  oprops <- getProps+  let mlst = getCollection osubjs' ln++      -- we only want to send in rdf:first/rdf:rest here+      fprops = filter ((`elem` [resRdfFirst, resRdfRest]) . fst) oprops++      osubjs' =+          case lctxt of+            SubjContext -> (ln, fprops) : osubjs+            _ -> osubjs ++      -- tr = "extractList " ++ show ln ++ " (" ++ show lctxt ++ ")\n -> osubjs= " ++ show osubjs ++ "\n -> opreds= " ++ show oprops ++ "\n -> mlst= " ++ show mlst ++ "\n"+  -- addTrace tr+  case mlst of+    -- sl is guaranteed to be free of (ln,fprops) here if lctxt is SubjContext+    Just (sl,ls,_) -> do+              setSubjs sl+              when (lctxt == SubjContext) $ setProps $ filter ((`notElem` [resRdfFirst, resRdfRest]) . fst) oprops+              return (Just ls)++    Nothing -> return Nothing+  +{-|+Removes the first occurrence of the item from the+association list, returning it's contents and the rest+of the list, if it exists.+-}+removeItem :: (Eq a) => [(a,b)] -> a -> Maybe (b, [(a,b)])+removeItem os x =+  let (as, bs) = break (\a -> fst a == x) os+  in case bs of+    ((_,b):bbs) -> Just (b, as ++ bbs)+    [] -> Nothing++----------------------------------------------------------------------+--  Define a top-level formatter function:+----------------------------------------------------------------------++-- | Convert the graph to text.+formatGraphAsText :: RDFGraph -> T.Text+formatGraphAsText = L.toStrict . formatGraphAsLazyText++-- | Convert the graph to text.+formatGraphAsLazyText :: RDFGraph -> L.Text+formatGraphAsLazyText = B.toLazyText . formatGraphAsBuilder+  +-- | Convert the graph to a Builder.+formatGraphAsBuilder :: RDFGraph -> B.Builder+formatGraphAsBuilder = formatGraphIndent "\n" True+  +-- | Convert the graph to a builder using the given indentation text.+formatGraphIndent ::+    B.Builder     -- ^ indentation text+    -> Bool       -- ^ are prefixes to be generated?+    -> RDFGraph   -- ^ graph+    -> B.Builder+formatGraphIndent indnt flag gr = +  let (res, _, _, _) = formatGraphDiag indnt flag gr+  in res+  +-- | Format graph and return additional information.+formatGraphDiag :: +  B.Builder  -- ^ indentation+  -> Bool    -- ^ are prefixes to be generated?+  -> RDFGraph +  -> (B.Builder, NodeGenLookupMap, Word32, [String])+formatGraphDiag indnt flag gr = +  let fg  = formatGraph indnt " .\n" False flag gr+      ngs = emptyNgs {+        prefixes = emptyLookupMap,+        nodeGen  = findMaxBnode gr+        }+             +      (out, fgs) = runState fg (emptyTFS ngs)+      ogs        = nodeGenSt fgs+  +  in (out, nodeMap ogs, nodeGen ogs, traceBuf fgs)++----------------------------------------------------------------------+--  Formatting as a monad-based computation+----------------------------------------------------------------------++formatGraph :: +  B.Builder     -- indentation string+  -> B.Builder  -- text to be placed after final statement+  -> Bool       -- True if a line break is to be inserted at the start+  -> Bool       -- True if prefix strings are to be generated+  -> RDFGraph   -- graph to convert+  -> Formatter B.Builder+formatGraph ind end dobreak dopref gr = do+  setIndent ind+  setLineBreak dobreak+  setGraph gr+  +  fp <- if dopref+        then formatPrefixes (getNamespaces gr)+        else return mempty+  more <- moreSubjects+  if more+    then do+      fr <- formatSubjects+      return $ mconcat [fp, fr, end]+    else return fp++formatPrefixes :: NamespaceMap -> Formatter B.Builder+formatPrefixes pmap = do+  let mls = map (pref . keyVal) (listLookupMap pmap)+  ls <- sequence mls+  return $ mconcat ls+    where+      pref (Just p,u) = nextLine $ mconcat ["@prefix ", B.fromText p, ": <", quoteB True (show u), "> ."]+      pref (_,u)      = nextLine $ mconcat ["@prefix : <", quoteB True (show u), "> ."]++{-+NOTE:+I expect there to be confusion below where I need to+convert from Text to Builder+-}++formatSubjects :: Formatter B.Builder+formatSubjects = do+  sb    <- nextSubject+  sbstr <- formatLabel SubjContext sb+  +  flagP <- moreProperties+  if flagP+    then do+      prstr <- formatProperties sb sbstr+      flagS <- moreSubjects+      if flagS+        then do+          fr <- formatSubjects+          return $ mconcat [prstr, " .", fr]+        else return prstr+           +    else do+      txt <- nextLine sbstr+    +      flagS <- moreSubjects+      if flagS+        then do+          fr <- formatSubjects+          return $ mconcat [txt, " .", fr]+        else return txt++{-+TODO: now we are throwing a Builder around it is awkward to+get the length of the text to calculate the indentation++So++  a) change the indentation scheme+  b) pass around text instead of builder++mkIndent :: L.Text -> L.Text+mkIndent inVal = L.replicate (L.length inVal) " "+-}++hackIndent :: B.Builder+hackIndent = "    "++formatProperties :: RDFLabel -> B.Builder -> Formatter B.Builder+formatProperties sb sbstr = do+  pr <- nextProperty sb+  prstr <- formatLabel PredContext pr+  obstr <- formatObjects sb pr $ mconcat [sbstr, " ", prstr]+  more  <- moreProperties+  let sbindent = hackIndent -- mkIndent sbstr+  if more+    then do+      fr <- formatProperties sb sbindent+      nl <- nextLine $ obstr `mappend` " ;"+      return $ nl `mappend` fr+    else nextLine obstr++formatObjects :: RDFLabel -> RDFLabel -> B.Builder -> Formatter B.Builder+formatObjects sb pr prstr = do+  ob    <- nextObject sb pr+  obstr <- formatLabel ObjContext ob+  more  <- moreObjects+  if more+    then do+      let prindent = hackIndent -- mkIndent prstr+      fr <- formatObjects sb pr prindent+      nl <- nextLine $ mconcat [prstr, " ", obstr, ","]+      return $ nl `mappend` fr+    else return $ mconcat [prstr, " ", obstr]++{-+Add a list inline. We are given the labels that constitute+the list, in order, so just need to display them surrounded+by ().+-}+insertList :: [RDFLabel] -> Formatter B.Builder+insertList [] = return "()" -- not convinced this can happen+insertList xs = do+  ls <- mapM (formatLabel ObjContext) xs+  return $ mconcat ("( " : intersperse " " ls) `mappend` " )"+    +{-+Add a blank node inline.+-}++insertBnode :: LabelContext -> RDFLabel -> Formatter B.Builder+insertBnode SubjContext lbl = do+  -- a safety check+  flag <- moreProperties+  if flag+    then do+      txt <- (`mappend` "\n") `liftM` formatProperties lbl ""+      return $ mconcat ["[] ", txt]+    else error $ "Internal error: expected properties with label: " ++ show lbl++insertBnode _ lbl = do+  ost <- get+  let osubjs = subjs ost+      oprops = props ost+      oobjs  = objs  ost++      (bsubj, rsubjs) = partition ((== lbl) . fst) osubjs++      rprops = case bsubj of+                 [(_,rs)] -> rs+                 _ -> []++      -- we essentially want to create a new subgraph+      -- for this node but it's not as simple as that since+      -- we could have something like+      --     :a :b [ :foo [ :bar "xx" ] ]+      -- so we still need to carry around the whole graph+      --+      nst = ost { subjs = rsubjs,+                  props = rprops,+                  objs  = []+                }++  put nst+  flag <- moreProperties+  txt <- if flag+         then (`mappend` "\n") `liftM` formatProperties lbl ""+         else return ""++  -- TODO: how do we restore the original set up?+  --       I can't believe the following is sufficient+  --+  nst' <- get+  let slist  = map fst $ subjs nst'+      nsubjs = filter (\(l,_) -> l `elem` slist) osubjs++  put $ nst' { subjs = nsubjs,+                       props = oprops, +                       objs  = oobjs+             }++  -- TODO: handle indentation?+  return $ mconcat ["[", txt, "]"]+  +----------------------------------------------------------------------+--  Formatting helpers+----------------------------------------------------------------------++setGraph :: RDFGraph -> Formatter ()+setGraph gr = do+  st <- get++  let ngs0 = nodeGenSt st+      pre' = mapMerge (prefixes ngs0) (getNamespaces gr)+      ngs' = ngs0 { prefixes = pre' }+      arcs = sortArcs $ getArcs gr+      nst  = st  { graph     = gr+                 , subjs     = arcTree arcs+                 , props     = []+                 , objs      = []+                 -- , formAvail = getFormulae gr+                 , nodeGenSt = ngs'+                 , bNodesCheck   = countBnodes arcs+                 }++  put nst++hasMore :: (TurtleFormatterState -> [b]) -> Formatter Bool+hasMore lens = (not . null . lens) `liftM` get++moreSubjects :: Formatter Bool+moreSubjects = hasMore subjs+-- moreSubjects = (not . null . subjs) `liftM` get++moreProperties :: Formatter Bool+moreProperties = hasMore props+-- moreProperties = (not . null . props) `liftM` get++moreObjects :: Formatter Bool+moreObjects = hasMore objs+-- moreObjects = (not . null . objs) `liftM` get++nextSubject :: Formatter RDFLabel+nextSubject = do+  st <- get++  let sb:sbs = subjs st+      nst = st  { subjs = sbs+                , props = snd sb+                , objs  = []+                }++  put nst+  return $ fst sb++nextProperty :: RDFLabel -> Formatter RDFLabel+nextProperty _ = do+  st <- get++  let pr:prs = props st+      nst = st  { props = prs+                 , objs  = snd pr+                 }++  put nst+  return $ fst pr++nextObject :: RDFLabel -> RDFLabel -> Formatter RDFLabel+nextObject _ _ = do+  st <- get++  let ob:obs = objs st+      nst = st { objs = obs }++  put nst+  return ob++nextLine :: B.Builder -> Formatter B.Builder+nextLine str = do+  ind <- getIndent+  brk <- getLineBreak+  if brk+    then return $ ind `mappend` str+    else do+      --  After first line, always insert line break+      setLineBreak True+      return str++--  Format a label+--  Most labels are simply displayed as provided, but there are a+--  number of wrinkles to take care of here:+--  (a) blank nodes automatically allocated on input, with node+--      identifiers of the form of a digit string nnn.  These are+--      not syntactically valid, and are reassigned node identifiers+--      of the form _nnn, where nnn is chosen so that is does not+--      clash with any other identifier in the graph.+--  (b) URI nodes:  if possible, replace URI with qname,+--      else display as <uri>+--  (c) formula nodes (containing graphs).+--  (d) use the "special-case" formats for integer/float/double+--      literals.      +--      +--  [[[TODO:]]]+--  (d) generate multi-line literals when appropriate+--+-- This is being updated to produce inline formula, lists and     +-- blank nodes. The code is not efficient.+--+--+-- Note: There is a lot less customisation possible in Turtle than N3.+--      ++formatLabel :: LabelContext -> RDFLabel -> Formatter B.Builder++{-+The "[..]" conversion is done last, after "()" and "{}" checks.+-}+formatLabel lctxt lab@(Blank (_:_)) = do+  mlst <- extractList lctxt lab+  case mlst of+    Just lst -> insertList lst+    Nothing -> do+      -- NOTE: unlike N3 we do not properly handle "formula"/named graphs+      -- also we only expand out bnodes into [...] format when it's a object.+      -- although we need to handle [] for the subject.+      nb1 <- getBnodesCheck+      if lctxt /= PredContext && lab `notElem` nb1+        then insertBnode lctxt lab+        else formatNodeId lab++-- formatLabel _ lab@(Res sn) = +formatLabel ctxt (Res sn)+  | ctxt == PredContext && sn == rdfType = return "a"+  | ctxt == ObjContext  && sn == rdfNil  = return "()"+  | otherwise = do+  pr <- getPrefixes+  let nsuri  = getScopeURI sn+      local  = getLName $ getScopeLocal sn+      premap = reverseLookupMap pr :: RevNamespaceMap+      prefix = mapFindMaybe nsuri premap+          +      name   = case prefix of+        Just (Just p) -> B.fromText $ quoteT True $ mconcat [p, ":", local] -- TODO: what are quoting rules for QNames+        _ -> mconcat ["<", quoteB True (show nsuri ++ T.unpack local), ">"]+      +  return name++-- The canonical notation for xsd:double in XSD, with an upper-case E,+-- does not match the syntax used in N3, so we need to convert here.     +-- Rather than converting back to a Double and then displaying that       +-- we just convert E to e for now.      +--      +formatLabel _ (Lit lit) = return $ quoteText lit+formatLabel _ (LangLit lit lcode) = return $ quoteText lit `mappend` "@" `mappend` B.fromText (fromLangTag lcode)+formatLabel _ (TypedLit lit dtype)+    | dtype == xsdDouble = return $ B.fromText $ T.toLower lit+    | dtype `elem` [xsdBoolean, xsdDecimal, xsdInteger] = return $ B.fromText lit+    | otherwise = return $ quoteText lit `mappend` "^^" `mappend` showScopedName dtype++formatLabel _ lab = return $ B.fromString $ show lab++{-|+Convert text into a format for display in Turtle. The idea+is to use one double quote unless three are needed, and to+handle adding necessary @\\@ characters, or conversion+for Unicode characters.+-}+quoteText :: T.Text -> B.Builder+quoteText txt = +  let st = T.unpack txt -- TODO: fix+      qst = quoteB (n==1) st+      n = if '\n' `elem` st || '"' `elem` st then 3 else 1+      qch = B.fromString (replicate n '"')+  in mconcat [qch, qst, qch]++formatNodeId :: RDFLabel -> Formatter B.Builder+formatNodeId lab@(Blank (lnc:_)) =+    if isDigit lnc then mapBlankNode lab else return $ B.fromString $ show lab+formatNodeId other = error $ "formatNodeId not expecting a " ++ show other -- to shut up -Wall++mapBlankNode :: RDFLabel -> Formatter B.Builder+mapBlankNode lab = do+  ngs <- getNgs+  let cmap = nodeMap ngs+      cval = nodeGen ngs+  nv <- case mapFind 0 lab cmap of+    0 -> do +      let nval = succ cval+          nmap = mapAdd cmap (lab, nval)+      setNgs $ ngs { nodeGen = nval, nodeMap = nmap }+      return nval+      +    n -> return n+  +  -- TODO: is this what we want?+  return $ "_:swish" `mappend` B.fromString (show nv)++-- TODO: need to be a bit more clever with this than we did in NTriples+--       not sure the following counts as clever enough ...+--  +showScopedName :: ScopedName -> B.Builder+{-+showScopedName (ScopedName n l) = +  let uri = nsURI n ++ l+  in quote uri+-}+showScopedName = quoteB True . show++----------------------------------------------------------------------+--  Graph-related helper functions+----------------------------------------------------------------------++newtype SortedArcs lb = SA [Arc lb]++sortArcs :: (Ord lb) => [Arc lb] -> SortedArcs lb+sortArcs = SA . sort++--  Rearrange a list of arcs into a tree of pairs which group together+--  all statements for a single subject, and similarly for multiple+--  objects of a common predicate.+--+arcTree :: (Eq lb) => SortedArcs lb -> SubjTree lb+arcTree (SA as) = commonFstEq (commonFstEq id) $ map spopair as+    where+        spopair (Arc s p o) = (s,(p,o))++{-+arcTree as = map spopair $ sort as+    where+        spopair (Arc s p o) = (s,[(p,[o])])+-}++--  Rearrange a list of pairs so that multiple occurrences of the first+--  are commoned up, and the supplied function is applied to each sublist+--  with common first elements to obtain the corresponding second value+commonFstEq :: (Eq a) => ( [b] -> c ) -> [(a,b)] -> [(a,c)]+commonFstEq f ps =+    [ (fst $ head sps,f $ map snd sps) | sps <- groupBy fstEq ps ]+    where+        fstEq (f1,_) (f2,_) = f1 == f2++findMaxBnode :: RDFGraph -> Word32+findMaxBnode = maximum . map getAutoBnodeIndex . labels++getAutoBnodeIndex   :: RDFLabel -> Word32+getAutoBnodeIndex (Blank ('_':lns)) = res where+    -- cf. prelude definition of read s ...+    res = case [x | (x,t) <- reads lns, ("","") <- lex t] of+            [x] -> x+            _   -> 0+getAutoBnodeIndex _                   = 0++{-+Find all blank nodes that occur+  - any number of times as a subject+  - 0 or 1 times as an object++Such nodes can be output using the "[..]" syntax. To make it simpler+to check we actually store those nodes that can not be expanded.++Note that we do not try and expand any bNode that is used in+a predicate position.++Should probably be using the SubjTree RDFLabel structure but this+is easier for now.++-}++countBnodes :: SortedArcs RDFLabel -> [RDFLabel]+countBnodes (SA as) = snd (foldl' ctr ([],[]) as)+    where+      -- first element of tuple are those blank nodes only seen once,+      -- second element those blank nodes seen multiple times+      --+      inc b@(b1s,bms) l@(Blank _) | l `elem` bms = b+                                  | l `elem` b1s = (delete l b1s, l:bms)+                                  | otherwise    = (l:b1s, bms)+      inc b _ = b++      -- if the bNode appears as a predicate we instantly add it to the+      -- list of nodes not to expand, even if only used once+      incP b@(b1s,bms) l@(Blank _) | l `elem` bms = b+                                   | l `elem` b1s = (delete l b1s, l:bms)+           			   | otherwise    = (b1s, l:bms)+      incP b _ = b++      ctr orig (Arc _ p o) = inc (incP orig p) o++--------------------------------------------------------------------------------+--+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke+--  All rights reserved.+--+--  This file is part of Swish.+--+--  Swish is free software; you can redistribute it and/or modify+--  it under the terms of the GNU General Public License as published by+--  the Free Software Foundation; either version 2 of the License, or+--  (at your option) any later version.+--+--  Swish is distributed in the hope that it will be useful,+--  but WITHOUT ANY WARRANTY; without even the implied warranty of+--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+--  GNU General Public License for more details.+--+--  You should have received a copy of the GNU General Public License+--  along with Swish; if not, write to:+--    The Free Software Foundation, Inc.,+--    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+--+--------------------------------------------------------------------------------
+ src/Swish/RDF/Graph.hs view
@@ -0,0 +1,1458 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE OverloadedStrings #-}++--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  Graph+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke+--  License     :  GPL V2+--+--  Maintainer  :  Douglas Burke+--  Stability   :  experimental+--  Portability :  CPP, FlexibleInstances, MultiParamTypeClasses, TypeSynonymInstances, OverloadedStrings+--+--  This module defines a memory-based RDF graph instance.+--+--------------------------------------------------------------------------------++------------------------------------------------------------+-- Simple labelled directed graph value+------------------------------------------------------------++module Swish.RDF.Graph+    ( +      -- * Labels+      RDFLabel(..), ToRDFLabel(..), FromRDFLabel(..)+    , isLiteral, isUntypedLiteral, isTypedLiteral, isXMLLiteral+    , isDatatyped, isMemberProp, isUri, isBlank, isQueryVar+    , getLiteralText, getScopedName, makeBlank+    , quote+    , quoteT+      +      -- * RDF Graphs+    , RDFTriple+    , toRDFTriple, fromRDFTriple+    , NSGraph(..)+    , RDFGraph+    , toRDFGraph, emptyRDFGraph {-, updateRDFGraph-}+    , NamespaceMap, RevNamespaceMap, RevNamespace+    , emptyNamespaceMap+    , LookupFormula(..), Formula, FormulaMap, emptyFormulaMap+    , addArc, merge+    , allLabels, allNodes, remapLabels, remapLabelList+    , newNode, newNodes+    , setNamespaces, getNamespaces+    , setFormulae, getFormulae, setFormula, getFormula+      +    -- * Re-export from GraphClass+    --+    -- | Note that @asubj@, @apred@ and @aobj@ have been+    -- removed in version @0.7.0.0@; use 'arcSubj', 'arcPred'+    -- or 'arcObj' instead.+    --+    , LDGraph(..), Label (..), Arc(..)+    , arc, Selector+      +    -- * Selected RDFLabel values+    --                                +    -- | The 'ToRDFLabel' instance of 'ScopedName' can also be used                                     +    -- to easily construct 'RDFLabel' versions of the terms defined+    -- in `Swish.RDF.Vocabulary`.+    +    -- ** RDF terms                                          +    --                                          +    -- | These terms are described in <http://www.w3.org/TR/rdf-syntax-grammar/>;                                          +    -- the version used is \"W3C Recommendation 10 February 2004\", <http://www.w3.org/TR/2004/REC-rdf-syntax-grammar-20040210/>.+    --                                          +    -- Some terms are listed within the RDF Schema terms below since their definition                                            +    -- is given within the RDF Schema document.                                          +    --                                          +    , resRdfRDF                                          +    , resRdfDescription      +    , resRdfID+    , resRdfAbout+    , resRdfParseType+    , resRdfResource+    , resRdfLi+    , resRdfNodeID+    , resRdfDatatype+    , resRdf1, resRdf2, resRdfn+    -- ** RDF Schema terms+    --                                 +    -- | These are defined by <http://www.w3.org/TR/rdf-schema/>; the version+    -- used is \"W3C Recommendation 10 February 2004\", <http://www.w3.org/TR/2004/REC-rdf-schema-20040210/>.+                        +    -- *** Classes+    --                                 +    -- | See the \"Classes\" section at <http://www.w3.org/TR/rdf-schema/#ch_classes> for more information.+    , resRdfsResource+    , resRdfsClass+    , resRdfsLiteral+    , resRdfsDatatype+    , resRdfXMLLiteral+    , resRdfProperty+    -- *** Properties+    --                                 +    -- | See the \"Properties\" section at <http://www.w3.org/TR/rdf-schema/#ch_classes> for more information.+    , resRdfsRange+    , resRdfsDomain+    , resRdfType+    , resRdfsSubClassOf+    , resRdfsSubPropertyOf+    , resRdfsLabel+    , resRdfsComment+    -- *** Containers+    --+    -- | See the \"Container Classes and Properties\" section at <http://www.w3.org/TR/rdf-schema/#ch_containervocab>.+    , resRdfsContainer+    , resRdfBag+    , resRdfSeq                                 +    , resRdfAlt  +    , resRdfsContainerMembershipProperty+    , resRdfsMember+    -- *** Collections+    --+    -- | See the \"Collections\" section at <http://www.w3.org/TR/rdf-schema/#ch_collectionvocab>.+    , resRdfList    +    , resRdfFirst+    , resRdfRest +    , resRdfNil +    -- *** Reification Vocabulary +    --  +    -- | See the \"Reification Vocabulary\" section at <http://www.w3.org/TR/rdf-schema/#ch_reificationvocab>.+    , resRdfStatement  +    , resRdfSubject  +    , resRdfPredicate  +    , resRdfObject  +    -- *** Utility Properties +    --  +    -- | See the \"Utility Properties\" section at <http://www.w3.org/TR/rdf-schema/#ch_utilvocab>.+    , resRdfsSeeAlso+    , resRdfsIsDefinedBy+    , resRdfValue  +    +    -- ** OWL     +    , resOwlSameAs+                    +    -- ** Miscellaneous     +    , resRdfdGeneralRestriction+    , resRdfdOnProperties, resRdfdConstraint, resRdfdMaxCardinality+    , resLogImplies+      +    -- * Exported for testing+    , grMatchMap, grEq+    , mapnode, maplist+    )+    where++import Swish.Namespace+    ( Namespace, makeNamespace, getNamespaceTuple+    , getScopedNameURI+    , ScopedName+    , getScopeLocal, getScopeNamespace+    , getQName+    , makeQNameScopedName+    , makeURIScopedName+    , nullScopedName+    )++import Swish.RDF.Vocabulary+{-    +    ( namespaceRDF+    , langTag, isLang+    , rdfType+    , rdfFirst, rdfRest, rdfNil, rdfXMLLiteral+    , rdfsMember+    , rdfdGeneralRestriction+    , rdfdOnProperties, rdfdConstraint, rdfdMaxCardinality+    , owlSameAs, logImplies+    , xsdBoolean, xsdDecimal, xsdFloat, xsdDouble, xsdInteger+    , xsdDateTime, xsdDate                                                +    )+-}++import Swish.GraphClass (LDGraph(..), Label (..), Arc(..), Selector)+import Swish.GraphClass (arc, arcLabels)+import Swish.GraphMatch (LabelMap, ScopedLabel(..))+import Swish.GraphMatch (graphMatch)+import Swish.QName (QName, getLName)++import Control.Applicative (Applicative, liftA, (<$>), (<*>))++import Network.URI (URI)++import Data.Monoid (Monoid(..))+import Data.Maybe (mapMaybe)+import Data.Char (ord, isDigit)+import Data.Hashable (hashWithSalt)+import Data.List (intersect, union, foldl')+import Data.LookupMap (LookupMap(..), LookupEntryClass(..))+import Data.LookupMap (listLookupMap, mapFind, mapFindMaybe, mapReplace, mapAddIfNew, mapVals, mapKeys )+import Data.Ord (comparing)+import Data.Word (Word32)++import Data.String (IsString(..))+import Data.Time (UTCTime, Day, ParseTime, parseTime, formatTime)++import System.Locale (defaultTimeLocale)  ++import Text.Printf++import qualified Data.Foldable as Foldable+import qualified Data.Traversable as Traversable++import qualified Data.Text as T+import qualified Data.Text.Read as T+-- import qualified Data.Text.Lazy as L+-- import Data.Text.Format (format)+-- import Data.Text.Buildable+-- import Data.Text.Format.Types (Only(..))++#if defined(__GLASGOW_HASKELL__) && (__GLASGOW_HASKELL__ >= 701)+import Data.Tuple (swap)+#else+swap :: (a,b) -> (b,a)+swap (a,b) = (b,a)+#endif++-- | RDF graph node values+--+--  cf. <http://www.w3.org/TR/rdf-concepts/#section-Graph-syntax>+--+--  This is extended from the RDF abstract graph syntax in the+--  following ways:+--+--  (a) a graph can be part of a resource node or blank node+--      (cf. Notation3 formulae)+--+--  (b) a \"variable\" node option is distinguished from a+--      blank node.+--      I have found this useful for encoding and handling+--      queries, even though query variables can be expressed+--      as blank nodes.+--+--  (c) a \"NoNode\" option is defined.+--      This might otherwise be handled by @Maybe (RDFLabel g)@.+--+-- Prior to version @0.7.0.0@, literals were represented by a+-- single constructor, @Lit@, with an optional argument. Language+-- codes for literals was also stored as a 'ScopedName' rather than+-- as a 'LanguageTag'.+--+data RDFLabel =+      Res ScopedName                    -- ^ resource+    | Lit T.Text                        -- ^ plain literal (<http://www.w3.org/TR/rdf-concepts/#dfn-plain-literal>)+    | LangLit T.Text LanguageTag        -- ^ plain literal+    | TypedLit T.Text ScopedName        -- ^ typed literal (<http://www.w3.org/TR/rdf-concepts/#dfn-typed-literal>)+    | Blank String                      -- ^ blank node+    | Var String                        -- ^ variable (not used in ordinary graphs)+    | NoNode                            -- ^ no node  (not used in ordinary graphs)++-- | Define equality of nodes possibly based on different graph types.+--+-- The equality of literals is taken from section 6.5.1 ("Literal+-- Equality") of the RDF Concepts and Abstract Document,+-- <http://www.w3.org/TR/2004/REC-rdf-concepts-20040210/#section-Literal-Equality>.+--+instance Eq RDFLabel where+    Res q1   == Res q2   = q1 == q2+    Blank b1 == Blank b2 = b1 == b2+    Var v1   == Var v2   = v1 == v2++    Lit s1         == Lit s2         = s1 == s2+    LangLit s1 l1  == LangLit s2 l2  = s1 == s2 && l1 == l2+    TypedLit s1 t1 == TypedLit s2 t2 = s1 == s2 && t1 == t2++    _  == _ = False++instance Show RDFLabel where+    show (Res sn)           = show sn+    show (Lit st)           = quote1Str st+    show (LangLit st lang)  = quote1Str st ++ "@"  ++ T.unpack (fromLangTag lang)+    show (TypedLit st dtype) +        | dtype `elem` [xsdBoolean, xsdDouble, xsdDecimal, xsdInteger] = T.unpack st+        | otherwise  = quote1Str st ++ "^^" ++ show dtype++    {-+    show (Lit st (Just nam))+        | isLang nam = quote1Str st ++ "@"  ++ T.unpack (langTag nam)+        | nam `elem` [xsdBoolean, xsdDouble, xsdDecimal, xsdInteger] = T.unpack st+        | otherwise  = quote1Str st ++ "^^" ++ show nam+    -}++    show (Blank ln)         = "_:"++ln+    show (Var ln)           = '?' : ln+    show NoNode             = "<NoNode>"++instance Ord RDFLabel where+    -- Optimize some common cases..+    compare (Res sn1)      (Res sn2)      = compare sn1 sn2+    compare (Blank ln1)    (Blank ln2)    = compare ln1 ln2+    compare (Res _)        (Blank _)      = LT+    compare (Blank _)      (Res _)        = GT++    compare (Lit s1)       (Lit s2)       = compare s1 s2++    -- .. else use show string comparison+    compare l1 l2 = comparing show l1 l2+    +    {- <= is not used if compare is provided+    -- Similarly for <=+    (Res qn1)   <= (Res qn2)      = qn1 <= qn2+    (Blank ln1) <= (Blank ln2)    = ln1 <= ln2+    (Res _)     <= (Blank _)      = True+    (Blank _)   <= (Res _)        = False+    l1 <= l2                      = show l1 <= show l2+    -}+    +instance Label RDFLabel where+    labelIsVar (Blank _)    = True+    labelIsVar (Var _)      = True+    labelIsVar _            = False++    getLocal   (Blank loc)  = loc+    getLocal   (Var   loc)  = '?':loc+    getLocal   (Res   sn)   = "Res_" ++ (T.unpack . getLName . getScopeLocal) sn+    getLocal   (NoNode)     = "None"+    getLocal   _            = "Lit_"++    makeLabel  ('?':loc)    = Var loc+    makeLabel  loc          = Blank loc++    labelHash seed lb       = hashWithSalt seed (showCanon lb)++instance IsString RDFLabel where+  fromString = Lit . T.pack++{-|+A type that can be converted to a RDF Label.++The String instance converts to an untyped literal+(so no language tag is assumed).++The `UTCTime` and `Day` instances assume values are in UTC.+ +The conversion for XSD types attempts to use the+canonical form described in section 2.3.1 of+<http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/#lexical-space>.+  +Note that this is similar to+'Swish.RDF.Datatype.toRDFLabel';+the code should probably be combined at some point.+-}++class ToRDFLabel a where+  toRDFLabel :: a -> RDFLabel+  +{-|+A type that can be converted from a RDF Label,+with the possibility of failure.+ +The String instance converts from an untyped literal+(so it can not be used with a string with a language tag).++The following conversions are supported for common XSD+types (out-of-band values result in @Nothing@):++ - @xsd:boolean@ to @Bool@++ - @xsd:integer@ to @Int@ and @Integer@++ - @xsd:float@ to @Float@++ - @xsd:double@ to @Double@++ - @xsd:dateTime@ to @UTCTime@++ - @xsd:date@ to @Day@++Note that this is similar to+'Swish.RDF.Datatype.fromRDFLabel'; +the code should probably be combined at some point.+-}++class FromRDFLabel a where+  fromRDFLabel :: RDFLabel -> Maybe a++-- instances for type conversion to/from RDFLabel+  +-- | This is just @id@.+instance ToRDFLabel RDFLabel where+  toRDFLabel = id+  +-- | This is just @Just . id@.  +instance FromRDFLabel RDFLabel where+  fromRDFLabel = Just . id+  +-- TODO: remove this hack when finished conversion to Text+maybeReadStr :: (Read a) => T.Text -> Maybe a  +maybeReadStr txt = case reads (T.unpack txt) of+  [(val, "")] -> Just val+  _ -> Nothing+  +maybeRead :: T.Reader a -> T.Text -> Maybe a+maybeRead rdr inTxt = +  case rdr inTxt of+    Right (val, "") -> Just val+    _ -> Nothing+    +fLabel :: (T.Text -> Maybe a) -> ScopedName -> RDFLabel -> Maybe a+fLabel conv dtype (TypedLit xs dt) | dt == dtype = conv xs+                                   | otherwise   = Nothing+fLabel _    _     _ = Nothing+  +tLabel :: (Show a) => ScopedName -> (String -> T.Text) -> a -> RDFLabel                      +tLabel dtype conv = flip TypedLit dtype . conv . show                      ++-- | The character is converted to an untyped literal of length one.+instance ToRDFLabel Char where+  toRDFLabel = Lit . T.singleton++-- | The label must be an untyped literal containing a single character.+instance FromRDFLabel Char where+  fromRDFLabel (Lit cs) | T.compareLength cs 1 == EQ = Just (T.head cs)+                        | otherwise = Nothing+  fromRDFLabel _ = Nothing++-- | Strings are converted to untyped literals.+instance ToRDFLabel String where+  toRDFLabel = Lit . T.pack++-- | Only untyped literals are converted to strings.+instance FromRDFLabel String where+  fromRDFLabel (Lit xs) = Just (T.unpack xs)+  fromRDFLabel _        = Nothing++textToBool :: T.Text -> Maybe Bool+textToBool s | s `elem` ["1", "true"]  = Just True+             | s `elem` ["0", "false"] = Just False+             | otherwise               = Nothing++-- | Converts to a literal with a @xsd:boolean@ datatype.+instance ToRDFLabel Bool where+  toRDFLabel b = TypedLit (if b then "true" else "false") xsdBoolean+                                                 +-- | Converts from a literal with a @xsd:boolean@ datatype. The+-- literal can be any of the supported XSD forms - e.g. \"0\" or+-- \"true\".+instance FromRDFLabel Bool where+  fromRDFLabel = fLabel textToBool xsdBoolean++-- fromRealFloat :: (RealFloat a, Buildable a) => ScopedName -> a -> RDFLabel+fromRealFloat :: (RealFloat a, PrintfArg a) => ScopedName -> a -> RDFLabel+fromRealFloat dtype f | isNaN f      = toL "NaN"+                      | isInfinite f = toL $ if f > 0.0 then "INF" else "-INF"+                      -- +                      -- Would like to use Data.Text.Format.format but there are                                                                        +                      -- issues with this module; 0.3.0.2 doesn't build under+                      -- 6.12.3 due to a missing RelaxedPolyRec language extension+                      -- and it relies on double-conversion which has issues+                      -- when used in ghci due to a dlopen issue with libstdc++.+                      -- +                      -- -- | otherwise    = toL $ L.toStrict $ format "{}" (Only f)  +                      -- +                      | otherwise    = toL $ T.pack $ printf "%E" f+                        +                        where+                          toL = flip TypedLit dtype++-- textToRealFloat :: (RealFloat a) => (a -> Maybe a) -> T.Text -> Maybe a+textToRealFloat :: (RealFloat a, Read a) => (a -> Maybe a) -> T.Text -> Maybe a+textToRealFloat conv = rconv+    where+      rconv "NaN"  = Just (0.0/0.0) -- how best to create a NaN?+      rconv "INF"  = Just (1.0/0.0) -- ditto for Infinity+      rconv "-INF" = Just ((-1.0)/0.0)+      rconv ival +        -- xsd semantics allows "2." but Haskell syntax does not.+        | T.null ival = Nothing+          +        | otherwise = case maybeReadStr ival of+          Just val -> conv val+          _        -> if T.last ival == '.' -- could drop the check+                      then maybeReadStr (T.snoc ival '0') >>= conv+                      else Nothing+                               +        {-++        Unfortunately T.rational does not handle "3.01e4" the same+        as read; see https://bitbucket.org/bos/text/issue/7/++        | otherwise = case maybeRead T.rational ival of+          Just val -> conv val+          _        -> if T.last ival == '.' -- could drop the check+                      then maybeRead T.rational (T.snoc ival '0') >>= conv+                      else Nothing+        -}+                        +        -- not sure the above is any improvement on the following+        -- -- | T.last ival == '.' = maybeRead T.rational (T.snoc ival '0') >>= conv+        -- -- | otherwise          = maybeRead T.rational ival >>= conv+      +textToFloat :: T.Text -> Maybe Float+textToFloat = +  let -- assume that an invalid value (NaN/Inf) from maybeRead means+      -- that the value is out of bounds for Float so we do not+      -- convert+      conv f | isNaN f || isInfinite f = Nothing+             | otherwise               = Just f+  in textToRealFloat conv++textToDouble :: T.Text -> Maybe Double      +textToDouble = textToRealFloat Just++-- | Converts to a literal with a @xsd:float@ datatype.+instance ToRDFLabel Float where+  toRDFLabel = fromRealFloat xsdFloat+  +-- | Converts from a literal with a @xsd:float@ datatype.+-- The conversion will fail if the value is outside the valid range of+-- a Haskell `Float`.+instance FromRDFLabel Float where+  fromRDFLabel = fLabel textToFloat xsdFloat+                 +-- | Converts to a literal with a @xsd:double@ datatype.+instance ToRDFLabel Double where+  toRDFLabel = fromRealFloat xsdDouble+  +-- | Converts from a literal with a @xsd:double@ datatype.+instance FromRDFLabel Double where+  fromRDFLabel = fLabel textToDouble xsdDouble+  +-- TODO: are there subtypes of xsd::integer that are  +--       useful here?  +--         +-- TODO: add in support for Int8/..., Word8/...  +--  ++-- | Converts to a literal with a @xsd:integer@ datatype.+instance ToRDFLabel Int where+  toRDFLabel = tLabel xsdInteger T.pack++{-+Since decimal will just over/under-flow when converting to Int+we go via Integer and explicitly check for overflow.+-}++textToInt :: T.Text -> Maybe Int+textToInt s = +  let conv :: Integer -> Maybe Int+      conv i = +        let lb = fromIntegral (minBound :: Int)+            ub = fromIntegral (maxBound :: Int)+        in if (i >= lb) && (i <= ub) then Just (fromIntegral i) else Nothing+  +  in maybeRead (T.signed T.decimal) s >>= conv++-- | Converts from a literal with a @xsd:integer@ datatype.+-- The conversion will fail if the value is outside the valid range of+-- a Haskell `Int`.+instance FromRDFLabel Int where+  fromRDFLabel = fLabel textToInt xsdInteger++-- | Converts to a literal with a @xsd:integer@ datatype.+instance ToRDFLabel Integer where+  toRDFLabel = tLabel xsdInteger T.pack++-- | Converts from a literal with a @xsd:integer@ datatype.+instance FromRDFLabel Integer where+  fromRDFLabel = fLabel (maybeRead (T.signed T.decimal)) xsdInteger++{-+Support an ISO-8601 style format supporting++  2005-02-28T00:00:00Z+  2004-12-31T19:01:00-05:00+  2005-07-14T03:18:56.234+01:00++fromUTCFormat is used to convert UTCTime to a string+for storage within a Lit.++toUTCFormat is used to convert a string into UTCTime;+we have to support +   no time zone+   Z+   +/-HH:MM++which means a somewhat messy convertor, which is written+for clarity rather than speed.+-}++fromUTCFormat :: UTCTime -> String+fromUTCFormat = formatTime defaultTimeLocale "%FT%T%QZ"+  +fromDayFormat :: Day -> String+fromDayFormat = formatTime defaultTimeLocale "%FZ"+  +toTimeFormat :: (ParseTime a) => String -> String -> Maybe a+toTimeFormat fmt inVal =+  let fmtHHMM = fmt ++ "%z"+      fmtZ = fmt ++ "Z"+      pt f = parseTime defaultTimeLocale f inVal+  in case pt fmtHHMM of+    o@(Just _) -> o+    _ -> case pt fmtZ of+      o@(Just _) -> o+      _ -> pt fmt +  +toUTCFormat :: T.Text -> Maybe UTCTime+toUTCFormat = toTimeFormat "%FT%T%Q" . T.unpack+    +toDayFormat :: T.Text -> Maybe Day+toDayFormat = toTimeFormat "%F" . T.unpack+    +-- | Converts to a literal with a @xsd:datetime@ datatype.+instance ToRDFLabel UTCTime where+  toRDFLabel = flip TypedLit xsdDateTime . T.pack . fromUTCFormat+  +-- | Converts from a literal with a @xsd:datetime@ datatype.+instance FromRDFLabel UTCTime where+  fromRDFLabel = fLabel toUTCFormat xsdDateTime+  +-- | Converts to a literal with a @xsd:date@ datatype.+instance ToRDFLabel Day where+  toRDFLabel = flip TypedLit xsdDate . T.pack . fromDayFormat++-- | Converts from a literal with a @xsd:date@ datatype.+instance FromRDFLabel Day where+  fromRDFLabel = fLabel toDayFormat xsdDate+  +-- | Converts to a Resource.+instance ToRDFLabel ScopedName where  +  toRDFLabel = Res++-- | Converts from a Resource.+instance FromRDFLabel ScopedName where+  fromRDFLabel (Res sn) = Just sn+  fromRDFLabel _        = Nothing+  +-- | Converts to a Resource.+instance ToRDFLabel QName where  +  toRDFLabel = Res . makeQNameScopedName Nothing+  +-- | Converts from a Resource.+instance FromRDFLabel QName where+  fromRDFLabel (Res sn) = Just $ getQName sn+  fromRDFLabel _        = Nothing+  +-- | Converts to a Resource.+instance ToRDFLabel URI where  +  toRDFLabel = Res . makeURIScopedName+  +-- | Converts from a Resource.+instance FromRDFLabel URI where+  fromRDFLabel (Res sn) = Just $ getScopedNameURI sn+  fromRDFLabel _        = Nothing++-- | Get the canonical string for RDF label.+--+--  This is used for hashing, so that equivalent labels always return+--  the same hash value.+--+--  TODO: can remove the use of quote1Str as all we care about is+--  a unique output, not that it is valid in any RDF format. Also+--  rename to showForHash or something like that, since it is only used+--  for this purpose.+--+showCanon :: RDFLabel -> String+showCanon (Res sn)           = "<"++show (getScopedNameURI sn)++">"+showCanon (Lit st)           = show st+showCanon (LangLit st lang)  = quote1Str st ++ "@"  ++ T.unpack (fromLangTag lang)+showCanon (TypedLit st dt)   = quote1Str st ++ "^^" ++ show (getScopedNameURI dt)+showCanon s                  = show s++-- | See `quote`.+quoteT :: Bool -> T.Text -> T.Text+quoteT f = T.pack . quote f . T.unpack ++{-| N3-style quoting rules for a string.++TODO: when flag is `False` need to worry about multiple quotes (> 2)+in a row.+-}++quote :: +  Bool  -- ^ @True@ if the string is to be displayed using one rather than three quotes.+  -> String -- ^ String to quote.+  -> String+quote _     []           = ""+quote False s@(c:'"':[]) | c == '\\'  = s -- handle triple-quoted strings ending in "+                         | otherwise  = [c, '\\', '"']++quote True  ('"': st)    = '\\':'"': quote True  st+quote True  ('\n':st)    = '\\':'n': quote True  st++quote True  ('\t':st)    = '\\':'t': quote True  st+quote False ('"': st)    =      '"': quote False st+quote False ('\n':st)    =     '\n': quote False st+quote False ('\t':st)    =     '\t': quote False st+quote f ('\r':st)    = '\\':'r': quote f st+quote f ('\\':st)    = '\\':'\\': quote f st -- not sure about this+quote f (c:st) = +  let nc = ord c+      rst = quote f st+      +      -- lazy way to convert to a string+      hstr = printf "%08X" nc+      ustr = hstr ++ rst++  in if nc > 0xffff +     then '\\':'U': ustr+     else if nc > 0x7e || nc < 0x20+          then '\\':'u': drop 4 ustr+          else c : rst++-- surround a string with quotes ("...")++quote1Str :: T.Text -> String+quote1Str t = '"' : quote False (T.unpack t) ++ "\""++---------------------------------------------------------+--  Selected RDFLabel values+---------------------------------------------------------++-- | @rdf:type@ from <http://www.w3.org/TR/rdf-schema/#ch_type>.+resRdfType :: RDFLabel+resRdfType = Res rdfType ++-- | @rdf:List@ from <http://www.w3.org/TR/rdf-schema/#ch_list>.+resRdfList :: RDFLabel+resRdfList = Res rdfList++-- | @rdf:first@ from <http://www.w3.org/TR/rdf-schema/#ch_first>.+resRdfFirst :: RDFLabel+resRdfFirst = Res rdfFirst ++-- | @rdf:rest@ from <http://www.w3.org/TR/rdf-schema/#ch_rest>.+resRdfRest :: RDFLabel+resRdfRest = Res rdfRest++-- | @rdf:nil@ from <http://www.w3.org/TR/rdf-schema/#ch_nil>.+resRdfNil :: RDFLabel+resRdfNil = Res rdfNil++-- | @rdfs:member@ from <http://www.w3.org/TR/rdf-schema/#ch_member>.+resRdfsMember :: RDFLabel+resRdfsMember = Res rdfsMember++-- | @rdfd:GeneralRestriction@.+resRdfdGeneralRestriction :: RDFLabel+resRdfdGeneralRestriction = Res rdfdGeneralRestriction++-- | @rdfd:onProperties@.+resRdfdOnProperties :: RDFLabel+resRdfdOnProperties       = Res rdfdOnProperties++-- | @rdfd:constraint@.+resRdfdConstraint :: RDFLabel+resRdfdConstraint         = Res rdfdConstraint++-- | @rdfd:maxCardinality@.+resRdfdMaxCardinality :: RDFLabel+resRdfdMaxCardinality     = Res rdfdMaxCardinality++-- | @rdfs:seeAlso@ from <http://www.w3.org/TR/rdf-schema/#ch_seealso>.+resRdfsSeeAlso :: RDFLabel+resRdfsSeeAlso = Res rdfsSeeAlso++-- | @rdf:value@ from <http://www.w3.org/TR/rdf-schema/#ch_value>.+resRdfValue :: RDFLabel+resRdfValue = Res rdfValue++-- | @owl:sameAs@.+resOwlSameAs :: RDFLabel+resOwlSameAs = Res owlSameAs++-- | @log:implies@.+resLogImplies :: RDFLabel+resLogImplies = Res logImplies++-- | @rdfs:label@ from <http://www.w3.org/TR/rdf-schema/#ch_label>.+resRdfsLabel :: RDFLabel+resRdfsLabel = Res rdfsLabel++-- | @rdfs:comment@ from <http://www.w3.org/TR/rdf-schema/#ch_comment>.+resRdfsComment :: RDFLabel+resRdfsComment = Res rdfsComment++-- | @rdf:Property@ from <http://www.w3.org/TR/rdf-schema/#ch_property>.+resRdfProperty :: RDFLabel+resRdfProperty = Res rdfProperty++-- | @rdfs:subPropertyOf@ from <http://www.w3.org/TR/rdf-schema/#ch_subpropertyof>.+resRdfsSubPropertyOf :: RDFLabel+resRdfsSubPropertyOf = Res rdfsSubPropertyOf++-- | @rdfs:subClassOf@ from <http://www.w3.org/TR/rdf-schema/#ch_subclassof>.+resRdfsSubClassOf :: RDFLabel+resRdfsSubClassOf = Res rdfsSubClassOf++-- | @rdfs:Class@ from <http://www.w3.org/TR/rdf-schema/#ch_class>.+resRdfsClass :: RDFLabel+resRdfsClass = Res rdfsClass++-- | @rdfs:Literal@ from <http://www.w3.org/TR/rdf-schema/#ch_literal>.+resRdfsLiteral :: RDFLabel+resRdfsLiteral = Res rdfsLiteral++-- | @rdfs:Datatype@ from <http://www.w3.org/TR/rdf-schema/#ch_datatype>.+resRdfsDatatype :: RDFLabel+resRdfsDatatype = Res rdfsDatatype++-- | @rdf:XMLLiteral@ from <http://www.w3.org/TR/rdf-schema/#ch_xmlliteral>.+resRdfXMLLiteral :: RDFLabel+resRdfXMLLiteral = Res rdfXMLLiteral++-- | @rdfs:range@ from <http://www.w3.org/TR/rdf-schema/#ch_range>.+resRdfsRange :: RDFLabel+resRdfsRange = Res rdfsRange++-- | @rdfs:domain@ from <http://www.w3.org/TR/rdf-schema/#ch_domain>.+resRdfsDomain :: RDFLabel+resRdfsDomain = Res rdfsDomain++-- | @rdfs:Container@ from <http://www.w3.org/TR/rdf-schema/#ch_container>.+resRdfsContainer :: RDFLabel+resRdfsContainer = Res rdfsContainer++-- | @rdf:Bag@ from <http://www.w3.org/TR/rdf-schema/#ch_bag>.+resRdfBag :: RDFLabel+resRdfBag = Res rdfBag++-- | @rdf:Seq@ from <http://www.w3.org/TR/rdf-schema/#ch_seq>.+resRdfSeq :: RDFLabel+resRdfSeq = Res rdfSeq++-- | @rdf:Alt@ from <http://www.w3.org/TR/rdf-schema/#ch_alt>.+resRdfAlt :: RDFLabel+resRdfAlt = Res rdfAlt++-- | @rdfs:ContainerMembershipProperty@ from <http://www.w3.org/TR/rdf-schema/#ch_containermembershipproperty>.+resRdfsContainerMembershipProperty :: RDFLabel+resRdfsContainerMembershipProperty = Res rdfsContainerMembershipProperty++-- | @rdfs:isDefinedBy@ from <http://www.w3.org/TR/rdf-schema/#ch_isdefinedby>.+resRdfsIsDefinedBy :: RDFLabel+resRdfsIsDefinedBy = Res rdfsIsDefinedBy++-- | @rdfs:Resource@ from <http://www.w3.org/TR/rdf-schema/#ch_resource>.+resRdfsResource :: RDFLabel+resRdfsResource = Res rdfsResource++-- | @rdf:Statement@ from <http://www.w3.org/TR/rdf-schema/#ch_statement>.+resRdfStatement :: RDFLabel+resRdfStatement = Res rdfStatement++-- | @rdf:subject@ from <http://www.w3.org/TR/rdf-schema/#ch_subject>.+resRdfSubject :: RDFLabel+resRdfSubject = Res rdfSubject++-- | @rdf:predicate@ from <http://www.w3.org/TR/rdf-schema/#ch_predicate>.+resRdfPredicate :: RDFLabel+resRdfPredicate = Res rdfPredicate++-- | @rdf:object@ from <http://www.w3.org/TR/rdf-schema/#ch_object>.+resRdfObject :: RDFLabel+resRdfObject = Res rdfObject++-- | @rdf:RDF@.+resRdfRDF :: RDFLabel+resRdfRDF = Res rdfRDF++-- | @rdf:Description@.+resRdfDescription :: RDFLabel+resRdfDescription = Res rdfDescription++-- | @rdf:ID@.+resRdfID :: RDFLabel+resRdfID = Res rdfID++-- | @rdf:about@.+resRdfAbout :: RDFLabel+resRdfAbout = Res rdfAbout++-- | @rdf:parseType@.+resRdfParseType :: RDFLabel+resRdfParseType = Res rdfParseType++-- | @rdf:resource@.+resRdfResource :: RDFLabel+resRdfResource = Res rdfResource++-- | @rdf:li@.+resRdfLi :: RDFLabel+resRdfLi = Res rdfLi++-- | @rdf:nodeID@.+resRdfNodeID :: RDFLabel+resRdfNodeID = Res rdfNodeID++-- | @rdf:datatype@.+resRdfDatatype :: RDFLabel+resRdfDatatype = Res rdfDatatype++-- | @rdf:_1@.+resRdf1 :: RDFLabel+resRdf1 = Res rdf1++-- | @rdf:_2@.+resRdf2 :: RDFLabel+resRdf2 = Res rdf2++-- | Create a @rdf:_n@ entity.+--+-- There is no check that the argument is not @0@.+resRdfn :: Word32 -> RDFLabel+resRdfn = Res . rdfn++---------------------------------------------------------+--  Additional functions on RDFLabel values+---------------------------------------------------------++-- |Test if supplied labal is a URI resource node+isUri :: RDFLabel -> Bool+isUri (Res _) = True+isUri  _      = False++-- |Test if supplied labal is a literal node+-- ('Lit', 'LangLit', or 'TypedLit').+isLiteral :: RDFLabel -> Bool+isLiteral (Lit _)        = True+isLiteral (LangLit _ _)  = True+isLiteral (TypedLit _ _) = True+isLiteral  _             = False++-- |Test if supplied labal is an untyped literal node (either+-- 'Lit' or 'LangLit').+isUntypedLiteral :: RDFLabel -> Bool+isUntypedLiteral (Lit _)       = True+isUntypedLiteral (LangLit _ _) = True+isUntypedLiteral  _            = False++-- |Test if supplied labal is a typed literal node ('TypedLit').+isTypedLiteral :: RDFLabel -> Bool+isTypedLiteral (TypedLit _ _) = True+isTypedLiteral  _             = False++-- |Test if supplied labal is a XML literal node+isXMLLiteral :: RDFLabel -> Bool+isXMLLiteral = isDatatyped rdfXMLLiteral++-- |Test if supplied label is a typed literal node of a given datatype+isDatatyped :: ScopedName -> RDFLabel -> Bool+isDatatyped d  (TypedLit _ dt) = d == dt+isDatatyped _  _               = False++-- |Test if supplied label is a container membership property+--+--  Check for namespace is RDF namespace and+--  first character of local name is '_' and+--  remaining characters of local name are all digits+isMemberProp :: RDFLabel -> Bool+isMemberProp (Res sn) =+  getScopeNamespace sn == namespaceRDF &&+  case T.uncons (getLName (getScopeLocal sn)) of+    Just ('_', t) -> T.all isDigit t+    _ -> False+isMemberProp _        = False++-- |Test if supplied labal is a blank node+isBlank :: RDFLabel -> Bool+isBlank (Blank _) = True+isBlank  _        = False++-- |Test if supplied labal is a query variable+isQueryVar :: RDFLabel -> Bool+isQueryVar (Var _) = True+isQueryVar  _      = False++-- |Extract text value from a literal node (including the+-- Language and Typed variants). The empty string is returned+-- for other nodes.+getLiteralText :: RDFLabel -> T.Text+getLiteralText (Lit s)        = s+getLiteralText (LangLit s _)  = s+getLiteralText (TypedLit s _) = s+getLiteralText  _             = ""++-- |Extract the ScopedName value from a resource node ('nullScopedName'+-- is returned for non-resource nodes).+getScopedName :: RDFLabel -> ScopedName+getScopedName (Res sn) = sn+getScopedName  _       = nullScopedName++-- |Make a blank node from a supplied query variable,+--  or return the supplied label unchanged.+--  (Use this in when substituting an existential for an+--  unsubstituted query variable.)+makeBlank :: RDFLabel -> RDFLabel+makeBlank  (Var loc)    = Blank loc+makeBlank  lb           = lb++-- | RDF Triple (statement)+-- +--   At present there is no check or type-level+--   constraint that stops the subject or+--   predicate of the triple from being a literal.+--+type RDFTriple = Arc RDFLabel++-- | Convert 3 RDF labels to a RDF triple.+--+--   See also @Swish.RDF.GraphClass.arcFromTriple@.+toRDFTriple :: +  (ToRDFLabel s, ToRDFLabel p, ToRDFLabel o) +  => s -- ^ Subject +  -> p -- ^ Predicate+  -> o -- ^ Object+  -> RDFTriple+toRDFTriple s p o = +  Arc (toRDFLabel s) (toRDFLabel p) (toRDFLabel o)++-- | Extract the contents of a RDF triple.+--+--   See also @Swish.RDF.GraphClass.arcToTriple@.+fromRDFTriple :: +  (FromRDFLabel s, FromRDFLabel p, FromRDFLabel o) +  => RDFTriple +  -> Maybe (s, p, o) -- ^ The conversion only succeeds if all three+                     --   components can be converted to the correct+                     --   Haskell types.+fromRDFTriple (Arc s p o) = +  (,,) <$> fromRDFLabel s <*> fromRDFLabel p <*> fromRDFLabel o+  +-- | Namespace prefix list entry++-- | A 'LookupMap' for name spaces.+type NamespaceMap = LookupMap Namespace++-- | A 'LookupMap' for reversed name spaces.+type RevNamespaceMap = LookupMap RevNamespace++-- | Support for reversed name spaces in a 'LookupMap'.+data RevNamespace = RevNamespace Namespace++instance LookupEntryClass RevNamespace URI (Maybe T.Text) where+    keyVal   (RevNamespace ns) = swap $ getNamespaceTuple ns+    newEntry (uri,pre) = RevNamespace (makeNamespace pre uri)++-- | Create an empty namespace map.+emptyNamespaceMap :: NamespaceMap+emptyNamespaceMap = LookupMap []++-- | Graph formula entry++data LookupFormula lb gr = Formula+    { formLabel :: lb -- ^ The label for the formula+    , formGraph :: gr -- ^ The contents of the formula+    }++instance (Eq lb, Eq gr) => Eq (LookupFormula lb gr) where+    f1 == f2 = formLabel f1 == formLabel f2 &&+               formGraph f1 == formGraph f2++instance (Label lb)+    => LookupEntryClass (LookupFormula lb (NSGraph lb)) lb (NSGraph lb)+    where+        keyVal fe      = (formLabel fe, formGraph fe)+        newEntry (k,v) = Formula { formLabel=k, formGraph=v }++instance (Label lb) => Show (LookupFormula lb (NSGraph lb))+    where+        show (Formula l g) = show l ++ " :- { " ++ showArcs "    " g ++ " }"++-- | A named formula.+type Formula lb = LookupFormula lb (NSGraph lb)++-- | A 'LookupMap' for named formulae.+type FormulaMap lb = LookupMap (LookupFormula lb (NSGraph lb))++-- | Create an empty formula map.+emptyFormulaMap :: FormulaMap RDFLabel+emptyFormulaMap = LookupMap []++{-  given up on trying to do Functor for formulae...+instance Functor (LookupFormula (NSGraph lb)) where+    fmap f fm = mapTranslateEntries (mapFormulaEntry f) fm+-}++formulaeMap :: (lb -> l2) -> FormulaMap lb -> FormulaMap l2+formulaeMap f = fmap (formulaEntryMap f) ++formulaEntryMap ::+    (lb -> l2)+    -> Formula lb+    -> Formula l2+formulaEntryMap f (Formula k gr) = Formula (f k) (fmap f gr)++formulaeMapA :: Applicative f => (lb -> f l2) -> +                FormulaMap lb -> f (FormulaMap l2)+formulaeMapA f = Traversable.traverse (formulaEntryMapA f)++formulaEntryMapA ::+  (Applicative f) => +  (lb -> f l2)+  -> Formula lb+  -> f (Formula l2)+formulaEntryMapA f (Formula k gr) = Formula `liftA` f k <*> Traversable.traverse f gr++{-+formulaeMapM ::+    (Monad m) => (lb -> m l2) -> FormulaMap lb -> m (FormulaMap l2)+formulaeMapM f = T.mapM (formulaEntryMapM f)++formulaEntryMapM ::+    (Monad m)+    => (lb -> m l2)+    -> Formula lb+    -> m (Formula l2)+formulaEntryMapM f (Formula k gr) =+  Formula `liftM` f k `ap` T.mapM f gr+    +-}++{-|++Memory-based graph with namespaces and subgraphs.++The primary means for adding arcs to an existing graph+are: ++ - `setArcs` from the `LDGraph` instance, which replaces the +    existing set of arcs and does not change the namespace +    map.++ - `addArc` which checks that the arc is unknown before+    adding it but does not change the namespace map or+    re-label any blank nodes in the arc.++-}+data NSGraph lb = NSGraph+    { namespaces :: NamespaceMap    -- ^ the namespaces to use+    , formulae   :: FormulaMap lb   -- ^ any associated formulae (a.k.a. sub- or named- graps)+    , statements :: [Arc lb]        -- ^ the statements in the graph+    }++instance (Label lb) => LDGraph NSGraph lb where+    emptyGraph   = NSGraph emptyNamespaceMap (LookupMap []) []+    getArcs      = statements +    setArcs g as = g { statements=as }++-- | The 'mappend' operation uses 'merge' rather than 'addGraphs'.+instance (Label lb) => Monoid (NSGraph lb) where+    mempty  = emptyGraph+    mappend = merge+  +instance Functor NSGraph where+  fmap f (NSGraph ns fml stmts) =+    NSGraph ns (formulaeMap f fml) ((map $ fmap f) stmts)++instance Foldable.Foldable NSGraph where+  foldMap = Traversable.foldMapDefault++instance Traversable.Traversable NSGraph where+  traverse f (NSGraph ns fml stmts) = +    NSGraph ns <$> formulaeMapA f fml <*> (Traversable.traverse $ Traversable.traverse f) stmts+  +instance (Label lb) => Eq (NSGraph lb) where+    (==) = grEq++instance (Label lb) => Show (NSGraph lb) where+    show     = grShow ""+    showList = grShowList ""++-- | Retrieve the namespace map in the graph.+getNamespaces :: NSGraph lb -> NamespaceMap+getNamespaces = namespaces++-- | Replace the namespace information in the graph.+setNamespaces      :: NamespaceMap -> NSGraph lb -> NSGraph lb+setNamespaces ns g = g { namespaces=ns }++-- | Retrieve the formulae in the graph.+getFormulae :: NSGraph lb -> FormulaMap lb+getFormulae = formulae++-- | Replace the formulae in the graph.+setFormulae      :: FormulaMap lb -> NSGraph lb -> NSGraph lb+setFormulae fs g = g { formulae=fs }++-- | Find a formula in the graph, if it exists.+getFormula     :: (Label lb) => NSGraph lb -> lb -> Maybe (NSGraph lb)+getFormula g l = mapFindMaybe l (formulae g)++-- | Add (or replace) a formula.+setFormula     :: (Label lb) => Formula lb -> NSGraph lb -> NSGraph lb+setFormula f g = g { formulae=mapReplace (formulae g) f }++{-|+Add an arc to the graph. It does not relabel any blank nodes in the input arc,+nor does it change the namespace map, +but it does ensure that the arc is unknown before adding it.+-}+addArc :: (Label lb) => Arc lb -> NSGraph lb -> NSGraph lb+addArc ar gr = gr { statements=addSetElem ar (statements gr) }++-- |Add element to the set.++addSetElem :: (Eq a) => a -> [a] -> [a]+addSetElem e es = if e `elem` es then es else e:es++grShowList :: (Label lb) => String -> [NSGraph lb] -> String -> String+grShowList _ []     = showString "[no graphs]"+grShowList p (g:gs) = showChar '[' . showString (grShow pp g) . showl gs+    where+        showl []     = showChar ']' -- showString $ "\n" ++ p ++ "]"+        showl (h:hs) = showString (",\n "++p++grShow pp h) . showl hs+        pp           = ' ':p++grShow   :: (Label lb) => String -> NSGraph lb -> String+grShow p g =+    "Graph, formulae: " ++ showForm ++ "\n" +++    p ++ "arcs: " ++ showArcs p g+    where+        showForm = foldr ((++) . (pp ++) . show) "" fml+        fml = listLookupMap (getFormulae g)+        pp = "\n    " ++ p++showArcs :: (Label lb) => String -> NSGraph lb -> String+showArcs p g = foldr ((++) . (pp ++) . show) "" (getArcs g)+    where+        pp = "\n    " ++ p++-- | Graph equality.+grEq :: (Label lb) => NSGraph lb -> NSGraph lb -> Bool+grEq g1 = fst . grMatchMap g1++-- | Match graphs, returning `True` if they are equivalent,+-- with a map of labels to equivalence class identifiers+-- (see 'graphMatch' for further details).+grMatchMap :: (Label lb) =>+    NSGraph lb -> NSGraph lb -> (Bool, LabelMap (ScopedLabel lb))+grMatchMap g1 g2 =+    graphMatch matchable (getArcs g1) (getArcs g2)+    where+        matchable l1 l2 = mapFormula g1 l1 == mapFormula g2 l2+        mapFormula g l  = mapFindMaybe l (getFormulae g)++-- |Merge RDF graphs, renaming blank and query variable nodes as+--  needed to neep variable nodes from the two graphs distinct in+--  the resulting graph.+--        +--  Currently formulae are not guaranteed to be preserved across a+--  merge.+--        +merge :: (Label lb) => NSGraph lb -> NSGraph lb -> NSGraph lb+merge gr1 gr2 =+    let bn1   = allLabels labelIsVar gr1+        bn2   = allLabels labelIsVar gr2+        dupbn = intersect bn1 bn2+        allbn = union bn1 bn2+    in addGraphs gr1 (remapLabels dupbn allbn id gr2)++-- |Return list of all labels (including properties) in the graph+--  satisfying a supplied filter predicate. This routine+--  includes the labels in any formulae.+allLabels :: (Label lb) => (lb -> Bool) -> NSGraph lb -> [lb]+allLabels p gr = filter p (unionNodes p (formulaNodes p gr) (labels gr) ) +                 +{- TODO: is the leading 'filter p' needed in allLabels?+-}++-- |Return list of all subjects and objects in the graph+--  satisfying a supplied filter predicate.+allNodes :: (Label lb) => (lb -> Bool) -> NSGraph lb -> [lb]+allNodes p = unionNodes p [] . nodes++-- | List all nodes in graph formulae satisfying a supplied predicate+formulaNodes :: (Label lb) => (lb -> Bool) -> NSGraph lb -> [lb]+formulaNodes p gr = foldl' (unionNodes p) fkeys (map (allLabels p) fvals)+    where+        -- fm :: (Label lb) => FormulaMap lb+        --                     LookupMap LookupFormula (NSGraph lb) lb+        fm    = formulae gr+        -- fvals :: (Label lb) => [NSGraph lb]+        fvals = mapVals fm+        -- fkeys :: (Label lb) => [lb]+        fkeys = filter p $ mapKeys fm++-- | Helper to filter variable nodes and merge with those found so far+unionNodes :: (Label lb) => (lb -> Bool) -> [lb] -> [lb] -> [lb]+unionNodes p ls1 ls2 = ls1 `union` filter p ls2++-- |Remap selected nodes in graph.+--+--  This is the node renaming operation that prevents graph-scoped+--  variable nodes from being merged when two graphs are merged.+remapLabels ::+    (Label lb)+    => [lb] -- ^ variable nodes to be renamed (@dupbn@)+    -> [lb] -- ^ variable nodes used that must be avoided (@allbn@)+    -> (lb -> lb) -- ^ node conversion function that is applied to nodes+    -- from @dupbn@ in the graph that are to be replaced by+    -- new blank nodes.  If no such conversion is required,+    -- supply @id@.  The function 'makeBlank' can be used to convert+    -- RDF query nodes into RDF blank nodes.+    -> NSGraph lb -- ^ graph in which nodes are to be renamed+    -> NSGraph lb+remapLabels dupbn allbn cnvbn = fmap (mapnode dupbn allbn cnvbn)++-- |Externally callable function to construct a list of (old,new)+--  values to be used for graph label remapping.+--+remapLabelList ::+    (Label lb)+    => [lb] -- ^ labels to be remaped+    -> [lb] -- ^ labels to be avoided by the remapping+    -> [(lb,lb)]+remapLabelList remap avoid = maplist remap avoid id []++-- | Remap a single graph node.+--+--  If the node is not one of those to be remapped,+--  the supplied value is returned unchanged.+mapnode ::+    (Label lb) => [lb] -> [lb] -> (lb -> lb) -> lb -> lb+mapnode dupbn allbn cnvbn nv =+    mapFind nv nv (LookupMap (maplist dupbn allbn cnvbn []))++-- | Construct a list of (oldnode,newnode) values to be used for+--  graph label remapping.  The function operates recursively, adding+--  new nodes generated to the accumulator and also to the+--  list of nodes to be avoided.+maplist ::+    (Label lb) +    => [lb]       -- ^ oldnode values+    -> [lb]       -- ^ nodes to be avoided+    -> (lb -> lb) +    -> [(lb,lb)]  -- ^ accumulator+    -> [(lb,lb)]+maplist []         _     _     mapbn = mapbn+maplist (dn:dupbn) allbn cnvbn mapbn = maplist dupbn allbn' cnvbn mapbn'+    where+        dnmap  = newNode (cnvbn dn) allbn+        mapbn' = (dn,dnmap):mapbn+        allbn' = dnmap:allbn++-- |Given a node and a list of existing nodes, find a new node for+--  the supplied node that does not clash with any existing node.+--  (Generates an non-terminating list of possible replacements, and+--  picks the first one that isn't already in use.)+--+--  TODO: optimize this for common case @nnn@ and @_nnn@:+--    always generate @_nnn@ and keep track of last allocated+--+newNode :: (Label lb) => lb -> [lb] -> lb+newNode dn existnodes =+    head $ newNodes dn existnodes++-- |Given a node and a list of existing nodes, generate a list of new+--  nodes for the supplied node that do not clash with any existing node.+newNodes :: (Label lb) => lb -> [lb] -> [lb]+newNodes dn existnodes =+    filter (not . (`elem` existnodes)) $ trynodes (noderootindex dn)++{- ++For now go with a 32-bit integer (since Int on my machine uses 32-bit+values). We could instead use a Whole class constraint from+Numeric.Natural (in semigroups), but it is probably better to+specialize here. The idea for using Word<X> rather than Int is to+make it obvious that we are only interested in values >= 0.++-}++noderootindex :: (Label lb) => lb -> (String, Word32)+noderootindex dn = (nh,nx) where+    (nh,nt) = splitnodeid $ getLocal dn+    nx      = if null nt then 0 else read nt++splitnodeid :: String -> (String,String)+splitnodeid = break isDigit++trynodes :: (Label lb) => (String, Word32) -> [lb]+trynodes (nr,nx) = [ makeLabel (nr++show n) | n <- iterate (+1) nx ]++{-+trybnodes :: (Label lb) => (String,Int) -> [lb]+trybnodes (nr,nx) = [ makeLabel (nr++show n) | n <- iterate (+1) nx ]+-}++-- | Memory-based RDF graph type++type RDFGraph = NSGraph RDFLabel++-- |Create a new RDF graph from a supplied list of arcs.+--+-- This version will attempt to fill up the namespace map+-- of the graph based on the input labels (including datatypes+-- on literals). For faster+-- creation of a graph you can use:+--+-- > emptyRDFGraph { statements = arcs }+--+-- which is how this routine was defined in version @0.3.1.1@+-- and earlier.+--+toRDFGraph :: +    [RDFTriple] -- ^ There is no check to remove repeated statements in this list,+                -- so it is suggested that you use 'Data.List.nub', or convert via 'Data.Set.Set',+                -- if your input may contain such elements.+    -> RDFGraph+toRDFGraph arcs = +  let lbls = concatMap arcLabels arcs+      +      getNS (Res s) = Just s+      getNS (TypedLit _ dt) = Just dt+      getNS _ = Nothing++      ns = mapMaybe (fmap getScopeNamespace . getNS) lbls+      nsmap = foldl' mapAddIfNew emptyNamespaceMap ns+  +  in mempty { namespaces = nsmap, statements = arcs }++-- |Create a new, empty RDF graph (it is just 'mempty').+--+emptyRDFGraph :: RDFGraph+emptyRDFGraph = mempty ++{-+-- |Update an RDF graph using a supplied list of arcs, keeping+--  prefix definitions and formula definitions from the original.+--+--  [[[TODO:  I think this may be redundant - the default graph+--  class has an update method which accepts a function to update+--  the arcs, not touching other parts of the graph value.]]]+updateRDFGraph :: RDFGraph -> [RDFTriple] -> RDFGraph+updateRDFGraph gr as = gr { statements=as }+-}++--------------------------------------------------------------------------------+--+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke+--  All rights reserved.+--+--  This file is part of Swish.+--+--  Swish is free software; you can redistribute it and/or modify+--  it under the terms of the GNU General Public License as published by+--  the Free Software Foundation; either version 2 of the License, or+--  (at your option) any later version.+--+--  Swish is distributed in the hope that it will be useful,+--  but WITHOUT ANY WARRANTY; without even the implied warranty of+--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+--  GNU General Public License for more details.+--+--  You should have received a copy of the GNU General Public License+--  along with Swish; if not, write to:+--    The Free Software Foundation, Inc.,+--    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+--+--------------------------------------------------------------------------------
− src/Swish/RDF/GraphClass.hs
@@ -1,248 +0,0 @@-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE DeriveFunctor, DeriveFoldable, DeriveTraversable #-}-------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  GraphClass---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  License     :  GPL V2------  Maintainer  :  Douglas Burke---  Stability   :  experimental---  Portability :  DeriveFunctor, DeriveFoldable, DeriveTraversable, MultiParamTypeClasses------  This module defines a Labelled Directed Graph and Label classes,---  and the Arc datatype.----------------------------------------------------------------------------------------------------------------------------------------------------- Define LDGraph, arc and related classes and types---------------------------------------------------------------module Swish.RDF.GraphClass-    ( LDGraph(..), replaceArcs-    , Label(..)-    , Arc(..), arcSubj, arcPred, arcObj, arc, arcToTriple, arcFromTriple-    , Selector-    , hasLabel, arcLabels -- , arcNodes-    )-where--import qualified Data.Foldable as F-import qualified Data.Traversable as T--import Data.Hashable (Hashable(..))--import Data.List (foldl', union, (\\))----  NOTE:  I wanted to declare this as a subclass of Functor, but---  the constraint on the label type seems to prevent that.---  So I've just declared specific instances to be Functors.-----{-|-Labelled Directed Graph class--Minimum required implementation:  `setArcs`, `getArcs` and `containedIn`-(although @containedIn@ may be removed as it is currently unused).--}-class (Eq (lg lb), Eq lb ) => LDGraph lg lb-    where-    --  empty graph-    --  emptyGr     :: lg lb    [[[TODO?]]]-    --  component-level operations-      -    -- | Replace the existing arcs in the graph.-    setArcs     :: [Arc lb] -> lg lb -> lg lb-    -    -- | Extract all the arcs from a graph-    getArcs     :: lg lb -> [Arc lb]-    -    -- | Extract those arcs that match the given `Selector`.-    extract     :: Selector lb -> lg lb -> lg lb-    extract sel = update (filter sel)-    -    -- | Add the two graphs-    add         :: lg lb -> lg lb -> lg lb-    add    addg = update (union (getArcs addg))-    -    -- | Remove those arcs in the first graph from the second-    -- graph-    delete :: lg lb  -- ^ g1-              -> lg lb -- ^ g2-              -> lg lb -- ^ g2 - g1 -> g3-    delete delg = update (\\ getArcs delg)-    -    -- | Enumerate the distinct labels contained in a graph;-    -- that is, any label that appears in the subject,-    -- predicate or object position of an `Arc`.-    labels      :: lg lb -> [lb]-    labels g    = foldl' union [] (map arcLabels (getArcs g))-    -    -- | Enumerate the distinct nodes contained in a graph;-    -- that is, any label that appears in the subject-    -- or object position of an `Arc`.-    nodes       :: lg lb -> [lb]-    nodes g     = foldl' union [] (map arcNodes (getArcs g))-    -    -- | Test for graph containment in another.-    ---    -- At present this is unused and may be removed in a future release.-    containedIn :: lg lb -> lg lb -> Bool -    -    -- | Update the arcs in a graph using a supplied function.-    update      :: ( [Arc lb] -> [Arc lb] ) -> lg lb -> lg lb-    update f g  = setArcs ( f (getArcs g) ) g--{--TODO:-  add a Monoid instance for LDGraph, so that we can remove the-  NSGraph instance in RDFGraph--  This means adding the emptyGr function to the interface--}---- |Function to replace arcs in a graph with a given list of arcs.------ This is identical to @flip setArcs@ and so may be removed.---    -replaceArcs :: (LDGraph lg lb) => lg lb -> [Arc lb] -> lg lb-replaceArcs = flip setArcs--- replaceArcs gr as = update (const as) gr---- | Label class------  A label may have a fixed binding, which means that the label identifies (is) a---  particular graph node, and different such labels are always distinct nodes.---  Alternatively, a label may be unbound (variable), which means that it is a---  placeholder for an unknown node label.  Unbound node labels are used as---  graph-local identifiers for indicating when the same node appears in---  several arcs.------  For the purposes of graph-isomorphism testing, fixed labels are matched when they---  are the same.  Variable labels may be matched with any other variable label.---  Our definition of isomorphism (for RDF graphs) does not match variable labels---  with fixed labels.--class (Eq lb, Show lb, Ord lb) => Label lb where-  -  -- | Does this node have a variable binding?-  labelIsVar  :: lb -> Bool           -    -  -- | Calculate the hash of the label using the supplied seed.-  labelHash   :: Int -> lb -> Int     -  -  -- could provide a default of -  --   labelHash = hashWithSalt-  -- but this would then force a Hashable constraint-    -  -- | Extract the local id from a variable node.                 -  getLocal    :: lb -> String-    -  -- | Make a label value from a local id.  -  makeLabel   :: String -> lb-    -  -- compare     :: lb -> lb -> Ordering-  -- compare l1 l2 = compare (show l1) (show l2)---- | Arc type--data Arc lb = Arc -              { asubj :: lb  -- ^ The subject of the arc.-              , apred :: lb  -- ^ The predicate (property) of the arc.-              , aobj :: lb   -- ^ The object of the arc.-              }-            deriving (Eq, Functor, F.Foldable, T.Traversable)--instance (Hashable lb) => Hashable (Arc lb) where-  hash (Arc s p o) = hash s `hashWithSalt` p `hashWithSalt` o-  hashWithSalt salt (Arc s p o) = salt `hashWithSalt` s `hashWithSalt` p `hashWithSalt` o---- | Return the subject of the arc.-arcSubj :: Arc lb -> lb-arcSubj = asubj---- | Return the predicate (property) of the arc.-arcPred :: Arc lb -> lb-arcPred = apred---- | Return the object of the arc.-arcObj :: Arc lb -> lb-arcObj = aobj---- | Create an arc.-arc :: lb      -- ^ The subject of the arc.-       -> lb   -- ^ The predicate of the arc.-       -> lb   -- ^ The object of the arc.-       -> Arc lb-arc = Arc---- | Convert an Arc into a tuple.-arcToTriple :: Arc lb -> (lb,lb,lb)-arcToTriple (Arc s p o) = (s, p, o)---- | Create an Arc from a tuple.-arcFromTriple :: (lb,lb,lb) -> Arc lb-arcFromTriple (s,p,o) = Arc s p o--instance Ord lb => Ord (Arc lb) where-  compare (Arc s1 p1 o1) (Arc s2 p2 o2)-    | cs /= EQ = cs-    | cp /= EQ = cp-    | otherwise = co-    where-      cs = compare s1 s2-      cp = compare p1 p2-      co = compare o1 o2--  {- not needed-  (Arc s1 p1 o1) <= (Arc s2 p2 o2)-    | s1 /= s2 = s1 <= s2-    | p1 /= p2 = p1 <= p2-    | otherwise = o1 <= o2-  -}--instance (Show lb) => Show (Arc lb) where-    show (Arc lb1 lb2 lb3) =-        "("++ show lb1 ++","++ show lb2 ++","++ show lb3 ++")"---- | Identify arcs.-type Selector lb = Arc lb -> Bool--hasLabel :: (Eq lb) => lb -> Arc lb -> Bool-hasLabel lbv lb = lbv `elem` arcLabels lb---- | Return all the labels in an arc.-arcLabels :: Arc lb -> [lb]-arcLabels (Arc lb1 lb2 lb3) = [lb1,lb2,lb3]---- | Return just the subject and object labels in the arc.-arcNodes :: Arc lb -> [lb]-arcNodes (Arc lb1 _ lb3) = [lb1,lb3]----------------------------------------------------------------------------------------  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  All rights reserved.------  This file is part of Swish.------  Swish is free software; you can redistribute it and/or modify---  it under the terms of the GNU General Public License as published by---  the Free Software Foundation; either version 2 of the License, or---  (at your option) any later version.------  Swish is distributed in the hope that it will be useful,---  but WITHOUT ANY WARRANTY; without even the implied warranty of---  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the---  GNU General Public License for more details.------  You should have received a copy of the GNU General Public License---  along with Swish; if not, write to:---    The Free Software Foundation, Inc.,---    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA------------------------------------------------------------------------------------
− src/Swish/RDF/GraphMatch.hs
@@ -1,622 +0,0 @@-{-# LANGUAGE FlexibleInstances, TypeSynonymInstances, MultiParamTypeClasses #-}-------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  GraphMatch---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  License     :  GPL V2------  Maintainer  :  Douglas Burke---  Stability   :  experimental---  Portability :  FlexibleInstances, TypeSynonymInstances, MultiParamTypeClasses------  This module contains graph-matching logic.------  The algorithm used is derived from a paper on RDF graph matching---  by Jeremy Carroll [1].------  [1] <http://www.hpl.hp.com/techreports/2001/HPL-2001-293.html>--------------------------------------------------------------------------------------module Swish.RDF.GraphMatch-      ( graphMatch,-        -- * Exported for testing-        LabelMap, GenLabelMap(..), LabelEntry, GenLabelEntry(..),-        ScopedLabel(..), makeScopedLabel, makeScopedArc,-        LabelIndex, EquivalenceClass, nullLabelVal, emptyMap,-        labelIsVar, labelHash,-        mapLabelIndex, setLabelHash, newLabelMap,-        graphLabels, assignLabelMap, newGenerationMap,-        graphMatch1, graphMatch2, equivalenceClasses, reclassify-      ) where--import Control.Exception.Base (assert)-import Control.Arrow (second)--import Data.Ord (comparing)-import Data.List (foldl', nub, sortBy, partition)-import Data.Hashable (combine)-import qualified Data.List as L--import Swish.RDF.GraphClass (Arc(..), Label(..),-                             arcLabels,-                             hasLabel, arcToTriple)-import Swish.Utils.LookupMap (LookupEntryClass(..), LookupMap(..),-                              makeLookupMap, listLookupMap, mapFind, mapReplaceAll,-                              mapAddIfNew, mapReplaceMap, mapMerge)-import Swish.Utils.ListHelpers (select, equiv, pairSort, pairGroup, pairUngroup)-------------------------------  Label index value type---------------------------------- | LabelIndex is a unique value assigned to each label, such that---  labels with different values are definitely different values---  in the graph;  e.g. do not map to each other in the graph---  bijection.  The first member is a generation counter that---  ensures new values are distinct from earlier passes.--type LabelIndex = (Int,Int)--nullLabelVal :: LabelIndex-nullLabelVal = (0,0)----------------------------  Label mapping types--------------------------data (Label lb) => GenLabelEntry lb lv = LabelEntry lb lv--type LabelEntry lb = GenLabelEntry lb LabelIndex--instance (Label lb, Eq lb, Show lb, Eq lv, Show lv)-    => LookupEntryClass (GenLabelEntry lb lv) lb lv where-    keyVal   (LabelEntry k v) = (k,v)-    newEntry (k,v)            = LabelEntry k v--instance (Label lb, Eq lb, Show lb, Eq lv, Show lv)-    => Show (GenLabelEntry lb lv) where-    show = entryShow--instance (Label lb, Eq lb, Show lb, Eq lv, Show lv)-    => Eq (GenLabelEntry lb lv) where-    (==) = entryEq---- | Type for label->index lookup table-data (Label lb, Eq lv, Show lv) => GenLabelMap lb lv =-    LabelMap Int (LookupMap (GenLabelEntry lb lv))--type LabelMap lb = GenLabelMap lb LabelIndex--instance (Label lb) => Show (LabelMap lb) where-    show = showLabelMap--instance (Label lb) => Eq (LabelMap lb) where-    LabelMap gen1 lmap1 == LabelMap gen2 lmap2 =-        gen1 == gen2 && es1 `equiv` es2-        where-            es1 = listLookupMap lmap1-            es2 = listLookupMap lmap2--emptyMap :: (Label lb) => LabelMap lb-emptyMap = LabelMap 1 $ makeLookupMap []-------------------------------  Equivalence class type---------------------------------- | Type for equivalence class description---  (An equivalence class is a collection of labels with---  the same LabelIndex value.)--type EquivalenceClass lb = (LabelIndex,[lb])--{--ecIndex :: EquivalenceClass lb -> LabelIndex-ecIndex = fst--}--ecLabels :: EquivalenceClass lb -> [lb]-ecLabels = snd--{--ecSize :: EquivalenceClass lb -> Int-ecSize = length . ecLabels--}--ecRemoveLabel :: (Label lb) => EquivalenceClass lb -> lb -> EquivalenceClass lb-ecRemoveLabel xs l = second (L.delete l) xs-----------------------------------------------------------------  Augmented graph label value - for graph matching------------------------------------------------------------------- | This instance of class label adds a graph identifier to---  each variable label, so that variable labels from---  different graphs are always seen as distinct values.------  The essential logic added by this class instance is embodied---  in the eq and hash functions.  Note that variable label hashes---  depend only on the graph in which they appear, and non-variable---  label hashes depend only on the variable.  Label hash values are---  used when initializing a label equivalence-class map (and, for---  non-variable labels, also for resolving hash collisions).--data (Label lb) => ScopedLabel lb = ScopedLabel Int lb--makeScopedLabel :: (Label lb) => Int -> lb -> ScopedLabel lb-makeScopedLabel = ScopedLabel --makeScopedArc :: (Label lb) => Int -> Arc lb -> Arc (ScopedLabel lb)-makeScopedArc scope = fmap (ScopedLabel scope)--instance (Label lb) => Label (ScopedLabel lb) where-    getLocal  lab    = error $ "getLocal for ScopedLabel: "++show lab-    makeLabel locnam = error $ "makeLabel for ScopedLabel: "++locnam-    labelIsVar (ScopedLabel _ lab)   = labelIsVar lab-    labelHash seed (ScopedLabel scope lab)-        | labelIsVar lab    = seed `combine` scope -- MH.hash seed $ show scope ++ "???"-        | otherwise         = labelHash seed lab--instance (Label lb) => Eq (ScopedLabel lb) where-    (ScopedLabel s1 l1) == (ScopedLabel s2 l2)-        = l1 == l2 && s1 == s2--instance (Label lb) => Show (ScopedLabel lb) where-    show (ScopedLabel s1 l1) = show s1 ++ ":" ++ show l1--instance (Label lb) => Ord (ScopedLabel lb) where-    compare (ScopedLabel s1 l1) (ScopedLabel s2 l2) =-        case compare s1 s2 of-            LT -> LT-            EQ -> compare l1 l2-            GT -> GT---- QUS: why doesn't this return Maybe (LabelMap (ScopedLabel lb)) ?---- | Graph matching function accepting two lists of arcs and---  returning a node map if successful----graphMatch :: (Label lb) =>-    (lb -> lb -> Bool)-    -- ^ a function that tests for additional constraints-    --   that may prevent the matching of a supplied pair-    --   of nodes.  Returns `True` if the supplied nodes may be-    --   matched.  (Used in RDF graph matching for checking-    --   that formula assignments are compatible.)-    -> [Arc lb] -- ^ the first graph to be compared, as a list of arcs-    -> [Arc lb] -- ^ the second graph to be compared, as a list of arcs-    -> (Bool,LabelMap (ScopedLabel lb))-    -- ^ If the first element is `True` then the second element maps each label-    --   to an equivalence class identifier, otherwise it is just-    --   `emptyMap`.-    ---graphMatch matchable gs1 gs2 =-    let-        sgs1    = {- trace "sgs1 " $ -} map (makeScopedArc 1) gs1-        sgs2    = {- trace "sgs2 " $ -} map (makeScopedArc 2) gs2-        ls1     = {- traceShow "ls1 " $ -} graphLabels sgs1-        ls2     = {- traceShow "ls2 " $ -} graphLabels sgs2-        lmap    = {- traceShow "lmap " $ -}-                  newGenerationMap $-                  assignLabelMap ls1 $-                  assignLabelMap ls2 emptyMap-        ec1     = {- traceShow "ec1 " $ -} equivalenceClasses lmap ls1-        ec2     = {- traceShow "ec2 " $ -} equivalenceClasses lmap ls2-        ecpairs = zip (pairSort ec1) (pairSort ec2)-        matchableScoped (ScopedLabel _ l1) (ScopedLabel _ l2) = matchable l1 l2-        match   = graphMatch1 False matchableScoped sgs1 sgs2 lmap ecpairs-    in-        if length ec1 /= length ec2 then (False,emptyMap) else match---- | Recursive graph matching function------  This function assumes that no variable label appears in both graphs.---  (Function `graphMatch`, which calls this, ensures that all variable---  labels are distinct.)------  TODO:------    * replace Equivalence class pair by @(index,[lb],[lb])@ ?------    * possible optimization:  the `graphMapEq` test should be---      needed only if `graphMatch2` has been used to guess a---      mapping;  either: ---          a) supply flag saying guess has been used, or---          b) move test to `graphMatch2` and use different---             test to prevent rechecking for each guess used.-----graphMatch1 :: -  (Label lb) -  => Bool-  -- ^ `True` if a guess has been used before trying this comparison,-  --   `False` if nodes are being matched without any guesswork-  -> (lb -> lb -> Bool)-  -- ^ Test for additional constraints that may prevent the matching-  --  of a supplied pair of nodes.  Returns `True` if the supplied-  --  nodes may be matched.-  -> [Arc lb] -  -- ^ (@gs1@ argument)-  --   first of two lists of arcs (triples) to be compared-  -> [Arc lb]-  -- ^ (@gs2@ argument)-  --   secind of two lists of arcs (triples) to be compared-  -> LabelMap lb-  -- ^ the map so far used to map label values to equivalence class-  --   values-  -> [(EquivalenceClass lb,EquivalenceClass lb)]-  -- ^ (the @ecpairs@ argument) list of pairs of corresponding-  --   equivalence classes of nodes from @gs1@ and @gs2@ that have not-  --   been confirmed in 1:1 correspondence with each other.  Each-  --   pair of equivalence classes contains nodes that must be placed-  --   in 1:1 correspondence with each other.-  ---  -> (Bool,LabelMap lb)-  -- ^ the pair @(match, map)@ where @match@ is @True@ if the supplied-  --   sets of arcs can be matched, in which case @map@ is a-  --   corresponding map from labels to equivalence class identifiers.-  --   When @match@ is @False@, @map@ is the most detailed equivalence-  --   class map obtained before a mismatch was detected or a guess-  --   was required -- this is intended to help identify where the-  --   graph mismatch may be.-graphMatch1 guessed matchable gs1 gs2 lmap ecpairs =-    let-        (secs,mecs) = partition uniqueEc ecpairs-        uniqueEc ( (_,[_])  , (_,[_])  ) = True-        uniqueEc (  _       ,  _       ) = False-        -        doMatch  ( (_,[l1]) , (_,[l2]) ) = labelMatch matchable lmap l1 l2-        doMatch  x = error $ "doMatch failue: " ++ show x -- keep -Wall happy--        ecEqSize ( (_,ls1)  , (_,ls2)  ) = length ls1 == length ls2-        eSize    ( (_,ls1)  , _        ) = length ls1-        ecCompareSize = comparing eSize-        (lmap',mecs',newEc,matchEc) = reclassify gs1 gs2 lmap mecs-        match2 = graphMatch2 matchable gs1 gs2 lmap $ sortBy ecCompareSize mecs-    in-        -- trace ("graphMatch1\nsingle ECs:\n"++show secs++-        --                   "\nmultiple ECs:\n"++show mecs++-        --                   "\n\n") $-        --  if mismatch in singleton equivalence classes, fail-        if not $ all doMatch secs then (False,lmap)-        else-        --  if no multi-member equivalence classes,-        --  check and return label map supplied-        -- trace ("graphMatch1\ngraphMapEq: "++show (graphMapEq lmap gs1 gs2)) $-        if null mecs then (graphMapEq lmap gs1 gs2,lmap)-        else-        --  if size mismatch in equivalence classes, fail-        -- trace ("graphMatch1\nall ecEqSize mecs: "++show (all ecEqSize mecs)) $-        -          --  invoke reclassification, and deal with result-          if not (all ecEqSize mecs) || not matchEc-            then (False, lmap)-            else if newEc-                   then graphMatch1 guessed matchable gs1 gs2 lmap' mecs'-                        --  if guess does not result in a match, return supplied label map-                   else if fst match2 then match2 else (False, lmap)--{--          if not $ all ecEqSize mecs then (False,lmap)-        else-        if not matchEc then (False,lmap)-        else-        if newEc then graphMatch1 guessed matchable gs1 gs2 lmap' mecs'-        else-        if fst match2 then match2 else (False,lmap)--}---- | Auxiliary graph matching function------  This function is called when deterministic decomposition of node---  mapping equivalence classes has run its course.------  It picks a pair of equivalence classes in ecpairs, and arbitrarily matches---  pairs of nodes in those equivalence classes, recursively calling the---  graph matching function until a suitable node mapping is discovered---  (success), or until all such pairs have been tried (failure).------  This function represents a point to which arbitrary choices are backtracked.---  The list comprehension 'glp' represents the alternative choices at the---  point of backtracking------  The selected pair of nodes are placed in a new equivalence class based on their---  original equivalence class value, but with a new NodeVal generation number.--graphMatch2 :: (Label lb) => (lb -> lb -> Bool)-    -> [Arc lb] -> [Arc lb]-    -> LabelMap lb -> [(EquivalenceClass lb,EquivalenceClass lb)]-    -> (Bool,LabelMap lb)-graphMatch2 _         _   _   _    [] = error "graphMatch2 sent an empty list" -- To keep -Wall happy-graphMatch2 matchable gs1 gs2 lmap ((ec1@(ev1,ls1),ec2@(ev2,ls2)):ecpairs) =-    let-        v1 = snd ev1-        --  Return any equivalence-mapping obtained by matching a pair-        --  of labels in the supplied list, or Nothing.-        try []            = (False,lmap)-        try ((l1,l2):lps) = if isEquiv try1 l1 l2 then try1 else try lps-            where-                try1     = graphMatch1 True matchable gs1 gs2 lmap' ecpairs'-                lmap'    = newLabelMap lmap [(l1,v1),(l2,v1)]-                ecpairs' = ((ev',[l1]),(ev',[l2])):ec':ecpairs-                ev'      = mapLabelIndex lmap' l1-                ec'      = (ecRemoveLabel ec1 l1, ecRemoveLabel ec2 l2)-                -- [[[TODO: replace this: if isJust try ?]]]-                isEquiv (False,_)   _  _  = False-                isEquiv (True,lm) x1 x2 =-                    mapLabelIndex m1 x1 == mapLabelIndex m2 x2-                    where-                        m1 = remapLabels gs1 lm [x1]-                        m2 = remapLabels gs2 lm [x2]-        --  glp is a list of label-pair candidates for matching,-        --  selected from the first label-equivalence class.-        --  NOTE:  final test is call of external matchable function-        glp = [ (l1,l2) | l1 <- ls1 , l2 <- ls2 , matchable l1 l2 ]-    in-        assert (ev1==ev2) -- "GraphMatch2: Equivalence class value mismatch" $-        $ try glp---- this was in Swish.Utils.MiscHelpers along with a simple hash-based function--- based on Sedgewick, Algorithms in C, p233. As we have now moved to using--- Data.Hashable it is not clear whether this is still necessary or sensible.----hashModulus :: Int-hashModulus = 16000001---- | Returns a string representation  of a LabelMap value----showLabelMap :: (Label lb) => LabelMap lb -> String-showLabelMap (LabelMap gn lmap) =-    "LabelMap gen="++ Prelude.show gn ++", map="++-    foldl' (++) "" (map (("\n    "++) . Prelude.show) es)-    where-        es = listLookupMap lmap---- | Map a label to its corresponding label index value in the supplied LabelMap----mapLabelIndex :: (Label lb) => LabelMap lb -> lb -> LabelIndex-mapLabelIndex (LabelMap _ lxms) lb = mapFind nullLabelVal lb lxms---- | Confirm that a given pair of labels are matchable, and are---  mapped to the same value by the supplied label map----labelMatch :: (Label lb)-    =>  (lb -> lb -> Bool) -> LabelMap lb -> lb -> lb -> Bool-labelMatch matchable lmap l1 l2 =-    matchable l1 l2 && (mapLabelIndex lmap l1 == mapLabelIndex lmap l1)---- | Replace selected values in a label map with new values from the supplied---  list of labels and new label index values.  The generation number is---  supplied from the current label map.  The generation number in the---  resulting label map is incremented.----newLabelMap :: (Label lb) => LabelMap lb -> [(lb,Int)] -> LabelMap lb-newLabelMap lmap []       = newGenerationMap lmap-newLabelMap lmap (lv:lvs) = setLabelHash (newLabelMap lmap lvs) lv---- | Replace a label and its associated value in a label map---  with a new value using the supplied hash value and the current---  `LabelMap` generation number.  If the key is not found, then no change---  is made to the label map.--setLabelHash :: (Label lb)-    => LabelMap lb -> (lb,Int) -> LabelMap lb-setLabelHash  (LabelMap g lmap) (lb,lh) =-    LabelMap g ( mapReplaceAll lmap $ newEntry (lb,(g,lh)) )---- | Increment the generation of the label map.------  Returns a new label map identical to the supplied value---  but with an incremented generation number.----newGenerationMap :: (Label lb) => LabelMap lb -> LabelMap lb-newGenerationMap (LabelMap g lvs) = LabelMap (g+1) lvs---- | Scan label list, assigning initial label map values,---  adding new values to the label map supplied.------  Label map values are assigned on the basis of the---  label alone, without regard for it's connectivity in---  the graph.  (cf. `reclassify`).------  All variable node labels are assigned the same initial---  value, as they may be matched with each other.----assignLabelMap :: (Label lb) => [lb] -> LabelMap lb -> LabelMap lb-assignLabelMap ns lmap = foldl' (flip assignLabelMap1) lmap ns--assignLabelMap1 :: (Label lb) => lb -> LabelMap lb -> LabelMap lb-assignLabelMap1 lab (LabelMap g lvs) = LabelMap g lvs'-    where-        lvs' = mapAddIfNew lvs $ newEntry (lab,(g,initVal lab))----  Calculate initial value for a node--initVal :: (Label lb) => lb -> Int-initVal = hashVal 0--hashVal :: (Label lb) => Int -> lb -> Int-hashVal seed lab =-  if labelIsVar lab then seed `combine` 23 else labelHash seed lab-  -- if labelIsVar lab then hash seed "???" else labelHash seed lab--equivalenceClasses :: -  (Label lb) -  => LabelMap lb -- ^ label map-  -> [lb]        -- ^ list of nodes to be reclassified-  -> [EquivalenceClass lb]-  -- ^ the equivalence classes of the supplied labels under the-  --   supplied label map-equivalenceClasses lmap ls =-    pairGroup $ map labelPair ls-    where-        labelPair l = (mapLabelIndex lmap l,l)---- | Reclassify labels------  Examines the supplied label equivalence classes (based on the supplied---  label map), and evaluates new equivalence subclasses based on node---  values and adjacency (for variable nodes) and rehashing---  (for non-variable nodes).------  Note, assumes that all all equivalence classes supplied are---  non-singletons;  i.e. contain more than one label.----reclassify :: -  (Label lb) -  => [Arc lb] -  -- ^ (the @gs1@ argument) the first of two lists of arcs (triples) to perform a-  --   basis for reclassifying the labels in the first equivalence-  --   class in each pair of @ecpairs@.-  -> [Arc lb]-  -- ^ (the @gs2@ argument) the second of two lists of arcs (triples) to perform a-  --   basis for reclassifying the labels in the second equivalence-  --   class in each pair of the @ecpairs@ argument-  -> LabelMap lb -  -- ^ the label map used for classification of the labels in-  --   the supplied equivalence classes-  -> [(EquivalenceClass lb,EquivalenceClass lb)]-  -- ^ (the @ecpairs@ argument) a list of pairs of corresponding equivalence classes of-  --   nodes from @gs1@ and @gs2@ that have not been confirmed-  --   in 1:1 correspondence with each other.-  -> (LabelMap lb,[(EquivalenceClass lb,EquivalenceClass lb)],Bool,Bool)-  -- ^ The output tuple consists of:-  ---  --  1) a revised label map reflecting the reclassification-  ---  --  2) a new list of equivalence class pairs based on the-  --   new node map-  ---  --  3) if the reclassification partitions any of the-  --     supplied equivalence classes then `True`, else `False`-  ---  --  4) if reclassification results in each equivalence class-  --     being split same-sized equivalence classes in the two graphs,-  --     then `True`, otherwise `False`.--reclassify gs1 gs2 lmap@(LabelMap _ lm) ecpairs =-    assert (gen1==gen2) -- "Label map generation mismatch"-      (LabelMap gen1 lm',ecpairs',newPart,matchPart)-    where-        LabelMap gen1 lm1 =-            remapLabels gs1 lmap $ foldl1 (++) $ map (ecLabels . fst) ecpairs-        LabelMap gen2 lm2 =-            remapLabels gs2 lmap $ foldl1 (++) $ map (ecLabels . snd) ecpairs-        lm' = mapReplaceMap lm $ mapMerge lm1 lm2-        -        tmap f (a,b) = (f a, f b)-        -        -- ecGroups :: [([EquivalenceClass lb],[EquivalenceClass lb])]-        ecGroups  = map (tmap remapEc) ecpairs-        ecpairs'  = concatMap (uncurry zip) ecGroups-        newPart   = any pairG1 lenGroups-        matchPart = all pairEq lenGroups-        lenGroups = map (tmap length) ecGroups-        pairEq = uncurry (==)-        pairG1 (p1,p2) = p1 > 1 || p2 > 1-        remapEc = pairGroup . map (newIndex lm') . pairUngroup -        newIndex x (_,lab) = (mapFind nullLabelVal lab x,lab)---- | Calculate a new index value for a supplied list of labels based on the---  supplied label map and adjacency calculations in the supplied graph----remapLabels :: -  (Label lb) -  => [Arc lb] -- ^ arcs used for adjacency calculations when remapping-  -> LabelMap lb -- ^ the current label index values-  -> [lb] -- ^ the graph labels for which new mappings are to be created-  -> LabelMap lb-  -- ^ the updated label map containing recalculated label index values-  -- for the given graph labels. The label map generation number is-  -- incremented by 1.-remapLabels gs lmap@(LabelMap gen _) ls =-    LabelMap gen' (LookupMap newEntries)-    where-        gen'                = gen+1-        newEntries          = [ newEntry (l, (gen',newIndex l)) | l <- ls ]-        newIndex l-            | labelIsVar l  = mapAdjacent l     -- adjacency classifies variable labels-            | otherwise     = hashVal gen l     -- otherwise rehash (to disentangle collisions)-        -- mapAdjacent l       = sum (sigsOver l) `rem` hashModulus-        mapAdjacent l       = sum (sigsOver l) `combine` hashModulus -- is this a sensible replacement for `rem` MH.hashModulus        -        sigsOver l          = select (hasLabel l) gs (arcSignatures lmap gs)---- | Return list of distinct labels used in a graph--graphLabels :: (Label lb) => [Arc lb] -> [lb]-graphLabels = nub . concatMap arcLabels---- | Calculate a signature value for each arc that can be used in constructing an---   adjacency based value for a node.  The adjacancy value for a label is obtained---   by summing the signatures of all statements containing that label.----arcSignatures :: -  (Label lb) -  => LabelMap lb -- ^ the current label index values-  -> [Arc lb] -- ^ calculate signatures for these arcs-  -> [Int] -- ^ the signatures of the arcs-arcSignatures lmap =-    map (sigCalc . arcToTriple) -    where-        sigCalc (s,p,o)  =-            ( labelVal2 s +-              labelVal2 p * 3 +-              labelVal2 o * 5 )-            `combine` hashModulus-            -- `rem` hashModulus-          -        labelVal         = mapLabelIndex lmap-        labelVal2        = uncurry (*) . labelVal---- | Return a new graph that is supplied graph with every node/arc---  mapped to a new value according to the supplied function.------  Used for testing for graph equivalence under a supplied---  label mapping;  e.g.------  >  if ( graphMap nodeMap gs1 ) `equiv` ( graphMap nodeMap gs2 ) then (same)----graphMap :: (Label lb) => LabelMap lb -> [Arc lb] -> [Arc LabelIndex]-graphMap = map . fmap . mapLabelIndex  -- graphMapStmt---- | Compare a pair of graphs for equivalence under a given mapping---   function.------  This is used to perform the ultimate test that two graphs are---  indeed equivalent:  guesswork in `graphMatch2` means that it is---  occasionally possible to construct a node mapping that generates---  the required singleton equivalence classes, but does not fully---  reflect the topology of the graphs.--graphMapEq :: (Label lb) => LabelMap lb -> [Arc lb] -> [Arc lb] -> Bool-graphMapEq lmap gs1 gs2 = graphMap lmap gs1 `equiv` graphMap lmap gs2----------------------------------------------------------------------------------------  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  All rights reserved.------  This file is part of Swish.------  Swish is free software; you can redistribute it and/or modify---  it under the terms of the GNU General Public License as published by---  the Free Software Foundation; either version 2 of the License, or---  (at your option) any later version.------  Swish is distributed in the hope that it will be useful,---  but WITHOUT ANY WARRANTY; without even the implied warranty of---  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the---  GNU General Public License for more details.------  You should have received a copy of the GNU General Public License---  along with Swish; if not, write to:---    The Free Software Foundation, Inc.,---    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA------------------------------------------------------------------------------------
− src/Swish/RDF/GraphMem.hs
@@ -1,166 +0,0 @@-{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}-{-# LANGUAGE DeriveFunctor, DeriveFoldable, DeriveTraversable #-}-------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  GraphMem---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  License     :  GPL V2------  Maintainer  :  Douglas Burke---  Stability   :  experimental---  Portability :  DeriveFunctor, DeriveFoldable, DeriveTraversable, FlexibleInstances, MultiParamTypeClasses------  This module defines a simple memory-based graph instance.----------------------------------------------------------------------------------------------------------------------------------------------------- Simple labelled directed graph value---------------------------------------------------------------module Swish.RDF.GraphMem-    ( GraphMem(..)-    , setArcs, getArcs, add, delete, extract, labels-    , LabelMem(..)-    , labelIsVar, labelHash-      -- For debug/test:-    , matchGraphMem-    ) where--import Swish.RDF.GraphClass-import Swish.RDF.GraphMatch--- import Swish.Utils.MiscHelpers (hash)--import Data.Hashable (Hashable(..), combine)-import Data.Ord (comparing)--import qualified Data.Foldable as F-import qualified Data.Traversable as T---- | Memory-based graph type --data GraphMem lb = GraphMem { arcs :: [Arc lb] }-                   deriving (Functor, F.Foldable, T.Traversable)-                            -instance (Label lb) => LDGraph GraphMem lb where-    getArcs      = arcs-    setArcs as g = g { arcs=as }-    -- gmap f g = g { arcs = (map $ fmap f) (arcs g) }-    containedIn = error "containedIn for LDGraph GraphMem lb is undefined!" -- TODO: should there be one defined?--instance (Label lb) => Eq (GraphMem lb) where-    (==) = graphEq--instance (Label lb) => Show (GraphMem lb) where-    show = graphShow--graphShow   :: (Label lb) => GraphMem lb -> String-graphShow g = "Graph:" ++ foldr ((++) . ("\n    " ++) . show) "" (arcs g)--{--toGraph :: (Label lb) => [Arc lb] -> GraphMem lb-toGraph as = GraphMem { arcs=nub as }--}---- |  Return Boolean graph equality--graphEq :: (Label lb) => GraphMem lb -> GraphMem lb -> Bool-graphEq g1 g2 = fst ( matchGraphMem g1 g2 )---- | GraphMem matching function accepting GraphMem value and returning---  node map if successful----matchGraphMem ::-  (Label lb)-  => GraphMem lb -  -> GraphMem lb-  -> (Bool,LabelMap (ScopedLabel lb))-  -- ^ if the first element is @True@ then the second value is a label-  --   map that maps each label to an equivalence-class identifier,-  --   otherwise `emptyMap`.-  ---matchGraphMem g1 g2 =-    let-        gs1     = arcs g1-        gs2     = arcs g2-        matchable l1 l2-            | labelIsVar l1 && labelIsVar l2 = True-            | labelIsVar l1 || labelIsVar l2 = False-            | otherwise                      = l1 == l2-    in-        graphMatch matchable gs1 gs2--{---- |  Return bijection between two graphs, or empty list-graphBiject :: (Label lb) => GraphMem lb -> GraphMem lb -> [(lb,lb)]-graphBiject g1 g2 = if null lmap then [] else zip (sortedls g1) (sortedls g2)-    where-        lmap        = graphMatch g1 g2-        sortedls g  = map snd $-                      (sortBy indexComp) $-                      equivalenceClasses (graphLabels $ arcs g) lmap-        classComp ec1 ec2 = indexComp (classIndexVal ec1) (classIndexVal ec2)-        indexComp (g1,v1) (g2,v2)-            | g1 == g2  = compare v1 v2-            | otherwise = compare g1 g2--}---- |  Minimal graph label value - for testing--data LabelMem-    = LF String-    | LV String--instance Hashable LabelMem where-  hash (LF l) = 1 `hashWithSalt` l-  hash (LV l) = 2 `hashWithSalt` l-  hashWithSalt salt (LF l) = salt `combine` 1 `hashWithSalt` l-  hashWithSalt salt (LV l) = salt `combine` 2 `hashWithSalt` l--instance Label LabelMem where-    labelIsVar (LV _)   = True-    labelIsVar _        = False-    getLocal   (LV loc) = loc-    getLocal   lab      = error "getLocal of non-variable label: " ++ show lab-    makeLabel           = LV -    -- labelHash  seed lb  = hash seed (show lb)-    labelHash = hashWithSalt--instance Eq LabelMem where-    (LF l1) == (LF l2)  = l1 == l2-    (LV l1) == (LV l2)  = l1 == l2-    _ == _              = False--instance Show LabelMem where-    show (LF l1)        = '!' : l1-    show (LV l2)        = '?' : l2--instance Ord LabelMem where-    compare = comparing show ----------------------------------------------------------------------------------------  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  All rights reserved.------  This file is part of Swish.------  Swish is free software; you can redistribute it and/or modify---  it under the terms of the GNU General Public License as published by---  the Free Software Foundation; either version 2 of the License, or---  (at your option) any later version.------  Swish is distributed in the hope that it will be useful,---  but WITHOUT ANY WARRANTY; without even the implied warranty of---  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the---  GNU General Public License for more details.------  You should have received a copy of the GNU General Public License---  along with Swish; if not, write to:---    The Free Software Foundation, Inc.,---    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA------------------------------------------------------------------------------------
− src/Swish/RDF/GraphPartition.hs
@@ -1,570 +0,0 @@------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  GraphPartition---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  License     :  GPL V2------  Maintainer  :  Douglas Burke---  Stability   :  experimental---  Portability :  H98------  This module contains functions for partitioning a graph into subgraphs---  that rooted from different subject nodes.--------------------------------------------------------------------------------------module Swish.RDF.GraphPartition-    ( PartitionedGraph(..), getArcs, getPartitions-    , GraphPartition(..), node, toArcs-    , partitionGraph, comparePartitions-    , partitionShowP-    )-where--import Swish.RDF.GraphClass-    ( Label(..)-    , Arc(..), arcSubj, arcObj, -    -- , hasLabel, arcLabels-    )--import Data.List (foldl', partition)--import Control.Monad.State-    ( MonadState(..), State, evalState )--import Data.Maybe (isJust, fromJust, mapMaybe)------------------------------------------------------------------  Data type for a partitioned graph----------------------------------------------------------------- |Representation of a graph as a collection of (possibly nested)---  partitions.  Each node in the graph appears at least once as the---  root value of a 'GraphPartition' value:------  * Nodes that are the subject of at least one statement appear as---    the first value of exactly one 'PartSub' constructor, and may---    also appear in any number of 'PartObj' constructors.------  * Nodes appearing only as objects of statements appear only in---    'PartObj' constructors.--data PartitionedGraph lb = PartitionedGraph [GraphPartition lb]-    deriving (Eq,Show)--getArcs :: PartitionedGraph lb -> [Arc lb]-getArcs (PartitionedGraph ps) = concatMap toArcs ps--getPartitions :: PartitionedGraph lb -> [GraphPartition lb]-getPartitions (PartitionedGraph ps) = ps---- QUS: is the list always guaranteed to be non-empty in PartSub?-data GraphPartition lb-    = PartObj lb-    | PartSub lb [(lb,GraphPartition lb)]--node :: GraphPartition lb -> lb-node (PartObj ob)   = ob-node (PartSub sb _) = sb--toArcs :: GraphPartition lb -> [Arc lb]-toArcs (PartObj _)      = []-toArcs (PartSub sb prs) = concatMap toArcs1 prs-    where-        toArcs1 (pr,ob) = Arc sb pr (node ob) : toArcs ob--instance (Label lb) => Eq (GraphPartition lb) where-    (==) = partitionEq--instance (Label lb) => Show (GraphPartition lb) where-    show = partitionShow----  Equality is based on total structural equivalence.---  This is not the same as graph equality.-partitionEq :: (Label lb) => GraphPartition lb -> GraphPartition lb -> Bool-partitionEq (PartObj o1)    (PartObj o2)    = o1 == o2-partitionEq (PartSub s1 p1) (PartSub s2 p2) = s1 == s2 && p1 == p2-partitionEq  _               _              = False--partitionShow :: (Label lb) => GraphPartition lb -> String-partitionShow (PartObj ob)          = show ob-partitionShow (PartSub sb [])       = "(" ++ show sb ++ ")" -- just to make -Wall happy, is this sensible?-partitionShow (PartSub sb (pr:prs)) =-    "("++ show sb ++ " " ++ showpr pr ++ concatMap ((" ; "++).showpr) prs ++ ")"-    where-        showpr (a,b) = show a ++ " " ++ show b-  -partitionShowP :: (Label lb) => String -> GraphPartition lb -> String-partitionShowP _    (PartObj ob)          = show ob-partitionShowP pref (PartSub sb [])       = pref ++ "(" ++ show sb ++ ")" -- just to make -Wall happy, is this sensible?-partitionShowP pref (PartSub sb (pr:prs)) =-    pref++"("++ show sb ++ " " ++ showpr pr ++ concatMap (((pref++"  ; ")++).showpr) prs ++ ")"-    where-        showpr (a,b) = show a ++ " " ++ partitionShowP (pref++"  ") b-----------------------------------------------------------------  Creating partitioned graphs------------------------------------------------------------------- |Turning a partitioned graph into a flat graph is easy.---  The interesting challenge is to turn a flat graph into a---  partitioned graph that is more useful for certain purposes.---  Currently, I'm interested in:---        ---  (1) isolating differences between graphs---        ---  (2) pretty-printing graphs------  For (1), the goal is to separate subgraphs that are known---  to be equivalent from subgraphs that are known to be different,---  such that: ------  * different sub-graphs are minimized,------  * different---  sub-graphs are placed into 1:1 correspondence (possibly with null---  subgraphs), and------  * only deterministic matching decisions are made.------  For (2), the goal is to decide when a subgraph is to be treated---  as nested in another partition, or treated as a new top-level partition.---  If a subgraph is referenced by exactly one graph partition, it should---  be nested in that partition, otherwise it should be a new top-level---  partition.------  Strategy.  Examining just subject and object nodes:------  * all non-blank subject nodes are the root of a top-level partition------  * blank subject nodes that are not the object of exactly one statement---     are the root of a top-level partition.------  * blank nodes referenced as the object of exactly 1 statement---     of an existing partition are the root of a sub-partition of the---     refering partition.------  * what remain are circular chains of blank nodes not referenced---     elsewhere:  for each such chain, pick a root node arbitrarily.----partitionGraph :: (Label lb) => [Arc lb] -> PartitionedGraph lb-partitionGraph arcs =-    makePartitions fixs topv1 intv1-    where-        (fixs,vars)  = partition isNonVar $ collect arcSubj arcs-        vars1        = collectMore arcObj arcs vars-        (intv,topv)  = partition objOnce vars1-        intv1        = map stripObj intv-        topv1        = map stripObj topv-        isNonVar     = not . labelIsVar . fst-        objOnce      = isSingle . snd . snd-        isSingle [_] = True-        isSingle _   = False-        stripObj (k,(s,_)) = (k,s)---- Local state type for partitioning function-type MakePartitionState lb = ([(lb,[Arc lb])],[(lb,[Arc lb])],[(lb,[Arc lb])])--makePartitions :: (Eq lb) =>-    [(lb,[Arc lb])] -> [(lb,[Arc lb])] -> [(lb,[Arc lb])] -> PartitionedGraph lb-makePartitions fixs topv intv =-    PartitionedGraph $ evalState (makePartitions1 []) (fixs,topv,intv)---- Use a state monad to keep track of arcs that have been incorporated into--- the resulting list of graph partitions.  The collections of arcs used to--- generate the list of partitions are supplied as theinitial state of the--- monad (see call of evalState above).----makePartitions1 :: (Eq lb) =>-    [(lb,[Arc lb])] -> State (MakePartitionState lb) [GraphPartition lb]-makePartitions1 [] =-    do  { s <- pickNextSubject-        ; if null s then return [] else makePartitions1 s-        }-makePartitions1 (sub:subs) =-    do  { ph <- makePartitions2 sub-        ; pt <- makePartitions1 subs-        ; return $ ph++pt-        }--makePartitions2 :: (Eq lb) =>-    (lb,[Arc lb]) -> State (MakePartitionState lb) [GraphPartition lb]-makePartitions2 subs =-    do  { (part,moresubs) <- makeStatements subs-        ; moreparts <- if not (null moresubs) then-            makePartitions1 moresubs-          else-            return []-        ; return $ part:moreparts-        }--makeStatements :: (Eq lb) =>-    (lb,[Arc lb])-    -> State (MakePartitionState lb) (GraphPartition lb,[(lb,[Arc lb])])-makeStatements (sub,stmts) =-    do  { propmore <- mapM makeStatement stmts-        ; let (props,moresubs) = unzip propmore-        ; return (PartSub sub props,concat moresubs)-        }--makeStatement :: (Eq lb) =>-    Arc lb-    -> State (MakePartitionState lb) ((lb,GraphPartition lb),[(lb,[Arc lb])])-makeStatement (Arc _ prop obj) =-    do  { intobj <- pickIntSubject obj-        ; (gpobj,moresubs) <- if null intobj-          then-            do  { ms <- pickVarSubject obj-                ; return (PartObj obj,ms)-                }-          else-            makeStatements (head intobj)-        ; return ((prop,gpobj),moresubs)-        }--pickNextSubject :: State (MakePartitionState lb) [(lb,[Arc lb])]-pickNextSubject =-    do  { (a1,a2,a3) <- get-        ; let (s,st) = case (a1,a2,a3) of-                (s1h:s1t,s2,s3) -> ([s1h],(s1t,s2,s3))-                ([],s2h:s2t,s3) -> ([s2h],([],s2t,s3))-                ([],[],s3h:s3t) -> ([s3h],([],[],s3t))-                ([],[],[])      -> ([]   ,([],[],[] ))-        ; put st-        ; return s-        }--pickIntSubject :: (Eq lb) =>-    lb -> State (MakePartitionState lb) [(lb,[Arc lb])]-pickIntSubject sub =-    do  { (s1,s2,s3) <- get-        ; let varsub = removeBy (\x->(x==).fst) sub s3-        ; if isJust varsub then-            do  { let (vs,s3new) = fromJust varsub-                ; put (s1,s2,s3new)-                ; return [vs]-                }-          else-            return []-        }--pickVarSubject :: (Eq lb) =>-    lb -> State (MakePartitionState lb) [(lb,[Arc lb])]-pickVarSubject sub =-    do  { (s1,s2,s3) <- get-        ; let varsub = removeBy (\x->(x==).fst) sub s2-        ; if isJust varsub then-            do  { let (vs,s2new) = fromJust varsub-                ; put (s1,s2new,s3)-                ; return [vs]-                }-          else-            return []-        }-----------------------------------------------------------------  Other useful functions-------------------------------------------------------------------  Create a list of pairs of corresponding Partitions that---  are unequal--comparePartitions :: (Label lb) =>-    PartitionedGraph lb -> PartitionedGraph lb-    -> [(Maybe (GraphPartition lb),Maybe (GraphPartition lb))]-comparePartitions (PartitionedGraph gp1) (PartitionedGraph gp2) =-    comparePartitions1 (reverse gp1) (reverse gp2)--comparePartitions1 :: (Label lb) =>-    [GraphPartition lb] -> [GraphPartition lb]-    -> [(Maybe (GraphPartition lb),Maybe (GraphPartition lb))]-comparePartitions1 pg1 pg2 =-        ds ++ [ (Just r1p,Nothing) | r1p<-r1 ]-           ++ [ (Nothing,Just r2p) | r2p<-r2 ]-    where-        (ds,r1,r2) = listDifferences comparePartitions2 pg1 pg2----  Compare two graph partitions, with three possible outcomes:---    Nothing    -> no match---    Just []    -> total match---    Just [...] -> partial match, with mismatched sub-partitions listed.------  A partial match occurs when the leading nodes are non-variable and---  equal, but something else in the partition does not match.------  A complete match can be achieved with variable nodes that have---  different labels----comparePartitions2 :: (Label lb) =>-    GraphPartition lb -> GraphPartition lb-    -> Maybe [(Maybe (GraphPartition lb),Maybe (GraphPartition lb))]-comparePartitions2 (PartObj l1) (PartObj l2) =-    if matchNodes l1 l2 then Just [] else Nothing-comparePartitions2 pg1@(PartSub l1 p1s) pg2@(PartSub l2 p2s) =-    if match then comp1 else Nothing-    where-        comp1  = case comparePartitions3 l1 l2 p1s p2s of-                    Nothing -> if matchVar then Nothing-                                           else Just [(Just pg1,Just pg2)]-                    Just [] -> Just []-                    Just ps -> {- if matchVar then Nothing else -} Just ps-        matchVar = labelIsVar l1 && labelIsVar l2-        match    = matchVar || l1 == l2-comparePartitions2 pg1 pg2 =-    if not (labelIsVar l1) && l1 == l2-        then Just [(Just pg1,Just pg2)]-        else Nothing-    where-        l1 = node pg1-        l2 = node pg2--comparePartitions3 :: (Label lb) =>-    lb -> lb -> [(lb,GraphPartition lb)] -> [(lb,GraphPartition lb)]-    -> Maybe [(Maybe (GraphPartition lb),Maybe (GraphPartition lb))]-comparePartitions3 l1 l2 s1s s2s = Just $-        ds ++ [ (Just (PartSub l1 [r1p]),Nothing) | r1p<-r1 ]-           ++ [ (Nothing,Just (PartSub l2 [r2p])) | r2p<-r2 ]-    where-        (ds,r1,r2) = listDifferences (comparePartitions4 l1 l2) s1s s2s--comparePartitions4 :: (Label lb) =>-    lb -> lb -> (lb,GraphPartition lb) -> (lb,GraphPartition lb)-    -> Maybe [(Maybe (GraphPartition lb),Maybe (GraphPartition lb))]-comparePartitions4 _ _ (p1,o1) (p2,o2) =-    if matchNodes p1 p2 then comp1 else Nothing-    where-        comp1   = case comparePartitions2 o1 o2 of-                    Nothing -> Just [(Just o1,Just o2)]-                    ds      -> ds--matchNodes :: (Label lb) => lb -> lb -> Bool-matchNodes l1 l2-    | labelIsVar l1 = labelIsVar l2-    | otherwise     = l1 == l2------------------------------------------------------------------  Helpers----------------------------------------------------------------- |Collect a list of items by some comparison of a selected component---  or other derived value.------  cmp     a comparison function that determines if a pair of values---          should be grouped together---  sel     a function that selects a value from any item------  Example:    collect fst [(1,'a'),(2,'b'),(1,'c')] =---                  [(1,[(1,'a'),(1,'c')]),(2,[(2,'b')])]----collect :: (Eq b) => (a->b) -> [a] -> [(b,[a])]-collect = collectBy (==)--collectBy :: (b->b->Bool) -> (a->b) -> [a] -> [(b,[a])]-collectBy cmp sel = map reverseCollection . collectBy1 cmp sel []--collectBy1 :: (b->b->Bool) -> (a->b) -> [(b,[a])] -> [a] -> [(b,[a])]-collectBy1 _   _   sofar []     = sofar-collectBy1 cmp sel sofar (a:as) =-    collectBy1 cmp sel (collectBy2 cmp sel a sofar) as--collectBy2 :: (b->b->Bool) -> (a->b) -> a -> [(b,[a])] -> [(b,[a])]-collectBy2 _   sel a [] = [(sel a,[a])]-collectBy2 cmp sel a (col@(k,as):cols)-    | cmp ka k  = (k,a:as):cols-    | otherwise = col:collectBy2 cmp sel a cols-    where-        ka = sel a--reverseCollection :: (b,[a]) -> (b,[a])-reverseCollection (k,as) = (k,reverse as)--{---- Example/test:-testCollect1 :: [(Int, [(Int, Char)])]-testCollect1 = collect fst [(1,'a'),(2,'b'),(1,'c'),(1,'d'),(2,'d'),(3,'d')]--testCollect2 :: Bool-testCollect2 = testCollect1-                == [ (1,[(1,'a'),(1,'c'),(1,'d')])-                   , (2,[(2,'b'),(2,'d')])-                   , (3,[(3,'d')])-                   ]--}---- |Add new values to an existing list of collections.---  The list of collections is not extended, but each collection is---  augmented with a further list of values from the supplied list,---  each of which are related to the existing collection in some way.------  NOTE: the basic pattern of 'collect' and 'collectMore' is similar,---  and might be generalized into a common set of core functions.----collectMore :: (Eq b) => (a->b) -> [a] -> [(b,c)] -> [(b,(c,[a]))]-collectMore = collectMoreBy (==)--collectMoreBy ::-    (b->b->Bool) -> (a->b) -> [a] -> [(b,c)] -> [(b,(c,[a]))]-collectMoreBy cmp sel as cols =-    map reverseMoreCollection $-    collectMoreBy1 cmp sel as (map (\ (b,cs) -> (b,(cs,[])) ) cols)--collectMoreBy1 ::-    (b->b->Bool) -> (a->b) -> [a] -> [(b,(c,[a]))] -> [(b,(c,[a]))]-collectMoreBy1 _   _   []     cols = cols-collectMoreBy1 cmp sel (a:as) cols =-    collectMoreBy1 cmp sel as (collectMoreBy2 cmp sel a cols)--collectMoreBy2 ::-    (b->b->Bool) -> (a->b) -> a -> [(b,(c,[a]))] -> [(b,(c,[a]))]-collectMoreBy2 _   _   _ [] = []-collectMoreBy2 cmp sel a (col@(k,(b,as)):cols)-    | cmp (sel a) k = (k,(b,a:as)):cols-    | otherwise     = col:collectMoreBy2 cmp sel a cols--reverseMoreCollection :: (b,(c,[a])) -> (b,(c,[a]))-reverseMoreCollection (k,(c,as)) = (k,(c,reverse as))--{---- Example/test:-testCollectMore1 =-    collectMore snd [(111,1),(112,1),(211,2),(311,3),(411,4)] testCollect1--testCollectMore2 :: Bool-testCollectMore2 = testCollectMore1-                == [ (1,([(1,'a'),(1,'c'),(1,'d')],[(111,1),(112,1)]))-                   , (2,([(2,'b'),(2,'d')],[(211,2)]))-                   , (3,([(3,'d')],[(311,3)]))-                   ]--}---- |Remove supplied element from a list using the supplied test---  function, and return Just the element remoived and the---  remaining list, or Nothing if no element was matched for removal.----{--remove :: (Eq a) => a -> [a] -> Maybe (a,[a])-remove = removeBy (==)--testRemove1  = remove 3 [1,2,3,4,5]-testRemove2  = testRemove1 == Just (3,[1,2,4,5])-testRemove3  = remove 3 [1,2,4,5]-testRemove4  = testRemove3 == Nothing-testRemove5  = remove 5 [1,2,4,5]-testRemove6  = testRemove5 == Just (5,[1,2,4])-testRemove7  = remove 1 [1,2,4]-testRemove8  = testRemove7 == Just (1,[2,4])-testRemove9  = remove 2 [2]-testRemove10 = testRemove9 == Just (2,[])---}--removeBy :: (b->a->Bool) -> b -> [a] -> Maybe (a,[a])-removeBy cmp a0 as = removeBy1 cmp a0 as []--removeBy1 :: (b->a->Bool) -> b -> [a] -> [a] -> Maybe (a,[a])-removeBy1 _   _  []     _     = Nothing-removeBy1 cmp a0 (a:as) sofar-    | cmp a0 a  = Just (a,reverseTo sofar as)-    | otherwise = removeBy1 cmp a0 as (a:sofar)---- |Reverse first argument, prepending the result to the second argument----reverseTo :: [a] -> [a] -> [a]-reverseTo front back = foldl' (flip (:)) back front---- |Remove each element from a list, returning a list of pairs,---  each of which is the element removed and the list remaining.----removeEach :: [a] -> [(a,[a])]-removeEach [] = []-removeEach (a:as) = (a,as):[ (a1,a:a1s) | (a1,a1s) <- removeEach as ]--{--testRemoveEach1 = removeEach [1,2,3,4,5]-testRemoveEach2 = testRemoveEach1 ==-    [ (1,[2,3,4,5])-    , (2,[1,3,4,5])-    , (3,[1,2,4,5])-    , (4,[1,2,3,5])-    , (5,[1,2,3,4])-    ]--}---- |List differences between the members of two lists, where corresponding---  elements may appear at arbitrary locations in the corresponding lists.------  Elements are compared using the function 'cmp', which returns:---  * Nothing  if the elements are completely unrelated---  * Just []  if the elements are identical---  * Just ds  if the elements are related but not identical, in which case---             ds is a list of values describing differences between them.------  Returns (ds,u1,u2), where:---  ds is null if the related elements from each list are identical,---  otherwise is a list of differences between the related elements.---  u1 is a list of elements in a1 not related to elements in a2.---  u2 is a list of elements in a2 not related to elements in a1.----listDifferences :: (a->a->Maybe [d]) -> [a] -> [a] -> ([d],[a],[a])-listDifferences _   []       a2s = ([],[],a2s)-listDifferences cmp (a1:a1t) a2s =-    case mcomp of-        Nothing       -> morediffs [] [a1] a1t a2s-        Just (ds,a2t) -> morediffs ds []   a1t a2t-    where-        -- mcomp finds identical match, if there is one, or-        -- the first element in a2s related to a1, or Nothing-        -- [choose was listToMaybe,-        --  but that didn't handle repeated properties well]-        mcomp = choose $ mapMaybe maybeResult comps-        comps = [ (cmp a1 a2,a2t) | (a2,a2t) <- removeEach a2s ]-        maybeResult (Nothing,_)   = Nothing-        maybeResult (Just ds,a2t) = Just (ds,a2t)-        morediffs xds xa1h xa1t xa2t  = (xds++xds1,xa1h++xa1r,xa2r)-            where-                (xds1,xa1r,xa2r) = listDifferences cmp xa1t xa2t-        choose  []       = Nothing-        choose  ds@(d:_) = choose1 d ds-        choose1 _ (d@([],_):_)  = Just d-        choose1 d []            = Just d-        choose1 d (_:ds)        = choose1 d ds--{--testcmp (l1,h1) (l2,h2)-    | (l1 >= h2) || (l2 >= h1) = Nothing-    | (l1 == l2) && (h1 == h2) = Just []-    | otherwise                = Just [((l1,h1),(l2,h2))]--testdiff1 = listDifferences testcmp-                [(12,15),(1,2),(3,4),(5,8),(10,11)]-                [(10,11),(0,1),(3,4),(6,9),(13,15)]-testdiff2 = testdiff1 == ([((12,15),(13,15)),((5,8),(6,9))],[(1,2)],[(0,1)])--}----------------------------------------------------------------------------------------  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  All rights reserved.------  This file is part of Swish.------  Swish is free software; you can redistribute it and/or modify---  it under the terms of the GNU General Public License as published by---  the Free Software Foundation; either version 2 of the License, or---  (at your option) any later version.------  Swish is distributed in the hope that it will be useful,---  but WITHOUT ANY WARRANTY; without even the implied warranty of---  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the---  GNU General Public License for more details.------  You should have received a copy of the GNU General Public License---  along with Swish; if not, write to:---    The Free Software Foundation, Inc.,---    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA------------------------------------------------------------------------------------
+ src/Swish/RDF/GraphShowLines.hs view
@@ -0,0 +1,59 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE TypeSynonymInstances #-}++--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  GraphShowLines+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke+--  License     :  GPL V2+--+--  Maintainer  :  Douglas Burke+--  Stability   :  experimental+--  Portability :  FlexibleInstances, TypeSynonymInstances+--+--  This module defines a `ShowLines` class instance for `RDFGraph`, to be+--  used when displaying RDF Graph values as part of a proof sequence,+--  etc.+--+--------------------------------------------------------------------------------++module Swish.RDF.GraphShowLines () where++import Swish.RDF.Graph (RDFGraph)+import Swish.RDF.Formatter.N3 (formatGraphIndent)++import Data.String.ShowLines (ShowLines(..))++-- import qualified Data.Text.Lazy as L+import qualified Data.Text.Lazy.Builder as B++instance ShowLines RDFGraph where+    -- showls linebreak = shows . L.unpack . B.toLazyText . formatGraphIndent linebreak False +    showls linebreak = shows . formatGraphIndent (B.fromString linebreak) False ++--------------------------------------------------------------------------------+--+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke+--  All rights reserved.+--+--  This file is part of Swish.+--+--  Swish is free software; you can redistribute it and/or modify+--  it under the terms of the GNU General Public License as published by+--  the Free Software Foundation; either version 2 of the License, or+--  (at your option) any later version.+--+--  Swish is distributed in the hope that it will be useful,+--  but WITHOUT ANY WARRANTY; without even the implied warranty of+--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+--  GNU General Public License for more details.+--+--  You should have received a copy of the GNU General Public License+--  along with Swish; if not, write to:+--    The Free Software Foundation, Inc.,+--    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+--+--------------------------------------------------------------------------------
− src/Swish/RDF/MapXsdDecimal.hs
@@ -1,72 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  MapXsdDecimal---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin,---                     2011 Douglas Burke, 2011 William Waites---  License     :  GPL V2------  Maintainer  :  Douglas Burke---  Stability   :  experimental---  Portability :  OverloadedStrings------  This module defines the datatytpe mapping and relation values---  used for RDF dataype xsd:decimal--------------------------------------------------------------------------------------module Swish.RDF.MapXsdDecimal (mapXsdDecimal) where--import Swish.RDF.Datatype (DatatypeMap(..))--import qualified Data.Text as T-import qualified Data.Text.Read as T-----------------------------------------------------------------  Implementation of DatatypeMap for xsd:decimal----------------------------------------------------------------- |mapXsdDecimal contains functions that perform lexical-to-value---  and value-to-canonical-lexical mappings for xsd:decimal values----mapXsdDecimal :: DatatypeMap Double-mapXsdDecimal = DatatypeMap-    { -- mapL2V :: T.Text -> Maybe Double-      mapL2V = \txt -> case T.double txt of-      	 Right (val, "") -> Just val-         _ -> Nothing-         -      -- mapV2L :: Double -> Maybe T.Text-      -- TODO: for now convert via String as issues with text-format-      --       (inability to use with ghci)   -    , mapV2L = Just . T.pack . show-    }----------------------------------------------------------------------------------------  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,---                2011 Douglas Burke, 2011 William Waites------  All rights reserved.------  This file is part of Swish.------  Swish is free software; you can redistribute it and/or modify---  it under the terms of the GNU General Public License as published by---  the Free Software Foundation; either version 2 of the License, or---  (at your option) any later version.------  Swish is distributed in the hope that it will be useful,---  but WITHOUT ANY WARRANTY; without even the implied warranty of---  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the---  GNU General Public License for more details.------  You should have received a copy of the GNU General Public License---  along with Swish; if not, write to:---    The Free Software Foundation, Inc.,---    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA------------------------------------------------------------------------------------
− src/Swish/RDF/MapXsdInteger.hs
@@ -1,69 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  MapXsdInteger---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  License     :  GPL V2------  Maintainer  :  Douglas Burke---  Stability   :  experimental---  Portability :  OverloadedStrings------  This module defines the datatytpe mapping and relation values---  used for RDF dataype xsd:integer--------------------------------------------------------------------------------------module Swish.RDF.MapXsdInteger (mapXsdInteger) where--import Swish.RDF.Datatype (DatatypeMap(..))--import qualified Data.Text as T-import qualified Data.Text.Read as T-----------------------------------------------------------------  Implementation of DatatypeMap for xsd:integer----------------------------------------------------------------- |mapXsdInteger contains functions that perform lexical-to-value---  and value-to-canonical-lexical mappings for xsd:integer values----mapXsdInteger :: DatatypeMap Integer-mapXsdInteger = DatatypeMap-    { -- mapL2V :: T.Text -> Maybe Integer-      mapL2V = \txt -> case T.signed T.decimal txt of-         Right (val, "") -> Just val-         _ -> Nothing-         -      -- mapV2L :: Integer -> Maybe T.Text-      -- TODO: for now convert via String as issues with text-format-      --       (inability to use with ghci)   -    , mapV2L = Just . T.pack . show-    }----------------------------------------------------------------------------------------  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  All rights reserved.------  This file is part of Swish.------  Swish is free software; you can redistribute it and/or modify---  it under the terms of the GNU General Public License as published by---  the Free Software Foundation; either version 2 of the License, or---  (at your option) any later version.------  Swish is distributed in the hope that it will be useful,---  but WITHOUT ANY WARRANTY; without even the implied warranty of---  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the---  GNU General Public License for more details.------  You should have received a copy of the GNU General Public License---  along with Swish; if not, write to:---    The Free Software Foundation, Inc.,---    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA------------------------------------------------------------------------------------
− src/Swish/RDF/N3Formatter.hs
@@ -1,941 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  N3Formatter---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  License     :  GPL V2------  Maintainer  :  Douglas Burke---  Stability   :  experimental---  Portability :  OverloadedStrings------  This Module implements a Notation 3 formatter (see [1], [2] and [3]),---  for an RDFGraph value.------ REFERENCES:------ (1) <http://www.w3.org/TeamSubmission/2008/SUBM-n3-20080114/>---     Notation3 (N3): A readable RDF syntax,---     W3C Team Submission 14 January 2008------ (2) <http://www.w3.org/DesignIssues/Notation3.html>---     Tim Berners-Lee's design issues series notes and description------ (2) <http://www.w3.org/2000/10/swap/Primer.html>---     Notation 3 Primer by Sean Palmer------  TODO:------   * Initial prefix list to include nested formulae;---      then don't need to update prefix list for these.------   * correct output of strings containing unsupported escape---     characters (such as @\\q@)------   * more flexible terminator generation for formatted formulae---     (for inline blank nodes.)--------------------------------------------------------------------------------------{--TODO:--The code used to determine whether a blank node can be written-using the "[]" short form could probably take advantage of the-GraphPartition module.---}--module Swish.RDF.N3Formatter-    ( NodeGenLookupMap-    , formatGraphAsText-    , formatGraphAsLazyText-    , formatGraphAsBuilder-    , formatGraphIndent  -    , formatGraphDiag-    )-where--import Swish.RDF.RDFGraph (-  RDFGraph, RDFLabel(..),-  NamespaceMap, RevNamespaceMap,-  emptyNamespaceMap,-  FormulaMap, emptyFormulaMap,-  getArcs, labels,-  setNamespaces, getNamespaces,-  getFormulae,-  emptyRDFGraph-  , quote-  , quoteT-  , resRdfFirst, resRdfRest, resRdfNil-  )--import Swish.RDF.Vocabulary (-  isLang, langTag, -  rdfType,-  rdfNil,-  owlSameAs, logImplies-  , xsdBoolean, xsdDecimal, xsdInteger, xsdDouble -  )--import Swish.RDF.GraphClass (Arc(..))--import Swish.Utils.LookupMap-    ( LookupEntryClass(..)-    , LookupMap, emptyLookupMap, reverseLookupMap-    , listLookupMap-    , mapFind, mapFindMaybe, mapAdd, mapDelete, mapMerge-    )--import Swish.Utils.Namespace (ScopedName, getScopeLocal, getScopeURI)--import Data.Char (isDigit)--import Data.List (foldl', delete, groupBy, partition, sort, intersperse)--import Data.Monoid (Monoid(..))-import Control.Monad (liftM, when)-import Control.Monad.State (State, modify, get, put, runState)---- it strikes me that using Lazy Text here is likely to be--- wrong; however I have done no profiling to back this--- assumption up!--import qualified Data.Text as T-import qualified Data.Text.Lazy as L-import qualified Data.Text.Lazy.Builder as B---- temporary conversion-quoteB :: Bool -> String -> B.Builder-quoteB f v = B.fromString $ quote f v---------------------------------------------------------------------------  Graph formatting state monad-----------------------------------------------------------------------------  The graph to be formatted is carried as part of the formatting---  state, so that decisions about what needs to be formatted can---  themselves be based upon and reflected in the state (e.g. if a---  decision is made to include a blank node inline, it can be removed---  from the graph state that remains to be formatted).--type SubjTree lb = [(lb,PredTree lb)]-type PredTree lb = [(lb,[lb])]--data N3FormatterState = N3FS-    { indent    :: B.Builder-    , lineBreak :: Bool-    , graph     :: RDFGraph-    , subjs     :: SubjTree RDFLabel-    , props     :: PredTree RDFLabel   -- for last subject selected-    , objs      :: [RDFLabel]          -- for last property selected-    , formAvail :: FormulaMap RDFLabel-    , formQueue :: [(RDFLabel,RDFGraph)]-    , nodeGenSt :: NodeGenState-    , bNodesCheck   :: [RDFLabel]      -- these bNodes are not to be converted to '[..]' format-    , traceBuf  :: [String]-    }-             -type Formatter a = State N3FormatterState a--emptyN3FS :: NodeGenState -> N3FormatterState-emptyN3FS ngs = N3FS-    { indent    = "\n"-    , lineBreak = False-    , graph     = emptyRDFGraph-    , subjs     = []-    , props     = []-    , objs      = []-    , formAvail = emptyFormulaMap-    , formQueue = []-    , nodeGenSt = ngs-    , bNodesCheck   = []-    , traceBuf  = []-    }----  | Node name generation state information that carries through---  and is updated by nested formulae-type NodeGenLookupMap = LookupMap (RDFLabel,Int)--data NodeGenState = Ngs-    { prefixes  :: NamespaceMap-    , nodeMap   :: NodeGenLookupMap-    , nodeGen   :: Int-    }--emptyNgs :: NodeGenState-emptyNgs = Ngs-    { prefixes  = emptyLookupMap-    , nodeMap   = emptyLookupMap-    , nodeGen   = 0-    }---- simple context for label creation--- (may be a temporary solution to the problem---  of label creation)----data LabelContext = SubjContext | PredContext | ObjContext-                    deriving (Eq, Show)--getIndent :: Formatter B.Builder-getIndent = indent `liftM` get--setIndent :: B.Builder -> Formatter ()-setIndent ind = modify $ \st -> st { indent = ind }--getLineBreak :: Formatter Bool-getLineBreak = lineBreak `liftM` get--setLineBreak :: Bool -> Formatter ()-setLineBreak brk = modify $ \st -> st { lineBreak = brk }--getNgs :: Formatter NodeGenState-getNgs = nodeGenSt `liftM` get--setNgs :: NodeGenState -> Formatter ()-setNgs ngs = modify $ \st -> st { nodeGenSt = ngs }--getPrefixes :: Formatter NamespaceMap-getPrefixes = prefixes `liftM` getNgs--getSubjs :: Formatter (SubjTree RDFLabel)-getSubjs = subjs `liftM` get--setSubjs :: SubjTree RDFLabel -> Formatter ()-setSubjs sl = modify $ \st -> st { subjs = sl }--getProps :: Formatter (PredTree RDFLabel)-getProps = props `liftM` get--setProps :: PredTree RDFLabel -> Formatter ()-setProps ps = modify $ \st -> st { props = ps }--{--getObjs :: Formatter ([RDFLabel])-getObjs = objs `liftM` get--setObjs :: [RDFLabel] -> Formatter ()-setObjs os = do-  st <- get-  put $ st { objs = os }--}--getBnodesCheck :: Formatter [RDFLabel]-getBnodesCheck = bNodesCheck `liftM` get--{--addTrace :: String -> Formatter ()-addTrace tr = do-  st <- get-  put $ st { traceBuf = tr : traceBuf st }--}-  -queueFormula :: RDFLabel -> Formatter ()-queueFormula fn = do-  st <- get-  let fa = formAvail st-      newState fv = st {-                      formAvail = mapDelete fa fn,-                      formQueue = (fn,fv) : formQueue st-                    }-  case mapFindMaybe fn fa of-    Nothing -> return ()-    Just v -> put (newState v) >> return ()--{--Return the graph associated with the label and delete it-from the store, if there is an association, otherwise-return Nothing.--}-extractFormula :: RDFLabel -> Formatter (Maybe RDFGraph)-extractFormula fn = do-  st <- get-  let fa = formAvail st-      newState = st { formAvail=mapDelete fa fn }-  case mapFindMaybe fn fa of-    Nothing -> return Nothing-    Just fv -> put newState >> return (Just fv)--{--moreFormulae :: Formatter Bool-moreFormulae =  do-  st <- get-  return $ not $ null (formQueue st)--nextFormula :: Formatter (RDFLabel,RDFGraph)-nextFormula = do-  st <- get-  let (nf : fq) = formQueue st-  put $ st { formQueue = fq }-  return nf---}---- list has a length of 1-len1 :: [a] -> Bool-len1 (_:[]) = True-len1 _ = False--{-|-Given a set of statements and a label, return the details of the-RDF collection referred to by label, or Nothing.--For label to be considered as representing a collection we require the-following conditions to hold (this is only to support the-serialisation using the '(..)' syntax and does not make any statement-about semantics of the statements with regard to RDF Collections):--  - there must be one rdf_first and one rdfRest statement-  - there must be no other predicates for the label---} -getCollection ::          -  SubjTree RDFLabel -- ^ statements organized by subject-  -> RDFLabel -- ^ does this label represent a list?-  -> Maybe (SubjTree RDFLabel, [RDFLabel], [RDFLabel])-     -- ^ the statements with the elements removed; the-     -- content elements of the collection (the objects of the rdf:first-     -- predicate) and the nodes that represent the spine of the-     -- collection (in reverse order, unlike the actual contents which are in-     -- order).-getCollection subjList lbl = go subjList lbl ([],[]) -    where-      go sl l (cs,ss) | l == resRdfNil = Just (sl, reverse cs, ss)-                      | otherwise = do-        (pList1, sl') <- removeItem sl l-        (pFirst, pList2) <- removeItem pList1 resRdfFirst-        (pNext, pList3) <- removeItem pList2 resRdfRest--        -- QUS: could I include these checks implicitly in the pattern matches above?-        -- ie instrad of (pFirst, pos1) <- ..-        -- have ([content], pos1) <- ...-        -- ?-        if and [len1 pFirst, len1 pNext, null pList3]-          then go sl' (head pNext) (head pFirst : cs, l : ss)-          else Nothing--{--TODO:--Should we change the preds/objs entries as well?---}-extractList :: LabelContext -> RDFLabel -> Formatter (Maybe [RDFLabel])-extractList lctxt ln = do-  osubjs <- getSubjs-  oprops <- getProps-  let mlst = getCollection osubjs' ln--      -- we only want to send in rdf:first/rdf:rest here-      fprops = filter ((`elem` [resRdfFirst, resRdfRest]) . fst) oprops--      osubjs' =-          case lctxt of-            SubjContext -> (ln, fprops) : osubjs-            _ -> osubjs --      -- tr = "extractList " ++ show ln ++ " (" ++ show lctxt ++ ")\n -> osubjs= " ++ show osubjs ++ "\n -> opreds= " ++ show oprops ++ "\n -> mlst= " ++ show mlst ++ "\n"-  -- addTrace tr-  case mlst of-    -- sl is guaranteed to be free of (ln,fprops) here if lctxt is SubjContext-    Just (sl,ls,_) -> do-              setSubjs sl-              when (lctxt == SubjContext) $ setProps $ filter ((`notElem` [resRdfFirst, resRdfRest]) . fst) oprops-              return (Just ls)--    Nothing -> return Nothing-  -{-|-Removes the first occurrence of the item from the-association list, returning it's contents and the rest-of the list, if it exists.--}-removeItem :: (Eq a) => [(a,b)] -> a -> Maybe (b, [(a,b)])-removeItem os x =-  let (as, bs) = break (\a -> fst a == x) os-  in case bs of-    ((_,b):bbs) -> Just (b, as ++ bbs)-    [] -> Nothing---------------------------------------------------------------------------  Define a top-level formatter function:-------------------------------------------------------------------------formatGraphAsText :: RDFGraph -> T.Text-formatGraphAsText = L.toStrict . formatGraphAsLazyText--formatGraphAsLazyText :: RDFGraph -> L.Text-formatGraphAsLazyText = B.toLazyText . formatGraphAsBuilder-  -formatGraphAsBuilder :: RDFGraph -> B.Builder-formatGraphAsBuilder = formatGraphIndent "\n" True-  -formatGraphIndent :: B.Builder -> Bool -> RDFGraph -> B.Builder-formatGraphIndent indnt flag gr = -  let (res, _, _, _) = formatGraphDiag indnt flag gr-  in res-  --- | Format graph and return additional information-formatGraphDiag :: -  B.Builder  -- ^ indentation-  -> Bool    -- ^ are prefixes to be generated?-  -> RDFGraph -  -> (B.Builder, NodeGenLookupMap, Int, [String])-formatGraphDiag indnt flag gr = -  let fg  = formatGraph indnt " .\n" False flag gr-      ngs = emptyNgs {-        prefixes = emptyLookupMap,-        nodeGen  = findMaxBnode gr-        }-             -      (out, fgs) = runState fg (emptyN3FS ngs)-      ogs        = nodeGenSt fgs-  -  in (out, nodeMap ogs, nodeGen ogs, traceBuf fgs)---------------------------------------------------------------------------  Formatting as a monad-based computation-------------------------------------------------------------------------formatGraph :: -  B.Builder     -- indentation string-  -> B.Builder  -- text to be placed after final statement-  -> Bool       -- True if a line break is to be inserted at the start-  -> Bool       -- True if prefix strings are to be generated-  -> RDFGraph   -- graph to convert-  -> Formatter B.Builder-formatGraph ind end dobreak dopref gr = do-  setIndent ind-  setLineBreak dobreak-  setGraph gr-  -  fp <- if dopref-        then formatPrefixes (getNamespaces gr)-        else return mempty-  more <- moreSubjects-  if more-    then do-      fr <- formatSubjects-      return $ mconcat [fp, fr, end]-    else return fp--formatPrefixes :: NamespaceMap -> Formatter B.Builder-formatPrefixes pmap = do-  let mls = map (pref . keyVal) (listLookupMap pmap)-  ls <- sequence mls-  return $ mconcat ls-    where-      pref (Just p,u) = nextLine $ mconcat ["@prefix ", B.fromText p, ": <", quoteB True (show u), "> ."]-      pref (_,u)      = nextLine $ mconcat ["@prefix : <", quoteB True (show u), "> ."]--{--NOTE:-I expect there to be confusion below where I need to-convert from Text to Builder--}--formatSubjects :: Formatter B.Builder-formatSubjects = do-  sb    <- nextSubject-  sbstr <- formatLabel SubjContext sb-  -  flagP <- moreProperties-  if flagP-    then do-      prstr <- formatProperties sb sbstr-      flagS <- moreSubjects-      if flagS-        then do-          fr <- formatSubjects-          return $ mconcat [prstr, " .", fr]-        else return prstr-           -    else do-      txt <- nextLine sbstr-    -      flagS <- moreSubjects-      if flagS-        then do-          fr <- formatSubjects-          return $ mconcat [txt, " .", fr]-        else return txt--{--TODO: now we are throwing a Builder around it is awkward to-get the length of the text to calculate the indentation--So--  a) change the indentation scheme-  b) pass around text instead of builder--mkIndent :: L.Text -> L.Text-mkIndent inVal = L.replicate (L.length inVal) " "--}--hackIndent :: B.Builder-hackIndent = "    "--formatProperties :: RDFLabel -> B.Builder -> Formatter B.Builder-formatProperties sb sbstr = do-  pr <- nextProperty sb-  prstr <- formatLabel PredContext pr-  obstr <- formatObjects sb pr $ mconcat [sbstr, " ", prstr]-  more  <- moreProperties-  let sbindent = hackIndent -- mkIndent sbstr-  if more-    then do-      fr <- formatProperties sb sbindent-      nl <- nextLine $ obstr `mappend` " ;"-      return $ nl `mappend` fr-    else nextLine obstr--formatObjects :: RDFLabel -> RDFLabel -> B.Builder -> Formatter B.Builder-formatObjects sb pr prstr = do-  ob    <- nextObject sb pr-  obstr <- formatLabel ObjContext ob-  more  <- moreObjects-  if more-    then do-      let prindent = hackIndent -- mkIndent prstr-      fr <- formatObjects sb pr prindent-      nl <- nextLine $ mconcat [prstr, " ", obstr, ","]-      return $ nl `mappend` fr-    else return $ mconcat [prstr, " ", obstr]--insertFormula :: RDFGraph -> Formatter B.Builder-insertFormula gr = do-  ngs0  <- getNgs-  ind   <- getIndent-  let grm = formatGraph (ind `mappend` "    ") "" True False-            (setNamespaces emptyNamespaceMap gr)--      (f3str, fgs') = runState grm (emptyN3FS ngs0)--  setNgs (nodeGenSt fgs')-  f4str <- nextLine " } "-  return $ mconcat [" { ",f3str, f4str]--{--Add a list inline. We are given the labels that constitute-the list, in order, so just need to display them surrounded-by ().--}-insertList :: [RDFLabel] -> Formatter B.Builder-insertList [] = return "()" -- not convinced this can happen-insertList xs = do-  ls <- mapM (formatLabel ObjContext) xs-  return $ mconcat ("( " : intersperse " " ls) `mappend` " )"-    -{--Add a blank node inline.--}--insertBnode :: LabelContext -> RDFLabel -> Formatter B.Builder-insertBnode SubjContext lbl = do-  flag <- moreProperties-  txt <- if flag-         then (`mappend` "\n") `liftM` formatProperties lbl ""-         else return ""--  -- TODO: handle indentation?-  return $ mconcat ["[", txt, "]"]--insertBnode _ lbl = do-  ost <- get-  let osubjs = subjs ost-      oprops = props ost-      oobjs  = objs  ost--      (bsubj, rsubjs) = partition ((== lbl) . fst) osubjs--      rprops = case bsubj of-                 [(_,rs)] -> rs-                 _ -> []--      -- we essentially want to create a new subgraph-      -- for this node but it's not as simple as that since-      -- we could have something like-      --     :a :b [ :foo [ :bar "xx" ] ]-      -- so we still need to carry around the whole graph-      ---      nst = ost { subjs = rsubjs,-                  props = rprops,-                  objs  = []-                }--  put nst-  flag <- moreProperties-  txt <- if flag-         then (`mappend` "\n") `liftM` formatProperties lbl ""-         else return ""--  -- TODO: how do we restore the original set up?-  --       I can't believe the following is sufficient-  ---  nst' <- get-  let slist  = map fst $ subjs nst'-      nsubjs = filter (\(l,_) -> l `elem` slist) osubjs--  put $ nst' { subjs = nsubjs,-                       props = oprops, -                       objs  = oobjs-             }--  -- TODO: handle indentation?-  return $ mconcat ["[", txt, "]"]-  --------------------------------------------------------------------------  Formatting helpers-------------------------------------------------------------------------setGraph :: RDFGraph -> Formatter ()-setGraph gr = do-  st <- get--  let ngs0 = nodeGenSt st-      pre' = mapMerge (prefixes ngs0) (getNamespaces gr)-      ngs' = ngs0 { prefixes = pre' }-      arcs = sortArcs $ getArcs gr-      nst  = st  { graph     = gr-                 , subjs     = arcTree arcs-                 , props     = []-                 , objs      = []-                 , formAvail = getFormulae gr-                 , nodeGenSt = ngs'-                 , bNodesCheck   = countBnodes arcs-                 }--  put nst--hasMore :: (N3FormatterState -> [b]) -> Formatter Bool-hasMore lens = (not . null . lens) `liftM` get--moreSubjects :: Formatter Bool-moreSubjects = hasMore subjs--- moreSubjects = (not . null . subjs) `liftM` get--moreProperties :: Formatter Bool-moreProperties = hasMore props--- moreProperties = (not . null . props) `liftM` get--moreObjects :: Formatter Bool-moreObjects = hasMore objs--- moreObjects = (not . null . objs) `liftM` get--nextSubject :: Formatter RDFLabel-nextSubject = do-  st <- get--  let sb:sbs = subjs st-      nst = st  { subjs = sbs-                , props = snd sb-                , objs  = []-                }--  put nst-  return $ fst sb--nextProperty :: RDFLabel -> Formatter RDFLabel-nextProperty _ = do-  st <- get--  let pr:prs = props st-      nst = st  { props = prs-                 , objs  = snd pr-                 }--  put nst-  return $ fst pr--nextObject :: RDFLabel -> RDFLabel -> Formatter RDFLabel-nextObject _ _ = do-  st <- get--  let ob:obs = objs st-      nst = st { objs = obs }--  put nst-  return ob--nextLine :: B.Builder -> Formatter B.Builder-nextLine str = do-  ind <- getIndent-  brk <- getLineBreak-  if brk-    then return $ ind `mappend` str-    else do-      --  After first line, always insert line break-      setLineBreak True-      return str----  Format a label---  Most labels are simply displayed as provided, but there are a---  number of wrinkles to take care of here:---  (a) blank nodes automatically allocated on input, with node---      identifiers of the form of a digit string nnn.  These are---      not syntactically valid, and are reassigned node identifiers---      of the form _nnn, where nnn is chosen so that is does not---      clash with any other identifier in the graph.---  (b) URI nodes:  if possible, replace URI with qname,---      else display as <uri>---  (c) formula nodes (containing graphs).---  (d) use the "special-case" formats for integer/float/double---      literals.      ---      ---  [[[TODO:]]]---  (d) generate multi-line literals when appropriate------ This is being updated to produce inline formula, lists and     --- blank nodes. The code is not efficient.-----specialTable :: [(ScopedName, String)]-specialTable = -  [ (rdfType, "a")-  , (owlSameAs, "=")-  , (logImplies, "=>")-  , (rdfNil, "()")-  ]--formatLabel :: LabelContext -> RDFLabel -> Formatter B.Builder-{--formatLabel lab@(Blank (_:_)) = do-  name <- formatNodeId lab-  queueFormula lab-  return name--}--{--The "[..]" conversion is done last, after "()" and "{}" checks.--}-formatLabel lctxt lab@(Blank (_:_)) = do-  mlst <- extractList lctxt lab-  case mlst of-    Just lst -> insertList lst-    Nothing -> do-              mfml <- extractFormula lab-              case mfml of-                Just fml -> insertFormula fml-                Nothing -> do-                          nb1 <- getBnodesCheck-                          if lctxt /= PredContext && lab `notElem` nb1-                            then insertBnode lctxt lab-                            else formatNodeId lab--formatLabel _ lab@(Res sn) = -  case lookup sn specialTable of-    Just txt -> return $ quoteB True txt -- TODO: do we need to quote?-    Nothing -> do-      pr <- getPrefixes-      let nsuri  = getScopeURI sn-          local  = getScopeLocal sn-          premap = reverseLookupMap pr :: RevNamespaceMap-          prefix = mapFindMaybe nsuri premap-          -          name   = case prefix of-                     Just (Just p) -> B.fromText $ quoteT True $ mconcat [p, ":", local] -- TODO: what are quoting rules for QNames-                     _ -> mconcat ["<", quoteB True (show nsuri ++ T.unpack local), ">"]-      -      {--          name   = case prefix of-                     Just p -> quoteB True (p ++ ":" ++ local) -- TODO: what are quoting rules for QNames-                     _ -> mconcat ["<", quoteB True (nsuri++local), ">"]-      -}-          -      queueFormula lab-      return name---- The canonical notation for xsd:double in XSD, with an upper-case E,--- does not match the syntax used in N3, so we need to convert here.     --- Rather than converting back to a Double and then displaying that       --- we just convert E to e for now.      ---      -formatLabel _ (Lit lit (Just dtype)) -  | dtype == xsdDouble = return $ B.fromText $ T.toLower lit-  | dtype `elem` [xsdBoolean, xsdDecimal, xsdInteger] = return $ B.fromText lit-  | otherwise = return $ quoteText lit `mappend` formatAnnotation dtype-formatLabel _ (Lit lit Nothing) = return $ quoteText lit--formatLabel _ lab = return $ B.fromString $ show lab---- the annotation for a literal (ie type or language)-formatAnnotation :: ScopedName -> B.Builder-formatAnnotation a  | isLang a  = "@" `mappend` B.fromText (langTag a)-                    | otherwise = "^^" `mappend` showScopedName a--{--We have to decide whether to use " or """ to quote-the string.--There is also no need to restrict the string to the-ASCII character set; this could be an option but we-can also leave Unicode as is (or at least convert to UTF-8).--If we use """ to surround the string then we protect the-last character if it is a " (assuming it isn't protected).--}--quoteText :: T.Text -> B.Builder-quoteText txt = -  let st = T.unpack txt -- TODO: fix-      qst = quoteB (n==1) st-      n = if '\n' `elem` st || '"' `elem` st then 3 else 1-      qch = B.fromString (replicate n '"')-  in mconcat [qch, qst, qch]--formatNodeId :: RDFLabel -> Formatter B.Builder-formatNodeId lab@(Blank (lnc:_)) =-    if isDigit lnc then mapBlankNode lab else return $ B.fromString $ show lab-formatNodeId other = error $ "formatNodeId not expecting a " ++ show other -- to shut up -Wall--mapBlankNode :: RDFLabel -> Formatter B.Builder-mapBlankNode lab = do-  ngs <- getNgs-  let cmap = nodeMap ngs-      cval = nodeGen ngs-  nv <- case mapFind 0 lab cmap of-    0 -> do -      let nval = succ cval-          nmap = mapAdd cmap (lab, nval)-      setNgs $ ngs { nodeGen = nval, nodeMap = nmap }-      return nval-      -    n -> return n-  -  -- TODO: is this what we want?-  return $ "_:swish" `mappend` B.fromString (show nv)---- TODO: need to be a bit more clever with this than we did in NTriples---       not sure the following counts as clever enough ...---  -showScopedName :: ScopedName -> B.Builder-{--showScopedName (ScopedName n l) = -  let uri = nsURI n ++ l-  in quote uri--}-showScopedName = quoteB True . show---------------------------------------------------------------------------  Graph-related helper functions-------------------------------------------------------------------------newtype SortedArcs lb = SA [Arc lb]--sortArcs :: (Ord lb) => [Arc lb] -> SortedArcs lb-sortArcs = SA . sort----  Rearrange a list of arcs into a tree of pairs which group together---  all statements for a single subject, and similarly for multiple---  objects of a common predicate.----arcTree :: (Eq lb) => SortedArcs lb -> SubjTree lb-arcTree (SA as) = commonFstEq (commonFstEq id) $ map spopair as-    where-        spopair (Arc s p o) = (s,(p,o))--{--arcTree as = map spopair $ sort as-    where-        spopair (Arc s p o) = (s,[(p,[o])])--}----  Rearrange a list of pairs so that multiple occurrences of the first---  are commoned up, and the supplied function is applied to each sublist---  with common first elements to obtain the corresponding second value-commonFstEq :: (Eq a) => ( [b] -> c ) -> [(a,b)] -> [(a,c)]-commonFstEq f ps =-    [ (fst $ head sps,f $ map snd sps) | sps <- groupBy fstEq ps ]-    where-        fstEq (f1,_) (f2,_) = f1 == f2--{---- Diagnostic code for checking arcTree logic:-testArcTree = (arcTree testArcTree1) == testArcTree2-testArcTree1 =-    [Arc "s1" "p11" "o111", Arc "s1" "p11" "o112"-    ,Arc "s1" "p12" "o121", Arc "s1" "p12" "o122"-    ,Arc "s2" "p21" "o211", Arc "s2" "p21" "o212"-    ,Arc "s2" "p22" "o221", Arc "s2" "p22" "o222"-    ]-testArcTree2 =-    [("s1",[("p11",["o111","o112"]),("p12",["o121","o122"])])-    ,("s2",[("p21",["o211","o212"]),("p22",["o221","o222"])])-    ]--}---findMaxBnode :: RDFGraph -> Int-findMaxBnode = maximum . map getAutoBnodeIndex . labels--getAutoBnodeIndex   :: RDFLabel -> Int-getAutoBnodeIndex (Blank ('_':lns)) = res where-    -- cf. prelude definition of read s ...-    res = case [x | (x,t) <- reads lns, ("","") <- lex t] of-            [x] -> x-            _   -> 0-getAutoBnodeIndex _                   = 0--{--Find all blank nodes that occur-  - any number of times as a subject-  - 0 or 1 times as an object--Such nodes can be output using the "[..]" syntax. To make it simpler-to check we actually store those nodes that can not be expanded.--Note that we do not try and expand any bNode that is used in-a predicate position.--Should probably be using the SubjTree RDFLabel structure but this-is easier for now.---}--countBnodes :: SortedArcs RDFLabel -> [RDFLabel]-countBnodes (SA as) = snd (foldl' ctr ([],[]) as)-    where-      -- first element of tuple are those blank nodes only seen once,-      -- second element those blank nodes seen multiple times-      ---      inc b@(b1s,bms) l@(Blank _) | l `elem` bms = b-                                  | l `elem` b1s = (delete l b1s, l:bms)-                                  | otherwise    = (l:b1s, bms)-      inc b _ = b--      -- if the bNode appears as a predicate we instantly add it to the-      -- list of nodes not to expand, even if only used once-      incP b@(b1s,bms) l@(Blank _) | l `elem` bms = b-                                   | l `elem` b1s = (delete l b1s, l:bms)-           			   | otherwise    = (b1s, l:bms)-      incP b _ = b--      ctr orig (Arc _ p o) = inc (incP orig p) o----------------------------------------------------------------------------------------  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  All rights reserved.------  This file is part of Swish.------  Swish is free software; you can redistribute it and/or modify---  it under the terms of the GNU General Public License as published by---  the Free Software Foundation; either version 2 of the License, or---  (at your option) any later version.------  Swish is distributed in the hope that it will be useful,---  but WITHOUT ANY WARRANTY; without even the implied warranty of---  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the---  GNU General Public License for more details.------  You should have received a copy of the GNU General Public License---  along with Swish; if not, write to:---    The Free Software Foundation, Inc.,---    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA------------------------------------------------------------------------------------
− src/Swish/RDF/N3Parser.hs
@@ -1,1145 +0,0 @@-{-# LANGUAGE OverloadedStrings #-} -- only used in 'fromMaybe "" mbase' line of parseN3-------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  N3Parser---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  License     :  GPL V2------  Maintainer  :  Douglas Burke---  Stability   :  experimental---  Portability :  OverloadedStrings------  This Module implements a Notation 3 parser (see [1], [2], [3]), returning a---  new 'RDFGraph' consisting of triples and namespace information parsed from---  the supplied N3 input string, or an error indication.------ REFERENCES:------ 1 <http://www.w3.org/TeamSubmission/2008/SUBM-n3-20080114/>---     Notation3 (N3): A readable RDF syntax,---     W3C Team Submission 14 January 2008------ 2 <http://www.w3.org/DesignIssues/Notation3.html>---     Tim Berners-Lee's design issues series notes and description------ 3 <http://www.w3.org/2000/10/swap/Primer.html>---     Notation 3 Primer by Sean Palmer------ NOTES:------  UTF-8 handling is not really tested.------  No performance testing has been applied.------  Not all N3 grammar elements are supported, including:------    - @\@forSome@ (we read it in but ignore the arguments)------    - @\@forAll@  (this causes a parse error)------    - formulae are lightly tested------    - string support is incomplete (e.g. unrecognized escape characters---      such as @\\q@ are probably handled incorrectly)--------------------------------------------------------------------------------------module Swish.RDF.N3Parser-    ( ParseResult-    , parseN3      -    , parseN3fromText      -    , parseAnyfromText-    , parseTextFromText, parseAltFromText-    , parseNameFromText -- , parsePrefixFromText-    , parseAbsURIrefFromText, parseLexURIrefFromText, parseURIref2FromText-    -    -- * Exports for parsers that embed Notation3 in a bigger syntax-    , N3Parser, N3State(..), SpecialMap-    -    , getPrefix -- a combination of the old defaultPrefix and namedPrefix productions-    , n3symbol -- replacement for uriRef2 -- TODO: check this is semantically correct      -    , quickVariable -- was varid      -    , lexUriRef       -    , document, subgraph                                                   -    , newBlankNode-    )-where--import Swish.RDF.RDFGraph-    ( RDFGraph, RDFLabel(..)-    , ToRDFLabel(..)-    , NamespaceMap-    , LookupFormula(..) -    , addArc -    , setFormula-    , setNamespaces-    , emptyRDFGraph-    )--import Swish.RDF.GraphClass (arc)--import Swish.Utils.LookupMap-    ( LookupMap(..)-    , LookupEntryClass(..)-    , mapFind, mapFindMaybe, mapReplaceOrAdd, mapAdd, mapReplace )--import Swish.Utils.Namespace-    ( Namespace, makeNamespace-    , ScopedName-    , getScopeNamespace-    , getScopedNameURI-    , getScopeNamespace-    , makeURIScopedName-    , makeQNameScopedName-    , makeNSScopedName-    , nullScopedName-    )--import Swish.Utils.QName (QName)--import Swish.RDF.Vocabulary-    ( langName-    , rdfType-    , rdfFirst, rdfRest, rdfNil-    , owlSameAs, logImplies-    , xsdBoolean, xsdInteger, xsdDecimal, xsdDouble-    )--import Swish.RDF.RDFParser-    ( SpecialMap-    , ParseResult-    , runParserWithError-    -- , mapPrefix-    , prefixTable-    , specialTable-    , ignore-    , notFollowedBy-    , endBy-    , sepEndBy-    , manyTill-    , noneOf-    , char-    , ichar-    , string-    , stringT-    , symbol-    , lexeme-    , whiteSpace-    , mkTypedLit-    , hex4  -    , hex8  -    , appendURIs-    )--import Control.Applicative-import Control.Monad (forM_, foldM)--import Network.URI (URI(..), parseURIReference)--import Data.Char (isSpace, isDigit, ord) -import Data.Maybe (fromMaybe, fromJust)--import qualified Data.Text as T-import qualified Data.Text.Lazy as L-import Text.ParserCombinators.Poly.StateText--------------------------------------------------------------------------- Define parser state and helper functions--------------------------------------------------------------------------- | N3 parser state-data N3State = N3State-        { graphState :: RDFGraph            -- Graph under construction-        , thisNode   :: RDFLabel            -- current context node (aka 'this')-        , prefixUris :: NamespaceMap        -- namespace prefix mapping table-        , syntaxUris :: SpecialMap          -- special name mapping table-        , nodeGen    :: Int                 -- blank node id generator-        , keywordsList :: [T.Text]          -- contents of the @keywords statement-        , allowLocalNames :: Bool           -- True if @keywords used so that bare names are QNames in default namespace-        }---- | Functions to update N3State vector (use with stUpdate)--setPrefix :: Maybe T.Text -> URI -> N3State -> N3State-setPrefix pre uri st =  st { prefixUris=p' }-    where-        p' = mapReplaceOrAdd (makeNamespace pre uri) (prefixUris st)---- | Set name for special syntax element-setSName :: String -> ScopedName -> N3State -> N3State-setSName nam snam st =  st { syntaxUris=s' }-    where-        s' = mapReplaceOrAdd (nam,snam) (syntaxUris st)--setSUri :: String -> URI -> N3State -> N3State-setSUri nam = setSName nam . makeURIScopedName---- | Set the list of tokens that can be used without needing the leading --- \@ symbol.-setKeywordsList :: [T.Text] -> N3State -> N3State-setKeywordsList ks st = st { keywordsList = ks, allowLocalNames = True }----  Functions to access state:---- | Get name for special syntax element, default null-getSName :: N3State -> String -> ScopedName-getSName st nam =  mapFind nullScopedName nam (syntaxUris st)--getSUri :: N3State -> String -> URI-getSUri st nam = getScopedNameURI $ getSName st nam----  Map prefix to URI-getPrefixURI :: N3State -> Maybe T.Text -> Maybe URI-getPrefixURI st pre = mapFindMaybe pre (prefixUris st)--getKeywordsList :: N3State -> [T.Text]-getKeywordsList = keywordsList--getAllowLocalNames :: N3State -> Bool-getAllowLocalNames = allowLocalNames----  Return function to update graph in N3 parser state,---  using the supplied function of a graph----updateGraph :: (RDFGraph -> RDFGraph) -> N3State -> N3State-updateGraph f s = s { graphState = f (graphState s) }---------------------------------------------------------------------------  Define top-level parser function:---  accepts a string and returns a graph or error-------------------------------------------------------------------------type N3Parser a = Parser N3State a---- | Parse a string as N3 (with no real base URI).--- --- See 'parseN3' if you need to provide a base URI.----parseN3fromText ::-  L.Text -- ^ input in N3 format.-  -> ParseResult-parseN3fromText = flip parseN3 Nothing---- | Parse a string with an optional base URI.---            --- See also 'parseN3fromString'.            ----parseN3 ::-  L.Text -- ^ input in N3 format.-  -> Maybe QName -- ^ optional base URI-  -> ParseResult-parseN3 txt mbase = parseAnyfromText document mbase txt--{---- useful for testing-test :: String -> RDFGraph-test = either error id . parseAnyfromString document Nothing--}--hashURI :: URI-hashURI = fromJust $ parseURIReference "#"--emptyState :: -  Maybe QName  -- ^ starting base for the graph-  -> N3State-emptyState mbase = -  let pmap   = LookupMap [makeNamespace Nothing hashURI]-      muri   = fmap (makeQNameScopedName Nothing) mbase-      smap   = LookupMap $ specialTable muri-  in N3State-     { graphState = emptyRDFGraph-     , thisNode   = NoNode-     , prefixUris = pmap-     , syntaxUris = smap-     , nodeGen    = 0-     , keywordsList = ["a", "is", "of", "true", "false"] -- not 100% sure about true/false here-     , allowLocalNames = False-     }----- TODO: change from QName to URI for the base?---- | Function to supply initial context and parse supplied term.----parseAnyfromText :: N3Parser a      -- ^ parser to apply-                    -> Maybe QName  -- ^ base URI of the input, or @Nothing@ to use default base value-                    -> L.Text       -- ^ input to be parsed-                    -> Either String a-parseAnyfromText parser mbase = runParserWithError parser (emptyState mbase)--newBlankNode :: N3Parser RDFLabel-newBlankNode = do-  n <- stQuery (succ . nodeGen)-  stUpdate $ \s -> s { nodeGen = n }-  return $ Blank (show n)-  ---  Test functions for selected element parsing---- TODO: remove these-  -parseTextFromText :: String -> L.Text -> Either String String-parseTextFromText s =-    parseAnyfromText (string s) Nothing--parseAltFromText :: String -> String -> L.Text -> Either String String-parseAltFromText s1 s2 =-    parseAnyfromText (string s1 <|> string s2) Nothing--parseNameFromText :: L.Text -> Either String String-parseNameFromText =-    parseAnyfromText n3NameStr Nothing--{--This has been made tricky by the attempt to remove the default list-of prefixes from the starting point of a N3 parse and the subsequent-attempt to add every new namespace we come across to the parser state.--So we add in the original default namespaces for testing, since-this routine is really for testing.--}--addTestPrefixes :: N3Parser ()-addTestPrefixes = stUpdate $ \st -> st { prefixUris = LookupMap prefixTable } -- should append to existing map--{--parsePrefixFromText :: L.Text -> Either String URI-parsePrefixFromText =-    parseAnyfromText p Nothing-      where-        p = do-          addTestPrefixes-          pref <- n3Name-          st   <- stGet-          case getPrefixURI st (Just pref) of-            Just uri -> return uri-            _ -> fail $ "Undefined prefix: '" ++ pref ++ "'"--}--parseAbsURIrefFromText :: L.Text -> Either String URI-parseAbsURIrefFromText =-    parseAnyfromText explicitURI Nothing--parseLexURIrefFromText :: L.Text -> Either String URI-parseLexURIrefFromText =-    parseAnyfromText lexUriRef Nothing--parseURIref2FromText :: L.Text -> Either String ScopedName-parseURIref2FromText = -    parseAnyfromText (addTestPrefixes *> n3symbol) Nothing---------------------------------------------------------------------------  Syntax productions--------------------------------------------------------------------------- helper routines--comma, semiColon , fullStop :: N3Parser ()-comma = ignore $ symbol ","-semiColon = ignore $ symbol ";"-fullStop = ignore $ symbol "."---- a specialization of bracket/between -br :: String -> String -> N3Parser a -> N3Parser a-br lsym rsym = bracket (symbol lsym) (symbol rsym)---- to make porting from parsec to polyparse easier-between :: Parser s lbr -> Parser s rbr -> Parser s a -> Parser s a-between = bracket---- The @ character is optional if the keyword is in the--- keyword list----atSign :: T.Text -> N3Parser ()-atSign s = do-  st <- stGet-  -  let p = ichar '@'-  -  if s `elem` getKeywordsList st-    then ignore $ optional p-    else p-         -atWord :: T.Text -> N3Parser T.Text-atWord s = do-  atSign s-  -  -- TODO: does it really make sense to add the not-followed-by-a-colon rule here?-  -- apply to both cases even though should only really be necessary-  -- when the at sign is not given-  ---  lexeme $ stringT s *> notFollowedBy (== ':')-  return s--{--Since operatorLabel can be used to add a label with an -unknown namespace, we need to ensure that the namespace-is added if not known. If the namespace prefix is already-in use then it is over-written (rather than add a new-prefix for the label).--TODO:-  - could we use the reverse lookupmap functionality to-    find if the given namespace URI is in the namespace-    list? If it is, use it's key otherwise do a-    mapReplaceOrAdd for the input namespace.-    --}-operatorLabel :: ScopedName -> N3Parser RDFLabel-operatorLabel snam = do-  st <- stGet-  let sns = getScopeNamespace snam-      opmap = prefixUris st-      pkey = entryKey sns-      pval = entryVal sns-      -      rval = Res snam-      -  -- the lookup and the replacement could be fused-  case mapFindMaybe pkey opmap of-    Just val | val == pval -> return rval-             | otherwise   -> do-               stUpdate $ \s -> s { prefixUris = mapReplace opmap sns }-               return rval-    -    _ -> do-      stUpdate $ \s -> s { prefixUris = mapAdd opmap sns }-      return rval-        -{--Add statement to graph in N3 parser state.--To support literals that are written directly/implicitly - i.e.  as-true/false/1/1.0/1.0e23 - rather than a string with an explicit-datatype we need to special case handling of the object label for-literals. Is this actually needed? The N3 Formatter now doesn't-display the xsd: datatypes on output, but there may be issues with-other formats (e.g RDF/XML once it is supported).---}--type AddStatement = RDFLabel -> N3Parser ()--addStatement :: RDFLabel -> RDFLabel -> AddStatement-addStatement s p o@(Lit _ (Just dtype)) | dtype `elem` [xsdBoolean, xsdInteger, xsdDecimal, xsdDouble] = do -  ost <- stGet-  let stmt = arc s p o-      oldp = prefixUris ost-      ogs = graphState ost-      newp = mapReplaceOrAdd (getScopeNamespace dtype) oldp-  stUpdate $ \st -> st { prefixUris = newp, graphState = addArc stmt ogs }-addStatement s p o = stUpdate (updateGraph (addArc (arc s p o) ))--addStatementRev :: RDFLabel -> RDFLabel -> AddStatement-addStatementRev o p s = addStatement s p o--{--A number of productions require a name, which starts with--[A-Z_a-z#x00c0-#x00d6#x00d8-#x00f6#x00f8-#x02ff#x0370-#x037d#x037f-#x1fff#x200c-#x200d#x2070-#x218f#x2c00-#x2fef#x3001-#xd7ff#xf900-#xfdcf#xfdf0-#xfffd#x00010000-#x000effff]--and then has--[\-0-9A-Z_a-z#x00b7#x00c0-#x00d6#x00d8-#x00f6#x00f8-#x037d#x037f-#x1fff#x200c-#x200d#x203f-#x2040#x2070-#x218f#x2c00-#x2fef#x3001-#xd7ff#xf900-#xfdcf#xfdf0-#xfffd#x00010000-#x000effff]*--we encode this as the n3Name production--}--isaz, is09, isaz09 :: Char -> Bool-isaz c = c >= 'a' && c <= 'z'-is09 c = c >= '0' && c <= '9'-isaz09 c = isaz c || is09 c--match :: (Ord a) => a -> [(a,a)] -> Bool-match v = any (\(l,h) -> v >= l && v <= h)--startChar :: Char -> Bool-startChar c = let i = ord c-              in c == '_' || -                 match c [('A', 'Z'), ('a', 'z')] ||-                 match i [(0x00c0, 0x00d6), (0x00d8, 0x00f6), (0x00f8, 0x02ff), -                          (0x0370, 0x037d), -                          (0x037f, 0x1fff), (0x200c, 0x200d), -                          (0x2070, 0x218f), (0x2c00, 0x2fef), (0x3001, 0xd7ff), -                          (0xf900, 0xfdcf), (0xfdf0, 0xfffd), -                          (0x00010000, 0x000effff)]           -  -inBody :: Char -> Bool-inBody c = let i = ord c-           in c `elem` "-_" || i == 0x007 ||-              match c [('0', '9'), ('A', 'Z'), ('a', 'z')] ||-              match i [(0x00c0, 0x00d6), (0x00d8, 0x00f6), (0x00f8, 0x037d), -                       (0x037f, 0x1fff), (0x200c, 0x200d), (0x203f, 0x2040), -                       (0x2070, 0x218f), (0x2c00, 0x2fef), (0x3001, 0xd7ff), -                       (0xf900, 0xfdcf), (0xfdf0, 0xfffd), -                       (0x00010000, 0x000effff)]           ---- should this be strict or lazy text?-n3Name :: N3Parser T.Text-n3Name = T.cons <$> n3Init <*> n3Body-  where-    n3Init = satisfy startChar-    n3Body = L.toStrict <$> manySatisfy inBody---n3NameStr :: N3Parser String-n3NameStr = T.unpack <$> n3Name--{--quickvariable ::=	\?[A-Z_a-z#x00c0-#x00d6#x00d8-#x00f6#x00f8-#x02ff#x0370-#x037d#x037f-#x1fff#x200c-#x200d#x2070-#x218f#x2c00-#x2fef#x3001-#xd7ff#xf900-#xfdcf#xfdf0-#xfffd#x00010000-#x000effff][\-0-9A-Z_a-z#x00b7#x00c0-#x00d6#x00d8-#x00f6#x00f8-#x037d#x037f-#x1fff#x200c-#x200d#x203f-#x2040#x2070-#x218f#x2c00-#x2fef#x3001-#xd7ff#xf900-#xfdcf#xfdf0-#xfffd#x00010000-#x000effff]*--}---- TODO: is mapping to Var correct?-quickVariable :: N3Parser RDFLabel-quickVariable = char '?' *> (Var <$> n3NameStr) --{--string ::=	("""[^"\\]*(?:(?:\\.|"(?!""))[^"\\]*)*""")|("[^"\\]*(?:\\.[^"\\]*)*")--or--string ::= tripleQuoted | singleQUoted---}--n3string :: N3Parser T.Text-n3string = tripleQuoted <|> singleQuoted --{--singleQuoted ::=  "[^"\\]*(?:\\.[^"\\]*)*"--asciiChars :: String-asciiChars = map chr [0x20..0x7e]--asciiCharsN3 :: String-asciiCharsN3 = filter (`notElem` "\\\"") asciiChars---}--digit :: N3Parser Char-digit = satisfy isDigit--{--This is very similar to NTriples accept that also allow the escaping of '-even though it is not required.--The Python rules allow \N{name}, where name is the Unicode name. It's-not clear whether we need to support this too, so for now we do not.---}-protectedChar :: N3Parser Char-protectedChar =-  (char 't' *> return '\t')-  <|> (char 'n' *> return '\n')-  <|> (char 'r' *> return '\r')-  <|> (char '"' *> return '"')-  <|> (char '\'' *> return '\'')-  <|> (char '\\' *> return '\\')-  <|> (char 'u' *> hex4)-  <|> (char 'U' *> hex8)---- Accept an escape character or any character as long as it isn't--- a new-line or quote. Unrecognized escape sequences should therefore--- be left alone by this. ----n3Character :: N3Parser Char-n3Character = -  (char '\\' *> (protectedChar <|> return '\\'))-  <|> noneOf "\"\n"-      -{--      <|> (oneOf asciiCharsN3 <?> "ASCII character")-              -- TODO: bodyChar and asciiCharsN3 overlap-      <|> (oneOf bodyChar <?> "Unicode character")--}              --sQuot :: N3Parser Char-sQuot = char '"'--{--TODO: there must be a better way of building up the Text--}--singleQuoted :: N3Parser T.Text-singleQuoted = fmap T.pack (bracket sQuot sQuot $ many n3Character)-    -{--tripleQUoted ::=	"""[^"\\]*(?:(?:\\.|"(?!""))[^"\\]*)*"""--}-tripleQuoted :: N3Parser T.Text-tripleQuoted = tQuot *> fmap T.pack (manyTill (n3Character <|> sQuot <|> char '\n') tQuot)-  where-    -- tQuot = try (count 3 sQuot)-    tQuot = exactly 3 sQuot--getDefaultPrefix :: N3Parser Namespace-getDefaultPrefix = do-  s <- stGet-  case getPrefixURI s Nothing of-    Just uri -> return $ makeNamespace Nothing uri-    _ -> fail "No default prefix defined; how unexpected!"--addBase :: URI -> N3Parser ()-addBase = stUpdate . setSUri "base" --addPrefix :: Maybe T.Text -> URI -> N3Parser ()-addPrefix p = stUpdate . setPrefix p --{-|-Update the set of keywords that can be given without-an \@ sign.--}-updateKeywordsList :: [T.Text] -> N3Parser ()-updateKeywordsList = stUpdate . setKeywordsList--{--document ::=		|	statements_optional EOF--}--document :: N3Parser RDFGraph-document = mkGr <$> (whiteSpace *> statementsOptional *> eof *> stGet)-  where-    mkGr s = setNamespaces (prefixUris s) (graphState s)--{--statements_optional ::=		|	statement  "."  statements_optional-		|	void---}--statementsOptional :: N3Parser ()-statementsOptional = ignore $ endBy (lexeme statement) fullStop-    -{--statement ::=		|	declaration-		|	existential-		|	simpleStatement-		|	universal---}--statement :: N3Parser ()-statement =-  declaration-  <|> existential-  <|> universal-  <|> simpleStatement-  -- having an error here leads to less informative errors in general, it seems-  -- <?> "statement (existential or universal quantification or a simple statement)"-  -{--declaration ::=		|	 "@base"  explicituri-		|	 "@keywords"  barename_csl-		|	 "@prefix"  prefix explicituri--}---- TODO: do we need the try statements here? atWord would need to have a try on '@'--- (if applicable) which should mean being able to get rid of try----declaration :: N3Parser ()-declaration = oneOf [-  atWord "base" >> explicitURI >>= addBase,-  atWord "keywords" >> bareNameCsl >>= updateKeywordsList,-  atWord "prefix" *> getPrefix-  ]--  {--  (try (atWord "base") >> explicitURI >>= addBase)-  <|>-  (try (atWord "keywords") >> bareNameCsl >>= updateKeywordsList)-  <|>-  (try (atWord "prefix") *> getPrefix)-  -}-  -getPrefix :: N3Parser ()  -getPrefix = do-  p <- lexeme prefix-  u <- explicitURI-  addPrefix p u--{--explicituri ::=	<[^>]*>--Note: white space is to be ignored within <>--}--explicitURI :: N3Parser URI-explicitURI = do-  let lb = char '<'-      rb = char '>'-  -  -- TODO: do the whitespace definitions match?-  ustr <- between lb rb $ many (satisfy (/= '>'))-  let uclean = filter (not . isSpace) ustr-  -  case parseURIReference uclean of-    Nothing -> fail $ "Unable to convert <" ++ uclean ++ "> to a URI"-    Just uref -> do-      s <- stGet-      let base = getSUri s "base"-      either fail return $ appendURIs base uref-      --- production from the old parser; used in SwishScript-lexUriRef :: N3Parser URI-lexUriRef = lexeme explicitURI--{--barename ::=	[A-Z_a-z#x00c0-#x00d6#x00d8-#x00f6#x00f8-#x02ff#x0370-#x037d#x037f-#x1fff#x200c-#x200d#x2070-#x218f#x2c00-#x2fef#x3001-#xd7ff#xf900-#xfdcf#xfdf0-#xfffd#x00010000-#x000effff][\-0-9A-Z_a-z#x00b7#x00c0-#x00d6#x00d8-#x00f6#x00f8-#x037d#x037f-#x1fff#x200c-#x200d#x203f-#x2040#x2070-#x218f#x2c00-#x2fef#x3001-#xd7ff#xf900-#xfdcf#xfdf0-#xfffd#x00010000-#x000effff]*-barename_csl ::=		|	barename barename_csl_tail-		|	void-barename_csl_tail ::=		|	 ","  barename barename_csl_tail-		|	void--}--bareNameCsl :: N3Parser [T.Text]-bareNameCsl = sepBy (lexeme bareName) comma--bareName :: N3Parser T.Text-bareName = n3Name --{--prefix ::=	([A-Z_a-z#x00c0-#x00d6#x00d8-#x00f6#x00f8-#x02ff#x0370-#x037d#x037f-#x1fff#x200c-#x200d#x2070-#x218f#x2c00-#x2fef#x3001-#xd7ff#xf900-#xfdcf#xfdf0-#xfffd#x00010000-#x000effff][\-0-9A-Z_a-z#x00b7#x00c0-#x00d6#x00d8-#x00f6#x00f8-#x037d#x037f-#x1fff#x200c-#x200d#x203f-#x2040#x2070-#x218f#x2c00-#x2fef#x3001-#xd7ff#xf900-#xfdcf#xfdf0-#xfffd#x00010000-#x000effff]*)?:--}--prefix :: N3Parser (Maybe T.Text)-prefix = optional (lexeme n3Name) <* char ':'-         --{--symbol ::=		|	explicituri-		|	qname-symbol_csl ::=		|	symbol symbol_csl_tail-		|	void-symbol_csl_tail ::=		|	 ","  symbol symbol_csl_tail-		|	void---}--n3symbol :: N3Parser ScopedName-n3symbol = -  (makeURIScopedName <$> explicitURI)-  <|> qname--symbolCsl :: N3Parser [ScopedName]-symbolCsl = sepBy (lexeme n3symbol) comma--{--qname ::=	(([A-Z_a-z#x00c0-#x00d6#x00d8-#x00f6#x00f8-#x02ff#x0370-#x037d#x037f-#x1fff#x200c-#x200d#x2070-#x218f#x2c00-#x2fef#x3001-#xd7ff#xf900-#xfdcf#xfdf0-#xfffd#x00010000-#x000effff][\-0-9A-Z_a-z#x00b7#x00c0-#x00d6#x00d8-#x00f6#x00f8-#x037d#x037f-#x1fff#x200c-#x200d#x203f-#x2040#x2070-#x218f#x2c00-#x2fef#x3001-#xd7ff#xf900-#xfdcf#xfdf0-#xfffd#x00010000-#x000effff]*)?:)?[A-Z_a-z#x00c0-#x00d6#x00d8-#x00f6#x00f8-#x02ff#x0370-#x037d#x037f-#x1fff#x200c-#x200d#x2070-#x218f#x2c00-#x2fef#x3001-#xd7ff#xf900-#xfdcf#xfdf0-#xfffd#x00010000-#x000effff][\-0-9A-Z_a-z#x00b7#x00c0-#x00d6#x00d8-#x00f6#x00f8-#x037d#x037f-#x1fff#x200c-#x200d#x203f-#x2040#x2070-#x218f#x2c00-#x2fef#x3001-#xd7ff#xf900-#xfdcf#xfdf0-#xfffd#x00010000-#x000effff]*--TODO:-  Note that, for now, we explicitly handle blank nodes-  (of the form _:name) direcly in pathItem'.-  This is not a good idea since qname' is used elsewhere-  and so shouldn't we do the same thing there too?--}--qname :: N3Parser ScopedName-qname =-  fmap (uncurry makeNSScopedName) (char ':' >> g)-  <|> (n3Name >>= fullOrLocalQName)-    where-      g = (,) <$> getDefaultPrefix <*> (n3Name <|> return "")-               -fullOrLocalQName :: T.Text -> N3Parser ScopedName-fullOrLocalQName name = -  (char ':' *> fullQName name)-  <|> localQName name-  -fullQName :: T.Text -> N3Parser ScopedName-fullQName name = makeNSScopedName <$> findPrefix name <*> (n3Name <|> pure "")-  -findPrefix :: T.Text -> N3Parser Namespace-findPrefix pre = do-  st <- stGet-  case mapFindMaybe (Just pre) (prefixUris st) of-    Just uri -> return $ makeNamespace (Just pre) uri-    Nothing  -> failBad $ "Prefix '" ++ T.unpack pre ++ ":' not bound."-  -localQName :: T.Text -> N3Parser ScopedName-localQName name = do-  st <- stGet-  if getAllowLocalNames st-    then let g = (,) <$> getDefaultPrefix <*> pure name-         in uncurry makeNSScopedName <$> g-            -    else fail ("Invalid 'bare' word: " ++ T.unpack name)-- TODO: not ideal error message; can we handle this case differently?--{--existential ::=		|	 "@forSome"  symbol_csl--For now we just read in the symbols and ignore them,-since we do not mark blank nodes as existentially quantified-(we assume this is the case).--TODO: fix this?--}--existential :: N3Parser ()--- existential = try (atWord "forSome") *> symbolCsl >> return ()-existential = atWord "forSome" *> symbolCsl *> pure ()--{--simpleStatement ::=		|	subject propertylist--}--simpleStatement :: N3Parser ()-simpleStatement = subject >>= propertyListWith-  -{--subject ::=		|	expression--}--subject :: N3Parser RDFLabel-subject = lexeme expression--{--expression ::=		|	pathitem pathtail-pathtail ::=		|	 "!"  expression-		|	 "^"  expression-		|	void---}--expression :: N3Parser RDFLabel-expression = do-  i <- pathItem-  -  let backwardExpr = char '!' *> return addStatementRev -      forwardExpr  = char '^' *> return addStatement-  -  mpt <- optional-        ( (,) <$> lexeme (forwardExpr <|> backwardExpr) <*> lexeme expression )-  case mpt of-    Nothing -> return i -    Just (addFunc, pt) -> do-      bNode <- newBlankNode-      addFunc bNode pt i-      return bNode-  -{--pathitem ::=		|	 "("  pathlist  ")" -		|	 "["  propertylist  "]" -		|	 "{"  formulacontent  "}" -		|	boolean-		|	literal-		|	numericliteral-		|	quickvariable-		|	symbol--pathlist ::=		|	expression pathlist-		|	void--Need to think about how to handle formulae, since need to know the context-of the call to know where to add them.--TOOD: may include direct support for blank nodes here,-namely convert _:stringval -> Blank stringval since although-this should be done by symbol the types don't seem to easily match-up (at first blush anyway)--}--pathItem :: N3Parser RDFLabel-pathItem = -  br "(" ")" pathList-  <|> br "[" "]" propertyListBNode-  <|> br "{" "}" formulaContent-  -- <|> try boolean-  <|> boolean-  <|> literal-  <|> numericLiteral-  <|> quickVariable-  <|> Blank <$> (string "_:" *> n3NameStr) -- TODO a hack that needs fixing-  <|> Res <$> n3symbol-  -{-  -we create a blank node for the list and return it, whilst-adding the list contents to the graph--}-pathList :: N3Parser RDFLabel-pathList = do-  cts <- many (lexeme expression)-  eNode <- operatorLabel rdfNil-  case cts of-    [] -> return eNode-      -    (c:cs) -> do-      sNode <- newBlankNode-      first <- operatorLabel rdfFirst-      addStatement sNode first c-      lNode <- foldM addElem sNode cs-      rest <- operatorLabel rdfRest-      addStatement lNode rest eNode-      return sNode--    where      -      addElem prevNode curElem = do-        bNode <- newBlankNode-        first <- operatorLabel rdfFirst-        rest <- operatorLabel rdfRest-        addStatement prevNode rest bNode-        addStatement bNode first curElem-        return bNode-        -{--formulacontent ::=		|	statementlist--statementlist ::=		|	statement statementtail-		|	void-statementtail ::=		|	 "."  statementlist-		|	void--}--restoreState :: N3State -> N3Parser N3State-restoreState origState = do-  oldState <- stGet-  stUpdate $ \_ -> origState { nodeGen = nodeGen oldState }-  return oldState--{--We create a subgraph and assign it to a blank node, returning the-blank node. At present it is a combination of the subgraph and formula-productions from the origial parser.--TODO: is it correct?--}-formulaContent :: N3Parser RDFLabel-formulaContent = do-  bNode <- newBlankNode-  pstate <- stGet-  stUpdate $ \st -> st { graphState = emptyRDFGraph, thisNode = bNode }-  statementList-  oldState <- restoreState pstate-  stUpdate $ updateGraph $ setFormula (Formula bNode (graphState oldState))-  return bNode-  -subgraph :: RDFLabel -> N3Parser RDFGraph-subgraph this = do-  pstate <- stGet-  stUpdate $ \st -> st { graphState = emptyRDFGraph, thisNode = this }-  statementsOptional    -- parse statements of formula-  oldState <- restoreState pstate  -  return $ graphState oldState-  -statementList :: N3Parser ()-statementList = ignore $ sepEndBy (lexeme statement) fullStop--{--boolean ::=		|	 "@false" -		|	 "@true" --}--boolean :: N3Parser RDFLabel-boolean = mkTypedLit xsdBoolean <$> -          (atWord "false" <|> atWord "true")-          -- (try (atWord "false") <|> atWord "true")-           -{--dtlang ::=		|	 "@"  langcode-		|	 "^^"  symbol-		|	void-literal ::=		|	string dtlang--langcode ::=	[a-z]+(-[a-z0-9]+)*---}--literal :: N3Parser RDFLabel-literal = Lit <$> n3string <*> optional dtlang-  -dtlang :: N3Parser ScopedName-dtlang = -  (char '@' *> langcode)-  <|> string "^^" *> n3symbol-  -- <|> (try (string "^^") *> n3symbol)--langcode :: N3Parser ScopedName-langcode = do-  h <- many1Satisfy isaz-  mt <- optional ( L.append <$> (char '-' *> pure (L.singleton '-')) <*> many1Satisfy isaz09)-  return $ langName $ L.toStrict $ L.append h (fromMaybe L.empty mt)-    -{--decimal ::=	[-+]?[0-9]+(\.[0-9]+)?-double ::=	[-+]?[0-9]+(\.[0-9]+)?([eE][-+]?[0-9]+)-integer ::=	[-+]?[0-9]+-numericliteral ::=		|	decimal-		|	double-		|	integer--We actually support 1. for decimal values which isn't supported -by the above production.--TODO: we could convert via something like--  maybeRead value :: Double >>= Just . toRDFLabel--which would mean we store the canonical XSD value in the-label, but it is not useful for the xsd:decimal case-since we currently don't have a Haskell type that-goes with it.--}--numericLiteral :: N3Parser RDFLabel-numericLiteral =-  -- -- try (mkTypedLit xsdDouble <$> n3double)-  -- try (d2s <$> n3double)-  -- <|> try (mkTypedLit xsdDecimal <$> n3decimal)-  d2s <$> n3double-  <|> mkTypedLit xsdDecimal . T.pack <$> n3decimal-  <|> mkTypedLit xsdInteger . T.pack <$> n3integer--n3sign :: N3Parser Char-n3sign = char '+' <|> char '-'--n3integer :: N3Parser String-n3integer = do-  ms <- optional n3sign-  ds <- many1 digit-  case ms of-    Just s -> return $ s : ds-    _ -> return ds--n3decimal :: N3Parser String--- n3decimal = (++) <$> n3integer <*> ( (:) <$> char '.' <*> many1 digit )-n3decimal = (++) <$> n3integer <*> ( (:) <$> char '.' <*> many digit )-           -n3double :: N3Parser String-n3double = (++) <$> n3decimal <*> ( (:) <$> satisfy (`elem` "eE") <*> n3integer )---- Convert a double, as returned by n3double, into it's--- canonical XSD form. We assume that n3double returns--- a syntactivally valid Double, so do not bother with reads here----d2s :: String -> RDFLabel-d2s s = toRDFLabel (read s :: Double)--{--propertylist ::=		|	verb object objecttail propertylisttail-		|	void-propertylisttail ::=		|	 ";"  propertylist-		|	void---}---- it's probably important that bNode is created *after*--- processing the plist (mainly for the assumptions made by--- formatting the output as N3; e.g. list/sequence ordering)----propertyListBNode :: N3Parser RDFLabel-propertyListBNode = do-  plist <- sepEndBy ((,) <$> lexeme verb <*> objectList) semiColon-  bNode <- newBlankNode-  let addList ((addFunc,vrb),items) = mapM_ (addFunc bNode vrb) items-  forM_ plist addList-  return bNode--propertyListWith :: RDFLabel -> N3Parser ()-propertyListWith subj = -  let -- term = lexeme verb >>= objectListWith subj-      term = lexeme verb >>= \(addFunc, vrb) -> objectListWith (addFunc subj vrb)-  in ignore $ sepEndBy term semiColon-  -{--object ::=		|	expression-objecttail ::=		|	 ","  object objecttail-		|	void--We change the production rule from objecttail to objectlist for lists of-objects (may change back).---}--object :: N3Parser RDFLabel-object = lexeme expression--objectList :: N3Parser [RDFLabel]-objectList = sepBy1 object comma--objectWith :: AddStatement -> N3Parser ()-objectWith addFunc = object >>= addFunc --objectListWith :: AddStatement -> N3Parser ()-objectListWith addFunc =-  ignore $ sepBy1 (objectWith addFunc) comma--{--objectList1 :: N3Parser [RDFLabel]-objectList1 = sepBy1 object comma--}--{--verb ::=		|	 "<=" -		|	 "=" -		|	 "=>" -		|	 "@a" -		|	 "@has"  expression-		|	 "@is"  expression  "@of" -		|	expression---}--verb :: N3Parser (RDFLabel -> RDFLabel -> AddStatement, RDFLabel)-verb = -  -- we check reverse first so that <= is tried before looking for a URI via expression rule-  (,) addStatementRev <$> verbReverse-  <|> (,) addStatement <$> verbForward---- those verbs for which subject is on the right and object on the left-verbReverse :: N3Parser RDFLabel-verbReverse =-  string "<=" *> operatorLabel logImplies-  <|> between (atWord "is") (atWord "of") (lexeme expression)--{--  try (string "<=") *> operatorLabel logImplies-  <|> between (try (atWord "is")) (atWord "of") (lexeme expression)--}---- those verbs with subject on the left and object on the right-verbForward :: N3Parser RDFLabel-verbForward =  -  -- (try (string "=>") *> operatorLabel logImplies)-  (string "=>" *> operatorLabel logImplies)-  <|> (string "=" *> operatorLabel owlSameAs)-  -- <|> (try (atWord "a") *> operatorLabel rdfType)-  <|> (atWord "a" *> operatorLabel rdfType)-  <|> (atWord "has" *> lexeme expression)-  <|> lexeme expression--{--universal ::=		|	 "@forAll"  symbol_csl--TODO: what needs to be done to support universal quantification--}-universal :: N3Parser ()-universal = -  -- try (atWord "forAll") *> -  atWord "forAll" *> -  failBad "universal (@forAll) currently unsupported." -  -- will be something like: *> symbolCsl----------------------------------------------------------------------------------------  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  All rights reserved.------  This file is part of Swish.------  Swish is free software; you can redistribute it and/or modify---  it under the terms of the GNU General Public License as published by---  the Free Software Foundation; either version 2 of the License, or---  (at your option) any later version.------  Swish is distributed in the hope that it will be useful,---  but WITHOUT ANY WARRANTY; without even the implied warranty of---  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the---  GNU General Public License for more details.------  You should have received a copy of the GNU General Public License---  along with Swish; if not, write to:---    The Free Software Foundation, Inc.,---    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA------------------------------------------------------------------------------------
− src/Swish/RDF/NTFormatter.hs
@@ -1,247 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  N3Formatter---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  License     :  GPL V2------  Maintainer  :  Douglas Burke---  Stability   :  experimental---  Portability :  OverloadedStrings------  This Module implements a NTriples formatter (see [1])---  for an RDFGraph value.--------- REFERENCES:------ 1 <http://www.w3.org/TR/rdf-testcases/#ntriples>---     RDF Test Cases---     W3C Recommendation 10 February 2004--------------------------------------------------------------------------------------module Swish.RDF.NTFormatter-    ( NodeGenLookupMap-    , formatGraphAsText-    , formatGraphAsLazyText-    , formatGraphAsBuilder-    )-where--import Swish.RDF.RDFGraph-  ( RDFGraph, RDFLabel(..)-  , getArcs-  )--import Swish.RDF.GraphClass-    ( Arc(..) )--import Swish.Utils.Namespace (ScopedName, getQName)-import Swish.RDF.Vocabulary (isLang, langTag)--import Swish.Utils.LookupMap-    ( LookupMap, emptyLookupMap-    , mapFind, mapAdd-    )--import Data.Char (ord, intToDigit, toUpper)--import Control.Monad.State-import Control.Applicative ((<$>))-import Data.Monoid---- it strikes me that using Lazy Text here is likely to be--- wrong; however I have done no profiling to back this--- assumption up!--import qualified Data.Text as T-import qualified Data.Text.Lazy as L-import qualified Data.Text.Lazy.Builder as B---------------------------------------------------------------------------  Graph formatting state monad-----------------------------------------------------------------------------  This is a lot simpler than other formatters.----  | Node name generation state information that carries through---  and is updated by nested formulae-type NodeGenLookupMap = LookupMap (RDFLabel,Int)--data NTFormatterState = NTFS { -      ntfsNodeMap :: NodeGenLookupMap,-      ntfsNodeGen :: Int-    } deriving Show--emptyNTFS :: NTFormatterState-emptyNTFS = NTFS {-              ntfsNodeMap = emptyLookupMap,-              ntfsNodeGen = 0-              }--type Formatter a = State NTFormatterState a---- | Convert a RDF graph to NTriples format.-formatGraphAsText :: RDFGraph -> T.Text-formatGraphAsText = L.toStrict . formatGraphAsLazyText---- | Convert a RDF graph to NTriples format.-formatGraphAsLazyText :: RDFGraph -> L.Text-formatGraphAsLazyText = B.toLazyText . formatGraphAsBuilder---- | Convert a RDF graph to NTriples format.-formatGraphAsBuilder :: RDFGraph -> B.Builder-formatGraphAsBuilder gr = fst $ runState (formatGraph gr) emptyNTFS---------------------------------------------------------------------------  Formatting as a monad-based computation-------------------------------------------------------------------------formatGraph :: RDFGraph -> Formatter B.Builder-formatGraph gr = mconcat <$> mapM formatArc (getArcs gr)---- TODO: this reverses the contents but may be faster?---       that is if I've got the order right in the mappend call--- formatGraphBuilder gr = foldl' (\a b -> b `mappend` (formatArcBuilder a)) B.empty (getArcs gr)--space, nl :: B.Builder-space = B.singleton ' '-nl    = " .\n"--formatArc :: Arc RDFLabel -> Formatter B.Builder-formatArc (Arc s p o) = do-  sl <- formatLabel s-  pl <- formatLabel p-  ol <- formatLabel o-  return $ mconcat [sl, space, pl, space, ol, nl]-  -- return $ sl `mappend` $ space `mappend` $ pl `mappend` $ space `mappend` $ ol `mappend` nl-  -{--If we have a blank node then can--  - use the label it contains-  - generate a new one on output--For now we create new labels whatever the input was since this-simplifies things, but it may be changed.--formatLabel :: RDFLabel -> Formatter String-formatLabel lab@(Blank (lnc:_)) = -  if isDigit lnc then mapBlankNode lab else return $ show lab-formatLabel lab = return $ show lab--}--squote, at, carets  :: B.Builder-squote = "\""-at     = "@"-carets = "^^"--formatLabel :: RDFLabel -> Formatter B.Builder-formatLabel lab@(Blank _) = mapBlankNode lab-formatLabel (Res sn) = return $ showScopedName sn-formatLabel (Lit lit Nothing) = return $ quoteText lit-formatLabel (Lit lit (Just nam)) | isLang nam = return $ mconcat [quoteText lit, at, B.fromText (langTag nam)]-                                 | otherwise  = return $ mconcat [quoteText lit, carets, showScopedName nam]---- do not expect to get the following, but include--- just in case rather than failing-formatLabel lab = return $ B.fromString $ show lab--mapBlankNode :: RDFLabel -> Formatter B.Builder-mapBlankNode lab = do-  st <- get-  let cmap = ntfsNodeMap st-      cval = ntfsNodeGen st--  nv <- case mapFind 0 lab cmap of-            0 -> do-              let nval = succ cval-                  nmap = mapAdd cmap (lab, nval)--              put $ st { ntfsNodeMap = nmap, ntfsNodeGen = nval }-              return nval--            n -> return n--  return $ "_:swish" `mappend` B.fromString (show nv)---- TODO: can we use Network.URI to protect the URI?-showScopedName :: ScopedName -> B.Builder-{--showScopedName (ScopedName n l) = -  let uri = T.pack (show (nsURI n)) `mappend` l-  in mconcat ["<", B.fromText (quote uri), ">"]--}--- showScopedName s = mconcat ["<", B.fromText (quote (T.pack (show (getQName s)))), ">"]-showScopedName s = B.fromText (quote (T.pack (show (getQName s)))) -- looks like qname already adds the <> around this--{--Swish.Utils.MiscHelpers contains a quote routine-which we expand upon here to match the NT syntax.--}--quoteText :: T.Text -> B.Builder-quoteText  st = mconcat [squote, B.fromText (quote st), squote]--{--QUS: should we be operating on Text like this?--}--quote :: T.Text -> T.Text-quote = T.concatMap quoteT--quoteT :: Char -> T.Text-quoteT '\\' = "\\\\"-quoteT '"'  = "\\\""-quoteT '\n' = "\\n"-quoteT '\t' = "\\t"-quoteT '\r' = "\\r"-quoteT c    = -  let nc = ord c-      -  in if nc > 0xffff -     then T.pack ('\\':'U': numToHex 8 nc)-     else if nc > 0x7e || nc < 0x20-          then T.pack ('\\':'u': numToHex 4 nc)-          else T.singleton c-                      --- we assume c > 0, n >= 0 and that the input value fits--- into the requested number of digits-numToHex :: Int -> Int -> String-numToHex c = go []-  where-    go s 0 = replicate (c - length s) '0' ++ s-    go s n = -      let (m,x) = divMod n 16-      in go (iToD x:s) m--    -- Data.Char.intToDigit uses lower-case Hex-    iToD x | x < 10    = intToDigit x-           | otherwise = toUpper $ intToDigit x-      ---------------------------------------------------------------------------------------  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  All rights reserved.------  This file is part of Swish.------  Swish is free software; you can redistribute it and/or modify---  it under the terms of the GNU General Public License as published by---  the Free Software Foundation; either version 2 of the License, or---  (at your option) any later version.------  Swish is distributed in the hope that it will be useful,---  but WITHOUT ANY WARRANTY; without even the implied warranty of---  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the---  GNU General Public License for more details.------  You should have received a copy of the GNU General Public License---  along with Swish; if not, write to:---    The Free Software Foundation, Inc.,---    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA------------------------------------------------------------------------------------
− src/Swish/RDF/NTParser.hs
@@ -1,427 +0,0 @@------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  NTParser---  Copyright   :  (c) 2011 Douglas Burke---  License     :  GPL V2------  Maintainer  :  Douglas Burke---  Stability   :  experimental---  Portability :  H98------  This Module implements a NTriples parser (see [1]), returning a---  new 'RDFGraph' consisting of triples and namespace information parsed from---  the supplied NTriples input string, or an error indication.------ REFERENCES:------ 1 <http://www.w3.org/TR/rdf-testcases/#ntriples>---     RDF Test Cases---     W3C Recommendation 10 February 2004--------------------------------------------------------------------------------------module Swish.RDF.NTParser-    ( ParseResult-    , parseNT      -    , parsefromString-    -      {--    -- * Exports for parsers that embed NTriples in a bigger syntax-    , NTParser, NTState(..)-    , ntripleDoc-    , line, ws, comment, eoln-    , character, name, triple-    , subject, predicate, object-    , uriref, urirefLbl-    , nodeID, literal, language-      -}-      -    )-where--import Swish.RDF.RDFGraph-    ( RDFGraph, RDFLabel(..)-    , addArc -    , emptyRDFGraph-    )--import Swish.RDF.GraphClass (arc)--import Swish.Utils.Namespace (ScopedName, makeURIScopedName)--import Swish.RDF.Vocabulary (langName)--import Swish.RDF.RDFParser ( ParseResult-    , runParserWithError-    , ignore-    , skipMany-    , noneOf-    , char-    , string-    , eoln-    , fullStop-    , hex4-    , hex8-    )-  -{--import Swish.RDF.RDFParser-    ( ParseResult, RDFParser-    , ignore-    , annotateParsecError-    )--}--import Control.Applicative--import Network.URI (parseURI)--import qualified Data.Text as T-import qualified Data.Text.Lazy as L--import Data.Char (ord) -import Data.Maybe (fromMaybe)--import Text.ParserCombinators.Poly.StateText--------------------------------------------------------------------------- Define parser state and helper functions--------------------------------------------------------------------------- | NT parser state-data NTState = NTState-        { graphState :: RDFGraph            -- Graph under construction-        }--emptyState :: NTState-emptyState = NTState { graphState = emptyRDFGraph }-           ---  Return function to update graph in NT parser state,---  using the supplied function of a graph. This is for use---  with stUpdate.----updateGraph :: (RDFGraph -> RDFGraph) -> NTState -> NTState-updateGraph f s = s { graphState = f (graphState s) }---------------------------------------------------------------------------  Define top-level parser function:---  accepts a string and returns a graph or error--------------------------------------------------------------------------- | Parser that carries around a `NTState` record.-type NTParser a = Parser NTState a---- | Parse a string.--- -parseNT ::-  L.Text -- ^ input in NTriples format.-  -> ParseResult-parseNT = parsefromText ntripleDoc--{---- useful for testing-test :: String -> RDFGraph-test = either error id . parseNT--}---- | Function to supply initial context and parse supplied term.----parsefromString :: -    NTParser a      -- ^ parser to apply-    -> String       -- ^ input to be parsed-    -> Either String a-parsefromString parser = parsefromText parser . L.pack---- | Function to supply initial context and parse supplied term.----parsefromText :: -    NTParser a      -- ^ parser to apply-    -> L.Text       -- ^ input to be parsed-    -> Either String a-parsefromText parser = runParserWithError parser emptyState---- helper routines--{--lineFeed :: NTParser ()-lineFeed = ignore (char '\r')--}---- Add statement to graph in NT parser state--addStatement :: RDFLabel -> RDFLabel -> RDFLabel -> NTParser ()-addStatement s p o = stUpdate (updateGraph (addArc (arc s p o) ))---------------------------------------------------------------------------  Syntax productions-------------------------------------------------------------------------{---EBNF from the specification, using the notation from XML 1.0, second edition,-is included inline below.--We do not force ASCII 7-bit semantics here yet.--space	::=	#x20 /* US-ASCII space - decimal 32 */	-cr	::=	#xD /* US-ASCII carriage return - decimal 13 */	-lf	::=	#xA /* US-ASCII line feed - decimal 10 */	-tab	::=	#x9 /* US-ASCII horizontal tab - decimal 9 */	--The productions are kept as close as possible to the specification-for now.---}--{--ntripleDoc	::=	line*	-line	::=	ws* ( comment | triple )? eoln	--We relax the rule that the input must be empty or end with a new line.--ntripleDoc :: NTParser RDFGraph-ntripleDoc = graphState <$> (many line *> eof *> getState)--line :: NTParser ()-line = skipMany ws *> optional (comment <|> triple) *> eoln--}--ntripleDoc :: NTParser RDFGraph-ntripleDoc = graphState <$> (sepBy line eoln *> optional eoln *> skipWS *> eof *> stGet)--line :: NTParser ()-line = skipWS *> ignore (optional (comment <|> triple))--{--ws	::=	space | tab	--Could use whiteSpace rule here, but that would permit-constructs (e.g. comments) where we do not support them.--}--isWS :: Char -> Bool-isWS = (`elem` " \t")--{--ws :: NTParser ()--- ws = ignore (char ' ' <|> tab)-ws = ignore $ satisfy isWS--}-           -skipWS :: NTParser ()-skipWS = ignore $ manySatisfy isWS--skip1WS :: NTParser ()-skip1WS = ignore $ many1Satisfy isWS--{--comment	::=	'#' ( character - ( cr | lf ) )*	--}--comment :: NTParser ()-comment = char '#' *> skipMany (noneOf "\r\n")--{--eoln	::=	cr | lf | cr lf	--}--{--name	::=	[A-Za-z][A-Za-z0-9]*	--}--isaz, isAZ, is09 :: Char -> Bool-isaz c = c >= 'a' && c <= 'z'-isAZ c = c >= 'A' && c <= 'Z'-is09 c = c >= '0' && c <= '9'--isHeadChar, isBodyChar :: Char -> Bool-isHeadChar c = isaz c || isAZ c-isBodyChar c = isHeadChar c || is09 c--name :: NTParser L.Text-name = L.cons <$> satisfy isHeadChar <*> manySatisfy isBodyChar--nameStr :: NTParser String-nameStr = L.unpack <$> name--{--triple	::=	subject ws+ predicate ws+ object ws* '.' ws*	---}--triple :: NTParser ()-triple = -  {- tryin to be fancy but addStatement is a Parser not a pure function-  addStatement -  <$> (subject <* skip1WS)-  <*> (predicate <* skip1WS)-  <*> (object <* (skipWS *> fullStop *> skipWS))-  -}-  -  do-    s <- subject-    skip1WS-    p <- predicate-    skip1WS-    o <- object-    skipWS-    fullStop-    skipWS-    addStatement s p o--{--subject	::=	uriref | nodeID	-predicate	::=	uriref	-object	::=	uriref | nodeID | literal	--}--subject :: NTParser RDFLabel-subject = urirefLbl <|> nodeID--predicate :: NTParser RDFLabel-predicate = urirefLbl--object :: NTParser RDFLabel-object = urirefLbl <|> nodeID <|> literal--{--uriref	::=	'<' absoluteURI '>'	-absoluteURI	::=	character+ with escapes as defined in section URI References	---}--uriref :: NTParser ScopedName-uriref = do-  -- not ideal, as want to reject invalid characters immediately rather than via parseURI-  ustr <- L.unpack <$> bracket (char '<') (char '>') (many1Satisfy (/= '>'))-  -- ustr <- bracket (char '<') (char '>') $ many1 character -- looks like need to exclude > from character-  -- ustr <- char '<' *> manyTill character (char '>')-  -  maybe (failBad ("Invalid URI: <" ++ ustr ++ ">"))-    (return . makeURIScopedName)-    (parseURI ustr)--urirefLbl :: NTParser RDFLabel-urirefLbl = Res <$> uriref--{--nodeID	::=	'_:' name	--}--nodeID :: NTParser RDFLabel-nodeID = Blank <$> (string "_:" *> nameStr)--{-  -literal	::=	langString | datatypeString	-langString	::=	'"' string '"' ( '@' language )?	-datatypeString	::=	'"' string '"' '^^' uriref	-language	::=	[a-z]+ ('-' [a-z0-9]+ )*-encoding a language tag.	-string	::=	character* with escapes as defined in section Strings	---}--literal :: NTParser RDFLabel-literal = Lit <$> (T.pack <$> ntstring) <*> optional dtlang--ntstring :: NTParser String-ntstring = bracket (char '"') (char '"') (many character)--dtlang :: NTParser ScopedName-dtlang = -    (char '@' *> language)-    <|> (string "^^" *> uriref)--language :: NTParser ScopedName-language = do-  h <- many1Satisfy isaz-  mt <- optional ( L.cons <$> char '-' <*> many1Satisfy (\c -> isaz c || is09 c) )-  return $ langName $ L.toStrict $ L.append h $ fromMaybe L.empty mt--{--String handling: --EBNF has:--character	::=	[#x20-#x7E] /* US-ASCII space to decimal 126 */	--Additional information from:--  http://www.w3.org/TR/rdf-testcases/#ntrip_strings--N-Triples strings are sequences of US-ASCII character productions encoding [UNICODE] character strings. The characters outside the US-ASCII range and some other specific characters are made available by \-escape sequences as follows:-- Unicode character- (with code point u)	N-Triples encoding- [#x0-#x8]	\uHHHH- 4 required hexadecimal digits HHHH encoding Unicode character u- #x9	\t- #xA	\n- [#xB-#xC]	\uHHHH- 4 required hexadecimal digits HHHH encoding Unicode character u- #xD	\r- [#xE-#x1F]	\uHHHH- 4 required hexadecimal digits HHHH encoding Unicode character u- [#x20-#x21]	the character u- #x22	\"- [#x23-#x5B]	the character u- #x5C	\\- [#x5D-#x7E]	the character u- [#x7F-#xFFFF]	\uHHHH- 4 required hexadecimal digits HHHH encoding Unicode character u- [#10000-#x10FFFF]	\UHHHHHHHH- 8 required hexadecimal digits HHHHHHHH encoding Unicode character u- where H is a hexadecimal digit: [#x30-#x39],[#x41-#x46] (0-9, uppercase A-F).--This escaping satisfies the [CHARMOD] section Reference Processing Model on making the full Unicode character range U+0 to U+10FFFF available to applications and providing only one way to escape any character.---}---- 0x22 is " and 0x5c is \--isAsciiChar :: Char -> Bool-isAsciiChar c = let i = ord c-                in i >= 0x20 && i <= 0x21 ||-                   i >= 0x23 && i <= 0x5b ||-                   i >= 0x5d && i <= 0x7e--protectedChar :: NTParser Char-protectedChar =-  (char 't' *> return '\t')-  <|> (char 'n' *> return '\n')-  <|> (char 'r' *> return '\r')-  <|> (char '"' *> return '"')-  <|> (char '\\' *> return '\\')-  <|> (char 'u' *> hex4)-  <|> (char 'U' *> hex8)--character :: NTParser Char-character = -  (char '\\' *> protectedChar)-  <|> satisfy isAsciiChar----------------------------------------------------------------------------------------  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  All rights reserved.------  This file is part of Swish.------  Swish is free software; you can redistribute it and/or modify---  it under the terms of the GNU General Public License as published by---  the Free Software Foundation; either version 2 of the License, or---  (at your option) any later version.------  Swish is distributed in the hope that it will be useful,---  but WITHOUT ANY WARRANTY; without even the implied warranty of---  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the---  GNU General Public License for more details.------  You should have received a copy of the GNU General Public License---  along with Swish; if not, write to:---    The Free Software Foundation, Inc.,---    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA------------------------------------------------------------------------------------
+ src/Swish/RDF/Parser/N3.hs view
@@ -0,0 +1,1190 @@+{-# LANGUAGE OverloadedStrings #-}++--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  N3+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke+--  License     :  GPL V2+--+--  Maintainer  :  Douglas Burke+--  Stability   :  experimental+--  Portability :  OverloadedStrings+--+--  This Module implements a Notation 3 parser, returning a+--  new 'RDFGraph' consisting of triples and namespace information parsed from+--  the supplied N3 input string, or an error indication.+--+-- REFERENCES:+--+-- - \"Notation3 (N3): A readable RDF syntax\",+--      W3C Team Submission 14 January 2008,+--      <http://www.w3.org/TeamSubmission/2008/SUBM-n3-20080114/>+--+-- - Tim Berners-Lee's design issues series notes and description,+--      <http://www.w3.org/DesignIssues/Notation3.html>+--+-- - Notation 3 Primer by Sean Palmer,+--      <http://www.w3.org/2000/10/swap/Primer.html>+--    +-- NOTES:+--+--  UTF-8 handling is not really tested.+--+--  No performance testing has been applied.+--+--  Not all N3 grammar elements are supported, including:+--+--    - @\@forSome@ (we read it in but ignore the arguments)+--+--    - @\@forAll@  (this causes a parse error)+--+--    - formulae are lightly tested+--+--    - string support is incomplete (e.g. unrecognized escape characters+--      such as @\\q@ are probably handled incorrectly)+--+--------------------------------------------------------------------------------++module Swish.RDF.Parser.N3+    ( ParseResult+    , parseN3      +    , parseN3fromText      +    , parseAnyfromText+    , parseTextFromText, parseAltFromText+    , parseNameFromText -- , parsePrefixFromText+    , parseAbsURIrefFromText, parseLexURIrefFromText, parseURIref2FromText+    +    -- * Exports for parsers that embed Notation3 in a bigger syntax+    , N3Parser, N3State(..), SpecialMap+    +    , getPrefix -- a combination of the old defaultPrefix and namedPrefix productions+    , n3symbol -- replacement for uriRef2 -- TODO: check this is semantically correct      +    , quickVariable -- was varid      +    , lexUriRef       +    , document, subgraph                                                   +    , newBlankNode+    )+where++import Swish.GraphClass (arc)+import Swish.Namespace+    ( Namespace+    , ScopedName+    , makeNamespace+    , getScopeNamespace+    , getScopedNameURI+    , getScopeNamespace+    , makeURIScopedName+    , makeQNameScopedName+    , makeNSScopedName+    , nullScopedName+    )+import Swish.QName (QName, newLName)++import Swish.RDF.Graph+    ( RDFGraph, RDFLabel(..)+    , ToRDFLabel(..)+    , NamespaceMap+    , LookupFormula(..) +    , addArc +    , setFormula+    , setNamespaces+    , emptyRDFGraph+    )++import Swish.RDF.Datatype (makeDatatypedLiteral)++import Swish.RDF.Vocabulary+    ( LanguageTag+    , toLangTag+    , rdfType+    , rdfFirst, rdfRest, rdfNil+    , owlSameAs, logImplies+    , xsdBoolean, xsdInteger, xsdDecimal, xsdDouble+    )++import Swish.RDF.Parser.Utils+    ( SpecialMap+    , ParseResult+    , runParserWithError+    -- , mapPrefix+    , prefixTable+    , specialTable+    , ignore+    , notFollowedBy+    , endBy+    , sepEndBy+    , manyTill+    , noneOf+    , char+    , ichar+    , string+    , stringT+    , symbol+    , lexeme+    , whiteSpace+    , hex4  +    , hex8  +    , appendURIs+    )++import Control.Applicative+import Control.Monad (forM_, foldM)++import Data.Char (isSpace, isDigit, ord, isAsciiLower) +import Data.LookupMap (LookupMap(..), LookupEntryClass(..))+import Data.LookupMap (mapFind, mapFindMaybe, mapAdd, mapReplace)+import Data.Maybe (fromMaybe, fromJust)+import Data.Word (Word32)++import Network.URI (URI(..), parseURIReference)++import Text.ParserCombinators.Poly.StateText++import qualified Data.Text as T+import qualified Data.Text.Lazy as L++----------------------------------------------------------------------+-- Define parser state and helper functions+----------------------------------------------------------------------++-- | N3 parser state+data N3State = N3State+        { graphState :: RDFGraph            -- Graph under construction+        , thisNode   :: RDFLabel            -- current context node (aka 'this')+        , prefixUris :: NamespaceMap        -- namespace prefix mapping table+        , syntaxUris :: SpecialMap          -- special name mapping table+        , nodeGen    :: Word32              -- blank node id generator+        , keywordsList :: [T.Text]          -- contents of the @keywords statement+        , allowLocalNames :: Bool           -- True if @keywords used so that bare names are QNames in default namespace+        }++-- | Functions to update N3State vector (use with stUpdate)++setPrefix :: Maybe T.Text -> URI -> N3State -> N3State+setPrefix pre uri st =  st { prefixUris=p' }+    where+        p' = mapReplace (prefixUris st) (makeNamespace pre uri) ++-- | Set name for special syntax element+setSName :: String -> ScopedName -> N3State -> N3State+setSName nam snam st =  st { syntaxUris=s' }+    where+        s' = mapReplace (syntaxUris st) (nam,snam)++setSUri :: String -> URI -> N3State -> N3State+setSUri nam = setSName nam . makeURIScopedName++-- | Set the list of tokens that can be used without needing the leading +-- \@ symbol.+setKeywordsList :: [T.Text] -> N3State -> N3State+setKeywordsList ks st = st { keywordsList = ks, allowLocalNames = True }++--  Functions to access state:++-- | Get name for special syntax element, default null+getSName :: N3State -> String -> ScopedName+getSName st nam =  mapFind nullScopedName nam (syntaxUris st)++getSUri :: N3State -> String -> URI+getSUri st nam = getScopedNameURI $ getSName st nam++--  Map prefix to URI+getPrefixURI :: N3State -> Maybe T.Text -> Maybe URI+getPrefixURI st pre = mapFindMaybe pre (prefixUris st)++getKeywordsList :: N3State -> [T.Text]+getKeywordsList = keywordsList++getAllowLocalNames :: N3State -> Bool+getAllowLocalNames = allowLocalNames++--  Return function to update graph in N3 parser state,+--  using the supplied function of a graph+--+updateGraph :: (RDFGraph -> RDFGraph) -> N3State -> N3State+updateGraph f s = s { graphState = f (graphState s) }++----------------------------------------------------------------------+--  Define top-level parser function:+--  accepts a string and returns a graph or error+----------------------------------------------------------------------++-- | The N3 parser.+type N3Parser a = Parser N3State a++-- | Parse a string as N3 (with no real base URI).+-- +-- See 'parseN3' if you need to provide a base URI.+--+parseN3fromText ::+  L.Text -- ^ input in N3 format.+  -> ParseResult+parseN3fromText = flip parseN3 Nothing++-- | Parse a string with an optional base URI.+--            +-- See also 'parseN3fromString'.            +--+parseN3 ::+  L.Text -- ^ input in N3 format.+  -> Maybe QName -- ^ optional base URI+  -> ParseResult+parseN3 txt mbase = parseAnyfromText document mbase txt++{-+-- useful for testing+test :: String -> RDFGraph+test = either error id . parseAnyfromString document Nothing+-}++hashURI :: URI+hashURI = fromJust $ parseURIReference "#"++emptyState :: +  Maybe QName  -- ^ starting base for the graph+  -> N3State+emptyState mbase = +  let pmap   = LookupMap [makeNamespace Nothing hashURI]+      muri   = fmap (makeQNameScopedName Nothing) mbase+      smap   = LookupMap $ specialTable muri+  in N3State+     { graphState = emptyRDFGraph+     , thisNode   = NoNode+     , prefixUris = pmap+     , syntaxUris = smap+     , nodeGen    = 0+     , keywordsList = ["a", "is", "of", "true", "false"] -- not 100% sure about true/false here+     , allowLocalNames = False+     }+++-- TODO: change from QName to URI for the base?++-- | Function to supply initial context and parse supplied term.+--+parseAnyfromText :: N3Parser a      -- ^ parser to apply+                    -> Maybe QName  -- ^ base URI of the input, or @Nothing@ to use default base value+                    -> L.Text       -- ^ input to be parsed+                    -> Either String a+parseAnyfromText parser mbase = runParserWithError parser (emptyState mbase)++-- | Create a new blank node.+newBlankNode :: N3Parser RDFLabel+newBlankNode = do+  n <- stQuery (succ . nodeGen)+  stUpdate $ \s -> s { nodeGen = n }+  return $ Blank (show n)+  +--  Test functions for selected element parsing++-- TODO: remove these+  +-- | Used in testing.+parseTextFromText :: String -> L.Text -> Either String String+parseTextFromText s =+    parseAnyfromText (string s) Nothing++-- | Used in testing.+parseAltFromText :: String -> String -> L.Text -> Either String String+parseAltFromText s1 s2 =+    parseAnyfromText (string s1 <|> string s2) Nothing++-- | Used in testing.+parseNameFromText :: L.Text -> Either String String+parseNameFromText =+    parseAnyfromText n3NameStr Nothing++{-+This has been made tricky by the attempt to remove the default list+of prefixes from the starting point of a N3 parse and the subsequent+attempt to add every new namespace we come across to the parser state.++So we add in the original default namespaces for testing, since+this routine is really for testing.+-}++addTestPrefixes :: N3Parser ()+addTestPrefixes = stUpdate $ \st -> st { prefixUris = LookupMap prefixTable } -- should append to existing map++{-+parsePrefixFromText :: L.Text -> Either String URI+parsePrefixFromText =+    parseAnyfromText p Nothing+      where+        p = do+          addTestPrefixes+          pref <- n3Name+          st   <- stGet+          case getPrefixURI st (Just pref) of+            Just uri -> return uri+            _ -> fail $ "Undefined prefix: '" ++ pref ++ "'"+-}++-- | Used in testing.+parseAbsURIrefFromText :: L.Text -> Either String URI+parseAbsURIrefFromText =+    parseAnyfromText explicitURI Nothing++-- | Used in testing.+parseLexURIrefFromText :: L.Text -> Either String URI+parseLexURIrefFromText =+    parseAnyfromText lexUriRef Nothing++-- | Used in testing.+parseURIref2FromText :: L.Text -> Either String ScopedName+parseURIref2FromText = +    parseAnyfromText (addTestPrefixes *> n3symbol) Nothing++----------------------------------------------------------------------+--  Syntax productions+----------------------------------------------------------------------++-- helper routines++comma, semiColon , fullStop :: N3Parser ()+comma = ignore $ symbol ","+semiColon = ignore $ symbol ";"+fullStop = ignore $ symbol "."++-- a specialization of bracket/between +br :: String -> String -> N3Parser a -> N3Parser a+br lsym rsym = bracket (symbol lsym) (symbol rsym)++-- to make porting from parsec to polyparse easier+between :: Parser s lbr -> Parser s rbr -> Parser s a -> Parser s a+between = bracket++-- The @ character is optional if the keyword is in the+-- keyword list+--+atSign :: T.Text -> N3Parser ()+atSign s = do+  st <- stGet+  +  let p = ichar '@'+  +  if s `elem` getKeywordsList st+    then ignore $ optional p+    else p+         +atWord :: T.Text -> N3Parser T.Text+atWord s = do+  atSign s+  +  -- TODO: does it really make sense to add the not-followed-by-a-colon rule here?+  -- apply to both cases even though should only really be necessary+  -- when the at sign is not given+  --+  lexeme $ stringT s *> notFollowedBy (== ':')+  return s++{-+Since operatorLabel can be used to add a label with an +unknown namespace, we need to ensure that the namespace+is added if not known. If the namespace prefix is already+in use then it is over-written (rather than add a new+prefix for the label).++TODO:+  - could we use the reverse lookupmap functionality to+    find if the given namespace URI is in the namespace+    list? If it is, use it's key otherwise do a+    mapReplace for the input namespace.+    +-}+operatorLabel :: ScopedName -> N3Parser RDFLabel+operatorLabel snam = do+  st <- stGet+  let sns = getScopeNamespace snam+      opmap = prefixUris st+      pkey = entryKey sns+      pval = entryVal sns+      +      rval = Res snam+      +  -- the lookup and the replacement could be fused+  case mapFindMaybe pkey opmap of+    Just val | val == pval -> return rval+             | otherwise   -> do+               stUpdate $ \s -> s { prefixUris = mapReplace opmap sns }+               return rval+    +    _ -> do+      stUpdate $ \s -> s { prefixUris = mapAdd opmap sns }+      return rval+        +{-+Add statement to graph in N3 parser state.++To support literals that are written directly/implicitly - i.e.  as+true/false/1/1.0/1.0e23 - rather than a string with an explicit+datatype we need to special case handling of the object label for+literals. Is this actually needed? The N3 Formatter now doesn't+display the xsd: datatypes on output, but there may be issues with+other formats (e.g RDF/XML once it is supported).++-}++type AddStatement = RDFLabel -> N3Parser ()++addStatement :: RDFLabel -> RDFLabel -> AddStatement+addStatement s p o@(TypedLit _ dtype) | dtype `elem` [xsdBoolean, xsdInteger, xsdDecimal, xsdDouble] = do +  ost <- stGet+  let stmt = arc s p o+      oldp = prefixUris ost+      ogs = graphState ost+      newp = mapReplace oldp (getScopeNamespace dtype)+  stUpdate $ \st -> st { prefixUris = newp, graphState = addArc stmt ogs }+addStatement s p o = stUpdate (updateGraph (addArc (arc s p o) ))++addStatementRev :: RDFLabel -> RDFLabel -> AddStatement+addStatementRev o p s = addStatement s p o++{-+A number of productions require a name, which starts with++[A-Z_a-z#x00c0-#x00d6#x00d8-#x00f6#x00f8-#x02ff#x0370-#x037d#x037f-#x1fff#x200c-#x200d#x2070-#x218f#x2c00-#x2fef#x3001-#xd7ff#xf900-#xfdcf#xfdf0-#xfffd#x00010000-#x000effff]++and then has++[\-0-9A-Z_a-z#x00b7#x00c0-#x00d6#x00d8-#x00f6#x00f8-#x037d#x037f-#x1fff#x200c-#x200d#x203f-#x2040#x2070-#x218f#x2c00-#x2fef#x3001-#xd7ff#xf900-#xfdcf#xfdf0-#xfffd#x00010000-#x000effff]*++we encode this as the n3Name production+-}++isaz, is09, isaz09 :: Char -> Bool+isaz = isAsciiLower+is09 = isDigit+isaz09 c = isaz c || is09 c++match :: (Ord a) => a -> [(a,a)] -> Bool+match v = any (\(l,h) -> v >= l && v <= h)++startChar :: Char -> Bool+startChar c = let i = ord c+              in c == '_' || +                 match c [('A', 'Z'), ('a', 'z')] ||+                 match i [(0x00c0, 0x00d6), (0x00d8, 0x00f6), (0x00f8, 0x02ff), +                          (0x0370, 0x037d), +                          (0x037f, 0x1fff), (0x200c, 0x200d), +                          (0x2070, 0x218f), (0x2c00, 0x2fef), (0x3001, 0xd7ff), +                          (0xf900, 0xfdcf), (0xfdf0, 0xfffd), +                          (0x00010000, 0x000effff)]           +  +inBody :: Char -> Bool+inBody c = let i = ord c+           in c `elem` "-_" || i == 0x007 ||+              match c [('0', '9'), ('A', 'Z'), ('a', 'z')] ||+              match i [(0x00c0, 0x00d6), (0x00d8, 0x00f6), (0x00f8, 0x037d), +                       (0x037f, 0x1fff), (0x200c, 0x200d), (0x203f, 0x2040), +                       (0x2070, 0x218f), (0x2c00, 0x2fef), (0x3001, 0xd7ff), +                       (0xf900, 0xfdcf), (0xfdf0, 0xfffd), +                       (0x00010000, 0x000effff)]           ++-- should this be strict or lazy text?+n3Name :: N3Parser T.Text+n3Name = T.cons <$> n3Init <*> n3Body+  where+    n3Init = satisfy startChar+    n3Body = L.toStrict <$> manySatisfy inBody+++n3NameStr :: N3Parser String+n3NameStr = T.unpack <$> n3Name++{-+quickvariable ::=	\?[A-Z_a-z#x00c0-#x00d6#x00d8-#x00f6#x00f8-#x02ff#x0370-#x037d#x037f-#x1fff#x200c-#x200d#x2070-#x218f#x2c00-#x2fef#x3001-#xd7ff#xf900-#xfdcf#xfdf0-#xfffd#x00010000-#x000effff][\-0-9A-Z_a-z#x00b7#x00c0-#x00d6#x00d8-#x00f6#x00f8-#x037d#x037f-#x1fff#x200c-#x200d#x203f-#x2040#x2070-#x218f#x2c00-#x2fef#x3001-#xd7ff#xf900-#xfdcf#xfdf0-#xfffd#x00010000-#x000effff]*+-}++-- TODO: is mapping to Var correct?+-- | Match @?<variable name>@.+quickVariable :: N3Parser RDFLabel+quickVariable = char '?' *> (Var <$> n3NameStr) ++{-+string ::=	("""[^"\\]*(?:(?:\\.|"(?!""))[^"\\]*)*""")|("[^"\\]*(?:\\.[^"\\]*)*")++or++string ::= tripleQuoted | singleQUoted++-}++n3string :: N3Parser T.Text+n3string = tripleQuoted <|> singleQuoted ++{-+singleQuoted ::=  "[^"\\]*(?:\\.[^"\\]*)*"++asciiChars :: String+asciiChars = map chr [0x20..0x7e]++asciiCharsN3 :: String+asciiCharsN3 = filter (`notElem` "\\\"") asciiChars++-}++digit :: N3Parser Char+digit = satisfy isDigit++{-+This is very similar to NTriples accept that also allow the escaping of '+even though it is not required.++The Python rules allow \N{name}, where name is the Unicode name. It's+not clear whether we need to support this too, so for now we do not.++-}+protectedChar :: N3Parser Char+protectedChar =+  (char 't' *> return '\t')+  <|> (char 'n' *> return '\n')+  <|> (char 'r' *> return '\r')+  <|> (char '"' *> return '"')+  <|> (char '\'' *> return '\'')+  <|> (char '\\' *> return '\\')+  <|> (char 'u' *> hex4)+  <|> (char 'U' *> hex8)++-- Accept an escape character or any character as long as it isn't+-- a new-line or quote. Unrecognized escape sequences should therefore+-- be left alone by this. +--+n3Character :: N3Parser Char+n3Character = +  (char '\\' *> (protectedChar <|> return '\\'))+  <|> noneOf "\"\n"+      +{-+      <|> (oneOf asciiCharsN3 <?> "ASCII character")+              -- TODO: bodyChar and asciiCharsN3 overlap+      <|> (oneOf bodyChar <?> "Unicode character")+-}              ++sQuot :: N3Parser Char+sQuot = char '"'++{-+TODO: there must be a better way of building up the Text+-}++singleQuoted :: N3Parser T.Text+singleQuoted = fmap T.pack (bracket sQuot sQuot $ many n3Character)+    +{-+tripleQUoted ::=	"""[^"\\]*(?:(?:\\.|"(?!""))[^"\\]*)*"""+-}+tripleQuoted :: N3Parser T.Text+tripleQuoted = tQuot *> fmap T.pack (manyTill (n3Character <|> sQuot <|> char '\n') tQuot)+  where+    -- tQuot = try (count 3 sQuot)+    tQuot = exactly 3 sQuot++getDefaultPrefix :: N3Parser Namespace+getDefaultPrefix = do+  s <- stGet+  case getPrefixURI s Nothing of+    Just uri -> return $ makeNamespace Nothing uri+    _ -> fail "No default prefix defined; how unexpected!"++addBase :: URI -> N3Parser ()+addBase = stUpdate . setSUri "base" ++addPrefix :: Maybe T.Text -> URI -> N3Parser ()+addPrefix p = stUpdate . setPrefix p ++{-|+Update the set of keywords that can be given without+an \@ sign.+-}+updateKeywordsList :: [T.Text] -> N3Parser ()+updateKeywordsList = stUpdate . setKeywordsList++{-+document ::=		|	statements_optional EOF+-}++-- | Process a N3 document, returning a graph.+document :: N3Parser RDFGraph+document = mkGr <$> (whiteSpace *> statementsOptional *> eof *> stGet)+  where+    mkGr s = setNamespaces (prefixUris s) (graphState s)++{-+statements_optional ::=		|	statement  "."  statements_optional+		|	void++-}++statementsOptional :: N3Parser ()+statementsOptional = ignore $ endBy (lexeme statement) fullStop+    +{-+statement ::=		|	declaration+		|	existential+		|	simpleStatement+		|	universal++-}++statement :: N3Parser ()+statement =+  declaration+  <|> existential+  <|> universal+  <|> simpleStatement+  -- having an error here leads to less informative errors in general, it seems+  -- <?> "statement (existential or universal quantification or a simple statement)"+  +{-+declaration ::=		|	 "@base"  explicituri+		|	 "@keywords"  barename_csl+		|	 "@prefix"  prefix explicituri+-}++-- TODO: do we need the try statements here? atWord would need to have a try on '@'+-- (if applicable) which should mean being able to get rid of try+--+declaration :: N3Parser ()+declaration = oneOf [+  atWord "base" >> explicitURI >>= addBase,+  atWord "keywords" >> bareNameCsl >>= updateKeywordsList,+  atWord "prefix" *> getPrefix+  ]++  {-+  (try (atWord "base") >> explicitURI >>= addBase)+  <|>+  (try (atWord "keywords") >> bareNameCsl >>= updateKeywordsList)+  <|>+  (try (atWord "prefix") *> getPrefix)+  -}++-- | Process the remainder of an @\@prefix@ line (after this+-- has been processed). The prefix value and URI are added to the parser+-- state.+getPrefix :: N3Parser ()  +getPrefix = do+  p <- lexeme prefix+  u <- explicitURI+  addPrefix p u++{-+explicituri ::=	<[^>]*>++Note: white space is to be ignored within <>+-}++explicitURI :: N3Parser URI+explicitURI = do+  let lb = char '<'+      rb = char '>'+  +  -- TODO: do the whitespace definitions match?+  ustr <- between lb rb $ many (satisfy (/= '>'))+  let uclean = filter (not . isSpace) ustr+  +  case parseURIReference uclean of+    Nothing -> fail $ "Unable to convert <" ++ uclean ++ "> to a URI"+    Just uref -> do+      s <- stGet+      let base = getSUri s "base"+      either fail return $ appendURIs base uref+      +-- production from the old parser; used in SwishScript+-- | An explicitly given URI followed by white space.+lexUriRef :: N3Parser URI+lexUriRef = lexeme explicitURI++{-+barename ::=	[A-Z_a-z#x00c0-#x00d6#x00d8-#x00f6#x00f8-#x02ff#x0370-#x037d#x037f-#x1fff#x200c-#x200d#x2070-#x218f#x2c00-#x2fef#x3001-#xd7ff#xf900-#xfdcf#xfdf0-#xfffd#x00010000-#x000effff][\-0-9A-Z_a-z#x00b7#x00c0-#x00d6#x00d8-#x00f6#x00f8-#x037d#x037f-#x1fff#x200c-#x200d#x203f-#x2040#x2070-#x218f#x2c00-#x2fef#x3001-#xd7ff#xf900-#xfdcf#xfdf0-#xfffd#x00010000-#x000effff]*+barename_csl ::=		|	barename barename_csl_tail+		|	void+barename_csl_tail ::=		|	 ","  barename barename_csl_tail+		|	void+-}++bareNameCsl :: N3Parser [T.Text]+bareNameCsl = sepBy (lexeme bareName) comma++bareName :: N3Parser T.Text+bareName = n3Name ++{-+prefix ::=	([A-Z_a-z#x00c0-#x00d6#x00d8-#x00f6#x00f8-#x02ff#x0370-#x037d#x037f-#x1fff#x200c-#x200d#x2070-#x218f#x2c00-#x2fef#x3001-#xd7ff#xf900-#xfdcf#xfdf0-#xfffd#x00010000-#x000effff][\-0-9A-Z_a-z#x00b7#x00c0-#x00d6#x00d8-#x00f6#x00f8-#x037d#x037f-#x1fff#x200c-#x200d#x203f-#x2040#x2070-#x218f#x2c00-#x2fef#x3001-#xd7ff#xf900-#xfdcf#xfdf0-#xfffd#x00010000-#x000effff]*)?:+-}++prefix :: N3Parser (Maybe T.Text)+prefix = optional (lexeme n3Name) <* char ':'+         ++{-+symbol ::=		|	explicituri+		|	qname+symbol_csl ::=		|	symbol symbol_csl_tail+		|	void+symbol_csl_tail ::=		|	 ","  symbol symbol_csl_tail+		|	void++-}++-- | Match a N3 symbol (an explicit URI or a QName)+-- and convert it to a 'ScopedName'.+n3symbol :: N3Parser ScopedName+n3symbol = +  (makeURIScopedName <$> explicitURI)+  <|> qname++symbolCsl :: N3Parser [ScopedName]+symbolCsl = sepBy (lexeme n3symbol) comma++{-+qname ::=	(([A-Z_a-z#x00c0-#x00d6#x00d8-#x00f6#x00f8-#x02ff#x0370-#x037d#x037f-#x1fff#x200c-#x200d#x2070-#x218f#x2c00-#x2fef#x3001-#xd7ff#xf900-#xfdcf#xfdf0-#xfffd#x00010000-#x000effff][\-0-9A-Z_a-z#x00b7#x00c0-#x00d6#x00d8-#x00f6#x00f8-#x037d#x037f-#x1fff#x200c-#x200d#x203f-#x2040#x2070-#x218f#x2c00-#x2fef#x3001-#xd7ff#xf900-#xfdcf#xfdf0-#xfffd#x00010000-#x000effff]*)?:)?[A-Z_a-z#x00c0-#x00d6#x00d8-#x00f6#x00f8-#x02ff#x0370-#x037d#x037f-#x1fff#x200c-#x200d#x2070-#x218f#x2c00-#x2fef#x3001-#xd7ff#xf900-#xfdcf#xfdf0-#xfffd#x00010000-#x000effff][\-0-9A-Z_a-z#x00b7#x00c0-#x00d6#x00d8-#x00f6#x00f8-#x037d#x037f-#x1fff#x200c-#x200d#x203f-#x2040#x2070-#x218f#x2c00-#x2fef#x3001-#xd7ff#xf900-#xfdcf#xfdf0-#xfffd#x00010000-#x000effff]*++TODO:+  Note that, for now, we explicitly handle blank nodes+  (of the form _:name) direcly in pathItem'.+  This is not a good idea since qname' is used elsewhere+  and so shouldn't we do the same thing there too?+-}++-- for now assume that the parsing rule for the local part+-- will not create an invalid LName.+toName :: Namespace -> T.Text -> ScopedName+toName ns l = +    case newLName l of+      Just local -> makeNSScopedName ns local+      _ -> error $ "Invalid local name: " ++ T.unpack l++qname :: N3Parser ScopedName+qname = qname1 <|> qname2++qname1 :: N3Parser ScopedName+qname1 = fmap (uncurry toName) (char ':' >> g)+    where+      g = (,) <$> getDefaultPrefix <*> (n3Name <|> return "")+               +qname2 :: N3Parser ScopedName+qname2 = n3Name >>= fullOrLocalQName++fullOrLocalQName :: T.Text -> N3Parser ScopedName+fullOrLocalQName name = +  (char ':' *> fullQName name)+  <|> localQName name+  +fullQName :: T.Text -> N3Parser ScopedName+fullQName name = toName <$> findPrefix name <*> (n3Name <|> pure "")+  +findPrefix :: T.Text -> N3Parser Namespace+findPrefix pre = do+  st <- stGet+  case mapFindMaybe (Just pre) (prefixUris st) of+    Just uri -> return $ makeNamespace (Just pre) uri+    Nothing  -> failBad $ "Prefix '" ++ T.unpack pre ++ ":' not bound."+  +localQName :: T.Text -> N3Parser ScopedName+localQName name = do+  st <- stGet+  if getAllowLocalNames st+    then let g = (,) <$> getDefaultPrefix <*> pure name+         in uncurry toName <$> g+            +    else fail ("Invalid 'bare' word: " ++ T.unpack name)-- TODO: not ideal error message; can we handle this case differently?++{-+existential ::=		|	 "@forSome"  symbol_csl++For now we just read in the symbols and ignore them,+since we do not mark blank nodes as existentially quantified+(we assume this is the case).++TODO: fix this?+-}++existential :: N3Parser ()+-- existential = try (atWord "forSome") *> symbolCsl >> return ()+existential = atWord "forSome" *> symbolCsl *> pure ()++{-+simpleStatement ::=		|	subject propertylist+-}++simpleStatement :: N3Parser ()+simpleStatement = subject >>= propertyListWith+  +{-+subject ::=		|	expression+-}++subject :: N3Parser RDFLabel+subject = lexeme expression++{-+expression ::=		|	pathitem pathtail+pathtail ::=		|	 "!"  expression+		|	 "^"  expression+		|	void++-}++expression :: N3Parser RDFLabel+expression = do+  i <- pathItem+  +  let backwardExpr = char '!' *> return addStatementRev +      forwardExpr  = char '^' *> return addStatement+  +  mpt <- optional+        ( (,) <$> lexeme (forwardExpr <|> backwardExpr) <*> lexeme expression )+  case mpt of+    Nothing -> return i +    Just (addFunc, pt) -> do+      bNode <- newBlankNode+      addFunc bNode pt i+      return bNode+  +{-+pathitem ::=		|	 "("  pathlist  ")" +		|	 "["  propertylist  "]" +		|	 "{"  formulacontent  "}" +		|	boolean+		|	literal+		|	numericliteral+		|	quickvariable+		|	symbol++pathlist ::=		|	expression pathlist+		|	void++Need to think about how to handle formulae, since need to know the context+of the call to know where to add them.++TOOD: may include direct support for blank nodes here,+namely convert _:stringval -> Blank stringval since although+this should be done by symbol the types don't seem to easily match+up (at first blush anyway)+-}++pathItem :: N3Parser RDFLabel+pathItem = +  br "(" ")" pathList+  <|> br "[" "]" propertyListBNode+  <|> br "{" "}" formulaContent+  -- <|> try boolean+  <|> boolean+  <|> literal+  <|> numericLiteral+  <|> quickVariable+  <|> Blank <$> (string "_:" *> n3NameStr) -- TODO a hack that needs fixing+  <|> Res <$> n3symbol+  +{-  +we create a blank node for the list and return it, whilst+adding the list contents to the graph+-}+pathList :: N3Parser RDFLabel+pathList = do+  cts <- many (lexeme expression)+  eNode <- operatorLabel rdfNil+  case cts of+    [] -> return eNode+      +    (c:cs) -> do+      sNode <- newBlankNode+      first <- operatorLabel rdfFirst+      addStatement sNode first c+      lNode <- foldM addElem sNode cs+      rest <- operatorLabel rdfRest+      addStatement lNode rest eNode+      return sNode++    where      +      addElem prevNode curElem = do+        bNode <- newBlankNode+        first <- operatorLabel rdfFirst+        rest <- operatorLabel rdfRest+        addStatement prevNode rest bNode+        addStatement bNode first curElem+        return bNode+        +{-+formulacontent ::=		|	statementlist++statementlist ::=		|	statement statementtail+		|	void+statementtail ::=		|	 "."  statementlist+		|	void+-}++restoreState :: N3State -> N3Parser N3State+restoreState origState = do+  oldState <- stGet+  stUpdate $ \_ -> origState { nodeGen = nodeGen oldState }+  return oldState++{-+We create a subgraph and assign it to a blank node, returning the+blank node. At present it is a combination of the subgraph and formula+productions from the origial parser.++TODO: is it correct?+-}+formulaContent :: N3Parser RDFLabel+formulaContent = do+  bNode <- newBlankNode+  pstate <- stGet+  stUpdate $ \st -> st { graphState = emptyRDFGraph, thisNode = bNode }+  statementList+  oldState <- restoreState pstate+  stUpdate $ updateGraph $ setFormula (Formula bNode (graphState oldState))+  return bNode++-- | Process a sub graph and assign it to the given label.  +subgraph :: RDFLabel -> N3Parser RDFGraph+subgraph this = do+  pstate <- stGet+  stUpdate $ \st -> st { graphState = emptyRDFGraph, thisNode = this }+  statementsOptional    -- parse statements of formula+  oldState <- restoreState pstate  +  return $ graphState oldState+  +statementList :: N3Parser ()+statementList = ignore $ sepEndBy (lexeme statement) fullStop++{-+boolean ::=		|	 "@false" +		|	 "@true" +-}++boolean :: N3Parser RDFLabel+boolean = makeDatatypedLiteral xsdBoolean <$> +          (atWord "false" <|> atWord "true")+          -- (try (atWord "false") <|> atWord "true")+           +{-+dtlang ::=		|	 "@"  langcode+		|	 "^^"  symbol+		|	void+literal ::=		|	string dtlang++langcode ::=	[a-z]+(-[a-z0-9]+)*++-}++literal :: N3Parser RDFLabel+literal = do+    lit <- n3string+    opt <- optional dtlang+    return $ case opt of+               Just (Left lcode)  -> LangLit lit lcode+               Just (Right dtype) -> TypedLit lit dtype+               _                  -> Lit lit+  +dtlang :: N3Parser (Either LanguageTag ScopedName)+dtlang = +  (char '@' *> (Left <$> langcode))+  <|> string "^^" *> (Right <$> n3symbol)++-- Note that toLangTag may fail since it does some extra+-- validation not done by the parser (mainly on the length of the+-- primary and secondary tags).+--+-- NOTE: This parser does not accept multiple secondary tags which RFC3066+-- does.+--+langcode :: N3Parser LanguageTag+langcode = do+    h <- many1Satisfy isaz+    mt <- optional (L.cons <$> char '-' <*> many1Satisfy isaz09)+    let lbl = L.toStrict $ L.append h $ fromMaybe L.empty mt+    case toLangTag lbl of+        Just lt -> return lt+        _ -> fail ("Invalid language tag: " ++ T.unpack lbl) -- should this be failBad?+    +{-+decimal ::=	[-+]?[0-9]+(\.[0-9]+)?+double ::=	[-+]?[0-9]+(\.[0-9]+)?([eE][-+]?[0-9]+)+integer ::=	[-+]?[0-9]++numericliteral ::=		|	decimal+		|	double+		|	integer++We actually support 1. for decimal values which isn't supported +by the above production.++TODO: we could convert via something like++  maybeRead value :: Double >>= Just . toRDFLabel++which would mean we store the canonical XSD value in the+label, but it is not useful for the xsd:decimal case+since we currently don't have a Haskell type that+goes with it.+-}++numericLiteral :: N3Parser RDFLabel+numericLiteral =+  -- -- try (makeDatatypedLiteral xsdDouble <$> n3double)+  -- try (d2s <$> n3double)+  -- <|> try (makeDatatypedLiteral xsdDecimal <$> n3decimal)+  d2s <$> n3double+  <|> makeDatatypedLiteral xsdDecimal . T.pack <$> n3decimal+  <|> makeDatatypedLiteral xsdInteger . T.pack <$> n3integer++n3sign :: N3Parser Char+n3sign = char '+' <|> char '-'++n3integer :: N3Parser String+n3integer = do+  ms <- optional n3sign+  ds <- many1 digit+  case ms of+    Just s -> return $ s : ds+    _ -> return ds++n3decimal :: N3Parser String+-- n3decimal = (++) <$> n3integer <*> ( (:) <$> char '.' <*> many1 digit )+n3decimal = (++) <$> n3integer <*> ( (:) <$> char '.' <*> many digit )+           +n3double :: N3Parser String+n3double = (++) <$> n3decimal <*> ( (:) <$> satisfy (`elem` "eE") <*> n3integer )++-- Convert a double, as returned by n3double, into it's+-- canonical XSD form. We assume that n3double returns+-- a syntactivally valid Double, so do not bother with reads here+--+d2s :: String -> RDFLabel+d2s s = toRDFLabel (read s :: Double)++{-+propertylist ::=		|	verb object objecttail propertylisttail+		|	void+propertylisttail ::=		|	 ";"  propertylist+		|	void++-}++-- it's probably important that bNode is created *after*+-- processing the plist (mainly for the assumptions made by+-- formatting the output as N3; e.g. list/sequence ordering)+--+propertyListBNode :: N3Parser RDFLabel+propertyListBNode = do+  plist <- sepEndBy ((,) <$> lexeme verb <*> objectList) semiColon+  bNode <- newBlankNode+  let addList ((addFunc,vrb),items) = mapM_ (addFunc bNode vrb) items+  forM_ plist addList+  return bNode++propertyListWith :: RDFLabel -> N3Parser ()+propertyListWith subj = +  let -- term = lexeme verb >>= objectListWith subj+      term = lexeme verb >>= \(addFunc, vrb) -> objectListWith (addFunc subj vrb)+  in ignore $ sepEndBy term semiColon+  +{-+object ::=		|	expression+objecttail ::=		|	 ","  object objecttail+		|	void++We change the production rule from objecttail to objectlist for lists of+objects (may change back).++-}++object :: N3Parser RDFLabel+object = lexeme expression++objectList :: N3Parser [RDFLabel]+objectList = sepBy1 object comma++objectWith :: AddStatement -> N3Parser ()+objectWith addFunc = object >>= addFunc ++objectListWith :: AddStatement -> N3Parser ()+objectListWith addFunc =+  ignore $ sepBy1 (objectWith addFunc) comma++{-+objectList1 :: N3Parser [RDFLabel]+objectList1 = sepBy1 object comma+-}++{-+verb ::=		|	 "<=" +		|	 "=" +		|	 "=>" +		|	 "@a" +		|	 "@has"  expression+		|	 "@is"  expression  "@of" +		|	expression++-}++verb :: N3Parser (RDFLabel -> RDFLabel -> AddStatement, RDFLabel)+verb = +  -- we check reverse first so that <= is tried before looking for a URI via expression rule+  (,) addStatementRev <$> verbReverse+  <|> (,) addStatement <$> verbForward++-- those verbs for which subject is on the right and object on the left+verbReverse :: N3Parser RDFLabel+verbReverse =+  string "<=" *> operatorLabel logImplies+  <|> between (atWord "is") (atWord "of") (lexeme expression)++{-+  try (string "<=") *> operatorLabel logImplies+  <|> between (try (atWord "is")) (atWord "of") (lexeme expression)+-}++-- those verbs with subject on the left and object on the right+verbForward :: N3Parser RDFLabel+verbForward =  +  -- (try (string "=>") *> operatorLabel logImplies)+  (string "=>" *> operatorLabel logImplies)+  <|> (string "=" *> operatorLabel owlSameAs)+  -- <|> (try (atWord "a") *> operatorLabel rdfType)+  <|> (atWord "a" *> operatorLabel rdfType)+  <|> (atWord "has" *> lexeme expression)+  <|> lexeme expression++{-+universal ::=		|	 "@forAll"  symbol_csl++TODO: what needs to be done to support universal quantification+-}+universal :: N3Parser ()+universal = +  -- try (atWord "forAll") *> +  atWord "forAll" *> +  failBad "universal (@forAll) currently unsupported." +  -- will be something like: *> symbolCsl++--------------------------------------------------------------------------------+--+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke+--  All rights reserved.+--+--  This file is part of Swish.+--+--  Swish is free software; you can redistribute it and/or modify+--  it under the terms of the GNU General Public License as published by+--  the Free Software Foundation; either version 2 of the License, or+--  (at your option) any later version.+--+--  Swish is distributed in the hope that it will be useful,+--  but WITHOUT ANY WARRANTY; without even the implied warranty of+--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+--  GNU General Public License for more details.+--+--  You should have received a copy of the GNU General Public License+--  along with Swish; if not, write to:+--    The Free Software Foundation, Inc.,+--    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+--+--------------------------------------------------------------------------------
+ src/Swish/RDF/Parser/NTriples.hs view
@@ -0,0 +1,410 @@+--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------++-- |+--  Module      :  NTriples+--  Copyright   :  (c) 2011, 2012 Douglas Burke+--  License     :  GPL V2+--+--  Maintainer  :  Douglas Burke+--  Stability   :  experimental+--  Portability :  H98+--+--  This Module implements a NTriples parser, returning a+--  new 'RDFGraph' consisting of triples and namespace information parsed from+--  the supplied NTriples input string, or an error indication.+--+-- REFERENCES:+--+--  - \"RDF Test Cases\",+--    W3C Recommendation 10 February 2004,+--    <http://www.w3.org/TR/rdf-testcases/#ntriples>+--+--------------------------------------------------------------------------------++module Swish.RDF.Parser.NTriples+    ( ParseResult+    , parseNT      +    )+where++import Swish.GraphClass (arc)+import Swish.Namespace (ScopedName, makeURIScopedName)++import Swish.RDF.Graph (RDFGraph, RDFLabel(..), addArc, emptyRDFGraph)+import Swish.RDF.Vocabulary (LanguageTag, toLangTag)++import Swish.RDF.Parser.Utils (ParseResult+    , runParserWithError+    , ignore+    , skipMany+    , noneOf+    , char+    , string+    , eoln+    , fullStop+    , hex4+    , hex8+    )+  +import Control.Applicative++import Network.URI (parseURI)++import qualified Data.Text as T+import qualified Data.Text.Lazy as L++import Data.Char (isAsciiLower, isAsciiUpper, isDigit, ord) +import Data.Maybe (fromMaybe)++import Text.ParserCombinators.Poly.StateText++----------------------------------------------------------------------+-- Define parser state and helper functions+----------------------------------------------------------------------++-- | NT parser state+data NTState = NTState+        { graphState :: RDFGraph            -- Graph under construction+        }++emptyState :: NTState+emptyState = NTState { graphState = emptyRDFGraph }+           +--  Return function to update graph in NT parser state,+--  using the supplied function of a graph. This is for use+--  with stUpdate.+--+updateGraph :: (RDFGraph -> RDFGraph) -> NTState -> NTState+updateGraph f s = s { graphState = f (graphState s) }++----------------------------------------------------------------------+--  Define top-level parser function:+--  accepts a string and returns a graph or error+----------------------------------------------------------------------++-- | Parser that carries around a NTState record.+type NTParser a = Parser NTState a++-- | Parse a string.+-- +parseNT ::+  L.Text -- ^ input in NTriples format.+  -> ParseResult+parseNT = parsefromText ntripleDoc++{-+-- useful for testing+test :: String -> RDFGraph+test = either error id . parseNT+-}++-- | Function to supply initial context and parse supplied term.+--+--  Used for debugging.+parsefromText :: +    NTParser a      -- ^ parser to apply+    -> L.Text       -- ^ input to be parsed+    -> Either String a+parsefromText parser = runParserWithError parser emptyState++-- helper routines++{-+lineFeed :: NTParser ()+lineFeed = ignore (char '\r')+-}++-- Add statement to graph in NT parser state++addStatement :: RDFLabel -> RDFLabel -> RDFLabel -> NTParser ()+addStatement s p o = stUpdate (updateGraph (addArc (arc s p o) ))++----------------------------------------------------------------------+--  Syntax productions+----------------------------------------------------------------------++{-++EBNF from the specification, using the notation from XML 1.0, second edition,+is included inline below.++We do not force ASCII 7-bit semantics here yet.++space	::=	#x20 /* US-ASCII space - decimal 32 */	+cr	::=	#xD /* US-ASCII carriage return - decimal 13 */	+lf	::=	#xA /* US-ASCII line feed - decimal 10 */	+tab	::=	#x9 /* US-ASCII horizontal tab - decimal 9 */	++The productions are kept as close as possible to the specification+for now.++-}++{-+ntripleDoc	::=	line*	+line	::=	ws* ( comment | triple )? eoln	++We relax the rule that the input must be empty or end with a new line.++ntripleDoc :: NTParser RDFGraph+ntripleDoc = graphState <$> (many line *> eof *> getState)++line :: NTParser ()+line = skipMany ws *> optional (comment <|> triple) *> eoln+-}++ntripleDoc :: NTParser RDFGraph+ntripleDoc = graphState <$> (sepBy line eoln *> optional eoln *> skipWS *> eof *> stGet)++line :: NTParser ()+line = skipWS *> ignore (optional (comment <|> triple))++{-+ws	::=	space | tab	++Could use whiteSpace rule here, but that would permit+constructs (e.g. comments) where we do not support them.+-}++isWS :: Char -> Bool+isWS = (`elem` " \t")++{-+ws :: NTParser ()+-- ws = ignore (char ' ' <|> tab)+ws = ignore $ satisfy isWS+-}+           +skipWS :: NTParser ()+skipWS = ignore $ manySatisfy isWS++skip1WS :: NTParser ()+skip1WS = ignore $ many1Satisfy isWS++{-+comment	::=	'#' ( character - ( cr | lf ) )*	+-}++comment :: NTParser ()+comment = char '#' *> skipMany (noneOf "\r\n")++{-+eoln	::=	cr | lf | cr lf	+-}++{-+name	::=	[A-Za-z][A-Za-z0-9]*	+-}++isaz, isAZ, is09 :: Char -> Bool+isaz = isAsciiLower+isAZ = isAsciiUpper+is09 = isDigit++isHeadChar, isBodyChar :: Char -> Bool+isHeadChar c = isaz c || isAZ c+isBodyChar c = isHeadChar c || is09 c++name :: NTParser L.Text+name = L.cons <$> satisfy isHeadChar <*> manySatisfy isBodyChar++nameStr :: NTParser String+nameStr = L.unpack <$> name++{-+triple	::=	subject ws+ predicate ws+ object ws* '.' ws*	++-}++triple :: NTParser ()+triple = +  {- tryin to be fancy but addStatement is a Parser not a pure function+  addStatement +  <$> (subject <* skip1WS)+  <*> (predicate <* skip1WS)+  <*> (object <* (skipWS *> fullStop *> skipWS))+  -}+  +  do+    s <- subject+    skip1WS+    p <- predicate+    skip1WS+    o <- object+    skipWS+    fullStop+    skipWS+    addStatement s p o++{-+subject	::=	uriref | nodeID	+predicate	::=	uriref	+object	::=	uriref | nodeID | literal	+-}++subject :: NTParser RDFLabel+subject = urirefLbl <|> nodeID++predicate :: NTParser RDFLabel+predicate = urirefLbl++object :: NTParser RDFLabel+object = urirefLbl <|> nodeID <|> literal++{-+uriref	::=	'<' absoluteURI '>'	+absoluteURI	::=	character+ with escapes as defined in section URI References	++-}++uriref :: NTParser ScopedName+uriref = do+  -- not ideal, as want to reject invalid characters immediately rather than via parseURI+  ustr <- L.unpack <$> bracket (char '<') (char '>') (many1Satisfy (/= '>'))+  -- ustr <- bracket (char '<') (char '>') $ many1 character -- looks like need to exclude > from character+  -- ustr <- char '<' *> manyTill character (char '>')+  +  maybe (failBad ("Invalid URI: <" ++ ustr ++ ">"))+    (return . makeURIScopedName)+    (parseURI ustr)++urirefLbl :: NTParser RDFLabel+urirefLbl = Res <$> uriref++{-+nodeID	::=	'_:' name	+-}++nodeID :: NTParser RDFLabel+nodeID = Blank <$> (string "_:" *> nameStr)++{-  +literal	::=	langString | datatypeString	+langString	::=	'"' string '"' ( '@' language )?	+datatypeString	::=	'"' string '"' '^^' uriref	+language	::=	[a-z]+ ('-' [a-z0-9]+ )*+encoding a language tag.	+string	::=	character* with escapes as defined in section Strings	++-}++literal :: NTParser RDFLabel+literal = do+    lit <- T.pack <$> ntstring+    opt <- optional dtlang+    return $ case opt of+               Just (Left lcode)  -> LangLit lit lcode+               Just (Right dtype) -> TypedLit lit dtype+               _                  -> Lit lit++ntstring :: NTParser String+ntstring = bracket (char '"') (char '"') (many character)++dtlang :: NTParser (Either LanguageTag ScopedName)+dtlang = +    (char '@' *> (Left <$> language))+    <|> (string "^^" *> (Right <$> uriref))++-- Note that toLangTag may fail since it does some extra+-- validation not done by the parser (mainly on the length of the+-- primary and secondary tags).+--+-- NOTE: This parser does not accept multiple secondary tags which RFC3066+-- does.+--+language :: NTParser LanguageTag+language = do+    h <- many1Satisfy isaz+    mt <- optional ( L.cons <$> char '-' <*> many1Satisfy (\c -> isaz c || is09 c) )+    let lbl = L.toStrict $ L.append h $ fromMaybe L.empty mt+    case toLangTag lbl of+        Just lt -> return lt+        _ -> fail ("Invalid language tag: " ++ T.unpack lbl) -- should this be failBad?++{-+String handling: ++EBNF has:++character	::=	[#x20-#x7E] /* US-ASCII space to decimal 126 */	++Additional information from:++  http://www.w3.org/TR/rdf-testcases/#ntrip_strings++N-Triples strings are sequences of US-ASCII character productions encoding [UNICODE] character strings. The characters outside the US-ASCII range and some other specific characters are made available by \-escape sequences as follows:++ Unicode character+ (with code point u)	N-Triples encoding+ [#x0-#x8]	\uHHHH+ 4 required hexadecimal digits HHHH encoding Unicode character u+ #x9	\t+ #xA	\n+ [#xB-#xC]	\uHHHH+ 4 required hexadecimal digits HHHH encoding Unicode character u+ #xD	\r+ [#xE-#x1F]	\uHHHH+ 4 required hexadecimal digits HHHH encoding Unicode character u+ [#x20-#x21]	the character u+ #x22	\"+ [#x23-#x5B]	the character u+ #x5C	\\+ [#x5D-#x7E]	the character u+ [#x7F-#xFFFF]	\uHHHH+ 4 required hexadecimal digits HHHH encoding Unicode character u+ [#10000-#x10FFFF]	\UHHHHHHHH+ 8 required hexadecimal digits HHHHHHHH encoding Unicode character u+ where H is a hexadecimal digit: [#x30-#x39],[#x41-#x46] (0-9, uppercase A-F).++This escaping satisfies the [CHARMOD] section Reference Processing Model on making the full Unicode character range U+0 to U+10FFFF available to applications and providing only one way to escape any character.++-}++-- 0x22 is " and 0x5c is \++isAsciiChar :: Char -> Bool+isAsciiChar c = let i = ord c+                in i >= 0x20 && i <= 0x21 ||+                   i >= 0x23 && i <= 0x5b ||+                   i >= 0x5d && i <= 0x7e++protectedChar :: NTParser Char+protectedChar =+  (char 't' *> return '\t')+  <|> (char 'n' *> return '\n')+  <|> (char 'r' *> return '\r')+  <|> (char '"' *> return '"')+  <|> (char '\\' *> return '\\')+  <|> (char 'u' *> hex4)+  <|> (char 'U' *> hex8)++character :: NTParser Char+character = +  (char '\\' *> protectedChar)+  <|> satisfy isAsciiChar++--------------------------------------------------------------------------------+--+--  Copyright (c) 2011, 2012 Douglas Burke+--  All rights reserved.+--+--  This file is part of Swish.+--+--  Swish is free software; you can redistribute it and/or modify+--  it under the terms of the GNU General Public License as published by+--  the Free Software Foundation; either version 2 of the License, or+--  (at your option) any later version.+--+--  Swish is distributed in the hope that it will be useful,+--  but WITHOUT ANY WARRANTY; without even the implied warranty of+--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+--  GNU General Public License for more details.+--+--  You should have received a copy of the GNU General Public License+--  along with Swish; if not, write to:+--    The Free Software Foundation, Inc.,+--    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+--+--------------------------------------------------------------------------------
+ src/Swish/RDF/Parser/Turtle.hs view
@@ -0,0 +1,1077 @@+{-# LANGUAGE OverloadedStrings #-} -- only used in 'fromMaybe "" mbase' line of parseN3++--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  Turtle+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke+--  License     :  GPL V2+--+--  Maintainer  :  Douglas Burke+--  Stability   :  experimental+--  Portability :  OverloadedStrings+--+--  This Module implements a Turtle parser, returning a+--  new 'RDFGraph' consisting of triples and namespace information parsed from+--  the supplied input string, or an error indication.+--+-- REFERENCES:+--+--  - \"Turtle, Terse RDF Triple Language\",+--    W3C Working Draft 09 August 2011 (<http://www.w3.org/TR/2011/WD-turtle-20110809/>),+--    <http://www.w3.org/TR/turtle/>+--+-- Notes:+--+-- At present there is a lot of overlap with the N3 Parser.+--+-- The parser needs to be updated to the latest working draft (10 July 2012,+-- <http://www.w3.org/TR/2012/WD-turtle-20120710/#sec-changelog>).+--+--------------------------------------------------------------------------------++module Swish.RDF.Parser.Turtle+    ( ParseResult+    , parseTurtle      +    , parseTurtlefromText      +    {-+    , parseAnyfromText+    , parseTextFromText, parseAltFromText+    , parseNameFromText -- , parsePrefixFromText+    , parseAbsURIrefFromText, parseLexURIrefFromText, parseURIref2FromText+    -}+      +      {-+    -- * Exports for parsers that embed Turtle in a bigger syntax+    , TurtleParser, TurtleState(..), SpecialMap+    +    , getPrefix -- a combination of the old defaultPrefix and namedPrefix productions+    , n3symbol -- replacement for uriRef2 -- TODO: check this is semantically correct      +    , quickVariable -- was varid      +    , lexUriRef       +    , document, subgraph                                                   +    , newBlankNode+       -}+    )+where++import Swish.GraphClass (arc)+import Swish.Namespace (Namespace, ScopedName)+import Swish.Namespace (makeNamespace, getScopeNamespace, getScopedNameURI+                       , getScopeNamespace, makeURIScopedName, makeNSScopedName)+import Swish.QName (newLName, emptyLName)++import Swish.RDF.Graph+    ( RDFGraph, RDFLabel(..)+    , ToRDFLabel(..)+    , NamespaceMap+    , addArc +    , setNamespaces+    , emptyRDFGraph+    )++import Swish.RDF.Vocabulary+    ( LanguageTag+    , toLangTag+    , rdfType+    , rdfFirst, rdfRest, rdfNil+    , xsdBoolean, xsdInteger, xsdDecimal, xsdDouble+    , defaultBase+    )++import Swish.RDF.Datatype (makeDatatypedLiteral)++import Swish.RDF.Parser.Utils+    ( ParseResult+    , runParserWithError+    , ignore+    , noneOf+    , char+    , ichar+    , string+    , stringT+    , symbol+    , isymbol+    , lexeme+    , whiteSpace+    , hex4  +    , hex8  +    , appendURIs+    )++import Control.Applicative+import Control.Monad (foldM)++import Data.Char (ord, isAsciiLower, isAsciiUpper, isDigit) +import Data.LookupMap (LookupMap(..), LookupEntryClass(..))+import Data.LookupMap (mapFindMaybe, mapAdd, mapReplace)+import Data.Maybe (fromMaybe, fromJust)+import Data.Word (Word32)++import Network.URI (URI(..), parseURIReference)++import Text.ParserCombinators.Poly.StateText++import qualified Data.Text as T+import qualified Data.Text.Lazy as L++----------------------------------------------------------------------+-- Define parser state and helper functions+----------------------------------------------------------------------++-- | Turtle parser state+data TurtleState = TurtleState+        { graphState :: RDFGraph            -- Graph under construction+        , prefixUris :: NamespaceMap        -- namespace prefix mapping table+        , baseUri    :: URI                 -- base URI+        , nodeGen    :: Word32              -- blank node id generator+        } deriving Show++-- | Functions to update TurtleState vector (use with stUpdate)++setPrefix :: Maybe T.Text -> URI -> TurtleState -> TurtleState+setPrefix pre uri st =  st { prefixUris=p' }+    where+        p' = mapReplace (prefixUris st) (makeNamespace pre uri) ++-- | Change the base+setBase :: URI -> TurtleState -> TurtleState+setBase buri st = st { baseUri = buri }++--  Functions to access state:++-- | Return the default prefix+getDefaultPrefix :: TurtleParser Namespace+getDefaultPrefix = do+  s <- stGet+  case getPrefixURI s Nothing of+    Just uri -> return $ makeNamespace Nothing uri+    _ -> failBad "No default prefix defined; how unexpected (probably a programming error)!"++--  Map prefix to URI (naming needs a scrub here)+getPrefixURI :: TurtleState -> Maybe T.Text -> Maybe URI+getPrefixURI st pre = mapFindMaybe pre (prefixUris st)++findPrefixNamespace :: Maybe L.Text -> TurtleParser Namespace+findPrefixNamespace (Just p) = findPrefix (L.toStrict p)+findPrefixNamespace Nothing  = getDefaultPrefix++--  Return function to update graph in Turtle parser state,+--  using the supplied function of a graph+--+updateGraph :: (RDFGraph -> RDFGraph) -> TurtleState -> TurtleState+updateGraph f s = s { graphState = f (graphState s) }++----------------------------------------------------------------------+--  Define top-level parser function:+--  accepts a string and returns a graph or error+----------------------------------------------------------------------++type TurtleParser a = Parser TurtleState a++-- | Parse as Turtle (with no real base URI).+-- +-- See 'parseTurtle' if you need to provide a base URI.+--+parseTurtlefromText ::+  L.Text -- ^ input in N3 format.+  -> ParseResult+parseTurtlefromText = flip parseTurtle Nothing++-- | Parse a string with an optional base URI.+--            +-- Unlike 'parseN3' we treat the base URI as a URI and not+-- a QName.+--+parseTurtle ::+  L.Text -- ^ input in N3 format.+  -> Maybe URI -- ^ optional base URI+  -> ParseResult+parseTurtle txt mbase = parseAnyfromText turtleDoc mbase txt++hashURI :: URI+hashURI = fromJust $ parseURIReference "#"++emptyState :: +  Maybe URI  -- ^ starting base for the graph+  -> TurtleState+emptyState mbase = +  let pmap   = LookupMap [makeNamespace Nothing hashURI]+      buri   = fromMaybe (getScopedNameURI defaultBase) mbase+  in TurtleState+     { graphState = emptyRDFGraph+     , prefixUris = pmap+     , baseUri    = buri+     , nodeGen    = 0+     }+  +-- | Function to supply initial context and parse supplied term.+--+parseAnyfromText :: +  TurtleParser a  -- ^ parser to apply+  -> Maybe URI    -- ^ base URI of the input, or @Nothing@ to use default base value+  -> L.Text       -- ^ input to be parsed+  -> Either String a+parseAnyfromText parser mbase = runParserWithError parser (emptyState mbase)++newBlankNode :: TurtleParser RDFLabel+newBlankNode = do+  n <- stQuery (succ . nodeGen)+  stUpdate $ \s -> s { nodeGen = n }+  return $ Blank (show n)+  +{-+This has been made tricky by the attempt to remove the default list+of prefixes from the starting point of a parse and the subsequent+attempt to add every new namespace we come across to the parser state.++So we add in the original default namespaces for testing, since+this routine is really for testing.++addTestPrefixes :: TurtleParser ()+addTestPrefixes = stUpdate $ \st -> st { prefixUris = LookupMap prefixTable } -- should append to existing map++-}++-- helper routines++comma, semiColon , fullStop :: TurtleParser ()+comma = isymbol ","+semiColon = isymbol ";"+fullStop = isymbol "."++sQuot, dQuot, sQuot3, dQuot3 :: TurtleParser ()+sQuot = ichar '\''+dQuot = ichar '"'+sQuot3 = ignore $ string "'''"+dQuot3 = ignore $ string "\"\"\""++match :: (Ord a) => a -> [(a,a)] -> Bool+match v = any (\(l,h) -> v >= l && v <= h)++-- a specialization of bracket+br :: String -> String -> TurtleParser a -> TurtleParser a+br lsym rsym = bracket (symbol lsym) (symbol rsym)++-- this is a lot simpler than N3+atWord :: T.Text -> TurtleParser ()+atWord s = char '@' *> lexeme (stringT s) *> pure ()++{-+Add statement to graph in the parser state; there is a special case+for the special-case literals in the grammar since we need to ensure+the necessary namespaces (in other words xsd) are added to the+namespace store.+-}++addStatement :: RDFLabel -> RDFLabel -> RDFLabel -> TurtleParser ()+addStatement s p o@(TypedLit _ dtype) | dtype `elem` [xsdBoolean, xsdInteger, xsdDecimal, xsdDouble] = do +  ost <- stGet+  let stmt = arc s p o+      oldp = prefixUris ost+      ogs = graphState ost+      newp = mapReplace oldp (getScopeNamespace dtype)+  stUpdate $ \st -> st { prefixUris = newp, graphState = addArc stmt ogs }+addStatement s p o = stUpdate (updateGraph (addArc (arc s p o) ))++isaz, isAZ, isaZ, is09, isaZ09 :: Char -> Bool+isaz = isAsciiLower+isAZ = isAsciiUpper+isaZ c = isaz c || isAZ c+is09 = isDigit+isaZ09 c = isaZ c || is09 c++{-+Convert a string representing a double into canonical+XSD form. The input string is assumed to be syntactically+valid so we use read rather than reads. We use the String read+rather than Text one because of issues I have had in some tests+with the accuracy of the Text one.+-}+d2s :: L.Text -> RDFLabel+d2s = +  let conv :: String -> Double+      conv = read+  in toRDFLabel . conv . L.unpack++{-+Since operatorLabel can be used to add a label with an +unknown namespace, we need to ensure that the namespace+is added if not known. If the namespace prefix is already+in use then it is over-written (rather than add a new+prefix for the label).++TODO:+  - could we use the reverse lookupmap functionality to+    find if the given namespace URI is in the namespace+    list? If it is, use it's key otherwise do a+    mapReplace for the input namespace.+    +-}+operatorLabel :: ScopedName -> TurtleParser RDFLabel+operatorLabel snam = do+  st <- stGet+  let sns = getScopeNamespace snam+      opmap = prefixUris st+      pkey = entryKey sns+      pval = entryVal sns+      +      rval = Res snam+      +  -- the lookup and the replacement could be fused+  case mapFindMaybe pkey opmap of+    Just val | val == pval -> return rval+             | otherwise   -> do+               stUpdate $ \s -> s { prefixUris = mapReplace opmap sns }+               return rval+    +    _ -> do+      stUpdate $ \s -> s { prefixUris = mapAdd opmap sns }+      return rval+        +findPrefix :: T.Text -> TurtleParser Namespace+findPrefix pre = do+  st <- stGet+  case mapFindMaybe (Just pre) (prefixUris st) of+    Just uri -> return $ makeNamespace (Just pre) uri+    Nothing  -> failBad $ "Undefined prefix '" ++ T.unpack pre ++ ":'."++{-++Syntax productions; the Turtle NBF grammar elements are from+http://www.w3.org/TR/turtle/turtle.bnf++The element names are converted to match Haskell syntax+and idioms where possible:++  - camel Case rather than underscores and all upper case++  - upper-case identifiers prepended by _ after above form++-}++{-+[1] turtleDoc ::= (statement)*+-}+turtleDoc :: TurtleParser RDFGraph+turtleDoc = mkGr <$> (whiteSpace *> many statement *> eof *> stGet)+  where+    mkGr s = setNamespaces (prefixUris s) (graphState s)++{-+[2] statement ::= directive "." + | triples "."+-}+statement :: TurtleParser ()+statement = (directive <|> triples) *> fullStop++{-+[3] directive ::= prefixID + | base+-}+directive :: TurtleParser ()+directive = lexeme (prefixID <|> base)++{-+[4] prefixID ::= PREFIX PNAME_NS IRI_REF +-}+prefixID :: TurtleParser ()+prefixID = do+  _prefix +  p <- lexeme _pnameNS+  u <- _iriRef+  stUpdate (setPrefix (fmap L.toStrict p) u)++{-+[5] base ::= BASE IRI_REF +-}+base :: TurtleParser ()+base = _base >> _iriRef >>= stUpdate . setBase++{-+[6] triples ::= subject predicateObjectList +-}++triples :: TurtleParser ()+triples = subject >>= predicateObjectList++{-+[7] predicateObjectList ::= verb objectList ( ";" verb objectList )* (";")? +-}++predicateObjectList :: RDFLabel -> TurtleParser ()+predicateObjectList subj = +  let term = verb >>= objectList subj+  in sepBy1 term semiColon *> ignore (optional semiColon)+  -- in sepBy1 (lexeme term) semiColon *> ignore (optional semiColon)+  +{-+[8] objectList ::= object ( "," object )* +-}++objectList :: RDFLabel -> RDFLabel -> TurtleParser ()+objectList subj prd = sepBy1 object comma >>= mapM_ (addStatement subj prd)++{-+[9] verb ::= predicate + | "a"+-}++verb :: TurtleParser RDFLabel+verb = predicate <|> (lexeme (char 'a') *> operatorLabel rdfType)+   +{-       +[10] subject ::= IRIref + | blank +-}++subject :: TurtleParser RDFLabel+subject = (Res <$> iriref) <|> blank++{-+[11] predicate ::= IRIref +-}++predicate :: TurtleParser RDFLabel+predicate = Res <$> iriref++{-+[12] object ::= IRIref + | blank + | literal+-}++object :: TurtleParser RDFLabel+object = (Res <$> iriref) <|> blank <|> literal++{-+[13] literal ::= RDFLiteral + | NumericLiteral + | BooleanLiteral +-}++literal :: TurtleParser RDFLabel+literal = lexeme $ rdfLiteral <|> numericLiteral <|> booleanLiteral++{-+[14] blank ::= BlankNode + | blankNodePropertyList + | collection ++Since both BlankNode and blankNodePropertyList can match '[ ... ]' we pull+that out and treat this as++  blank ::= BLANK_NODE_LABEL+     | "[" (predicateObjectList | WS*) "]"+     | collection++blank :: TurtleParser RDFLabel+blank = lexeme (blankNode <|> blankNodePropertyList <|> collection)++-}++blank :: TurtleParser RDFLabel+blank = lexeme (_blankNodeLabel+                <|>+                bracket (char '[') (char ']') handleBlankNode+                <|>+                collection+                )++{-+[15] blankNodePropertyList ::= "[" predicateObjectList "]" ++We now match the brackets in the parent rule.++blankNodePropertyList :: TurtleParser RDFLabel+blankNodePropertyList = do+  bNode <- newBlankNode+  -- br "[" "]" (predicateObjectList bNode)+  bracket (satisfy (=='['))+    (satisfy (==']'))+    (_manyws *> predicateObjectList bNode *> _manyws)+  -- ignore (optional _manyws) -- TODO: this is a hack+  return bNode++-}++handleBlankNode :: TurtleParser RDFLabel+handleBlankNode = do+  bNode <- newBlankNode+  _manyws+  ignore $ optional $ predicateObjectList bNode+  _manyws+  return bNode+++{-+[16] collection ::= "(" object* ")" +-}++collection :: TurtleParser RDFLabel+collection = do+  os <- br "(" ")" (many object)+  eNode <- operatorLabel rdfNil+  case os of+    [] -> return eNode+    +    (x:xs) -> do+      sNode <- newBlankNode+      first <- operatorLabel rdfFirst+      addStatement sNode first x+      lNode <- foldM addElem sNode xs+      rest <- operatorLabel rdfRest+      addStatement lNode rest eNode+      return sNode++    where      +      addElem prevNode curElem = do+        bNode <- newBlankNode+        first <- operatorLabel rdfFirst+        rest <- operatorLabel rdfRest+        addStatement prevNode rest bNode+        addStatement bNode first curElem+        return bNode++{-+[17] <BASE> ::= "@base" +-}++_base :: TurtleParser ()+_base = atWord "base"++{-+[18] <PREFIX> ::= "@prefix" +-}++_prefix :: TurtleParser ()+_prefix = atWord "prefix"++{-+[19] <UCHAR> ::= ( "\\u" [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] ) + | ( "\\U" [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] + [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] ) ++-}++_uchar :: TurtleParser Char+_uchar = char '\\' *> _uchar'+         +_uchar' :: TurtleParser Char+_uchar' = (char 'u' *> hex4) <|> (char 'U' *> hex8)++{-+[60s] RDFLiteral ::= String ( LANGTAG | ( "^^" IRIref ) )? +-}++rdfLiteral :: TurtleParser RDFLabel+rdfLiteral = do+  lbl <- L.toStrict <$> turtleString+  opt <- optional ((Left <$> _langTag) <|> (string "^^" *> (Right <$> iriref)))+  return $ case opt of+             Just (Left lcode)  -> LangLit lbl lcode+             Just (Right dtype) -> TypedLit lbl dtype+             _                  -> Lit lbl++{-+[61s] NumericLiteral ::= NumericLiteralUnsigned + | NumericLiteralPositive + | NumericLiteralNegative +-}++numericLiteral :: TurtleParser RDFLabel+numericLiteral = numericLiteralNegative <|> numericLiteralPositive <|> numericLiteralUnsigned++{-+[62s] NumericLiteralUnsigned ::= INTEGER + | DECIMAL + | DOUBLE +-}++numericLiteralUnsigned :: TurtleParser RDFLabel+numericLiteralUnsigned = +  d2s <$> _double+  <|> +  (makeDatatypedLiteral xsdDecimal . L.toStrict <$> _decimal)+  <|> +  (makeDatatypedLiteral xsdInteger . L.toStrict <$> _integer)++{-+[63s] NumericLiteralPositive ::= INTEGER_POSITIVE + | DECIMAL_POSITIVE + | DOUBLE_POSITIVE +-}++numericLiteralPositive :: TurtleParser RDFLabel+numericLiteralPositive =+  d2s <$> _doublePositive+  <|> +  (makeDatatypedLiteral xsdDecimal . L.toStrict <$> _decimalPositive)+  <|> +  (makeDatatypedLiteral xsdInteger . L.toStrict <$> _integerPositive)++{-+[64s] NumericLiteralNegative ::= INTEGER_NEGATIVE + | DECIMAL_NEGATIVE + | DOUBLE_NEGATIVE +-}++numericLiteralNegative :: TurtleParser RDFLabel+numericLiteralNegative = +  d2s <$> _doubleNegative+  <|> +  (makeDatatypedLiteral xsdDecimal . L.toStrict <$> _decimalNegative)+  <|> +  (makeDatatypedLiteral xsdInteger . L.toStrict <$> _integerNegative)+   +{-+[65s] BooleanLiteral ::= "true" + | "false"+-}++booleanLiteral :: TurtleParser RDFLabel+booleanLiteral = makeDatatypedLiteral xsdBoolean . T.pack <$> (string "true" <|> string "false")++{-+[66s] String ::= STRING_LITERAL1 + | STRING_LITERAL2 + | STRING_LITERAL_LONG1 + | STRING_LITERAL_LONG2+-}++turtleString :: TurtleParser L.Text+turtleString = +  lexeme (+    _stringLiteralLong1 <|> _stringLiteral1 <|>+    _stringLiteralLong2 <|> _stringLiteral2)++{-+[67s] IRIref ::= IRI_REF + | PrefixedName +-}++iriref :: TurtleParser ScopedName+iriref = lexeme ((makeURIScopedName <$> _iriRef) <|> prefixedName)++{-+[68s] PrefixedName ::= PNAME_LN + | PNAME_NS +-}++prefixedName :: TurtleParser ScopedName+prefixedName = +  _pnameLN <|> +  flip makeNSScopedName emptyLName <$> (_pnameNS >>= findPrefixNamespace)++{-+[69s] BlankNode ::= BLANK_NODE_LABEL + | ANON ++blankNode :: TurtleParser RDFLabel+blankNode = lexeme (_blankNodeLabel <|> _anon)++-}++{-+[70s] <IRI_REF> ::= "<" ( [^<>\"{}|^`\\] - [#0000- ] | UCHAR )* ">" ++Read [#0000- ] as [#x00-#x20] from+http://lists.w3.org/Archives/Public/public-rdf-comments/2011Aug/0011.html++Unlike N3, whitespace is significant within the surrounding <>.++At present relying on Network.URI to define what characters are valid+in a URI. This is not necessarily ideal.+-}++_iriRef :: TurtleParser URI+_iriRef = do+  utxt <- bracket (char '<') (char '>') $ manySatisfy (/= '>') -- TODO: fix+  let ustr = L.unpack utxt+  case parseURIReference ustr of+    Nothing -> fail $ "Unable to convert <" ++ ustr ++ "> to a URI"+    Just uref -> do+      s <- stGet+      either fail return $ appendURIs (baseUri s) uref++{-+[71s] <PNAME_NS> ::= (PN_PREFIX)? ":" +-}++_pnameNS :: TurtleParser (Maybe L.Text)+_pnameNS = optional _pnPrefix <* char ':'++{-+[72s] <PNAME_LN> ::= PNAME_NS PN_LOCAL +-}++_pnameLN :: TurtleParser ScopedName+_pnameLN = do+  ns <- _pnameNS >>= findPrefixNamespace+  l <- fmap L.toStrict _pnLocal+  case newLName l of+    Just lname -> return $ makeNSScopedName ns lname+    _ -> fail $ "Invalid local name: '" ++ T.unpack l ++ "'"++{-+[73s] <BLANK_NODE_LABEL> ::= "_:" PN_LOCAL +-}++_blankNodeLabel :: TurtleParser RDFLabel+_blankNodeLabel = (Blank . L.unpack) <$> (string "_:" *> _pnLocal)++{-++These are unused in the grammar.++[74s] <VAR1> ::= "?" VARNAME +[75s] <VAR2> ::= "$" VARNAME +-}++{-+[76s] <LANGTAG> ::= BASE + | PREFIX + | "@" [a-zA-Z]+ ( "-" [a-zA-Z0-9]+ )* ++I am ignoring the BASE and PREFIX lines here as they don't make sense to me.+-}++-- Note that toLangTag may fail since it does some extra+-- validation not done by the parser (mainly on the length of the+-- primary and secondary tags).+--+-- NOTE: This parser does not accept multiple secondary tags which RFC3066+-- does.+--+_langTag :: TurtleParser LanguageTag+_langTag = do+    ichar '@'+    h <- many1Satisfy isaZ+    mt <- optional (L.cons <$> char '-' <*> many1Satisfy isaZ09)+    let lbl = L.toStrict $ L.append h $ fromMaybe L.empty mt+    case toLangTag lbl of+        Just lt -> return lt+        _ -> fail ("Invalid language tag: " ++ T.unpack lbl) -- should this be failBad?+  +{-+[77s] <INTEGER> ::= [0-9]+ +-}++_integer :: TurtleParser L.Text+_integer = many1Satisfy is09++{-+[78s] <DECIMAL> ::= [0-9]+ "." [0-9]* + | "." [0-9]+ ++We try to produce a canonical form for the+numbers.+-}++_decimal :: TurtleParser L.Text+_decimal = +  let dpart = L.cons <$> char '.' <*> (fromMaybe "0" <$> optional _integer)+  in +   (L.append <$> _integer <*> dpart)+   <|>+   (L.append "0." <$> (char '.' *> _integer))++{-+[79s] <DOUBLE> ::= [0-9]+ "." [0-9]* EXPONENT + | "." ( [0-9] )+ EXPONENT + | ( [0-9] )+ EXPONENT ++Unlike _decimal, the canonical form is enforced+later on, although it could be done here.+-}++_double :: TurtleParser L.Text+_double = +  (L.append <$> _decimal <*> _exponent)+  <|>+  (L.append <$> _integer <*> _exponent)+  +{-+[80s] <INTEGER_POSITIVE> ::= "+" INTEGER +[81s] <DECIMAL_POSITIVE> ::= "+" DECIMAL +[82s] <DOUBLE_POSITIVE> ::= "+" DOUBLE +-}++_integerPositive, _decimalPositive, _doublePositive :: TurtleParser L.Text+_integerPositive = char '+' *> _integer+_decimalPositive = char '+' *> _decimal+_doublePositive = char '+' *> _double++{-+[83s] <INTEGER_NEGATIVE> ::= "-" INTEGER +[84s] <DECIMAL_NEGATIVE> ::= "-" DECIMAL +[85s] <DOUBLE_NEGATIVE> ::= "-" DOUBLE +-}++_integerNegative, _decimalNegative, _doubleNegative :: TurtleParser L.Text+_integerNegative = L.cons <$> char '-' <*> _integer+_decimalNegative = L.cons <$> char '-' <*> _decimal+_doubleNegative = L.cons <$> char '-' <*> _double++{-+[86s] <EXPONENT> ::= [eE] [+-]? [0-9]+ +-}++_exponent :: TurtleParser L.Text+_exponent = do+  ignore $ satisfy (`elem` "eE")+  ms <- optional (satisfy (`elem` "+-"))+  e <- _integer+  case ms of+    Just '-' -> return $ L.append "E-" e+    _        -> return $ L.cons 'E' e+  +{-+[87s] <STRING_LITERAL1> ::= "'" ( ( [^'\\\n\r] ) | ECHAR | UCHAR )* "'" +[88s] <STRING_LITERAL2> ::= '"' ( ( [^\"\\\n\r] ) | ECHAR | UCHAR )* '"' +[89s] <STRING_LITERAL_LONG1> ::= "'''" ( ( "'" | "''" )? ( [^'\\] | ECHAR | UCHAR ) )* "'''" +[90s] <STRING_LITERAL_LONG2> ::= '"""' ( ( '"' | '""' )? ( [^\"\\] | ECHAR | UCHAR ) )* '"""' +-}++_stringLiteral1, _stringLiteral2 :: TurtleParser L.Text+_stringLiteral1 = _stringIt sQuot (_tChars "'\\\n\r")+_stringLiteral2 = _stringIt dQuot (_tChars "\"\\\n\r")++_stringLiteralLong1, _stringLiteralLong2 :: TurtleParser L.Text+_stringLiteralLong1 = _stringItLong sQuot3 (_tCharsLong '\'' "'\\")+_stringLiteralLong2 = _stringItLong dQuot3 (_tCharsLong '"' "\"\\")++_stringIt :: TurtleParser a -> TurtleParser Char -> TurtleParser L.Text+_stringIt sep chars = L.pack <$> bracket sep sep (many chars)++_stringItLong :: TurtleParser a -> TurtleParser L.Text -> TurtleParser L.Text+_stringItLong sep chars = L.concat <$> bracket sep sep (many chars)++_tChars :: String -> TurtleParser Char+_tChars excl = (char '\\' *> (_echar' <|> _uchar'))+               <|> noneOf excl++_tCharsLong :: Char -> String -> TurtleParser L.Text+_tCharsLong c excl = do+  mq <- optional $ oneOrTwo c+  r <- _tChars excl+  return $ L.append (fromMaybe L.empty mq) (L.singleton r)++oneOrTwo :: Char -> TurtleParser L.Text+oneOrTwo c = do+  a <- char c+  mb <- optional (char c)+  case mb of+    Just b -> return $ L.pack [a,b]+    _      -> return $ L.singleton a++{-+[91s] <ECHAR> ::= "\\" [tbnrf\\\"'] +-}++_echar :: TurtleParser Char+_echar = char '\\' *> _echar'++_echar' :: TurtleParser Char+_echar' = +  (char 't' *> pure '\t') <|>+  (char 'b' *> pure '\b') <|>+  (char 'n' *> pure '\n') <|>+  (char 'r' *> pure '\r') <|>+  (char '\\' *> pure '\\') <|>+  (char '"' *> pure '"') <|>+  (char '\'' *> pure '\'')+++{-++Unused.++[92s] <NIL> ::= "(" (WS)* ")" +-}++{-+[93s] <WS> ::= " " + | "\t" + | "\r" + | "\n"++_ws :: TurtleParser ()+_ws = ignore $ satisfy (`elem` " \t\r\n")++-}++_manyws :: TurtleParser ()+_manyws = ignore $ manySatisfy (`elem` " \t\r\n")++{-+[94s] <ANON> ::= "[" (WS)* "]" ++Unused as we do not support the use of ANON in the BlankNode+terminal.++_anon :: TurtleParser RDFLabel+_anon = br "[" "]" _manyws *> newBlankNode++-}++{-+[95s] <PN_CHARS_BASE> ::= [A-Z] + | [a-z] + | [#00C0-#00D6] + | [#00D8-#00F6] + | [#00F8-#02FF] + | [#0370-#037D] + | [#037F-#1FFF] + | [#200C-#200D] + | [#2070-#218F] + | [#2C00-#2FEF] + | [#3001-#D7FF] + | [#F900-#FDCF] + | [#FDF0-#FFFD] + | [#10000-#EFFFF] + | UCHAR ++TODO: may want to make this a Char -> Bool selector for+use with manySatisfy rather than a combinator.+-}++_pnCharsBase :: TurtleParser Char+_pnCharsBase = +  let f c = let i = ord c+            in isaZ c || +               match i [(0xc0, 0xd6), (0xd8, 0xf6), (0xf8, 0x2ff),+                        (0x370, 0x37d), (0x37f, 0x1fff), (0x200c, 0x200d),+                        (0x2070, 0x218f), (0x2c00, 0x2fef), (0x3001, 0xd7ff),+                        (0xf900, 0xfdcf), (0xfdf0, 0xfffd), (0x10000, 0xeffff)]+  in satisfy f <|> _uchar++{-+[96s] <PN_CHARS_U> ::= PN_CHARS_BASE + | "_"+-}++_pnCharsU :: TurtleParser Char+_pnCharsU = _pnCharsBase <|> char '_'++{-++Only used in VAR1/2 rules which are themselves unused.++Unused in the grammar (other than+[97s] <VARNAME> ::= ( PN_CHARS_U | [0-9] ) ( PN_CHARS_U | [0-9] | #00B7 | [#0300-#036F] | [# + 203F-#2040] )* +-}++{-+[98s] <PN_CHARS> ::= PN_CHARS_U + | "-" + | [0-9] + | #00B7 + | [#0300-#036F] + | [#203F-#2040] +-}++_pnChars :: TurtleParser Char+_pnChars = +  _pnCharsU +  <|> +  satisfy (\c -> let i = ord c +                 in c == '-' || isDigit c || i == 0xb7 ||+                    match i [(0x0300, 0x036f), (0x203f, 0x2040)])++{-+[99s] <PN_PREFIX> ::= PN_CHARS_BASE ( ( PN_CHARS | "." )* PN_CHARS )? +-}++_pnPrefix :: TurtleParser L.Text+_pnPrefix = L.cons <$> _pnCharsBase <*> _pnRest+  +{-+[100s] <PN_LOCAL> ::= ( PN_CHARS_U | [0-9] ) ( ( PN_CHARS | "." )* PN_CHARS )? +-}     ++_pnLocal :: TurtleParser L.Text+_pnLocal = L.cons <$> (_pnCharsU <|> satisfy is09) +           <*> _pnRest++{-+Extracted from PN_PREFIX and PN_LOCAL is++<PN_REST> :== ( ( PN_CHARS | "." )* PN_CHARS )?++We assume below that the match is only ever done for small strings, so+the cost of the foldr isn't likely to be large. Let's see how well+this assumption holds up.++-}++_pnRest :: TurtleParser L.Text+_pnRest = do+  lbl <- many (_pnChars <|> char '.')+  let (nret, lclean) = clean lbl+      +      -- a simple difference list implementation+      edl = id+      snocdl x xs = xs . (x:)+      appenddl = (.)+      replicatedl n x = (replicate n x ++)+  +      -- this started out as a simple automaton/transducer from+      -- http://www.haskell.org/pipermail/haskell-cafe/2011-September/095347.html+      -- but then I decided to complicate it+      -- +      clean :: String -> (Int, String)+      clean = go 0 edl+        where+          go n acc [] = (n, acc [])+          go n acc ('.':xs) = go (n+1) acc xs +          go 0 acc (x:xs) = go 0 (snocdl x acc) xs+          go n acc (x:xs) = go 0 (appenddl acc (snocdl x (replicatedl n '.'))) xs++  reparse $ L.replicate (fromIntegral nret) (L.singleton '.')+  return $ L.pack lclean++{-+Original from ++ chop = go 0 []+        where+        -- go :: State -> Stack -> String -> String+        go 0 _ [] = []+        go 0 _ (x:xs)+            | isSpace x = go 1 [x] xs+            | otherwise = x : go 0 xs++        go 1 ss [] = []+        go 1 ss (x:xs)+            | isSpace c = go 1 (x:ss) xs+            | otherwise = reverse ss ++ x : go 0 xs++-}++--------------------------------------------------------------------------------+--+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke+--  All rights reserved.+--+--  This file is part of Swish.+--+--  Swish is free software; you can redistribute it and/or modify+--  it under the terms of the GNU General Public License as published by+--  the Free Software Foundation; either version 2 of the License, or+--  (at your option) any later version.+--+--  Swish is distributed in the hope that it will be useful,+--  but WITHOUT ANY WARRANTY; without even the implied warranty of+--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+--  GNU General Public License for more details.+--+--  You should have received a copy of the GNU General Public License+--  along with Swish; if not, write to:+--    The Free Software Foundation, Inc.,+--    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+--+--------------------------------------------------------------------------------
+ src/Swish/RDF/Parser/Utils.hs view
@@ -0,0 +1,385 @@+{-# LANGUAGE OverloadedStrings #-}+--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  Utils+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke+--  License     :  GPL V2+--+--  Maintainer  :  Douglas Burke+--  Stability   :  experimental+--  Portability :  OverloadedStrings+--+--  Support for the RDF Parsing modules.+--+--------------------------------------------------------------------------------++module Swish.RDF.Parser.Utils+    ( SpecialMap+    -- , mapPrefix+              +    -- tables+    , prefixTable, specialTable++    -- parser+    , runParserWithError+    , ParseResult+    , ignore+    , char+    , ichar+    , string+    , stringT+    , symbol+    , isymbol+    , lexeme+    , notFollowedBy+    , whiteSpace+    , skipMany+    , skipMany1+    , endBy+    , sepEndBy+    , sepEndBy1+    , manyTill+    , noneOf+    , eoln+    , fullStop+    , hex4+    , hex8+    , appendURIs+    )+    where++import Swish.Namespace (Namespace, makeNamespace, ScopedName)++import Swish.RDF.Graph (RDFGraph)+import Swish.RDF.Vocabulary+    ( namespaceRDF+    , namespaceRDFS+    , namespaceRDFD+    , namespaceOWL+    , namespaceLOG+    , rdfType+    , rdfFirst, rdfRest, rdfNil+    , owlSameAs, logImplies+    , defaultBase+    )++import Data.Char (isSpace, isHexDigit, chr)+import Data.LookupMap (LookupMap(..))+import Data.Maybe (fromMaybe, fromJust)++import Network.URI (URI(..), relativeTo, parseURIReference)++import Text.ParserCombinators.Poly.StateText++import qualified Data.Text      as T+import qualified Data.Text.Lazy as L+import qualified Data.Text.Read as R++-- Code++-- | Append the two URIs. Should probably be moved+--   out of RDFParser. It is also just a thin wrapper around+--   `Network.URI.relativeTo`.+appendURIs ::+  URI     -- ^ The base URI+  -> URI  -- ^ The URI to append (it can be an absolute URI).+  -> Either String URI+appendURIs base uri =+  case uriScheme uri of+    "" -> case uri `relativeTo` base of+          Just out -> Right out+          _ -> Left $ "Unable to append <" ++ show uri ++ "> to base=<" ++ show base ++ ">"+    _  -> Right uri+  +-- | Type for special name lookup table+type SpecialMap = LookupMap (String, ScopedName)++{-+-- | Lookup prefix in table and return the matching URI.+--+--   If the prefix is unknown then we currently error+--   out (used to return 'prefix:' or ':' but now using+--   URIs I am changing this behavior). This may well be+--   backed out.+mapPrefix :: NamespaceMap -> Maybe String -> URI+mapPrefix pmap pfix = +  case mapFindMaybe pfix pmap of+    Just uri -> uri+    Nothing  -> error $ "Unable to find prefix: " ++ show pfix -- fromMaybe "" pfix ++ ":"+-}+  +{-+mapPrefix ps p@(Just pre) = mapFind (pre++":") p ps+mapPrefix ps _ = mapFind ":" Nothing ps+-}++-- | Define default table of namespaces+prefixTable :: [Namespace]+prefixTable =   [ namespaceRDF+                , namespaceRDFS+                , namespaceRDFD     -- datatypes+                , namespaceOWL+                , namespaceLOG+                , makeNamespace Nothing $ fromJust (parseURIReference "#") -- is this correct?+                ]++-- | Define default special-URI table.+specialTable ::+    Maybe ScopedName  -- ^ initial base URI, otherwise uses 'defaultBase'+    -> [(String,ScopedName)]+specialTable mbase =+  [ ("a",         rdfType    ),+    ("equals",    owlSameAs  ),+    ("implies",   logImplies ),+    ("listfirst", rdfFirst   ),+    ("listrest",  rdfRest    ),+    ("listnull",  rdfNil     ),+    ("base",      fromMaybe defaultBase mbase ) +  ]++-- Parser routines, heavily based on Parsec combinators++-- | Run the parser and return the successful parse or an error+-- message which consists of the standard Polyparse error plus+-- a fragment of the unparsed input to provide context.+--+runParserWithError :: +  Parser a b -- ^ parser (carrying state) to apply+  -> a       -- ^ starting state for the parser+  -> L.Text       -- ^ input to be parsed+  -> Either String b+runParserWithError parser state0 input = +  let (result, _, unparsed) = runParser parser state0 input+     +      -- TODO: work out how best to report error context; for now just take the+      -- next 40 characters and assume there is enough context.+      econtext = if L.null unparsed+                 then "\n(at end of the text)\n"+                 else "\nRemaining input:\n" ++ +                      case L.compareLength unparsed 40 of+                        GT -> L.unpack (L.take 40 unparsed) ++ "..."+                        _ -> L.unpack unparsed++  in case result of+    Left emsg -> Left $ emsg ++ econtext+    _ -> result++-- | The result of a parse, which is either an error message or a graph.+type ParseResult = Either String RDFGraph+++-- | Run the parser and ignore the result.+ignore :: (Applicative f) => f a -> f ()+ignore f = f *> pure ()++-- | Match the character.+char :: Char -> Parser s Char+char c = satisfy (==c)++-- | Match the character, ignoring the result.+ichar :: Char -> Parser s ()+ichar = ignore . char++-- TODO: is there a better way to do this?+-- | Match the text.+string :: String -> Parser s String+string = mapM char+  +-- | Match the text.+stringT :: T.Text -> Parser s T.Text+stringT s = string (T.unpack s) >> return s++-- | Run the parser 'many' times and ignore the result.+skipMany :: Parser s a -> Parser s ()+skipMany = ignore . many+  +-- | Run the parser 'many1' times and ignore the result.+skipMany1 :: Parser s a -> Parser s ()+skipMany1 = ignore . many1++-- | Match zero or more occurences of+-- parser followed by separator.+endBy :: +    Parser s a    -- ^ parser+    -> Parser s b -- ^ separator+    -> Parser s [a]+endBy p sep = many (p <* sep)++-- | Match zero or more occurences of the parser followed+-- by the separator.+sepEndBy :: +    Parser s a    -- ^ parser+    -> Parser s b -- ^ separator+    -> Parser s [a]+sepEndBy p sep = sepEndBy1 p sep <|> pure []++-- | Accept one or more occurences of the parser+-- separated by the separator. Unlike 'endBy' the+-- last separator is optional.+sepEndBy1 :: +    Parser s a    -- ^ parser+    -> Parser s b -- ^ separator+    -> Parser s [a]+sepEndBy1 p sep = do+  x <- p+  (sep *> ((x:) <$> sepEndBy p sep)) <|> return [x]+  +-- | Accept zero or more runs of the parser+-- ending with the delimiter.+manyTill :: +    Parser s a    -- ^ parser+    -> Parser s b -- ^ delimiter+    -> Parser s [a]+manyTill p end = go+  where+    go = (end *> return [])+         <|>+         ((:) <$> p <*> go)++-- | Accept any character that is not a member of the given string.+noneOf :: String -> Parser s Char           +noneOf istr = satisfy (`notElem` istr)++-- | Matches '.'.           +fullStop :: Parser s ()+fullStop = ichar '.'++-- | Match the end-of-line sequence (@"\\n"@, @"\\r"@, or @"\\r\\n"@). +eoln :: Parser s ()+-- eoln = ignore (newline <|> (lineFeed *> optional newline))+-- eoln = ignore (try (string "\r\n") <|> string "\r" <|> string "\n")+eoln = ignore (oneOf [string "\r\n", string "\r", string "\n"])++-- | Succeed if the next character does not match the given function.+notFollowedBy :: (Char -> Bool) -> Parser s ()+notFollowedBy p = do+  c <- next+  if p c +    then fail $ "Unexpected character: " ++ show [c]+    else reparse $ L.singleton c++-- | Match the given string and any trailing 'whiteSpace'.+symbol :: String -> Parser s String+symbol = lexeme . string++-- | As 'symbol' but ignoring the result.+isymbol :: String -> Parser s ()+isymbol = ignore . symbol++-- | Convert a parser into one that also matches, and ignores,+-- trailing 'whiteSpace'.+lexeme :: Parser s a -> Parser s a+lexeme p = p <* whiteSpace++-- | Match white space: a space or a comment (@#@ character and anything following it+-- up to to a new line).+whiteSpace :: Parser s ()+whiteSpace = skipMany (simpleSpace <|> oneLineComment)++simpleSpace :: Parser s ()+simpleSpace = ignore $ many1Satisfy isSpace++-- TODO: this should use eoln rather than a check on \n+oneLineComment :: Parser s ()+oneLineComment = ichar '#' *> manySatisfy (/= '\n') *> pure ()++{-++Not sure we can get this with polyparse++-- | Annotate a Parsec error with the local context - i.e. the actual text+-- that caused the error and preceeding/succeeding lines (if available)+--+annotateParsecError :: +    Int -- ^ the number of extra lines to include in the context (<=0 is ignored)+    -> [String] -- ^ text being parsed+    -> ParseError -- ^ the parse error+    -> String -- ^ Parsec error with additional context+annotateParsecError extraLines ls err = +    -- the following is based on the show instance of ParseError+    let ePos = errorPos err+        lNum = sourceLine ePos+        cNum = sourceColumn ePos+        -- it is possible to be at the end of the input so need+        -- to check; should produce better output than this in this+        -- case+        nLines = length ls+        ln1 = lNum - 1+        eln = max 0 extraLines+        lNums = [max 0 (ln1 - eln) .. min (nLines-1) (ln1 + eln)]+        +        beforeLines = map (ls !!) $ filter (< ln1) lNums+        afterLines  = map (ls !!) $ filter (> ln1) lNums+        +        -- in testing was able to get a line number after the text so catch this+        -- case; is it still necessary?+        errorLine = if ln1 >= nLines then "" else ls !! ln1+        arrowLine = replicate (cNum-1) ' ' ++ "^"+        finalLine = "(line " ++ show lNum ++ ", column " ++ show cNum ++ " indicated by the '^' sign above):"+        +        eHdr = "" : beforeLines ++ errorLine : arrowLine : afterLines ++ [finalLine]+        eMsg = showErrorMessages "or" "unknown parse error" "expecting" "unexpected" "end of input"+               (errorMessages err)++    in unlines eHdr ++ eMsg++-}++{-+Handle hex encoding; the spec for N3 and NTriples suggest that+only upper-case A..F are valid but you can find lower-case values+out there so support these too.+-}++hexDigit :: Parser a Char+-- hexDigit = satisfy (`elem` ['0'..'9'] ++ ['A'..'F'])+hexDigit = satisfy isHexDigit++-- | A four-digit hex value (e.g. @1a34@ or @03F1@).+hex4 :: Parser a Char+hex4 = do+  digs <- exactly 4 hexDigit+  let mhex = R.hexadecimal (T.pack digs)+  case mhex of+    Left emsg     -> failBad $ "Internal error: unable to parse hex4: " ++ emsg+    Right (v, "") -> return $ chr v+    Right (_, vs) -> failBad $ "Internal error: hex4 remainder = " ++ T.unpack vs++-- | An eight-digit hex value that has a maximum of @0010FFFF@.+hex8 :: Parser a Char+hex8 = do+  digs <- exactly 8 hexDigit+  let mhex = R.hexadecimal (T.pack digs)+  case mhex of+    Left emsg     -> failBad $ "Internal error: unable to parse hex8: " ++ emsg+    Right (v, "") -> if v <= 0x10FFFF+                     then return $ chr v+                     else failBad "\\UHHHHHHHH format is limited to a maximum of \\U0010FFFF"+    Right (_, vs) -> failBad $ "Internal error: hex8 remainder = " ++ T.unpack vs+        +--------------------------------------------------------------------------------+--+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke+--  All rights reserved.+--+--  This file is part of Swish.+--+--  Swish is free software; you can redistribute it and/or modify+--  it under the terms of the GNU General Public License as published by+--  the Free Software Foundation; either version 2 of the License, or+--  (at your option) any later version.+--+--  Swish is distributed in the hope that it will be useful,+--  but WITHOUT ANY WARRANTY; without even the implied warranty of+--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+--  GNU General Public License for more details.+--+--  You should have received a copy of the GNU General Public License+--  along with Swish; if not, write to:+--    The Free Software Foundation, Inc.,+--    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+--+--------------------------------------------------------------------------------
src/Swish/RDF/Proof.hs view
@@ -1,279 +1,357 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE UndecidableInstances #-}+ -------------------------------------------------------------------------------- --  See end of this file for licence information. -------------------------------------------------------------------------------- -- | --  Module      :  Proof---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke --  License     :  GPL V2 -- --  Maintainer  :  Douglas Burke --  Stability   :  experimental---  Portability :  H98------  This module defines a framework for constructing proofs---  over some expression form.  It is intended to be used---  with RDF graphs, but the structures aim to be quite---  generic with respect to the expression forms allowed.+--  Portability :  FlexibleInstances, UndecidableInstances -----  It does not define any proof-finding strategy.+--  This module instantiates the 'Proof' framework for+--  constructing proofs over 'RDFGraph' expressions.+--  The intent is that this can be used to test some+--  correspondences between the RDF Model theory and+--  corresponding proof theory based on closure rules+--  applied to the graph, per <http://www.w3.org/TR/rdf-mt/>. -- --------------------------------------------------------------------------------  module Swish.RDF.Proof-    ( Proof(..), Step(..)-    , checkProof, explainProof, checkStep, showProof, showsProof, showsFormula )+    ( RDFProof, RDFProofStep+    , makeRDFProof, makeRDFProofStep+    , makeRdfInstanceEntailmentRule+    , makeRdfSubgraphEntailmentRule+    , makeRdfSimpleEntailmentRule ) where -import Swish.RDF.Ruleset (Ruleset(..))+import Swish.GraphClass (Label(..), LDGraph(..))+import Swish.Namespace (ScopedName)+import Swish.Proof (Proof(..), Step(..))+import Swish.Rule (Expression(..), Rule(..))+import Swish.VarBinding (makeVarBinding) -import Swish.RDF.Rule-    ( Expression(..), Formula(..), Rule(..)-    , showsFormula, showsFormulae )+import Swish.RDF.Graph (RDFLabel(..), RDFGraph)+import Swish.RDF.Graph (merge, allLabels, remapLabelList)+import Swish.RDF.Query (rdfQueryInstance, rdfQuerySubs)+import Swish.RDF.Ruleset (RDFFormula, RDFRule, RDFRuleset) -import Swish.Utils.ShowM (ShowM(..))+import Swish.Utils.ListHelpers (subset, flist) -import Swish.Utils.ListHelpers (subset)+import Data.List (subsequences)+import Data.LookupMap (makeLookupMap, mapFind)+import Data.Monoid (Monoid(..)) -import Data.List (union, intersect, intercalate, foldl')-import Data.Maybe (catMaybes, isNothing)+------------------------------------------------------------+--  Type instantiation of Proof framework for RDFGraph data+------------------------------------------------------------+--+--  This is a partial instantiation of the proof framework.+--  Details for applying inference rules are specific to the+--  graph instance type.  ---------------------------------------------------------------  Proof framework+--  Proof datatypes for graph values ------------------------------------------------------------ --- |Step in proof chain------  The display name for a proof step comes from the display name of its---  consequence formula.-data Step ex = Step-    { stepRule :: Rule ex           -- ^ Inference rule used-    , stepAnt  :: [Formula ex]      -- ^ Antecedents of inference rule-    , stepCon  :: Formula ex        -- ^ Named consequence of inference rule-    } deriving Show+-- The following is an orphan instance --- |Proof is a structure that presents a chain of rule applications---  that yield a result expression from a given expression-data Proof ex = Proof-    { proofContext :: [Ruleset ex]  -- ^ Proof context:  list of rulesets,-                                    --   each of which provides a number of-                                    --   axioms and rules.-    , proofInput   :: Formula ex    -- ^ Given expression-    , proofResult  :: Formula ex    -- ^ Result expression-    , proofChain   :: [Step ex]     -- ^ Chain of inference rule applications-                                    --   progressing from input to result-    }+-- |Instances of 'LDGraph' are also instance of the+--  @Expression@ class, for which proofs can be constructed.+--  The empty RDF graph is always @True@ (other enduring+--  truths are asserted as axioms).+instance (Label lb, LDGraph lg lb) => Expression (lg lb) where+    isValid = null . getArcs  --- |Return a list of axioms from all the rulesets in a proof-proofAxioms :: Proof a -> [Formula a]-proofAxioms = concatMap rsAxioms . proofContext+------------------------------------------------------------+--  Define RDF-specific types for proof framework+------------------------------------------------------------ --- |Return a list of rules from all the rulesets in a proof-proofRules :: Proof a -> [Rule a]-proofRules = concatMap rsRules . proofContext+-- | An RDF proof.+type RDFProof     = Proof RDFGraph --- |Return list of axioms actually referenced by a proof-proofAxiomsUsed :: Proof ex -> [Formula ex]-proofAxiomsUsed proof = foldl' union [] $ map stepAxioms (proofChain proof)-    where-        stepAxioms st = stepAnt st `intersect` proofAxioms proof+-- | A step in an RDF proof.+type RDFProofStep = Step RDFGraph --- |Check consistency of given proof.---  The supplied rules and axioms are assumed to be correct.-checkProof :: (Expression ex) => Proof ex -> Bool-checkProof pr =-    checkProof1 (proofRules pr) initExpr (proofChain pr) goalExpr-    where-        initExpr = formExpr (proofInput pr) : map formExpr (proofAxioms pr)-        goalExpr = formExpr $ proofResult pr+------------------------------------------------------------+--  Helper functions for constructing proofs on RDF graphs+------------------------------------------------------------ -checkProof1 :: (Expression ex) => [Rule ex] -> [ex] -> [Step ex] -> ex -> Bool-checkProof1 _     prev []       res = res `elem` prev-checkProof1 rules prev (st:steps) res =-    checkStep rules prev st &&-    checkProof1 rules (formExpr (stepCon st):prev) steps res+-- |Make an RDF graph proof step.+--+makeRDFProofStep ::+    RDFRule  -- ^ rule to use for this step+    -> [RDFFormula] -- ^ antecedent RDF formulae for this step+    -> RDFFormula -- ^ RDF formula that is the consequent for this step +    -> RDFProofStep+makeRDFProofStep rul ants con = Step+    { stepRule = rul+    , stepAnt  = ants+    , stepCon  = con+    } --- | A proof step is valid if rule is in list of rules---  and the antecedents are sufficient to obtain the conclusion---  and the antecedents are in the list of formulae already proven.+-- |Make an RDF proof. -----  Note:  this function depends on the ruleName of any rule being---  unique among all rules.  In particular the name of the step rule---  being in correspondence with the name of one of the indicated---  valid rules of inference.-checkStep :: -  (Expression ex) -  => [Rule ex]   -- ^ rules-  -> [ex]        -- ^ antecedants-  -> Step ex     -- ^ the step to validate-  -> Bool        -- ^ @True@ if the step is valid+makeRDFProof ::+    [RDFRuleset]      -- ^ RDF rulesets that constitute a proof context for this proof+    -> RDFFormula     -- ^ initial statement from which the goal is claimed to be proven+    -> RDFFormula     -- ^ statement that is claimed to be proven+    -> [RDFProofStep] -- ^ the chain of inference rules in the proof.+    -> RDFProof+makeRDFProof rsets base goal steps = Proof+    { proofContext = rsets+    , proofInput   = base+    , proofResult  = goal+    , proofChain   = steps+    } -checkStep rules prev step = isNothing $ explainStep rules prev step+------------------------------------------------------------+--  RDF instance entailment inference rule+------------------------------------------------------------ -{-+-- |Make an inference rule dealing with RDF instance entailment;+--  i.e. entailments that are due to replacement of a URI or literal+--  node with a blank node.+--+--  The part of this rule expected to be useful is 'checkInference'.+--  The 'fwdApply' and 'bwdApply' functions defined here may return+--  rather large results if applied to graphs with many variables or+--  a large vocabulary, and are defined for experimentation.+--+--  Forward and backward chaining is performed with respect to a+--  specified vocabulary.  In the case of backward chaining, it would+--  otherwise be impossible to bound the options thus generated.+--  In the case of forward chaining, it is often not desirable to+--  have the properties generalized.  If forward or backward backward+--  chaining will not be used, supply an empty vocabulary.+--  Note:  graph method 'Swish.RDF.Graph.allNodes' can be used to obtain a list of all+--  the subjects and objects used in a  graph, not counting nested+--  formulae;  use a call of the form:+--+--  >  allNodes (not . labelIsVar) graph+--+makeRdfInstanceEntailmentRule :: +  ScopedName     -- ^ name+  -> [RDFLabel]  -- ^ vocabulary+  -> RDFRule+makeRdfInstanceEntailmentRule name vocab = newrule+    where+        newrule = Rule+            { ruleName = name+            , fwdApply = rdfInstanceEntailFwdApply vocab+            , bwdApply = rdfInstanceEntailBwdApply vocab+            , checkInference = rdfInstanceEntailCheckInference+            } -Is the following an optimisation of the above?+--  Instance entailment forward chaining+--+--  Note:  unless the initial graph is small, the total result+--  here could be very large.  The existential generalizations are+--  sequenced in increasing number of substitutions applied.+--+--  The instances generated are all copies of the merge of the+--  supplied graphs, with some or all of the non-variable nodes+--  replaced by blank nodes.+rdfInstanceEntailFwdApply :: [RDFLabel] -> [RDFGraph] -> [RDFGraph]+rdfInstanceEntailFwdApply vocab ante =+    let+        --  Merge antecedents to single graph, renaming bnodes if needed.+        --  (Null test and using 'foldl1' to avoid merging if possible.)+        mergeGraph  = if null ante then mempty+                        else foldl1 merge ante+        --  Obtain lists of variable and non-variable nodes+        --  (was: nonvarNodes = allLabels (not . labelIsVar) mergeGraph)+        nonvarNodes = vocab+        varNodes    = allLabels labelIsVar mergeGraph+        --  Obtain list of possible remappings for non-variable nodes+        mapList     = remapLabelList nonvarNodes varNodes+        mapSubLists = (tail . subsequences) mapList+        mapGr ls = fmap (\l -> mapFind l l (makeLookupMap ls))+    in+        --  Return all remappings of the original merged graph+        flist (map mapGr mapSubLists) mergeGraph -checkStep rules prev step =-    -- Rule name is one of supplied rules, and-    (ruleName srul `elem` map ruleName rules) &&-    -- Antecedent expressions are all previously accepted expressions-    (sant `subset` prev)   &&-    -- Inference rule yields concequence from antecendents-    checkInference srul sant scon-    where-        --  Rule from proof step:-        srul = stepRule step-        --  Antecedent expressions from proof step:-        sant = map formExpr $ stepAnt step-        --  Consequentent expression from proof step:-        scon = formExpr $ stepCon step--}+--  Instance entailment backward chaining (for specified vocabulary)+--+--  [[[TODO:  this is an incomplete implementation, there being no+--  provision for instantiating some variables and leaving others+--  alone.  This can be overcome in many cases by combining instance+--  and subgraph chaining.+--  Also, there is no provision for instantiating some variables in+--  a triple and leaving others alone.  This may be fixed later if+--  this function is really needed to be completely faithful to the+--  precise notion of instance entailment.]]]+rdfInstanceEntailBwdApply :: [RDFLabel] -> RDFGraph -> [[RDFGraph]]+rdfInstanceEntailBwdApply vocab cons =+    let+        --  Obtain list of variable nodes+        varNodes     = allLabels labelIsVar cons+        --  Generate a substitution for each combination of variable+        --  and vocabulary node.+        varBindings  = map (makeVarBinding . zip varNodes) vocSequences+        vocSequences = powerSequencesLen (length varNodes) vocab+    in+        --  Generate a substitution for each combination of variable+        --  and vocabulary:+        [ rdfQuerySubs [v] cons | v <- varBindings ] +--  Instance entailment inference checker+rdfInstanceEntailCheckInference :: [RDFGraph] -> RDFGraph -> Bool+rdfInstanceEntailCheckInference ante cons =+    let+        mante = if null ante then mempty        -- merged antecedents+                    else foldl1 merge ante+        qvars = rdfQueryInstance cons mante     -- all query matches+        bsubs = rdfQuerySubs qvars cons         -- all back substitutions+    in+        --  Return True if any back-substitution matches the original+        --  merged antecendent graph.+        elem mante bsubs -{--    (formExpr (stepCon step) `elem` sfwd)-    -- (or $ map (`subset` sant) sbwd)-    where-        --  Rule from proof step:-        srul = stepRule step-        --  Antecedent expressions from proof step:-        sant = map formExpr $ stepAnt step-        --  Forward chaining from antecedents of proof step-        scon = map formExpr $ stepCon step-        --  Forward chaining from antecedents of proof step+------------------------------------------------------------+--  Powersequence (?) -- all sequences from some base values+------------------------------------------------------------ -        sfwd = fwdApply srul sant-        --  Backward chaining from consequent of proof step-        --  (Does not work because of introduction of existentials)-        sbwd = bwdApply srul (formExpr $ stepCon step)--}+-- |Construct list of lists of sequences of increasing length+powerSeqBylen :: [a] -> [[a]] -> [[[a]]]+powerSeqBylen rs ps = ps : powerSeqBylen rs (powerSeqNext rs ps) --- |Check proof. If there is an error then return information--- about the failing step.-explainProof ::-    (Expression ex) => Proof ex -> Maybe String-explainProof pr =-    explainProof1 (proofRules pr) initExpr (proofChain pr) goalExpr-    where-        initExpr = formExpr (proofInput pr) : map formExpr (proofAxioms pr)-        goalExpr = formExpr $ proofResult pr+-- |Return sequences of length n+1 given original sequence+--  and list of all sequences of length n+powerSeqNext :: [a] -> [[a]] -> [[a]]+powerSeqNext rs rss = [ h:t | t <- rss, h <- rs ] -explainProof1 ::-    (Expression ex) => [Rule ex] -> [ex] -> [Step ex] -> ex -> Maybe String-explainProof1 _     prev []       res   =-    if res `elem` prev then Nothing else Just "Result not demonstrated"-explainProof1 rules prev (st:steps) res =-    case explainStep rules prev st  of-        Nothing -> explainProof1 rules (formExpr (stepCon st):prev) steps res-        Just ex -> Just ("Invalid step: "++show (formName $ stepCon st)++": "++ex)+-- |Return all powersequences of a given length+powerSequencesLen :: Int -> [a] -> [[a]]+powerSequencesLen len rs = powerSeqBylen rs [[]] !! len --- | A proof step is valid if rule is in list of rules---  and the antecedents are sufficient to obtain the conclusion---  and the antecedents are in the list of formulae already proven.+--  Instance entailment notes. -----  Note:  this function depends on the ruleName of any rule being---  unique among all rules.  In particular the name of the step rule---  being in correspondence with the name of one of the indicated---  valid rules of inference.+--  Relation to simple entailment (s-entails): ---explainStep :: -  (Expression ex) -  => [Rule ex]  -- ^ rules-  -> [ex]       -- ^ previous-  -> Step ex    -- ^ step-  -> Maybe String -- ^ @Nothing@ if step is okay, otherwise a string indicating the error-explainStep rules prev step =-        if null errors then Nothing else Just $ intercalate ", " errors-    where-        --  Rule from proof step:-        srul = stepRule step-        --  Antecedent expressions from proof step:-        sant = map formExpr $ stepAnt step-        --  Consequentent expression from proof step:-        scon = formExpr $ stepCon step-        --  Tests for step to be valid-        errors = catMaybes-            -- Rule name is one of supplied rules, and-            [ require (ruleName srul `elem` map ruleName rules)-                      ("rule "++show (ruleName srul)++" not present")-            -- Antecedent expressions are all previously accepted expressions-            , require (sant `subset` prev)-                      "antecedent not axiom or previous result"-            -- Inference rule yields consequence from antecedents-            , require (checkInference srul sant scon)-                      "rule does not deduce consequence from antecedents"-            ]-        require b s = if b then Nothing else Just s---- |Create a displayable form of a proof, returned as a `ShowS` value.+--  (1) back-substitution yields original graph+--  ex:s1 ex:p1 ex:o1  s-entails  ex:s1 ex:p1 _:o1  by [_:o1/ex:o1] -----  This function is intended to allow the calling function some control---  of multiline displays by providing:+--  (2) back-substitution yields original graph+--  ex:s1 ex:p1 ex:o1  s-entails  ex:s1 ex:p1 _:o2  by [_:o2/ex:o1]+--  ex:s1 ex:p1  _:o1             ex:s1 ex:p1 _:o3     [_:o3/_:o1] -----  (1) the first line of the proof is not preceded by any text, so---      it may be appended to some preceding text on the same line,+--  (3) back-substitution does not yield original graph+--  ex:s1 ex:p1 ex:o1  s-entails  ex:s1 ex:p1 _:o2  by [_:o2/ex:o1]+--  ex:s1 ex:p1  _:o1             ex:s1 ex:p1 _:o3     [_:o3/ex:o1] -----  (2) the supplied newline string is used to separate lines of the---      formatted text, and may include any desired indentation, and+--  (4) consider+--  ex:s1 ex:p1 ex:o1  s-entails  ex:s1 ex:p1 ex:o1+--  ex:s1 ex:p1 ex:o2             ex:s1 ex:p1 ex:o2+--  ex:s1 ex:p1 ex:o3             ex:s1 ex:p1 _:o1+--                                ex:s1 ex:p1 _:o2+--  where [_:o1/ex:o1,_:o2/ex:o2] yields a simple entailment but not+--  an instance entailment, but [_:o1/ex:o3,_:o2/ex:o3] is also+--  (arguably) an instance entailment.  Therefore, it is not sufficient+--  to look only at the "largest" substitutions to determine instance+--  entailment. -----  (3) no newline is output following the final line of text.-showsProof :: -  (ShowM ex) -  => String    -- ^ newline string-  -> Proof ex -  -> ShowS-showsProof newline proof =-    if null axioms then shProof else shAxioms . shProof+--  All this means that when checking for instance entailment by+--  back substitution, all of the query results must be checked.+--  This seems clumsy.  If this function is heavily used with+--  multiple query matches, a modified query that uses each+--  triple of the target graph exactly once may be required.++------------------------------------------------------------+--  RDF subgraph entailment inference rule+------------------------------------------------------------++-- |Make an inference rule dealing with RDF subgraph entailment.+--  The part of this rule expected to be useful is 'checkInference'.+--  The 'fwdApply' function defined here may return rather large+--  results.  But in the name of completeness and experimentation+--  with the possibilities of lazy evaluation, it has been defined.+--+--  Backward chaining is not performed, as there is no reasonable way+--  to choose a meaningful supergraph of that supplied.+makeRdfSubgraphEntailmentRule :: ScopedName -> RDFRule+makeRdfSubgraphEntailmentRule name = newrule     where-        axioms = proofAxiomsUsed proof-        shAxioms =-            showString    ("Axioms:" ++ newline) .-            showsFormulae newline (proofAxiomsUsed proof) newline-        shProof =-            showString    ("Input:" ++ newline) .-            showsFormula  newline (proofInput  proof) .-            showString    (newline ++ "Proof:" ++ newline) .-            showsSteps    newline (proofChain  proof)+        newrule = Rule+            { ruleName = name+            , fwdApply = rdfSubgraphEntailFwdApply+            , bwdApply = const []+            , checkInference = rdfSubgraphEntailCheckInference+            } --- |Returns a simple string representation of a proof.-showProof :: -  (ShowM ex) -  => String    -- ^ newline string-  -> Proof ex -  -> String-showProof newline proof = showsProof newline proof ""+--  Subgraph entailment forward chaining+--+--  Note:  unless the initial graph is small, the total result+--  here could be very large.  The subgraphs are sequenced in+--  increasing size of the sub graph.+rdfSubgraphEntailFwdApply :: [RDFGraph] -> [RDFGraph]+rdfSubgraphEntailFwdApply ante =+    let+        --  Merge antecedents to single graph, renaming bnodes if needed.+        --  (Null test and using 'foldl1' to avoid merging if possible.)+        mergeGraph  = if null ante then mempty+                        else foldl1 merge ante+    in+        --  Return all subgraphs of the full graph constructed above+        map (setArcs mergeGraph) (init $ tail $ subsequences $ getArcs mergeGraph) --- |Create a displayable form of a list of labelled proof steps-showsSteps :: (ShowM ex) => String -> [Step ex] -> ShowS-showsSteps _       []     = id-showsSteps newline [s]    = showsStep  newline s-showsSteps newline (s:ss) = showsStep  newline s .-                            showString newline .-                            showsSteps newline ss+--  Subgraph entailment inference checker+--+--  This is of dubious utiltiy, as it doesn't allow for node renaming.+--  The simple entailment inference rule is probably more useful here.+rdfSubgraphEntailCheckInference :: [RDFGraph] -> RDFGraph -> Bool+rdfSubgraphEntailCheckInference ante cons =+    let+        --  Combine antecedents to single graph, renaming bnodes if needed.+        --  (Null test and using 'foldl1' to avoid merging if possible.)+        fullGraph  = if null ante then mempty+                        else foldl1 addGraphs ante+    in+        --  Check each consequent graph arc is in the antecedent graph+        getArcs cons `subset` getArcs fullGraph --- |Create a displayable form of a labelled proof step.-showsStep :: (ShowM ex) => String -> Step ex -> ShowS-showsStep newline s = showsFormula newline (stepCon s) .-                      showString newline .-                      showString ("  (by ["++rulename++"] from "++antnames++")")+------------------------------------------------------------+--  RDF simple entailment inference rule+------------------------------------------------------------++-- |Make an inference rule dealing with RDF simple entailment.+--  The part of this rule expected to be useful is 'checkInference'.+--  The 'fwdApply' and 'bwdApply' functions defined return null+--  results, indicating that they are not useful for the purposes+--  of proof discovery.+makeRdfSimpleEntailmentRule :: ScopedName -> RDFRule+makeRdfSimpleEntailmentRule name = newrule     where-        rulename = show . ruleName $ stepRule s-        antnames = showNames $ map (show . formName) (stepAnt s)+        newrule = Rule+            { ruleName = name+            , fwdApply = const []+            , bwdApply = const []+            , checkInference = rdfSimpleEntailCheckInference+            } --- |Return a string containing a list of names.-showNames :: [String] -> String-showNames []      = "<nothing>"-showNames [n]     = showName n-showNames [n1,n2] = showName n1 ++ " and " ++ showName n2-showNames (n1:ns) = showName n1 ++ ", " ++ showNames ns+--  Simple entailment inference checker+--+--  Note:  antecedents here are presumed to share bnodes.+--         (Use 'merge' instead of 'add' for non-shared bnodes)+--+rdfSimpleEntailCheckInference :: [RDFGraph] -> RDFGraph -> Bool+rdfSimpleEntailCheckInference ante cons =+    let agr = if null ante then mempty else foldl1 addGraphs ante+    in not $ null $ rdfQueryInstance cons agr --- |Return a string representing a single name.-showName :: String -> String-showName n = "["++n++"]"+{- original..+        not $ null $ rdfQueryInstance cons (foldl1 merge ante)+-}  -------------------------------------------------------------------------------- -----  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke   --  All rights reserved. -- --  This file is part of Swish.
+ src/Swish/RDF/ProofContext.hs view
@@ -0,0 +1,852 @@+{-# LANGUAGE OverloadedStrings #-}++--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  ProofContext+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke+--  License     :  GPL V2+--+--  Maintainer  :  Douglas Burke+--  Stability   :  experimental+--  Portability :  OverloadedStrings+--+--  This module contains proof-context declarations based on+--  the RDF, RDFS, and RDF datatyping semantics specifications.+--  These definitions consist of namespaces (for identification+--  in proofs), axioms and inference rules.+--+--------------------------------------------------------------------------------++module Swish.RDF.ProofContext ( rulesetRDF +                              , rulesetRDFS+                              , rulesetRDFD) where++import Swish.Datatype (typeMkCanonicalForm)+import Swish.Namespace (Namespace, makeNSScopedName)+import Swish.QName (LName)+import Swish.Ruleset (makeRuleset)+import Swish.VarBinding (VarBindingModify(..))+import Swish.VarBinding (applyVarBinding, addVarBinding, makeVarFilterModify, varFilterDisjunction)++import Swish.RDF.BuiltIn.Datatypes (findRDFDatatype)++import Swish.RDF.Proof (makeRdfSubgraphEntailmentRule+                       , makeRdfSimpleEntailmentRule )++import Swish.RDF.Ruleset +    ( RDFFormula, RDFRule, RDFRuleset +    , makeRDFFormula+    , makeN3ClosureRule+    , makeN3ClosureSimpleRule+    , makeN3ClosureModifyRule+    , makeN3ClosureAllocatorRule+    , makeNodeAllocTo )++import Swish.RDF.VarBinding+    ( RDFVarBinding+    , RDFVarBindingModify+    , RDFVarBindingFilter+    , rdfVarBindingUriRef, rdfVarBindingBlank+    , rdfVarBindingLiteral+    , rdfVarBindingUntypedLiteral +    , rdfVarBindingXMLLiteral, rdfVarBindingDatatyped+    , rdfVarBindingMemberProp+    )++import Swish.RDF.Graph (RDFLabel(..), isUri)++import Swish.RDF.Vocabulary+    ( namespaceRDFD+    , scopeRDF+    , scopeRDFS+    , scopeRDFD+    )++import Data.Monoid (Monoid(..))++import qualified Data.Text.Lazy.Builder as B++------------------------------------------------------------+--  Define query binding filter auxiliaries+------------------------------------------------------------++makeFormula :: Namespace -> LName -> B.Builder -> RDFFormula+makeFormula = makeRDFFormula++requireAny :: [RDFVarBindingFilter] -> RDFVarBindingFilter+requireAny = varFilterDisjunction++isLiteralV :: String -> RDFVarBindingFilter+isLiteralV = rdfVarBindingLiteral . Var++isUntypedLitV :: String -> RDFVarBindingFilter+isUntypedLitV = rdfVarBindingUntypedLiteral . Var++isXMLLitV :: String -> RDFVarBindingFilter+isXMLLitV = rdfVarBindingXMLLiteral . Var++isUriRefV :: String -> RDFVarBindingFilter+isUriRefV = rdfVarBindingUriRef . Var++isBlankV :: String -> RDFVarBindingFilter+isBlankV = rdfVarBindingBlank . Var++isDatatypedV :: String -> String -> RDFVarBindingFilter+isDatatypedV d l = rdfVarBindingDatatyped (Var d) (Var l)++isMemberPropV :: String -> RDFVarBindingFilter+isMemberPropV = rdfVarBindingMemberProp . Var++allocateTo :: String -> String -> [RDFLabel] -> RDFVarBindingModify+allocateTo bv av = makeNodeAllocTo (Var bv) (Var av)++--  Create new binding for datatype+valueSame :: String -> String -> String -> String -> RDFVarBindingModify+valueSame val1 typ1 val2 typ2 =+    sameDatatypedValue (Var val1) (Var typ1) (Var val2) (Var typ2)++--  Variable binding modifier to create new binding to a canonical+--  form of a datatyped literal.+sameDatatypedValue ::+    RDFLabel -> RDFLabel -> RDFLabel -> RDFLabel -> RDFVarBindingModify+sameDatatypedValue val1 typ1 val2 typ2 = VarBindingModify+        { vbmName   = makeNSScopedName namespaceRDFD "sameValue"+        , vbmApply  = sameDatatypedValueApplyAll val1 typ1 val2 typ2+        , vbmVocab  = [val1,typ1,val2,typ2]+        , vbmUsage  = [[val2]]+        }++sameDatatypedValueApplyAll ::+    RDFLabel -> RDFLabel -> RDFLabel -> RDFLabel+    -> [RDFVarBinding]+    -> [RDFVarBinding]+sameDatatypedValueApplyAll val1 typ1 val2 typ2 =+    concatMap (sameDatatypedValueApply val1 typ1 val2 typ2) ++--  Auxiliary function that handles variable binding updates+--  for sameDatatypedValue+sameDatatypedValueApply ::+    RDFLabel -> RDFLabel -> RDFLabel -> RDFLabel+    -> RDFVarBinding+    -> [RDFVarBinding]+sameDatatypedValueApply val1 typ1 val2 typ2 vbind =+    result+    where+        v1    = applyVarBinding vbind val1+        t1    = applyVarBinding vbind typ1+        t2    = applyVarBinding vbind typ2+        sametype = getCanonical v1 t1 t2+        result   =+            if isUri t1 && isUri t2 then+                if t1 == t2 then+                    case sametype of+                      Just st -> [addVarBinding val2 st vbind]+                      _ -> []+                else+                    error "subtype conversions not yet defined"+            else+                []++{-+getCanonical :: RDFLabel -> RDFLabel -> RDFLabel -> Maybe RDFLabel+getCanonical v1 t1 t2 =+    if isDatatyped dqn1 v1 && isJust mdt1 then+        liftM mkLit $ typeMkCanonicalForm dt1 (getLiteralText v1)+    else+        Nothing+    where+        dqn1  = getRes t1+        dqn2  = getRes t2+        mdt1  = findRDFDatatype dqn1+        dt1   = fromJust mdt1+        mkLit = flip Lit (Just dqn2)++        getRes (Res dqnam) = dqnam+        getRes x = error $ "Expected a Resource, sent " ++ show x -- for -Wall+-}++getCanonical :: RDFLabel -> RDFLabel -> RDFLabel -> Maybe RDFLabel+getCanonical (TypedLit v dt) (Res dqn1) (Res dqn2) =+    if dt == dqn1+    then case findRDFDatatype dqn1 of+           Just dt1 -> flip TypedLit dqn2 `fmap` typeMkCanonicalForm dt1 v+           _ -> Nothing++    else Nothing++getCanonical _                _          _  = Nothing++++{- -- Test data+qnamint = ScopedName namespaceXSD "integer"+xsdint  = Res qnamint+lab010  = Lit "010" (Just qnamint)+can010  = getCanonical lab010 xsdint xsdint+nsex    = Namespace "ex" "http://example.org/"+resexp  = Res (ScopedName nsex "p")+resexs  = Res (ScopedName nsex "s")++vara = Var "a"+varb = Var "b"+varc = Var "c"+vard = Var "d"+varp = Var "p"+vars = Var "s"+vart = Var "t"++vb1  = makeVarBinding [(vara,lab010),(varb,xsdint),(vard,xsdint)]+vb2  = sameDatatypedValueApply vara varb varc vard vb1+vb3  = vbmApply (sameDatatypedValue vara varb varc vard) [vb1]+vb3t = vb3 == vb2+vb4  = vbmApply (valueSame "a" "b" "c" "d") [vb1]+vb4t = vb4 == vb2+vb5  = vbmApply (valueSame "a" "b" "c" "b") [vb1]+vb5t = vb5 == vb2++vb6  = makeVarBinding [(vars,lab010),(varp,resexp),(vara,resexs),(vard,xsdint)]+vb7  = vbmApply (valueSame "s" "d" "t" "d") [vb6]+vb8  = makeVarBinding [(vars,lab010),(varp,resexp),(vara,resexs),(vard,xsdint)+                      ,(vart,fromJust can010)]+vb8t = vb7 == [vb8]+-- -}++------------------------------------------------------------+--  Common definitions+------------------------------------------------------------++------------------------------------------------------------+--  Define RDF axioms+------------------------------------------------------------++-- scopeRDF  = Namespace "rs-rdf"  "http://id.ninebynine.org/2003/Ruleset/rdf#"++--  RDF axioms (from RDF semantics document, section 3.1)+--+--  (See also, container property rules below)+--+rdfa1 :: RDFFormula+rdfa1 = makeFormula scopeRDF "a1" "rdf:type      rdf:type rdf:Property ."++rdfa2 :: RDFFormula+rdfa2 = makeFormula scopeRDF "a2" "rdf:subject   rdf:type rdf:Property ."++rdfa3 :: RDFFormula+rdfa3 = makeFormula scopeRDF "a3" "rdf:predicate rdf:type rdf:Property ."++rdfa4 :: RDFFormula+rdfa4 = makeFormula scopeRDF "a4" "rdf:object    rdf:type rdf:Property ."++rdfa5 :: RDFFormula+rdfa5 = makeFormula scopeRDF "a5" "rdf:first     rdf:type rdf:Property ."++rdfa6 :: RDFFormula+rdfa6 = makeFormula scopeRDF "a6" "rdf:rest      rdf:type rdf:Property ."++rdfa7 :: RDFFormula+rdfa7 = makeFormula scopeRDF "a7" "rdf:value     rdf:type rdf:Property ."++rdfa8 :: RDFFormula+rdfa8 = makeFormula scopeRDF "a8" "rdf:nil       rdf:type rdf:List ."++axiomsRDF :: [RDFFormula]+axiomsRDF =+    [ rdfa1,  rdfa2,  rdfa3,  rdfa4,  rdfa5+    , rdfa6,  rdfa7,  rdfa8+    ]++------------------------------------------------------------+--  Define RDF rules+------------------------------------------------------------++--  RDF subgraph entailment (from RDF semantics document section 2)+--+rdfsub :: RDFRule +rdfsub = makeRdfSubgraphEntailmentRule (makeNSScopedName scopeRDF "sub")++--  RDF simple entailment (from RDF semantics document section 7.1)+--  (Note: rules se1 and se2 are combined here, because the scope of+--  the "allocatedTo" modifier is the application of a single rule.)+--+rdfse :: RDFRule+rdfse = makeRdfSimpleEntailmentRule (makeNSScopedName scopeRDF "se")++--  RDF bnode-for-literal assignments (from RDF semantics document section 7.1)+--+rdflg :: RDFRule+rdflg = makeN3ClosureAllocatorRule scopeRDF "lg"+            "?x  ?a ?l . "+            "?x  ?a ?b . ?b rdf:_allocatedTo ?l ."+            (makeVarFilterModify $ isLiteralV "l")+            (allocateTo "b" "l")++--  RDF bnode-for-literal back-tracking (from RDF semantics document section 7.1)+--+rdfgl :: RDFRule+rdfgl = makeN3ClosureSimpleRule scopeRDF "gl"+            "?x  ?a ?l . ?b rdf:_allocatedTo ?l . "+            "?x  ?a ?b ."++--  RDF entailment rules (from RDF semantics document section 7.2)+--+--  (Note, statements with property rdf:_allocatedTo are introduced to+--  track bnodes introduced according to rule rdflf [presumably this+--  is actually rdflg?])+--+rdfr1 :: RDFRule+rdfr1 = makeN3ClosureSimpleRule scopeRDF "r1"+            "?x ?a ?y ."+            "?a rdf:type rdf:Property ."++rdfr2 :: RDFRule+rdfr2 = makeN3ClosureRule scopeRDF "r2"+            "?x  ?a ?b . ?b rdf:_allocatedTo ?l . "+            "?b rdf:type rdf:XMLLiteral ."+            (makeVarFilterModify $ isXMLLitV "l")++--  Container property axioms (from RDF semantics document section 3.1)+--+--  (Using here an inference rule with a filter in place of an axiom schema)+--+--  This is a restricted form of the given axioms, in that the axioms+--  are asserted only for container membership terms that appear in+--  the graph.+--+--  (This may be very inefficient for forward chaining when dealing with+--  large graphs:  may need to look at query logic to see if the search for+--  container membership properties can be optimized.  This may call for a+--  custom inference rule.)+--+rdfcp1 :: RDFRule+rdfcp1 = makeN3ClosureRule scopeRDF "cp1"+            "?x  ?c ?y . "+            "?c rdf:type rdf:Property ."+            (makeVarFilterModify $ isMemberPropV "c")++rdfcp2 :: RDFRule+rdfcp2 = makeN3ClosureRule scopeRDF "cp2"+            "?c  ?p ?y . "+            "?c rdf:type rdf:Property ."+            (makeVarFilterModify $ isMemberPropV "c")++rdfcp3 :: RDFRule+rdfcp3 = makeN3ClosureRule scopeRDF "cp3"+            "?x  ?p ?c . "+            "?c rdf:type rdf:Property ."+            (makeVarFilterModify $ isMemberPropV "c")++--  Collect RDF rules+--+rulesRDF :: [RDFRule]+rulesRDF =+    [ rdfsub,     rdfse+    , rdflg,      rdfgl+    , rdfr1,      rdfr2+    , rdfcp1,     rdfcp2,     rdfcp3+    ]++-- | Ruleset for RDF inference.++rulesetRDF :: RDFRuleset+rulesetRDF = makeRuleset scopeRDF axiomsRDF rulesRDF++------------------------------------------------------------+--  Define RDFS axioms+------------------------------------------------------------++-- scopeRDFS = Namespace "rdfs" "http://id.ninebynine.org/2003/Ruleset/rdfs#"++--  RDFS axioms (from RDF semantics document, section 4.1)+--+--  (See also, container property rules below)+--++rdfsa01 :: RDFFormula+rdfsa01 = makeFormula scopeRDFS "a01"+    "rdf:type           rdfs:domain rdfs:Resource ."++rdfsa02 :: RDFFormula+rdfsa02 = makeFormula scopeRDFS "a02"+    "rdf:type           rdfs:range  rdfs:Class ."++rdfsa03 :: RDFFormula+rdfsa03 = makeFormula scopeRDFS "a03"+    "rdfs:domain        rdfs:domain rdf:Property ."++rdfsa04 :: RDFFormula+rdfsa04 = makeFormula scopeRDFS "a04"+    "rdfs:domain        rdfs:range  rdfs:Class ."++rdfsa05 :: RDFFormula+rdfsa05 = makeFormula scopeRDFS "a05"+    "rdfs:range         rdfs:domain rdf:Property ."++rdfsa06 :: RDFFormula+rdfsa06 = makeFormula scopeRDFS "a06"+    "rdfs:range         rdfs:range  rdfs:Class ."++rdfsa07 :: RDFFormula+rdfsa07 = makeFormula scopeRDFS "a07"+    "rdfs:subPropertyOf rdfs:domain rdf:Property ."++rdfsa08 :: RDFFormula+rdfsa08 = makeFormula scopeRDFS "a08"+    "rdfs:subPropertyOf rdfs:range  rdf:Property ."++rdfsa09 :: RDFFormula+rdfsa09 = makeFormula scopeRDFS "a09"+    "rdfs:subClassOf    rdfs:domain rdfs:Class ."++rdfsa10 :: RDFFormula+rdfsa10 = makeFormula scopeRDFS "a10"+    "rdfs:subClassOf    rdfs:range  rdfs:Class ."++rdfsa11 :: RDFFormula+rdfsa11 = makeFormula scopeRDFS "a11"+    "rdf:subject        rdfs:domain rdf:Statement ."++rdfsa12 :: RDFFormula+rdfsa12 = makeFormula scopeRDFS "a12"+    "rdf:subject        rdfs:range  rdfs:Resource ."++rdfsa13 :: RDFFormula+rdfsa13 = makeFormula scopeRDFS "a13"+    "rdf:predicate      rdfs:domain rdf:Statement ."++rdfsa14 :: RDFFormula+rdfsa14 = makeFormula scopeRDFS "a14"+    "rdf:predicate      rdfs:range  rdfs:Resource ."++rdfsa15 :: RDFFormula+rdfsa15 = makeFormula scopeRDFS "a15"+    "rdf:object         rdfs:domain rdf:Statement ."++rdfsa16 :: RDFFormula+rdfsa16 = makeFormula scopeRDFS "a16"+    "rdf:object         rdfs:range  rdfs:Resource ."++rdfsa17 :: RDFFormula+rdfsa17 = makeFormula scopeRDFS "a17"+    "rdfs:member        rdfs:domain rdfs:Resource ."++rdfsa18 :: RDFFormula+rdfsa18 = makeFormula scopeRDFS "a18"+    "rdfs:member        rdfs:range  rdfs:Resource ."++rdfsa19 :: RDFFormula+rdfsa19 = makeFormula scopeRDFS "a19"+    "rdf:first          rdfs:domain rdf:List ."++rdfsa20 :: RDFFormula+rdfsa20 = makeFormula scopeRDFS "a20"+    "rdf:first          rdfs:range  rdfs:Resource ."++rdfsa21 :: RDFFormula+rdfsa21 = makeFormula scopeRDFS "a21"+    "rdf:rest           rdfs:domain rdf:List ."++rdfsa22 :: RDFFormula+rdfsa22 = makeFormula scopeRDFS "a22"+    "rdf:rest           rdfs:range  rdf:List ."++rdfsa23 :: RDFFormula+rdfsa23 = makeFormula scopeRDFS "a23"+    "rdfs:seeAlso       rdfs:domain rdfs:Resource ."++rdfsa24 :: RDFFormula+rdfsa24 = makeFormula scopeRDFS "a24"+    "rdfs:seeAlso       rdfs:range  rdfs:Resource ."++rdfsa25 :: RDFFormula+rdfsa25 = makeFormula scopeRDFS "a25"+    "rdfs:isDefinedBy   rdfs:domain rdfs:Resource ."++rdfsa26 :: RDFFormula+rdfsa26 = makeFormula scopeRDFS "a26"+    "rdfs:isDefinedBy   rdfs:range  rdfs:Resource ."++rdfsa27 :: RDFFormula+rdfsa27 = makeFormula scopeRDFS "a27"+    "rdfs:isDefinedBy   rdfs:subPropertyOf rdfs:seeAlso ."++rdfsa28 :: RDFFormula+rdfsa28 = makeFormula scopeRDFS "a28"+    "rdfs:comment       rdfs:domain rdfs:Resource ."++rdfsa29 :: RDFFormula+rdfsa29 = makeFormula scopeRDFS "a29"+    "rdfs:comment       rdfs:range  rdfs:Literal ."++rdfsa30 :: RDFFormula+rdfsa30 = makeFormula scopeRDFS "a30"+    "rdfs:label         rdfs:domain rdfs:Resource ."++rdfsa31 :: RDFFormula+rdfsa31 = makeFormula scopeRDFS "a31"+    "rdfs:label         rdfs:range  rdfs:Literal ."++rdfsa32 :: RDFFormula+rdfsa32 = makeFormula scopeRDFS "a32"+    "rdf:value          rdfs:domain rdfs:Resource ."++rdfsa33 :: RDFFormula+rdfsa33 = makeFormula scopeRDFS "a33"+    "rdf:value          rdfs:range  rdfs:Resource ."++rdfsa34 :: RDFFormula+rdfsa34 = makeFormula scopeRDFS "a34"+    "rdf:Alt            rdfs:subClassOf    rdfs:Container ."++rdfsa35 :: RDFFormula+rdfsa35 = makeFormula scopeRDFS "a35"+    "rdf:Bag            rdfs:subClassOf    rdfs:Container ."++rdfsa36 :: RDFFormula+rdfsa36 = makeFormula scopeRDFS "a36"+    "rdf:Seq            rdfs:subClassOf    rdfs:Container ."++rdfsa37 :: RDFFormula+rdfsa37 = makeFormula scopeRDFS "a37"+    "rdfs:ContainerMembershipProperty rdfs:subClassOf rdf:Property ."++rdfsa38 :: RDFFormula+rdfsa38 = makeFormula scopeRDFS "a38"+    "rdf:XMLLiteral     rdf:type           rdfs:Datatype ."++rdfsa39 :: RDFFormula+rdfsa39 = makeFormula scopeRDFS "a39"+    "rdf:XMLLiteral     rdfs:subClassOf    rdfs:Literal ."++rdfsa40 :: RDFFormula+rdfsa40 = makeFormula scopeRDFS "a40"+    "rdfs:Datatype      rdfs:subClassOf    rdfs:Class ."++axiomsRDFS :: [RDFFormula]+axiomsRDFS =+    [          rdfsa01, rdfsa02, rdfsa03, rdfsa04+    , rdfsa05, rdfsa06, rdfsa07, rdfsa08, rdfsa09+    , rdfsa10, rdfsa11, rdfsa12, rdfsa13, rdfsa14+    , rdfsa15, rdfsa16, rdfsa17, rdfsa18, rdfsa19+    , rdfsa20, rdfsa21, rdfsa22, rdfsa23, rdfsa24+    , rdfsa25, rdfsa26, rdfsa27, rdfsa28, rdfsa29+    , rdfsa30, rdfsa31, rdfsa32, rdfsa33, rdfsa34+    , rdfsa35, rdfsa36, rdfsa37, rdfsa38, rdfsa39+    , rdfsa40+    ]++------------------------------------------------------------+--  Define RDFS rules+------------------------------------------------------------++{-+rdfr2 = makeN3ClosureRule scopeRDF "r2"+            "?x  ?a ?b . ?b rdf:_allocatedTo ?l . "+            "?b rdf:type rdf:XMLLiteral ."+            (makeVarFilterModify $ isXMLLit "?l")+-}++--  RDFS entailment rules (from RDF semantics document section 7.2)+--+--  (Note, statements with property rdf:_allocatedTo are introduced to+--  track bnodes introduced according to rule rdflf [presumably this+--  is actually rdflg?])+--+rdfsr1 :: RDFRule+rdfsr1 = makeN3ClosureRule scopeRDFS "r1"+            "?x  ?a ?b . ?b rdf:_allocatedTo ?l . "+            "?b rdf:type rdfs:Literal ."+            (makeVarFilterModify $ isUntypedLitV "l" )++rdfsr2 :: RDFRule+rdfsr2 = makeN3ClosureSimpleRule scopeRDFS "r2"+            "?x ?a ?y . ?a rdfs:domain ?z ."+            "?x rdf:type ?z ."++rdfsr3 :: RDFRule+rdfsr3 = makeN3ClosureRule scopeRDFS "r3"+            "?u ?a ?v . ?a rdfs:range ?z ."+            "?v rdf:type ?z ."+            (makeVarFilterModify $ requireAny [isUriRefV "v",isBlankV "v"])++rdfsr4a :: RDFRule+rdfsr4a = makeN3ClosureSimpleRule scopeRDFS "r4a"+            "?x ?a ?y ."+            "?x rdf:type rdfs:Resource ."++rdfsr4b :: RDFRule+rdfsr4b = makeN3ClosureRule scopeRDFS "r4b"+            "?x ?a ?u ."+            "?u rdf:type rdfs:Resource ."+            (makeVarFilterModify $ requireAny [isUriRefV "u",isBlankV "u"])++rdfsr5 :: RDFRule+rdfsr5  = makeN3ClosureSimpleRule scopeRDFS "r5"+            "?a rdfs:subPropertyOf ?b . ?b rdfs:subPropertyOf ?c ."+            "?a rdfs:subPropertyOf ?c ."++rdfsr6 :: RDFRule+rdfsr6  = makeN3ClosureSimpleRule scopeRDFS "r6"+            "?x rdf:type rdf:Property ."+            "?x rdfs:subPropertyOf ?x ."++rdfsr7 :: RDFRule+rdfsr7  = makeN3ClosureSimpleRule scopeRDFS "r7"+            "?x ?a ?y . ?a rdfs:subPropertyOf ?b ."+            "?x ?b ?y ."++rdfsr8 :: RDFRule+rdfsr8  = makeN3ClosureSimpleRule scopeRDFS "r8"+            "?x rdf:type rdfs:Class ."+            "?x rdfs:subClassOf rdfs:Resource ."++rdfsr9 :: RDFRule+rdfsr9  = makeN3ClosureSimpleRule scopeRDFS "r9"+            "?x rdfs:subClassOf ?y . ?a rdf:type ?x ."+            "?a rdf:type ?y ."++rdfsr10 :: RDFRule+rdfsr10 = makeN3ClosureSimpleRule scopeRDFS "r10"+            "?x rdf:type rdfs:Class ."+            "?x rdfs:subClassOf ?x ."++rdfsr11 :: RDFRule+rdfsr11 = makeN3ClosureSimpleRule scopeRDFS "r11"+            "?x rdfs:subClassOf ?y . ?y rdfs:subClassOf ?z ."+            "?x rdfs:subClassOf ?z ."++rdfsr12 :: RDFRule+rdfsr12 = makeN3ClosureSimpleRule scopeRDFS "r12"+            "?x rdf:type rdfs:ContainerMembershipProperty ."+            "?x rdfs:subPropertyOf rdfs:member ."++rdfsr13 :: RDFRule+rdfsr13 = makeN3ClosureSimpleRule scopeRDFS "r13"+            "?x rdf:type rdfs:Datatype ."+            "?x rdfs:subClassOf rdfs:Literal ."++--  These are valid only under an extensional strengthening of RDFS,+--  discussed in section 7.3.1 of the RDF semantics specification:++{-+rdfsrext1 :: RDFRule+rdfsrext1 = makeN3ClosureSimpleRule scopeRDFS "ext1"+            "?x rdfs:domain ?y . ?y rdfs:subClassOf ?z ."+            "?x rdfs:domain ?z ."++rdfsrext2 :: RDFRule+rdfsrext2 = makeN3ClosureSimpleRule scopeRDFS "ext2"+            "?x rdfs:range ?y . ?y rdfs:subClassOf ?z ."+            "?x rdfs:range ?z ."++rdfsrext3 :: RDFRule+rdfsrext3 = makeN3ClosureSimpleRule scopeRDFS "ext3"+            "?x rdfs:domain ?y . ?z rdfs:subPropertyOf ?x ."+            "?z rdfs:domain ?y ."++rdfsrext4 :: RDFRule+rdfsrext4 = makeN3ClosureSimpleRule scopeRDFS "ext4"+            "?x rdfs:range ?y . ?z rdfs:subPropertyOf ?x ."+            "?z rdfs:range ?y ."++rdfsrext5 :: RDFRule+rdfsrext5 = makeN3ClosureSimpleRule scopeRDFS "ext5"+            "rdf:type rdfs:subPropertyOf ?z . ?z rdfs:domain ?y ."+            "rdfs:Resource rdfs:subClassOf ?y ."++rdfsrext6 :: RDFRule+rdfsrext6 = makeN3ClosureSimpleRule scopeRDFS "rext6"+            "rdfs:subClassOf rdfs:subPropertyOf ?z . ?z rdfs:domain ?y ."+            "rdfs:Class rdfs:subClassOf ?y ."++rdfsrext7 :: RDFRule+rdfsrext7 = makeN3ClosureSimpleRule scopeRDFS "rext7"+            "rdfs:subPropertyOf rdfs:subPropertyOf ?z . ?z rdfs:domain ?y ."+            "rdfs:Property rdfs:subClassOf ?y ."++rdfsrext8 :: RDFRule+rdfsrext8 = makeN3ClosureSimpleRule scopeRDFS "rext8"+            "rdfs:subClassOf rdfs:subPropertyOf ?z . ?z rdfs:range ?y ."+            "rdfs:Class rdfs:subClassOf ?y ."++rdfsrext9 :: RDFRule+rdfsrext9 = makeN3ClosureSimpleRule scopeRDFS "rext9"+            "rdfs:subPropertyOf rdfs:subPropertyOf ?z . ?z rdfs:range ?y ."+            "rdfs:Property rdfs:subClassOf ?y ."++-}++--  Container property axioms (from RDF semantics document section 4.1)+--+--  (Using here an inference rule with a filter in place of an axiom schema)+--+--  This is a restricted form of the given axioms, in that the axioms+--  are asserted only for container membership terms that appear in+--  the graph.+--+--  (This may be very inefficient for forward chaining when dealing with+--  large graphs:  may need to look at query logic to see if the search for+--  container membership properties can be optimized.  This may call for a+--  custom inference rule.)+--+rdfscp11 :: RDFRule+rdfscp11 = makeN3ClosureRule scopeRDFS "cp11"+            "?x  ?c ?y . "+            "?c rdf:type rdfs:ContainerMembershipProperty ."+            (makeVarFilterModify $ isMemberPropV "c")++rdfscp12 :: RDFRule+rdfscp12 = makeN3ClosureRule scopeRDFS "cp12"+            "?c  ?p ?y . "+            "?c rdf:type rdfs:ContainerMembershipProperty ."+            (makeVarFilterModify $ isMemberPropV "c")++rdfscp13 :: RDFRule+rdfscp13 = makeN3ClosureRule scopeRDFS "cp13"+            "?x  ?p ?c . "+            "?c rdf:type rdfs:ContainerMembershipProperty ."+            (makeVarFilterModify $ isMemberPropV "c")++rdfscp21 :: RDFRule+rdfscp21 = makeN3ClosureRule scopeRDFS "cp21"+            "?x  ?c ?y . "+            "?c rdfs:domain rdfs:Resource ."+            (makeVarFilterModify $ isMemberPropV "c")++rdfscp22 :: RDFRule+rdfscp22 = makeN3ClosureRule scopeRDFS "cp22"+            "?c  ?p ?y . "+            "?c rdfs:domain rdfs:Resource ."+            (makeVarFilterModify $ isMemberPropV "c")++rdfscp23 :: RDFRule+rdfscp23 = makeN3ClosureRule scopeRDFS "cp23"+            "?x  ?p ?c . "+            "?c rdfs:domain rdfs:Resource ."+            (makeVarFilterModify $ isMemberPropV "c")++rdfscp31 :: RDFRule+rdfscp31 = makeN3ClosureRule scopeRDFS "cp31"+            "?x  ?c ?y . "+            "?c rdfs:range rdfs:Resource ."+            (makeVarFilterModify $ isMemberPropV "c")++rdfscp32 :: RDFRule+rdfscp32 = makeN3ClosureRule scopeRDFS "cp32"+            "?c  ?p ?y . "+            "?c rdfs:range rdfs:Resource ."+            (makeVarFilterModify $ isMemberPropV "c")++rdfscp33 :: RDFRule+rdfscp33 = makeN3ClosureRule scopeRDFS "cp33"+            "?x  ?p ?c . "+            "?c rdfs:range rdfs:Resource ."+            (makeVarFilterModify $ isMemberPropV "c")++--  Collect RDFS rules+--+rulesRDFS :: [RDFRule]+rulesRDFS =+    [ rdfsr1,    rdfsr2,    rdfsr3,    rdfsr4a,   rdfsr4b+    , rdfsr5,    rdfsr6,    rdfsr7,    rdfsr8,    rdfsr9+    , rdfsr10,   rdfsr11,   rdfsr12,   rdfsr13+    , rdfscp11,   rdfscp12,   rdfscp13+    , rdfscp21,   rdfscp22,   rdfscp23+    , rdfscp31,   rdfscp32,   rdfscp33+    ]++-- | Ruleset for RDFS inference.++rulesetRDFS :: RDFRuleset+rulesetRDFS = makeRuleset scopeRDFS axiomsRDFS rulesRDFS++------------------------------------------------------------+--  Define RDFD (datatyping) axioms+------------------------------------------------------------++-- scopeRDFD = Namespace "rdfd" "http://id.ninebynine.org/2003/Ruleset/rdfd#"++axiomsRDFD :: [RDFFormula]+axiomsRDFD =+    [+    ]++------------------------------------------------------------+--  Define RDFD (datatyping) axioms+------------------------------------------------------------++--  RDFD closure rules from semantics document, section 7.4++--  Infer type of datatyped literal+--+rdfdr1 :: RDFRule+rdfdr1 = makeN3ClosureRule scopeRDFD "r1"+            "?d rdf:type rdfs:Datatype . ?a ?p ?l . ?b rdf:_allocatedTo ?l . "+            "?b rdf:type ?d ."+            (makeVarFilterModify $ isDatatypedV "d" "l")++--  Equivalent literals with same datatype:+--  (generate canonical form, or operate in proof mode only)+--+rdfdr2 :: RDFRule+rdfdr2 = makeN3ClosureRule scopeRDFD "r2"+            "?d rdf:type rdfs:Datatype . ?a ?p ?s ."+            "?a ?p ?t ."+            (valueSame "s" "d" "t" "d")++{- Note that valueSame does datatype check.  Otherwise use:+rdfdr2 = makeN3ClosureModifyRule scopeRDFD "r2"+            "?d rdf:type rdfs:Datatype . ?a ?p ?s ."+            "?a ?p ?t ."+            (makeVarFilterModify $ isDatatypedV "d" "s")+            (valueSame "s" "d" "t" "d")+-}++--  Equivalent literals with different datatypes:+--  (generate canonical form, or operate in proof mode only)+--+rdfdr3 :: RDFRule+rdfdr3 = makeN3ClosureModifyRule scopeRDFD "r3"+            ( "?d rdf:type rdfs:Datatype . ?e rdf:type rdfs:Datatype . " `mappend`+              "?a ?p ?s ." )+            "?a ?p ?t ."+            (makeVarFilterModify $ isDatatypedV "s" "d")+            (valueSame "s" "d" "t" "e")++--  Collect RDFD rules+--+rulesRDFD :: [RDFRule]+rulesRDFD =+    [ rdfdr1, rdfdr2, rdfdr3+    ]++-- | Ruleset for RDFD (datatyping) inference.+--+rulesetRDFD :: RDFRuleset+rulesetRDFD = makeRuleset scopeRDFD axiomsRDFD rulesRDFD++--------------------------------------------------------------------------------+--+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke  +--  All rights reserved.+--+--  This file is part of Swish.+--+--  Swish is free software; you can redistribute it and/or modify+--  it under the terms of the GNU General Public License as published by+--  the Free Software Foundation; either version 2 of the License, or+--  (at your option) any later version.+--+--  Swish is distributed in the hope that it will be useful,+--  but WITHOUT ANY WARRANTY; without even the implied warranty of+--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+--  GNU General Public License for more details.+--+--  You should have received a copy of the GNU General Public License+--  along with Swish; if not, write to:+--    The Free Software Foundation, Inc.,+--    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+--+--------------------------------------------------------------------------------
+ src/Swish/RDF/Query.hs view
@@ -0,0 +1,651 @@+--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  Query+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke+--  License     :  GPL V2+--+--  Maintainer  :  Douglas Burke+--  Stability   :  experimental+--  Portability :  H98+--+--  This module defines functions for querying an RDF graph to obtain+--  a set of variable substitutions, and to apply a set of variable+--  substitutions to a query pattern to obtain a new graph.+--+--  It also defines a few primitive graph access functions.+--+--  A minimal example is shown below, where we query a very simple+--  graph:+--+-- >>> :m + Swish.RDF Swish.RDF.Parser.N3 Swish.RDF.Query+-- >>> :set -XOverloadedStrings+-- >>> let qparse = either error id . parseN3fromText+-- >>> let igr = qparse "@prefix a: <http://example.com/>. a:a a a:A ; a:foo a:bar. a:b a a:B ; a:foo a:bar."+-- >>> let qgr = qparse "?node a ?type."+-- >>> rdfQueryFind qgr igr+-- [[(?type,a:B),(?node,a:b)],[(?type,a:A),(?node,a:a)]]+-- >>> let bn = (toRDFLabel . Data.Maybe.fromJust . Network.URI.parseURI) "http://example.com/B"+-- >>> rdfFindArcs (rdfObjEq bn) igr+-- [(a:b,rdf:type,a:B)]+-- >>> Data.Maybe.mapMaybe (flip Swish.RDF.VarBinding.vbMap (Var "type")) $ rdfQueryFind qgr igr+-- [a:B,a:A]+-- +--------------------------------------------------------------------------------++module Swish.RDF.Query+    ( rdfQueryFind, rdfQueryFilter+    , rdfQueryBack, rdfQueryBackFilter, rdfQueryBackModify+    , rdfQueryInstance+    , rdfQuerySubs, rdfQueryBackSubs+    , rdfQuerySubsAll+    , rdfQuerySubsBlank, rdfQueryBackSubsBlank+    , rdfFindArcs, rdfSubjEq, rdfPredEq, rdfObjEq+    , rdfFindPredVal, rdfFindPredInt, rdfFindValSubj+    , rdfFindList+    -- * Utility routines+    , allp+    , anyp+    -- * Exported for testing+    , rdfQuerySubs2 )+where++import Swish.Datatype (DatatypeMap(..))+import Swish.VarBinding (VarBinding(..), VarBindingModify(..), VarBindingFilter(..))+import Swish.VarBinding (makeVarBinding, applyVarBinding, joinVarBindings)++import Swish.RDF.Graph+    ( Arc(..), LDGraph(..)+    , arcSubj, arcPred, arcObj+    , RDFLabel(..)+    , isDatatyped, isBlank, isQueryVar+    , getLiteralText, makeBlank+    , RDFTriple+    , RDFGraph+    , allLabels, remapLabels+    , resRdfFirst+    , resRdfRest+    , resRdfNil+    )++import Swish.RDF.VarBinding (RDFVarBinding, RDFVarBindingFilter)+import Swish.RDF.VarBinding (nullRDFVarBinding)++import Swish.RDF.Datatype.XSD.MapInteger (mapXsdInteger)++import Swish.RDF.Vocabulary (xsdInteger, xsdNonNegInteger)++import Swish.Utils.ListHelpers (flist)++import Control.Monad (when)+import Control.Monad.State (State, runState, modify)++import Data.Maybe (mapMaybe, isJust, fromJust)+import Data.Monoid (Monoid(..))++import qualified Data.Set as S+import qualified Data.Traversable as Traversable++------------------------------------------------------------+--  Primitive RDF graph queries+------------------------------------------------------------++-- | Basic graph-query function.+--+--  The triples of the query graph are matched sequentially+--  against the target graph, each taking account of any+--  variable bindings that have already been determined,+--  and adding new variable bindings as triples containing+--  query variables are matched against the graph.+--+rdfQueryFind :: +  RDFGraph -- ^ The query graph.+  -> RDFGraph -- ^ The target graph.+  -> [RDFVarBinding]+  -- ^ Each element represents a set of variable bindings that make the query graph a+  -- subgraph of the target graph. The list can be empty.+rdfQueryFind =+    rdfQueryPrim1 matchQueryVariable nullRDFVarBinding . getArcs++--  Helper function to match query against a graph.+--  A node-query function is supplied to determine how query nodes+--  are matched against target graph nodes.  Also supplied is+--  an initial variable binding.+--+rdfQueryPrim1 ::+    NodeQuery RDFLabel -> RDFVarBinding -> [Arc RDFLabel]+    -> RDFGraph+    -> [RDFVarBinding]+rdfQueryPrim1 _     initv []       _  = [initv]+rdfQueryPrim1 nodeq initv (qa:qas) tg =+    let+        qam  = fmap (applyVarBinding initv) qa      -- subst vars already bound+        newv = rdfQueryPrim2 nodeq qam tg           -- new bindings, or null+    in+        concat+            [ rdfQueryPrim1 nodeq v2 qas tg+            | v1 <- newv+            , let v2 = joinVarBindings initv v1+            ]++--  Match single query term against graph, and return any new sets+--  of variable bindings thus defined, or [] if the query term+--  cannot be matched.  Each of the RDFVarBinding values returned+--  represents an alternative possible match for the query arc.+--+rdfQueryPrim2 ::+    NodeQuery RDFLabel -> Arc RDFLabel+    -> RDFGraph+    -> [RDFVarBinding]+rdfQueryPrim2 nodeq qa tg =+        mapMaybe (getBinding nodeq qa) (getArcs tg)++-- |RDF query filter.+--+--  This function applies a supplied query binding+--  filter to the result from a call of 'rdfQueryFind'.+--+--  If none of the query bindings found satisfy the filter, a null+--  list is returned (which is what 'rdfQueryFind' returns if the+--  query cannot be satisfied).+--+--  (Because of lazy evaluation, this should be as efficient as+--  applying the filter as the search proceeds.  I started to build+--  the filter logic into the query function itself, with consequent+--  increase in complexity, until I remembered lazy evaluation lets+--  me keep things separate.)+--+rdfQueryFilter ::+    RDFVarBindingFilter -> [RDFVarBinding] -> [RDFVarBinding]+rdfQueryFilter qbf = filter (vbfTest qbf)++------------------------------------------------------------+--  Backward-chaining RDF graph queries+------------------------------------------------------------++-- |Reverse graph-query function.+--+--  Similar to 'rdfQueryFind', but with different success criteria.+--  The query graph is matched against the supplied graph,+--  but not every triple of the query is required to be matched.+--  Rather, every triple of the target graph must be matched,+--  and substitutions for just the variables thus bound are+--  returned.  In effect, these are subsitutions in the query+--  that entail the target graph (where @rdfQueryFind@ returns+--  substitutions that are entailed by the target graph).+--+--  Multiple substitutions may be used together, so the result+--  returned is a list of lists of query bindings.  Each inner+--  list contains several variable bindings that must all be applied+--  separately to the closure antecendents to obtain a collection of+--  expressions that together are antecedent to the supplied+--  conclusion.  A null list of bindings returned means the+--  conclusion can be inferred without any antecedents.+--+--  Note:  in back-chaining, the conditions required to prove each+--  target triple are derived independently, using the inference rule+--  for each such triple, so there are no requirements to check+--  consistency with previously determined variable bindings, as+--  there are when doing forward chaining.  A result of this is that+--  there may be redundant triples generated by the back-chaining+--  process.  Any process using back-chaining should deal with the+--  results returned accordingly.+--+--  An empty outer list is returned if no combination of+--  substitutions can infer the supplied target.+--+rdfQueryBack :: RDFGraph -> RDFGraph -> [[RDFVarBinding]]+rdfQueryBack qg tg =+    rdfQueryBack1 matchQueryVariable [] (getArcs qg) (getArcs tg)++rdfQueryBack1 ::+    NodeQuery RDFLabel -> [RDFVarBinding] -> [Arc RDFLabel] -> [Arc RDFLabel]+    -> [[RDFVarBinding]]+rdfQueryBack1 _     initv _   []       = [initv]+rdfQueryBack1 nodeq initv qas (ta:tas) = concat+    [ rdfQueryBack1 nodeq (nv:initv) qas tas+    | nv <- rdfQueryBack2 nodeq qas ta ]++--  Match a query against a single graph term, and return any new sets of+--  variable bindings thus defined.  Each member of the result is an+--  alternative possible set of variable bindings.  An empty list returned+--  means no match.+--+rdfQueryBack2 ::+    NodeQuery RDFLabel -> [Arc RDFLabel] -> Arc RDFLabel+    -> [RDFVarBinding]+rdfQueryBack2 nodeq qas ta =+    [ fromJust b | qa <- qas, let b = getBinding nodeq qa ta, isJust b ]++-- |RDF back-chaining query filter.  This function applies a supplied+--  query binding filter to the result from a call of 'rdfQueryBack'.+--+--  Each inner list contains bindings that must all be used to satisfy+--  the backchain query, so if any query binding does not satisfy the+--  filter, the entire corresponding row is removed+rdfQueryBackFilter ::+    RDFVarBindingFilter -> [[RDFVarBinding]] -> [[RDFVarBinding]]+rdfQueryBackFilter qbf = filter (all (vbfTest qbf))++-- |RDF back-chaining query modifier.  This function applies a supplied+--  query binding modifier to the result from a call of 'rdfQueryBack'.+--+--  Each inner list contains bindings that must all be used to satisfy+--  a backchaining query, so if any query binding does not satisfy the+--  filter, the entire corresponding row is removed+--+rdfQueryBackModify ::+    VarBindingModify a b -> [[VarBinding a b]] -> [[VarBinding a b]]+rdfQueryBackModify qbm = concatMap (rdfQueryBackModify1 qbm)++--  Auxiliary back-chaining query variable binding modifier function:+--  for a supplied list of variable bindings, all of which must be used+--  together when backchaining:+--  (a) make each list member into a singleton list+--  (b) apply the binding modifier to each such list, which may result+--      in a list with zero, one or more elements.+--  (c) return the  sequence of these, each member of which is+--      an alternative list of variable bindings, where the members of+--      each alternative must be used together.+--+rdfQueryBackModify1 ::+    VarBindingModify a b -> [VarBinding a b] -> [[VarBinding a b]]+rdfQueryBackModify1 qbm = mapM (vbmApply qbm . (:[]))++------------------------------------------------------------+--  Simple entailment graph query+------------------------------------------------------------++-- |Simple entailment (instance) graph query.+--+--  This function queries a graph to find instances of the+--  query graph in the target graph.  It is very similar+--  to the normal forward chaining query 'rdfQueryFind',+--  except that blank nodes rather than query variable nodes+--  in the query graph are matched against nodes in the target+--  graph.  Neither graph should contain query variables.+--+--  An instance is defined by the RDF semantics specification,+--  per <http://www.w3.org/TR/rdf-mt/>, and is obtained by replacing+--  blank nodes with URIs, literals or other blank nodes.  RDF+--  simple entailment can be determined in terms of instances.+--  This function looks for a subgraph of the target graph that+--  is an instance of the query graph, which is a necessary and+--  sufficient condition for RDF entailment (see the Interpolation+--  Lemma in RDF Semantics, section 1.2).+--+--  It is anticipated that this query function can be used in+--  conjunction with backward chaining to determine when the+--  search for sufficient antecendents to determine some goal+--  has been concluded.+rdfQueryInstance :: RDFGraph -> RDFGraph -> [RDFVarBinding]+rdfQueryInstance =+    rdfQueryPrim1 matchQueryBnode nullRDFVarBinding . getArcs++------------------------------------------------------------+--  Primitive RDF graph query support functions+------------------------------------------------------------++-- |Type of query node testing function.  Return value is:+--+--  * @Nothing@    if no match+--+--  * @Just True@  if match with new variable binding+--+--  * @Just False@ if match with new variable binding+--+type NodeQuery a = a -> a -> Maybe Bool++--  Extract query binding from matching a single query triple with a+--  target triple, returning:+--  - Nothing if the query is not matched+--  - Just nullVarBinding if there are no new variable bindings+--  - Just binding is a new query binding for this match+getBinding ::+    NodeQuery RDFLabel -> Arc RDFLabel -> Arc RDFLabel+    -> Maybe RDFVarBinding+getBinding nodeq (Arc s1 p1 o1) (Arc s2 p2 o2) =+    makeBinding [(s1,s2),(p1,p2),(o1,o2)] []+    where+        makeBinding [] bs = Just $ makeVarBinding bs+        makeBinding (vr@(v,r):bvrs) bs =+            case nodeq v r of+                Nothing    -> Nothing+                Just False -> makeBinding bvrs bs+                Just True  -> makeBinding bvrs (vr:bs)++--  Match variable node against target node, returning+--  Nothing if they do not match, Just True if a variable+--  node is matched (thereby creating a new variable binding)+--  or Just False if a non-blank node is matched.+matchQueryVariable :: NodeQuery RDFLabel+matchQueryVariable (Var _) _ = Just True+matchQueryVariable q t+    | q == t    = Just False+    | otherwise = Nothing++--  Match blank query node against target node, returning+--  Nothing if they do not match, Just True if a blank node+--  is matched (thereby creating a new equivalence) or+--  Just False if a non-blank node is matched.+matchQueryBnode :: NodeQuery RDFLabel+matchQueryBnode (Blank _) _ = Just True+matchQueryBnode q t+    | q == t    = Just False+    | otherwise = Nothing++------------------------------------------------------------+--  Substitute results from RDF query back into a graph+------------------------------------------------------------++-- |Graph substitution function.+--+--  Uses the supplied variable bindings to substitute variables in+--  a supplied graph, returning a list of result graphs corresponding+--  to each set of variable bindings applied to the input graph.+--  This function is used for formward chaining substitutions, and+--  returns only those result graphs for which all query variables+--  are bound.+rdfQuerySubs :: [RDFVarBinding] -> RDFGraph -> [RDFGraph]+rdfQuerySubs vars gr =+    map fst $ filter (null . snd) $ rdfQuerySubsAll vars gr++-- |Graph back-substitution function.+--+--  Uses the supplied variable bindings from 'rdfQueryBack' to perform+--  a series of variable substitutions in a supplied graph, returning+--  a list of lists of result graphs corresponding to each set of variable+--  bindings applied to the input graphs.+--+--  The outer list of the result contains alternative antecedent lists+--  that satisfy the query goal.  Each inner list contains graphs that+--  must all be inferred to satisfy the query goal.+rdfQueryBackSubs ::+    [[RDFVarBinding]] -> RDFGraph -> [[(RDFGraph,[RDFLabel])]]+rdfQueryBackSubs varss gr = [ rdfQuerySubsAll v gr | v <- varss ]++-- |Graph substitution function.+--+--  This function performs the substitutions and returns a list of+--  result graphs each paired with a list unbound variables in each.+rdfQuerySubsAll :: [RDFVarBinding] -> RDFGraph -> [(RDFGraph,[RDFLabel])]+rdfQuerySubsAll vars gr = [ rdfQuerySubs2 v gr | v <- vars ]++-- |Graph substitution function.+--+--  This function performs each of the substitutions in 'vars', and+--  replaces any nodes corresponding to unbound query variables+--  with new blank nodes.+rdfQuerySubsBlank :: [RDFVarBinding] -> RDFGraph -> [RDFGraph]+rdfQuerySubsBlank vars gr =+    [ remapLabels vs bs makeBlank g+    | v <- vars+    , let (g,vs) = rdfQuerySubs2 v gr+    , let bs     = allLabels isBlank g+    ]++-- |Graph back-substitution function, replacing variables with bnodes.+--+--  Uses the supplied variable bindings from 'rdfQueryBack' to perform+--  a series of variable substitutions in a supplied graph, returning+--  a list of lists of result graphs corresponding to each set of variable+--  bindings applied to the input graphs.+--+--  The outer list of the result contains alternative antecedent lists+--  that satisfy the query goal.  Each inner list contains graphs that+--  must all be inferred to satisfy the query goal.+rdfQueryBackSubsBlank :: [[RDFVarBinding]] -> RDFGraph -> [[RDFGraph]]+rdfQueryBackSubsBlank varss gr = [ rdfQuerySubsBlank v gr | v <- varss ]++-- |This function applies a substitution for a single set of variable+--  bindings, returning the result and a list of unbound variables.+--  It uses a state transformer monad to collect the list of+--  unbound variables.+--+--  Adding an empty graph forces elimination of duplicate arcs.+rdfQuerySubs2 :: RDFVarBinding -> RDFGraph -> (RDFGraph,[RDFLabel])+rdfQuerySubs2 varb gr = (addGraphs mempty g, S.toList vs)+    where+        (g,vs) = runState ( Traversable.traverse (mapNode varb) gr ) S.empty++--  Auxiliary monad function for rdfQuerySubs2.+--  This returns a state transformer Monad which in turn returns the+--  substituted node value based on the supplied query variable bindings.+--  The monad state is a set of labels which accumulates all those+--  variables seen for which no substitution was available.+mapNode :: RDFVarBinding -> RDFLabel -> State (S.Set RDFLabel) RDFLabel+mapNode varb lab =+    case vbMap varb lab of+        Just v  -> return v+        Nothing -> when (isQueryVar lab) (modify (S.insert lab)) >> return lab++------------------------------------------------------------+--  Simple lightweight query primitives+------------------------------------------------------------+--+--  [[[TODO:  modify above code to use these for all graph queries]]]++-- |Test if a value satisfies all predicates in a list+--+allp :: [a->Bool] -> a -> Bool+allp ps a = and (flist ps a)++{-+allptest0 = allp [(>=1),(>=2),(>=3)] 0     -- False+allptest1 = allp [(>=1),(>=2),(>=3)] 1     -- False+allptest2 = allp [(>=1),(>=2),(>=3)] 2     -- False+allptest3 = allp [(>=1),(>=2),(>=3)] 3     -- True+allptest  = and [not allptest0,not allptest1,not allptest2,allptest3]+-}++-- |Test if a value satisfies any predicate in a list+--+anyp :: [a->Bool] -> a -> Bool+anyp ps a = or (flist ps a)++{-+anyptest0 = anyp [(>=1),(>=2),(>=3)] 0     -- False+anyptest1 = anyp [(>=1),(>=2),(>=3)] 1     -- True+anyptest2 = anyp [(>=1),(>=2),(>=3)] 2     -- True+anyptest3 = anyp [(>=1),(>=2),(>=3)] 3     -- True+anyptest  = and [not anyptest0,anyptest1,anyptest2,anyptest3]+-}+++-- |Take a predicate on an+--  RDF statement and a graph, and returns all statements in the graph+--  satisfying that predicate.+--+--  Use combinations of these as follows:+--+--  * find all statements with given subject:+--          @rdfFindArcs (rdfSubjEq s)@+--+--  * find all statements with given property:+--          @rdfFindArcs (rdfPredEq p)@+--+--  * find all statements with given object:+--          @rdfFindArcs (rdfObjEq  o)@+--+--  * find all statements matching conjunction of these conditions:+--          @rdfFindArcs ('allp' [...])@+--+--  * find all statements matching disjunction of these conditions:+--          @rdfFindArcs ('anyp' [...])@+--+--  Custom predicates can also be used.+--+rdfFindArcs :: (RDFTriple -> Bool) -> RDFGraph -> [RDFTriple]+rdfFindArcs p = filter p . getArcs++-- |Test if statement has given subject+rdfSubjEq :: RDFLabel -> RDFTriple -> Bool+rdfSubjEq s = (s==) . arcSubj++-- |Test if statement has given predicate+rdfPredEq :: RDFLabel -> RDFTriple -> Bool+rdfPredEq p = (p==) . arcPred++-- |Test if statement has given object+rdfObjEq  :: RDFLabel -> RDFTriple -> Bool+rdfObjEq o  = (o==) . arcObj++{-+-- |Find statements with given subject+rdfFindSubj :: RDFLabel -> RDFGraph -> [RDFTriple]+rdfFindSubj s = rdfFindArcs (rdfSubjEq s)++-- |Find statements with given predicate+rdfFindPred :: RDFLabel -> RDFGraph -> [RDFTriple]+rdfFindPred p = rdfFindArcs (rdfPredEq p)+-}++-- |Find values of given predicate for a given subject+rdfFindPredVal :: +  RDFLabel    -- ^ subject+  -> RDFLabel -- ^ predicate+  -> RDFGraph +  -> [RDFLabel]+rdfFindPredVal s p = map arcObj . rdfFindArcs (allp [rdfSubjEq s,rdfPredEq p])++-- |Find integer values of a given predicate for a given subject+rdfFindPredInt :: +  RDFLabel     -- ^ subject+  -> RDFLabel  -- ^ predicate+  -> RDFGraph -> [Integer]+rdfFindPredInt s p = mapMaybe getint . filter isint . pvs+    where+        pvs = rdfFindPredVal s p+        isint  = anyp+            [ isDatatyped xsdInteger+            , isDatatyped xsdNonNegInteger+            ]+        getint = mapL2V mapXsdInteger . getLiteralText++-- |Find all subjects that match (subject, predicate, object) in the graph.+rdfFindValSubj :: +  RDFLabel     -- ^ predicate+  -> RDFLabel  -- ^ object+  -> RDFGraph +  -> [RDFLabel]+rdfFindValSubj p o = map arcSubj . rdfFindArcs (allp [rdfPredEq p,rdfObjEq o])++------------------------------------------------------------+--  List query+------------------------------------------------------------++-- |Return a list of nodes that comprise an rdf:collection value,+--  given the head element of the collection.  If the list is+--  ill-formed then an arbitrary value is returned.+--+rdfFindList :: RDFGraph -> RDFLabel -> [RDFLabel]+rdfFindList gr hd = findhead $ rdfFindList gr findrest+    where+        findhead  = headOr (const []) $+                    map (:) (rdfFindPredVal hd resRdfFirst gr)+        findrest  = headOr resRdfNil (rdfFindPredVal hd resRdfRest gr)+        {-+        findhead  = headOr (const [])+                    [ (ob:) | Arc _ sb ob <- subgr, sb == resRdfFirst ]+        findrest  = headOr resRdfNil+                    [ ob | Arc _ sb ob <- subgr, sb == resRdfRest  ]+        subgr     = filter ((==) hd . arcSubj) $ getArcs gr+        -}+        headOr    = foldr const+        -- headOr _ (x:_) = x+        -- headOr x []    = x++------------------------------------------------------------+--  Interactive tests+------------------------------------------------------------++{-+s1 = Blank "s1"+p1 = Blank "p1"+o1 = Blank "o1"+s2 = Blank "s2"+p2 = Blank "p2"+o2 = Blank "o2"+qs1 = Var "s1"+qp1 = Var "p1"+qo1 = Var "o1"+qs2 = Var "s2"+qp2 = Var "p2"+qo2 = Var "o2"++qa1 = Arc qs1 qp1 qo1+qa2 = Arc qs2 qp2 qo2+qa3 = Arc qs2  p2 qo2+ta1 = Arc s1 p1 o1+ta2 = Arc s2 p2 o2++g1  = toRDFGraph [ta1,ta2]+g2  = toRDFGraph [qa3]++gb1  = getBinding matchQueryVariable qa1 ta1    -- ?s1=_:s1, ?p1=_:p1, ?o1=_:o1+gvs1 = qbMap (fromJust gb1) qs1                 -- _:s1+gvp1 = qbMap (fromJust gb1) qp1                 -- _:p1+gvo1 = qbMap (fromJust gb1) qo1                 -- _:o1+gvs2 = qbMap (fromJust gb1) qs2                 -- Nothing++gb3  = getBinding matchQueryVariable qa3 ta1    -- Nothing+gb4  = getBinding matchQueryVariable qa3 ta2    -- ?s2=_:s1, ?o2=_:o1++mqvs1 = matchQueryVariable qs2 s1+mqvp1 = matchQueryVariable p2  p1++--  rdfQueryFind++qfa  = rdfQueryFind g2 g1++qp2a = rdfQueryPrim2 matchQueryVariable qa3 g1+-}++{- more tests++qb1a = rdfQueryBack1 [] [qa1] [ta1,ta2]+qb1 = rdfQueryBack1 [] [qa1,qa2] [ta1,ta2]+ql1 = length qb1+qv1 = map (qb1!!0!!0) [qs1,qp1,qo1,qs2,qp2,qo2]+qv2 = map (qb1!!0!!1) [qs1,qp1,qo1,qs2,qp2,qo2]+qv3 = map (qb1!!1!!0) [qs1,qp1,qo1,qs2,qp2,qo2]+qv4 = map (qb1!!1!!1) [qs1,qp1,qo1,qs2,qp2,qo2]+qv5 = map (qb1!!2!!0) [qs1,qp1,qo1,qs2,qp2,qo2]+qv6 = map (qb1!!2!!1) [qs1,qp1,qo1,qs2,qp2,qo2]+qv7 = map (qb1!!3!!0) [qs1,qp1,qo1,qs2,qp2,qo2]+qv8 = map (qb1!!3!!1) [qs1,qp1,qo1,qs2,qp2,qo2]++qb2 = rdfQueryBack2 matchQueryVariable [qa1,qa2] ta1+ql2 = length qb2+qv1 = map (qbMap $ head qb2)        [qs1,qp1,qo1,qs2,qp2,qo2]+qv2 = map (qbMap $ head $ tail qb2) [qs1,qp1,qo1,qs2,qp2,qo2]+qb3 = rdfQueryBack2 matchQueryVariable [qa1,qa3] ta1++-}+++--------------------------------------------------------------------------------+--+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke +--  All rights reserved.+--+--  This file is part of Swish.+--+--  Swish is free software; you can redistribute it and/or modify+--  it under the terms of the GNU General Public License as published by+--  the Free Software Foundation; either version 2 of the License, or+--  (at your option) any later version.+--+--  Swish is distributed in the hope that it will be useful,+--  but WITHOUT ANY WARRANTY; without even the implied warranty of+--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+--  GNU General Public License for more details.+--+--  You should have received a copy of the GNU General Public License+--  along with Swish; if not, write to:+--    The Free Software Foundation, Inc.,+--    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+--+--------------------------------------------------------------------------------
− src/Swish/RDF/RDFDatatype.hs
@@ -1,202 +0,0 @@------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  RDFDatatype---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  License     :  GPL V2------  Maintainer  :  Douglas Burke---  Stability   :  experimental---  Portability :  H98------  This module defines the structures used by Swish to represent and---  manipulate RDF datatypes.--------------------------------------------------------------------------------------module Swish.RDF.RDFDatatype-    ( RDFDatatype-    , RDFDatatypeVal-    , RDFDatatypeMod-    , RDFModifierFn, RDFApplyModifier-    , makeRdfDtOpenVarBindingModify, makeRdfDtOpenVarBindingModifiers-    , applyRDFDatatypeMod-    , RDFDatatypeSub-    , fromRDFLabel, toRDFLabel, makeDatatypedLiteral-    )-where--import Swish.RDF.RDFGraph-    ( RDFLabel(..)-    , isDatatyped-    , getLiteralText-    , RDFGraph-    )--import Swish.RDF.RDFVarBinding-    ( RDFVarBinding, RDFOpenVarBindingModify )--import Swish.RDF.Datatype-    ( Datatype -- , typeName, typeRules-    , DatatypeVal(..)-    , DatatypeMap(..)-    , DatatypeMod(..), ModifierFn-    , ApplyModifier-    , DatatypeSub(..)-    )--import Swish.RDF.VarBinding (VarBindingModify(..))-import Swish.Utils.Namespace (ScopedName)--import Control.Monad (liftM)-import Data.Maybe (fromMaybe, isJust, fromJust)--import qualified Data.Text as T-----------------------------------------------------------------  Specialize datatype framework types for use with RDF----------------------------------------------------------------- |RDF datatype wrapper used with RDF graph values----type RDFDatatype = Datatype RDFGraph RDFLabel RDFLabel---- |RDF datatype value used with RDF graph values----type RDFDatatypeVal vt = DatatypeVal RDFGraph vt RDFLabel RDFLabel---- |RDF datatype modifier used with RDF graph values----type RDFDatatypeMod vt = DatatypeMod vt RDFLabel RDFLabel---- |Describe a subtype/supertype relationship between a pair---  of RDF datatypes.----type RDFDatatypeSub supvt subvt = DatatypeSub RDFGraph RDFLabel RDFLabel supvt subvt---- |RDF value modifier function type------  This indicates a modifier function that operates on 'RDFLabel' values.----type RDFModifierFn = ModifierFn RDFLabel---- |RDF value modifier application function type------  This indicates a function that applies RDFModifierFn functions.----type RDFApplyModifier = ApplyModifier RDFLabel RDFLabel-------------------------------------------------------------------  Functions for creating datatype variable binding modifiers------------------------------------------------------------------- |Create an 'RDFOpenVarBindingModify' value.------  The key purpose of this function is to lift the supplied---  variable constraint functions from operating on data values directly---  to a corresponding list of functions that operate on values contained---  in RDF graph labels (i.e. RDF literal nodes).  It also applies---  node type checking, such that if the actual RDF nodes supplied do---  not contain appropriate values then the variable binding is not---  accepted.----makeRdfDtOpenVarBindingModify ::-    RDFDatatypeVal vt-    -- ^ is an 'RDFDatatype' value containing details of the datatype-    --   for which a variable binding modifier is created.-    -> RDFDatatypeMod vt -    -- ^ is the data value modifier value that defines the calculations-    --   that are used to implement a variable binding modifier.-    -> RDFOpenVarBindingModify-makeRdfDtOpenVarBindingModify dtval dtmod =-    dmAppf dtmod (dmName dtmod) $ map (makeRDFModifierFn dtval) (dmModf dtmod)---- |Create all RDFOpenVarBindingModify values for a given datatype value.---  See 'makeRdfDtOpenVarBindingModify'.----makeRdfDtOpenVarBindingModifiers ::-    RDFDatatypeVal vt -    -- ^  is an 'RDFDatatype' value containing details of the datatype-    --    for which variable binding modifiers are created.-    -> [RDFOpenVarBindingModify]-makeRdfDtOpenVarBindingModifiers dtval =-    map (makeRdfDtOpenVarBindingModify dtval) (tvalMod dtval)---- |Apply a datatype modifier using supplied RDF labels to a supplied---  RDF variable binding.----applyRDFDatatypeMod ::-    RDFDatatypeVal vt -> RDFDatatypeMod vt -> [RDFLabel] -> [RDFVarBinding]-    -> [RDFVarBinding]-applyRDFDatatypeMod dtval dtmod lbs =-    vbmApply (makeRdfDtOpenVarBindingModify dtval dtmod lbs)---- |Given details of a datatype and a single value constraint function,---  return a new constraint function that operates on 'RDFLabel' values.------  The returned constraint function incorporates checks for appropriately---  typed literal nodes, and returns similarly typed literal nodes.----makeRDFModifierFn ::-    RDFDatatypeVal vt -> ModifierFn vt -> RDFModifierFn-makeRDFModifierFn dtval fn ivs =-    let-        ivals = mapM (fromRDFLabel dtval) ivs-        ovals | isJust ivals = fn (fromJust ivals)-              | otherwise    = []-    in-        fromMaybe [] $ mapM (toRDFLabel dtval) ovals-----------------------------------------------------------------  Helpers to map between datatype values and RDFLabels----------------------------------------------------------------- | Convert from a typed literal to a Haskell value,--- with the possibility of failure.-fromRDFLabel ::-    RDFDatatypeVal vt -> RDFLabel -> Maybe vt-fromRDFLabel dtv lab-    | isDatatyped dtnam lab = mapL2V dtmap $ getLiteralText lab-    | otherwise             = Nothing-    where-        dtnam = tvalName dtv-        dtmap = tvalMap dtv---- | Convert a Haskell value to a typed literal (label),--- with the possibility of failure.-toRDFLabel :: RDFDatatypeVal vt -> vt -> Maybe RDFLabel-toRDFLabel dtv =-    liftM (makeDatatypedLiteral dtnam) . mapV2L dtmap-    where-        dtnam = tvalName dtv-        dtmap = tvalMap dtv---- | Create a typed literal from the given value.-makeDatatypedLiteral :: ScopedName -> T.Text -> RDFLabel-makeDatatypedLiteral dtnam txtval =-    Lit txtval (Just dtnam)----------------------------------------------------------------------------------------  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  All rights reserved.------  This file is part of Swish.------  Swish is free software; you can redistribute it and/or modify---  it under the terms of the GNU General Public License as published by---  the Free Software Foundation; either version 2 of the License, or---  (at your option) any later version.------  Swish is distributed in the hope that it will be useful,---  but WITHOUT ANY WARRANTY; without even the implied warranty of---  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the---  GNU General Public License for more details.------  You should have received a copy of the GNU General Public License---  along with Swish; if not, write to:---    The Free Software Foundation, Inc.,---    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA------------------------------------------------------------------------------------
− src/Swish/RDF/RDFDatatypeXsdDecimal.hs
@@ -1,487 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  RDFDatatypeXsdDecimal---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin,---                     2011 Douglas Burke---  License     :  GPL V2------  Maintainer  :  Douglas Burke---  Stability   :  experimental---  Portability :  OverloadedStrings------  This module defines the structures used by Swish to represent and---  manipulate RDF @xsd:double@ datatyped literals.--------------------------------------------------------------------------------------module Swish.RDF.RDFDatatypeXsdDecimal-    ( rdfDatatypeXsdDecimal-    , rdfDatatypeValXsdDecimal-    , typeNameXsdDecimal, namespaceXsdDecimal-    , axiomsXsdDecimal, rulesXsdDecimal-    , prefixXsdDecimal-    )-where--import Swish.RDF.RDFRuleset-    ( RDFFormula, RDFRule, RDFRuleset -    , makeRDFGraphFromN3Builder-    , makeRDFFormula-    )--import Swish.RDF.RDFDatatype-    ( RDFDatatype-    , RDFDatatypeVal-    , RDFDatatypeMod-    , makeRdfDtOpenVarBindingModifiers-    )--import Swish.RDF.ClassRestrictionRule (makeRDFDatatypeRestrictionRules)-import Swish.RDF.MapXsdDecimal (mapXsdDecimal)--import Swish.RDF.Datatype-    ( Datatype(..)-    , DatatypeVal(..)-    , DatatypeRel(..), DatatypeRelPr-    , altArgs-    , UnaryFnTable,    unaryFnApp-    , BinaryFnTable,   binaryFnApp-    , DatatypeMod(..) -    , makeVmod11inv, makeVmod11-    , makeVmod21inv, makeVmod21-    , makeVmod20-    )--import Swish.RDF.Ruleset (makeRuleset)--import Swish.Utils.Namespace (Namespace, ScopedName, namespaceToBuilder, makeNSScopedName)--import Swish.RDF.Vocabulary-    ( namespaceRDF-    , namespaceRDFS-    , namespaceRDFD-    , namespaceXSD-    , namespaceXsdType-    )--import Data.Monoid(Monoid(..))--import qualified Data.Text as T-import qualified Data.Text.Lazy.Builder as B-----------------------------------------------------------------  Misc values-----------------------------------------------------------------  Local name for Double datatype-nameXsdDecimal :: T.Text-nameXsdDecimal      = "decimal"---- |Type name for xsd:double datatype-typeNameXsdDecimal :: ScopedName-typeNameXsdDecimal  = makeNSScopedName namespaceXSD nameXsdDecimal---- |Namespace for xsd:double datatype functions-namespaceXsdDecimal :: Namespace-namespaceXsdDecimal = namespaceXsdType nameXsdDecimal-----------------------------------------------------------------  Declare exported RDFDatatype value for xsd:double---------------------------------------------------------------rdfDatatypeXsdDecimal :: RDFDatatype-rdfDatatypeXsdDecimal = Datatype rdfDatatypeValXsdDecimal-----------------------------------------------------------------  Implmentation of RDFDatatypeVal for xsd:double----------------------------------------------------------------- |Define Datatype value for @xsd:double@.------  Members of this datatype decimal values.------  The lexical form consists of an optional @+@ or @-@---  followed by a sequence of decimal digits.------  The canonical lexical form has leading zeros and @+@ sign removed.----rdfDatatypeValXsdDecimal :: RDFDatatypeVal Double-rdfDatatypeValXsdDecimal = DatatypeVal-    { tvalName      = typeNameXsdDecimal-    , tvalRules     = rdfRulesetXsdDecimal  -- Ruleset RDFGraph-    , tvalMkRules   = makeRDFDatatypeRestrictionRules rdfDatatypeValXsdDecimal-                                            -- RDFGraph -> [RDFRules]-    , tvalMkMods    = makeRdfDtOpenVarBindingModifiers rdfDatatypeValXsdDecimal-    , tvalMap       = mapXsdDecimal         -- DatatypeMap Double-    , tvalRel       = relXsdDecimal         -- [DatatypeRel Double]-    , tvalMod       = modXsdDecimal         -- [DatatypeMod Double]-    }---- |relXsdDecimal contains arithmetic and other relations for xsd:double values.------  The functions are inspired by those defined by CWM as math: properties---  (<http://www.w3.org/2000/10/swap/doc/CwmBuiltins.html>).----relXsdDecimal :: [DatatypeRel Double]-relXsdDecimal =-    [ relXsdDecimalAbs-    , relXsdDecimalNeg-    , relXsdDecimalSum-    , relXsdDecimalDiff-    , relXsdDecimalProd-    , relXsdDecimalPower-    , relXsdDecimalEq-    , relXsdDecimalNe-    , relXsdDecimalLt-    , relXsdDecimalLe-    , relXsdDecimalGt-    , relXsdDecimalGe-    ]--mkDecRel2 ::-    T.Text -> DatatypeRelPr Double -> UnaryFnTable Double-    -> DatatypeRel Double-mkDecRel2 nam pr fns = DatatypeRel-    { dtRelName = makeNSScopedName namespaceXsdDecimal nam-    , dtRelFunc = altArgs pr fns unaryFnApp-    }--mkDecRel3 ::-    T.Text -> DatatypeRelPr Double -> BinaryFnTable Double-    -> DatatypeRel Double-mkDecRel3 nam pr fns = DatatypeRel-    { dtRelName = makeNSScopedName namespaceXsdDecimal nam-    , dtRelFunc = altArgs pr fns binaryFnApp-    }--relXsdDecimalAbs :: DatatypeRel Double-relXsdDecimalAbs = mkDecRel2 "abs" (const True)-    [ ( (>=0),      [ (abs,1) ] )-    , ( const True, [ (id,0), (negate,0) ] )-    ]--relXsdDecimalNeg :: DatatypeRel Double-relXsdDecimalNeg = mkDecRel2 "neg" (const True)-    [ ( const True, [ (negate,1) ] )-    , ( const True, [ (negate,0) ] )-    ]--relXsdDecimalSum :: DatatypeRel Double-relXsdDecimalSum = mkDecRel3 "sum" (const True)-    [ ( const True, [ ((+),1,2) ] )-    , ( const True, [ ((-),0,2) ] )-    , ( const True, [ ((-),0,1) ] )-    ]--relXsdDecimalDiff :: DatatypeRel Double-relXsdDecimalDiff = mkDecRel3 "diff" (const True)-    [ ( const True, [ ((-),1,2) ] )-    , ( const True, [ ((+),0,2) ] )-    , ( const True, [ ((-),1,0) ] )-    ]--relXsdDecimalProd :: DatatypeRel Double-relXsdDecimalProd = mkDecRel3 "prod" (const True)-    [ ( const True, [ ((*),1,2) ] )-    , ( const True, [ ((/),0,2) ] )-    , ( const True, [ ((/),0,1) ] )-    ]--relXsdDecimalPower :: DatatypeRel Double-relXsdDecimalPower = mkDecRel3 "power" (const True)-    [ ( const True, [ ((**),1,2) ] )-    , ( const True, [ ] )-    , ( (>=0),      [ ] )-    ]--liftL2 :: (a->a->Bool) -> ([a]->a) -> ([a]->a) -> [a] -> Bool-liftL2 p i1 i2 as = p (i1 as) (i2 as)--lcomp :: (a->a->Bool) -> [a] -> Bool-lcomp p = liftL2 p head (head . tail)---- eq--relXsdDecimalEq :: DatatypeRel Double-relXsdDecimalEq = mkDecRel2 "eq" (lcomp (==))-    ( repeat (const True, []) )---- ne--relXsdDecimalNe :: DatatypeRel Double-relXsdDecimalNe = mkDecRel2 "ne" (lcomp (/=))-    ( repeat (const True, []) )---- lt--relXsdDecimalLt :: DatatypeRel Double-relXsdDecimalLt = mkDecRel2 "lt" (lcomp (<))-    ( repeat (const True, []) )---- le--relXsdDecimalLe :: DatatypeRel Double-relXsdDecimalLe = mkDecRel2 "le" (lcomp (<=))-    ( repeat (const True, []) )---- gt--relXsdDecimalGt :: DatatypeRel Double-relXsdDecimalGt = mkDecRel2 "gt" (lcomp (>))-    ( repeat (const True, []) )---- ge--relXsdDecimalGe :: DatatypeRel Double-relXsdDecimalGe = mkDecRel2 "ge" (lcomp (>=))-    ( repeat (const True, []) )---- |modXsdDecimal contains variable binding modifiers for xsd:double values.------  The functions are selected from those defined by CWM as math:---  properties---  (<http://www.w3.org/2000/10/swap/doc/CwmBuiltins.html>).----modXsdDecimal :: [RDFDatatypeMod Double]-modXsdDecimal =-    [ modXsdDecimalAbs-    , modXsdDecimalNeg-    , modXsdDecimalSum-    , modXsdDecimalDiff-    , modXsdDecimalProd-    , modXsdDecimalPower-    , modXsdDecimalEq-    , modXsdDecimalNe-    , modXsdDecimalLt-    , modXsdDecimalLe-    , modXsdDecimalGt-    , modXsdDecimalGe-    ]--modXsdDecimalAbs :: RDFDatatypeMod Double-modXsdDecimalAbs = DatatypeMod-    { dmName = makeNSScopedName namespaceXsdDecimal "abs"-    , dmModf = [ f0, f1 ]-    , dmAppf = makeVmod11-    }-    where-        f0 vs@[v1,v2] = if v1 == abs v2 then vs else []-        f0 _          = []-        f1 [v2]       = [abs v2]-        f1 _          = []--modXsdDecimalNeg :: RDFDatatypeMod Double-modXsdDecimalNeg = DatatypeMod-    { dmName = makeNSScopedName namespaceXsdDecimal "neg"-    , dmModf = [ f0, f1, f1 ]-    , dmAppf = makeVmod11inv-    }-    where-        f0 vs@[v1,v2] = if v1 == negate v2 then vs else []-        f0 _          = []-        f1 [vi]       = [-vi]-        f1 _          = []--modXsdDecimalSum :: RDFDatatypeMod Double-modXsdDecimalSum = DatatypeMod-    { dmName = makeNSScopedName namespaceXsdDecimal "sum"-    , dmModf = [ f0, f1, f2, f2 ]-    , dmAppf = makeVmod21inv-    }-    where-        f0 vs@[v1,v2,v3] = if v1 == v2+v3 then vs else []-        f0 _             = []-        f1 [v2,v3]       = [v2+v3]-        f1 _             = []-        f2 [v1,vi]       = [v1-vi]-        f2 _             = []--modXsdDecimalDiff :: RDFDatatypeMod Double-modXsdDecimalDiff = DatatypeMod-    { dmName = makeNSScopedName namespaceXsdDecimal "diff"-    , dmModf = [ f0, f1, f2, f3 ]-    , dmAppf = makeVmod21inv-    }-    where-        f0 vs@[v1,v2,v3] = if v1 == v2-v3 then vs else []-        f0 _             = []-        f1 [v2,v3]       = [v2-v3]-        f1 _             = []-        f2 [v1,v3]       = [v1+v3]-        f2 _             = []-        f3 [v1,v2]       = [v2-v1]-        f3 _             = []--modXsdDecimalProd :: RDFDatatypeMod Double-modXsdDecimalProd = DatatypeMod-    { dmName = makeNSScopedName namespaceXsdDecimal "prod"-    , dmModf = [ f0, f1, f2, f2 ]-    , dmAppf = makeVmod21inv-    }-    where-        f0 vs@[v1,v2,v3] = if v1 == v2*v3 then vs else []-        f0 _             = []-        f1 [v2,v3]       = [v2*v3]-        f1 _             = []-        f2 [v1,vi]       = [v1/vi]-        f2 _             = []--modXsdDecimalPower :: RDFDatatypeMod Double-modXsdDecimalPower = DatatypeMod-    { dmName = makeNSScopedName namespaceXsdDecimal "power"-    , dmModf = [ f0, f1 ]-    , dmAppf = makeVmod21-    }-    where-        f0 vs@[v1,v2,v3] = if v1 == (v2**v3 :: Double) then vs else []-        f0 _             = []-        f1 [v2,v3]       = [v2**v3 :: Double]-        f1 _             = []--modXsdDecimalEq, modXsdDecimalNe, modXsdDecimalLt, modXsdDecimalLe, modXsdDecimalGt, modXsdDecimalGe :: RDFDatatypeMod Double -modXsdDecimalEq = modXsdDecimalCompare "eq" (==)-modXsdDecimalNe = modXsdDecimalCompare "ne" (/=)-modXsdDecimalLt = modXsdDecimalCompare "lt" (<)-modXsdDecimalLe = modXsdDecimalCompare "le" (<=)-modXsdDecimalGt = modXsdDecimalCompare "gt" (>)-modXsdDecimalGe = modXsdDecimalCompare "ge" (>=)--modXsdDecimalCompare ::-    T.Text -> (Double->Double->Bool) -> RDFDatatypeMod Double-modXsdDecimalCompare nam rel = DatatypeMod-    { dmName = makeNSScopedName namespaceXsdDecimal nam-    , dmModf = [ f0 ]-    , dmAppf = makeVmod20-    }-    where-        f0 vs@[v1,v2] = if rel v1 v2 then vs else []-        f0 _          = []---- |rulesetXsdDecimal contains rules and axioms that allow additional---  deductions when xsd:double values appear in a graph.------  The rules defined here are concerned with basic decimal arithmetic---  operations: +, -, *, /, **------  makeRuleset :: Namespace -> [Formula ex] -> [Rule ex] -> Ruleset ex----rdfRulesetXsdDecimal :: RDFRuleset-rdfRulesetXsdDecimal =-    makeRuleset namespaceXsdDecimal axiomsXsdDecimal rulesXsdDecimal--mkPrefix :: Namespace -> B.Builder-mkPrefix = namespaceToBuilder--prefixXsdDecimal :: B.Builder-prefixXsdDecimal = -  mconcat-  [ mkPrefix namespaceRDF-  , mkPrefix namespaceRDFS-  , mkPrefix namespaceRDFD-  , mkPrefix namespaceXSD-  , mkPrefix namespaceXsdDecimal-  ]--mkAxiom :: T.Text -> B.Builder -> RDFFormula-mkAxiom local gr =-    makeRDFFormula namespaceXsdDecimal local (prefixXsdDecimal `mappend` gr)--axiomsXsdDecimal :: [RDFFormula]-axiomsXsdDecimal =-    [ mkAxiom "dt"      "xsd:double rdf:type rdfs:Datatype ."-    ]--rulesXsdDecimal :: [RDFRule]-rulesXsdDecimal = makeRDFDatatypeRestrictionRules rdfDatatypeValXsdDecimal gr-    where-        gr = makeRDFGraphFromN3Builder rulesXsdDecimalBuilder----- I have removed the newline which was added between each line---- to improve the clarity of parser errors.-----rulesXsdDecimalBuilder :: B.Builder-rulesXsdDecimalBuilder = -  mconcat-  [ prefixXsdDecimal-    , "xsd_decimal:Abs a rdfd:GeneralRestriction ; "-    , "  rdfd:onProperties (rdf:_1 rdf:_2) ; "-    , "  rdfd:constraint xsd_decimal:abs ; "-    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "-    , "xsd_decimal:Neg a rdfd:GeneralRestriction ; "-    , "  rdfd:onProperties (rdf:_1 rdf:_2) ; "-    , "  rdfd:constraint xsd_decimal:neg ; "-    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "-    , "xsd_decimal:Sum a rdfd:GeneralRestriction ; "-    , "  rdfd:onProperties (rdf:_1 rdf:_2 rdf:_3) ; "-    , "  rdfd:constraint xsd_decimal:sum ; "-    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "-    , "xsd_decimal:Diff a rdfd:GeneralRestriction ; "-    , "  rdfd:onProperties (rdf:_1 rdf:_2 rdf:_3) ; "-    , "  rdfd:constraint xsd_decimal:diff ; "-    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "-    , "xsd_decimal:Prod a rdfd:GeneralRestriction ; "-    , "  rdfd:onProperties (rdf:_1 rdf:_2 rdf:_3) ; "-    , "  rdfd:constraint xsd_decimal:prod ; "-    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "-    , "xsd_decimal:DivMod a rdfd:GeneralRestriction ; "-    , "  rdfd:onProperties (rdf:_1 rdf:_2 rdf:_3 rdf:_4) ; "-    , "  rdfd:constraint xsd_decimal:divmod ; "-    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "-    , "xsd_decimal:Power a rdfd:GeneralRestriction ; "-    , "  rdfd:onProperties (rdf:_1 rdf:_2 rdf:_3) ; "-    , "  rdfd:constraint xsd_decimal:power ; "-    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "-    , "xsd_decimal:Eq a rdfd:GeneralRestriction ; "-    , "  rdfd:onProperties (rdf:_1 rdf:_2) ; "-    , "  rdfd:constraint xsd_decimal:eq ; "-    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "-    , "xsd_decimal:Ne a rdfd:GeneralRestriction ; "-    , "  rdfd:onProperties (rdf:_1 rdf:_2) ; "-    , "  rdfd:constraint xsd_decimal:ne ; "-    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "-    , "xsd_decimal:Lt a rdfd:GeneralRestriction ; "-    , "  rdfd:onProperties (rdf:_1 rdf:_2) ; "-    , "  rdfd:constraint xsd_decimal:lt ; "-    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "-    , "xsd_decimal:Le a rdfd:GeneralRestriction ; "-    , "  rdfd:onProperties (rdf:_1 rdf:_2) ; "-    , "  rdfd:constraint xsd_decimal:le ; "-    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "-    , "xsd_decimal:Gt a rdfd:GeneralRestriction ; "-    , "  rdfd:onProperties (rdf:_1 rdf:_2) ; "-    , "  rdfd:constraint xsd_decimal:gt ; "-    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "-    , "xsd_decimal:Ge a rdfd:GeneralRestriction ; "-    , "  rdfd:onProperties (rdf:_1 rdf:_2) ; "-    , "  rdfd:constraint xsd_decimal:ge ; "-    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "-    ]-  ---------------------------------------------------------------------------------------  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,---                2011 Douglas Burke, 2011 William Waites---  All rights reserved.------  This file is part of Swish.------  Swish is free software; you can redistribute it and/or modify---  it under the terms of the GNU General Public License as published by---  the Free Software Foundation; either version 2 of the License, or---  (at your option) any later version.------  Swish is distributed in the hope that it will be useful,---  but WITHOUT ANY WARRANTY; without even the implied warranty of---  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the---  GNU General Public License for more details.------  You should have received a copy of the GNU General Public License---  along with Swish; if not, write to:---    The Free Software Foundation, Inc.,---    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA------------------------------------------------------------------------------------
− src/Swish/RDF/RDFDatatypeXsdInteger.hs
@@ -1,539 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  RDFDatatypeXsdInteger---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  License     :  GPL V2------  Maintainer  :  Douglas Burke---  Stability   :  experimental---  Portability :  OverloadedStrings------  This module defines the structures used by Swish to represent and---  manipulate RDF @xsd:integer@ datatyped literals.--------------------------------------------------------------------------------------module Swish.RDF.RDFDatatypeXsdInteger-    ( rdfDatatypeXsdInteger-    , rdfDatatypeValXsdInteger-    , typeNameXsdInteger, namespaceXsdInteger-    , axiomsXsdInteger, rulesXsdInteger-    , prefixXsdInteger-    )-where--import Swish.RDF.RDFRuleset-    ( RDFFormula, RDFRule, RDFRuleset -    , makeRDFGraphFromN3Builder-    , makeRDFFormula-    )--import Swish.RDF.RDFDatatype-    ( RDFDatatype-    , RDFDatatypeVal-    , RDFDatatypeMod-    , makeRdfDtOpenVarBindingModifiers-    )--import Swish.RDF.ClassRestrictionRule (makeRDFDatatypeRestrictionRules)-import Swish.RDF.MapXsdInteger (mapXsdInteger)--import Swish.RDF.Datatype-    ( Datatype(..)-    , DatatypeVal(..)-    , DatatypeRel(..), DatatypeRelPr-    , altArgs-    , UnaryFnTable,    unaryFnApp-    , BinaryFnTable,   binaryFnApp-    , BinMaybeFnTable, binMaybeFnApp-    , DatatypeMod(..) -    , makeVmod11inv, makeVmod11-    , makeVmod21inv, makeVmod21-    , makeVmod20-    , makeVmod22-    )--import Swish.RDF.Ruleset (makeRuleset)--import Swish.Utils.Namespace (Namespace, ScopedName, namespaceToBuilder, makeNSScopedName)--import Swish.RDF.Vocabulary-    ( namespaceRDF-    , namespaceRDFS-    , namespaceRDFD-    , namespaceXSD-    , namespaceXsdType-    )--import Data.Monoid(Monoid(..))-import Control.Monad (liftM)-import Data.Maybe (maybeToList)--import qualified Data.Text as T-import qualified Data.Text.Lazy.Builder as B-----------------------------------------------------------------  Misc values-----------------------------------------------------------------  Local name for Integer datatype-nameXsdInteger :: T.Text-nameXsdInteger      = "integer"---- |Type name for xsd:integer datatype-typeNameXsdInteger :: ScopedName-typeNameXsdInteger  = makeNSScopedName namespaceXSD nameXsdInteger---- |Namespace for xsd:integer datatype functions-namespaceXsdInteger :: Namespace-namespaceXsdInteger = namespaceXsdType nameXsdInteger----  Compose with function of two arguments-c2 :: (b -> c) -> (a -> d -> b) -> a -> d -> c-c2 = (.) . (.)----  Integer power (exponentiation) function---  returns Nothing if exponent is negative.----intPower :: Integer -> Integer -> Maybe Integer-intPower a b = if b < 0 then Nothing else Just (intPower1 a b)-    where-        intPower1 x y-            | q == 1           = atopsq*x-            | p == 0           = 1-            | otherwise        = atopsq-            where-                (p,q)  = y `divMod` 2-                atop   = intPower1 x p-                atopsq = atop*atop-----------------------------------------------------------------  Declare exported RDFDatatype value for xsd:integer---------------------------------------------------------------rdfDatatypeXsdInteger :: RDFDatatype-rdfDatatypeXsdInteger = Datatype rdfDatatypeValXsdInteger-----------------------------------------------------------------  Implmentation of RDFDatatypeVal for xsd:integer----------------------------------------------------------------- |Define Datatype value for @xsd:integer@.------  Members of this datatype are positive or negative integer values.------  The lexical form consists of an optional @+@ or @-@---  followed by a sequence of decimal digits.------  The canonical lexical form has leading zeros and @+@ sign removed.----rdfDatatypeValXsdInteger :: RDFDatatypeVal Integer-rdfDatatypeValXsdInteger = DatatypeVal-    { tvalName      = typeNameXsdInteger-    , tvalRules     = rdfRulesetXsdInteger  -- Ruleset RDFGraph-    , tvalMkRules   = makeRDFDatatypeRestrictionRules rdfDatatypeValXsdInteger-                                            -- RDFGraph -> [RDFRules]-    , tvalMkMods    = makeRdfDtOpenVarBindingModifiers rdfDatatypeValXsdInteger-    , tvalMap       = mapXsdInteger         -- DatatypeMap Integer-    , tvalRel       = relXsdInteger         -- [DatatypeRel Integer]-    , tvalMod       = modXsdInteger         -- [DatatypeMod Integer]-    }---- |relXsdInteger contains arithmetic and other relations for xsd:Integer values.------  The functions are inspired by those defined by CWM as math: properties---  (<http://www.w3.org/2000/10/swap/doc/CwmBuiltins.html>).----relXsdInteger :: [DatatypeRel Integer]-relXsdInteger =-    [ relXsdIntegerAbs-    , relXsdIntegerNeg-    , relXsdIntegerSum-    , relXsdIntegerDiff-    , relXsdIntegerProd-    , relXsdIntegerDivMod-    , relXsdIntegerPower-    , relXsdIntegerEq-    , relXsdIntegerNe-    , relXsdIntegerLt-    , relXsdIntegerLe-    , relXsdIntegerGt-    , relXsdIntegerGe-    ]--mkIntRel2 ::-    T.Text -> DatatypeRelPr Integer -> UnaryFnTable Integer-    -> DatatypeRel Integer-mkIntRel2 nam pr fns = DatatypeRel-    { dtRelName = makeNSScopedName namespaceXsdInteger nam-    , dtRelFunc = altArgs pr fns unaryFnApp-    }--mkIntRel3 ::-    T.Text -> DatatypeRelPr Integer -> BinaryFnTable Integer-    -> DatatypeRel Integer-mkIntRel3 nam pr fns = DatatypeRel-    { dtRelName = makeNSScopedName namespaceXsdInteger nam-    , dtRelFunc = altArgs pr fns binaryFnApp-    }--mkIntRel3maybe ::-    T.Text -> DatatypeRelPr Integer -> BinMaybeFnTable Integer-    -> DatatypeRel Integer-mkIntRel3maybe nam pr fns = DatatypeRel-    { dtRelName = makeNSScopedName namespaceXsdInteger nam-    , dtRelFunc = altArgs pr fns binMaybeFnApp-    }--relXsdIntegerAbs :: DatatypeRel Integer-relXsdIntegerAbs = mkIntRel2 "abs" (const True)-    [ ( (>=0),      [ (abs,1) ] )-    , ( const True, [ (id,0), (negate,0) ] )-    ]--relXsdIntegerNeg :: DatatypeRel Integer-relXsdIntegerNeg = mkIntRel2 "neg" (const True)-    [ ( const True, [ (negate,1) ] )-    , ( const True, [ (negate,0) ] )-    ]--relXsdIntegerSum :: DatatypeRel Integer-relXsdIntegerSum = mkIntRel3 "sum" (const True)-    [ ( const True, [ ((+),1,2) ] )-    , ( const True, [ ((-),0,2) ] )-    , ( const True, [ ((-),0,1) ] )-    ]--relXsdIntegerDiff :: DatatypeRel Integer-relXsdIntegerDiff = mkIntRel3 "diff" (const True)-    [ ( const True, [ ((-),1,2) ] )-    , ( const True, [ ((+),0,2) ] )-    , ( const True, [ ((-),1,0) ] )-    ]--relXsdIntegerProd :: DatatypeRel Integer-relXsdIntegerProd = mkIntRel3 "prod" (const True)-    [ ( const True, [ ((*),1,2) ] )-    , ( const True, [ (div,0,2) ] )-    , ( const True, [ (div,0,1) ] )-    ]--relXsdIntegerDivMod :: DatatypeRel Integer-relXsdIntegerDivMod = mkIntRel3 "divmod" (const True)-    [ ( const True, [ (div,2,3) ] )-    , ( const True, [ (mod,2,3) ] )-    , ( const True, [ ] )-    , ( const True, [ ] )-    ]--relXsdIntegerPower :: DatatypeRel Integer-relXsdIntegerPower = mkIntRel3maybe "power" (const True)-    [ ( const True, [ (liftM (:[]) `c2` intPower,1,2) ] )-    , ( const True, [ ] )-    , ( (>=0),      [ ] )-    ]--liftL2 :: (a->a->Bool) -> ([a]->a) -> ([a]->a) -> [a] -> Bool-liftL2 p i1 i2 as = p (i1 as) (i2 as)--lcomp :: (a->a->Bool) -> [a] -> Bool-lcomp p = liftL2 p head (head . tail)---- eq--relXsdIntegerEq :: DatatypeRel Integer-relXsdIntegerEq = mkIntRel2 "eq" (lcomp (==))-    ( repeat (const True, []) )---- ne--relXsdIntegerNe :: DatatypeRel Integer-relXsdIntegerNe = mkIntRel2 "ne" (lcomp (/=))-    ( repeat (const True, []) )---- lt--relXsdIntegerLt :: DatatypeRel Integer-relXsdIntegerLt = mkIntRel2 "lt" (lcomp (<))-    ( repeat (const True, []) )---- le--relXsdIntegerLe :: DatatypeRel Integer-relXsdIntegerLe = mkIntRel2 "le" (lcomp (<=))-    ( repeat (const True, []) )---- gt--relXsdIntegerGt :: DatatypeRel Integer-relXsdIntegerGt = mkIntRel2 "gt" (lcomp (>))-    ( repeat (const True, []) )---- ge--relXsdIntegerGe :: DatatypeRel Integer-relXsdIntegerGe = mkIntRel2 "ge" (lcomp (>=))-    ( repeat (const True, []) )---- |modXsdInteger contains variable binding modifiers for xsd:Integer values.------  The functions are selected from those defined by CWM as math:---  properties---  (<http://www.w3.org/2000/10/swap/doc/CwmBuiltins.html>).----modXsdInteger :: [RDFDatatypeMod Integer]-modXsdInteger =-    [ modXsdIntegerAbs-    , modXsdIntegerNeg-    , modXsdIntegerSum-    , modXsdIntegerDiff-    , modXsdIntegerProd-    , modXsdIntegerDivMod-    , modXsdIntegerPower-    , modXsdIntegerEq-    , modXsdIntegerNe-    , modXsdIntegerLt-    , modXsdIntegerLe-    , modXsdIntegerGt-    , modXsdIntegerGe-    ]--modXsdIntegerAbs :: RDFDatatypeMod Integer-modXsdIntegerAbs = DatatypeMod-    { dmName = makeNSScopedName namespaceXsdInteger "abs"-    , dmModf = [ f0, f1 ]-    , dmAppf = makeVmod11-    }-    where-        f0 vs@[v1,v2] = if v1 == abs v2 then vs else []-        f0 _          = []-        f1 [v2]       = [abs v2]-        f1 _          = []--modXsdIntegerNeg :: RDFDatatypeMod Integer-modXsdIntegerNeg = DatatypeMod-    { dmName = makeNSScopedName namespaceXsdInteger "neg"-    , dmModf = [ f0, f1, f1 ]-    , dmAppf = makeVmod11inv-    }-    where-        f0 vs@[v1,v2] = if v1 == negate v2 then vs else []-        f0 _          = []-        f1 [vi]       = [-vi]-        f1 _          = []--modXsdIntegerSum :: RDFDatatypeMod Integer-modXsdIntegerSum = DatatypeMod-    { dmName = makeNSScopedName namespaceXsdInteger "sum"-    , dmModf = [ f0, f1, f2, f2 ]-    , dmAppf = makeVmod21inv-    }-    where-        f0 vs@[v1,v2,v3] = if v1 == v2+v3 then vs else []-        f0 _             = []-        f1 [v2,v3]       = [v2+v3]-        f1 _             = []-        f2 [v1,vi]       = [v1-vi]-        f2 _             = []--modXsdIntegerDiff :: RDFDatatypeMod Integer-modXsdIntegerDiff = DatatypeMod-    { dmName = makeNSScopedName namespaceXsdInteger "diff"-    , dmModf = [ f0, f1, f2, f3 ]-    , dmAppf = makeVmod21inv-    }-    where-        f0 vs@[v1,v2,v3] = if v1 == v2-v3 then vs else []-        f0 _             = []-        f1 [v2,v3]       = [v2-v3]-        f1 _             = []-        f2 [v1,v3]       = [v1+v3]-        f2 _             = []-        f3 [v1,v2]       = [v2-v1]-        f3 _             = []--modXsdIntegerProd :: RDFDatatypeMod Integer-modXsdIntegerProd = DatatypeMod-    { dmName = makeNSScopedName namespaceXsdInteger "prod"-    , dmModf = [ f0, f1, f2, f2 ]-    , dmAppf = makeVmod21inv-    }-    where-        f0 vs@[v1,v2,v3] = if v1 == v2*v3 then vs else []-        f0 _             = []-        f1 [v2,v3]       = [v2*v3]-        f1 _             = []-        f2 [v1,vi]       = if r == 0 then [q] else []-            where (q,r)  = quotRem v1 vi-        f2 _             = []--modXsdIntegerDivMod :: RDFDatatypeMod Integer-modXsdIntegerDivMod = DatatypeMod-    { dmName = makeNSScopedName namespaceXsdInteger "divmod"-    , dmModf = [ f0, f1 ]-    , dmAppf = makeVmod22-    }-    where-        f0 vs@[v1,v2,v3,v4] = if (v1,v2) == divMod v3 v4 then vs else []-        f0 _                = []-        f1 [v3,v4]          = [v1,v2] where (v1,v2) = divMod v3 v4-        f1 _                = []--modXsdIntegerPower :: RDFDatatypeMod Integer-modXsdIntegerPower = DatatypeMod-    { dmName = makeNSScopedName namespaceXsdInteger "power"-    , dmModf = [ f0, f1 ]-    , dmAppf = makeVmod21-    }-    where-        f0 vs@[v1,v2,v3] = if Just v1 == intPower v2 v3 then vs else []-        f0 _             = []-        f1 [v2,v3]       = maybeToList (intPower v2 v3)-        f1 _             = []--modXsdIntegerEq, modXsdIntegerNe, modXsdIntegerLt, modXsdIntegerLe, modXsdIntegerGt, modXsdIntegerGe :: RDFDatatypeMod Integer -modXsdIntegerEq = modXsdIntegerCompare "eq" (==)-modXsdIntegerNe = modXsdIntegerCompare "ne" (/=)-modXsdIntegerLt = modXsdIntegerCompare "lt" (<)-modXsdIntegerLe = modXsdIntegerCompare "le" (<=)-modXsdIntegerGt = modXsdIntegerCompare "gt" (>)-modXsdIntegerGe = modXsdIntegerCompare "ge" (>=)--modXsdIntegerCompare ::-    T.Text -> (Integer->Integer->Bool) -> RDFDatatypeMod Integer-modXsdIntegerCompare nam rel = DatatypeMod-    { dmName = makeNSScopedName namespaceXsdInteger nam-    , dmModf = [ f0 ]-    , dmAppf = makeVmod20-    }-    where-        f0 vs@[v1,v2] = if rel v1 v2 then vs else []-        f0 _          = []---- |rulesetXsdInteger contains rules and axioms that allow additional---  deductions when xsd:integer values appear in a graph.------  The rules defined here are concerned with basic integer arithmetic---  operations: +, -, *, div, rem------  makeRuleset :: Namespace -> [Formula ex] -> [Rule ex] -> Ruleset ex----rdfRulesetXsdInteger :: RDFRuleset-rdfRulesetXsdInteger =-    makeRuleset namespaceXsdInteger axiomsXsdInteger rulesXsdInteger--mkPrefix :: Namespace -> B.Builder-mkPrefix = namespaceToBuilder--prefixXsdInteger :: B.Builder-prefixXsdInteger = -  mconcat-  [ mkPrefix namespaceRDF-  , mkPrefix namespaceRDFS-  , mkPrefix namespaceRDFD-  , mkPrefix namespaceXSD-  , mkPrefix namespaceXsdInteger-  ]--mkAxiom :: T.Text -> B.Builder -> RDFFormula-mkAxiom local gr =-    makeRDFFormula namespaceXsdInteger local (prefixXsdInteger `mappend` gr)--axiomsXsdInteger :: [RDFFormula]-axiomsXsdInteger =-    [ mkAxiom "dt"      "xsd:integer rdf:type rdfs:Datatype ."-    ]--rulesXsdInteger :: [RDFRule]-rulesXsdInteger = makeRDFDatatypeRestrictionRules rdfDatatypeValXsdInteger gr-    where-        gr = makeRDFGraphFromN3Builder rulesXsdIntegerBuilder----- I have removed the newline which was added between each line---- to improve the clarity of parser errors.-----rulesXsdIntegerBuilder :: B.Builder-rulesXsdIntegerBuilder = -  mconcat-  [ prefixXsdInteger-    , "xsd_integer:Abs a rdfd:GeneralRestriction ; "-    , "  rdfd:onProperties (rdf:_1 rdf:_2) ; "-    , "  rdfd:constraint xsd_integer:abs ; "-    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "-    , "xsd_integer:Neg a rdfd:GeneralRestriction ; "-    , "  rdfd:onProperties (rdf:_1 rdf:_2) ; "-    , "  rdfd:constraint xsd_integer:neg ; "-    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "-    , "xsd_integer:Sum a rdfd:GeneralRestriction ; "-    , "  rdfd:onProperties (rdf:_1 rdf:_2 rdf:_3) ; "-    , "  rdfd:constraint xsd_integer:sum ; "-    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "-    , "xsd_integer:Diff a rdfd:GeneralRestriction ; "-    , "  rdfd:onProperties (rdf:_1 rdf:_2 rdf:_3) ; "-    , "  rdfd:constraint xsd_integer:diff ; "-    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "-    , "xsd_integer:Prod a rdfd:GeneralRestriction ; "-    , "  rdfd:onProperties (rdf:_1 rdf:_2 rdf:_3) ; "-    , "  rdfd:constraint xsd_integer:prod ; "-    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "-    , "xsd_integer:DivMod a rdfd:GeneralRestriction ; "-    , "  rdfd:onProperties (rdf:_1 rdf:_2 rdf:_3 rdf:_4) ; "-    , "  rdfd:constraint xsd_integer:divmod ; "-    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "-    , "xsd_integer:Power a rdfd:GeneralRestriction ; "-    , "  rdfd:onProperties (rdf:_1 rdf:_2 rdf:_3) ; "-    , "  rdfd:constraint xsd_integer:power ; "-    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "-    , "xsd_integer:Eq a rdfd:GeneralRestriction ; "-    , "  rdfd:onProperties (rdf:_1 rdf:_2) ; "-    , "  rdfd:constraint xsd_integer:eq ; "-    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "-    , "xsd_integer:Ne a rdfd:GeneralRestriction ; "-    , "  rdfd:onProperties (rdf:_1 rdf:_2) ; "-    , "  rdfd:constraint xsd_integer:ne ; "-    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "-    , "xsd_integer:Lt a rdfd:GeneralRestriction ; "-    , "  rdfd:onProperties (rdf:_1 rdf:_2) ; "-    , "  rdfd:constraint xsd_integer:lt ; "-    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "-    , "xsd_integer:Le a rdfd:GeneralRestriction ; "-    , "  rdfd:onProperties (rdf:_1 rdf:_2) ; "-    , "  rdfd:constraint xsd_integer:le ; "-    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "-    , "xsd_integer:Gt a rdfd:GeneralRestriction ; "-    , "  rdfd:onProperties (rdf:_1 rdf:_2) ; "-    , "  rdfd:constraint xsd_integer:gt ; "-    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "-    , "xsd_integer:Ge a rdfd:GeneralRestriction ; "-    , "  rdfd:onProperties (rdf:_1 rdf:_2) ; "-    , "  rdfd:constraint xsd_integer:ge ; "-    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "-    ]-  ---------------------------------------------------------------------------------------  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  All rights reserved.------  This file is part of Swish.------  Swish is free software; you can redistribute it and/or modify---  it under the terms of the GNU General Public License as published by---  the Free Software Foundation; either version 2 of the License, or---  (at your option) any later version.------  Swish is distributed in the hope that it will be useful,---  but WITHOUT ANY WARRANTY; without even the implied warranty of---  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the---  GNU General Public License for more details.------  You should have received a copy of the GNU General Public License---  along with Swish; if not, write to:---    The Free Software Foundation, Inc.,---    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA------------------------------------------------------------------------------------
− src/Swish/RDF/RDFDatatypeXsdString.hs
@@ -1,333 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  RDFDatatypeXsdString---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  License     :  GPL V2------  Maintainer  :  Douglas Burke---  Stability   :  experimental---  Portability :  OverloadedStrings------  This module defines the structures used by Swish to represent and---  manipulate RDF @xsd:string@ datatyped literals.---------------------------------------------------------------------------------------- TODO: this should convert to/from T.Text rather than String--module Swish.RDF.RDFDatatypeXsdString-    ( rdfDatatypeXsdString-    , rdfDatatypeValXsdString-    , typeNameXsdString, namespaceXsdString-    , axiomsXsdString, rulesXsdString-    , prefixXsdString-    )-    where--import Swish.RDF.RDFRuleset-    ( RDFFormula, RDFRule, RDFRuleset-    , makeRDFGraphFromN3Builder-    , makeRDFFormula-    , makeN3ClosureRule-    )--import Swish.RDF.RDFVarBinding (RDFVarBindingModify)--import Swish.RDF.RDFDatatype-    ( RDFDatatype-    , RDFDatatypeVal-    , RDFDatatypeMod-    , makeRdfDtOpenVarBindingModifiers-    )--import Swish.RDF.RDFGraph (RDFLabel(..))-import Swish.RDF.ClassRestrictionRule (makeRDFDatatypeRestrictionRules)--import Swish.RDF.Datatype-    ( Datatype(..)-    , DatatypeVal(..)-    , DatatypeMap(..)-    , DatatypeRel(..), DatatypeRelPr-    , altArgs-    , UnaryFnTable,  unaryFnApp-    , DatatypeMod(..) -    , makeVmod20-    )--import Swish.RDF.Ruleset (makeRuleset)-import Swish.Utils.Namespace (Namespace, ScopedName, namespaceToBuilder, makeNSScopedName)--import Swish.RDF.Vocabulary-    ( namespaceRDF-    , namespaceRDFS-    , namespaceRDFD-    , namespaceXSD-    , namespaceXsdType-    )--import Swish.RDF.VarBinding (VarBinding(..), addVarBinding, VarBindingModify(..))--import Data.Monoid(Monoid(..))--import qualified Data.Text as T-import qualified Data.Text.Lazy.Builder as B-----------------------------------------------------------------  Misc values-----------------------------------------------------------------  Local name for Integer datatype-nameXsdString :: T.Text-nameXsdString = "string"---- | Type name for @xsd:string@ datatype-typeNameXsdString :: ScopedName-typeNameXsdString  = makeNSScopedName namespaceXSD nameXsdString---- |Namespace for @xsd:string@ datatype functions-namespaceXsdString :: Namespace-namespaceXsdString = namespaceXsdType nameXsdString-----------------------------------------------------------------  Declare exported RDFDatatype value for xsd:integer---------------------------------------------------------------rdfDatatypeXsdString :: RDFDatatype-rdfDatatypeXsdString = Datatype rdfDatatypeValXsdString-----------------------------------------------------------------  Implmentation of RDFDatatypeVal for xsd:integer----------------------------------------------------------------- |Define Datatype value for @xsd:string@.----rdfDatatypeValXsdString :: RDFDatatypeVal T.Text-rdfDatatypeValXsdString = DatatypeVal-    { tvalName      = typeNameXsdString-    , tvalRules     = rdfRulesetXsdString-    , tvalMkRules   = makeRDFDatatypeRestrictionRules rdfDatatypeValXsdString-    , tvalMkMods    = makeRdfDtOpenVarBindingModifiers rdfDatatypeValXsdString-    , tvalMap       = mapXsdString-    , tvalRel       = relXsdString-    , tvalMod       = modXsdString-    }---- |mapXsdString contains functions that perform lexical-to-value---  and value-to-canonical-lexical mappings for @xsd:string@ values------  These are identity mappings.----mapXsdString :: DatatypeMap T.Text-mapXsdString = DatatypeMap-    { mapL2V = Just-    , mapV2L = Just-    }---- |relXsdString contains useful relations for @xsd:string@ values.----relXsdString :: [DatatypeRel T.Text]-relXsdString =-    [ relXsdStringEq-    , relXsdStringNe-    ]--mkStrRel2 ::-    T.Text -> DatatypeRelPr T.Text -> UnaryFnTable T.Text-    -> DatatypeRel T.Text-mkStrRel2 nam pr fns = -  DatatypeRel-    { dtRelName = makeNSScopedName namespaceXsdString nam-    , dtRelFunc = altArgs pr fns unaryFnApp-    }--{--mkStrRel3 ::-    String -> DatatypeRelPr String -> BinaryFnTable String-    -> DatatypeRel String-mkStrRel3 nam pr fns = DatatypeRel-    { dtRelName = ScopedName namespaceXsdString nam-    , dtRelFunc = altArgs pr fns binaryFnApp-    }--mkStrRel3maybe ::-    String -> DatatypeRelPr String -> BinMaybeFnTable String-    -> DatatypeRel String-mkStrRel3maybe nam pr fns = DatatypeRel-    { dtRelName = ScopedName namespaceXsdString nam-    , dtRelFunc = altArgs pr fns binMaybeFnApp-    }--}--liftL2 :: (a->a->Bool) -> ([a]->a) -> ([a]->a) -> [a] -> Bool-liftL2 p i1 i2 as = p (i1 as) (i2 as)--lcomp :: (a->a->Bool) -> [a] -> Bool-lcomp p = liftL2 p head (head . tail)---- eq--relXsdStringEq :: DatatypeRel T.Text-relXsdStringEq = mkStrRel2 "eq" (lcomp (==))-    ( repeat (const True, []) )---- ne--relXsdStringNe :: DatatypeRel T.Text-relXsdStringNe = mkStrRel2 "ne" (lcomp (/=))-    ( repeat (const True, []) )---- |modXsdString contains variable binding modifiers for @xsd:string@ values.----modXsdString :: [RDFDatatypeMod T.Text]-modXsdString =-    [ modXsdStringEq-    , modXsdStringNe-    ]--modXsdStringEq, modXsdStringNe :: RDFDatatypeMod T.Text-modXsdStringEq = modXsdStringCompare "eq" (==)-modXsdStringNe = modXsdStringCompare "ne" (/=)--modXsdStringCompare ::-    T.Text -> (T.Text->T.Text->Bool) -> RDFDatatypeMod T.Text-modXsdStringCompare nam rel = DatatypeMod-    { dmName = makeNSScopedName namespaceXsdString nam-    , dmModf = [ f0 ]-    , dmAppf = makeVmod20-    }-    where-        f0 vs@[v1,v2] = if rel v1 v2 then vs else []-        f0 _          = []---- |rulesetXsdString contains rules and axioms that allow additional---  deductions when xsd:string values appear in a graph.------  makeRuleset :: Namespace -> [Formula ex] -> [Rule ex] -> Ruleset ex----rdfRulesetXsdString :: RDFRuleset-rdfRulesetXsdString =-    makeRuleset namespaceXsdString axiomsXsdString rulesXsdString--mkPrefix :: Namespace -> B.Builder-mkPrefix = namespaceToBuilder--prefixXsdString :: B.Builder-prefixXsdString = -  mconcat-  [ mkPrefix namespaceRDF-  , mkPrefix namespaceRDFS-  , mkPrefix namespaceRDFD-  , mkPrefix namespaceXSD-  , mkPrefix namespaceXsdString-  ]-  -mkAxiom :: T.Text -> B.Builder -> RDFFormula-mkAxiom local gr =-    makeRDFFormula namespaceXsdString local (prefixXsdString `mappend` gr)--axiomsXsdString :: [RDFFormula]-axiomsXsdString =-    [ mkAxiom "dt"      "xsd:string rdf:type rdfs:Datatype ."-    ]--rulesXsdString :: [RDFRule]-rulesXsdString = rulesXsdStringClosure ++ rulesXsdStringRestriction--rulesXsdStringRestriction :: [RDFRule]-rulesXsdStringRestriction =-    makeRDFDatatypeRestrictionRules rdfDatatypeValXsdString gr-    where-        gr = makeRDFGraphFromN3Builder rulesXsdStringBuilder--rulesXsdStringBuilder :: B.Builder-rulesXsdStringBuilder = -  mconcat-  [ prefixXsdString-    , "xsd_string:Eq a rdfd:GeneralRestriction ; "-    , "  rdfd:onProperties (rdf:_1 rdf:_2) ; "-    , "  rdfd:constraint xsd_string:eq ; "-    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "-    , "xsd_string:Ne a rdfd:GeneralRestriction ; "-    , "  rdfd:onProperties (rdf:_1 rdf:_2) ; "-    , "  rdfd:constraint xsd_string:ne ; "-    , "  rdfd:maxCardinality \"1\"^^xsd:nonNegativeInteger . "-    ]-  -rulesXsdStringClosure :: [RDFRule]-rulesXsdStringClosure =-    [ xsdstrls-    , xsdstrsl-    ]----  Infer string from plain literal-xsdstrls :: RDFRule-xsdstrls = makeN3ClosureRule namespaceXsdString "ls"-            "?a ?p ?l ."-            "?a ?p ?s ."-            (stringPlain "s" "l")----  Infer plain literal from string-xsdstrsl :: RDFRule-xsdstrsl = makeN3ClosureRule namespaceXsdString "sl"-            "?a ?p ?s ."-            "?a ?p ?l ."-            (stringPlain "s" "l")----  Map between string and plain literal values-stringPlain :: String -> String -> RDFVarBindingModify-stringPlain svar lvar = stringPlainValue (Var svar) (Var lvar)----  Variable binding modifier to create new binding to a canonical---  form of a datatyped literal.-stringPlainValue ::-    RDFLabel -> RDFLabel -> RDFVarBindingModify-stringPlainValue svar lvar = VarBindingModify-        { vbmName   = makeNSScopedName namespaceRDFD "stringPlain"-        , vbmApply  = concatMap app1-        , vbmVocab  = [svar,lvar]-        , vbmUsage  = [[svar],[lvar],[]]-        }-    where-        app1 vbind = app2 (vbMap vbind svar) (vbMap vbind lvar) vbind-        app2 (Just (Lit s (Just _)))-             (Just (Lit l Nothing))-             vbind-             | s == l-             = [vbind]-        app2 (Just (Lit s (Just _)))-             Nothing-             vbind-             = [addVarBinding lvar (Lit s Nothing) vbind]-        app2 Nothing-             (Just (Lit l Nothing))-             vbind-             = [addVarBinding svar (Lit l (Just typeNameXsdString)) vbind]-        app2 _ _ _ = []----------------------------------------------------------------------------------------  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  All rights reserved.------  This file is part of Swish.------  Swish is free software; you can redistribute it and/or modify---  it under the terms of the GNU General Public License as published by---  the Free Software Foundation; either version 2 of the License, or---  (at your option) any later version.------  Swish is distributed in the hope that it will be useful,---  but WITHOUT ANY WARRANTY; without even the implied warranty of---  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the---  GNU General Public License for more details.------  You should have received a copy of the GNU General Public License---  along with Swish; if not, write to:---    The Free Software Foundation, Inc.,---    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA------------------------------------------------------------------------------------
− src/Swish/RDF/RDFGraph.hs
@@ -1,1396 +0,0 @@-{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, TypeSynonymInstances #-}-{-# LANGUAGE OverloadedStrings #-}-------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  RDFGraph---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  License     :  GPL V2------  Maintainer  :  Douglas Burke---  Stability   :  experimental---  Portability :  FlexibleInstances, MultiParamTypeClasses, TypeSynonymInstances, OverloadedStrings------  This module defines a memory-based RDF graph instance.----------------------------------------------------------------------------------------------------------------------------------------------------- Simple labelled directed graph value---------------------------------------------------------------module Swish.RDF.RDFGraph-    ( -      -- * Labels-      RDFLabel(..), ToRDFLabel(..), FromRDFLabel(..)-    , isLiteral, isUntypedLiteral, isTypedLiteral, isXMLLiteral-    , isDatatyped, isMemberProp, isUri, isBlank, isQueryVar-    , getLiteralText, getScopedName, makeBlank-    , quote-    , quoteT-      -      -- * RDF Graphs-    , RDFTriple-    , toRDFTriple, fromRDFTriple-    , NSGraph(..)-    , RDFGraph-    , toRDFGraph, emptyRDFGraph {-, updateRDFGraph-}-    , NamespaceMap, RevNamespaceMap, RevNamespace-    , emptyNamespaceMap-    , LookupFormula(..), Formula, FormulaMap, emptyFormulaMap-    , addArc, merge-    , allLabels, allNodes, remapLabels, remapLabelList-    , newNode, newNodes-    , setNamespaces, getNamespaces-    , setFormulae, getFormulae, setFormula, getFormula-      -    -- * Re-export from GraphClass-    , LDGraph(..), Label (..), Arc(..)-    , arc, arcSubj, arcPred, arcObj, Selector-      -    -- * Selected RDFLabel values-    --                                -    -- | The 'ToRDFLabel' instance of 'ScopedName' can also be used                                     -    -- to easily construct 'RDFLabel' versions of the terms defined-    -- in `Swish.RDF.Vocabulary`.-    -    -- ** RDF terms                                          -    --                                          -    -- | These terms are described in <http://www.w3.org/TR/rdf-syntax-grammar/>;                                          -    -- the version used is \"W3C Recommendation 10 February 2004\", <http://www.w3.org/TR/2004/REC-rdf-syntax-grammar-20040210/>.-    --                                          -    -- Some terms are listed within the RDF Schema terms below since their definition                                            -    -- is given within the RDF Schema document.                                          -    --                                          -    , resRdfRDF                                          -    , resRdfDescription      -    , resRdfID-    , resRdfAbout-    , resRdfParseType-    , resRdfResource-    , resRdfLi-    , resRdfNodeID-    , resRdfDatatype-    , resRdf1, resRdf2, resRdfn-    -- ** RDF Schema terms-    --                                 -    -- | These are defined by <http://www.w3.org/TR/rdf-schema/>; the version-    -- used is \"W3C Recommendation 10 February 2004\", <http://www.w3.org/TR/2004/REC-rdf-schema-20040210/>.-                        -    -- *** Classes-    --                                 -    -- | See the \"Classes\" section at <http://www.w3.org/TR/rdf-schema/#ch_classes> for more information.-    , resRdfsResource-    , resRdfsClass-    , resRdfsLiteral-    , resRdfsDatatype-    , resRdfXMLLiteral-    , resRdfProperty-    -- *** Properties-    --                                 -    -- | See the \"Properties\" section at <http://www.w3.org/TR/rdf-schema/#ch_classes> for more information.-    , resRdfsRange-    , resRdfsDomain-    , resRdfType-    , resRdfsSubClassOf-    , resRdfsSubPropertyOf-    , resRdfsLabel-    , resRdfsComment-    -- *** Containers-    ---    -- | See the \"Container Classes and Properties\" section at <http://www.w3.org/TR/rdf-schema/#ch_containervocab>.-    , resRdfsContainer-    , resRdfBag-    , resRdfSeq                                 -    , resRdfAlt  -    , resRdfsContainerMembershipProperty-    , resRdfsMember-    -- *** Collections-    ---    -- | See the \"Collections\" section at <http://www.w3.org/TR/rdf-schema/#ch_collectionvocab>.-    , resRdfList    -    , resRdfFirst-    , resRdfRest -    , resRdfNil -    -- *** Reification Vocabulary -    --  -    -- | See the \"Reification Vocabulary\" section at <http://www.w3.org/TR/rdf-schema/#ch_reificationvocab>.-    , resRdfStatement  -    , resRdfSubject  -    , resRdfPredicate  -    , resRdfObject  -    -- *** Utility Properties -    --  -    -- | See the \"Utility Properties\" section at <http://www.w3.org/TR/rdf-schema/#ch_utilvocab>.-    , resRdfsSeeAlso-    , resRdfsIsDefinedBy-    , resRdfValue  -    -    -- ** OWL     -    , resOwlSameAs-                    -    -- ** Miscellaneous     -    , resRdfdGeneralRestriction-    , resRdfdOnProperties, resRdfdConstraint, resRdfdMaxCardinality-    , resLogImplies-      -    -- * Exported for testing-    , grMatchMap, grEq-    , mapnode, maplist-    )-    where--import Swish.Utils.Namespace-    ( Namespace, makeNamespace, getNamespaceTuple-    , getScopedNameURI-    , ScopedName-    , getScopeLocal, getScopeNamespace-    , getQName-    , makeQNameScopedName-    , makeURIScopedName-    , nullScopedName-    )--import Swish.RDF.Vocabulary-{-    -    ( namespaceRDF-    , langTag, isLang-    , rdfType-    , rdfFirst, rdfRest, rdfNil, rdfXMLLiteral-    , rdfsMember-    , rdfdGeneralRestriction-    , rdfdOnProperties, rdfdConstraint, rdfdMaxCardinality-    , owlSameAs, logImplies-    , xsdBoolean, xsdDecimal, xsdFloat, xsdDouble, xsdInteger-    , xsdDateTime, xsdDate                                                -    )--}--import Swish.RDF.GraphClass-    ( LDGraph(..), Label (..)-    , Arc(..), arc, arcSubj, arcPred, arcObj, arcLabels-    , Selector )--import Swish.RDF.GraphMatch (graphMatch, LabelMap, ScopedLabel(..))--import Swish.Utils.QName (QName)-import Swish.Utils.MiscHelpers (hash)-import Swish.Utils.ListHelpers (addSetElem)--import Swish.Utils.LookupMap-    ( LookupMap(..), LookupEntryClass(..)-    , listLookupMap-    , mapFind, mapFindMaybe, mapReplaceOrAdd, mapAddIfNew-    , mapVals, mapKeys )--import qualified Data.Foldable as Foldable-import qualified Data.Traversable as Traversable--import qualified Data.Text as T-import qualified Data.Text.Read as T--- import qualified Data.Text.Lazy as L--- import Data.Text.Format (format)--- import Data.Text.Buildable--- import Data.Text.Format.Types (Only(..))--import Control.Applicative (Applicative, liftA, (<$>), (<*>))--import Network.URI (URI)--import Data.Monoid (Monoid(..))-import Data.Maybe (mapMaybe)-import Data.Char (ord, isDigit)-import Data.List (intersect, union, findIndices, foldl')-import Data.Ord (comparing)--- import Data.Tuple (swap) not in 6.12.3-import Data.String (IsString(..))-import Data.Time (UTCTime, Day, ParseTime, parseTime, formatTime)-import System.Locale (defaultTimeLocale)  -import Text.Printf--swap :: (a,b) -> (b,a)-swap (a,b) = (b,a)---- | RDF graph node values------  cf. <http://www.w3.org/TR/rdf-concepts/#section-Graph-syntax>------  This is extended from the RDF abstract graph syntax in the---  following ways:------  (a) a graph can be part of a resource node or blank node---      (cf. Notation3 formulae)------  (b) a \"variable\" node option is distinguished from a---      blank node.---      I have found this useful for encoding and handling---      queries, even though query variables can be expressed---      as blank nodes.------  (c) a \"NoNode\" option is defined.---      This might otherwise be handled by @Maybe (RDFLabel g)@.----data RDFLabel =-      Res ScopedName                    -- ^ resource-    | Lit T.Text (Maybe ScopedName)     -- ^ literal [type/language]-    | Blank String                      -- ^ blank node-    | Var String                        -- ^ variable (not used in ordinary graphs)-    | NoNode                            -- ^ no node  (not used in ordinary graphs)---- TODO: should Lit be split up so that can easily differentiate between--- a type and a language tag?---- | Define equality of nodes possibly based on different graph types.------ The equality of literals is taken from section 6.5.1 ("Literal--- Equality") of the RDF Concepts and Abstract Document,--- <http://www.w3.org/TR/2004/REC-rdf-concepts-20040210/#section-Literal-Equality>.----instance Eq RDFLabel where-    Res q1   == Res q2   = q1 == q2-    Blank b1 == Blank b2 = b1 == b2-    Var v1   == Var v2   = v1 == v2--    Lit s1 Nothing   == Lit s2 Nothing   = s1 == s2-    Lit s1 (Just t1) == Lit s2 (Just t2) = s1 == s2 && (t1 == t2 ||-                                                        (isLang t1 && isLang t2 &&-                                                         (T.toLower . langTag) t1 == (T.toLower . langTag) t2))-    -    _  == _ = False--instance Show RDFLabel where-    show (Res sn)           = show sn-    show (Lit st Nothing)   = quote1Str st-    show (Lit st (Just nam))-        | isLang nam = quote1Str st ++ "@"  ++ T.unpack (langTag nam)-        | nam `elem` [xsdBoolean, xsdDouble, xsdDecimal, xsdInteger] = T.unpack st-        | otherwise  = quote1Str st ++ "^^" ++ show nam-    show (Blank ln)         = "_:"++ln-    show (Var ln)           = '?' : ln-    show NoNode             = "<NoNode>"--instance Ord RDFLabel where-    -- Optimize some common cases..-    compare (Res sn1)      (Res sn2)      = compare sn1 sn2-    compare (Blank ln1)    (Blank ln2)    = compare ln1 ln2-    compare (Res _)        (Blank _)      = LT-    compare (Blank _)      (Res _)        = GT-    -- .. else use show string comparison-    compare l1 l2 = comparing show l1 l2-    -    {- <= is not used if compare is provided-    -- Similarly for <=-    (Res qn1)   <= (Res qn2)      = qn1 <= qn2-    (Blank ln1) <= (Blank ln2)    = ln1 <= ln2-    (Res _)     <= (Blank _)      = True-    (Blank _)   <= (Res _)        = False-    l1 <= l2                      = show l1 <= show l2-    -}-    -instance Label RDFLabel where-    labelIsVar (Blank _)    = True-    labelIsVar (Var _)      = True-    labelIsVar _            = False-    getLocal   (Blank loc)  = loc-    getLocal   (Var   loc)  = '?':loc-    getLocal   (Res   sn)   = "Res_" ++ T.unpack (getScopeLocal sn)-    getLocal   (NoNode)     = "None"-    getLocal   _            = "Lit_"-    makeLabel  ('?':loc)    = Var loc-    makeLabel  loc          = Blank loc-    labelHash seed lb       = hash seed (showCanon lb)--instance IsString RDFLabel where-  fromString = flip Lit Nothing . T.pack--{-|-A type that can be converted to a RDF Label.--The String instance converts to an untyped literal-(so no language tag is assumed).--The `UTCTime` and `Day` instances assume values are in UTC.- -The conversion for XSD types attempts to use the-canonical form described in section 2.3.1 of-<http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/#lexical-space>.-  -Note that this is very similar to-'Swish.RDF.RDFDatatype.toRDFLabel' and should be moved into-a @Swish.RDF.Datatype@ module.--}--class ToRDFLabel a where-  toRDFLabel :: a -> RDFLabel-  -{-|-A type that can be converted from a RDF Label,-with the possibility of failure.- -The String instance converts from an untyped literal-(so it can not be used with a string with a language tag).--The following conversions are supported for common XSD-types (out-of-band values result in @Nothing@):-- - @xsd:boolean@ to @Bool@-- - @xsd:integer@ to @Int@ and @Integer@-- - @xsd:float@ to @Float@-- - @xsd:double@ to @Double@-- - @xsd:dateTime@ to @UTCTime@-- - @xsd:date@ to @Day@--Note that this is very similar to-'Swish.RDF.RDFDatatype.fromRDFLabel' and should be moved into-a @Swish.RDF.Datatype@ module.--}--class FromRDFLabel a where-  fromRDFLabel :: RDFLabel -> Maybe a---- instances for type conversion to/from RDFLabel-  --- | This is just @id@.-instance ToRDFLabel RDFLabel where-  toRDFLabel = id-  --- | This is just @Just . id@.  -instance FromRDFLabel RDFLabel where-  fromRDFLabel = Just . id-  --- TODO: remove this hack when finished conversion to Text-maybeReadStr :: (Read a) => T.Text -> Maybe a  -maybeReadStr txt = case reads (T.unpack txt) of-  [(val, "")] -> Just val-  _ -> Nothing-  -maybeRead :: T.Reader a -> T.Text -> Maybe a-maybeRead rdr inTxt = -  case rdr inTxt of-    Right (val, "") -> Just val-    _ -> Nothing-    -fLabel :: (T.Text -> Maybe a) -> ScopedName -> RDFLabel -> Maybe a-fLabel conv dtype (Lit xs (Just dt)) | dt == dtype = conv xs-                                     | otherwise   = Nothing-fLabel _    _     _ = Nothing-  -tLabel :: (Show a) => ScopedName -> (String -> T.Text) -> a -> RDFLabel                      -tLabel dtype conv = flip Lit (Just dtype) . conv . show                      ---- | The character is converted to an untyped literal of length one.-instance ToRDFLabel Char where-  toRDFLabel = flip Lit Nothing . T.singleton---- | The label must be an untyped literal containing a single character.-instance FromRDFLabel Char where-  fromRDFLabel (Lit cs Nothing) | T.compareLength cs 1 == EQ = Just (T.head cs)-                                | otherwise = Nothing-  fromRDFLabel _ = Nothing---- | Strings are converted to untyped literals.-instance ToRDFLabel String where-  toRDFLabel = flip Lit Nothing . T.pack---- | Only untyped literals are converted to strings.-instance FromRDFLabel String where-  fromRDFLabel (Lit xs Nothing) = Just (T.unpack xs)-  fromRDFLabel _ = Nothing--textToBool :: T.Text -> Maybe Bool-textToBool s | s `elem` ["1", "true"]  = Just True-             | s `elem` ["0", "false"] = Just False-             | otherwise               = Nothing---- | Converts to a literal with a @xsd:boolean@ datatype.-instance ToRDFLabel Bool where-  toRDFLabel b = Lit (if b then "true" else "false") (Just xsdBoolean)-                                                 --- | Converts from a literal with a @xsd:boolean@ datatype. The--- literal can be any of the supported XSD forms - e.g. \"0\" or--- \"true\".-instance FromRDFLabel Bool where-  fromRDFLabel = fLabel textToBool xsdBoolean---- fromRealFloat :: (RealFloat a, Buildable a) => ScopedName -> a -> RDFLabel-fromRealFloat :: (RealFloat a, PrintfArg a) => ScopedName -> a -> RDFLabel-fromRealFloat dtype f | isNaN f      = toL "NaN"-                      | isInfinite f = toL $ if f > 0.0 then "INF" else "-INF"-                      -- -                      -- Would like to use Data.Text.Format.format but there are                                                                        -                      -- issues with this module; 0.3.0.2 doesn't build under-                      -- 6.12.3 due to a missing RelaxedPolyRec language extension-                      -- and it relies on double-conversion which has issues-                      -- when used in ghci due to a dlopen issue with libstdc++.-                      -- -                      -- -- | otherwise    = toL $ L.toStrict $ format "{}" (Only f)  -                      -- -                      | otherwise    = toL $ T.pack $ printf "%E" f-                        -                        where-                          toL = flip Lit (Just dtype)---- textToRealFloat :: (RealFloat a) => (a -> Maybe a) -> T.Text -> Maybe a-textToRealFloat :: (RealFloat a, Read a) => (a -> Maybe a) -> T.Text -> Maybe a-textToRealFloat conv = rconv-    where-      rconv "NaN"  = Just (0.0/0.0) -- how best to create a NaN?-      rconv "INF"  = Just (1.0/0.0) -- ditto for Infinity-      rconv "-INF" = Just ((-1.0)/0.0)-      rconv ival -        -- xsd semantics allows "2." but Haskell syntax does not.-        | T.null ival = Nothing-          -        | otherwise = case maybeReadStr ival of-          Just val -> conv val-          _        -> if T.last ival == '.' -- could drop the check-                      then maybeReadStr (T.snoc ival '0') >>= conv-                      else Nothing-                               -        {---        Unfortunately T.rational does not handle "3.01e4" the same-        as read; see https://bitbucket.org/bos/text/issue/7/--        | otherwise = case maybeRead T.rational ival of-          Just val -> conv val-          _        -> if T.last ival == '.' -- could drop the check-                      then maybeRead T.rational (T.snoc ival '0') >>= conv-                      else Nothing-        -}-                        -        -- not sure the above is any improvement on the following-        -- -- | T.last ival == '.' = maybeRead T.rational (T.snoc ival '0') >>= conv-        -- -- | otherwise          = maybeRead T.rational ival >>= conv-      -textToFloat :: T.Text -> Maybe Float-textToFloat = -  let -- assume that an invalid value (NaN/Inf) from maybeRead means-      -- that the value is out of bounds for Float so we do not-      -- convert-      conv f | isNaN f || isInfinite f = Nothing-             | otherwise               = Just f-  in textToRealFloat conv--textToDouble :: T.Text -> Maybe Double      -textToDouble = textToRealFloat Just---- | Converts to a literal with a @xsd:float@ datatype.-instance ToRDFLabel Float where-  toRDFLabel = fromRealFloat xsdFloat-  --- | Converts from a literal with a @xsd:float@ datatype.--- The conversion will fail if the value is outside the valid range of--- a Haskell `Float`.-instance FromRDFLabel Float where-  fromRDFLabel = fLabel textToFloat xsdFloat-                 --- | Converts to a literal with a @xsd:double@ datatype.-instance ToRDFLabel Double where-  toRDFLabel = fromRealFloat xsdDouble-  --- | Converts from a literal with a @xsd:double@ datatype.-instance FromRDFLabel Double where-  fromRDFLabel = fLabel textToDouble xsdDouble-  --- TODO: are there subtypes of xsd::integer that are  ---       useful here?  ---         --- TODO: add in support for Int8/..., Word8/...  ---  ---- | Converts to a literal with a @xsd:integer@ datatype.-instance ToRDFLabel Int where-  toRDFLabel = tLabel xsdInteger T.pack--{--Since decimal will just over/under-flow when converting to Int-we go via Integer and explicitlu check for overflow.--}--textToInt :: T.Text -> Maybe Int-textToInt s = -  let conv :: Integer -> Maybe Int-      conv i = -        let lb = fromIntegral (minBound :: Int)-            ub = fromIntegral (maxBound :: Int)-        in if (i >= lb) && (i <= ub) then Just (fromIntegral i) else Nothing-  -  in maybeRead (T.signed T.decimal) s >>= conv---- | Converts from a literal with a @xsd:integer@ datatype.--- The conversion will fail if the value is outside the valid range of--- a Haskell `Int`.-instance FromRDFLabel Int where-  fromRDFLabel = fLabel textToInt xsdInteger---- | Converts to a literal with a @xsd:integer@ datatype.-instance ToRDFLabel Integer where-  toRDFLabel = tLabel xsdInteger T.pack---- | Converts from a literal with a @xsd:integer@ datatype.-instance FromRDFLabel Integer where-  fromRDFLabel = fLabel (maybeRead (T.signed T.decimal)) xsdInteger--{--Support an ISO-8601 style format supporting--  2005-02-28T00:00:00Z-  2004-12-31T19:01:00-05:00-  2005-07-14T03:18:56.234+01:00--fromUTCFormat is used to convert UTCTime to a string-for storage within a Lit.--toUTCFormat is used to convert a string into UTCTime;-we have to support -   no time zone-   Z-   +/-HH:MM--which means a somewhat messy convertor, which is written-for clarity rather than speed.--}--fromUTCFormat :: UTCTime -> String-fromUTCFormat = formatTime defaultTimeLocale "%FT%T%QZ"-  -fromDayFormat :: Day -> String-fromDayFormat = formatTime defaultTimeLocale "%FZ"-  -toTimeFormat :: (ParseTime a) => String -> String -> Maybe a-toTimeFormat fmt inVal =-  let fmtHHMM = fmt ++ "%z"-      fmtZ = fmt ++ "Z"-      pt f = parseTime defaultTimeLocale f inVal-  in case pt fmtHHMM of-    o@(Just _) -> o-    _ -> case pt fmtZ of-      o@(Just _) -> o-      _ -> pt fmt -  -toUTCFormat :: T.Text -> Maybe UTCTime-toUTCFormat = toTimeFormat "%FT%T%Q" . T.unpack-    -toDayFormat :: T.Text -> Maybe Day-toDayFormat = toTimeFormat "%F" . T.unpack-    --- | Converts to a literal with a @xsd:datetime@ datatype.-instance ToRDFLabel UTCTime where-  toRDFLabel = flip Lit (Just xsdDateTime) . T.pack . fromUTCFormat-  --- | Converts from a literal with a @xsd:datetime@ datatype.-instance FromRDFLabel UTCTime where-  fromRDFLabel = fLabel toUTCFormat xsdDateTime-  --- | Converts to a literal with a @xsd:date@ datatype.-instance ToRDFLabel Day where-  toRDFLabel = flip Lit (Just xsdDate) . T.pack . fromDayFormat---- | Converts from a literal with a @xsd:date@ datatype.-instance FromRDFLabel Day where-  fromRDFLabel = fLabel toDayFormat xsdDate-  --- | Converts to a Resource.-instance ToRDFLabel ScopedName where  -  toRDFLabel = Res---- | Converts from a Resource.-instance FromRDFLabel ScopedName where-  fromRDFLabel (Res sn) = Just sn-  fromRDFLabel _        = Nothing-  --- | Converts to a Resource.-instance ToRDFLabel QName where  -  toRDFLabel = Res . makeQNameScopedName Nothing-  --- | Converts from a Resource.-instance FromRDFLabel QName where-  fromRDFLabel (Res sn) = Just $ getQName sn-  fromRDFLabel _        = Nothing-  --- | Converts to a Resource.-instance ToRDFLabel URI where  -  toRDFLabel = Res . makeURIScopedName-  --- | Converts from a Resource.-instance FromRDFLabel URI where-  fromRDFLabel (Res sn) = Just $ getScopedNameURI sn-  fromRDFLabel _        = Nothing---- | Get the canonical string for RDF label.------  This is used for hashing, so that equivalent labels always return---  the same hash value.---    -showCanon :: RDFLabel -> String-showCanon (Res sn)           = "<"++show (getScopedNameURI sn)++">"-showCanon (Lit st (Just nam))-        | isLang nam = quote1Str st ++ "@"  ++ T.unpack (langTag nam)-        | otherwise  = quote1Str st ++ "^^" ++ show (getScopedNameURI nam)-showCanon s                  = show s---- | See `quote`.-quoteT :: Bool -> T.Text -> T.Text-quoteT f = T.pack . quote f . T.unpack --{-| N3-style quoting rules for a string.--TODO: when flag is `False` need to worry about multiple quotes (> 2)-in a row.--}--quote :: -  Bool  -- ^ @True@ if the string is to be displayed using one rather than three quotes.-  -> String -- ^ String to quote.-  -> String-quote _     []           = ""-quote False s@(c:'"':[]) | c == '\\'  = s -- handle triple-quoted strings ending in "-                         | otherwise  = [c, '\\', '"']--quote True  ('"': st)    = '\\':'"': quote True  st-quote True  ('\n':st)    = '\\':'n': quote True  st--quote True  ('\t':st)    = '\\':'t': quote True  st-quote False ('"': st)    =      '"': quote False st-quote False ('\n':st)    =     '\n': quote False st-quote False ('\t':st)    =     '\t': quote False st-quote f ('\r':st)    = '\\':'r': quote f st-quote f ('\\':st)    = '\\':'\\': quote f st -- not sure about this-quote f (c:st) = -  let nc = ord c-      rst = quote f st-      -      -- lazy way to convert to a string-      hstr = printf "%08X" nc-      ustr = hstr ++ rst--  in if nc > 0xffff -     then '\\':'U': ustr-     else if nc > 0x7e || nc < 0x20-          then '\\':'u': drop 4 ustr-          else c : rst---- surround a string with quotes ("...")--quote1Str :: T.Text -> String-quote1Str t = '"' : quote False (T.unpack t) ++ ['"']--------------------------------------------------------------  Selected RDFLabel values-------------------------------------------------------------- | @rdf:type@ from <http://www.w3.org/TR/rdf-schema/#ch_type>.-resRdfType :: RDFLabel-resRdfType = Res rdfType ---- | @rdf:List@ from <http://www.w3.org/TR/rdf-schema/#ch_list>.-resRdfList :: RDFLabel-resRdfList = Res rdfList---- | @rdf:first@ from <http://www.w3.org/TR/rdf-schema/#ch_first>.-resRdfFirst :: RDFLabel-resRdfFirst = Res rdfFirst ---- | @rdf:rest@ from <http://www.w3.org/TR/rdf-schema/#ch_rest>.-resRdfRest :: RDFLabel-resRdfRest = Res rdfRest---- | @rdf:nil@ from <http://www.w3.org/TR/rdf-schema/#ch_nil>.-resRdfNil :: RDFLabel-resRdfNil = Res rdfNil---- | @rdfs:member@ from <http://www.w3.org/TR/rdf-schema/#ch_member>.-resRdfsMember :: RDFLabel-resRdfsMember = Res rdfsMember---- | @rdfd:GeneralRestriction@.-resRdfdGeneralRestriction :: RDFLabel-resRdfdGeneralRestriction = Res rdfdGeneralRestriction---- | @rdfd:onProperties@.-resRdfdOnProperties :: RDFLabel-resRdfdOnProperties       = Res rdfdOnProperties---- | @rdfd:constraint@.-resRdfdConstraint :: RDFLabel-resRdfdConstraint         = Res rdfdConstraint---- | @rdfd:maxCardinality@.-resRdfdMaxCardinality :: RDFLabel-resRdfdMaxCardinality     = Res rdfdMaxCardinality---- | @rdfs:seeAlso@ from <http://www.w3.org/TR/rdf-schema/#ch_seealso>.-resRdfsSeeAlso :: RDFLabel-resRdfsSeeAlso = Res rdfsSeeAlso---- | @rdf:value@ from <http://www.w3.org/TR/rdf-schema/#ch_value>.-resRdfValue :: RDFLabel-resRdfValue = Res rdfValue---- | @owl:sameAs@.-resOwlSameAs :: RDFLabel-resOwlSameAs = Res owlSameAs---- | @log:implies@.-resLogImplies :: RDFLabel-resLogImplies = Res logImplies---- | @rdfs:label@ from <http://www.w3.org/TR/rdf-schema/#ch_label>.-resRdfsLabel :: RDFLabel-resRdfsLabel = Res rdfsLabel---- | @rdfs:comment@ from <http://www.w3.org/TR/rdf-schema/#ch_comment>.-resRdfsComment :: RDFLabel-resRdfsComment = Res rdfsComment---- | @rdf:Property@ from <http://www.w3.org/TR/rdf-schema/#ch_property>.-resRdfProperty :: RDFLabel-resRdfProperty = Res rdfProperty---- | @rdfs:subPropertyOf@ from <http://www.w3.org/TR/rdf-schema/#ch_subpropertyof>.-resRdfsSubPropertyOf :: RDFLabel-resRdfsSubPropertyOf = Res rdfsSubPropertyOf---- | @rdfs:subClassOf@ from <http://www.w3.org/TR/rdf-schema/#ch_subclassof>.-resRdfsSubClassOf :: RDFLabel-resRdfsSubClassOf = Res rdfsSubClassOf---- | @rdfs:Class@ from <http://www.w3.org/TR/rdf-schema/#ch_class>.-resRdfsClass :: RDFLabel-resRdfsClass = Res rdfsClass---- | @rdfs:Literal@ from <http://www.w3.org/TR/rdf-schema/#ch_literal>.-resRdfsLiteral :: RDFLabel-resRdfsLiteral = Res rdfsLiteral---- | @rdfs:Datatype@ from <http://www.w3.org/TR/rdf-schema/#ch_datatype>.-resRdfsDatatype :: RDFLabel-resRdfsDatatype = Res rdfsDatatype---- | @rdf:XMLLiteral@ from <http://www.w3.org/TR/rdf-schema/#ch_xmlliteral>.-resRdfXMLLiteral :: RDFLabel-resRdfXMLLiteral = Res rdfXMLLiteral---- | @rdfs:range@ from <http://www.w3.org/TR/rdf-schema/#ch_range>.-resRdfsRange :: RDFLabel-resRdfsRange = Res rdfsRange---- | @rdfs:domain@ from <http://www.w3.org/TR/rdf-schema/#ch_domain>.-resRdfsDomain :: RDFLabel-resRdfsDomain = Res rdfsDomain---- | @rdfs:Container@ from <http://www.w3.org/TR/rdf-schema/#ch_container>.-resRdfsContainer :: RDFLabel-resRdfsContainer = Res rdfsContainer---- | @rdf:Bag@ from <http://www.w3.org/TR/rdf-schema/#ch_bag>.-resRdfBag :: RDFLabel-resRdfBag = Res rdfBag---- | @rdf:Seq@ from <http://www.w3.org/TR/rdf-schema/#ch_seq>.-resRdfSeq :: RDFLabel-resRdfSeq = Res rdfSeq---- | @rdf:Alt@ from <http://www.w3.org/TR/rdf-schema/#ch_alt>.-resRdfAlt :: RDFLabel-resRdfAlt = Res rdfAlt---- | @rdfs:ContainerMembershipProperty@ from <http://www.w3.org/TR/rdf-schema/#ch_containermembershipproperty>.-resRdfsContainerMembershipProperty :: RDFLabel-resRdfsContainerMembershipProperty = Res rdfsContainerMembershipProperty---- | @rdfs:isDefinedBy@ from <http://www.w3.org/TR/rdf-schema/#ch_isdefinedby>.-resRdfsIsDefinedBy :: RDFLabel-resRdfsIsDefinedBy = Res rdfsIsDefinedBy---- | @rdfs:Resource@ from <http://www.w3.org/TR/rdf-schema/#ch_resource>.-resRdfsResource :: RDFLabel-resRdfsResource = Res rdfsResource---- | @rdf:Statement@ from <http://www.w3.org/TR/rdf-schema/#ch_statement>.-resRdfStatement :: RDFLabel-resRdfStatement = Res rdfStatement---- | @rdf:subject@ from <http://www.w3.org/TR/rdf-schema/#ch_subject>.-resRdfSubject :: RDFLabel-resRdfSubject = Res rdfSubject---- | @rdf:predicate@ from <http://www.w3.org/TR/rdf-schema/#ch_predicate>.-resRdfPredicate :: RDFLabel-resRdfPredicate = Res rdfPredicate---- | @rdf:object@ from <http://www.w3.org/TR/rdf-schema/#ch_object>.-resRdfObject :: RDFLabel-resRdfObject = Res rdfObject---- | @rdf:RDF@.-resRdfRDF :: RDFLabel-resRdfRDF = Res rdfRDF---- | @rdf:Description@.-resRdfDescription :: RDFLabel-resRdfDescription = Res rdfDescription---- | @rdf:ID@.-resRdfID :: RDFLabel-resRdfID = Res rdfID---- | @rdf:about@.-resRdfAbout :: RDFLabel-resRdfAbout = Res rdfAbout---- | @rdf:parseType@.-resRdfParseType :: RDFLabel-resRdfParseType = Res rdfParseType---- | @rdf:resource@.-resRdfResource :: RDFLabel-resRdfResource = Res rdfResource---- | @rdf:li@.-resRdfLi :: RDFLabel-resRdfLi = Res rdfLi---- | @rdf:nodeID@.-resRdfNodeID :: RDFLabel-resRdfNodeID = Res rdfNodeID---- | @rdf:datatype@.-resRdfDatatype :: RDFLabel-resRdfDatatype = Res rdfDatatype---- | @rdf:_1@.-resRdf1 :: RDFLabel-resRdf1 = Res rdf1---- | @rdf:_2@.-resRdf2 :: RDFLabel-resRdf2 = Res rdf2---- | Create a @rdf:_n@ entity.-resRdfn :: Int -> RDFLabel-resRdfn = Res . rdfn--------------------------------------------------------------  Additional functions on RDFLabel values-------------------------------------------------------------- |Test if supplied labal is a URI resource node-isUri :: RDFLabel -> Bool-isUri (Res _) = True-isUri  _      = False---- |Test if supplied labal is a literal node-isLiteral :: RDFLabel -> Bool-isLiteral (Lit _ _) = True-isLiteral  _        = False---- |Test if supplied labal is an untyped literal node-isUntypedLiteral :: RDFLabel -> Bool-isUntypedLiteral (Lit _ Nothing  ) = True-isUntypedLiteral (Lit _ (Just tn)) = isLang tn-isUntypedLiteral  _                = False---- |Test if supplied labal is an untyped literal node-isTypedLiteral :: RDFLabel -> Bool-isTypedLiteral (Lit _ (Just tn)) = not (isLang tn)-isTypedLiteral  _                = False---- |Test if supplied labal is an XML literal node-isXMLLiteral :: RDFLabel -> Bool-isXMLLiteral = isDatatyped rdfXMLLiteral---- |Test if supplied label is an typed literal node of a given datatype-isDatatyped :: ScopedName -> RDFLabel -> Bool-isDatatyped d  (Lit _ (Just n)) = n == d-isDatatyped _  _                = False---- |Test if supplied label is a container membership property------  Check for namespace is RDF namespace and---  first character of local name is '_' and---  remaining characters of local name are all digits-isMemberProp :: RDFLabel -> Bool-isMemberProp (Res sn) =-  getScopeNamespace sn == namespaceRDF &&-  case T.uncons (getScopeLocal sn) of-    Just ('_', t) -> T.all isDigit t-    _ -> False-isMemberProp _        = False---- |Test if supplied labal is a blank node-isBlank :: RDFLabel -> Bool-isBlank (Blank _) = True-isBlank  _        = False---- |Test if supplied labal is a query variable-isQueryVar :: RDFLabel -> Bool-isQueryVar (Var _) = True-isQueryVar  _      = False---- |Extract text value from a literal node-getLiteralText :: RDFLabel -> T.Text-getLiteralText (Lit s _) = s-getLiteralText  _        = ""---- |Extract ScopedName value from a resource node-getScopedName :: RDFLabel -> ScopedName-getScopedName (Res sn) = sn-getScopedName  _       = nullScopedName---- |Make a blank node from a supplied query variable,---  or return the supplied label unchanged.---  (Use this in when substituting an existential for an---  unsubstituted query variable.)-makeBlank :: RDFLabel -> RDFLabel-makeBlank  (Var loc)    = Blank loc-makeBlank  lb           = lb---- | RDF Triple (statement)--- ---   At present there is no check or type-level---   constraint that stops the subject or---   predicate of the triple from being a literal.----type RDFTriple = Arc RDFLabel---- | Convert 3 RDF labels to a RDF triple.------   See also @Swish.RDF.GraphClass.arcFromTriple@.-toRDFTriple :: -  (ToRDFLabel s, ToRDFLabel p, ToRDFLabel o) -  => s -- ^ Subject -  -> p -- ^ Predicate-  -> o -- ^ Object-  -> RDFTriple-toRDFTriple s p o = -  Arc (toRDFLabel s) (toRDFLabel p) (toRDFLabel o)---- | Extract the contents of a RDF triple.------   See also @Swish.RDF.GraphClass.arcToTriple@.-fromRDFTriple :: -  (FromRDFLabel s, FromRDFLabel p, FromRDFLabel o) -  => RDFTriple -  -> Maybe (s, p, o) -- ^ The conversion only succeeds if all three-                     --   components can be converted to the correct-                     --   Haskell types.-fromRDFTriple (Arc s p o) = -  (,,) <$> fromRDFLabel s <*> fromRDFLabel p <*> fromRDFLabel o-  --- | Namespace prefix list entry--type NamespaceMap = LookupMap Namespace--data RevNamespace = RevNamespace Namespace--instance LookupEntryClass RevNamespace URI (Maybe T.Text) where-    keyVal   (RevNamespace ns) = swap $ getNamespaceTuple ns-    newEntry (uri,pre) = RevNamespace (makeNamespace pre uri)--type RevNamespaceMap = LookupMap RevNamespace---- | Create an empty namespace map.-emptyNamespaceMap :: NamespaceMap-emptyNamespaceMap = LookupMap []---- | Graph formula entry--data LookupFormula lb gr = Formula-    { formLabel :: lb -- ^ The label for the formula-    , formGraph :: gr -- ^ The contents of the formula-    }--instance (Eq lb, Eq gr) => Eq (LookupFormula lb gr) where-    f1 == f2 = formLabel f1 == formLabel f2 &&-               formGraph f1 == formGraph f2--instance (Label lb)-    => LookupEntryClass (LookupFormula lb (NSGraph lb)) lb (NSGraph lb)-    where-        keyVal fe      = (formLabel fe, formGraph fe)-        newEntry (k,v) = Formula { formLabel=k, formGraph=v }--instance (Label lb) => Show (LookupFormula lb (NSGraph lb))-    where-        show (Formula l g) = show l ++ " :- { " ++ showArcs "    " g ++ " }"--type Formula lb = LookupFormula lb (NSGraph lb)--type FormulaMap lb = LookupMap (LookupFormula lb (NSGraph lb))---- | Create an empty formula map.-emptyFormulaMap :: FormulaMap RDFLabel-emptyFormulaMap = LookupMap []--{-  given up on trying to do Functor for formulae...-instance Functor (LookupFormula (NSGraph lb)) where-    fmap f fm = mapTranslateEntries (mapFormulaEntry f) fm--}--formulaeMap :: (lb -> l2) -> FormulaMap lb -> FormulaMap l2-formulaeMap f = fmap (formulaEntryMap f) --formulaEntryMap ::-    (lb -> l2)-    -> Formula lb-    -> Formula l2-formulaEntryMap f (Formula k gr) = Formula (f k) (fmap f gr)--formulaeMapA :: Applicative f => (lb -> f l2) -> -                FormulaMap lb -> f (FormulaMap l2)-formulaeMapA f = Traversable.traverse (formulaEntryMapA f)--formulaEntryMapA ::-  (Applicative f) => -  (lb -> f l2)-  -> Formula lb-  -> f (Formula l2)-formulaEntryMapA f (Formula k gr) = Formula `liftA` f k <*> Traversable.traverse f gr--{--formulaeMapM ::-    (Monad m) => (lb -> m l2) -> FormulaMap lb -> m (FormulaMap l2)-formulaeMapM f = T.mapM (formulaEntryMapM f)--formulaEntryMapM ::-    (Monad m)-    => (lb -> m l2)-    -> Formula lb-    -> m (Formula l2)-formulaEntryMapM f (Formula k gr) =-  Formula `liftM` f k `ap` T.mapM f gr-    --}--{-|--Memory-based graph with namespaces and subgraphs.--The primary means for adding arcs to an existing graph-are: -- - `setArcs` from the `LDGraph` instance, which replaces the -    existing set of arcs and does not change the namespace -    map.-- - `addArc` which checks that the arc is unknown before-    adding it but does not change the namespace map or-    re-label any blank nodes in the arc.---}-data NSGraph lb = NSGraph-    { namespaces :: NamespaceMap    -- ^ the namespaces to use-    , formulae   :: FormulaMap lb   -- ^ any associated formulae (a.k.a. sub- or named- graps)-    , statements :: [Arc lb]        -- ^ the statements in the graph-    }---- | Retrieve the namespace map in the graph.-getNamespaces :: NSGraph lb -> NamespaceMap-getNamespaces = namespaces---- | Replace the namespace information in the graph.-setNamespaces      :: NamespaceMap -> NSGraph lb -> NSGraph lb-setNamespaces ns g = g { namespaces=ns }---- | Retrieve the formulae in the graph.-getFormulae :: NSGraph lb -> FormulaMap lb-getFormulae = formulae---- | Replace the formulae in the graph.-setFormulae      :: FormulaMap lb -> NSGraph lb -> NSGraph lb-setFormulae fs g = g { formulae=fs }---- | Find a formula in the graph, if it exists.-getFormula     :: (Label lb) => NSGraph lb -> lb -> Maybe (NSGraph lb)-getFormula g l = mapFindMaybe l (formulae g)---- | Add (or replace) a formula.-setFormula     :: (Label lb) => Formula lb -> NSGraph lb -> NSGraph lb-setFormula f g = g { formulae=mapReplaceOrAdd f (formulae g) }--instance (Label lb) => Monoid (NSGraph lb) where-  mempty = NSGraph emptyNamespaceMap (LookupMap []) []-  mappend = merge-  -instance (Label lb) => LDGraph NSGraph lb where-    getArcs      = statements -    setArcs as g = g { statements=as }-    containedIn = error "containedIn for LDGraph NSGraph lb is undefined!" -- TODO: should there be one defined?--{-|-Add an arc to the graph. It does not relabel any blank nodes in the input arc,-nor does it change the namespace map, -but it does ensure that the arc is unknown before adding it.--}-addArc :: (Label lb) => Arc lb -> NSGraph lb -> NSGraph lb-addArc ar gr = gr { statements=addSetElem ar (statements gr) }--instance Functor NSGraph where-  fmap f (NSGraph ns fml stmts) =-    NSGraph ns (formulaeMap f fml) ((map $ fmap f) stmts)--instance Foldable.Foldable NSGraph where-  foldMap = Traversable.foldMapDefault--instance Traversable.Traversable NSGraph where-  traverse f (NSGraph ns fml stmts) = -    NSGraph ns <$> formulaeMapA f fml <*> (Traversable.traverse $ Traversable.traverse f) stmts-  -instance (Label lb) => Eq (NSGraph lb) where-    (==) = grEq--instance (Label lb) => Show (NSGraph lb) where-    show     = grShow ""-    showList = grShowList ""--grShowList :: (Label lb) => String -> [NSGraph lb] -> String -> String-grShowList _ []     = showString "[no graphs]"-grShowList p (g:gs) = showChar '[' . showString (grShow pp g) . showl gs-    where-        showl []     = showChar ']' -- showString $ "\n" ++ p ++ "]"-        showl (h:hs) = showString (",\n "++p++grShow pp h) . showl hs-        pp           = ' ':p--grShow   :: (Label lb) => String -> NSGraph lb -> String-grShow p g =-    "Graph, formulae: " ++ showForm ++ "\n" ++-    p ++ "arcs: " ++ showArcs p g-    where-        showForm = foldr ((++) . (pp ++) . show) "" fml-        fml = listLookupMap (getFormulae g)-        pp = "\n    " ++ p--showArcs :: (Label lb) => String -> NSGraph lb -> String-showArcs p g = foldr ((++) . (pp ++) . show) "" (getArcs g)-    where-        pp = "\n    " ++ p--grEq :: (Label lb) => NSGraph lb -> NSGraph lb -> Bool-grEq g1 = fst . grMatchMap g1--grMatchMap :: (Label lb) =>-    NSGraph lb -> NSGraph lb -> (Bool, LabelMap (ScopedLabel lb))-grMatchMap g1 g2 =-    graphMatch matchable (getArcs g1) (getArcs g2)-    where-        matchable l1 l2 = mapFormula g1 l1 == mapFormula g2 l2-        mapFormula g l  = mapFindMaybe l (getFormulae g)---- |Merge RDF graphs, renaming blank and query variable nodes as---  needed to neep variable nodes from the two graphs distinct in---  the resulting graph.---        ---  Currently formulae are not guaranteed to be preserved across a---  merge.---        -merge :: (Label lb) => NSGraph lb -> NSGraph lb -> NSGraph lb-merge gr1 gr2 =-    let-        bn1   = allLabels labelIsVar gr1-        bn2   = allLabels labelIsVar gr2-        dupbn = intersect bn1 bn2-        allbn = union bn1 bn2-    in-        add gr1 (remapLabels dupbn allbn id gr2)---- |Return list of all labels (including properties) in the graph---  satisfying a supplied filter predicate. This routine---  includes the labels in any formulae.-allLabels :: (Label lb) => (lb -> Bool) -> NSGraph lb -> [lb]-allLabels p gr = filter p (unionNodes p (formulaNodes p gr) (labels gr) ) -                 -{- TODO: is the leading 'filter p' needed in allLabels?--}---- |Return list of all subjects and objects in the graph---  satisfying a supplied filter predicate.-allNodes :: (Label lb) => (lb -> Bool) -> NSGraph lb -> [lb]-allNodes p = unionNodes p [] . nodes---- | List all nodes in graph formulae satisfying a supplied predicate-formulaNodes :: (Label lb) => (lb -> Bool) -> NSGraph lb -> [lb]-formulaNodes p gr = foldl' (unionNodes p) fkeys (map (allLabels p) fvals)-    where-        -- fm :: (Label lb) => FormulaMap lb-        --                     LookupMap LookupFormula (NSGraph lb) lb-        fm    = formulae gr-        -- fvals :: (Label lb) => [NSGraph lb]-        fvals = mapVals fm-        -- fkeys :: (Label lb) => [lb]-        fkeys = filter p $ mapKeys fm---- | Helper to filter variable nodes and merge with those found so far-unionNodes :: (Label lb) => (lb -> Bool) -> [lb] -> [lb] -> [lb]-unionNodes p ls1 ls2 = ls1 `union` filter p ls2---- |Remap selected nodes in graph.------  This is the node renaming operation that prevents graph-scoped---  variable nodes from being merged when two graphs are merged.-remapLabels ::-    (Label lb)-    => [lb] -- ^ variable nodes to be renamed (@dupbn@)-    -> [lb] -- ^ variable nodes used that must be avoided (@allbn@)-    -> (lb -> lb) -- ^ node conversion function that is applied to nodes-    -- from @dupbn@ in the graph that are to be replaced by-    -- new blank nodes.  If no such conversion is required,-    -- supply @id@.  The function 'makeBlank' can be used to convert-    -- RDF query nodes into RDF blank nodes.-    -> NSGraph lb -- ^ graph in which nodes are to be renamed-    -> NSGraph lb-remapLabels dupbn allbn cnvbn = fmap (mapnode dupbn allbn cnvbn)---- |Externally callable function to construct a list of (old,new)---  values to be used for graph label remapping.----remapLabelList ::-    (Label lb)-    => [lb] -- ^ labels to be remaped-    -> [lb] -- ^ labels to be avoided by the remapping-    -> [(lb,lb)]-remapLabelList remap avoid = maplist remap avoid id []---- | Remap a single graph node.------  If the node is not one of those to be remapped,---  the supplied value is returned unchanged.-mapnode ::-    (Label lb) => [lb] -> [lb] -> (lb -> lb) -> lb -> lb-mapnode dupbn allbn cnvbn nv =-    mapFind nv nv (LookupMap (maplist dupbn allbn cnvbn []))---- | Construct a list of (oldnode,newnode) values to be used for---  graph label remapping.  The function operates recursively, adding---  new nodes generated to the accumulator and also to the---  list of nodes to be avoided.-maplist ::-    (Label lb) -    => [lb]       -- ^ oldnode values-    -> [lb]       -- ^ nodes to be avoided-    -> (lb -> lb) -    -> [(lb,lb)]  -- ^ accumulator-    -> [(lb,lb)]-maplist []         _     _     mapbn = mapbn-maplist (dn:dupbn) allbn cnvbn mapbn = maplist dupbn allbn' cnvbn mapbn'-    where-        dnmap  = newNode (cnvbn dn) allbn-        mapbn' = (dn,dnmap):mapbn-        allbn' = dnmap:allbn---- |Given a node and a list of existing nodes, find a new node for---  the supplied node that does not clash with any existing node.---  (Generates an non-terminating list of possible replacements, and---  picks the first one that isn't already in use.)------  TODO: optimize this for common case @nnn@ and @_nnn@:---    always generate @_nnn@ and keep track of last allocated----newNode :: (Label lb) => lb -> [lb] -> lb-newNode dn existnodes =-    head $ newNodes dn existnodes---- |Given a node and a list of existing nodes, generate a list of new---  nodes for the supplied node that do not clash with any existing node.-newNodes :: (Label lb) => lb -> [lb] -> [lb]-newNodes dn existnodes =-    filter (not . (`elem` existnodes)) $ trynodes (noderootindex dn)--noderootindex :: (Label lb) => lb -> (String,Int)-noderootindex dn = (nh,nx) where-    (nh,nt) = splitnodeid $ getLocal dn-    nx      = if null nt then 0 else read nt--splitnodeid :: String -> (String,String)-splitnodeid dn = splitAt (tx+1) dn where-    tx = last $ (-1):findIndices (not . isDigit) dn--trynodes :: (Label lb) => (String,Int) -> [lb]-trynodes (nr,nx) = [ makeLabel (nr++show n) | n <- iterate (+1) nx ]--{--trybnodes :: (Label lb) => (String,Int) -> [lb]-trybnodes (nr,nx) = [ makeLabel (nr++show n) | n <- iterate (+1) nx ]--}---- | Memory-based RDF graph type--type RDFGraph = NSGraph RDFLabel---- |Create a new RDF graph from a supplied list of arcs------ This version will attempt to fill up the namespace map--- of the graph based on the input labels (including datatypes--- on literals). For faster--- creation of a graph you can use:------ > emptyRDFGraph { statements = arcs }------ which is how this routine was defined in version @0.3.1.1@--- and earlier.----toRDFGraph :: [RDFTriple] -> RDFGraph--- toRDFGraph arcs = emptyRDFGraph { statements = arcs }-toRDFGraph arcs = -  let lbls = concatMap arcLabels arcs-      -      getNS (Res s) = Just s-      getNS (Lit _ (Just tn)) | not (isLang tn) = Just tn-                              | otherwise = Nothing-      getNS _ = Nothing--      ns = mapMaybe (fmap getScopeNamespace . getNS) lbls-      nsmap = foldl' mapAddIfNew emptyNamespaceMap ns-  -  in mempty { namespaces = nsmap, statements = arcs }---- |Create a new, empty RDF graph.------ This uses `mempty` from the `Monoid` instance--- of `NSGraph`.----emptyRDFGraph :: RDFGraph-emptyRDFGraph = mempty --{---- |Update an RDF graph using a supplied list of arcs, keeping---  prefix definitions and formula definitions from the original.------  [[[TODO:  I think this may be redundant - the default graph---  class has an update method which accepts a function to update---  the arcs, not touching other parts of the graph value.]]]-updateRDFGraph :: RDFGraph -> [RDFTriple] -> RDFGraph-updateRDFGraph gr as = gr { statements=as }--}----------------------------------------------------------------------------------------  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  All rights reserved.------  This file is part of Swish.------  Swish is free software; you can redistribute it and/or modify---  it under the terms of the GNU General Public License as published by---  the Free Software Foundation; either version 2 of the License, or---  (at your option) any later version.------  Swish is distributed in the hope that it will be useful,---  but WITHOUT ANY WARRANTY; without even the implied warranty of---  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the---  GNU General Public License for more details.------  You should have received a copy of the GNU General Public License---  along with Swish; if not, write to:---    The Free Software Foundation, Inc.,---    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA------------------------------------------------------------------------------------
− src/Swish/RDF/RDFGraphShowM.hs
@@ -1,57 +0,0 @@-{-# LANGUAGE TypeSynonymInstances #-}-{-# LANGUAGE FlexibleInstances #-} -- needed for ghc 7.2-------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  RDFGraphShowM---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  License     :  GPL V2------  Maintainer  :  Douglas Burke---  Stability   :  experimental---  Portability :  TypeSynonymInstances, FlexibleInstances------  This module defines a `ShowM` class instance for `RDFGraph`, to be---  used when displaying RDF Graph values as part of a proof sequence,---  etc.--------------------------------------------------------------------------------------module Swish.RDF.RDFGraphShowM() where--import Swish.RDF.RDFGraph (RDFGraph)-import Swish.RDF.N3Formatter (formatGraphIndent)-import Swish.Utils.ShowM (ShowM(..))---- import qualified Data.Text.Lazy as L-import qualified Data.Text.Lazy.Builder as B--instance ShowM RDFGraph where-    -- showms linebreak = shows . L.unpack . B.toLazyText . formatGraphIndent linebreak False -    showms linebreak = shows . formatGraphIndent (B.fromString linebreak) False ----------------------------------------------------------------------------------------  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  All rights reserved.------  This file is part of Swish.------  Swish is free software; you can redistribute it and/or modify---  it under the terms of the GNU General Public License as published by---  the Free Software Foundation; either version 2 of the License, or---  (at your option) any later version.------  Swish is distributed in the hope that it will be useful,---  but WITHOUT ANY WARRANTY; without even the implied warranty of---  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the---  GNU General Public License for more details.------  You should have received a copy of the GNU General Public License---  along with Swish; if not, write to:---    The Free Software Foundation, Inc.,---    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA------------------------------------------------------------------------------------
− src/Swish/RDF/RDFParser.hs
@@ -1,353 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  RDFParser---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  License     :  GPL V2------  Maintainer  :  Douglas Burke---  Stability   :  experimental---  Portability :  OverloadedStrings------  Support for the RDF Parsing modules.--------------------------------------------------------------------------------------module Swish.RDF.RDFParser-    ( SpecialMap-    -- , mapPrefix-              -    -- tables-    , prefixTable, specialTable--    -- parser-    , runParserWithError-    , ParseResult-    , ignore-    , char-    , ichar-    , string-    , stringT-    , symbol-    , isymbol-    , lexeme-    , notFollowedBy-    , whiteSpace-    , skipMany-    , skipMany1-    , endBy-    , sepEndBy-    , sepEndBy1-    , manyTill-    , noneOf-    , eoln-    , fullStop-    , mkTypedLit-    , hex4-    , hex8-    , appendURIs-    )-    where--import Swish.RDF.RDFGraph (RDFGraph, RDFLabel(..))-import Swish.RDF.Vocabulary-    ( namespaceRDF-    , namespaceRDFS-    , namespaceRDFD-    , namespaceOWL-    , namespaceLOG-    , rdfType-    , rdfFirst, rdfRest, rdfNil-    , owlSameAs, logImplies-    , defaultBase-    )--import Swish.Utils.LookupMap (LookupMap(..))-import Swish.Utils.Namespace (Namespace, makeNamespace, ScopedName)--import qualified Data.Text      as T-import qualified Data.Text.Lazy as L-import qualified Data.Text.Read as R--import Text.ParserCombinators.Poly.StateText--import Network.URI (URI(..), relativeTo, parseURIReference)--import Data.Char (isSpace, isHexDigit, chr)-import Data.Maybe (fromMaybe, fromJust)---- Code---- | Append the two URIs. Should probably be moved---   out of RDFParser. It is also just a thin wrapper around---   `Network.URI.relativeTo`.--appendURIs ::-  URI     -- ^ The base URI-  -> URI  -- ^ The URI to append (it can be an absolute URI).-  -> Either String URI-appendURIs base uri =-  case uriScheme uri of-    "" -> case uri `relativeTo` base of-          Just out -> Right out-          _ -> Left $ "Unable to append <" ++ show uri ++ "> to base=<" ++ show base ++ ">"-    _  -> Right uri-  --- | Type for special name lookup table-type SpecialMap = LookupMap (String,ScopedName)--{---- | Lookup prefix in table and return the matching URI.------   If the prefix is unknown then we currently error---   out (used to return 'prefix:' or ':' but now using---   URIs I am changing this behavior). This may well be---   backed out.-mapPrefix :: NamespaceMap -> Maybe String -> URI-mapPrefix pmap pfix = -  case mapFindMaybe pfix pmap of-    Just uri -> uri-    Nothing  -> error $ "Unable to find prefix: " ++ show pfix -- fromMaybe "" pfix ++ ":"--}-  -{--mapPrefix ps p@(Just pre) = mapFind (pre++":") p ps-mapPrefix ps _ = mapFind ":" Nothing ps--}---- | Define default table of namespaces-prefixTable :: [Namespace]-prefixTable =   [ namespaceRDF-                , namespaceRDFS-                , namespaceRDFD     -- datatypes-                , namespaceOWL-                , namespaceLOG-                , makeNamespace Nothing $ fromJust (parseURIReference "#") -- is this correct?-                ]--{-|-Define default special-URI table.-The optional argument defines the initial base URI.--}-specialTable :: Maybe ScopedName -> [(String,ScopedName)]-specialTable mbase =-  [ ("a",         rdfType    ),-    ("equals",    owlSameAs  ),-    ("implies",   logImplies ),-    ("listfirst", rdfFirst   ),-    ("listrest",  rdfRest    ),-    ("listnull",  rdfNil     ),-    ("base",      fromMaybe defaultBase mbase ) -  ]---- Parser routines, heavily based on Parsec combinators---- | Run the parser and return the successful parse or an error--- message which consists of the standard Polyparse error plus--- a fragment of the unparsed input to provide context.----runParserWithError :: -  Parser a b -- ^ parser (carrying state) to apply-  -> a       -- ^ starting state for the parser-  -> L.Text       -- ^ input to be parsed-  -> Either String b-runParserWithError parser state0 input = -  let (result, _, unparsed) = runParser parser state0 input-     -      -- TODO: work out how best to report error context; for now just take the-      -- next 40 characters and assume there is enough context.-      econtext = if L.null unparsed-                 then "\n(at end of the text)\n"-                 else "\nRemaining input:\n" ++ -                      case L.compareLength unparsed 40 of-                        GT -> L.unpack (L.take 40 unparsed) ++ "..."-                        _ -> L.unpack unparsed--  in case result of-    Left emsg -> Left $ emsg ++ econtext-    _ -> result--type ParseResult = Either String RDFGraph--ignore :: (Applicative f) => f a -> f ()-ignore f = f *> pure ()--char :: Char -> Parser s Char-char c = satisfy (==c)--ichar :: Char -> Parser s ()-ichar = ignore . char---- TODO: is there a better way to do this?-string :: String -> Parser s String-string = mapM char-  -stringT :: T.Text -> Parser s T.Text-stringT s = string (T.unpack s) >> return s--skipMany :: Parser s a -> Parser s ()-skipMany = ignore . many-  -skipMany1 :: Parser s a -> Parser s ()-skipMany1 = ignore . many1-  -endBy :: Parser s a -> Parser s b -> Parser s [a]-endBy p sep = many (p <* sep)--sepEndBy :: Parser s a -> Parser s b -> Parser s [a]-sepEndBy p sep = sepEndBy1 p sep <|> pure []---- is the separator optional?-sepEndBy1 :: Parser s a -> Parser s b -> Parser s [a]-sepEndBy1 p sep = do-  x <- p-  (sep *> ((x:) <$> sepEndBy p sep)) <|> return [x]-  -manyTill :: Parser s a -> Parser s b -> Parser s [a]-manyTill p end = go-  where-    go = (end *> return [])-         <|>-         ((:) <$> p <*> go)---noneOf :: String -> Parser s Char           -noneOf istr = satisfy (`notElem` istr)-           -fullStop :: Parser s ()-fullStop = ichar '.'--eoln :: Parser s ()--- eoln = ignore (newline <|> (lineFeed *> optional newline))--- eoln = ignore (try (string "\r\n") <|> string "\r" <|> string "\n")-eoln = ignore (oneOf [string "\r\n", string "\r", string "\n"])-       -notFollowedBy :: (Char -> Bool) -> Parser s ()-notFollowedBy p = do-  c <- next-  if p c -    then fail $ "Unexpected character: " ++ show [c]-    else reparse $ L.singleton c--symbol :: String -> Parser s String-symbol = lexeme . string--isymbol :: String -> Parser s ()-isymbol = ignore . symbol--lexeme :: Parser s a -> Parser s a-lexeme p = p <* whiteSpace--whiteSpace :: Parser s ()-whiteSpace = skipMany (simpleSpace <|> oneLineComment)--simpleSpace :: Parser s ()-simpleSpace = ignore $ many1Satisfy isSpace--oneLineComment :: Parser s ()-oneLineComment = ichar '#' *> manySatisfy (/= '\n') *> pure ()--{---Not sure we can get this with polyparse---- | Annotate a Parsec error with the local context - i.e. the actual text--- that caused the error and preceeding/succeeding lines (if available)----annotateParsecError :: -    Int -- ^ the number of extra lines to include in the context (<=0 is ignored)-    -> [String] -- ^ text being parsed-    -> ParseError -- ^ the parse error-    -> String -- ^ Parsec error with additional context-annotateParsecError extraLines ls err = -    -- the following is based on the show instance of ParseError-    let ePos = errorPos err-        lNum = sourceLine ePos-        cNum = sourceColumn ePos-        -- it is possible to be at the end of the input so need-        -- to check; should produce better output than this in this-        -- case-        nLines = length ls-        ln1 = lNum - 1-        eln = max 0 extraLines-        lNums = [max 0 (ln1 - eln) .. min (nLines-1) (ln1 + eln)]-        -        beforeLines = map (ls !!) $ filter (< ln1) lNums-        afterLines  = map (ls !!) $ filter (> ln1) lNums-        -        -- in testing was able to get a line number after the text so catch this-        -- case; is it still necessary?-        errorLine = if ln1 >= nLines then "" else ls !! ln1-        arrowLine = replicate (cNum-1) ' ' ++ "^"-        finalLine = "(line " ++ show lNum ++ ", column " ++ show cNum ++ " indicated by the '^' sign above):"-        -        eHdr = "" : beforeLines ++ errorLine : arrowLine : afterLines ++ [finalLine]-        eMsg = showErrorMessages "or" "unknown parse error" "expecting" "unexpected" "end of input"-               (errorMessages err)--    in unlines eHdr ++ eMsg---}---- | Create a typed literal.-mkTypedLit ::-  ScopedName -- ^ the type-  -> T.Text  -- ^ the value-  -> RDFLabel-mkTypedLit u v = Lit v (Just u)--{--Handle hex encoding; the spec for N3 and NTriples suggest that-only upper-case A..F are valid but you can find lower-case values-out there so support these too.--}--hexDigit :: Parser a Char--- hexDigit = satisfy (`elem` ['0'..'9'] ++ ['A'..'F'])-hexDigit = satisfy isHexDigit--hex4 :: Parser a Char-hex4 = do-  digs <- exactly 4 hexDigit-  let mhex = R.hexadecimal (T.pack digs)-  case mhex of-    Left emsg     -> failBad $ "Internal error: unable to parse hex4: " ++ emsg-    Right (v, "") -> return $ chr v-    Right (_, vs) -> failBad $ "Internal error: hex4 remainder = " ++ T.unpack vs-        -hex8 :: Parser a Char-hex8 = do-  digs <- exactly 8 hexDigit-  let mhex = R.hexadecimal (T.pack digs)-  case mhex of-    Left emsg     -> failBad $ "Internal error: unable to parse hex8: " ++ emsg-    Right (v, "") -> if v <= 0x10FFFF-                     then return $ chr v-                     else failBad "\\UHHHHHHHH format is limited to a maximum of \\U0010FFFF"-    Right (_, vs) -> failBad $ "Internal error: hex8 remainder = " ++ T.unpack vs-        ---------------------------------------------------------------------------------------  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  All rights reserved.------  This file is part of Swish.------  Swish is free software; you can redistribute it and/or modify---  it under the terms of the GNU General Public License as published by---  the Free Software Foundation; either version 2 of the License, or---  (at your option) any later version.------  Swish is distributed in the hope that it will be useful,---  but WITHOUT ANY WARRANTY; without even the implied warranty of---  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the---  GNU General Public License for more details.------  You should have received a copy of the GNU General Public License---  along with Swish; if not, write to:---    The Free Software Foundation, Inc.,---    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA------------------------------------------------------------------------------------
− src/Swish/RDF/RDFProof.hs
@@ -1,380 +0,0 @@-{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  RDFProof---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  License     :  GPL V2------  Maintainer  :  Douglas Burke---  Stability   :  experimental---  Portability :  FlexibleInstances, UndecidableInstances------  This module instantiates the 'Proof' framework for---  constructing proofs over 'RDFGraph' expressions.---  The intent is that this can be used to test some---  correspondences between the RDF Model theory and---  corresponding proof theory based on closure rules---  applied to the graph, per <http://www.w3.org/TR/rdf-mt/>.--------------------------------------------------------------------------------------module Swish.RDF.RDFProof-    ( RDFProof, RDFProofStep-    , makeRDFProof, makeRDFProofStep-    , makeRdfInstanceEntailmentRule-    , makeRdfSubgraphEntailmentRule-    , makeRdfSimpleEntailmentRule )-where--import Swish.RDF.RDFQuery-    (  rdfQueryInstance-    , rdfQuerySubs -    )--import Swish.RDF.RDFRuleset-    ( RDFFormula, RDFRule, RDFRuleset )--import Swish.RDF.RDFGraph-    ( RDFLabel(..), RDFGraph-    --, makeBlank-    , merge , allLabels , remapLabelList-    {-, newNode, newNodes-    , toRDFGraph -}, emptyRDFGraph-    )--import Swish.RDF.VarBinding (makeVarBinding)--import Swish.RDF.Proof-    ( Proof(..), Step(..) )--import Swish.RDF.Rule-    ( Expression(..), Rule(..) )--import Swish.Utils.Namespace (ScopedName)--import Swish.RDF.GraphClass-    ( Label(..), LDGraph(..), replaceArcs )--import Swish.Utils.LookupMap-    ( makeLookupMap, mapFind )--import Swish.Utils.ListHelpers-    ( subset-    , powerSet-    , powerSequencesLen-    , flist-    )-----------------------------------------------------------------  Type instantiation of Proof framework for RDFGraph data-------------------------------------------------------------------  This is a partial instantiation of the proof framework.---  Details for applying inference rules are specific to the---  graph instance type.-----------------------------------------------------------------  Proof datatypes for graph values----------------------------------------------------------------- The following is an orphan instance---- |Instances of 'LDGraph' are also instance of the---  @Expression@ class, for which proofs can be constructed.---  The empty RDF graph is always @True@ (other enduring---  truths are asserted as axioms).-instance (Label lb, LDGraph lg lb) => Expression (lg lb) where-    isValid = null . getArcs -----------------------------------------------------------------  Define RDF-specific types for proof framework---------------------------------------------------------------type RDFProof     = Proof RDFGraph--type RDFProofStep = Step RDFGraph-----------------------------------------------------------------  Helper functions for constructing proofs on RDF graphs----------------------------------------------------------------- |Make an RDF graph proof step.----makeRDFProofStep ::-    RDFRule  -- ^ rule to use for this step-    -> [RDFFormula] -- ^ antecedent RDF formulae for this step-    -> RDFFormula -- ^ RDF formula that is the consequent for this step -    -> RDFProofStep-makeRDFProofStep rul ants con = Step-    { stepRule = rul-    , stepAnt  = ants-    , stepCon  = con-    }---- |Make an RDF proof.----makeRDFProof ::-    [RDFRuleset]      -- ^ RDF rulesets that constitute a proof context for this proof-    -> RDFFormula     -- ^ initial statement from which the goal is claimed to be proven-    -> RDFFormula     -- ^ statement that is claimed to be proven-    -> [RDFProofStep]-    -> RDFProof-makeRDFProof rsets base goal steps = Proof-    { proofContext = rsets-    , proofInput   = base-    , proofResult  = goal-    , proofChain   = steps-    }-----------------------------------------------------------------  RDF instance entailment inference rule----------------------------------------------------------------- |Make an inference rule dealing with RDF instance entailment;---  i.e. entailments that are due to replacement of a URI or literal---  node with a blank node.------  The part of this rule expected to be useful is 'checkInference'.---  The 'fwdApply' and 'bwdApply' functions defined here may return---  rather large results if applied to graphs with many variables or---  a large vocabulary, and are defined for experimentation.------  Forward and backward chaining is performed with respect to a---  specified vocabulary.  In the case of backward chaining, it would---  otherwise be impossible to bound the options thus generated.---  In the case of forward chaining, it is often not desirable to---  have the properties generalized.  If forward or backward backward---  chaining will not be used, supply an empty vocabulary.---  Note:  graph method 'allNodes' can be used to obtain a list of all---  the subjects and objuects used ina  graph, not counting nested---  formulae;  use a call of the form:------  >  allNodes (not . labelIsVar) graph----makeRdfInstanceEntailmentRule :: -  ScopedName     -- ^ name-  -> [RDFLabel]  -- ^ vocabulary-  -> RDFRule-makeRdfInstanceEntailmentRule name vocab = newrule-    where-        newrule = Rule-            { ruleName = name-            , fwdApply = rdfInstanceEntailFwdApply vocab-            , bwdApply = rdfInstanceEntailBwdApply vocab-            , checkInference = rdfInstanceEntailCheckInference-            }----  Instance entailment forward chaining------  Note:  unless the initial graph is small, the total result---  here could be very large.  The existential generalizations are---  sequenced in increasing number of substitutions applied.---  This sequencing is determined by the powerset function used,---  which generates subsets in increasing order of size---  (see module 'ListHelpers').------  The instances generated are all copies of the merge of the---  supplied graphs, with some or all of the non-variable nodes---  replaced by blank nodes.-rdfInstanceEntailFwdApply :: [RDFLabel] -> [RDFGraph] -> [RDFGraph]-rdfInstanceEntailFwdApply vocab ante =-    let-        --  Merge antecedents to single graph, renaming bnodes if needed.-        --  (Null test and using 'foldl1' to avoid merging if possible.)-        mergeGraph  = if null ante then emptyRDFGraph-                        else foldl1 merge ante-        --  Obtain lists of variable and non-variable nodes-        --  (was: nonvarNodes = allLabels (not . labelIsVar) mergeGraph)-        nonvarNodes = vocab-        varNodes    = allLabels labelIsVar mergeGraph-        --  Obtain list of possible remappings for non-variable nodes-        mapList     = remapLabelList nonvarNodes varNodes-        mapSubLists = powerSet mapList-        mapGr ls = fmap (\l -> mapFind l l (makeLookupMap ls))-    in-        --  Return all remappings of the original merged graph-        flist (map mapGr mapSubLists) mergeGraph----  Instance entailment backward chaining (for specified vocabulary)------  [[[TODO:  this is an incomplete implementation, there being no---  provision for instantiating some variables and leaving others---  alone.  This can be overcome in many cases by combining instance---  and subgraph chaining.---  Also, there is no provision for instantiating some variables in---  a triple and leaving others alone.  This may be fixed later if---  this function is really needed to be completely faithful to the---  precise notion of instance entailment.]]]-rdfInstanceEntailBwdApply :: [RDFLabel] -> RDFGraph -> [[RDFGraph]]-rdfInstanceEntailBwdApply vocab cons =-    let-        --  Obtain list of variable nodes-        varNodes     = allLabels labelIsVar cons-        --  Generate a substitution for each combination of variable-        --  and vocabulary node.-        varBindings  = map (makeVarBinding . zip varNodes) vocSequences-        vocSequences = powerSequencesLen (length varNodes) vocab-    in-        --  Generate a substitution for each combination of variable-        --  and vocabulary:-        [ rdfQuerySubs [v] cons | v <- varBindings ]----  Instance entailment inference checker-rdfInstanceEntailCheckInference :: [RDFGraph] -> RDFGraph -> Bool-rdfInstanceEntailCheckInference ante cons =-    let-        mante = if null ante then emptyRDFGraph -- merged antecedents-                    else foldl1 merge ante-        qvars = rdfQueryInstance cons mante     -- all query matches-        bsubs = rdfQuerySubs qvars cons         -- all back substitutions-    in-        --  Return True if any back-substitution matches the original-        --  merged antecendent graph.-        any (mante ==) bsubs----  Instance entailment notes.------  Relation to simple entailment (s-entails):------  (1) back-substitution yields original graph---  ex:s1 ex:p1 ex:o1  s-entails  ex:s1 ex:p1 _:o1  by [_:o1/ex:o1]------  (2) back-substitution yields original graph---  ex:s1 ex:p1 ex:o1  s-entails  ex:s1 ex:p1 _:o2  by [_:o2/ex:o1]---  ex:s1 ex:p1  _:o1             ex:s1 ex:p1 _:o3     [_:o3/_:o1]------  (3) back-substitution does not yield original graph---  ex:s1 ex:p1 ex:o1  s-entails  ex:s1 ex:p1 _:o2  by [_:o2/ex:o1]---  ex:s1 ex:p1  _:o1             ex:s1 ex:p1 _:o3     [_:o3/ex:o1]------  (4) consider---  ex:s1 ex:p1 ex:o1  s-entails  ex:s1 ex:p1 ex:o1---  ex:s1 ex:p1 ex:o2             ex:s1 ex:p1 ex:o2---  ex:s1 ex:p1 ex:o3             ex:s1 ex:p1 _:o1---                                ex:s1 ex:p1 _:o2---  where [_:o1/ex:o1,_:o2/ex:o2] yields a simple entailment but not---  an instance entailment, but [_:o1/ex:o3,_:o2/ex:o3] is also---  (arguably) an instance entailment.  Therefore, it is not sufficient---  to look only at the "largest" substitutions to determine instance---  entailment.------  All this means that when checking for instance entailment by---  back substitution, all of the query results must be checked.---  This seems clumsy.  If this function is heavily used with---  multiple query matches, a modified query that uses each---  triple of the target graph exactly once may be required.-----------------------------------------------------------------  RDF subgraph entailment inference rule----------------------------------------------------------------- |Make an inference rule dealing with RDF subgraph entailment.---  The part of this rule expected to be useful is 'checkInference'.---  The 'fwdApply' function defined here may return rather large---  results.  But in the name of completeness and experimentation---  with the possibilities of lazy evaluation, it has been defined.------  Backward chaining is not performed, as there is no reasonable way---  to choose a meaningful supergraph of that supplied.-makeRdfSubgraphEntailmentRule :: ScopedName -> RDFRule-makeRdfSubgraphEntailmentRule name = newrule-    where-        newrule = Rule-            { ruleName = name-            , fwdApply = rdfSubgraphEntailFwdApply-            , bwdApply = const []-            , checkInference = rdfSubgraphEntailCheckInference-            }----  Subgraph entailment forward chaining------  Note:  unless the initial graph is small, the total result---  here could be very large.  The subgraphs are sequenced in---  increasing size of the sub graph.  This sequencing is determined---  by the 'powerSet' function used which generates subsets in---  increasing order of size (see module 'ListHelpers').-rdfSubgraphEntailFwdApply :: [RDFGraph] -> [RDFGraph]-rdfSubgraphEntailFwdApply ante =-    let-        --  Merge antecedents to single graph, renaming bnodes if needed.-        --  (Null test and using 'foldl1' to avoid merging if possible.)-        mergeGraph  = if null ante then emptyRDFGraph-                        else foldl1 merge ante-    in-        --  Return all subgraphs of the full graph constructed above-        map (replaceArcs mergeGraph) (init $ powerSet $ getArcs mergeGraph)----  Subgraph entailment inference checker------  This is of dubious utiltiy, as it doesn't allow for node renaming.---  The simple entailment inference rule is probably more useful here.-rdfSubgraphEntailCheckInference :: [RDFGraph] -> RDFGraph -> Bool-rdfSubgraphEntailCheckInference ante cons =-    let-        --  Combine antecedents to single graph, renaming bnodes if needed.-        --  (Null test and using 'foldl1' to avoid merging if possible.)-        fullGraph  = if null ante then emptyRDFGraph-                        else foldl1 add ante-    in-        --  Check each consequent graph arc is in the antecedent graph-        getArcs cons `subset` getArcs fullGraph-----------------------------------------------------------------  RDF simple entailment inference rule----------------------------------------------------------------- |Make an inference rule dealing with RDF simple entailment.---  The part of this rule expected to be useful is 'checkInference'.---  The 'fwdApply' and 'bwdApply' functions defined return null---  results, indicating that they are not useful for the purposes---  of proof discovery.-makeRdfSimpleEntailmentRule :: ScopedName -> RDFRule-makeRdfSimpleEntailmentRule name = newrule-    where-        newrule = Rule-            { ruleName = name-            , fwdApply = const []-            , bwdApply = const []-            , checkInference = rdfSimpleEntailCheckInference-            }----  Simple entailment inference checker------  Note:  antecedents here are presumed to share bnodes.---         (Use 'merge' instead of 'add' for non-shared bnodes)----rdfSimpleEntailCheckInference :: [RDFGraph] -> RDFGraph -> Bool-rdfSimpleEntailCheckInference ante cons =-    let agr = if null ante then emptyRDFGraph else foldl1 add ante-    in-        not $ null $ rdfQueryInstance cons agr--{- original..-        not $ null $ rdfQueryInstance cons (foldl1 merge ante)--}----------------------------------------------------------------------------------------  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke  ---  All rights reserved.------  This file is part of Swish.------  Swish is free software; you can redistribute it and/or modify---  it under the terms of the GNU General Public License as published by---  the Free Software Foundation; either version 2 of the License, or---  (at your option) any later version.------  Swish is distributed in the hope that it will be useful,---  but WITHOUT ANY WARRANTY; without even the implied warranty of---  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the---  GNU General Public License for more details.------  You should have received a copy of the GNU General Public License---  along with Swish; if not, write to:---    The Free Software Foundation, Inc.,---    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA------------------------------------------------------------------------------------
− src/Swish/RDF/RDFProofContext.hs
@@ -1,848 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  RDFProofContext---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  License     :  GPL V2------  Maintainer  :  Douglas Burke---  Stability   :  experimental---  Portability :  OverloadedStrings------  This module contains proof-context declarations based on---  the RDF, RDFS, and RDF datatyping semantics specifications.---  These definitions consist of namespaces (for identification---  in proofs), axioms and inference rules.--------------------------------------------------------------------------------------module Swish.RDF.RDFProofContext ( rulesetRDF -                                 , rulesetRDFS-                                 , rulesetRDFD) where--import Swish.RDF.BuiltInDatatypes (findRDFDatatype)--import Swish.RDF.RDFProof-    ( makeRdfSubgraphEntailmentRule-    , makeRdfSimpleEntailmentRule )--import Swish.RDF.RDFRuleset -    ( RDFFormula, RDFRule, RDFRuleset -    , makeRDFFormula-    , makeN3ClosureRule-    , makeN3ClosureSimpleRule-    , makeN3ClosureModifyRule-    , makeN3ClosureAllocatorRule-    , makeNodeAllocTo )--import Swish.RDF.RDFVarBinding-    ( RDFVarBinding-    , RDFVarBindingModify-    , RDFVarBindingFilter-    , rdfVarBindingUriRef, rdfVarBindingBlank-    , rdfVarBindingLiteral-    , rdfVarBindingUntypedLiteral -    , rdfVarBindingXMLLiteral, rdfVarBindingDatatyped-    , rdfVarBindingMemberProp-    )--import Swish.RDF.RDFGraph-    ( RDFLabel(..)-    , isUri, isDatatyped-    , getLiteralText )--import Swish.RDF.VarBinding-    ( applyVarBinding-    , addVarBinding-    , VarBindingModify(..)-    , makeVarFilterModify-    , varFilterDisjunction-    )--import Swish.RDF.Ruleset (makeRuleset)-import Swish.RDF.Datatype (typeMkCanonicalForm)-import Swish.Utils.Namespace (Namespace, makeNSScopedName)--import Swish.RDF.Vocabulary-    ( namespaceRDFD-    , scopeRDF-    , scopeRDFS-    , scopeRDFD-    )--import Control.Monad (liftM)-import Data.Monoid (Monoid(..))-import Data.Maybe (isJust, fromJust)--import qualified Data.Text as T-import qualified Data.Text.Lazy.Builder as B-----------------------------------------------------------------  Define query binding filter auxiliaries---------------------------------------------------------------makeFormula :: Namespace -> T.Text -> B.Builder -> RDFFormula-makeFormula = makeRDFFormula--requireAny :: [RDFVarBindingFilter] -> RDFVarBindingFilter-requireAny = varFilterDisjunction--isLiteralV :: String -> RDFVarBindingFilter-isLiteralV = rdfVarBindingLiteral . Var--isUntypedLitV :: String -> RDFVarBindingFilter-isUntypedLitV = rdfVarBindingUntypedLiteral . Var--isXMLLitV :: String -> RDFVarBindingFilter-isXMLLitV = rdfVarBindingXMLLiteral . Var--isUriRefV :: String -> RDFVarBindingFilter-isUriRefV = rdfVarBindingUriRef . Var--isBlankV :: String -> RDFVarBindingFilter-isBlankV = rdfVarBindingBlank . Var--isDatatypedV :: String -> String -> RDFVarBindingFilter-isDatatypedV d l = rdfVarBindingDatatyped (Var d) (Var l)--isMemberPropV :: String -> RDFVarBindingFilter-isMemberPropV = rdfVarBindingMemberProp . Var--allocateTo :: String -> String -> [RDFLabel] -> RDFVarBindingModify-allocateTo bv av = makeNodeAllocTo (Var bv) (Var av)----  Create new binding for datatype-valueSame :: String -> String -> String -> String -> RDFVarBindingModify-valueSame val1 typ1 val2 typ2 =-    sameDatatypedValue (Var val1) (Var typ1) (Var val2) (Var typ2)----  Variable binding modifier to create new binding to a canonical---  form of a datatyped literal.-sameDatatypedValue ::-    RDFLabel -> RDFLabel -> RDFLabel -> RDFLabel -> RDFVarBindingModify-sameDatatypedValue val1 typ1 val2 typ2 = VarBindingModify-        { vbmName   = makeNSScopedName namespaceRDFD "sameValue"-        , vbmApply  = sameDatatypedValueApplyAll val1 typ1 val2 typ2-        , vbmVocab  = [val1,typ1,val2,typ2]-        , vbmUsage  = [[val2]]-        }--sameDatatypedValueApplyAll ::-    RDFLabel -> RDFLabel -> RDFLabel -> RDFLabel-    -> [RDFVarBinding]-    -> [RDFVarBinding]-sameDatatypedValueApplyAll val1 typ1 val2 typ2 =-    concatMap (sameDatatypedValueApply val1 typ1 val2 typ2) ----  Auxiliary function that handles variable binding updates---  for sameDatatypedValue-sameDatatypedValueApply ::-    RDFLabel -> RDFLabel -> RDFLabel -> RDFLabel-    -> RDFVarBinding-    -> [RDFVarBinding]-sameDatatypedValueApply val1 typ1 val2 typ2 vbind =-    result-    where-        v1    = applyVarBinding vbind val1-        t1    = applyVarBinding vbind typ1-        t2    = applyVarBinding vbind typ2-        sametype = getCanonical v1 t1 t2-        result   =-            if isUri t1 && isUri t2 then-                if t1 == t2 then-                    case sametype of-                      Just st -> [addVarBinding val2 st vbind]-                      _ -> []-                else-                    error "subtype conversions not yet defined"-            else-                []--getCanonical :: RDFLabel -> RDFLabel -> RDFLabel -> Maybe RDFLabel-getCanonical v1 t1 t2 =-    if isDatatyped dqn1 v1 && isJust mdt1 then-        liftM mkLit $ typeMkCanonicalForm dt1 (getLiteralText v1)-    else-        Nothing-    where-        dqn1  = getRes t1-        dqn2  = getRes t2-        mdt1  = findRDFDatatype dqn1-        dt1   = fromJust mdt1-        mkLit = flip Lit (Just dqn2)--        getRes (Res dqnam) = dqnam-        getRes x = error $ "Expected a Resource, sent " ++ show x -- for -Wall--{- -- Test data-qnamint = ScopedName namespaceXSD "integer"-xsdint  = Res qnamint-lab010  = Lit "010" (Just qnamint)-can010  = getCanonical lab010 xsdint xsdint-nsex    = Namespace "ex" "http://example.org/"-resexp  = Res (ScopedName nsex "p")-resexs  = Res (ScopedName nsex "s")--vara = Var "a"-varb = Var "b"-varc = Var "c"-vard = Var "d"-varp = Var "p"-vars = Var "s"-vart = Var "t"--vb1  = makeVarBinding [(vara,lab010),(varb,xsdint),(vard,xsdint)]-vb2  = sameDatatypedValueApply vara varb varc vard vb1-vb3  = vbmApply (sameDatatypedValue vara varb varc vard) [vb1]-vb3t = vb3 == vb2-vb4  = vbmApply (valueSame "a" "b" "c" "d") [vb1]-vb4t = vb4 == vb2-vb5  = vbmApply (valueSame "a" "b" "c" "b") [vb1]-vb5t = vb5 == vb2--vb6  = makeVarBinding [(vars,lab010),(varp,resexp),(vara,resexs),(vard,xsdint)]-vb7  = vbmApply (valueSame "s" "d" "t" "d") [vb6]-vb8  = makeVarBinding [(vars,lab010),(varp,resexp),(vara,resexs),(vard,xsdint)-                      ,(vart,fromJust can010)]-vb8t = vb7 == [vb8]--- -}-----------------------------------------------------------------  Common definitions------------------------------------------------------------------------------------------------------------------------------  Define RDF axioms----------------------------------------------------------------- scopeRDF  = Namespace "rs-rdf"  "http://id.ninebynine.org/2003/Ruleset/rdf#"----  RDF axioms (from RDF semantics document, section 3.1)------  (See also, container property rules below)----rdfa1 :: RDFFormula-rdfa1 = makeFormula scopeRDF "a1" "rdf:type      rdf:type rdf:Property ."--rdfa2 :: RDFFormula-rdfa2 = makeFormula scopeRDF "a2" "rdf:subject   rdf:type rdf:Property ."--rdfa3 :: RDFFormula-rdfa3 = makeFormula scopeRDF "a3" "rdf:predicate rdf:type rdf:Property ."--rdfa4 :: RDFFormula-rdfa4 = makeFormula scopeRDF "a4" "rdf:object    rdf:type rdf:Property ."--rdfa5 :: RDFFormula-rdfa5 = makeFormula scopeRDF "a5" "rdf:first     rdf:type rdf:Property ."--rdfa6 :: RDFFormula-rdfa6 = makeFormula scopeRDF "a6" "rdf:rest      rdf:type rdf:Property ."--rdfa7 :: RDFFormula-rdfa7 = makeFormula scopeRDF "a7" "rdf:value     rdf:type rdf:Property ."--rdfa8 :: RDFFormula-rdfa8 = makeFormula scopeRDF "a8" "rdf:nil       rdf:type rdf:List ."--axiomsRDF :: [RDFFormula]-axiomsRDF =-    [ rdfa1,  rdfa2,  rdfa3,  rdfa4,  rdfa5-    , rdfa6,  rdfa7,  rdfa8-    ]-----------------------------------------------------------------  Define RDF rules-----------------------------------------------------------------  RDF subgraph entailment (from RDF semantics document section 2)----rdfsub :: RDFRule -rdfsub = makeRdfSubgraphEntailmentRule (makeNSScopedName scopeRDF "sub")----  RDF simple entailment (from RDF semantics document section 7.1)---  (Note: rules se1 and se2 are combined here, because the scope of---  the "allocatedTo" modifier is the application of a single rule.)----rdfse :: RDFRule-rdfse = makeRdfSimpleEntailmentRule (makeNSScopedName scopeRDF "se")----  RDF bnode-for-literal assignments (from RDF semantics document section 7.1)----rdflg :: RDFRule-rdflg = makeN3ClosureAllocatorRule scopeRDF "lg"-            "?x  ?a ?l . "-            "?x  ?a ?b . ?b rdf:_allocatedTo ?l ."-            (makeVarFilterModify $ isLiteralV "l")-            (allocateTo "b" "l")----  RDF bnode-for-literal back-tracking (from RDF semantics document section 7.1)----rdfgl :: RDFRule-rdfgl = makeN3ClosureSimpleRule scopeRDF "gl"-            "?x  ?a ?l . ?b rdf:_allocatedTo ?l . "-            "?x  ?a ?b ."----  RDF entailment rules (from RDF semantics document section 7.2)------  (Note, statements with property rdf:_allocatedTo are introduced to---  track bnodes introduced according to rule rdflf [presumably this---  is actually rdflg?])----rdfr1 :: RDFRule-rdfr1 = makeN3ClosureSimpleRule scopeRDF "r1"-            "?x ?a ?y ."-            "?a rdf:type rdf:Property ."--rdfr2 :: RDFRule-rdfr2 = makeN3ClosureRule scopeRDF "r2"-            "?x  ?a ?b . ?b rdf:_allocatedTo ?l . "-            "?b rdf:type rdf:XMLLiteral ."-            (makeVarFilterModify $ isXMLLitV "l")----  Container property axioms (from RDF semantics document section 3.1)------  (Using here an inference rule with a filter in place of an axiom schema)------  This is a restricted form of the given axioms, in that the axioms---  are asserted only for container membership terms that appear in---  the graph.------  (This may be very inefficient for forward chaining when dealing with---  large graphs:  may need to look at query logic to see if the search for---  container membership properties can be optimized.  This may call for a---  custom inference rule.)----rdfcp1 :: RDFRule-rdfcp1 = makeN3ClosureRule scopeRDF "cp1"-            "?x  ?c ?y . "-            "?c rdf:type rdf:Property ."-            (makeVarFilterModify $ isMemberPropV "c")--rdfcp2 :: RDFRule-rdfcp2 = makeN3ClosureRule scopeRDF "cp2"-            "?c  ?p ?y . "-            "?c rdf:type rdf:Property ."-            (makeVarFilterModify $ isMemberPropV "c")--rdfcp3 :: RDFRule-rdfcp3 = makeN3ClosureRule scopeRDF "cp3"-            "?x  ?p ?c . "-            "?c rdf:type rdf:Property ."-            (makeVarFilterModify $ isMemberPropV "c")----  Collect RDF rules----rulesRDF :: [RDFRule]-rulesRDF =-    [ rdfsub,     rdfse-    , rdflg,      rdfgl-    , rdfr1,      rdfr2-    , rdfcp1,     rdfcp2,     rdfcp3-    ]---- | Ruleset for RDF inference.--rulesetRDF :: RDFRuleset-rulesetRDF = makeRuleset scopeRDF axiomsRDF rulesRDF-----------------------------------------------------------------  Define RDFS axioms----------------------------------------------------------------- scopeRDFS = Namespace "rdfs" "http://id.ninebynine.org/2003/Ruleset/rdfs#"----  RDFS axioms (from RDF semantics document, section 4.1)------  (See also, container property rules below)-----rdfsa01 :: RDFFormula-rdfsa01 = makeFormula scopeRDFS "a01"-    "rdf:type           rdfs:domain rdfs:Resource ."--rdfsa02 :: RDFFormula-rdfsa02 = makeFormula scopeRDFS "a02"-    "rdf:type           rdfs:range  rdfs:Class ."--rdfsa03 :: RDFFormula-rdfsa03 = makeFormula scopeRDFS "a03"-    "rdfs:domain        rdfs:domain rdf:Property ."--rdfsa04 :: RDFFormula-rdfsa04 = makeFormula scopeRDFS "a04"-    "rdfs:domain        rdfs:range  rdfs:Class ."--rdfsa05 :: RDFFormula-rdfsa05 = makeFormula scopeRDFS "a05"-    "rdfs:range         rdfs:domain rdf:Property ."--rdfsa06 :: RDFFormula-rdfsa06 = makeFormula scopeRDFS "a06"-    "rdfs:range         rdfs:range  rdfs:Class ."--rdfsa07 :: RDFFormula-rdfsa07 = makeFormula scopeRDFS "a07"-    "rdfs:subPropertyOf rdfs:domain rdf:Property ."--rdfsa08 :: RDFFormula-rdfsa08 = makeFormula scopeRDFS "a08"-    "rdfs:subPropertyOf rdfs:range  rdf:Property ."--rdfsa09 :: RDFFormula-rdfsa09 = makeFormula scopeRDFS "a09"-    "rdfs:subClassOf    rdfs:domain rdfs:Class ."--rdfsa10 :: RDFFormula-rdfsa10 = makeFormula scopeRDFS "a10"-    "rdfs:subClassOf    rdfs:range  rdfs:Class ."--rdfsa11 :: RDFFormula-rdfsa11 = makeFormula scopeRDFS "a11"-    "rdf:subject        rdfs:domain rdf:Statement ."--rdfsa12 :: RDFFormula-rdfsa12 = makeFormula scopeRDFS "a12"-    "rdf:subject        rdfs:range  rdfs:Resource ."--rdfsa13 :: RDFFormula-rdfsa13 = makeFormula scopeRDFS "a13"-    "rdf:predicate      rdfs:domain rdf:Statement ."--rdfsa14 :: RDFFormula-rdfsa14 = makeFormula scopeRDFS "a14"-    "rdf:predicate      rdfs:range  rdfs:Resource ."--rdfsa15 :: RDFFormula-rdfsa15 = makeFormula scopeRDFS "a15"-    "rdf:object         rdfs:domain rdf:Statement ."--rdfsa16 :: RDFFormula-rdfsa16 = makeFormula scopeRDFS "a16"-    "rdf:object         rdfs:range  rdfs:Resource ."--rdfsa17 :: RDFFormula-rdfsa17 = makeFormula scopeRDFS "a17"-    "rdfs:member        rdfs:domain rdfs:Resource ."--rdfsa18 :: RDFFormula-rdfsa18 = makeFormula scopeRDFS "a18"-    "rdfs:member        rdfs:range  rdfs:Resource ."--rdfsa19 :: RDFFormula-rdfsa19 = makeFormula scopeRDFS "a19"-    "rdf:first          rdfs:domain rdf:List ."--rdfsa20 :: RDFFormula-rdfsa20 = makeFormula scopeRDFS "a20"-    "rdf:first          rdfs:range  rdfs:Resource ."--rdfsa21 :: RDFFormula-rdfsa21 = makeFormula scopeRDFS "a21"-    "rdf:rest           rdfs:domain rdf:List ."--rdfsa22 :: RDFFormula-rdfsa22 = makeFormula scopeRDFS "a22"-    "rdf:rest           rdfs:range  rdf:List ."--rdfsa23 :: RDFFormula-rdfsa23 = makeFormula scopeRDFS "a23"-    "rdfs:seeAlso       rdfs:domain rdfs:Resource ."--rdfsa24 :: RDFFormula-rdfsa24 = makeFormula scopeRDFS "a24"-    "rdfs:seeAlso       rdfs:range  rdfs:Resource ."--rdfsa25 :: RDFFormula-rdfsa25 = makeFormula scopeRDFS "a25"-    "rdfs:isDefinedBy   rdfs:domain rdfs:Resource ."--rdfsa26 :: RDFFormula-rdfsa26 = makeFormula scopeRDFS "a26"-    "rdfs:isDefinedBy   rdfs:range  rdfs:Resource ."--rdfsa27 :: RDFFormula-rdfsa27 = makeFormula scopeRDFS "a27"-    "rdfs:isDefinedBy   rdfs:subPropertyOf rdfs:seeAlso ."--rdfsa28 :: RDFFormula-rdfsa28 = makeFormula scopeRDFS "a28"-    "rdfs:comment       rdfs:domain rdfs:Resource ."--rdfsa29 :: RDFFormula-rdfsa29 = makeFormula scopeRDFS "a29"-    "rdfs:comment       rdfs:range  rdfs:Literal ."--rdfsa30 :: RDFFormula-rdfsa30 = makeFormula scopeRDFS "a30"-    "rdfs:label         rdfs:domain rdfs:Resource ."--rdfsa31 :: RDFFormula-rdfsa31 = makeFormula scopeRDFS "a31"-    "rdfs:label         rdfs:range  rdfs:Literal ."--rdfsa32 :: RDFFormula-rdfsa32 = makeFormula scopeRDFS "a32"-    "rdf:value          rdfs:domain rdfs:Resource ."--rdfsa33 :: RDFFormula-rdfsa33 = makeFormula scopeRDFS "a33"-    "rdf:value          rdfs:range  rdfs:Resource ."--rdfsa34 :: RDFFormula-rdfsa34 = makeFormula scopeRDFS "a34"-    "rdf:Alt            rdfs:subClassOf    rdfs:Container ."--rdfsa35 :: RDFFormula-rdfsa35 = makeFormula scopeRDFS "a35"-    "rdf:Bag            rdfs:subClassOf    rdfs:Container ."--rdfsa36 :: RDFFormula-rdfsa36 = makeFormula scopeRDFS "a36"-    "rdf:Seq            rdfs:subClassOf    rdfs:Container ."--rdfsa37 :: RDFFormula-rdfsa37 = makeFormula scopeRDFS "a37"-    "rdfs:ContainerMembershipProperty rdfs:subClassOf rdf:Property ."--rdfsa38 :: RDFFormula-rdfsa38 = makeFormula scopeRDFS "a38"-    "rdf:XMLLiteral     rdf:type           rdfs:Datatype ."--rdfsa39 :: RDFFormula-rdfsa39 = makeFormula scopeRDFS "a39"-    "rdf:XMLLiteral     rdfs:subClassOf    rdfs:Literal ."--rdfsa40 :: RDFFormula-rdfsa40 = makeFormula scopeRDFS "a40"-    "rdfs:Datatype      rdfs:subClassOf    rdfs:Class ."--axiomsRDFS :: [RDFFormula]-axiomsRDFS =-    [          rdfsa01, rdfsa02, rdfsa03, rdfsa04-    , rdfsa05, rdfsa06, rdfsa07, rdfsa08, rdfsa09-    , rdfsa10, rdfsa11, rdfsa12, rdfsa13, rdfsa14-    , rdfsa15, rdfsa16, rdfsa17, rdfsa18, rdfsa19-    , rdfsa20, rdfsa21, rdfsa22, rdfsa23, rdfsa24-    , rdfsa25, rdfsa26, rdfsa27, rdfsa28, rdfsa29-    , rdfsa30, rdfsa31, rdfsa32, rdfsa33, rdfsa34-    , rdfsa35, rdfsa36, rdfsa37, rdfsa38, rdfsa39-    , rdfsa40-    ]-----------------------------------------------------------------  Define RDFS rules---------------------------------------------------------------{--rdfr2 = makeN3ClosureRule scopeRDF "r2"-            "?x  ?a ?b . ?b rdf:_allocatedTo ?l . "-            "?b rdf:type rdf:XMLLiteral ."-            (makeVarFilterModify $ isXMLLit "?l")--}----  RDFS entailment rules (from RDF semantics document section 7.2)------  (Note, statements with property rdf:_allocatedTo are introduced to---  track bnodes introduced according to rule rdflf [presumably this---  is actually rdflg?])----rdfsr1 :: RDFRule-rdfsr1 = makeN3ClosureRule scopeRDFS "r1"-            "?x  ?a ?b . ?b rdf:_allocatedTo ?l . "-            "?b rdf:type rdfs:Literal ."-            (makeVarFilterModify $ isUntypedLitV "l" )--rdfsr2 :: RDFRule-rdfsr2 = makeN3ClosureSimpleRule scopeRDFS "r2"-            "?x ?a ?y . ?a rdfs:domain ?z ."-            "?x rdf:type ?z ."--rdfsr3 :: RDFRule-rdfsr3 = makeN3ClosureRule scopeRDFS "r3"-            "?u ?a ?v . ?a rdfs:range ?z ."-            "?v rdf:type ?z ."-            (makeVarFilterModify $ requireAny [isUriRefV "v",isBlankV "v"])--rdfsr4a :: RDFRule-rdfsr4a = makeN3ClosureSimpleRule scopeRDFS "r4a"-            "?x ?a ?y ."-            "?x rdf:type rdfs:Resource ."--rdfsr4b :: RDFRule-rdfsr4b = makeN3ClosureRule scopeRDFS "r4b"-            "?x ?a ?u ."-            "?u rdf:type rdfs:Resource ."-            (makeVarFilterModify $ requireAny [isUriRefV "u",isBlankV "u"])--rdfsr5 :: RDFRule-rdfsr5  = makeN3ClosureSimpleRule scopeRDFS "r5"-            "?a rdfs:subPropertyOf ?b . ?b rdfs:subPropertyOf ?c ."-            "?a rdfs:subPropertyOf ?c ."--rdfsr6 :: RDFRule-rdfsr6  = makeN3ClosureSimpleRule scopeRDFS "r6"-            "?x rdf:type rdf:Property ."-            "?x rdfs:subPropertyOf ?x ."--rdfsr7 :: RDFRule-rdfsr7  = makeN3ClosureSimpleRule scopeRDFS "r7"-            "?x ?a ?y . ?a rdfs:subPropertyOf ?b ."-            "?x ?b ?y ."--rdfsr8 :: RDFRule-rdfsr8  = makeN3ClosureSimpleRule scopeRDFS "r8"-            "?x rdf:type rdfs:Class ."-            "?x rdfs:subClassOf rdfs:Resource ."--rdfsr9 :: RDFRule-rdfsr9  = makeN3ClosureSimpleRule scopeRDFS "r9"-            "?x rdfs:subClassOf ?y . ?a rdf:type ?x ."-            "?a rdf:type ?y ."--rdfsr10 :: RDFRule-rdfsr10 = makeN3ClosureSimpleRule scopeRDFS "r10"-            "?x rdf:type rdfs:Class ."-            "?x rdfs:subClassOf ?x ."--rdfsr11 :: RDFRule-rdfsr11 = makeN3ClosureSimpleRule scopeRDFS "r11"-            "?x rdfs:subClassOf ?y . ?y rdfs:subClassOf ?z ."-            "?x rdfs:subClassOf ?z ."--rdfsr12 :: RDFRule-rdfsr12 = makeN3ClosureSimpleRule scopeRDFS "r12"-            "?x rdf:type rdfs:ContainerMembershipProperty ."-            "?x rdfs:subPropertyOf rdfs:member ."--rdfsr13 :: RDFRule-rdfsr13 = makeN3ClosureSimpleRule scopeRDFS "r13"-            "?x rdf:type rdfs:Datatype ."-            "?x rdfs:subClassOf rdfs:Literal ."----  These are valid only under an extensional strengthening of RDFS,---  discussed in section 7.3.1 of the RDF semantics specification:--{--rdfsrext1 :: RDFRule-rdfsrext1 = makeN3ClosureSimpleRule scopeRDFS "ext1"-            "?x rdfs:domain ?y . ?y rdfs:subClassOf ?z ."-            "?x rdfs:domain ?z ."--rdfsrext2 :: RDFRule-rdfsrext2 = makeN3ClosureSimpleRule scopeRDFS "ext2"-            "?x rdfs:range ?y . ?y rdfs:subClassOf ?z ."-            "?x rdfs:range ?z ."--rdfsrext3 :: RDFRule-rdfsrext3 = makeN3ClosureSimpleRule scopeRDFS "ext3"-            "?x rdfs:domain ?y . ?z rdfs:subPropertyOf ?x ."-            "?z rdfs:domain ?y ."--rdfsrext4 :: RDFRule-rdfsrext4 = makeN3ClosureSimpleRule scopeRDFS "ext4"-            "?x rdfs:range ?y . ?z rdfs:subPropertyOf ?x ."-            "?z rdfs:range ?y ."--rdfsrext5 :: RDFRule-rdfsrext5 = makeN3ClosureSimpleRule scopeRDFS "ext5"-            "rdf:type rdfs:subPropertyOf ?z . ?z rdfs:domain ?y ."-            "rdfs:Resource rdfs:subClassOf ?y ."--rdfsrext6 :: RDFRule-rdfsrext6 = makeN3ClosureSimpleRule scopeRDFS "rext6"-            "rdfs:subClassOf rdfs:subPropertyOf ?z . ?z rdfs:domain ?y ."-            "rdfs:Class rdfs:subClassOf ?y ."--rdfsrext7 :: RDFRule-rdfsrext7 = makeN3ClosureSimpleRule scopeRDFS "rext7"-            "rdfs:subPropertyOf rdfs:subPropertyOf ?z . ?z rdfs:domain ?y ."-            "rdfs:Property rdfs:subClassOf ?y ."--rdfsrext8 :: RDFRule-rdfsrext8 = makeN3ClosureSimpleRule scopeRDFS "rext8"-            "rdfs:subClassOf rdfs:subPropertyOf ?z . ?z rdfs:range ?y ."-            "rdfs:Class rdfs:subClassOf ?y ."--rdfsrext9 :: RDFRule-rdfsrext9 = makeN3ClosureSimpleRule scopeRDFS "rext9"-            "rdfs:subPropertyOf rdfs:subPropertyOf ?z . ?z rdfs:range ?y ."-            "rdfs:Property rdfs:subClassOf ?y ."---}----  Container property axioms (from RDF semantics document section 4.1)------  (Using here an inference rule with a filter in place of an axiom schema)------  This is a restricted form of the given axioms, in that the axioms---  are asserted only for container membership terms that appear in---  the graph.------  (This may be very inefficient for forward chaining when dealing with---  large graphs:  may need to look at query logic to see if the search for---  container membership properties can be optimized.  This may call for a---  custom inference rule.)----rdfscp11 :: RDFRule-rdfscp11 = makeN3ClosureRule scopeRDFS "cp11"-            "?x  ?c ?y . "-            "?c rdf:type rdfs:ContainerMembershipProperty ."-            (makeVarFilterModify $ isMemberPropV "c")--rdfscp12 :: RDFRule-rdfscp12 = makeN3ClosureRule scopeRDFS "cp12"-            "?c  ?p ?y . "-            "?c rdf:type rdfs:ContainerMembershipProperty ."-            (makeVarFilterModify $ isMemberPropV "c")--rdfscp13 :: RDFRule-rdfscp13 = makeN3ClosureRule scopeRDFS "cp13"-            "?x  ?p ?c . "-            "?c rdf:type rdfs:ContainerMembershipProperty ."-            (makeVarFilterModify $ isMemberPropV "c")--rdfscp21 :: RDFRule-rdfscp21 = makeN3ClosureRule scopeRDFS "cp21"-            "?x  ?c ?y . "-            "?c rdfs:domain rdfs:Resource ."-            (makeVarFilterModify $ isMemberPropV "c")--rdfscp22 :: RDFRule-rdfscp22 = makeN3ClosureRule scopeRDFS "cp22"-            "?c  ?p ?y . "-            "?c rdfs:domain rdfs:Resource ."-            (makeVarFilterModify $ isMemberPropV "c")--rdfscp23 :: RDFRule-rdfscp23 = makeN3ClosureRule scopeRDFS "cp23"-            "?x  ?p ?c . "-            "?c rdfs:domain rdfs:Resource ."-            (makeVarFilterModify $ isMemberPropV "c")--rdfscp31 :: RDFRule-rdfscp31 = makeN3ClosureRule scopeRDFS "cp31"-            "?x  ?c ?y . "-            "?c rdfs:range rdfs:Resource ."-            (makeVarFilterModify $ isMemberPropV "c")--rdfscp32 :: RDFRule-rdfscp32 = makeN3ClosureRule scopeRDFS "cp32"-            "?c  ?p ?y . "-            "?c rdfs:range rdfs:Resource ."-            (makeVarFilterModify $ isMemberPropV "c")--rdfscp33 :: RDFRule-rdfscp33 = makeN3ClosureRule scopeRDFS "cp33"-            "?x  ?p ?c . "-            "?c rdfs:range rdfs:Resource ."-            (makeVarFilterModify $ isMemberPropV "c")----  Collect RDFS rules----rulesRDFS :: [RDFRule]-rulesRDFS =-    [ rdfsr1,    rdfsr2,    rdfsr3,    rdfsr4a,   rdfsr4b-    , rdfsr5,    rdfsr6,    rdfsr7,    rdfsr8,    rdfsr9-    , rdfsr10,   rdfsr11,   rdfsr12,   rdfsr13-    , rdfscp11,   rdfscp12,   rdfscp13-    , rdfscp21,   rdfscp22,   rdfscp23-    , rdfscp31,   rdfscp32,   rdfscp33-    ]---- | Ruleset for RDFS inference.--rulesetRDFS :: RDFRuleset-rulesetRDFS = makeRuleset scopeRDFS axiomsRDFS rulesRDFS-----------------------------------------------------------------  Define RDFD (datatyping) axioms----------------------------------------------------------------- scopeRDFD = Namespace "rdfd" "http://id.ninebynine.org/2003/Ruleset/rdfd#"--axiomsRDFD :: [RDFFormula]-axiomsRDFD =-    [-    ]-----------------------------------------------------------------  Define RDFD (datatyping) axioms-----------------------------------------------------------------  RDFD closure rules from semantics document, section 7.4----  Infer type of datatyped literal----rdfdr1 :: RDFRule-rdfdr1 = makeN3ClosureRule scopeRDFD "r1"-            "?d rdf:type rdfs:Datatype . ?a ?p ?l . ?b rdf:_allocatedTo ?l . "-            "?b rdf:type ?d ."-            (makeVarFilterModify $ isDatatypedV "d" "l")----  Equivalent literals with same datatype:---  (generate canonical form, or operate in proof mode only)----rdfdr2 :: RDFRule-rdfdr2 = makeN3ClosureRule scopeRDFD "r2"-            "?d rdf:type rdfs:Datatype . ?a ?p ?s ."-            "?a ?p ?t ."-            (valueSame "s" "d" "t" "d")--{- Note that valueSame does datatype check.  Otherwise use:-rdfdr2 = makeN3ClosureModifyRule scopeRDFD "r2"-            "?d rdf:type rdfs:Datatype . ?a ?p ?s ."-            "?a ?p ?t ."-            (makeVarFilterModify $ isDatatypedV "d" "s")-            (valueSame "s" "d" "t" "d")--}----  Equivalent literals with different datatypes:---  (generate canonical form, or operate in proof mode only)----rdfdr3 :: RDFRule-rdfdr3 = makeN3ClosureModifyRule scopeRDFD "r3"-            ( "?d rdf:type rdfs:Datatype . ?e rdf:type rdfs:Datatype . " `mappend`-              "?a ?p ?s ." )-            "?a ?p ?t ."-            (makeVarFilterModify $ isDatatypedV "s" "d")-            (valueSame "s" "d" "t" "e")----  Collect RDFD rules----rulesRDFD :: [RDFRule]-rulesRDFD =-    [ rdfdr1, rdfdr2, rdfdr3-    ]---- | Ruleset for RDFD (datatyping) inference.----rulesetRDFD :: RDFRuleset-rulesetRDFD = makeRuleset scopeRDFD axiomsRDFD rulesRDFD----------------------------------------------------------------------------------------  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke  ---  All rights reserved.------  This file is part of Swish.------  Swish is free software; you can redistribute it and/or modify---  it under the terms of the GNU General Public License as published by---  the Free Software Foundation; either version 2 of the License, or---  (at your option) any later version.------  Swish is distributed in the hope that it will be useful,---  but WITHOUT ANY WARRANTY; without even the implied warranty of---  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the---  GNU General Public License for more details.------  You should have received a copy of the GNU General Public License---  along with Swish; if not, write to:---    The Free Software Foundation, Inc.,---    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA------------------------------------------------------------------------------------
− src/Swish/RDF/RDFQuery.hs
@@ -1,636 +0,0 @@------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  RDFQuery---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  License     :  GPL V2------  Maintainer  :  Douglas Burke---  Stability   :  experimental---  Portability :  H98------  This module defines functions for querying an RDF graph to obtain---  a set of variable substitutions, and to apply a set of variable---  substitutions to a query pattern to obtain a new graph.------  It also defines a few primitive graph access functions.------  A minimal example is shown below, where we query a very simple---  graph:------ >>> :m + Swish.RDF Swish.RDF.N3Parser Swish.RDF.RDFQuery--- >>> :set -XOverloadedStrings--- >>> let qparse = either error id . parseN3fromText--- >>> let igr = qparse "@prefix a: <http://example.com/>. a:a a a:A ; a:foo a:bar. a:b a a:B ; a:foo a:bar."--- >>> let qgr = qparse "?node a ?type."--- >>> rdfQueryFind qgr igr--- [[(?type,a:B),(?node,a:b)],[(?type,a:A),(?node,a:a)]]--- >>> let bn = (toRDFLabel . Data.Maybe.fromJust . Network.URI.parseURI) "http://example.com/B"--- >>> rdfFindArcs (rdfObjEq bn) igr--- [(a:b,rdf:type,a:B)]--- >>> Data.Maybe.mapMaybe (flip Swish.RDF.VarBinding.vbMap (Var "type")) $ rdfQueryFind qgr igr--- [a:B,a:A]--- -----------------------------------------------------------------------------------module Swish.RDF.RDFQuery-    ( rdfQueryFind, rdfQueryFilter-    , rdfQueryBack, rdfQueryBackFilter, rdfQueryBackModify-    , rdfQueryInstance-    , rdfQuerySubs, rdfQueryBackSubs-    , rdfQuerySubsAll-    , rdfQuerySubsBlank, rdfQueryBackSubsBlank-    , rdfFindArcs, rdfSubjEq, rdfPredEq, rdfObjEq-    , rdfFindPredVal, rdfFindPredInt, rdfFindValSubj-    , rdfFindList-    -- debug-    , rdfQuerySubs2 )-where--import Swish.RDF.RDFVarBinding-    ( RDFVarBinding, nullRDFVarBinding-    , RDFVarBindingFilter-    )--import Swish.RDF.RDFGraph-    ( Arc(..), LDGraph(..)-    , arcSubj, arcPred, arcObj-    , RDFLabel(..)-    , isDatatyped, isBlank, isQueryVar-    , getLiteralText, makeBlank-    , RDFTriple-    , RDFGraph, emptyRDFGraph-    , allLabels, remapLabels-    , resRdfFirst-    , resRdfRest-    , resRdfNil-    )--import Swish.RDF.MapXsdInteger (mapXsdInteger)-import Swish.RDF.Datatype (DatatypeMap(..))--import Swish.RDF.VarBinding-    ( VarBinding(..)-    , makeVarBinding-    , applyVarBinding, joinVarBindings-    , VarBindingModify(..)-    , VarBindingFilter(..)-    )--import Swish.RDF.Vocabulary (xsdInteger, xsdNonNegInteger)--import Swish.Utils.ListHelpers (listProduct, allp, anyp)--import qualified Data.Traversable as Traversable--import Control.Monad.State (State, runState, modify)--import Data.Maybe (mapMaybe, isJust, fromJust)---- import qualified Data.Text as T-----------------------------------------------------------------  Primitive RDF graph queries----------------------------------------------------------------- | Basic graph-query function.------  The triples of the query graph are matched sequentially---  against the target graph, each taking account of any---  variable bindings that have already been determined,---  and adding new variable bindings as triples containing---  query variables are matched against the graph.----rdfQueryFind :: -  RDFGraph -- ^ The query graph.-  -> RDFGraph -- ^ The target graph.-  -> [RDFVarBinding]-  -- ^ Each element represents a set of variable bindings that make the query graph a-  -- subgraph of the target graph. The list can be empty.-rdfQueryFind =-    rdfQueryPrim1 matchQueryVariable nullRDFVarBinding . getArcs----  Helper function to match query against a graph.---  A node-query function is supplied to determine how query nodes---  are matched against target graph nodes.  Also supplied is---  an initial variable binding.----rdfQueryPrim1 ::-    NodeQuery RDFLabel -> RDFVarBinding -> [Arc RDFLabel]-    -> RDFGraph-    -> [RDFVarBinding]-rdfQueryPrim1 _     initv []       _  = [initv]-rdfQueryPrim1 nodeq initv (qa:qas) tg =-    let-        qam  = fmap (applyVarBinding initv) qa      -- subst vars already bound-        newv = rdfQueryPrim2 nodeq qam tg           -- new bindings, or null-    in-        concat-            [ rdfQueryPrim1 nodeq v2 qas tg-            | v1 <- newv-            , let v2 = joinVarBindings initv v1-            ]----  Match single query term against graph, and return any new sets---  of variable bindings thus defined, or [] if the query term---  cannot be matched.  Each of the RDFVarBinding values returned---  represents an alternative possible match for the query arc.----rdfQueryPrim2 ::-    NodeQuery RDFLabel -> Arc RDFLabel-    -> RDFGraph-    -> [RDFVarBinding]-rdfQueryPrim2 nodeq qa tg =-        mapMaybe (getBinding nodeq qa) (getArcs tg)---- |RDF query filter.------  This function applies a supplied query binding---  filter to the result from a call of 'rdfQueryFind'.------  If none of the query bindings found satisfy the filter, a null---  list is returned (which is what 'rdfQueryFind' returns if the---  query cannot be satisfied).------  (Because of lazy evaluation, this should be as efficient as---  applying the filter as the search proceeds.  I started to build---  the filter logic into the query function itself, with consequent---  increase in complexity, until I remembered lazy evaluation lets---  me keep things separate.)----rdfQueryFilter ::-    RDFVarBindingFilter -> [RDFVarBinding] -> [RDFVarBinding]-rdfQueryFilter qbf = filter (vbfTest qbf)-----------------------------------------------------------------  Backward-chaining RDF graph queries----------------------------------------------------------------- |Reverse graph-query function.------  Similar to 'rdfQueryFind', but with different success criteria.---  The query graph is matched against the supplied graph,---  but not every triple of the query is required to be matched.---  Rather, every triple of the target graph must be matched,---  and substitutions for just the variables thus bound are---  returned.  In effect, these are subsitutions in the query---  that entail the target graph (where @rdfQueryFind@ returns---  substitutions that are entailed by the target graph).------  Multiple substitutions may be used together, so the result---  returned is a list of lists of query bindings.  Each inner---  list contains several variable bindings that must all be applied---  separately to the closure antecendents to obtain a collection of---  expressions that together are antecedent to the supplied---  conclusion.  A null list of bindings returned means the---  conclusion can be inferred without any antecedents.------  Note:  in back-chaining, the conditions required to prove each---  target triple are derived independently, using the inference rule---  for each such triple, so there are no requirements to check---  consistency with previously determined variable bindings, as---  there are when doing forward chaining.  A result of this is that---  there may be redundant triples generated by the back-chaining---  process.  Any process using back-chaining should deal with the---  results returned accordingly.------  An empty outer list is returned if no combination of---  substitutions can infer the supplied target.----rdfQueryBack :: RDFGraph -> RDFGraph -> [[RDFVarBinding]]-rdfQueryBack qg tg =-    rdfQueryBack1 matchQueryVariable [] (getArcs qg) (getArcs tg)--rdfQueryBack1 ::-    NodeQuery RDFLabel -> [RDFVarBinding] -> [Arc RDFLabel] -> [Arc RDFLabel]-    -> [[RDFVarBinding]]-rdfQueryBack1 _     initv _   []       = [initv]-rdfQueryBack1 nodeq initv qas (ta:tas) = concat-    [ rdfQueryBack1 nodeq (nv:initv) qas tas-    | nv <- rdfQueryBack2 nodeq qas ta ]----  Match a query against a single graph term, and return any new sets of---  variable bindings thus defined.  Each member of the result is an---  alternative possible set of variable bindings.  An empty list returned---  means no match.----rdfQueryBack2 ::-    NodeQuery RDFLabel -> [Arc RDFLabel] -> Arc RDFLabel-    -> [RDFVarBinding]-rdfQueryBack2 nodeq qas ta =-    [ fromJust b | qa <- qas, let b = getBinding nodeq qa ta, isJust b ]---- |RDF back-chaining query filter.  This function applies a supplied---  query binding filter to the result from a call of 'rdfQueryBack'.------  Each inner list contains bindings that must all be used to satisfy---  the backchain query, so if any query binding does not satisfy the---  filter, the entire corresponding row is removed-rdfQueryBackFilter ::-    RDFVarBindingFilter -> [[RDFVarBinding]] -> [[RDFVarBinding]]-rdfQueryBackFilter qbf = filter (all (vbfTest qbf))---- |RDF back-chaining query modifier.  This function applies a supplied---  query binding modifier to the result from a call of 'rdfQueryBack'.------  Each inner list contains bindings that must all be used to satisfy---  a backchaining query, so if any query binding does not satisfy the---  filter, the entire corresponding row is removed----rdfQueryBackModify ::-    VarBindingModify a b -> [[VarBinding a b]] -> [[VarBinding a b]]-rdfQueryBackModify qbm = concatMap (rdfQueryBackModify1 qbm)----  Auxiliary back-chaining query variable binding modifier function:---  for a supplied list of variable bindings, all of which must be used---  together when backchaining:---  (a) make each list member into a singleton list---  (b) apply the binding modifier to each such list, which may result---      in a list with zero, one or more elements.---  (c) return the listProduct of these, each member of which is---      an alternative list of variable bindings, where the members of---      each alternative must be used together.----rdfQueryBackModify1 ::-    VarBindingModify a b -> [VarBinding a b] -> [[VarBinding a b]]-rdfQueryBackModify1 qbm qbs = listProduct $ map (vbmApply qbm . (:[])) qbs-----------------------------------------------------------------  Simple entailment graph query----------------------------------------------------------------- |Simple entailment (instance) graph query.------  This function queries a graph to find instances of the---  query graph in the target graph.  It is very similar---  to the normal forward chaining query 'rdfQueryFind',---  except that blank nodes rather than query variable nodes---  in the query graph are matched against nodes in the target---  graph.  Neither graph should contain query variables.------  An instance is defined by the RDF semantics specification,---  per <http://www.w3.org/TR/rdf-mt/>, and is obtained by replacing---  blank nodes with URIs, literals or other blank nodes.  RDF---  simple entailment can be determined in terms of instances.---  This function looks for a subgraph of the target graph that---  is an instance of the query graph, which is a necessary and---  sufficient condition for RDF entailment (see the Interpolation---  Lemma in RDF Semantics, section 1.2).------  It is anticipated that this query function can be used in---  conjunction with backward chaining to determine when the---  search for sufficient antecendents to determine some goal---  has been concluded.-rdfQueryInstance :: RDFGraph -> RDFGraph -> [RDFVarBinding]-rdfQueryInstance =-    rdfQueryPrim1 matchQueryBnode nullRDFVarBinding . getArcs-----------------------------------------------------------------  Primitive RDF graph query support functions----------------------------------------------------------------- |Type of query node testing function.  Return value is:------  * @Nothing@    if no match------  * @Just True@  if match with new variable binding------  * @Just False@ if match with new variable binding----type NodeQuery a = a -> a -> Maybe Bool----  Extract query binding from matching a single query triple with a---  target triple, returning:---  - Nothing if the query is not matched---  - Just nullVarBinding if there are no new variable bindings---  - Just binding is a new query binding for this match-getBinding ::-    NodeQuery RDFLabel -> Arc RDFLabel -> Arc RDFLabel-    -> Maybe RDFVarBinding-getBinding nodeq (Arc s1 p1 o1) (Arc s2 p2 o2) =-    makeBinding [(s1,s2),(p1,p2),(o1,o2)] []-    where-        makeBinding [] bs = Just $ makeVarBinding bs-        makeBinding (vr@(v,r):bvrs) bs =-            case nodeq v r of-                Nothing    -> Nothing-                Just False -> makeBinding bvrs bs-                Just True  -> makeBinding bvrs (vr:bs)----  Match variable node against target node, returning---  Nothing if they do not match, Just True if a variable---  node is matched (thereby creating a new variable binding)---  or Just False if a non-blank node is matched.-matchQueryVariable :: NodeQuery RDFLabel-matchQueryVariable (Var _) _ = Just True-matchQueryVariable q t-    | q == t    = Just False-    | otherwise = Nothing----  Match blank query node against target node, returning---  Nothing if they do not match, Just True if a blank node---  is matched (thereby creating a new equivalence) or---  Just False if a non-blank node is matched.-matchQueryBnode :: NodeQuery RDFLabel-matchQueryBnode (Blank _) _ = Just True-matchQueryBnode q t-    | q == t    = Just False-    | otherwise = Nothing-----------------------------------------------------------------  Substitute results from RDF query back into a graph----------------------------------------------------------------- |Graph substitution function.------  Uses the supplied variable bindings to substitute variables in---  a supplied graph, returning a list of result graphs corresponding---  to each set of variable bindings applied to the input graph.---  This function is used for formward chaining substitutions, and---  returns only those result graphs for which all query variables---  are bound.-rdfQuerySubs :: [RDFVarBinding] -> RDFGraph -> [RDFGraph]-rdfQuerySubs vars gr =-    map fst $ filter (null . snd) $ rdfQuerySubsAll vars gr---- |Graph back-substitution function.------  Uses the supplied variable bindings from 'rdfQueryBack' to perform---  a series of variable substitutions in a supplied graph, returning---  a list of lists of result graphs corresponding to each set of variable---  bindings applied to the input graphs.------  The outer list of the result contains alternative antecedent lists---  that satisfy the query goal.  Each inner list contains graphs that---  must all be inferred to satisfy the query goal.-rdfQueryBackSubs ::-    [[RDFVarBinding]] -> RDFGraph -> [[(RDFGraph,[RDFLabel])]]-rdfQueryBackSubs varss gr = [ rdfQuerySubsAll v gr | v <- varss ]---- |Graph substitution function.------  This function performs the substitutions and returns a list of---  result graphs each paired with a list unbound variables in each.-rdfQuerySubsAll :: [RDFVarBinding] -> RDFGraph -> [(RDFGraph,[RDFLabel])]-rdfQuerySubsAll vars gr = [ rdfQuerySubs2 v gr | v <- vars ]---- |Graph substitution function.------  This function performs each of the substitutions in 'vars', and---  replaces any nodes corresponding to unbound query variables---  with new blank nodes.-rdfQuerySubsBlank :: [RDFVarBinding] -> RDFGraph -> [RDFGraph]-rdfQuerySubsBlank vars gr =-    [ remapLabels vs bs makeBlank g-    | v <- vars-    , let (g,vs) = rdfQuerySubs2 v gr-    , let bs     = allLabels isBlank g-    ]---- |Graph back-substitution function, replacing variables with bnodes.------  Uses the supplied variable bindings from 'rdfQueryBack' to perform---  a series of variable substitutions in a supplied graph, returning---  a list of lists of result graphs corresponding to each set of variable---  bindings applied to the input graphs.------  The outer list of the result contains alternative antecedent lists---  that satisfy the query goal.  Each inner list contains graphs that---  must all be inferred to satisfy the query goal.-rdfQueryBackSubsBlank :: [[RDFVarBinding]] -> RDFGraph -> [[RDFGraph]]-rdfQueryBackSubsBlank varss gr = [ rdfQuerySubsBlank v gr | v <- varss ]----  This function applies a substitution for a single set of variable---  bindings, returning the result and a list of unbound variables.---  It uses a state transformer monad to collect the list of---  unbound variables.------  Adding an empty graph forces elimination of duplicate arcs.-rdfQuerySubs2 :: RDFVarBinding -> RDFGraph -> (RDFGraph,[RDFLabel])-rdfQuerySubs2 varb gr = (add emptyRDFGraph g,vs)-    where-        (g,vs) = runState ( Traversable.traverse (mapNode varb) gr ) []----  Auxiliary monad function for rdfQuerySubs2.---  This returns a state transformer Monad which in turn returns the---  substituted node value based on the supplied query variable bindings.---  The monad state is a list of labels which accumulates all those---  variables seen for which no substitution was available.-mapNode :: RDFVarBinding -> RDFLabel -> State [RDFLabel] RDFLabel-mapNode varb lab =-    case vbMap varb lab of-        Just v  -> return v-        Nothing ->-            if isQueryVar lab then-                do  { modify (addVar lab)-                    ; return lab-                    }-            else-                return lab----  Add variable to list of variables, if not already there-addVar :: RDFLabel -> [RDFLabel] -> [RDFLabel]-addVar var vars = if var `elem` vars then vars else var:vars-----------------------------------------------------------------  Simple lightweight query primitives-------------------------------------------------------------------  [[[TODO:  modify above code to use these for all graph queries]]]---- |Take a predicate on an---  RDF statement and a graph, and returns all statements in the graph---  satisfying that predicate.------  Use combinations of these as follows:------  * find all statements with given subject:---          @rdfFindArcs (rdfSubjEq s)@------  * find all statements with given property:---          @rdfFindArcs (rdfPredEq p)@------  * find all statements with given object:---          @rdfFindArcs (rdfObjEq  o)@------  * find all statements matching conjunction of these conditions:---          @rdfFindArcs ('allp' [...])@------  * find all statements matching disjunction of these conditions:---          @rdfFindArcs ('anyp' [...])@------  Custom predicates can also be used.----rdfFindArcs :: (RDFTriple -> Bool) -> RDFGraph -> [RDFTriple]-rdfFindArcs p = filter p . getArcs---- |Test if statement has given subject-rdfSubjEq :: RDFLabel -> RDFTriple -> Bool-rdfSubjEq s = (s==) . arcSubj---- |Test if statement has given predicate-rdfPredEq :: RDFLabel -> RDFTriple -> Bool-rdfPredEq p = (p==) . arcPred---- |Test if statement has given object-rdfObjEq  :: RDFLabel -> RDFTriple -> Bool-rdfObjEq o  = (o==) . arcObj--{---- |Find statements with given subject-rdfFindSubj :: RDFLabel -> RDFGraph -> [RDFTriple]-rdfFindSubj s = rdfFindArcs (rdfSubjEq s)---- |Find statements with given predicate-rdfFindPred :: RDFLabel -> RDFGraph -> [RDFTriple]-rdfFindPred p = rdfFindArcs (rdfPredEq p)--}---- |Find values of given predicate for a given subject-rdfFindPredVal :: -  RDFLabel    -- ^ subject-  -> RDFLabel -- ^ predicate-  -> RDFGraph -  -> [RDFLabel]-rdfFindPredVal s p = map arcObj . rdfFindArcs (allp [rdfSubjEq s,rdfPredEq p])---- |Find integer values of a given predicate for a given subject-rdfFindPredInt :: -  RDFLabel     -- ^ subject-  -> RDFLabel  -- ^ predicate-  -> RDFGraph -> [Integer]-rdfFindPredInt s p = mapMaybe getint . filter isint . pvs-    where-        pvs = rdfFindPredVal s p-        isint  = anyp-            [ isDatatyped xsdInteger-            , isDatatyped xsdNonNegInteger-            ]-        getint = mapL2V mapXsdInteger . getLiteralText---- |Find all subjects that match (subject, predicate, object) in the graph.-rdfFindValSubj :: -  RDFLabel     -- ^ predicate-  -> RDFLabel  -- ^ object-  -> RDFGraph -  -> [RDFLabel]-rdfFindValSubj p o = map arcSubj . rdfFindArcs (allp [rdfPredEq p,rdfObjEq o])-----------------------------------------------------------------  List query----------------------------------------------------------------- |Return a list of nodes that comprise an rdf:collection value,---  given the head element of the collection.  If the list is---  ill-formed then an arbitrary value is returned.----rdfFindList :: RDFGraph -> RDFLabel -> [RDFLabel]-rdfFindList gr hd = findhead $ rdfFindList gr findrest-    where-        findhead  = headOr (const []) $-                    map (:) (rdfFindPredVal hd resRdfFirst gr)-        findrest  = headOr resRdfNil (rdfFindPredVal hd resRdfRest gr)-        {--        findhead  = headOr (const [])-                    [ (ob:) | Arc _ sb ob <- subgr, sb == resRdfFirst ]-        findrest  = headOr resRdfNil-                    [ ob | Arc _ sb ob <- subgr, sb == resRdfRest  ]-        subgr     = filter ((==) hd . arcSubj) $ getArcs gr-        -}-        headOr    = foldr const-        -- headOr _ (x:_) = x-        -- headOr x []    = x-----------------------------------------------------------------  Interactive tests---------------------------------------------------------------{--s1 = Blank "s1"-p1 = Blank "p1"-o1 = Blank "o1"-s2 = Blank "s2"-p2 = Blank "p2"-o2 = Blank "o2"-qs1 = Var "s1"-qp1 = Var "p1"-qo1 = Var "o1"-qs2 = Var "s2"-qp2 = Var "p2"-qo2 = Var "o2"--qa1 = Arc qs1 qp1 qo1-qa2 = Arc qs2 qp2 qo2-qa3 = Arc qs2  p2 qo2-ta1 = Arc s1 p1 o1-ta2 = Arc s2 p2 o2--g1  = toRDFGraph [ta1,ta2]-g2  = toRDFGraph [qa3]--gb1  = getBinding matchQueryVariable qa1 ta1    -- ?s1=_:s1, ?p1=_:p1, ?o1=_:o1-gvs1 = qbMap (fromJust gb1) qs1                 -- _:s1-gvp1 = qbMap (fromJust gb1) qp1                 -- _:p1-gvo1 = qbMap (fromJust gb1) qo1                 -- _:o1-gvs2 = qbMap (fromJust gb1) qs2                 -- Nothing--gb3  = getBinding matchQueryVariable qa3 ta1    -- Nothing-gb4  = getBinding matchQueryVariable qa3 ta2    -- ?s2=_:s1, ?o2=_:o1--mqvs1 = matchQueryVariable qs2 s1-mqvp1 = matchQueryVariable p2  p1----  rdfQueryFind--qfa  = rdfQueryFind g2 g1--qp2a = rdfQueryPrim2 matchQueryVariable qa3 g1--}--{- more tests--qb1a = rdfQueryBack1 [] [qa1] [ta1,ta2]-qb1 = rdfQueryBack1 [] [qa1,qa2] [ta1,ta2]-ql1 = length qb1-qv1 = map (qb1!!0!!0) [qs1,qp1,qo1,qs2,qp2,qo2]-qv2 = map (qb1!!0!!1) [qs1,qp1,qo1,qs2,qp2,qo2]-qv3 = map (qb1!!1!!0) [qs1,qp1,qo1,qs2,qp2,qo2]-qv4 = map (qb1!!1!!1) [qs1,qp1,qo1,qs2,qp2,qo2]-qv5 = map (qb1!!2!!0) [qs1,qp1,qo1,qs2,qp2,qo2]-qv6 = map (qb1!!2!!1) [qs1,qp1,qo1,qs2,qp2,qo2]-qv7 = map (qb1!!3!!0) [qs1,qp1,qo1,qs2,qp2,qo2]-qv8 = map (qb1!!3!!1) [qs1,qp1,qo1,qs2,qp2,qo2]--qb2 = rdfQueryBack2 matchQueryVariable [qa1,qa2] ta1-ql2 = length qb2-qv1 = map (qbMap $ head qb2)        [qs1,qp1,qo1,qs2,qp2,qo2]-qv2 = map (qbMap $ head $ tail qb2) [qs1,qp1,qo1,qs2,qp2,qo2]-qb3 = rdfQueryBack2 matchQueryVariable [qa1,qa3] ta1---}-----------------------------------------------------------------------------------------  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke ---  All rights reserved.------  This file is part of Swish.------  Swish is free software; you can redistribute it and/or modify---  it under the terms of the GNU General Public License as published by---  the Free Software Foundation; either version 2 of the License, or---  (at your option) any later version.------  Swish is distributed in the hope that it will be useful,---  but WITHOUT ANY WARRANTY; without even the implied warranty of---  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the---  GNU General Public License for more details.------  You should have received a copy of the GNU General Public License---  along with Swish; if not, write to:---    The Free Software Foundation, Inc.,---    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA------------------------------------------------------------------------------------
− src/Swish/RDF/RDFRuleset.hs
@@ -1,546 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  RDFRuleset---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  License     :  GPL V2------  Maintainer  :  Douglas Burke---  Stability   :  experimental---  Portability :  OverloadedStrings------  This module defines some datatypes and functions that are---  used to define rules and rulesets over RDF graphs.------  For the routines that accept a graph in N3 format, the following---  namespaces are pre-defined for use by the graph:---     @rdf:@ and @rdfs:@.--------------------------------------------------------------------------------------module Swish.RDF.RDFRuleset-    ( RDFFormula, RDFRule, RDFRuleMap-    , RDFClosure, RDFRuleset, RDFRulesetMap-    , nullRDFFormula-    , GraphClosure(..), makeGraphClosureRule-    , makeRDFGraphFromN3Builder-    , makeRDFFormula-    , makeRDFClosureRule-      -- * Create rules using Notation3 statements-    , makeN3ClosureRule-    , makeN3ClosureSimpleRule-    , makeN3ClosureModifyRule-    , makeN3ClosureAllocatorRule-    , makeNodeAllocTo-      -- * Debugging-    , graphClosureFwdApply, graphClosureBwdApply-    )-where--import Swish.RDF.RDFQuery-    ( rdfQueryFind-    , rdfQueryBack, rdfQueryBackModify-    , rdfQuerySubs-    , rdfQuerySubsBlank-    )--import Swish.RDF.RDFGraph-    ( RDFLabel(..), RDFGraph-    , makeBlank, newNodes-    , merge, allLabels-    , toRDFGraph, emptyRDFGraph )--import Swish.RDF.RDFVarBinding (RDFVarBinding, RDFVarBindingModify)-import Swish.RDF.N3Parser (parseN3)-import Swish.RDF.Ruleset (Ruleset(..), RulesetMap)--import Swish.RDF.Rule-    ( Formula(..), Rule(..), RuleMap-    , fwdCheckInference-    , nullSN-    )--import Swish.RDF.VarBinding-    ( makeVarBinding-    , applyVarBinding, joinVarBindings-    , VarBindingModify(..)-    , vbmCompose-    , varBindingId-    )--import Swish.Utils.Namespace (Namespace, ScopedName, makeNSScopedName, namespaceToBuilder)-import Swish.RDF.Vocabulary (swishName, namespaceRDF, namespaceRDFS)--{--import Swish.RDF.Proof-    ( Proof(..), Step(..) )--}--import Swish.RDF.GraphClass (Label(..), Arc(..), LDGraph(..))-import Swish.Utils.ListHelpers (equiv, flist)--import Data.List (nub)-import Data.Maybe (fromMaybe)-import Data.Monoid (Monoid(..))--import qualified Data.Text as T-import qualified Data.Text.Lazy.Builder as B-----------------------------------------------------------------  Datatypes for RDF ruleset---------------------------------------------------------------type RDFFormula     = Formula RDFGraph--type RDFRule        = Rule RDFGraph--type RDFRuleMap     = RuleMap RDFGraph--type RDFClosure     = GraphClosure RDFLabel--type RDFRuleset     = Ruleset RDFGraph--type RDFRulesetMap  = RulesetMap RDFGraph-----------------------------------------------------------------  Declare null RDF formula----------------------------------------------------------------- | The null RDF formula.-nullRDFFormula :: Formula RDFGraph-nullRDFFormula = Formula-    { formName = nullSN "nullRDFGraph"-    , formExpr = emptyRDFGraph-    }-----------------------------------------------------------------  Datatype for graph closure rule----------------------------------------------------------------- |Datatype for constructing a graph closure rule-data GraphClosure lb = GraphClosure-    { nameGraphRule :: ScopedName   -- ^ Name of rule for proof display-    , ruleAnt       :: [Arc lb]     -- ^ Antecedent triples pattern-                                    --   (may include variable nodes)-    , ruleCon       :: [Arc lb]     -- ^ Consequent triples pattern-                                    --   (may include variable nodes)-    , ruleModify    :: VarBindingModify lb lb-                                    -- ^ Structure that defines additional-                                    --   constraints and/or variable-                                    --   bindings based on other matched-                                    --   query variables.  Matching the-                                    --   antecedents.  Use 'varBindingId' if-                                    --   no additional variable constraints-                                    --   or bindings are added beyond those-                                    --   arising from graph queries.-    }--instance (Label lb) => Eq (GraphClosure lb) where-    c1 == c2 = nameGraphRule c1 == nameGraphRule c2 &&-               ruleAnt c1 `equiv` ruleAnt c2 &&-               ruleCon c1 `equiv` ruleCon c2--instance (Label lb) => Show (GraphClosure lb) where-    show c = "GraphClosure " ++ show (nameGraphRule c)-----------------------------------------------------------------  Define inference rule based on RDF graph closure rule----------------------------------------------------------------- |Define a value of type Rule based on an RDFClosure value.-makeGraphClosureRule :: GraphClosure RDFLabel -> Rule RDFGraph-makeGraphClosureRule grc = newrule-    where-        newrule = Rule-            { ruleName       = nameGraphRule grc-            , fwdApply       = graphClosureFwdApply grc-            , bwdApply       = graphClosureBwdApply grc-            , checkInference = fwdCheckInference newrule-            }---- | Forward chaining function based on RDF graph closure description------  Note:  antecedents here are presumed to share bnodes.----graphClosureFwdApply :: -  GraphClosure RDFLabel -  -> [RDFGraph] -  -> [RDFGraph]-graphClosureFwdApply grc grs =-    let gr   = if null grs then emptyRDFGraph else foldl1 add grs-        vars = queryFind (ruleAnt grc) gr-        varm = vbmApply (ruleModify grc) vars-        cons = querySubs varm (ruleCon grc)-    in-        {--        seq cons $-        seq (trace "\ngraphClosureFwdApply") $-        seq (traceShow "\nvars: " vars) $-        seq (traceShow "\nvarm: " varm) $-        seq (traceShow "\ncons: " cons) $-        seq (trace "\n") $-        -}-        --  Return null list or single result graph that is the union-        --  (not merge) of individual results:-        if null cons then [] else [foldl1 add cons]-        -- cons {- don't merge results -}---- | Backward chaining function based on RDF graph closure description-graphClosureBwdApply :: GraphClosure RDFLabel -> RDFGraph -> [[RDFGraph]]-graphClosureBwdApply grc gr =-    let vars = rdfQueryBackModify (ruleModify grc) $-               queryBack (ruleCon grc) gr-        --  This next function eliminates duplicate variable bindings.-        --  It is strictly redundant, but comparing variable-        --  bindings is much cheaper than comparing graphs.-        --  I don't know if many duplicate graphs will be result-        --  of exact duplicate variable bindings, so this may be-        --  not very effective.-        varn = map nub vars-    in-        --  The 'nub ante' below eliminates duplicate antecedent graphs,-        --  based on graph matching, which tests for equivalence under-        --  bnode renaming, with a view to reducing redundant arcs in-        --  the merged antecedent graph, hence less to prove in-        --  subsequent back-chaining steps.-        ---        --  Each antecedent is reduced to a single RDF graph, when-        --  bwdApply specifies a list of expressions corresponding to-        --  each antecedent.-        [ [foldl1 merge (nub ante)]-          | vs <- varn-          , let ante = querySubsBlank vs (ruleAnt grc) ]-----------------------------------------------------------------  RDF graph query and substitution support functions---------------------------------------------------------------queryFind :: [Arc RDFLabel] -> RDFGraph -> [RDFVarBinding]-queryFind qas = rdfQueryFind (toRDFGraph qas)--queryBack :: [Arc RDFLabel] -> RDFGraph -> [[RDFVarBinding]]-queryBack qas = rdfQueryBack (toRDFGraph qas)--querySubs :: [RDFVarBinding] -> [Arc RDFLabel] -> [RDFGraph]-querySubs vars = rdfQuerySubs vars . toRDFGraph--querySubsBlank :: [RDFVarBinding] -> [Arc RDFLabel] -> [RDFGraph]-querySubsBlank vars = rdfQuerySubsBlank vars . toRDFGraph-----------------------------------------------------------------  Method for creating an RDF formula value from N3 text---------------------------------------------------------------mkPrefix :: Namespace -> B.Builder-mkPrefix = namespaceToBuilder--prefixRDF :: B.Builder-prefixRDF = -  mconcat -  [ mkPrefix namespaceRDF-  , mkPrefix namespaceRDFS-    ]---- |Helper function to parse a string containing Notation3---  and return the corresponding RDFGraph value.----makeRDFGraphFromN3Builder :: B.Builder -> RDFGraph-makeRDFGraphFromN3Builder b = -  let t = B.toLazyText (prefixRDF `mappend` b)-  in case parseN3 t Nothing of-    Left  msg -> error msg-    Right gr  -> gr---- |Create an RDF formula.-makeRDFFormula ::-    Namespace     -- ^ namespace to which the formula is allocated-    -> T.Text     -- ^ local name for the formula in the namespace-    -> B.Builder  -- ^ graph in Notation 3 format-    -> RDFFormula-makeRDFFormula scope local gr = -  Formula-    { formName = makeNSScopedName scope local-    , formExpr = makeRDFGraphFromN3Builder gr-    }-----------------------------------------------------------------  Create an RDF closure rule from supplied graphs----------------------------------------------------------------- |Constructs an RDF graph closure rule.  That is, a rule that---  given some set of antecedent statements returns new statements---  that may be added to the graph.----makeRDFClosureRule ::-    ScopedName -- ^ scoped name for the new rule-    -> [RDFGraph] -- ^ RDFGraphs that are the entecedent of the rule.-                  ---                  -- (Note:  bnodes and variable names are assumed to be shared-                  -- by all the entecedent graphs supplied.  /is this right?/)-    -> RDFGraph   -- ^ the consequent graph-    -> RDFVarBindingModify -- ^ is a variable binding modifier value that may impose-    --          additional conditions on the variable bindings that-    --          can be used for this inference rule, or which may-    --          cause new values to be allocated for unbound variables.-    --          These modifiers allow for certain inference patterns-    --          that are not captured by simple "closure rules", such-    --          as the allocation of bnodes corresponding to literals,-    --          and are an extension point for incorporating datatypes-    --          into an inference process.-    ---    --          If no additional constraints or variable bindings are-    --          to be applied, use value 'varBindingId'-    ---    -> RDFRule-makeRDFClosureRule sname antgrs congr vmod = makeGraphClosureRule-    GraphClosure-        { nameGraphRule = sname-        , ruleAnt       = concatMap getArcs antgrs-        , ruleCon       = getArcs congr-        , ruleModify    = vmod-        }-----------------------------------------------------------------  Methods to create an RDF closure rule from N3 input-------------------------------------------------------------------  These functions are used internally by Swish to construct---  rules from textual descriptions.---- |Constructs an RDF graph closure rule.  That is, a rule that---  given some set of antecedent statements returns new statements---  that may be added to the graph.  This is the basis for---  implementation of most of the inference rules given in the---  RDF formal semantics document.----makeN3ClosureRule ::-    Namespace -- ^ namespace to which the rule is allocated-    -> T.Text -- ^ local name for the rule in the namespace-    -> B.Builder -    -- ^ the Notation3 representation-    --   of the antecedent graph.  (Note: multiple antecedents-    --   can be handled by combining multiple graphs.)-    -> B.Builder -- ^ the Notation3 representation of the consequent graph.-    -> RDFVarBindingModify-    -- ^ a variable binding modifier value that may impose-    --   additional conditions on the variable bindings that-    --   can be used for this inference rule, or which may-    --   cause new values to be allocated for unbound variables.-    --   These modifiers allow for certain inference patterns-    --   that are not captured by simple closure rules, such-    --   as the allocation of bnodes corresponding to literals,-    --   and are an extension point for incorporating datatypes-    --   into an inference process.-    ---    --   If no additional constraints or variable bindings are-    --   to be applied, use a value of 'varBindingId', or use-    --   'makeN3ClosureSimpleRule'.-    -> RDFRule-makeN3ClosureRule scope local ant con =-    makeRDFClosureRule (makeNSScopedName scope local) [antgr] congr-    where-        antgr = makeRDFGraphFromN3Builder ant-        congr = makeRDFGraphFromN3Builder con---- |Construct a simple RDF graph closure rule without---  additional node allocations or variable binding constraints.----makeN3ClosureSimpleRule ::-    Namespace -- ^ namespace to which the rule is allocated-    -> T.Text -- ^ local name for the rule in the namepace-    -> B.Builder-    -- ^ the Notation3 representation-    --   of the antecedent graph.  (Note: multiple antecedents-    --   can be handled by combining multiple graphs.)-    -> B.Builder  -- ^ the Notation3 representation of the consequent graph.-    -> RDFRule-makeN3ClosureSimpleRule scope local ant con =-    makeN3ClosureRule scope local ant con varBindingId---- |Constructs an RDF graph closure rule that incorporates---  a variable binding filter and a variable binding modifier.----makeN3ClosureModifyRule ::-    Namespace -- ^ namespace to which the rule is allocated-    -> T.Text -- ^ local name for the rule in the given namespace-    -> B.Builder -- ^ the Notation3 representation-    --                of the antecedent graph.  (Note: multiple antecedents-    --                can be handled by combining multiple graphs.)-    -> B.Builder -- ^ the Notation3 representation of the consequent graph.-    -> RDFVarBindingModify-    -- ^ a variable binding modifier value that may impose-    --   additional conditions on the variable bindings that-    --   can be used for this inference rule (@vflt@).-    ---    --   These modifiers allow for certain inference patterns-    --   that are not captured by simple closure rules, such-    --   as deductions that pertain only to certain kinds of-    --   nodes in a graph.-    -> RDFVarBindingModify-    -- ^ a variable binding modifier that is applied to the-    --   variable bindings obtained, typically to create some-    --   additional variable bindings.  This is applied before-    --   the preceeding filter rule (@vflt@).-    -> RDFRule-makeN3ClosureModifyRule scope local ant con vflt vmod =-    makeN3ClosureRule scope local ant con modc-    where-        modc  = fromMaybe varBindingId $ vbmCompose vmod vflt--{--    makeRDFClosureRule (ScopedName scope local) [antgr] congr modc-    where-        antgr = makeRDFGraphFromN3String ant-        congr = makeRDFGraphFromN3String con-        modc  = case vbmCompose vmod vflt of-            Just x  -> x-            Nothing -> varBindingId--}---- |Construct an RDF graph closure rule with a bnode allocator.------  This function is rather like 'makeN3ClosureModifyRule', except that---  the variable binding modifier is a function from the variables in---  the variables and bnodes contained in the antecedent graph.----makeN3ClosureAllocatorRule ::-    Namespace -- ^ namespace to which the rule is allocated-    -> T.Text -- ^ local name for the rule in the given namespace-    -> B.Builder -- ^ the Notation3 representation-    --                of the antecedent graph.  (Note: multiple antecedents-    --                can be handled by combining multiple graphs.)-    -> B.Builder -- ^ the Notation3 representation of the consequent graph.-    -> RDFVarBindingModify-    -- ^ variable binding modifier value that may impose-    --   additional conditions on the variable bindings that-    --   can be used for this inference rule (@vflt@).-    -> ( [RDFLabel] -> RDFVarBindingModify )-    -- ^ function applied to a list of nodes to yield a-    --   variable binding modifier value.-    ---    --   The supplied parameter is applied to a list of all of-    --   the variable nodes (including all blank nodes) in the-    --   antecedent graph, and then composed with the @vflt@-    --   value.  This allows any node allocation-    --   function to avoid allocating any blank nodes that-    --   are already used in the antecedent graph.-    --   (See 'makeNodeAllocTo').-    -> RDFRule-makeN3ClosureAllocatorRule scope local ant con vflt aloc =-    makeRDFClosureRule (makeNSScopedName scope local) [antgr] congr modc-    where-        antgr = makeRDFGraphFromN3Builder ant-        congr = makeRDFGraphFromN3Builder con-        vmod  = aloc (allLabels labelIsVar antgr)-        modc  = fromMaybe varBindingId $ vbmCompose vmod vflt-----------------------------------------------------------------  Query binding modifier for "allocated to" logic----------------------------------------------------------------- |This function defines a variable binding modifier that---  allocates a new blank node for each value bound to---  a query variable, and binds it to another variable---  in each query binding.------  This provides a single binding for query variables that would---  otherwise be unbound by a query.  For example, consider the---  inference pattern:---        ---  >  ?a hasUncle ?c => ?a hasFather ?b . ?b hasBrother ?c .---        ---  For a given @?a@ and @?c@, there is insufficient information---  here to instantiate a value for variable @?b@.  Using this---  function as part of a graph instance closure rule allows---  forward chaining to allocate a single bnode for each---  occurrence of @?a@, so that given:---        ---  >  Jimmy hasUncle Fred .---  >  Jimmy hasUncle Bob .------  leads to exactly one bnode inference of:------  >  Jimmy hasFather _:f .------  giving:------  >  Jimmy hasFather _:f .---  >  _:f   hasBrother Fred .---  >  _:f   hasBrother Bob .------  rather than:------  >  Jimmy hasFather _:f1 .---  >  _:f1  hasBrother Fred .---  >  Jimmy hasFather _:f2 .---  >  _:f2  hasBrother Bob .------  This form of constrained allocation of bnodes is also required for---  some of the inference patterns described by the RDF formal semantics,---  particularly those where bnodes are substituted for URIs or literals.----makeNodeAllocTo ::-    RDFLabel      -- ^ variable node to which a new blank node is bound-    -> RDFLabel   -- ^ variable which is bound in each query to a graph-                  --  node to which new blank nodes are allocated.-    -> [RDFLabel]-    -> RDFVarBindingModify-makeNodeAllocTo bindvar alocvar exbnode = VarBindingModify-        { vbmName   = swishName "makeNodeAllocTo"-        , vbmApply  = applyNodeAllocTo bindvar alocvar exbnode-        , vbmVocab  = [alocvar,bindvar]-        , vbmUsage  = [[bindvar]]-        }----  Auxiliary function that performs the node allocation defined---  by makeNodeAllocTo.------  bindvar is a variable node to which a new blank node is bound---  alocvar is a variable which is bound in each query to a graph---          node to which new blank nodes are allocated.---  exbnode is a list of existing blank nodes, to be avoided by---          the new blank node allocator.---  vars    is a list of variable bindings to which new bnode---          allocations for the indicated bindvar are to be added.----applyNodeAllocTo ::-    RDFLabel -> RDFLabel -> [RDFLabel] -> [RDFVarBinding] -> [RDFVarBinding]-applyNodeAllocTo bindvar alocvar exbnode vars =-    let-        app       = applyVarBinding-        alocnodes = zip (nub $ flist (map app vars) alocvar)-                        (newNodes (makeBlank bindvar) exbnode)-        newvb var = joinVarBindings-            ( makeVarBinding $ head-              [ [(bindvar,b)] | (v,b) <- alocnodes, app var alocvar == v ] )-            var-    in-        map newvb vars-----------------------------------------------------------------------------------------  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke  ---  All rights reserved.------  This file is part of Swish.------  Swish is free software; you can redistribute it and/or modify---  it under the terms of the GNU General Public License as published by---  the Free Software Foundation; either version 2 of the License, or---  (at your option) any later version.------  Swish is distributed in the hope that it will be useful,---  but WITHOUT ANY WARRANTY; without even the implied warranty of---  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the---  GNU General Public License for more details.------  You should have received a copy of the GNU General Public License---  along with Swish; if not, write to:---    The Free Software Foundation, Inc.,---    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA------------------------------------------------------------------------------------
− src/Swish/RDF/RDFVarBinding.hs
@@ -1,187 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  RDFVarBinding---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  License     :  GPL V2------  Maintainer  :  Douglas Burke---  Stability   :  experimental---  Portability :  OverloadedStrings------  This module instantiates the `VarBinding` types and methods for use---  with RDF graph labels.----------------------------------------------------------------------------------------  See module RDFQueryTest for test cases.--module Swish.RDF.RDFVarBinding-    ( RDFVarBinding, nullRDFVarBinding-    , RDFVarBindingModify, RDFOpenVarBindingModify, RDFOpenVarBindingModifyMap-    , RDFVarBindingFilter-    , rdfVarBindingUriRef, rdfVarBindingBlank-    , rdfVarBindingLiteral-    , rdfVarBindingUntypedLiteral, rdfVarBindingTypedLiteral-    , rdfVarBindingXMLLiteral, rdfVarBindingDatatyped-    , rdfVarBindingMemberProp-    )-where--import Swish.RDF.RDFGraph-    ( RDFLabel(..)-    , isLiteral, isUntypedLiteral, isTypedLiteral, isXMLLiteral-    , isDatatyped, isMemberProp, isUri, isBlank-    )--import Swish.RDF.VarBinding-    ( VarBinding(..), nullVarBinding-    , applyVarBinding -    , VarBindingModify(..), OpenVarBindingModify-    , VarBindingFilter(..)-    , makeVarTestFilter-    )--import Swish.RDF.Vocabulary-    ( swishName )--import Swish.Utils.LookupMap-    ( LookupMap(..) )-----------------------------------------------------------------  Types for RDF query variable bindings and modifiers----------------------------------------------------------------- |@RDFVarBinding@ is the specific type type of a variable---  binding value used with RDF graph queries. -type RDFVarBinding  = VarBinding RDFLabel RDFLabel---- | maps no query variables.-nullRDFVarBinding :: RDFVarBinding-nullRDFVarBinding = nullVarBinding---- |Define type of query binding modifier for RDF graph inference-type RDFVarBindingModify = VarBindingModify RDFLabel RDFLabel---- |Open variable binding modifier that operates on RDFLabel values----type RDFOpenVarBindingModify = OpenVarBindingModify RDFLabel RDFLabel---- |Define type for lookup map of open query binding modifiers-type RDFOpenVarBindingModifyMap = LookupMap RDFOpenVarBindingModify---- |@RDFVarBindingFilter@ is a function type that tests to see if---  a query binding satisfies some criterion, and is used to---  create a variable binding modifier that simply filers---  given variable bindings.------  Queries often want to apply some kind of filter or condition---  to the variable bindings that are processed.  In inference rules,---  it sometimes seems desirable to stipulate additional conditions on---  the things that are matched.------  This function type is used to perform such tests.---  A number of simple implementations are included.----type RDFVarBindingFilter = VarBindingFilter RDFLabel RDFLabel-----------------------------------------------------------------  Declare some query binding filters----------------------------------------------------------------- |This function generates a query binding filter that ensures---  an indicated variable is bound to a URI reference.-rdfVarBindingUriRef :: RDFLabel -> RDFVarBindingFilter-rdfVarBindingUriRef =-    makeVarTestFilter (swishName "rdfVarBindingUriRef") isUri---- |This function generates a query binding filter that ensures---  an indicated variable is bound to a blank node.-rdfVarBindingBlank :: RDFLabel -> RDFVarBindingFilter-rdfVarBindingBlank =-    makeVarTestFilter (swishName "rdfVarBindingBlank") isBlank---- |This function generates a query binding filter that ensures---  an indicated variable is bound to a literal value.-rdfVarBindingLiteral :: RDFLabel -> RDFVarBindingFilter-rdfVarBindingLiteral =-    makeVarTestFilter (swishName "rdfVarBindingLiteral") isLiteral---- |This function generates a query binding filter that ensures---  an indicated variable is bound to an untyped literal value.-rdfVarBindingUntypedLiteral :: RDFLabel -> RDFVarBindingFilter-rdfVarBindingUntypedLiteral =-    makeVarTestFilter (swishName "rdfVarBindingUntypedLiteral") isUntypedLiteral---- |This function generates a query binding filter that ensures---  an indicated variable is bound to a typed literal value.-rdfVarBindingTypedLiteral :: RDFLabel -> RDFVarBindingFilter-rdfVarBindingTypedLiteral =-    makeVarTestFilter (swishName "rdfVarBindingTypedLiteral") isTypedLiteral---- |This function generates a query binding filter that ensures---  an indicated variable is bound to an XML literal value.-rdfVarBindingXMLLiteral :: RDFLabel -> RDFVarBindingFilter-rdfVarBindingXMLLiteral =-    makeVarTestFilter (swishName "rdfVarBindingXMLLiteral") isXMLLiteral---- |This function generates a query binding filter that ensures---  an indicated variable is bound to container membership property.-rdfVarBindingMemberProp :: RDFLabel -> RDFVarBindingFilter-rdfVarBindingMemberProp =-    makeVarTestFilter (swishName "rdfVarBindingMemberProp") isMemberProp---- |This function generates a query binding filter that ensures---  an indicated variable is bound to a literal value with a---  datatype whose URI is bound to another node----rdfVarBindingDatatyped ::-  RDFLabel    -- ^ variable bound to the required datatype. -  -> RDFLabel -- ^ variable bound to the literal node to be tested.-  -> RDFVarBindingFilter-rdfVarBindingDatatyped dvar lvar = VarBindingFilter-    { vbfName   = swishName "rdfVarBindingDatatyped"-    , vbfVocab  = [dvar,lvar]-    , vbfTest   = \vb -> testDatatyped vb dvar lvar-    }--testDatatyped :: RDFVarBinding -> RDFLabel -> RDFLabel -> Bool-testDatatyped vb dvar lvar = and-        [ isUri dtype-        , isDatatyped dqnam $ applyVarBinding vb lvar-        ]-        where-            dtype = applyVarBinding vb dvar-            -- NOTE: dqnam is not evaluated unless (isUri dtype)-            --       but add in a _ handler to appease -Wall-            -- dqnam = case dtype of { (Res x) -> x }-            dqnam = case dtype of-              Res x -> x-              _ -> error $ "dqnam should not be evaluated with " ++ show dtype----------------------------------------------------------------------------------------  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke  ---  All rights reserved.------  This file is part of Swish.------  Swish is free software; you can redistribute it and/or modify---  it under the terms of the GNU General Public License as published by---  the Free Software Foundation; either version 2 of the License, or---  (at your option) any later version.------  Swish is distributed in the hope that it will be useful,---  but WITHOUT ANY WARRANTY; without even the implied warranty of---  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the---  GNU General Public License for more details.------  You should have received a copy of the GNU General Public License---  along with Swish; if not, write to:---    The Free Software Foundation, Inc.,---    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA------------------------------------------------------------------------------------
− src/Swish/RDF/Rule.hs
@@ -1,247 +0,0 @@-{-# LANGUAGE MultiParamTypeClasses, OverloadedStrings #-}-------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  Rule---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  License     :  GPL V2------  Maintainer  :  Douglas Burke---  Stability   :  experimental---  Portability :  MultiParamTypeClasses, OverloadedStrings------  This module defines a framework for defining inference rules---  over some expression form.  It is intended to be used with---  RDF graphs, but the structures aim to be quite generic with---  respect to the expression forms allowed.--------------------------------------------------------------------------------------module Swish.RDF.Rule-       ( Expression(..), Formula(..), Rule(..), RuleMap-       , nullScope, nullSN, nullFormula, nullRule-       , fwdCheckInference, bwdCheckInference-       , showsFormula, showsFormulae, showsWidth-       )-       where--import Swish.Utils.Namespace (Namespace, makeNamespace, ScopedName, makeScopedName, makeNSScopedName)-import Swish.Utils.LookupMap (LookupEntryClass(..), LookupMap(..))-import Swish.Utils.ShowM (ShowM(..))--import Network.URI (URI, parseURI)-import Data.Maybe (fromJust)--import qualified Data.Text as T-----------------------------------------------------------------  Expressions----------------------------------------------------------------- |Expression is a type class for values over which proofs---  may be constructed.-class (Eq ex) => Expression ex where-    -- |Is expression true in all interpretations?-    --  If so, then its truth is assumed without justification.-    isValid :: ex -> Bool-----------------------------------------------------------------  Formula:  a named expression----------------------------------------------------------------- |A Formula is a named expression.-data Formula ex = Formula-    { formName :: ScopedName        -- ^ Name used for formula in proof chain-    , formExpr :: ex                -- ^ Named formula value-    } deriving Show---- |Define equality of formulae as equality of formula names-instance Eq (Formula ex) where-    f1 == f2 = formName f1 == formName f2---- |Define ordering of formulae based on formula names-instance Ord (Formula ex) where-    f1 <= f2 = formName f1 <= formName f2--instance LookupEntryClass (Formula ex) ScopedName (Formula ex)-    where-    newEntry (_,form) = form-    keyVal form = (formName form, form)---- | The namespace @http:\/\/id.ninebynine.org\/2003\/Ruleset\/null@ with the prefix @null:@.-nullScope :: Namespace-nullScope = makeNamespace (Just "null") nullScopeURI---- | Create a scoped name with the null namespace.-nullSN :: -  T.Text -- ^ local name.-  -> ScopedName-nullSN = makeNSScopedName nullScope--tU :: String -> URI-tU = fromJust . parseURI--nullScopeURI :: URI-nullScopeURI = tU "http://id.ninebynine.org/2003/Ruleset/null"---- | The null formula.-nullFormula :: Formula ex-nullFormula = Formula-    { formName = makeScopedName (Just "null") nullScopeURI "nullFormula"-    , formExpr = error "Null formula"-    }---- testf1 = Formula "f1" ('f',1)--- testf2 = Formula "f2" ('f',2)---- |Return a displayable form of a list of labelled formulae-showsFormulae :: -  (ShowM ex) -  => String        -- ^ newline-  -> [Formula ex]  -- ^ the formulae to show-  -> String        -- ^ text to be placed after the formulae-  -> ShowS-showsFormulae _       []     _     = id-showsFormulae newline [f]    after = showsFormula  newline f .-                                     showString    after-showsFormulae newline (f:fs) after = showsFormula  newline f .-                                     showString    newline .-                                     showsFormulae newline fs after---- |Create a displayable form of a labelled formula-showsFormula :: -  (ShowM ex) -  => String      -- ^ newline-  -> Formula ex  -- ^ formula-  -> ShowS-showsFormula newline f =-    showsWidth 16 ("["++show (formName f)++"] ") .-    showms (newline ++ replicate 16 ' ') (formExpr f)-----------------------------------------------------------------  Rule----------------------------------------------------------------- |Rule is a data type for inference rules that can be used---  to construct a step in a proof.-data Rule ex = Rule-    {-      -- |Name of rule, for use when displaying a proof-      ruleName :: ScopedName,-      -      -- |Forward application of a rule, takes a list of-      --  expressions and returns a list (possibly empty)-      --  of forward applications of the rule to combinations-      --  of the antecedent expressions.-      --  Note that all of the results returned can be assumed to-      --  be (simultaneously) true, given the antecedents provided.-      fwdApply :: [ex] -> [ex],-      -      -- |Backward application of a rule, takes an expression-      --  and returns a list of alternative antecedents, each of-      --  which is a list of expressions that jointly yield the-      --  given consequence through application of the inference-      --  rule.  An empty list is returned if no antecedents-      --  will allow the consequence to be inferred.-      bwdApply :: ex -> [[ex]],-      -      -- |Inference check.  Takes a list of antecedent expressions-      --  and a consequent expression, returning True if the-      --  consequence can be obtained from the antecedents by-      --  application of the rule.  When the antecedents and-      --  consequent are both given, this is generally more efficient-      --  that using either forward or backward chaining.-      --  Also, a particular rule may not fully support either-      --  forward or backward chaining, but all rules are required-      --  to fully support this function.-      ---      --  A default implementation based on forward chaining is-      --  given below.-      checkInference :: [ex] -> ex -> Bool -    }---- |Define equality of rules as equality of the rule names.-instance Eq (Rule ex) where-    r1 == r2 = ruleName r1 == ruleName r2---- |Define ordering of rules based on the rule names.-instance Ord (Rule ex) where-    r1 <= r2 = ruleName r1 <= ruleName r2--instance Show (Rule ex) where-    show rl = "Rule "++show (ruleName rl)--instance LookupEntryClass (Rule ex) ScopedName (Rule ex)-    where-    newEntry (_,rule) = rule-    keyVal rule = (ruleName rule, rule)--type RuleMap ex = LookupMap (Rule ex)---- | Checks that consequence is a result of--- applying the rule to the antecedants.-fwdCheckInference :: -  (Eq ex) -  => Rule ex   -- ^ rule-  -> [ex]      -- ^ antecedants-  -> ex        -- ^ consequence-  -> Bool-fwdCheckInference rule ante cons =-    cons `elem` fwdApply rule ante--bwdCheckInference ::-  (Eq ex) -  => Rule ex   -- ^ rule-  -> [ex]      -- ^ antecedants-  -> ex        -- ^ consequence-  -> Bool-bwdCheckInference rule ante cons = any checkAnts (bwdApply rule cons)-    where-        checkAnts = all (`elem` ante)---- | The null rule.-nullRule :: Rule ex-nullRule = Rule-    { ruleName = makeScopedName (Just "null") nullScopeURI "nullRule"-    , fwdApply = \ _ -> []-    , bwdApply = \ _ -> []-    , checkInference = \ _ _ -> False-    }-----------------------------------------------------------------  Shows formatting support functions---------------------------------------------------------------- |Show a string left justified in a field of at least the specified---  number of characters width.-showsWidth :: Int -> String -> ShowS-showsWidth wid str more = str++replicate pad ' '++more-    where-        pad = wid - length str-----------------------------------------------------------------------------------------  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  All rights reserved.------  This file is part of Swish.------  Swish is free software; you can redistribute it and/or modify---  it under the terms of the GNU General Public License as published by---  the Free Software Foundation; either version 2 of the License, or---  (at your option) any later version.------  Swish is distributed in the hope that it will be useful,---  but WITHOUT ANY WARRANTY; without even the implied warranty of---  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the---  GNU General Public License for more details.------  You should have received a copy of the GNU General Public License---  along with Swish; if not, write to:---    The Free Software Foundation, Inc.,---    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA------------------------------------------------------------------------------------
src/Swish/RDF/Ruleset.hs view
@@ -1,158 +1,525 @@-{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}+ -------------------------------------------------------------------------------- --  See end of this file for licence information. -------------------------------------------------------------------------------- -- | --  Module      :  Ruleset---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke --  License     :  GPL V2 -- --  Maintainer  :  Douglas Burke --  Stability   :  experimental---  Portability :  MultiParamTypeClasses+--  Portability :  OverloadedStrings -----  This module defines a ruleset data type, used to collect information---  about a ruleset that may contribute torwards inferences in RDF;---  e.g. RDF and RDFS are rulesets.+--  This module defines some datatypes and functions that are+--  used to define rules and rulesets over RDF graphs. -----  A ruleset consists of a namespace, a collection of axioms, and---  a collection of rules.+--  For the routines that accept a graph in N3 format, the following+--  namespaces are pre-defined for use by the graph:+--     @rdf:@ and @rdfs:@. -- --------------------------------------------------------------------------------  module Swish.RDF.Ruleset-    ( Ruleset(..), RulesetMap-    , makeRuleset, getRulesetNamespace, getRulesetAxioms, getRulesetRules-    , getRulesetAxiom, getRulesetRule-    , getContextAxiom, getMaybeContextAxiom-    , getContextRule,  getMaybeContextRule+    (+     -- * Data types for RDF Ruleset+     RDFFormula, RDFRule, RDFRuleMap+    , RDFClosure, RDFRuleset, RDFRulesetMap+    , nullRDFFormula+    , GraphClosure(..), makeGraphClosureRule+    , makeRDFGraphFromN3Builder+    , makeRDFFormula+    , makeRDFClosureRule+      -- * Create rules using Notation3 statements+    , makeN3ClosureRule+    , makeN3ClosureSimpleRule+    , makeN3ClosureModifyRule+    , makeN3ClosureAllocatorRule+    , makeNodeAllocTo+      -- * Debugging+    , graphClosureFwdApply, graphClosureBwdApply     ) where -import Swish.Utils.Namespace (Namespace, ScopedName)-import Swish.RDF.Rule (Formula(..), Rule(..))+import Swish.Namespace (Namespace, ScopedName)+import Swish.Namespace (makeNSScopedName, namespaceToBuilder)+import Swish.QName (LName)+import Swish.Rule (Formula(..), Rule(..), RuleMap)+import Swish.Rule (fwdCheckInference, nullSN)+import Swish.Ruleset (Ruleset(..), RulesetMap)+import Swish.GraphClass (Label(..), Arc(..), LDGraph(..))+import Swish.VarBinding (VarBindingModify(..))+import Swish.VarBinding (makeVarBinding, applyVarBinding, joinVarBindings, vbmCompose, varBindingId) -import Swish.Utils.LookupMap-    ( LookupEntryClass(..), LookupMap(..)-    , mapFindMaybe+import Swish.RDF.Query+    ( rdfQueryFind+    , rdfQueryBack, rdfQueryBackModify+    , rdfQuerySubs+    , rdfQuerySubsBlank     ) -{--Used for the Show instance of Ruleset, which was-used for debugging but has been removed as not-really needed by the general user.+import Swish.RDF.Graph+    ( RDFLabel(..), RDFGraph+    , makeBlank, newNodes+    , merge, allLabels+    , toRDFGraph) -import Swish.Utils.ShowM (ShowM(..))-import Data.List (intercalate)--}+import Swish.RDF.VarBinding (RDFVarBinding, RDFVarBindingModify)+import Swish.RDF.Parser.N3 (parseN3) -import Data.Maybe (fromMaybe, listToMaybe, mapMaybe)+import Swish.RDF.Vocabulary (swishName, namespaceRDF, namespaceRDFS) --- | A Rule set.+import Swish.Utils.ListHelpers (equiv, flist) -data Ruleset ex = Ruleset-    { rsNamespace :: Namespace    -- ^ Namespace.-    , rsAxioms    :: [Formula ex] -- ^ Axioms.-    , rsRules     :: [Rule ex]    -- ^ Rules.+import Data.List (nub)+import Data.Maybe (fromMaybe)+import Data.Monoid (Monoid(..))++import qualified Data.Text.Lazy.Builder as B++------------------------------------------------------------+--  Datatypes for RDF ruleset+------------------------------------------------------------++-- | A named formula expressed as a RDF Graph.+type RDFFormula     = Formula RDFGraph++-- | A named inference rule expressed in RDF.+type RDFRule        = Rule RDFGraph++-- | A 'LookupMap' for 'RDFRule' rules.+type RDFRuleMap     = RuleMap RDFGraph++-- | A 'GraphClosure' for RDF statements.+type RDFClosure     = GraphClosure RDFLabel++-- | A 'Ruleset' for RDF.+type RDFRuleset     = Ruleset RDFGraph++-- | 'LookupMap' for 'RDFRuleset'.+type RDFRulesetMap  = RulesetMap RDFGraph++------------------------------------------------------------+--  Declare null RDF formula+------------------------------------------------------------++-- | The null RDF formula.+nullRDFFormula :: Formula RDFGraph+nullRDFFormula = Formula+    { formName = nullSN "nullRDFGraph"+    , formExpr = mempty     } -{-+------------------------------------------------------------+--  Datatype for graph closure rule+------------------------------------------------------------ -Used for debugging.+-- |Datatype for constructing a graph closure rule+data GraphClosure lb = GraphClosure+    { nameGraphRule :: ScopedName   -- ^ Name of rule for proof display+    , ruleAnt       :: [Arc lb]     -- ^ Antecedent triples pattern+                                    --   (may include variable nodes)+    , ruleCon       :: [Arc lb]     -- ^ Consequent triples pattern+                                    --   (may include variable nodes)+    , ruleModify    :: VarBindingModify lb lb+                                    -- ^ Structure that defines additional+                                    --   constraints and/or variable+                                    --   bindings based on other matched+                                    --   query variables.  Matching the+                                    --   antecedents.  Use 'varBindingId' if+                                    --   no additional variable constraints+                                    --   or bindings are added beyond those+                                    --   arising from graph queries.+    } -instance (ShowM ex) => Show (Ruleset ex) where-  show (Ruleset ns axs rls) = -    intercalate "\n" -    [ "Ruleset: " ++ show ns-    , "Axioms:" ]-    ++ (showsFormulae "\n" axs -       (intercalate "\n" ("Rules:" : map show rls))) ""--}+instance (Label lb) => Eq (GraphClosure lb) where+    c1 == c2 = nameGraphRule c1 == nameGraphRule c2 &&+               ruleAnt c1 `equiv` ruleAnt c2 &&+               ruleCon c1 `equiv` ruleCon c2 --- | Ruleset comparisons are based only on their namespace components.-instance Eq (Ruleset ex) where-    r1 == r2 = rsNamespace r1 == rsNamespace r2+instance (Label lb) => Show (GraphClosure lb) where+    show c = "GraphClosure " ++ show (nameGraphRule c) -instance LookupEntryClass (Ruleset ex) Namespace (Ruleset ex)+------------------------------------------------------------+--  Define inference rule based on RDF graph closure rule+------------------------------------------------------------++-- |Define a value of type Rule based on an RDFClosure value.+makeGraphClosureRule :: GraphClosure RDFLabel -> Rule RDFGraph+makeGraphClosureRule grc = newrule     where-        keyVal   r@(Ruleset k _ _) = (k,r)-        newEntry (_,r)             = r+        newrule = Rule+            { ruleName       = nameGraphRule grc+            , fwdApply       = graphClosureFwdApply grc+            , bwdApply       = graphClosureBwdApply grc+            , checkInference = fwdCheckInference newrule+            } -type RulesetMap ex = LookupMap (Ruleset ex)+-- | Forward chaining function based on RDF graph closure description+--+--  Note:  antecedents here are presumed to share bnodes.+--+graphClosureFwdApply :: +  GraphClosure RDFLabel +  -> [RDFGraph] +  -> [RDFGraph]+graphClosureFwdApply grc grs =+    let gr   = if null grs then mempty else foldl1 addGraphs grs+        vars = queryFind (ruleAnt grc) gr+        varm = vbmApply (ruleModify grc) vars+        cons = querySubs varm (ruleCon grc)+    in+        {-+        seq cons $+        seq (trace "\ngraphClosureFwdApply") $+        seq (traceShow "\nvars: " vars) $+        seq (traceShow "\nvarm: " varm) $+        seq (traceShow "\ncons: " cons) $+        seq (trace "\n") $+        -}+        --  Return null list or single result graph that is the union+        --  (not merge) of individual results:+        if null cons then [] else [foldl1 addGraphs cons]+        -- cons {- don't merge results -} --- | Create a ruleset.-makeRuleset :: Namespace -> [Formula ex] -> [Rule ex] -> Ruleset ex-makeRuleset nsp fms rls = Ruleset-    { rsNamespace = nsp-    , rsAxioms    = fms-    , rsRules     = rls+-- | Backward chaining function based on RDF graph closure description+graphClosureBwdApply :: GraphClosure RDFLabel -> RDFGraph -> [[RDFGraph]]+graphClosureBwdApply grc gr =+    let vars = rdfQueryBackModify (ruleModify grc) $+               queryBack (ruleCon grc) gr+        --  This next function eliminates duplicate variable bindings.+        --  It is strictly redundant, but comparing variable+        --  bindings is much cheaper than comparing graphs.+        --  I don't know if many duplicate graphs will be result+        --  of exact duplicate variable bindings, so this may be+        --  not very effective.+        varn = map nub vars+    in+        --  The 'nub ante' below eliminates duplicate antecedent graphs,+        --  based on graph matching, which tests for equivalence under+        --  bnode renaming, with a view to reducing redundant arcs in+        --  the merged antecedent graph, hence less to prove in+        --  subsequent back-chaining steps.+        --+        --  Each antecedent is reduced to a single RDF graph, when+        --  bwdApply specifies a list of expressions corresponding to+        --  each antecedent.+        [ [foldl1 merge (nub ante)]+          | vs <- varn+          , let ante = querySubsBlank vs (ruleAnt grc) ]++------------------------------------------------------------+--  RDF graph query and substitution support functions+------------------------------------------------------------++queryFind :: [Arc RDFLabel] -> RDFGraph -> [RDFVarBinding]+queryFind qas = rdfQueryFind (toRDFGraph qas)++queryBack :: [Arc RDFLabel] -> RDFGraph -> [[RDFVarBinding]]+queryBack qas = rdfQueryBack (toRDFGraph qas)++querySubs :: [RDFVarBinding] -> [Arc RDFLabel] -> [RDFGraph]+querySubs vars = rdfQuerySubs vars . toRDFGraph++querySubsBlank :: [RDFVarBinding] -> [Arc RDFLabel] -> [RDFGraph]+querySubsBlank vars = rdfQuerySubsBlank vars . toRDFGraph++------------------------------------------------------------+--  Method for creating an RDF formula value from N3 text+------------------------------------------------------------++mkPrefix :: Namespace -> B.Builder+mkPrefix = namespaceToBuilder++prefixRDF :: B.Builder+prefixRDF = +  mconcat +  [ mkPrefix namespaceRDF+  , mkPrefix namespaceRDFS+    ]++-- |Helper function to parse a string containing Notation3+--  and return the corresponding RDFGraph value.+--+makeRDFGraphFromN3Builder :: B.Builder -> RDFGraph+makeRDFGraphFromN3Builder b = +  let t = B.toLazyText (prefixRDF `mappend` b)+  in case parseN3 t Nothing of+    Left  msg -> error msg+    Right gr  -> gr++-- |Create an RDF formula.+makeRDFFormula ::+    Namespace     -- ^ namespace to which the formula is allocated+    -> LName      -- ^ local name for the formula in the namespace+    -> B.Builder  -- ^ graph in Notation 3 format+    -> RDFFormula+makeRDFFormula scope local gr = +  Formula+    { formName = makeNSScopedName scope local+    , formExpr = makeRDFGraphFromN3Builder gr     } --- | Extract the namespace of a ruleset.-getRulesetNamespace :: Ruleset ex -> Namespace-getRulesetNamespace = rsNamespace+------------------------------------------------------------+--  Create an RDF closure rule from supplied graphs+------------------------------------------------------------ --- | Extract the axioms from a ruleset.-getRulesetAxioms :: Ruleset ex -> [Formula ex]-getRulesetAxioms = rsAxioms+-- |Constructs an RDF graph closure rule.  That is, a rule that+--  given some set of antecedent statements returns new statements+--  that may be added to the graph.+--+makeRDFClosureRule ::+    ScopedName -- ^ scoped name for the new rule+    -> [RDFGraph] -- ^ RDFGraphs that are the entecedent of the rule.+                  --+                  -- (Note:  bnodes and variable names are assumed to be shared+                  -- by all the entecedent graphs supplied.  /is this right?/)+    -> RDFGraph   -- ^ the consequent graph+    -> RDFVarBindingModify -- ^ is a variable binding modifier value that may impose+    --          additional conditions on the variable bindings that+    --          can be used for this inference rule, or which may+    --          cause new values to be allocated for unbound variables.+    --          These modifiers allow for certain inference patterns+    --          that are not captured by simple "closure rules", such+    --          as the allocation of bnodes corresponding to literals,+    --          and are an extension point for incorporating datatypes+    --          into an inference process.+    --+    --          If no additional constraints or variable bindings are+    --          to be applied, use value 'varBindingId'+    --+    -> RDFRule+makeRDFClosureRule sname antgrs congr vmod = makeGraphClosureRule+    GraphClosure+        { nameGraphRule = sname+        , ruleAnt       = concatMap getArcs antgrs+        , ruleCon       = getArcs congr+        , ruleModify    = vmod+        } --- | Extract the rules from a ruleset.-getRulesetRules :: Ruleset ex -> [Rule ex]-getRulesetRules = rsRules+------------------------------------------------------------+--  Methods to create an RDF closure rule from N3 input+------------------------------------------------------------+--+--  These functions are used internally by Swish to construct+--  rules from textual descriptions. --- | Find a named axiom in a ruleset.-getRulesetAxiom :: ScopedName -> Ruleset ex -> Maybe (Formula ex)-getRulesetAxiom nam rset =-    mapFindMaybe nam (LookupMap (getRulesetAxioms rset))-    -- listToMaybe $ filter ( (matchName nam) . formName ) $ getRulesetAxioms rset+-- |Constructs an RDF graph closure rule.  That is, a rule that+--  given some set of antecedent statements returns new statements+--  that may be added to the graph.  This is the basis for+--  implementation of most of the inference rules given in the+--  RDF formal semantics document.+--+makeN3ClosureRule ::+    Namespace -- ^ namespace to which the rule is allocated+    -> LName  -- ^ local name for the rule in the namespace+    -> B.Builder +    -- ^ the Notation3 representation+    --   of the antecedent graph.  (Note: multiple antecedents+    --   can be handled by combining multiple graphs.)+    -> B.Builder -- ^ the Notation3 representation of the consequent graph.+    -> RDFVarBindingModify+    -- ^ a variable binding modifier value that may impose+    --   additional conditions on the variable bindings that+    --   can be used for this inference rule, or which may+    --   cause new values to be allocated for unbound variables.+    --   These modifiers allow for certain inference patterns+    --   that are not captured by simple closure rules, such+    --   as the allocation of bnodes corresponding to literals,+    --   and are an extension point for incorporating datatypes+    --   into an inference process.+    --+    --   If no additional constraints or variable bindings are+    --   to be applied, use a value of 'varBindingId', or use+    --   'makeN3ClosureSimpleRule'.+    -> RDFRule+makeN3ClosureRule scope local ant con =+    makeRDFClosureRule (makeNSScopedName scope local) [antgr] congr+    where+        antgr = makeRDFGraphFromN3Builder ant+        congr = makeRDFGraphFromN3Builder con --- | Find a named rule in a ruleset. -getRulesetRule :: ScopedName -> Ruleset ex -> Maybe (Rule ex)-getRulesetRule nam rset =-    mapFindMaybe nam (LookupMap (getRulesetRules rset))-    -- listToMaybe $ filter ( (matchName nam) . ruleName ) $ getRulesetRules rset+-- |Construct a simple RDF graph closure rule without+--  additional node allocations or variable binding constraints.+--+makeN3ClosureSimpleRule ::+    Namespace -- ^ namespace to which the rule is allocated+    -> LName  -- ^ local name for the rule in the namepace+    -> B.Builder+    -- ^ the Notation3 representation+    --   of the antecedent graph.  (Note: multiple antecedents+    --   can be handled by combining multiple graphs.)+    -> B.Builder  -- ^ the Notation3 representation of the consequent graph.+    -> RDFRule+makeN3ClosureSimpleRule scope local ant con =+    makeN3ClosureRule scope local ant con varBindingId --- | Find a named axiom in a proof context.-getContextAxiom :: -  ScopedName -- ^ Name of axiom.-  -> Formula ex -- ^ Default axiom (used if named component does not exist).-  -> [Ruleset ex] -- ^ Rulesets to search.-  -> Formula ex-getContextAxiom nam def rsets = fromMaybe def (getMaybeContextAxiom nam rsets)-    {--    foldr (flip fromMaybe) def $ map (getRulesetAxiom nam) rsets-    -}+-- |Constructs an RDF graph closure rule that incorporates+--  a variable binding filter and a variable binding modifier.+--+makeN3ClosureModifyRule ::+    Namespace -- ^ namespace to which the rule is allocated+    -> LName  -- ^ local name for the rule in the given namespace+    -> B.Builder -- ^ the Notation3 representation+    --                of the antecedent graph.  (Note: multiple antecedents+    --                can be handled by combining multiple graphs.)+    -> B.Builder -- ^ the Notation3 representation of the consequent graph.+    -> RDFVarBindingModify+    -- ^ a variable binding modifier value that may impose+    --   additional conditions on the variable bindings that+    --   can be used for this inference rule (@vflt@).+    --+    --   These modifiers allow for certain inference patterns+    --   that are not captured by simple closure rules, such+    --   as deductions that pertain only to certain kinds of+    --   nodes in a graph.+    -> RDFVarBindingModify+    -- ^ a variable binding modifier that is applied to the+    --   variable bindings obtained, typically to create some+    --   additional variable bindings.  This is applied before+    --   the preceeding filter rule (@vflt@).+    -> RDFRule+makeN3ClosureModifyRule scope local ant con vflt vmod =+    makeN3ClosureRule scope local ant con modc+    where+        modc  = fromMaybe varBindingId $ vbmCompose vmod vflt --- | Find a named axiom in a proof context.-getMaybeContextAxiom ::-  ScopedName -- ^ Name of axiom.-  -> [Ruleset ex] -- ^ Rulesets to search.-  -> Maybe (Formula ex)-getMaybeContextAxiom nam rsets =-    listToMaybe $ mapMaybe (getRulesetAxiom nam) rsets+{-+    makeRDFClosureRule (ScopedName scope local) [antgr] congr modc+    where+        antgr = makeRDFGraphFromN3String ant+        congr = makeRDFGraphFromN3String con+        modc  = case vbmCompose vmod vflt of+            Just x  -> x+            Nothing -> varBindingId+-} --- | Find a named rule in a proof context.-getContextRule :: -  ScopedName -- ^ Name of rule.-  -> Rule ex -- ^ Default rule (used if named component does not exist).-  -> [Ruleset ex] -- ^ Rulesets to search.-  -> Rule ex-getContextRule nam def rsets = fromMaybe def (getMaybeContextRule nam rsets)-    {--    foldr (flip fromMaybe) def $ map (getRulesetRule nam) rsets-    -}+-- |Construct an RDF graph closure rule with a bnode allocator.+--+--  This function is rather like 'makeN3ClosureModifyRule', except that+--  the variable binding modifier is a function from the variables in+--  the variables and bnodes contained in the antecedent graph.+--+makeN3ClosureAllocatorRule ::+    Namespace -- ^ namespace to which the rule is allocated+    -> LName  -- ^ local name for the rule in the given namespace+    -> B.Builder -- ^ the Notation3 representation+    --                of the antecedent graph.  (Note: multiple antecedents+    --                can be handled by combining multiple graphs.)+    -> B.Builder -- ^ the Notation3 representation of the consequent graph.+    -> RDFVarBindingModify+    -- ^ variable binding modifier value that may impose+    --   additional conditions on the variable bindings that+    --   can be used for this inference rule (@vflt@).+    -> ( [RDFLabel] -> RDFVarBindingModify )+    -- ^ function applied to a list of nodes to yield a+    --   variable binding modifier value.+    --+    --   The supplied parameter is applied to a list of all of+    --   the variable nodes (including all blank nodes) in the+    --   antecedent graph, and then composed with the @vflt@+    --   value.  This allows any node allocation+    --   function to avoid allocating any blank nodes that+    --   are already used in the antecedent graph.+    --   (See 'makeNodeAllocTo').+    -> RDFRule+makeN3ClosureAllocatorRule scope local ant con vflt aloc =+    makeRDFClosureRule (makeNSScopedName scope local) [antgr] congr modc+    where+        antgr = makeRDFGraphFromN3Builder ant+        congr = makeRDFGraphFromN3Builder con+        vmod  = aloc (allLabels labelIsVar antgr)+        modc  = fromMaybe varBindingId $ vbmCompose vmod vflt --- | Find a named rule in a proof context.-getMaybeContextRule :: -  ScopedName -- ^ Name of rule.-  -> [Ruleset ex] -- ^ Rulesets to search.-  -> Maybe (Rule ex)-getMaybeContextRule nam rsets =-    listToMaybe $ mapMaybe (getRulesetRule nam) rsets+------------------------------------------------------------+--  Query binding modifier for "allocated to" logic+------------------------------------------------------------ +-- |This function defines a variable binding modifier that+--  allocates a new blank node for each value bound to+--  a query variable, and binds it to another variable+--  in each query binding.+--+--  This provides a single binding for query variables that would+--  otherwise be unbound by a query.  For example, consider the+--  inference pattern:+--        +--  >  ?a hasUncle ?c => ?a hasFather ?b . ?b hasBrother ?c .+--        +--  For a given @?a@ and @?c@, there is insufficient information+--  here to instantiate a value for variable @?b@.  Using this+--  function as part of a graph instance closure rule allows+--  forward chaining to allocate a single bnode for each+--  occurrence of @?a@, so that given:+--        +--  >  Jimmy hasUncle Fred .+--  >  Jimmy hasUncle Bob .+--+--  leads to exactly one bnode inference of:+--+--  >  Jimmy hasFather _:f .+--+--  giving:+--+--  >  Jimmy hasFather _:f .+--  >  _:f   hasBrother Fred .+--  >  _:f   hasBrother Bob .+--+--  rather than:+--+--  >  Jimmy hasFather _:f1 .+--  >  _:f1  hasBrother Fred .+--  >  Jimmy hasFather _:f2 .+--  >  _:f2  hasBrother Bob .+--+--  This form of constrained allocation of bnodes is also required for+--  some of the inference patterns described by the RDF formal semantics,+--  particularly those where bnodes are substituted for URIs or literals.+--+makeNodeAllocTo ::+    RDFLabel      -- ^ variable node to which a new blank node is bound+    -> RDFLabel   -- ^ variable which is bound in each query to a graph+                  --  node to which new blank nodes are allocated.+    -> [RDFLabel]+    -> RDFVarBindingModify+makeNodeAllocTo bindvar alocvar exbnode = VarBindingModify+        { vbmName   = swishName "makeNodeAllocTo"+        , vbmApply  = applyNodeAllocTo bindvar alocvar exbnode+        , vbmVocab  = [alocvar,bindvar]+        , vbmUsage  = [[bindvar]]+        }++--  Auxiliary function that performs the node allocation defined+--  by makeNodeAllocTo.+--+--  bindvar is a variable node to which a new blank node is bound+--  alocvar is a variable which is bound in each query to a graph+--          node to which new blank nodes are allocated.+--  exbnode is a list of existing blank nodes, to be avoided by+--          the new blank node allocator.+--  vars    is a list of variable bindings to which new bnode+--          allocations for the indicated bindvar are to be added.+--+applyNodeAllocTo ::+    RDFLabel -> RDFLabel -> [RDFLabel] -> [RDFVarBinding] -> [RDFVarBinding]+applyNodeAllocTo bindvar alocvar exbnode vars =+    let+        app       = applyVarBinding+        alocnodes = zip (nub $ flist (map app vars) alocvar)+                        (newNodes (makeBlank bindvar) exbnode)+        newvb var = joinVarBindings+            ( makeVarBinding $ head+              [ [(bindvar,b)] | (v,b) <- alocnodes, app var alocvar == v ] )+            var+    in+        map newvb vars++ -------------------------------------------------------------------------------- -----  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke   --  All rights reserved. -- --  This file is part of Swish.
− src/Swish/RDF/SwishCommands.hs
@@ -1,422 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  SwishCommands---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  License     :  GPL V2------  Maintainer  :  Douglas Burke---  Stability   :  experimental---  Portability :  OverloadedStrings------  SwishCommands:  functions to deal with indivudual Swish command options.--------------------------------------------------------------------------------------module Swish.RDF.SwishCommands-    ( swishFormat-    , swishBase-    -- , swishVerbose-    , swishInput-    , swishOutput-    , swishMerge-    , swishCompare-    , swishGraphDiff-    , swishScript-    )-where--import Swish.RDF.SwishMonad-    ( SwishStateIO, SwishState(..), SwishStatus(..)-    , setFormat, setBase, setGraph-    , resetInfo, resetError, setStatus-    -- , setVerbose-    , SwishFormat(..)-    , swishError-    , reportLine-    )--import Swish.RDF.SwishScript (parseScriptFromText)--import Swish.RDF.GraphPartition-    ( GraphPartition(..)-    , partitionGraph, comparePartitions-    , partitionShowP-    )--import Swish.RDF.RDFGraph-    ( RDFGraph, merge )--import qualified Swish.RDF.TurtleFormatter as TTLF-import qualified Swish.RDF.N3Formatter as N3F-import qualified Swish.RDF.NTFormatter as NTF--import Swish.RDF.TurtleParser (parseTurtle)-import Swish.RDF.N3Parser (parseN3)-import Swish.RDF.NTParser (parseNT)-import Swish.RDF.RDFParser (appendURIs)--import Swish.RDF.GraphClass-    ( LDGraph(..)-    , Label(..)-    )--import Swish.Utils.QName (QName, qnameFromURI, qnameFromFilePath, getQNameURI)--import System.IO-    ( Handle, openFile, IOMode(..)-    , hPutStr, hPutStrLn, hClose-    , hIsReadable, hIsWritable-    , stdin, stdout-    )--import Network.URI (parseURIReference)--import Control.Monad.Trans (MonadTrans(..))-import Control.Monad.State (modify, gets)-import Control.Monad (liftM, when)--import qualified Data.Text.Lazy as T-import qualified Data.Text.Lazy.IO as IO-import System.IO.Error--import Data.Maybe (isJust, fromMaybe)-----------------------------------------------------------------  Set file format to supplied value----------------------------------------------------------------- the second argument allows for options to be passed along--- with the format (a la cwm) but this is not supported yet----swishFormat :: SwishFormat -> Maybe String -> SwishStateIO ()-swishFormat fmt _ = modify (setFormat fmt)-----------------------------------------------------------------  Set base URI to supplied value----------------------------------------------------------------- the Maybe String argument is ignored (a result of a lack of--- design with the command-line processing)----swishBase :: Maybe QName -> Maybe String -> SwishStateIO ()-swishBase mb _ = modify (setBase mb)-----------------------------------------------------------------  Read graph from named file---------------------------------------------------------------swishInput :: Maybe String -> SwishStateIO ()-swishInput fnam =-  swishReadGraph fnam >>= maybe (return ()) (modify . setGraph)-  ----------------------------------------------------------------  Merge graph from named file---------------------------------------------------------------swishMerge :: Maybe String -> SwishStateIO ()-swishMerge fnam =-  swishReadGraph fnam >>= maybe (return ()) (modify . mergeGraph)-    -mergeGraph :: RDFGraph -> SwishState -> SwishState-mergeGraph gr state = state { graph = newgr }-    where-        newgr = merge gr (graph state)-----------------------------------------------------------------  Compare graph from named file---------------------------------------------------------------swishCompare :: Maybe String -> SwishStateIO ()-swishCompare fnam =-  swishReadGraph fnam >>= maybe (return ()) compareGraph-    -compareGraph :: RDFGraph -> SwishStateIO ()-compareGraph gr = do-  oldGr <- gets graph-  let exitCode = if gr == oldGr then SwishSuccess else SwishGraphCompareError-  modify $ setStatus exitCode-  ----------------------------------------------------------------  Display graph differences from named file---------------------------------------------------------------swishGraphDiff :: Maybe String -> SwishStateIO ()-swishGraphDiff fnam =-  swishReadGraph fnam >>= maybe (return ()) diffGraph--diffGraph :: RDFGraph -> SwishStateIO ()-diffGraph gr = do-  oldGr <- gets graph-  let p1 = partitionGraph (getArcs oldGr)-      p2 = partitionGraph (getArcs gr)-      diffs = comparePartitions p1 p2-      -  swishWriteFile (swishOutputDiffs diffs) Nothing-  -swishOutputDiffs :: (Label lb) =>-    [(Maybe (GraphPartition lb),Maybe (GraphPartition lb))]-    -> Maybe String -    -> Handle-    -> SwishStateIO ()-swishOutputDiffs diffs fnam hnd = do-  lift $ hPutStrLn hnd ("Graph differences: "++show (length diffs))-  mapM_ (swishOutputDiff fnam hnd) (zip [1..] diffs)--swishOutputDiff :: (Label lb) =>-    Maybe String -    -> Handle-    -> (Int,(Maybe (GraphPartition lb),Maybe (GraphPartition lb)))-    -> SwishStateIO ()-swishOutputDiff fnam hnd (diffnum,(part1,part2)) = do-  lift $ hPutStrLn hnd ("---- Difference "++show diffnum++" ----")-  lift $ hPutStr hnd "Graph 1:"-  swishOutputPart fnam hnd part1-  lift $ hPutStr hnd "Graph 2:"-  swishOutputPart fnam hnd part2--swishOutputPart :: (Label lb) =>-    Maybe String -    -> Handle -    -> Maybe (GraphPartition lb) -    -> SwishStateIO ()-swishOutputPart _ hnd part = -  let out = maybe "\n(No arcs)" (partitionShowP "\n") part-  in lift $ hPutStrLn hnd out-----------------------------------------------------------------  Execute script from named file---------------------------------------------------------------swishScript :: Maybe String -> SwishStateIO ()-swishScript fnam = swishReadScript fnam >>= mapM_ swishCheckResult--swishReadScript :: Maybe String -> SwishStateIO [SwishStateIO ()]-swishReadScript = swishReadFile swishParseScript []--{-|-Calculate the base URI to use; it combines the file name-with any user-supplied base.--If both the file name and user-supplied base are Nothing-then the value --   http://id.ninebynine.org/2003/Swish/--is used.--Needs some work.--}--defURI :: QName-defURI = "http://id.ninebynine.org/2003/Swish/"--calculateBaseURI ::-  Maybe FilePath -- ^ file name-  -> SwishStateIO QName -- ^ base URI-calculateBaseURI Nothing = fromMaybe defURI `liftM` gets base-calculateBaseURI (Just fnam) =-  case parseURIReference fnam of-    Just furi -> do-      mbase <- gets base-      case mbase of-        Just buri -> case appendURIs (getQNameURI buri) furi of-          Left emsg -> fail emsg -- TODO: think about this ...-          Right res -> return $ qnameFromURI res-        Nothing -> lift $ qnameFromFilePath fnam-        -    Nothing -> fail $ "Unable to convert to URI: filepath=" ++ fnam--swishParseScript ::-  Maybe String -- file name (or "stdin" if Nothing)-  -> T.Text    -- script contents-  -> SwishStateIO [SwishStateIO ()]-swishParseScript mfpath inp = do-  buri <- calculateBaseURI mfpath-  case parseScriptFromText (Just buri) inp of-    Left err -> do-      let inName = maybe "standard input" ("file " ++) mfpath-      swishError ("Script syntax error in " ++ inName ++ ": "++err) SwishDataInputError-      return []-              -    Right scs -> return scs--swishCheckResult :: SwishStateIO () -> SwishStateIO ()-swishCheckResult swishcommand = do-  swishcommand-  er <- gets errormsg-  case er of  -    Just x -> swishError x SwishExecutionError >> modify resetError-    _      -> return ()-    -  ms <- gets infomsg-  case ms of-    Just x -> reportLine x >> modify resetInfo-    _      -> return ()-----------------------------------------------------------------  Output graph to named file---------------------------------------------------------------swishOutput :: Maybe String -> SwishStateIO ()-swishOutput = swishWriteFile swishOutputGraph-   -swishOutputGraph :: Maybe String -> Handle -> SwishStateIO ()-swishOutputGraph _ hnd = do-  fmt <- gets format-  -  let writeOut formatter = do-        out <- gets $ formatter . graph-        lift $ IO.hPutStrLn hnd out-        -  case fmt of-    N3 -> writeOut N3F.formatGraphAsLazyText-    NT -> writeOut NTF.formatGraphAsLazyText-    Turtle -> writeOut TTLF.formatGraphAsLazyText-    -- _  -> swishError ("Unsupported file format: "++show fmt) SwishArgumentError-----------------------------------------------------------------  Common input functions-------------------------------------------------------------------  Keep the logic separate for reading file data and---  parsing it to an RDF graph value.--swishReadGraph :: Maybe String -> SwishStateIO (Maybe RDFGraph)-swishReadGraph = swishReadFile swishParse Nothing---- | Open a file (or stdin), read its contents, and process them.----swishReadFile :: -  (Maybe String -> T.Text -> SwishStateIO a) -- ^ Convert filename and contents into desired value-  -> a -- ^ the value to use if the file can not be read in-  -> Maybe String -- ^ the file name or @stdin@ if @Nothing@-  -> SwishStateIO a-swishReadFile conv errVal fnam = -  let reader (h,f,i) = do-        res <- conv fnam i-        when f $ lift $ hClose h -- given that we use IO.hGetContents not sure the close is needed-        return res-  -  in swishOpenFile fnam >>= maybe (return errVal) reader---- | Open and read file, returning its handle and content, or Nothing--- WARNING:  the handle must not be closed until input is fully evaluated----swishOpenFile :: Maybe String -> SwishStateIO (Maybe (Handle, Bool, T.Text))-swishOpenFile Nothing     = readFromHandle stdin Nothing-swishOpenFile (Just fnam) = do-  o <- lift $ try $ openFile fnam ReadMode-  case o of-    Left  _ -> do-      swishError ("Cannot open file: "++fnam) SwishDataAccessError-      return Nothing-      -    Right hnd -> readFromHandle hnd $ Just ("file: " ++ fnam)--readFromHandle :: Handle -> Maybe String -> SwishStateIO (Maybe (Handle, Bool, T.Text))-readFromHandle hdl mlbl = do-  hrd <- lift $ hIsReadable hdl-  if hrd-    then do-      fc <- lift $ IO.hGetContents hdl-      return $ Just (hdl, isJust mlbl, fc)-  -    else do-      lbl <- case mlbl of-        Just l  -> lift (hClose hdl) >> return l-        Nothing -> return "standard input"-      swishError ("Cannot read from " ++ lbl) SwishDataAccessError-      return Nothing--swishParse :: -  Maybe String -- ^ filename (if not stdin)-  -> T.Text    -- ^ contents of file-  -> SwishStateIO (Maybe RDFGraph)-swishParse mfpath inp = do-  fmt <- gets format-  buri <- calculateBaseURI mfpath-  -  let toError eMsg =-        swishError (show fmt ++ " syntax error in " ++ inName ++ ": " ++ eMsg) SwishDataInputError -        >> return Nothing-        -      inName = maybe "standard input" ("file " ++) mfpath-  -      readIn reader = case reader inp of-        Left eMsg -> toError eMsg-        Right res -> return $ Just res-             -  case fmt of-    Turtle -> readIn (`parseTurtle` Just (getQNameURI buri))-    N3 -> readIn (`parseN3` Just buri)-    NT -> readIn parseNT-    {--    _  -> swishError ("Unsupported file format: "++show fmt) SwishArgumentError >>-          return Nothing-    -}-    -swishWriteFile :: -  (Maybe String -> Handle -> SwishStateIO ()) -- ^ given a file name and a handle, write to it-  -> Maybe String-  -> SwishStateIO ()-swishWriteFile conv fnam =  -  let hdlr (h, c) = conv fnam h >> when c (lift $ hClose h)-  in swishCreateWriteableFile fnam >>= maybe (return ()) hdlr-   --- | Open file for writing, returning its handle, or Nothing---  Also returned is a flag indicating whether or not the---  handled should be closed when writing is done (if writing---  to standard output, the handle should not be closed as the---  run-time system should deal with that).-swishCreateWriteableFile :: Maybe String -> SwishStateIO (Maybe (Handle,Bool))-swishCreateWriteableFile Nothing = do-  hwt <- lift $ hIsWritable stdout-  if hwt-    then return $ Just (stdout, False)-    else do-      swishError "Cannot write to standard output" SwishDataAccessError-      return Nothing-  -swishCreateWriteableFile (Just fnam) = do-  o <- lift $ try $ openFile fnam WriteMode-  case o of-    Left _ -> do-      swishError ("Cannot open file for writing: " ++ fnam) SwishDataAccessError-      return Nothing-      -    Right hnd -> do-      hwt <- lift $ hIsWritable hnd-      if hwt-        then return $ Just (hnd, True)-        else do-          lift $ hClose hnd-          swishError ("Cannot write to file: "++fnam) SwishDataAccessError-          return Nothing-  ---------------------------------------------------------------------------------------  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke  ---  All rights reserved.------  This file is part of Swish.------  Swish is free software; you can redistribute it and/or modify---  it under the terms of the GNU General Public License as published by---  the Free Software Foundation; either version 2 of the License, or---  (at your option) any later version.------  Swish is distributed in the hope that it will be useful,---  but WITHOUT ANY WARRANTY; without even the implied warranty of---  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the---  GNU General Public License for more details.------  You should have received a copy of the GNU General Public License---  along with Swish; if not, write to:---    The Free Software Foundation, Inc.,---    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA------------------------------------------------------------------------------------
− src/Swish/RDF/SwishMain.hs
@@ -1,291 +0,0 @@------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  SwishMain---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  License     :  GPL V2------  Maintainer  :  Douglas Burke---  Stability   :  experimental---  Portability :  H98------  I anticipate that this module may be used as a starting point for---  creating new programs rather then as a complete program in its own---  right.  The functionality built into this code is selected with a---  view to testing the Haskell modules for handling RDF rather than---  for performing any particular application processing (though---  development as a tool with some broader utility is not ruled out).------  With the following in ghci:------ >>> :m + Swish--- >>> :set prompt "swish> "------ then we can run a Swish script (format described in "Swish.RDF.SwishScript")--- by saying:------ >>> runSwish "-s=script.ss"--- ExitSuccess------ or convert a file from Turtle to NTriples format with:------ >>> runSwish "-ttl -i=foo.ttl -nt -o=foo.nt"--- ExitSuccess------ You can also use `validateCommands` by giving it the individual commands,--- such as------ >>> let Right cs = validateCommands ["-ttl", "-i=file1.ttl", "-c=file2.ttl"]--- >>> cs--- [SwishAction: -ttl,SwishAction: -i=file1.ttl,SwishAction: -c=file2.ttl]--- >>> st <- runSwishActions cs--- >>> st--- The graphs do not compare as equal.----------------------------------------------------------------------------------------  TODO:------  * Add RDF/XML input and output-----module Swish.RDF.SwishMain (-  SwishStatus(..), SwishAction,-  runSwish,-  runSwishActions,-  displaySwishHelp,-  splitArguments,-  validateCommands-  ) where--import Swish.RDF.SwishCommands-    ( swishFormat-    , swishBase-    , swishInput-    , swishOutput-    , swishMerge-    , swishCompare-    , swishGraphDiff-    , swishScript-    )--import Swish.RDF.SwishMonad-    ( SwishStateIO, SwishState(..), SwishStatus(..)-    , emptyState-    , SwishFormat(..)-    )--import Swish.Utils.QName (qnameFromURI)-import Swish.Utils.ListHelpers (breakAll)--import Control.Monad.State (execStateT)-import Control.Monad (liftM)--import Network.URI (parseURI)--import Data.Char (isSpace)-import Data.Either (partitionEithers)--import System.Exit (ExitCode(ExitSuccess, ExitFailure))-----------------------------------------------------------------  Command line description----------------------------------------------------------------- we do not display the version in the help file to avoid having--- to include the Paths_swish module (so that we can use this from--- an interactive environment).-----usageText :: [String]-usageText =-    [ "Swish: Read, merge, write, compare and process RDF graphs."-    , ""-    , "Usage: swish option option ..."-    , ""-    , "where the options are processed from left to right, and may be"-    , "any of the following:"-    , "-h        display this message."-    , "-?        display this message."-    , "-v        display Swish version and quit."-    , "-q        do not display Swish version on start up."-    , "-nt       use Ntriples format for subsequent input and output."-    , "-ttl      use Turtle format for subsequent input and output."-    , "-n3       use Notation3 format for subsequent input and output (default)"-    , "-i[=file] read file in selected format into the graph workspace,"-    , "          replacing any existing graph."-    , "-m[=file] merge file in selected format with the graph workspace."-    , "-c[=file] compare file in selected format with the graph workspace."-    , "-d[=file] show graph differences between the file in selected"-    , "          format and the graph workspace.  Differences are displayed"-    , "          to the standard output stream."-    , "-o[=file] write the graph workspace to a file in the selected format."-    , "-s[=file] read and execute Swish script commands from the named file."-    , "-b[=base] set or clear the base URI. The semantics of this are not"-    , "          fully defined yet."-    , ""-    , "    If an optional filename value is omitted, the standard input"-    , "    or output stream is used, as appropriate."-    , ""-    , "Exit status codes:"-    , "Success - operation completed successfully/graphs compare equal"-    , "1 - graphs compare different"-    , "2 - input data format error"-    , "3 - file access problem"-    , "4 - command line error"-    , "5 - script file execution error"-    , ""-    , "Examples:"-    , ""-    , "swish -i=file"-    , "    read file as Notation3, and report any syntax errors."-    , "swish -i=file1 -o=file2"-    , "    read file1 as Notation3, report any syntax errors, and output the"-    , "    resulting graph as reformatted Notation3 (the output format"-    , "    is not perfect but may be improved)."-    , "swish -nt -i=file -n3 -o"-    , "    read file as NTriples and output as Notation3 to the screen."-    , "swich -i=file1 -c=file2"-    , "    read file1 and file2 as notation3, report any syntax errors, and"-    , "    if both are OK, compare the resulting graphs to indicate whether"-    , "    or not they are equivalent."-    ]---- | Write out the help for Swish-displaySwishHelp :: IO ()-displaySwishHelp = mapM_ putStrLn usageText-----------------------------------------------------------------  Swish command line interpreter-------------------------------------------------------------------  This is a composite monad combining some state with an IO---  Monad.  lift allows a pure IO monad to be used as a step---  of the computation.----        --- | Return any arguments that need processing immediately, namely                     --- the \"help\", \"quiet\" and \"version\" options.----splitArguments :: [String] -> ([String], [String])-splitArguments = partitionEithers . map splitArgument--splitArgument :: String -> Either String String-splitArgument "-?" = Left "-h"-splitArgument "-h" = Left "-h"-splitArgument "-v" = Left "-v"-splitArgument "-q" = Left "-q"-splitArgument x    = Right x---- | Represent a Swish action. At present there is no way to create these--- actions other than 'validateCommands'.--- -newtype SwishAction = SA (String, SwishStateIO ())--instance Show SwishAction where-  show (SA (lbl,_)) = "SwishAction: " ++ lbl---- | Given a list of command-line arguments create the list of actions--- to perform or a string and status value indicating an input error.-validateCommands :: [String] -> Either (String, SwishStatus) [SwishAction]-validateCommands args = -  let (ls, rs) = partitionEithers (map validateCommand args)-  in case ls of-    (e:_) -> Left e-    []    -> Right rs-  --- This allows you to say "-nt=foo" and currently ignores the values--- passed through. This may change---    -validateCommand :: String -> Either (String, SwishStatus) SwishAction-validateCommand cmd =-  let (nam,more) = break (=='=') cmd-      arg        = drop 1 more-      marg       = if null arg then Nothing else Just arg-      -      wrap f = Right $ SA (cmd, f marg)-  in case nam of-    "-ttl"  -> wrap $ swishFormat Turtle-    "-nt"   -> wrap $ swishFormat NT-    "-n3"   -> wrap $ swishFormat N3-    "-i"    -> wrap swishInput-    "-m"    -> wrap swishMerge-    "-c"    -> wrap swishCompare-    "-d"    -> wrap swishGraphDiff-    "-o"    -> wrap swishOutput-    "-b"    -> validateBase cmd marg-    "-s"    -> wrap swishScript-    _       -> Left ("Invalid command line argument: "++cmd, SwishArgumentError)---- | Execute the given set of actions.-swishCommands :: [SwishAction] -> SwishStateIO ()-swishCommands = mapM_ swishCommand---- | Execute an action.-swishCommand :: SwishAction -> SwishStateIO ()-swishCommand (SA (_,act)) = act--validateBase :: String -> Maybe String -> Either (String, SwishStatus) SwishAction-validateBase arg Nothing  = Right $ SA (arg, swishBase Nothing Nothing)-validateBase arg (Just b) =-  case fmap qnameFromURI (parseURI b) of-    j@(Just _) -> Right $ SA (arg, swishBase j Nothing)-    _      -> Left ("Invalid base URI <" ++ b ++ ">", SwishArgumentError)-  ----------------------------------------------------------------  Interactive test function (e.g. for use in ghci)----------------------------------------------------------------- this ignores the "flags" options, namely---    -q / -h / -? / -v---- | Parse and run the given string as if given at the command--- line. The \"quiet\", \"version\" and \"help\" options are--- ignored.----runSwish :: String -> IO ExitCode-runSwish cmdline = do-  let args = breakAll isSpace cmdline-      (_, cmds) = splitArguments args-      -  case validateCommands cmds of-    Left (emsg, ecode) -> do-      putStrLn $ "Swish exit: " ++ emsg-      return $ ExitFailure $ fromEnum ecode-      -    Right acts -> do-      ec <- runSwishActions acts-      case ec of-        SwishSuccess -> return ExitSuccess-        _  -> do-          putStrLn $ "Swish exit: " ++ show ec-          return $ ExitFailure $ fromEnum ec---- | Execute the given set of actions.-runSwishActions :: [SwishAction] -> IO SwishStatus-runSwishActions acts = exitcode `liftM` execStateT (swishCommands acts) emptyState----------------------------------------------------------------------------------------  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  All rights reserved.------  This file is part of Swish.------  Swish is free software; you can redistribute it and/or modify---  it under the terms of the GNU General Public License as published by---  the Free Software Foundation; either version 2 of the License, or---  (at your option) any later version.------  Swish is distributed in the hope that it will be useful,---  but WITHOUT ANY WARRANTY; without even the implied warranty of---  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the---  GNU General Public License for more details.------  You should have received a copy of the GNU General Public License---  along with Swish; if not, write to:---    The Free Software Foundation, Inc.,---    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA------------------------------------------------------------------------------------
− src/Swish/RDF/SwishMonad.hs
@@ -1,270 +0,0 @@-{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  SwishMonad---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  License     :  GPL V2------  Maintainer  :  Douglas Burke---  Stability   :  experimental---  Portability :  FlexibleInstances, MultiParamTypeClasses------  Composed state and IO monad for Swish--------------------------------------------------------------------------------------module Swish.RDF.SwishMonad-    ( SwishStateIO, SwishState(..), SwishStatus(..)-    , setFormat, setBase, setGraph-    , modGraphs, findGraph, findFormula-    , modRules, findRule-    , modRulesets, findRuleset-    , findOpenVarModify, findDatatype-    , setInfo, resetInfo, setError, resetError-    , setStatus-    -- , setVerbose-    , emptyState-    , SwishFormat(..)-    , NamedGraph(..), NamedGraphMap-    , swishError-    , reportLines, reportLine-    )-where--import Swish.RDF.RDFGraph-    ( RDFGraph, emptyRDFGraph )--import Swish.RDF.RDFRuleset-    ( RDFFormula, RDFRule, RDFRuleMap, RDFRuleset, RDFRulesetMap )--import Swish.RDF.RDFDatatype-    ( RDFDatatype )--import Swish.RDF.RDFVarBinding-    ( RDFOpenVarBindingModify-    )--import Swish.RDF.BuiltInMap-    ( findRDFOpenVarBindingModifier-    , findRDFDatatype-    , rdfRulesetMap-    )--import Swish.RDF.Ruleset-    ( getMaybeContextAxiom-    , getMaybeContextRule-    )--import Swish.RDF.Rule-    ( Formula(..)-    )--import Swish.Utils.Namespace (ScopedName, getScopeNamespace)-import Swish.Utils.QName (QName)--import Swish.Utils.LookupMap-    ( LookupEntryClass(..), LookupMap(..)-    , emptyLookupMap-    , mapFindMaybe-    , mapVals-    )--import Control.Monad.Trans (MonadTrans(..))-import Control.Monad.State (StateT(..), modify)--import System.IO (hPutStrLn, stderr)--{-|-The supported input and output formats.--}-data SwishFormat = -  Turtle  -- ^ Turtle format-  | N3    -- ^ N3 format-  | NT    -- ^ NTriples format-    deriving Eq--instance Show SwishFormat where-  show N3  = "N3"-  show NT  = "Ntriples"-  show Turtle = "Turtle"-  -- show RDF = "RDF/XML"---- | The State for a Swish \"program\".-  -data SwishState = SwishState-    { format    :: SwishFormat      -- ^ format to use for I/O-    , base      :: Maybe QName      -- ^ base to use rather than file name-    , graph     :: RDFGraph         -- ^ current graph-    , graphs    :: NamedGraphMap    -- ^ script processor named graphs-    , rules     :: RDFRuleMap       -- ^ script processor named rules-    , rulesets  :: RDFRulesetMap    -- ^ script processor rulesets-    , infomsg   :: Maybe String     -- ^ information message, or Nothing-    , errormsg  :: Maybe String     -- ^ error message, or Nothing-    , exitcode  :: SwishStatus      -- ^ current status message-    }---- | Status of the processor----data SwishStatus =-  SwishSuccess               -- ^ successful run-  | SwishGraphCompareError   -- ^ graphs do not compare-  | SwishDataInputError      -- ^ input data problem (ie format/syntax)-  | SwishDataAccessError     -- ^ data access error-  | SwishArgumentError       -- ^ command-line argument error-  | SwishExecutionError      -- ^ error executing a Swish script-    deriving (Eq, Enum)--instance Show SwishStatus where-  show SwishSuccess           = "Success."-  show SwishGraphCompareError = "The graphs do not compare as equal."-  show SwishDataInputError    = "There was a format or syntax error in the input data."-  show SwishDataAccessError   = "There was a problem accessing data."-  show SwishArgumentError     = "Argument error: use -h or -? for help."-  show SwishExecutionError    = "There was a problem executing a Swish script."--type SwishStateIO a = StateT SwishState IO a--emptyState :: SwishState-emptyState = SwishState-    { format    = N3-    , base      = Nothing-    , graph     = emptyRDFGraph-    , graphs    = emptyLookupMap-    , rules     = emptyLookupMap-    , rulesets  = rdfRulesetMap-    , infomsg   = Nothing-    , errormsg  = Nothing-    , exitcode  = SwishSuccess-    }--setFormat :: SwishFormat -> SwishState -> SwishState-setFormat   fm state = state { format = fm }--setBase :: Maybe QName -> SwishState -> SwishState-setBase bs state = state { base = bs }--setGraph :: RDFGraph -> SwishState -> SwishState-setGraph    gr state = state { graph = gr }--modGraphs ::-    ( NamedGraphMap -> NamedGraphMap ) -> SwishState -> SwishState-modGraphs grmod state = state { graphs = grmod (graphs state) }--findGraph :: ScopedName -> SwishState -> Maybe [RDFGraph]-findGraph nam state = mapFindMaybe nam (graphs state)--findFormula :: ScopedName -> SwishState -> Maybe RDFFormula-findFormula nam state = case findGraph nam state of-        Nothing  -> getMaybeContextAxiom nam (mapVals $ rulesets state)-        Just []  -> Just $ Formula nam emptyRDFGraph-        Just grs -> Just $ Formula nam (head grs)--modRules ::-    ( RDFRuleMap -> RDFRuleMap ) -> SwishState -> SwishState-modRules rlmod state = state { rules = rlmod (rules state) }--findRule :: ScopedName -> SwishState -> Maybe RDFRule-findRule nam state =-    let-        localrule   = mapFindMaybe nam (rules state)-        contextrule = getMaybeContextRule nam $ mapVals $ rulesets state-    in-        case localrule of-            Nothing -> contextrule-            justlr  -> justlr--modRulesets ::-    ( RDFRulesetMap -> RDFRulesetMap ) -> SwishState -> SwishState-modRulesets rsmod state = state { rulesets = rsmod (rulesets state) }--findRuleset ::-    ScopedName -> SwishState -> Maybe RDFRuleset-findRuleset nam state = mapFindMaybe (getScopeNamespace nam) (rulesets state)--findOpenVarModify :: ScopedName -> SwishState -> Maybe RDFOpenVarBindingModify-findOpenVarModify nam _ = findRDFOpenVarBindingModifier nam--findDatatype :: ScopedName -> SwishState -> Maybe RDFDatatype-findDatatype nam _ = findRDFDatatype nam--setInfo :: String -> SwishState -> SwishState-setInfo msg state = state { infomsg = Just msg }--resetInfo :: SwishState -> SwishState-resetInfo state = state { infomsg = Nothing }--setError :: String -> SwishState -> SwishState-setError msg state = state { errormsg = Just msg }--resetError :: SwishState -> SwishState-resetError state = state { errormsg = Nothing }--setStatus :: SwishStatus -> SwishState -> SwishState-setStatus ec state = state { exitcode = ec }--{--setVerbose :: Bool -> SwishState -> SwishState-setVerbose f state = state { banner = f }--}---- | The graphs dictionary contains named graphs and/or lists---  of graphs that are created and used by script statements.--data NamedGraph = NamedGraph-    { ngName    :: ScopedName-    , ngGraph   :: [RDFGraph]-    }--instance LookupEntryClass NamedGraph ScopedName [RDFGraph]-    where-        keyVal   (NamedGraph k v) = (k,v)-        newEntry (k,v)            = NamedGraph k v--type NamedGraphMap = LookupMap NamedGraph---- | Report error and set exit status code--swishError :: String -> SwishStatus -> SwishStateIO ()-swishError msg sts = do-  reportLines [msg, show sts ++ "\n"]-  -- when (sts == 4) $ reportLine "Use 'Swish -h' or 'Swish -?' for help\n"-  modify $ setStatus sts---- | Output text to the standard error stream------  Each string in the supplied list is a line of text to---  be displayed.--reportLines  :: [String] -> SwishStateIO ()-reportLines = mapM_ reportLine --reportLine  :: String -> SwishStateIO ()-reportLine line =-    -- lift putStrLn line-    lift $ hPutStrLn stderr line----------------------------------------------------------------------------------------  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke ---  All rights reserved.------  This file is part of Swish.------  Swish is free software; you can redistribute it and/or modify---  it under the terms of the GNU General Public License as published by---  the Free Software Foundation; either version 2 of the License, or---  (at your option) any later version.------  Swish is distributed in the hope that it will be useful,---  but WITHOUT ANY WARRANTY; without even the implied warranty of---  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the---  GNU General Public License for more details.------  You should have received a copy of the GNU General Public License---  along with Swish; if not, write to:---    The Free Software Foundation, Inc.,---    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA------------------------------------------------------------------------------------
− src/Swish/RDF/SwishScript.hs
@@ -1,1512 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}------------------------------------------------------------------------------------  See end of this file for licence information.----------------------------------------------------------------------------------{- |-Module      :  SwishScript-Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke-License     :  GPL V2--Maintainer  :  Douglas Burke-Stability   :  experimental-Portability :  OverloadedStrings--This module implements the Swish script processor:  it parses a script-from a supplied string, and returns a list of Swish state transformer-functions whose effect, when applied to a state value, is to implement-the supplied script.---}--module Swish.RDF.SwishScript-    ( -      -- * Syntax-      -- $syntax-      -      -- ** Defining a prefix-      -- $prefixLine-      -      -- ** Naming a graph-      -- $nameItem-      -      -- ** Reading and writing graphs-      -      -- $readGraph-      -      -- $writeGraph-      -      -- ** Merging graphs-      -- $mergeGraphs-      -      -- ** Comparing graphs-      -      -- $compareGraphs-      -      -- $assertEquiv-      -      -- $assertMember-      -      -- ** Defining rules-      -      -- $defineRule-      -      -- $defineRuleset-      -      -- $defineConstraints-      -      -- ** Apply a rule-      -- $fwdChain-      -      -- $bwdChain-      -      -- ** Define a proof-      -- $proof-      -      -- * An example script-      -- $exampleScript-      -      -- * Parsing-      -      parseScriptFromText -    )-where--import Swish.RDF.SwishMonad-    ( SwishStateIO, SwishStatus(..) -    , modGraphs, findGraph, findFormula-    , modRules, findRule-    , modRulesets, findRuleset-    , findOpenVarModify, findDatatype-    , setInfo, setError, setStatus-    , NamedGraph(..)-    )--import Swish.RDF.RDFDatatype-    ( RDFDatatype )--import Swish.RDF.RDFRuleset-    ( RDFFormula, RDFRule-    , RDFRuleset-    , makeRDFClosureRule-    )--import Swish.RDF.RDFProof-    ( RDFProofStep, makeRDFProof, makeRDFProofStep )--import Swish.RDF.RDFVarBinding-    ( RDFVarBindingModify-    )--import Swish.RDF.RDFGraphShowM()--import Swish.RDF.RDFGraph-    ( RDFGraph, RDFLabel(..)-    , emptyRDFGraph-    , NamespaceMap-    , setNamespaces-    , merge, add-    )--import Swish.RDF.RDFParser (whiteSpace, lexeme, symbol, eoln, manyTill)--import Swish.RDF.N3Parser-    ( parseAnyfromText-    , parseN3      -    , N3Parser, N3State(..)-    , getPrefix-    , subgraph-    , n3symbol -- was uriRef2,-    , quickVariable -- was varid-    , lexUriRef-    , newBlankNode-    )--import Swish.RDF.N3Formatter (formatGraphAsBuilder)-import Swish.RDF.Datatype (typeMkRules)-import Swish.RDF.Proof (explainProof, showsProof)-import Swish.RDF.Ruleset (makeRuleset, getRulesetRule, getMaybeContextRule)-import Swish.RDF.Rule (Formula(..), Rule(..)) -import Swish.RDF.VarBinding (composeSequence)--import Swish.Utils.Namespace (ScopedName, getScopeNamespace)-import Swish.Utils.QName (QName, qnameFromURI)-import Swish.Utils.LookupMap (mapReplaceOrAdd)-import Swish.Utils.ListHelpers (equiv, flist)--import qualified Data.Text.Lazy as L-import qualified Data.Text.Lazy.Builder as B-import qualified Data.Text.Lazy.IO as LIO-import Text.ParserCombinators.Poly.StateText--import Control.Monad (unless, when, liftM)-import Control.Monad.State (modify, gets, lift)--import Network.URI (URI(..))--import Data.Monoid (Monoid(..))--import qualified System.IO.Error as IO--------------------------------------------------------------------  The parser used to be based on the Notation3 parser, and used many---  of the same syntax productions, but the top-level productions used---  are quite different. With the parser re-write it's less clear---  what is going on.------ NOTE: during the parser re-write we strip out some of this functionality--- ---- | Parser for Swish script processor-parseScriptFromText :: -  Maybe QName -- ^ Default base for the script-  -> L.Text   -- ^ Swish script-  -> Either String [SwishStateIO ()]-parseScriptFromText = parseAnyfromText script ---------------------------------------------------------------------------  Syntax productions-------------------------------------------------------------------------between :: Parser s lbr -> Parser s rbr -> Parser s a -> Parser s a-between = bracket--n3SymLex :: N3Parser ScopedName-n3SymLex = lexeme n3symbol--setTo :: N3Parser ()-setTo = isymbol ":-"--semicolon :: N3Parser ()-semicolon = isymbol ";"--comma :: N3Parser ()-comma = isymbol ","--commentText :: N3Parser String-commentText = semicolon *> restOfLine--script :: N3Parser [SwishStateIO ()]-script = do-  whiteSpace-  scs <- many command-  eof-  return scs--isymbol :: String -> N3Parser ()-isymbol s = symbol s >> return ()--command :: N3Parser (SwishStateIO ())-command =-  prefixLine-  <|> nameItem-  <|> readGraph-  <|> writeGraph-  <|> mergeGraphs-  <|> compareGraphs-  <|> assertEquiv-  <|> assertMember-  <|> defineRule-  <|> defineRuleset-  <|> defineConstraints-  <|> checkProofCmd-  <|> fwdChain-  <|> bwdChain--prefixLine :: N3Parser (SwishStateIO ())-prefixLine = do-  -- try $ isymbol "@prefix"-  isymbol "@prefix"-  getPrefix-  whiteSpace-  isymbol "."-  return $ return ()----  name :- graph---  name :- ( graph* )-nameItem :: N3Parser (SwishStateIO ())-nameItem = -  ssAddGraph <$> n3SymLex <*> (symbol ":-" *> graphOrList)-  -maybeURI :: N3Parser (Maybe URI)-maybeURI = (Just <$> lexUriRef) <|> return Nothing----  @read name  [ <uri> ]-readGraph :: N3Parser (SwishStateIO ())-readGraph = commandName "@read" *> (ssRead <$> n3SymLex <*> maybeURI)----  @write name [ <uri> ] ; Comment-writeGraph :: N3Parser (SwishStateIO ())-writeGraph =-        do  { commandName "@write"-            ; n <- n3SymLex-            ; let gs = ssGetList n :: SwishStateIO (Either String [RDFGraph])-            ; muri <- maybeURI-            ; c <- commentText-            ; return $ ssWriteList muri gs c-            }----  @merge ( name* ) => name-mergeGraphs :: N3Parser (SwishStateIO ())-mergeGraphs = do-  commandName "@merge"-  gs <- graphList-  isymbol "=>"-  n <- n3SymLex-  return $ ssMerge n gs---- @compare  name name-compareGraphs :: N3Parser (SwishStateIO ())-compareGraphs =-  commandName "@compare" *> (ssCompare <$> n3SymLex <*> n3SymLex)-  --- @<command> name name ; Comment-assertArgs :: (ScopedName -> ScopedName -> String -> SwishStateIO ())-              -> String -> N3Parser (SwishStateIO ())-assertArgs assertFunc cName = do-  commandName $ '@':cName-  assertFunc <$> n3SymLex <*> n3SymLex <*> commentText-      ---  @asserteq name name ; Comment-assertEquiv :: N3Parser (SwishStateIO ())-assertEquiv = assertArgs ssAssertEq "asserteq" -        ---  @assertin name name ; Comment              -assertMember :: N3Parser (SwishStateIO ())-assertMember = assertArgs ssAssertIn "assertin"-  ---  @rule name :- ( name* ) => name [ | ( (name var*)* ) ]               -defineRule :: N3Parser (SwishStateIO ())-defineRule =-        do  { commandName "@rule"-            ; rn <- n3SymLex-            ; setTo-            ; ags <- graphOrList-            ; isymbol "=>"-            ; cg  <- graphExpr-            ; vms <- varModifiers <|> pure []-            ; return $ ssDefineRule rn ags cg vms-            }----  @ruleset name :- ( name* ) ; ( name* )-defineRuleset :: N3Parser (SwishStateIO ())-defineRuleset =-  commandName "@ruleset" *>      -  (ssDefineRuleset <$> n3SymLex <*> (setTo *> nameList) <*> (semicolon *> nameList))-  ---  @constraints pref :- ( name* ) | ( name* )-defineConstraints :: N3Parser (SwishStateIO ())-defineConstraints =-  commandName "@constraints" *>      -  (ssDefineConstraints <$> n3SymLex <*> (setTo *> graphOrList) <*> (symbol "|" *> nameOrList))-  ---  @proof name ( name* )---    @input name---    @step name ( name* ) => name  # rule-name, antecedents, consequent---    @result name-checkProofCmd :: N3Parser (SwishStateIO ())-checkProofCmd =-        do  { commandName "@proof"-            ; pn  <- n3SymLex-            ; sns <- nameList-            ; commandName "@input"-            ; igf <- formulaExpr-            ; sts <- many checkStep-            ; commandName "@result"-            ; rgf <- formulaExpr-            ; return $ ssCheckProof pn sns igf sts rgf-            }--checkStep ::-    N3Parser (Either String [RDFRuleset]-                -> SwishStateIO (Either String RDFProofStep))-checkStep =-  commandName "@step" *>      -  (ssCheckStep <$> n3SymLex <*> formulaList <*> (symbol "=>" *> formulaExpr))----  #   ruleset rule (antecedents) => result---  @fwdchain pref name ( name* ) => name-fwdChain :: N3Parser (SwishStateIO ())-fwdChain =-        do  { commandName "@fwdchain"-            ; sn  <- n3SymLex-            ; rn  <- n3SymLex-            ; ags <- graphOrList-            ; isymbol "=>"-            ; cn  <- n3SymLex-            ; s <- stGet-            ; let prefs = prefixUris s-            ; return $ ssFwdChain sn rn ags cn prefs-            }----  #   ruleset rule consequent <= (antecedent-alts)---  @bwdchain pref name graph <= name-bwdChain :: N3Parser (SwishStateIO ())-bwdChain =-        do  { commandName "@bwdchain"-            ; sn  <- n3SymLex-            ; rn  <- n3SymLex-            ; cg  <- graphExpr-            ; isymbol "<="-            ; an  <- n3SymLex-            ; s <- stGet-            ; let prefs = prefixUris s-            ; return $ ssBwdChain sn rn cg an prefs-            }---------------------------------------------------------------------------  Syntax clause helpers--------------------------------------------------------------------------- TODO: is the loss of identLetter a problem?-commandName :: String -> N3Parser ()--- commandName cmd = try (string cmd *> notFollowedBy identLetter *> whiteSpace)-commandName cmd = symbol cmd *> pure ()--restOfLine :: N3Parser String-restOfLine = manyTill (satisfy (const True)) eoln <* whiteSpace-  -br :: N3Parser a -> N3Parser a-br = between (symbol "(") (symbol ")")--nameList :: N3Parser [ScopedName]-nameList = br $ many n3SymLex-  -toList :: a -> [a]-toList = (:[])-           -nameOrList :: N3Parser [ScopedName]-nameOrList =-  (toList <$> n3SymLex)      -  <|> nameList-  -graphExpr :: N3Parser (SwishStateIO (Either String RDFGraph))-graphExpr =-        graphOnly-    <|>-        do  { f <- formulaExpr-            ; return $ liftM (liftM formExpr) f-            }--graphOnly :: N3Parser (SwishStateIO (Either String RDFGraph))-graphOnly =-        do  { isymbol "{"-            ; b <- newBlankNode-            ; g <- subgraph b-            ; isymbol "}"-            ; s <- stGet-            ; let gp = setNamespaces (prefixUris s) g-            ; return $ return (Right gp)-            }--graphList :: N3Parser [SwishStateIO (Either String RDFGraph)]-graphList = br (many graphExpr)--graphOrList :: N3Parser [SwishStateIO (Either String RDFGraph)]-graphOrList =-  (toList <$> graphExpr)-  <|> graphList--formulaExpr :: N3Parser (SwishStateIO (Either String RDFFormula))-formulaExpr = n3SymLex >>= namedGraph--namedGraph :: ScopedName -> N3Parser (SwishStateIO (Either String RDFFormula))-namedGraph n =-  (ssAddReturnFormula n <$> (setTo *> graphOnly))-  <|> return (ssGetFormula n)--formulaList :: N3Parser [SwishStateIO (Either String RDFFormula)]-formulaList = between (symbol "(") (symbol ")") (many formulaExpr)--varModifiers :: N3Parser [(ScopedName,[RDFLabel])]-varModifiers = symbol "|" *> varModList--varModList :: N3Parser [(ScopedName,[RDFLabel])]-varModList = -  br (sepBy varMod comma)-  <|> toList <$> lexeme varMod--varMod :: N3Parser (ScopedName,[RDFLabel])-varMod = (,) <$> n3SymLex <*> many (lexeme quickVariable)---------------------------------------------------------------------------  SwishState helper functions-----------------------------------------------------------------------------  The functions below operate in the SwishStateIO monad, and are used---  to assemble an executable version of the parsed script.---- | Return a message to the user. At present the message begins with '# '--- but this may be removed.----ssReport :: -  String  -- ^ message contents-  -> SwishStateIO ()--- ssReport msg = lift $ putStrLn $ "# " ++ msg-ssReport msg = modify $ setInfo $ "# " ++ msg--ssReportLabel :: -  String     -- ^ label for the message-  -> String  -- ^ message contents-  -> SwishStateIO ()-ssReportLabel lbl msg = ssReport $ lbl ++ ": " ++ msg--ssAddReturnFormula ::-    ScopedName -> SwishStateIO (Either String RDFGraph)-    -> SwishStateIO (Either String RDFFormula)-ssAddReturnFormula nam gf =-        do  { egr <- gf-            ; ssAddGraph nam [return egr]-            ; return $ liftM (Formula nam) egr-            }--ssAddGraph ::-    ScopedName -> [SwishStateIO (Either String RDFGraph)]-    -> SwishStateIO ()-ssAddGraph nam gf =-    let errmsg = "Graph/list not added: "++show nam++"; "-    in-        do  { esg <- sequence gf        -- [Either String RDFGraph]-            ; let egs = sequence esg    -- Either String [RDFGraph]-            ; let fgs = case egs of-                    Left  er -> setError  (errmsg++er)-                    Right gs -> modGraphs (mapReplaceOrAdd (NamedGraph nam gs))-            ; modify fgs-            }--ssGetGraph :: ScopedName -> SwishStateIO (Either String RDFGraph)-ssGetGraph nam = liftM head <$> ssGetList nam-  -ssGetFormula :: ScopedName -> SwishStateIO (Either String RDFFormula)-ssGetFormula nam = gets find-    where-        find st = case findFormula nam st of-            Nothing -> Left ("Formula not present: "++show nam)-            Just gr -> Right gr--ssGetList :: ScopedName -> SwishStateIO (Either String [RDFGraph])-ssGetList nam = gets find-    where-        find st = case findGraph nam st of-            Nothing  -> Left ("Graph or list not present: "++show nam)-            Just grs -> Right grs--ssRead :: ScopedName -> Maybe URI -> SwishStateIO ()-ssRead nam muri = ssAddGraph nam [ssReadGraph muri]--ssReadGraph :: Maybe URI -> SwishStateIO (Either String RDFGraph)-ssReadGraph muri = -  let gf inp = case inp of-        Left  es -> Left es-        Right is -> parseN3 is (fmap qnameFromURI muri)-        -  in gf `liftM` getResourceData muri--ssWriteList ::-    Maybe URI -> SwishStateIO (Either String [RDFGraph]) -> String-    -> SwishStateIO ()-ssWriteList muri gf comment = do-  esgs <- gf-  case esgs of-    Left  er   -> modify $ setError ("Cannot write list: "++er)-    Right []   -> putResourceData Nothing (B.fromLazyText (L.concat ["# ", L.pack comment, "\n+ Swish: Writing empty list"]))-    Right [gr] -> ssWriteGraph muri gr comment-    Right grs  -> mapM_ writegr (zip [(0::Int)..] grs)-      where-        writegr (n,gr) = ssWriteGraph (murin muri n) gr-                         ("["++show n++"] "++comment)-        murin Nothing    _ = Nothing-        murin (Just uri) n = -          let rp = reverse $ uriPath uri-              (rLastSet, rRest) = break (=='/') rp-              (before, after) = break (=='.') $ reverse rLastSet-              newPath = reverse rRest ++ "/" ++ before ++ show n ++ after-          in case rLastSet of-            "" -> error $ "Invalid URI (path ends in /): " ++ show uri-            _ -> Just $ uri { uriPath = newPath }-         -  --{--ssWrite ::-    Maybe String -> SwishStateIO (Either String RDFGraph) -> String-    -> SwishStateIO ()-ssWrite muri gf comment =-        do  { esg <- gf-            ; case esg of-                Left  er -> modify $ setError ("Cannot write graph: "++er)-                Right gr -> ssWriteGraph muri gr comment-            }--}--ssWriteGraph :: Maybe URI -> RDFGraph -> String -> SwishStateIO ()-ssWriteGraph muri gr comment =-    putResourceData muri (c `mappend` formatGraphAsBuilder gr)-    where-        c = B.fromLazyText $ L.concat ["# ", L.pack comment, "\n"]--ssMerge ::-    ScopedName -> [SwishStateIO (Either String RDFGraph)]-    -> SwishStateIO ()-ssMerge nam gfs =-    let errmsg = "Graph merge not defined: "++show nam++"; "-    in-        do  { ssReportLabel "Merge" (show nam)-            ; esg <- sequence gfs       -- [Either String RDFGraph]-            ; let egs = sequence esg    -- Either String [RDFGraph]-            ; let fgs = case egs of-                    Left  er -> setError  (errmsg++er)-                    Right [] -> setError  (errmsg++"No graphs to merge")-                    Right gs -> modGraphs (mapReplaceOrAdd (NamedGraph nam [g]))-                            where g = foldl1 merge gs-            ; modify fgs-            }--ssCompare :: ScopedName -> ScopedName -> SwishStateIO ()-ssCompare n1 n2 =-        do  { ssReportLabel "Compare" (show n1 ++ " " ++ show n2)-            ; g1 <- ssGetGraph n1-            ; g2 <- ssGetGraph n2-            ; when (g1 /= g2) (modify $ setStatus SwishGraphCompareError)-            }--ssAssertEq :: ScopedName -> ScopedName -> String -> SwishStateIO ()-ssAssertEq n1 n2 comment =-    let er1 = ":\n  Graph or list compare not performed:  invalid graph/list."-    in-        do  { ssReportLabel "AssertEq" comment-            ; g1 <- ssGetList n1-            ; g2 <- ssGetList n2-            ; case (g1,g2) of-                (Left er,_) -> modify $ setError (comment++er1++"\n  "++er)-                (_,Left er) -> modify $ setError (comment++er1++"\n  "++er)-                (Right gr1,Right gr2) -> -                    unless (equiv gr1 gr2) $ modify $-                      setError (comment++":\n  Graph "++show n1-                                ++" differs from "++show n2++".")-            }--ssAssertIn :: ScopedName -> ScopedName -> String -> SwishStateIO ()-ssAssertIn n1 n2 comment =-    let er1 = ":\n  Membership test not performed:  invalid graph."-        er2 = ":\n  Membership test not performed:  invalid list."-    in-        do  { ssReportLabel "AssertIn" comment-            ; g1 <- ssGetGraph n1-            ; g2 <- ssGetList  n2-            ; case (g1,g2) of-                (Left er,_) -> modify $ setError (comment++er1++"\n  "++er)-                (_,Left er) -> modify $ setError (comment++er2++"\n  "++er)-                (Right gr,Right gs) ->-                    unless (gr `elem` gs) $ modify $-                    setError (comment++":\n  Graph "++show n1-                              ++" not a member of "++show n2)-            }----  Note:  this is probably incomplete, though it should work in simple cases.---  A complete solution would have the binding modifiers subject to---  re-arrangement to suit the actual bound variables encountered.---  See VarBinding.findCompositions and VarBinding.findComposition------  This code should be adequate if variable bindings are always used---  in combinations consisting of a single modifier followed by any number---  of filters.----ssDefineRule ::-    ScopedName-    -> [SwishStateIO (Either String RDFGraph)]-    -> SwishStateIO (Either String RDFGraph)-    -> [(ScopedName,[RDFLabel])]-    -> SwishStateIO ()-ssDefineRule rn agfs cgf vmds =-    let errmsg1 = "Rule definition error in antecedent graph(s): "-        errmsg2 = "Rule definition error in consequent graph: "-        errmsg3 = "Rule definition error in variable modifier(s): "-        errmsg4 = "Incompatible variable binding modifier sequence"-    in-        do  { aesg <- sequence agfs     -- [Either String RDFGraph]-            ; let ags = sequence aesg   :: Either String [RDFGraph]-            ; cg <- cgf                 -- Either String RDFGraph-            ; let vmfs = map ssFindVarModify vmds-            ; evms <- sequence vmfs     -- [Either String RDFVarBindingModify]-            ; let vms = sequence evms   :: Either String [RDFVarBindingModify]-            ; let frl = case (ags,cg,vms) of-                    (Left er,_,_) -> setError (errmsg1++er)-                    (_,Left er,_) -> setError (errmsg2++er)-                    (_,_,Left er) -> setError (errmsg3++er)-                    (Right agrs,Right cgr,Right vbms) ->-                        let-                            newRule = makeRDFClosureRule rn agrs cgr-                        in-                        case composeSequence vbms of-                            Just vm -> modRules (mapReplaceOrAdd (newRule vm))-                            Nothing -> setError errmsg4-            ; modify frl-            }--ssFindVarModify ::-    (ScopedName,[RDFLabel]) -> SwishStateIO (Either String RDFVarBindingModify)-ssFindVarModify (nam,lbs) = gets $ \st ->-  case findOpenVarModify nam st of-    Just ovbm -> Right (ovbm lbs)-    Nothing   -> Left  ("Undefined modifier: "++show nam)--ssDefineRuleset ::-    ScopedName-    -> [ScopedName]-    -> [ScopedName]-    -> SwishStateIO ()-ssDefineRuleset sn ans rns =-    let errmsg1 = "Error in ruleset axiom(s): "-        errmsg2 = "Error in ruleset rule(s): "-    in-        do  { let agfs = mapM ssGetFormula ans-                                        :: SwishStateIO [Either String RDFFormula]-            ; aesg <- agfs              -- [Either String RDFFormula]-            ; let eags = sequence aesg  :: Either String [RDFFormula]-            ; let erlf = mapM ssFindRule rns-                                        :: SwishStateIO [Either String RDFRule]-            ; rles <- erlf              -- [Either String RDFRule]-            ; let erls = sequence rles  :: Either String [RDFRule]-            ; let frs = case (eags,erls) of-                    (Left er,_) -> setError (errmsg1++er)-                    (_,Left er) -> setError (errmsg2++er)-                    (Right ags,Right rls) ->-                        modRulesets (mapReplaceOrAdd rs)-                        where-                            rs = makeRuleset (getScopeNamespace sn) ags rls-            ; modify frs-            }--ssFindRule :: ScopedName -> SwishStateIO (Either String RDFRule)-ssFindRule nam = gets find-    where-        find st = case findRule nam st of-            Nothing -> Left ("Rule not found: "++show nam)-            Just rl -> Right rl--ssDefineConstraints  ::-    ScopedName-    -> [SwishStateIO (Either String RDFGraph)]-    -> [ScopedName]-    -> SwishStateIO ()-ssDefineConstraints  sn cgfs dtns =-    let errmsg1 = "Error in constraint graph(s): "-        errmsg2 = "Error in datatype(s): "-    in-        do  { cges <- sequence cgfs     -- [Either String RDFGraph]-            ; let ecgs = sequence cges  :: Either String [RDFGraph]-            ; let ecgr = case ecgs of-                    Left er   -> Left er-                    Right []  -> Right emptyRDFGraph-                    Right grs -> Right $ foldl1 merge grs-            ; edtf <- mapM ssFindDatatype dtns-                                        -- [Either String RDFDatatype]-            ; let edts = sequence edtf   :: Either String [RDFDatatype]-            ; let frs = case (ecgr,edts) of-                    (Left er,_) -> setError (errmsg1++er)-                    (_,Left er) -> setError (errmsg2++er)-                    (Right cgr,Right dts) ->-                        modRulesets (mapReplaceOrAdd rs)-                        where-                            rs  = makeRuleset (getScopeNamespace sn) [] rls-                            rls = concatMap (`typeMkRules` cgr) dts-            ; modify frs-            }--ssFindDatatype :: ScopedName -> SwishStateIO (Either String RDFDatatype)-ssFindDatatype nam = gets find-    where-        find st = case findDatatype nam st of-            Nothing -> Left ("Datatype not found: "++show nam)-            Just dt -> Right dt---ssCheckProof ::-    ScopedName                                      -- proof name-    -> [ScopedName]                                 -- ruleset names-    -> SwishStateIO (Either String RDFFormula)      -- input formula-    -> [Either String [RDFRuleset]                  -- proof step from rulesets-        -> SwishStateIO (Either String RDFProofStep)]-    -> SwishStateIO (Either String RDFFormula)      -- result formula-    -> SwishStateIO ()-ssCheckProof pn sns igf stfs rgf =-    let-        infmsg1 = "Proof satisfied: "-        errmsg1 = "Error in proof ruleset(s): "-        errmsg2 = "Error in proof input: "-        errmsg3 = "Error in proof step(s): "-        errmsg4 = "Error in proof goal: "-        errmsg5 = "Proof not satisfied: "-        proofname = " (Proof "++show pn++")"-    in-        do  { let rs1 = map ssFindRuleset sns       :: [SwishStateIO (Either String RDFRuleset)]-            ; rs2 <- sequence rs1                   -- [Either String RDFRuleset]-            ; let erss = sequence rs2               :: Either String [RDFRuleset]-            ; eig <- igf                            -- Either String RDFFormula-            ; let st1  = sequence $ flist stfs erss :: SwishStateIO [Either String RDFProofStep]-            ; st2 <- st1                            -- [Either String RDFProofStep]-            ; let ests = sequence st2               :: Either String [RDFProofStep]-            ; erg  <- rgf                           -- Either String RDFFormula-            ; let proof = case (erss,eig,ests,erg) of-                    (Left er,_,_,_) -> Left (errmsg1++er++proofname)-                    (_,Left er,_,_) -> Left (errmsg2++er++proofname)-                    (_,_,Left er,_) -> Left (errmsg3++er++proofname)-                    (_,_,_,Left er) -> Left (errmsg4++er++proofname)-                    (Right rss, Right ig, Right sts, Right rg) ->-                        Right (makeRDFProof rss ig rg sts)-            ; when False $ case proof of-                    (Left  _)  -> return ()-                    (Right pr) -> putResourceData Nothing $-                                    B.fromLazyText (L.concat ["Proof ", L.pack (show pn), "\n"])-                                    `mappend`-                                    B.fromString (showsProof "\n" pr "\n")-                                    -- TODO: clean up-            ; let checkproof = case proof of-                    (Left  er) -> setError er-                    (Right pr) ->-                        case explainProof pr of-                            Nothing -> setInfo (infmsg1++show pn)-                            Just ex -> setError (errmsg5++show pn++", "++ex)-                        {--                        if not $ checkProof pr then-                            setError (errmsg5++show pn)-                        else-                            setInfo (infmsg1++show pn)-                        -}-            ; modify checkproof-            }--ssCheckStep ::-    ScopedName                                      -- rule name-    -> [SwishStateIO (Either String RDFFormula)]    -- antecedent graph formulae-    -> SwishStateIO (Either String RDFFormula)      -- consequent graph formula-    -> Either String [RDFRuleset]                   -- rulesets-    -> SwishStateIO (Either String RDFProofStep)    -- resulting proof step-ssCheckStep _  _    _    (Left  er)  = return $ Left er-ssCheckStep rn eagf ecgf (Right rss) =-    let-        errmsg1 = "Rule not in proof step ruleset(s): "-        errmsg2 = "Error in proof step antecedent graph(s): "-        errmsg3 = "Error in proof step consequent graph: "-    in-        do  { let mrul = getMaybeContextRule rn rss :: Maybe RDFRule-            ; esag <- sequence eagf                 -- [Either String RDFFormula]]-            ; let eags = sequence esag              :: Either String [RDFFormula]-            ; ecg  <- ecgf                          -- Either String RDFFormula-            ; let est = case (mrul,eags,ecg) of-                    (Nothing,_,_) -> Left (errmsg1++show rn)-                    (_,Left er,_) -> Left (errmsg2++er)-                    (_,_,Left er) -> Left (errmsg3++er)-                    (Just rul,Right ags,Right cg) ->-                        Right $ makeRDFProofStep rul ags cg-            ; return est-            }--ssFwdChain ::-    ScopedName                                      -- ruleset name-    -> ScopedName                                   -- rule name-    -> [SwishStateIO (Either String RDFGraph)]      -- antecedent graphs-    -> ScopedName                                   -- consequent graph name-    -> NamespaceMap                                 -- prefixes for new graph-    -> SwishStateIO ()-ssFwdChain sn rn agfs cn prefs =-    let-        errmsg1 = "FwdChain rule error: "-        errmsg2 = "FwdChain antecedent error: "-    in-        do  { erl  <- ssFindRulesetRule sn rn-            ; aesg <- sequence agfs     -- [Either String RDFGraph]-            ; let eags = sequence aesg   :: Either String [RDFGraph]-            ; let fcr = case (erl,eags) of-                    (Left er,_) -> setError (errmsg1++er)-                    (_,Left er) -> setError (errmsg2++er)-                    (Right rl,Right ags) ->-                        modGraphs (mapReplaceOrAdd (NamedGraph cn [cg]))-                        where-                            cg = case fwdApply rl ags of-                                []  -> emptyRDFGraph-                                grs -> setNamespaces prefs $ foldl1 add grs-            ; modify fcr-            }--ssFindRulesetRule ::-    ScopedName -> ScopedName -> SwishStateIO (Either String RDFRule)-ssFindRulesetRule sn rn = gets find-    where-        find st = case findRuleset sn st of-            Nothing -> Left ("Ruleset not found: "++show sn)-            Just rs -> find1 rs-        find1 rs = case getRulesetRule rn rs of-            Nothing -> Left ("Rule not in ruleset: "++show sn++": "++show rn)-            Just rl -> Right rl--ssFindRuleset ::-    ScopedName -> SwishStateIO (Either String RDFRuleset)-ssFindRuleset sn = gets find-    where-        find st = case findRuleset sn st of-            Nothing -> Left ("Ruleset not found: "++show sn)-            Just rs -> Right rs--ssBwdChain ::-    ScopedName                                      -- ruleset name-    -> ScopedName                                   -- rule name-    -> SwishStateIO (Either String RDFGraph)        -- consequent graphs-    -> ScopedName                                   -- antecedent alts name-    -> NamespaceMap                                 -- prefixes for new graphs-    -> SwishStateIO ()-ssBwdChain sn rn cgf an prefs =-    let-        errmsg1 = "BwdChain rule error: "-        errmsg2 = "BwdChain goal error: "-    in-        do  { erl <- ssFindRulesetRule sn rn-            ; ecg <- cgf                -- Either String RDFGraph-            ; let fcr = case (erl,ecg) of-                    (Left er,_) -> setError (errmsg1++er)-                    (_,Left er) -> setError (errmsg2++er)-                    (Right rl,Right cg) ->-                        modGraphs (mapReplaceOrAdd (NamedGraph an ags))-                        where-                            ags  = map mergegr (bwdApply rl cg)-                            mergegr grs = case grs of-                                [] -> emptyRDFGraph-                                _  -> setNamespaces prefs $ foldl1 add grs-            ; modify fcr-            }----  Temporary implementation:  just read local file WNH     ---  (Add logic to separate filenames from URIs, and---  attempt HTTP GET, or similar.)-getResourceData :: Maybe URI -> SwishStateIO (Either String L.Text)-getResourceData muri =-    case muri of-        Nothing  -> fromStdin-        Just uri -> fromUri uri-    where-    fromStdin =-        do  { dat <- lift LIO.getContents-            ; return $ Right dat-            }-    fromUri = fromFile-    fromFile uri | uriScheme uri == "file:" = Right `fmap` lift (LIO.readFile $ uriPath uri)-                 | otherwise = error $ "Unsupported file name for read: " ++ show uri-                               ---  Temporary implementation:  just write local file---  (Need to add logic to separate filenames from URIs, and---  attempt HTTP PUT, or similar.)-putResourceData :: Maybe URI -> B.Builder -> SwishStateIO ()-putResourceData muri gsh =-    do  { ios <- lift $ IO.try $-            case muri of-                Nothing  -> toStdout-                Just uri -> toUri uri-        ; case ios of-            Left  ioe -> modify $ setError-                            ("Error writing graph: "++-                             IO.ioeGetErrorString ioe)-            Right a   -> return a-        }-    where-        toStdout  = LIO.putStrLn gstr-        toUri uri | uriScheme uri == "file:" = LIO.writeFile (uriPath uri) gstr-                  | otherwise                = error $ "Unsupported file name for write: " ++ show uri-        gstr = B.toLazyText gsh--{- $syntax--The script syntax is based loosely on Notation3, and the script parser is an-extension of the Notation3 parser in the module "Swish.RDF.N3Parser".-The comment character is @#@ and white space is ignored.--> script            := command *-> command           := prefixLine        |->                      nameItem          |->                      readGraph         |->                      writeGraph        |->                      mergeGraphs       |->                      compareGraphs     |->                      assertEquiv       |->                      assertMember      |->                      defineRule        |->                      defineRuleset     |->                      defineConstraints |->                      checkProofCmd     |->                      fwdChain          |->                      bwdChain ---}--{- $prefixLine--> prefixLine        := @prefix [<prefix>]: <uri> .--Define a namespace prefix and URI. --The prefix thus defined is available for use in any subsequent script-command, and also in any graphs contained within the script file. (So,-prefix declarations do not need to be repeated for each graph-contained within the script.)--Graphs read from external files must contain their own prefix-declarations.--Example:--  > @prefix gex: <http://example1.com/graphs/>.-  > @prefix :    <http://example2.com/id/>.---}--{- $nameItem--> nameItem          := name :- graph     |->                      name :- ( graph* )--Graphs or lists of graphs can be given a name for use in other-statements.  A name is a qname (prefix:local) or a URI enclosed in-angle--Example:--> @prefix ex1: <http://example.com/graphs/> .-> @prefix ex2: <http://example.com/statements/> .->-> ex1:gr1 :- { ->     ex2:foo a ex2:Foo .->     ex2:bar a ex2:Bar .->     ex2:Bar rdfs:subClassOf ex2:Foo .-> }---}--{- $readGraph--> readGraph         := @read name [<uri>]--The @\@read@ command reads in the contents of the given URI-- which at present only supports reading local files, so-no HTTP access - and stores it under the given name.--If no URI is given then the file is read from standard input.--Example:--  > @prefix ex: <http://example.com/> .-  > @read ex:foo <foo.n3>---}--{- $writeGraph--> writeGraph        := @write name [<uri>] ; comment--The @\@write@ command writes out the contents of the given graph-- which at present only supports writing local files, so-no HTTP access. The comment text is written as a comment line-preceeding the graph contents.--If no URI is given then the file is written to the standard output.--Example:--  > @prefix ex: <http://example.com/> .-  > @read ex:gr1 <graph1.n3>-  > @read ex:gr2 <graph2.n3>-  > @merge (ex:gr1 ex:gr2) => ex:gr3-  > @write ex:gr3 ; the merged data-  > @write ex:gr3 <merged.n3> ; merge of graph1.n3 and graph2.n3---}--{- $mergeGraphs--> mergeGraphs       := @merge ( name* ) => name--Create a new named graph that is the merge two or more graphs,-renaming bnodes as required to avoid node-merging.--When the merge command is run, the message--  > # Merge: <output graph name>--will be created on the standard output channel.--Example:--  > @prefix gex: <http://example.com/graph/>.-  > @prefix ex: <http://example.com/statements/>.-  > gex:gr1 :- { ex:foo ex:bar _:b1 . }-  > gex:gr2 :- { _:b1 ex:foobar 23. }-  > @merge (gex:gr1 gex:gr2) => gex:gr3-  > @write gex:gr3 ; merged graphs--When run in Swish, this creates the following output (along with-several other namespace declarations):-- > # merged graphs- > @prefix ex: <http://example.com/statements/> .- > ex:foo ex:bar [] .- > [- >  ex:foobar "23"^^xsd:integer- > ] .---}--{- $compareGraphs--> compareGraphs     := @compare name name--Compare two graphs for isomorphism, setting the Swish exit status to-reflect the result.--When the compare command is run, the message--  > # Compare: <graph1> <graph2>--will be created on the standard output channel.--Example:--  > @prefix gex: <http://example.com/graphs/>.-  > @read gex:gr1 <graph1.n3>-  > @read gex:gr2 <graph2.n3>-  > @compare gex:gr1 gex:gr2---}--{- $assertEquiv--> assertEquiv       := @asserteq name name ; comment--Test two graphs or lists of graphs for isomorphism, reporting if they-differ. The comment text is included with any report generated.--When the command is run, the message--  > # AssertEq: <comment>--will be created on the standard output channel.--Example:--  > @prefix ex:  <http://id.ninebynine.org/wip/2003/swishtest/> .-  >-  > # Set up the graphs for the rules-  > ex:Rule01Ant :- { ?p ex:son ?o . }-  > ex:Rule01Con :- { ?o a ex:Male ; ex:parent ?p . }-  >-  > # Create a rule and a ruleset-  > @rule ex:Rule01 :- ( ex:Rule01Ant ) => ex:Rule01Con-  > @ruleset ex:rules :- (ex:TomSonDick ex:TomSonHarry) ; (ex:Rule01)-  >-  > # Apply the rule-  > @fwdchain ex:rules ex:Rule01 { :Tom ex:son :Charles . } => ex:Rule01fwd-  >-  > # Compare the results to the expected value-  > ex:ExpectedRule01fwd :- { :Charles a ex:Male ; ex:parent :Tom . }  -  > @asserteq ex:Rule01fwd ex:ExpectedRule01fwd-  >    ; Infer that Charles is male and has parent Tom---}--{- $assertMember--> assertMember      := @assertin name name ; comment--Test if a graph is isomorphic to a member of a list of graphs,-reporting if no match is found. The comment text is included with any-report generated.--Example:--> @bwdchain pv:rules :PassengerVehicle ex:Test01Inp <= :t1b-> -> @assertin ex:Test01Bwd0 :t1b ; Backward chain component test (0)-> @assertin ex:Test01Bwd1 :t1b ; Backward chain component test (1)---}--{- $defineRule--> defineRule        := @rule name :- ( name* ) => name-> defineRule        := @rule name :- ( name* ) => name->                       | ( (name var*)* )--Define a named Horn-style rule. --The list of names preceding and following @=>@ are the antecedent and consequent-graphs, respectivelu. Both sets may contain variable nodes of the form -@?var@.--The optional part, after the @|@ separator, is a list of variable-binding modifiers, each of which consists of a name and a list of-variables (@?var@) to which the modifier is applied. Variable binding-modifiers are built in to Swish, and are used to incorporate datatype-value inferences into a rule.  ---}--{- $defineRuleset--> defineRuleset     := @ruleset name :- ( name* ) ; ( name* ) --Define a named ruleset (a collection of axioms and rules). The first-list of names are the axioms that are part of the ruleset, and the-second list are the rules.---}--{- $defineConstraints--> defineConstraints := @constraints pref :- ( name* ) | [ name | ( name* ) ]--Define a named ruleset containing class-restriction rules based on a-datatype value constraint. The first list of-names is a list of graphs that together comprise the class-restriction-definitions (rule names are the names of the corresponding restriction-classes). The second list of names is a list of datatypes whose-datatype relations are referenced by the class restriction-definitions.---}--{- $fwdChain--> fwdChain          := @fwdchain pref name ( name* ) => name--Define a new graph obtained by forward-chaining a rule. The first name-is the ruleset to be used. The second name is the rule name. The list-of names are the antecedent graphs to which the rule is applied. The-name following the @=>@ names a new graph that is the result of formward-chaining from the given antecedents using the indicated rule.---}--{- $bwdChain--> bwdChain          := @bwdchain pref name graph <= name--Define a new list of alternative graphs obtained by backward-chaining-a rule. The first name is the ruleset to be used. The second name is-the rule name. The third name (before the @<=@) is the name of a goal graph-from which to backward chain. The final name (after the @<=@) names a new-list of graphs, each of which is an alternative antecedent from which-the given goal can be deduced using the indicated rule.----}--{- $proof--> checkProofCmd     := proofLine nl->                      inputLine nl->                      (stepLine nl)*->                      resultLine-> proofLine         := @proof name ( name* )--Check a proof, reporting the step that fails, if any.--The @\@proof@ line names the proof and specifies a list rulesets-(proof context) used.  The remaining lines specify the input-expression (@\@input@), proof steps (@\@step@) and the final result-(@\@result@) that is demonstrated by the proof.--> inputLine         := @input name--In a proof, indicates an input expression upon which the proof is-based. Exactly one of these immediately follows the @\@proof@ command.--> stepLine          := @step name ( name* ) => name--This defines a step of the proof; any number of these immediately-follow the @\@input@ command.--It indicates the name of the rule applied for this step, a list of-antecedent graphs, and a named graph that is deduced by this step.-For convenience, the deduced graph may introduce a new named graph-using an expression of the form:--  > name :- { statements }--> resultLine        := @result name--This defines the goal of the proof, and completes a proof-definition. Exactly one of these immediately follows the @\@step@-commands.  For convenience, the result statement may introduce a new-named graph using an expression of the form:--  > name :- { statements }---}--{- $exampleScript--This is the example script taken from-<http://www.ninebynine.org/Software/swish-0.2.1.html#sec-script-example>-with the proof step adjusted so that it passes.--> # -- Example Swish script ---> #-> # Comment lines start with a '#'-> #-> # The script syntax is loosely based on Notation3, but it is a quite -> # different language, except that embedded graphs (enclosed in {...})-> # are encoded using Notation3 syntax.-> #-> # -- Prefix declarations ---> #-> # As well as being used for all labels defined and used by the script-> # itself, these are applied to all graph expressions within the script -> # file, and to graphs created by scripted inferences, -> # but are not applied to any graphs read in from an external source.-> -> @prefix ex:  <http://id.ninebynine.org/wip/2003/swishtest/> .-> @prefix pv:  <http://id.ninebynine.org/wip/2003/swishtest/pv/> .-> @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .-> @prefix xsd_integer: <http://id.ninebynine.org/2003/XMLSchema/integer#> .-> @prefix rs_rdf:  <http://id.ninebynine.org/2003/Ruleset/rdf#> .-> @prefix rs_rdfs: <http://id.ninebynine.org/2003/Ruleset/rdfs#> .-> @prefix :   <http://id.ninebynine.org/default/> .-> -> # Additionally, prefix declarations are provided automatically for:-> #    @prefix rdf:   <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .-> #    @prefix rdfs:  <file://www.w3.org/2000/01/rdf-schema#> .-> #    @prefix rdfd:  <http://id.ninebynine.org/2003/rdfext/rdfd#> .-> #    @prefix rdfo:  <http://id.ninebynine.org/2003/rdfext/rdfo#> .-> #    @prefix owl:   <http://www.w3.org/2002/07/owl#> .-> -> # -- Simple named graph declarations ---> -> ex:Rule01Ant :- { ?p ex:son ?o . }-> -> ex:Rule01Con :- { ?o a ex:Male ; ex:parent ?p . }-> -> ex:TomSonDick :- { :Tom ex:son :Dick . }-> ex:TomSonHarry :- { :Tom ex:son :Harry . }-> -> # -- Named rule definition ---> -> @rule ex:Rule01 :- ( ex:Rule01Ant ) => ex:Rule01Con-> -> # -- Named ruleset definition ---> #-> # A 'ruleset' is a collection of axioms and rules.-> #-> # Currently, the ruleset is identified using the namespace alone;-> # i.e. the 'rules' in 'ex:rules' below is not used.  -> # This is under review.-> -> @ruleset ex:rules :- (ex:TomSonDick ex:TomSonHarry) ; (ex:Rule01)-> -> # -- Forward application of rule ---> #-> # The rule is identified here by ruleset and a name within the ruleset.-> -> @fwdchain ex:rules ex:Rule01 { :Tom ex:son :Charles . } => ex:Rule01fwd-> -> # -- Compare graphs ---> #-> # Compare result of inference with expected result.-> # This is a graph isomorphism test rather than strict equality, -> # to allow for bnode renaming.-> # If the graphs are not equal, a message is generated, which-> # includes the comment (';' to end of line)-> -> ex:ExpectedRule01fwd :- { :Charles a ex:Male ; ex:parent :Tom . }  -> -> @asserteq ex:Rule01fwd ex:ExpectedRule01fwd->    ; Infer that Charles is male and has parent Tom-> -> # -- Display graph (to screen and a file) ---> #-> # The comment is included in the output.-> -> @write ex:Rule01fwd ; Charles is male and has parent Tom-> @write ex:Rule01fwd <Example1.n3> ; Charles is male and has parent Tom-> -> # -- Read graph from file ---> #-> # Creates a new named graph in the Swish environment.-> -> @read ex:Rule01inp <Example1.n3>-> -> # -- Proof check ---> #-> # This proof uses the built-in RDF and RDFS rulesets, -> # which are the RDF- and RDFS- entailment rules described in the RDF-> # formal semantics document.-> #-> # To prove:-> #     ex:foo ex:prop "a" .-> # RDFS-entails-> #     ex:foo ex:prop _:x .-> #     _:x rdf:type rdfs:Resource .-> #-> # If the proof is not valid according to the axioms and rules of the -> # ruleset(s) used and antecedents given, then an error is reported -> # indicating the failed proof step.-> -> ex:Input  :- { ex:foo ex:prop "a" . }-> ex:Result :- { ex:foo ex:prop _:a . _:a rdf:type rdfs:Resource . }-> -> @proof ex:Proof ( rs_rdf:rules rs_rdfs:rules )->   @input  ex:Input->   @step   rs_rdfs:r3 ( rs_rdfs:a10 rs_rdfs:a39 )->           => ex:Stepa :- { rdfs:Literal rdf:type rdfs:Class . }->   @step   rs_rdfs:r8 ( ex:Stepa )->           => ex:Stepb :- { rdfs:Literal rdfs:subClassOf rdfs:Resource . }->   @step   rs_rdf:lg ( ex:Input )->           => ex:Stepc :- { ex:foo ex:prop _:a . _:a rdf:_allocatedTo "a" . }->   @step   rs_rdfs:r1 ( ex:Stepc )->           => ex:Stepd :- { _:a rdf:type rdfs:Literal . }->   @step   rs_rdfs:r9 ( ex:Stepb ex:Stepd )->           => ex:Stepe :- { _:a rdf:type rdfs:Resource . }->   @step   rs_rdf:se  ( ex:Stepc ex:Stepd ex:Stepe )->           => ex:Result->   @result ex:Result-> -> # -- Restriction based datatype inferencing ---> #-> # Datatype inferencing based on a general class restriction and-> # a predefined relation (per idea noted by Pan and Horrocks).-> -> ex:VehicleRule :-->   { :PassengerVehicle a rdfd:GeneralRestriction ;->       rdfd:onProperties (:totalCapacity :seatedCapacity :standingCapacity) ;->       rdfd:constraint xsd_integer:sum ;->       rdfd:maxCardinality "1"^^xsd:nonNegativeInteger . }-> -> # Define a new ruleset based on a declaration of a constraint class-> # and reference to built-in datatype.-> # The datatype constraint xsd_integer:sum is part of the definition -> # of datatype xsd:integer that is cited in the constraint ruleset-> # declaration.  It relates named properties of a class instance.-> -> @constraints pv:rules :- ( ex:VehicleRule ) | xsd:integer-> -> # Input data for test cases:-> -> ex:Test01Inp :-->   { _:a1 a :PassengerVehicle ;->       :seatedCapacity "30"^^xsd:integer ;->       :standingCapacity "20"^^xsd:integer . }-> -> # Forward chaining test case:-> -> ex:Test01Fwd :- { _:a1 :totalCapacity "50"^^xsd:integer . }-> -> @fwdchain pv:rules :PassengerVehicle ex:Test01Inp => :t1f-> @asserteq :t1f ex:Test01Fwd  ; Forward chain test-> -> # Backward chaining test case:-> #-> # Note that the result of backward chaining is a list of alternatives,-> # any one of which is sufficient to derive the given conclusion.-> -> ex:Test01Bwd0 :-->   { _:a1 a :PassengerVehicle .->     _:a1 :totalCapacity "50"^^xsd:integer .->     _:a1 :seatedCapacity "30"^^xsd:integer . }-> -> ex:Test01Bwd1 :-->   { _:a1 a :PassengerVehicle .->     _:a1 :totalCapacity "50"^^xsd:integer .->     _:a1 :standingCapacity "20"^^xsd:integer . }-> -> # Declare list of graphs:-> -> ex:Test01Bwd :- ( ex:Test01Bwd0 ex:Test01Bwd1 )-> -> @bwdchain pv:rules :PassengerVehicle ex:Test01Inp <= :t1b-> @asserteq :t1b ex:Test01Bwd  ; Backward chain test-> -> # Can test for graph membership in a list-> -> @assertin ex:Test01Bwd0 :t1b ; Backward chain component test (0)-> @assertin ex:Test01Bwd1 :t1b ; Backward chain component test (1)-> -> # -- Merge graphs ---> #-> # Merging renames bnodes to avoid collisions.-> -> @merge ( ex:Test01Bwd0 ex:Test01Bwd1 ) => ex:Merged-> -> # This form of comparison sets the Swish exit status based on the result.-> -> ex:ExpectedMerged :-->   { _:a1 a :PassengerVehicle .->     _:a1 :totalCapacity "50"^^xsd:integer .->     _:a1 :seatedCapacity "30"^^xsd:integer .->     _:a2 a :PassengerVehicle .->     _:a2 :totalCapacity "50"^^xsd:integer .->     _:a2 :standingCapacity "20"^^xsd:integer . }-> -> @compare ex:Merged ex:ExpectedMerged-> -> # End of example script--If saved in the file example.ss, then it can be evaluated by saying-either of:--> % Swish -s=example.ss--or, from @ghci@:--> Prelude> :m + Swish.RDF.SwishMain-> Prelude Swish.RDF.SwishMain> :set prompt "Swish> "-> Swish> runSwish "-s=example.ss"--and the output is--> # AssertEq: Infer that Charles is male and has parent Tom-> # Charles is male and has parent Tom-> @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .-> @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .-> @prefix rdfd: <http://id.ninebynine.org/2003/rdfext/rdfd#> .-> @prefix owl: <http://www.w3.org/2002/07/owl#> .-> @prefix log: <http://www.w3.org/2000/10/swap/log#> .-> @prefix : <http://id.ninebynine.org/default/> .-> @prefix ex: <http://id.ninebynine.org/wip/2003/swishtest/> .-> @prefix pv: <http://id.ninebynine.org/wip/2003/swishtest/pv/> .-> @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .-> @prefix xsd_integer: <http://id.ninebynine.org/2003/XMLSchema/integer#> .-> @prefix rs_rdf: <http://id.ninebynine.org/2003/Ruleset/rdf#> .-> @prefix rs_rdfs: <http://id.ninebynine.org/2003/Ruleset/rdfs#> .-> :Charles ex:parent :Tom ;->          a ex:Male .-> -> Proof satisfied: ex:Proof-> # AssertEq: Forward chain test-> # AssertEq: Backward chain test-> # AssertIn: Backward chain component test (0)-> # AssertIn: Backward chain component test (1)-> # Merge: ex:Merged-> # Compare: ex:Merged ex:ExpectedMerged---}----------------------------------------------------------------------------------------  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke  ---  All rights reserved.------  This file is part of Swish.------  Swish is free software; you can redistribute it and/or modify---  it under the terms of the GNU General Public License as published by---  the Free Software Foundation; either version 2 of the License, or---  (at your option) any later version.------  Swish is distributed in the hope that it will be useful,---  but WITHOUT ANY WARRANTY; without even the implied warranty of---  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the---  GNU General Public License for more details.------  You should have received a copy of the GNU General Public License---  along with Swish; if not, write to:---    The Free Software Foundation, Inc.,---    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA------------------------------------------------------------------------------------
− src/Swish/RDF/TurtleFormatter.hs
@@ -1,879 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  TurtleFormatter---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  License     :  GPL V2------  Maintainer  :  Douglas Burke---  Stability   :  experimental---  Portability :  OverloadedStrings------  This Module implements a Turtle formatter ---  for an RDFGraph value. See---  <http://www.w3.org/TR/turtle/>---  \"Turtle, Terse RDF Triple Language\",---  W3C Working Draft 09 August 2011 (<http://www.w3.org/TR/2011/WD-turtle-20110809/>)--------------------------------------------------------------------------------------{--TODO:--The code used to determine whether a blank node can be written-using the "[]" short form could probably take advantage of the-GraphPartition module.---}--module Swish.RDF.TurtleFormatter-    ( NodeGenLookupMap-    , formatGraphAsText-    , formatGraphAsLazyText-    , formatGraphAsBuilder-    , formatGraphIndent  -    , formatGraphDiag-      -      -- * Auxillary routines-    , quoteText-    )-where--import Swish.RDF.RDFGraph (-  RDFGraph, RDFLabel(..)-  , NamespaceMap, RevNamespaceMap-  -- emptyNamespaceMap,-  -- FormulaMap, emptyFormulaMap,-  , getArcs-  , labels-  -- , setNamespaces-  , getNamespaces,-  -- getFormulae,-  emptyRDFGraph-  , quote-  , quoteT-  , resRdfFirst, resRdfRest, resRdfNil-  )--import Swish.RDF.Vocabulary (-  isLang-  , langTag -  , rdfType-  , rdfNil-  , xsdBoolean, xsdDecimal, xsdInteger, xsdDouble -  )--import Swish.RDF.GraphClass (Arc(..))--import Swish.Utils.LookupMap-    ( LookupEntryClass(..)-    , LookupMap, emptyLookupMap, reverseLookupMap-    , listLookupMap-    , mapFind, mapFindMaybe, mapAdd-    -- , mapDelete-    , mapMerge-    )--import Swish.Utils.Namespace (ScopedName, getScopeLocal, getScopeURI)--import Data.Char (isDigit)--import Data.List (foldl', delete, groupBy, partition, sort, intersperse)--import Data.Monoid (Monoid(..))-import Control.Monad (liftM, when)-import Control.Monad.State (State, modify, get, put, runState)---- it strikes me that using Lazy Text here is likely to be--- wrong; however I have done no profiling to back this--- assumption up!--import qualified Data.Text as T-import qualified Data.Text.Lazy as L-import qualified Data.Text.Lazy.Builder as B---- temporary conversion-quoteB :: Bool -> String -> B.Builder-quoteB f v = B.fromString $ quote f v---------------------------------------------------------------------------  Graph formatting state monad-----------------------------------------------------------------------------  The graph to be formatted is carried as part of the formatting---  state, so that decisions about what needs to be formatted can---  themselves be based upon and reflected in the state (e.g. if a---  decision is made to include a blank node inline, it can be removed---  from the graph state that remains to be formatted).--type SubjTree lb = [(lb,PredTree lb)]-type PredTree lb = [(lb,[lb])]--data TurtleFormatterState = TFS-    { indent    :: B.Builder-    , lineBreak :: Bool-    , graph     :: RDFGraph-    , subjs     :: SubjTree RDFLabel-    , props     :: PredTree RDFLabel   -- for last subject selected-    , objs      :: [RDFLabel]          -- for last property selected-    -- , formAvail :: FormulaMap RDFLabel-    -- , formQueue :: [(RDFLabel,RDFGraph)]-    , nodeGenSt :: NodeGenState-    , bNodesCheck   :: [RDFLabel]      -- these bNodes are not to be converted to '[..]' format-    , traceBuf  :: [String]-    }-             -type Formatter a = State TurtleFormatterState a--emptyTFS :: NodeGenState -> TurtleFormatterState-emptyTFS ngs = TFS-    { indent    = "\n"-    , lineBreak = False-    , graph     = emptyRDFGraph-    , subjs     = []-    , props     = []-    , objs      = []-    -- , formAvail = emptyFormulaMap-    -- , formQueue = []-    , nodeGenSt = ngs-    , bNodesCheck   = []-    , traceBuf  = []-    }----  | Node name generation state information that carries through---  and is updated by nested formulae-type NodeGenLookupMap = LookupMap (RDFLabel,Int)--data NodeGenState = Ngs-    { prefixes  :: NamespaceMap-    , nodeMap   :: NodeGenLookupMap-    , nodeGen   :: Int-    }--emptyNgs :: NodeGenState-emptyNgs = Ngs-    { prefixes  = emptyLookupMap-    , nodeMap   = emptyLookupMap-    , nodeGen   = 0-    }---- simple context for label creation--- (may be a temporary solution to the problem---  of label creation)----data LabelContext = SubjContext | PredContext | ObjContext-                    deriving (Eq, Show)--getIndent :: Formatter B.Builder-getIndent = indent `liftM` get--setIndent :: B.Builder -> Formatter ()-setIndent ind = modify $ \st -> st { indent = ind }--getLineBreak :: Formatter Bool-getLineBreak = lineBreak `liftM` get--setLineBreak :: Bool -> Formatter ()-setLineBreak brk = modify $ \st -> st { lineBreak = brk }--getNgs :: Formatter NodeGenState-getNgs = nodeGenSt `liftM` get--setNgs :: NodeGenState -> Formatter ()-setNgs ngs = modify $ \st -> st { nodeGenSt = ngs }--getPrefixes :: Formatter NamespaceMap-getPrefixes = prefixes `liftM` getNgs--getSubjs :: Formatter (SubjTree RDFLabel)-getSubjs = subjs `liftM` get--setSubjs :: SubjTree RDFLabel -> Formatter ()-setSubjs sl = modify $ \st -> st { subjs = sl }--getProps :: Formatter (PredTree RDFLabel)-getProps = props `liftM` get--setProps :: PredTree RDFLabel -> Formatter ()-setProps ps = modify $ \st -> st { props = ps }--{--getObjs :: Formatter ([RDFLabel])-getObjs = objs `liftM` get--setObjs :: [RDFLabel] -> Formatter ()-setObjs os = do-  st <- get-  put $ st { objs = os }--}--getBnodesCheck :: Formatter [RDFLabel]-getBnodesCheck = bNodesCheck `liftM` get--{--addTrace :: String -> Formatter ()-addTrace tr = do-  st <- get-  put $ st { traceBuf = tr : traceBuf st }--}-  -{--queueFormula :: RDFLabel -> Formatter ()-queueFormula fn = do-  st <- get-  let fa = formAvail st-      newState fv = st {-                      formAvail = mapDelete fa fn,-                      formQueue = (fn,fv) : formQueue st-                    }-  case mapFindMaybe fn fa of-    Nothing -> return ()-    Just v -> put (newState v) >> return ()--}--{--Return the graph associated with the label and delete it-from the store, if there is an association, otherwise-return Nothing.--extractFormula :: RDFLabel -> Formatter (Maybe RDFGraph)-extractFormula fn = do-  st <- get-  let fa = formAvail st-      newState = st { formAvail=mapDelete fa fn }-  case mapFindMaybe fn fa of-    Nothing -> return Nothing-    Just fv -> put newState >> return (Just fv)---}--{--moreFormulae :: Formatter Bool-moreFormulae =  do-  st <- get-  return $ not $ null (formQueue st)--nextFormula :: Formatter (RDFLabel,RDFGraph)-nextFormula = do-  st <- get-  let (nf : fq) = formQueue st-  put $ st { formQueue = fq }-  return nf---}---- list has a length of 1-len1 :: [a] -> Bool-len1 (_:[]) = True-len1 _ = False--{-|-Given a set of statements and a label, return the details of the-RDF collection referred to by label, or Nothing.--For label to be considered as representing a collection we require the-following conditions to hold (this is only to support the-serialisation using the '(..)' syntax and does not make any statement-about semantics of the statements with regard to RDF Collections):--  - there must be one rdf_first and one rdfRest statement-  - there must be no other predicates for the label---} -getCollection ::          -  SubjTree RDFLabel -- ^ statements organized by subject-  -> RDFLabel -- ^ does this label represent a list?-  -> Maybe (SubjTree RDFLabel, [RDFLabel], [RDFLabel])-     -- ^ the statements with the elements removed; the-     -- content elements of the collection (the objects of the rdf:first-     -- predicate) and the nodes that represent the spine of the-     -- collection (in reverse order, unlike the actual contents which are in-     -- order).-getCollection subjList lbl = go subjList lbl ([],[]) -    where-      go sl l (cs,ss) | l == resRdfNil = Just (sl, reverse cs, ss)-                      | otherwise = do-        (pList1, sl') <- removeItem sl l-        (pFirst, pList2) <- removeItem pList1 resRdfFirst-        (pNext, pList3) <- removeItem pList2 resRdfRest--        -- QUS: could I include these checks implicitly in the pattern matches above?-        -- ie instrad of (pFirst, pos1) <- ..-        -- have ([content], pos1) <- ...-        -- ?-        if and [len1 pFirst, len1 pNext, null pList3]-          then go sl' (head pNext) (head pFirst : cs, l : ss)-          else Nothing--{--TODO:--Should we change the preds/objs entries as well?---}-extractList :: LabelContext -> RDFLabel -> Formatter (Maybe [RDFLabel])-extractList lctxt ln = do-  osubjs <- getSubjs-  oprops <- getProps-  let mlst = getCollection osubjs' ln--      -- we only want to send in rdf:first/rdf:rest here-      fprops = filter ((`elem` [resRdfFirst, resRdfRest]) . fst) oprops--      osubjs' =-          case lctxt of-            SubjContext -> (ln, fprops) : osubjs-            _ -> osubjs --      -- tr = "extractList " ++ show ln ++ " (" ++ show lctxt ++ ")\n -> osubjs= " ++ show osubjs ++ "\n -> opreds= " ++ show oprops ++ "\n -> mlst= " ++ show mlst ++ "\n"-  -- addTrace tr-  case mlst of-    -- sl is guaranteed to be free of (ln,fprops) here if lctxt is SubjContext-    Just (sl,ls,_) -> do-              setSubjs sl-              when (lctxt == SubjContext) $ setProps $ filter ((`notElem` [resRdfFirst, resRdfRest]) . fst) oprops-              return (Just ls)--    Nothing -> return Nothing-  -{-|-Removes the first occurrence of the item from the-association list, returning it's contents and the rest-of the list, if it exists.--}-removeItem :: (Eq a) => [(a,b)] -> a -> Maybe (b, [(a,b)])-removeItem os x =-  let (as, bs) = break (\a -> fst a == x) os-  in case bs of-    ((_,b):bbs) -> Just (b, as ++ bbs)-    [] -> Nothing---------------------------------------------------------------------------  Define a top-level formatter function:-------------------------------------------------------------------------formatGraphAsText :: RDFGraph -> T.Text-formatGraphAsText = L.toStrict . formatGraphAsLazyText--formatGraphAsLazyText :: RDFGraph -> L.Text-formatGraphAsLazyText = B.toLazyText . formatGraphAsBuilder-  -formatGraphAsBuilder :: RDFGraph -> B.Builder-formatGraphAsBuilder = formatGraphIndent "\n" True-  -formatGraphIndent :: B.Builder -> Bool -> RDFGraph -> B.Builder-formatGraphIndent indnt flag gr = -  let (res, _, _, _) = formatGraphDiag indnt flag gr-  in res-  --- | Format graph and return additional information-formatGraphDiag :: -  B.Builder  -- ^ indentation-  -> Bool    -- ^ are prefixes to be generated?-  -> RDFGraph -  -> (B.Builder, NodeGenLookupMap, Int, [String])-formatGraphDiag indnt flag gr = -  let fg  = formatGraph indnt " .\n" False flag gr-      ngs = emptyNgs {-        prefixes = emptyLookupMap,-        nodeGen  = findMaxBnode gr-        }-             -      (out, fgs) = runState fg (emptyTFS ngs)-      ogs        = nodeGenSt fgs-  -  in (out, nodeMap ogs, nodeGen ogs, traceBuf fgs)---------------------------------------------------------------------------  Formatting as a monad-based computation-------------------------------------------------------------------------formatGraph :: -  B.Builder     -- indentation string-  -> B.Builder  -- text to be placed after final statement-  -> Bool       -- True if a line break is to be inserted at the start-  -> Bool       -- True if prefix strings are to be generated-  -> RDFGraph   -- graph to convert-  -> Formatter B.Builder-formatGraph ind end dobreak dopref gr = do-  setIndent ind-  setLineBreak dobreak-  setGraph gr-  -  fp <- if dopref-        then formatPrefixes (getNamespaces gr)-        else return mempty-  more <- moreSubjects-  if more-    then do-      fr <- formatSubjects-      return $ mconcat [fp, fr, end]-    else return fp--formatPrefixes :: NamespaceMap -> Formatter B.Builder-formatPrefixes pmap = do-  let mls = map (pref . keyVal) (listLookupMap pmap)-  ls <- sequence mls-  return $ mconcat ls-    where-      pref (Just p,u) = nextLine $ mconcat ["@prefix ", B.fromText p, ": <", quoteB True (show u), "> ."]-      pref (_,u)      = nextLine $ mconcat ["@prefix : <", quoteB True (show u), "> ."]--{--NOTE:-I expect there to be confusion below where I need to-convert from Text to Builder--}--formatSubjects :: Formatter B.Builder-formatSubjects = do-  sb    <- nextSubject-  sbstr <- formatLabel SubjContext sb-  -  flagP <- moreProperties-  if flagP-    then do-      prstr <- formatProperties sb sbstr-      flagS <- moreSubjects-      if flagS-        then do-          fr <- formatSubjects-          return $ mconcat [prstr, " .", fr]-        else return prstr-           -    else do-      txt <- nextLine sbstr-    -      flagS <- moreSubjects-      if flagS-        then do-          fr <- formatSubjects-          return $ mconcat [txt, " .", fr]-        else return txt--{--TODO: now we are throwing a Builder around it is awkward to-get the length of the text to calculate the indentation--So--  a) change the indentation scheme-  b) pass around text instead of builder--mkIndent :: L.Text -> L.Text-mkIndent inVal = L.replicate (L.length inVal) " "--}--hackIndent :: B.Builder-hackIndent = "    "--formatProperties :: RDFLabel -> B.Builder -> Formatter B.Builder-formatProperties sb sbstr = do-  pr <- nextProperty sb-  prstr <- formatLabel PredContext pr-  obstr <- formatObjects sb pr $ mconcat [sbstr, " ", prstr]-  more  <- moreProperties-  let sbindent = hackIndent -- mkIndent sbstr-  if more-    then do-      fr <- formatProperties sb sbindent-      nl <- nextLine $ obstr `mappend` " ;"-      return $ nl `mappend` fr-    else nextLine obstr--formatObjects :: RDFLabel -> RDFLabel -> B.Builder -> Formatter B.Builder-formatObjects sb pr prstr = do-  ob    <- nextObject sb pr-  obstr <- formatLabel ObjContext ob-  more  <- moreObjects-  if more-    then do-      let prindent = hackIndent -- mkIndent prstr-      fr <- formatObjects sb pr prindent-      nl <- nextLine $ mconcat [prstr, " ", obstr, ","]-      return $ nl `mappend` fr-    else return $ mconcat [prstr, " ", obstr]--{--Add a list inline. We are given the labels that constitute-the list, in order, so just need to display them surrounded-by ().--}-insertList :: [RDFLabel] -> Formatter B.Builder-insertList [] = return "()" -- not convinced this can happen-insertList xs = do-  ls <- mapM (formatLabel ObjContext) xs-  return $ mconcat ("( " : intersperse " " ls) `mappend` " )"-    -{--Add a blank node inline.--}--insertBnode :: LabelContext -> RDFLabel -> Formatter B.Builder-insertBnode SubjContext lbl = do-  -- a safety check-  flag <- moreProperties-  if flag-    then do-      txt <- (`mappend` "\n") `liftM` formatProperties lbl ""-      return $ mconcat ["[] ", txt]-    else error $ "Internal error: expected properties with label: " ++ show lbl--insertBnode _ lbl = do-  ost <- get-  let osubjs = subjs ost-      oprops = props ost-      oobjs  = objs  ost--      (bsubj, rsubjs) = partition ((== lbl) . fst) osubjs--      rprops = case bsubj of-                 [(_,rs)] -> rs-                 _ -> []--      -- we essentially want to create a new subgraph-      -- for this node but it's not as simple as that since-      -- we could have something like-      --     :a :b [ :foo [ :bar "xx" ] ]-      -- so we still need to carry around the whole graph-      ---      nst = ost { subjs = rsubjs,-                  props = rprops,-                  objs  = []-                }--  put nst-  flag <- moreProperties-  txt <- if flag-         then (`mappend` "\n") `liftM` formatProperties lbl ""-         else return ""--  -- TODO: how do we restore the original set up?-  --       I can't believe the following is sufficient-  ---  nst' <- get-  let slist  = map fst $ subjs nst'-      nsubjs = filter (\(l,_) -> l `elem` slist) osubjs--  put $ nst' { subjs = nsubjs,-                       props = oprops, -                       objs  = oobjs-             }--  -- TODO: handle indentation?-  return $ mconcat ["[", txt, "]"]-  --------------------------------------------------------------------------  Formatting helpers-------------------------------------------------------------------------setGraph :: RDFGraph -> Formatter ()-setGraph gr = do-  st <- get--  let ngs0 = nodeGenSt st-      pre' = mapMerge (prefixes ngs0) (getNamespaces gr)-      ngs' = ngs0 { prefixes = pre' }-      arcs = sortArcs $ getArcs gr-      nst  = st  { graph     = gr-                 , subjs     = arcTree arcs-                 , props     = []-                 , objs      = []-                 -- , formAvail = getFormulae gr-                 , nodeGenSt = ngs'-                 , bNodesCheck   = countBnodes arcs-                 }--  put nst--hasMore :: (TurtleFormatterState -> [b]) -> Formatter Bool-hasMore lens = (not . null . lens) `liftM` get--moreSubjects :: Formatter Bool-moreSubjects = hasMore subjs--- moreSubjects = (not . null . subjs) `liftM` get--moreProperties :: Formatter Bool-moreProperties = hasMore props--- moreProperties = (not . null . props) `liftM` get--moreObjects :: Formatter Bool-moreObjects = hasMore objs--- moreObjects = (not . null . objs) `liftM` get--nextSubject :: Formatter RDFLabel-nextSubject = do-  st <- get--  let sb:sbs = subjs st-      nst = st  { subjs = sbs-                , props = snd sb-                , objs  = []-                }--  put nst-  return $ fst sb--nextProperty :: RDFLabel -> Formatter RDFLabel-nextProperty _ = do-  st <- get--  let pr:prs = props st-      nst = st  { props = prs-                 , objs  = snd pr-                 }--  put nst-  return $ fst pr--nextObject :: RDFLabel -> RDFLabel -> Formatter RDFLabel-nextObject _ _ = do-  st <- get--  let ob:obs = objs st-      nst = st { objs = obs }--  put nst-  return ob--nextLine :: B.Builder -> Formatter B.Builder-nextLine str = do-  ind <- getIndent-  brk <- getLineBreak-  if brk-    then return $ ind `mappend` str-    else do-      --  After first line, always insert line break-      setLineBreak True-      return str----  Format a label---  Most labels are simply displayed as provided, but there are a---  number of wrinkles to take care of here:---  (a) blank nodes automatically allocated on input, with node---      identifiers of the form of a digit string nnn.  These are---      not syntactically valid, and are reassigned node identifiers---      of the form _nnn, where nnn is chosen so that is does not---      clash with any other identifier in the graph.---  (b) URI nodes:  if possible, replace URI with qname,---      else display as <uri>---  (c) formula nodes (containing graphs).---  (d) use the "special-case" formats for integer/float/double---      literals.      ---      ---  [[[TODO:]]]---  (d) generate multi-line literals when appropriate------ This is being updated to produce inline formula, lists and     --- blank nodes. The code is not efficient.--------- Note: There is a lot less customisation possible in Turtle than N3.---      --formatLabel :: LabelContext -> RDFLabel -> Formatter B.Builder--{--The "[..]" conversion is done last, after "()" and "{}" checks.--}-formatLabel lctxt lab@(Blank (_:_)) = do-  mlst <- extractList lctxt lab-  case mlst of-    Just lst -> insertList lst-    Nothing -> do-      -- NOTE: unlike N3 we do not properly handle "formula"/named graphs-      -- also we only expand out bnodes into [...] format when it's a object.-      -- although we need to handle [] for the subject.-      nb1 <- getBnodesCheck-      if lctxt /= PredContext && lab `notElem` nb1-        then insertBnode lctxt lab-        else formatNodeId lab---- formatLabel _ lab@(Res sn) = -formatLabel ctxt (Res sn)-  | ctxt == PredContext && sn == rdfType = return "a"-  | ctxt == ObjContext  && sn == rdfNil  = return "()"-  | otherwise = do-  pr <- getPrefixes-  let nsuri  = getScopeURI sn-      local  = getScopeLocal sn-      premap = reverseLookupMap pr :: RevNamespaceMap-      prefix = mapFindMaybe nsuri premap-          -      name   = case prefix of-        Just (Just p) -> B.fromText $ quoteT True $ mconcat [p, ":", local] -- TODO: what are quoting rules for QNames-        _ -> mconcat ["<", quoteB True (show nsuri ++ T.unpack local), ">"]-      -  return name---- The canonical notation for xsd:double in XSD, with an upper-case E,--- does not match the syntax used in N3, so we need to convert here.     --- Rather than converting back to a Double and then displaying that       --- we just convert E to e for now.      ---      -formatLabel _ (Lit lit (Just dtype)) -  | dtype == xsdDouble = return $ B.fromText $ T.toLower lit-  | dtype `elem` [xsdBoolean, xsdDecimal, xsdInteger] = return $ B.fromText lit-  | otherwise = return $ quoteText lit `mappend` formatAnnotation dtype-formatLabel _ (Lit lit Nothing) = return $ quoteText lit--formatLabel _ lab = return $ B.fromString $ show lab---- the annotation for a literal (ie type or language)-formatAnnotation :: ScopedName -> B.Builder-formatAnnotation a  | isLang a  = "@" `mappend` B.fromText (langTag a)-                    | otherwise = "^^" `mappend` showScopedName a--{-|-Convert text into a format for display in Turtle. The idea-is to use one double quote unless three are needed, and to-handle adding necessary @\\@ characters, or conversion-for Unicode characters.--}-quoteText :: T.Text -> B.Builder-quoteText txt = -  let st = T.unpack txt -- TODO: fix-      qst = quoteB (n==1) st-      n = if '\n' `elem` st || '"' `elem` st then 3 else 1-      qch = B.fromString (replicate n '"')-  in mconcat [qch, qst, qch]--formatNodeId :: RDFLabel -> Formatter B.Builder-formatNodeId lab@(Blank (lnc:_)) =-    if isDigit lnc then mapBlankNode lab else return $ B.fromString $ show lab-formatNodeId other = error $ "formatNodeId not expecting a " ++ show other -- to shut up -Wall--mapBlankNode :: RDFLabel -> Formatter B.Builder-mapBlankNode lab = do-  ngs <- getNgs-  let cmap = nodeMap ngs-      cval = nodeGen ngs-  nv <- case mapFind 0 lab cmap of-    0 -> do -      let nval = succ cval-          nmap = mapAdd cmap (lab, nval)-      setNgs $ ngs { nodeGen = nval, nodeMap = nmap }-      return nval-      -    n -> return n-  -  -- TODO: is this what we want?-  return $ "_:swish" `mappend` B.fromString (show nv)---- TODO: need to be a bit more clever with this than we did in NTriples---       not sure the following counts as clever enough ...---  -showScopedName :: ScopedName -> B.Builder-{--showScopedName (ScopedName n l) = -  let uri = nsURI n ++ l-  in quote uri--}-showScopedName = quoteB True . show---------------------------------------------------------------------------  Graph-related helper functions-------------------------------------------------------------------------newtype SortedArcs lb = SA [Arc lb]--sortArcs :: (Ord lb) => [Arc lb] -> SortedArcs lb-sortArcs = SA . sort----  Rearrange a list of arcs into a tree of pairs which group together---  all statements for a single subject, and similarly for multiple---  objects of a common predicate.----arcTree :: (Eq lb) => SortedArcs lb -> SubjTree lb-arcTree (SA as) = commonFstEq (commonFstEq id) $ map spopair as-    where-        spopair (Arc s p o) = (s,(p,o))--{--arcTree as = map spopair $ sort as-    where-        spopair (Arc s p o) = (s,[(p,[o])])--}----  Rearrange a list of pairs so that multiple occurrences of the first---  are commoned up, and the supplied function is applied to each sublist---  with common first elements to obtain the corresponding second value-commonFstEq :: (Eq a) => ( [b] -> c ) -> [(a,b)] -> [(a,c)]-commonFstEq f ps =-    [ (fst $ head sps,f $ map snd sps) | sps <- groupBy fstEq ps ]-    where-        fstEq (f1,_) (f2,_) = f1 == f2--findMaxBnode :: RDFGraph -> Int-findMaxBnode = maximum . map getAutoBnodeIndex . labels--getAutoBnodeIndex   :: RDFLabel -> Int-getAutoBnodeIndex (Blank ('_':lns)) = res where-    -- cf. prelude definition of read s ...-    res = case [x | (x,t) <- reads lns, ("","") <- lex t] of-            [x] -> x-            _   -> 0-getAutoBnodeIndex _                   = 0--{--Find all blank nodes that occur-  - any number of times as a subject-  - 0 or 1 times as an object--Such nodes can be output using the "[..]" syntax. To make it simpler-to check we actually store those nodes that can not be expanded.--Note that we do not try and expand any bNode that is used in-a predicate position.--Should probably be using the SubjTree RDFLabel structure but this-is easier for now.---}--countBnodes :: SortedArcs RDFLabel -> [RDFLabel]-countBnodes (SA as) = snd (foldl' ctr ([],[]) as)-    where-      -- first element of tuple are those blank nodes only seen once,-      -- second element those blank nodes seen multiple times-      ---      inc b@(b1s,bms) l@(Blank _) | l `elem` bms = b-                                  | l `elem` b1s = (delete l b1s, l:bms)-                                  | otherwise    = (l:b1s, bms)-      inc b _ = b--      -- if the bNode appears as a predicate we instantly add it to the-      -- list of nodes not to expand, even if only used once-      incP b@(b1s,bms) l@(Blank _) | l `elem` bms = b-                                   | l `elem` b1s = (delete l b1s, l:bms)-           			   | otherwise    = (b1s, l:bms)-      incP b _ = b--      ctr orig (Arc _ p o) = inc (incP orig p) o----------------------------------------------------------------------------------------  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  All rights reserved.------  This file is part of Swish.------  Swish is free software; you can redistribute it and/or modify---  it under the terms of the GNU General Public License as published by---  the Free Software Foundation; either version 2 of the License, or---  (at your option) any later version.------  Swish is distributed in the hope that it will be useful,---  but WITHOUT ANY WARRANTY; without even the implied warranty of---  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the---  GNU General Public License for more details.------  You should have received a copy of the GNU General Public License---  along with Swish; if not, write to:---    The Free Software Foundation, Inc.,---    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA------------------------------------------------------------------------------------
− src/Swish/RDF/TurtleParser.hs
@@ -1,1062 +0,0 @@-{-# LANGUAGE OverloadedStrings #-} -- only used in 'fromMaybe "" mbase' line of parseN3-------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  TurtleParser---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  License     :  GPL V2------  Maintainer  :  Douglas Burke---  Stability   :  experimental---  Portability :  OverloadedStrings------  This Module implements a Turtle parser (see [1]), returning a---  new 'RDFGraph' consisting of triples and namespace information parsed from---  the supplied input string, or an error indication.------ REFERENCES:------ 1 <http://www.w3.org/TR/turtle/>---     Turtle, Terse RDF Triple Language---     W3C Working Draft 09 August 2011 (<http://www.w3.org/TR/2011/WD-turtle-20110809/>)------ Notes:------ At present there is a lot of overlap with the N3 Parser.--------------------------------------------------------------------------------------module Swish.RDF.TurtleParser-    ( ParseResult-    , parseTurtle      -    , parseTurtlefromText      -    {--    , parseAnyfromText-    , parseTextFromText, parseAltFromText-    , parseNameFromText -- , parsePrefixFromText-    , parseAbsURIrefFromText, parseLexURIrefFromText, parseURIref2FromText-    -}-      -      {--    -- * Exports for parsers that embed Turtle in a bigger syntax-    , TurtleParser, TurtleState(..), SpecialMap-    -    , getPrefix -- a combination of the old defaultPrefix and namedPrefix productions-    , n3symbol -- replacement for uriRef2 -- TODO: check this is semantically correct      -    , quickVariable -- was varid      -    , lexUriRef       -    , document, subgraph                                                   -    , newBlankNode-       -}-    )-where--import Swish.RDF.RDFGraph-    ( RDFGraph, RDFLabel(..)-    , ToRDFLabel(..)-    , NamespaceMap-    , addArc -    , setNamespaces-    , emptyRDFGraph-    )--import Swish.RDF.GraphClass (arc)--import Swish.Utils.LookupMap-    ( LookupMap(..)-    , LookupEntryClass(..)-    , mapFindMaybe, mapReplaceOrAdd, mapAdd, mapReplace )--import Swish.Utils.Namespace-    ( Namespace, makeNamespace-    , ScopedName-    , getScopeNamespace-    , getScopedNameURI-    , getScopeNamespace-    , makeURIScopedName-    , makeNSScopedName-    )--import Swish.RDF.Vocabulary-    ( langName-    , rdfType-    , rdfFirst, rdfRest, rdfNil-    , xsdBoolean, xsdInteger, xsdDecimal, xsdDouble-    , defaultBase-    )--import Swish.RDF.RDFParser-    ( ParseResult-    , runParserWithError-    , ignore-    , noneOf-    , char-    , ichar-    , string-    , stringT-    , symbol-    , isymbol-    , lexeme-    , whiteSpace-    , mkTypedLit-    , hex4  -    , hex8  -    , appendURIs-    )--import Control.Applicative-import Control.Monad (foldM)--import Network.URI (URI(..), parseURIReference)--import Data.Char (ord) -import Data.Maybe (fromMaybe, fromJust)--import qualified Data.Text as T-import qualified Data.Text.Lazy as L-import Text.ParserCombinators.Poly.StateText--------------------------------------------------------------------------- Define parser state and helper functions--------------------------------------------------------------------------- | Turtle parser state-data TurtleState = TurtleState-        { graphState :: RDFGraph            -- Graph under construction-        , prefixUris :: NamespaceMap        -- namespace prefix mapping table-        , baseUri    :: URI                 -- base URI-        , nodeGen    :: Int                 -- blank node id generator-        } deriving Show---- | Functions to update TurtleState vector (use with stUpdate)--setPrefix :: Maybe T.Text -> URI -> TurtleState -> TurtleState-setPrefix pre uri st =  st { prefixUris=p' }-    where-        p' = mapReplaceOrAdd (makeNamespace pre uri) (prefixUris st)---- | Change the base-setBase :: URI -> TurtleState -> TurtleState-setBase buri st = st { baseUri = buri }----  Functions to access state:---- | Return the default prefix-getDefaultPrefix :: TurtleParser Namespace-getDefaultPrefix = do-  s <- stGet-  case getPrefixURI s Nothing of-    Just uri -> return $ makeNamespace Nothing uri-    _ -> failBad "No default prefix defined; how unexpected (probably a programming error)!"----  Map prefix to URI (naming needs a scrub here)-getPrefixURI :: TurtleState -> Maybe T.Text -> Maybe URI-getPrefixURI st pre = mapFindMaybe pre (prefixUris st)--findPrefixNamespace :: Maybe L.Text -> TurtleParser Namespace-findPrefixNamespace (Just p) = findPrefix (L.toStrict p)-findPrefixNamespace Nothing  = getDefaultPrefix----  Return function to update graph in Turtle parser state,---  using the supplied function of a graph----updateGraph :: (RDFGraph -> RDFGraph) -> TurtleState -> TurtleState-updateGraph f s = s { graphState = f (graphState s) }---------------------------------------------------------------------------  Define top-level parser function:---  accepts a string and returns a graph or error-------------------------------------------------------------------------type TurtleParser a = Parser TurtleState a---- | Parse as Turtle (with no real base URI).--- --- See 'parseTurtle' if you need to provide a base URI.----parseTurtlefromText ::-  L.Text -- ^ input in N3 format.-  -> ParseResult-parseTurtlefromText = flip parseTurtle Nothing---- | Parse a string with an optional base URI.---            --- Unlike 'parseN3' we treat the base URI as a URI and not--- a QName.----parseTurtle ::-  L.Text -- ^ input in N3 format.-  -> Maybe URI -- ^ optional base URI-  -> ParseResult-parseTurtle txt mbase = parseAnyfromText turtleDoc mbase txt--hashURI :: URI-hashURI = fromJust $ parseURIReference "#"--emptyState :: -  Maybe URI  -- ^ starting base for the graph-  -> TurtleState-emptyState mbase = -  let pmap   = LookupMap [makeNamespace Nothing hashURI]-      buri   = fromMaybe (getScopedNameURI defaultBase) mbase-  in TurtleState-     { graphState = emptyRDFGraph-     , prefixUris = pmap-     , baseUri    = buri-     , nodeGen    = 0-     }-  --- | Function to supply initial context and parse supplied term.----parseAnyfromText :: -  TurtleParser a  -- ^ parser to apply-  -> Maybe URI    -- ^ base URI of the input, or @Nothing@ to use default base value-  -> L.Text       -- ^ input to be parsed-  -> Either String a-parseAnyfromText parser mbase = runParserWithError parser (emptyState mbase)--newBlankNode :: TurtleParser RDFLabel-newBlankNode = do-  n <- stQuery (succ . nodeGen)-  stUpdate $ \s -> s { nodeGen = n }-  return $ Blank (show n)-  -{--This has been made tricky by the attempt to remove the default list-of prefixes from the starting point of a parse and the subsequent-attempt to add every new namespace we come across to the parser state.--So we add in the original default namespaces for testing, since-this routine is really for testing.--addTestPrefixes :: TurtleParser ()-addTestPrefixes = stUpdate $ \st -> st { prefixUris = LookupMap prefixTable } -- should append to existing map---}---- helper routines--comma, semiColon , fullStop :: TurtleParser ()-comma = isymbol ","-semiColon = isymbol ";"-fullStop = isymbol "."--sQuot, dQuot, sQuot3, dQuot3 :: TurtleParser ()-sQuot = ichar '\''-dQuot = ichar '"'-sQuot3 = ignore $ string "'''"-dQuot3 = ignore $ string "\"\"\""--match :: (Ord a) => a -> [(a,a)] -> Bool-match v = any (\(l,h) -> v >= l && v <= h)---- a specialization of bracket-br :: String -> String -> TurtleParser a -> TurtleParser a-br lsym rsym = bracket (symbol lsym) (symbol rsym)---- this is a lot simpler than N3-atWord :: T.Text -> TurtleParser ()-atWord s = char '@' *> lexeme (stringT s) *> pure ()--{--Add statement to graph in the parser state; there is a special case-for the special-case literals in the grammar since we need to ensure-the necessary namespaces (in other words xsd) are added to the-namespace store.--}--addStatement :: RDFLabel -> RDFLabel -> RDFLabel -> TurtleParser ()-addStatement s p o@(Lit _ (Just dtype)) | dtype `elem` [xsdBoolean, xsdInteger, xsdDecimal, xsdDouble] = do -  ost <- stGet-  let stmt = arc s p o-      oldp = prefixUris ost-      ogs = graphState ost-      newp = mapReplaceOrAdd (getScopeNamespace dtype) oldp-  stUpdate $ \st -> st { prefixUris = newp, graphState = addArc stmt ogs }-addStatement s p o = stUpdate (updateGraph (addArc (arc s p o) ))--isaz, isAZ, isaZ, is09, isaZ09 :: Char -> Bool-isaz c = c >= 'a' && c <= 'z'-isAZ c = c >= 'A' && c <= 'Z'-isaZ c = isaz c || isAZ c-is09 c = c >= '0' && c <= '9'-isaZ09 c = isaZ c || is09 c--{--Convert a string representing a double into canonical-XSD form. The input string is assumed to be syntactically-valid so we use read rather than reads. We use the String read-rather than Text one because of issues I have had in some tests-with the accuracy of the Text one.--}-d2s :: L.Text -> RDFLabel-d2s = -  let conv :: String -> Double-      conv = read-  in toRDFLabel . conv . L.unpack--{--Since operatorLabel can be used to add a label with an -unknown namespace, we need to ensure that the namespace-is added if not known. If the namespace prefix is already-in use then it is over-written (rather than add a new-prefix for the label).--TODO:-  - could we use the reverse lookupmap functionality to-    find if the given namespace URI is in the namespace-    list? If it is, use it's key otherwise do a-    mapReplaceOrAdd for the input namespace.-    --}-operatorLabel :: ScopedName -> TurtleParser RDFLabel-operatorLabel snam = do-  st <- stGet-  let sns = getScopeNamespace snam-      opmap = prefixUris st-      pkey = entryKey sns-      pval = entryVal sns-      -      rval = Res snam-      -  -- the lookup and the replacement could be fused-  case mapFindMaybe pkey opmap of-    Just val | val == pval -> return rval-             | otherwise   -> do-               stUpdate $ \s -> s { prefixUris = mapReplace opmap sns }-               return rval-    -    _ -> do-      stUpdate $ \s -> s { prefixUris = mapAdd opmap sns }-      return rval-        -findPrefix :: T.Text -> TurtleParser Namespace-findPrefix pre = do-  st <- stGet-  case mapFindMaybe (Just pre) (prefixUris st) of-    Just uri -> return $ makeNamespace (Just pre) uri-    Nothing  -> failBad $ "Undefined prefix '" ++ T.unpack pre ++ ":'."--{---Syntax productions; the Turtle NBF grammar elements are from-http://www.w3.org/TR/turtle/turtle.bnf--The element names are converted to match Haskell syntax-and idioms where possible:--  - camel Case rather than underscores and all upper case--  - upper-case identifiers prepended by _ after above form---}--{--[1] turtleDoc ::= (statement)*--}-turtleDoc :: TurtleParser RDFGraph-turtleDoc = mkGr <$> (whiteSpace *> many statement *> eof *> stGet)-  where-    mkGr s = setNamespaces (prefixUris s) (graphState s)--{--[2] statement ::= directive "." - | triples "."--}-statement :: TurtleParser ()-statement = (directive <|> triples) *> fullStop--{--[3] directive ::= prefixID - | base--}-directive :: TurtleParser ()-directive = lexeme (prefixID <|> base)--{--[4] prefixID ::= PREFIX PNAME_NS IRI_REF --}-prefixID :: TurtleParser ()-prefixID = do-  _prefix -  p <- lexeme _pnameNS-  u <- _iriRef-  stUpdate (setPrefix (fmap L.toStrict p) u)--{--[5] base ::= BASE IRI_REF --}-base :: TurtleParser ()-base = _base >> _iriRef >>= stUpdate . setBase--{--[6] triples ::= subject predicateObjectList --}--triples :: TurtleParser ()-triples = subject >>= predicateObjectList--{--[7] predicateObjectList ::= verb objectList ( ";" verb objectList )* (";")? --}--predicateObjectList :: RDFLabel -> TurtleParser ()-predicateObjectList subj = -  let term = verb >>= objectList subj-  in sepBy1 term semiColon *> ignore (optional semiColon)-  -- in sepBy1 (lexeme term) semiColon *> ignore (optional semiColon)-  -{--[8] objectList ::= object ( "," object )* --}--objectList :: RDFLabel -> RDFLabel -> TurtleParser ()-objectList subj prd = sepBy1 object comma >>= mapM_ (addStatement subj prd)--{--[9] verb ::= predicate - | "a"--}--verb :: TurtleParser RDFLabel-verb = predicate <|> (lexeme (char 'a') *> operatorLabel rdfType)-   -{-       -[10] subject ::= IRIref - | blank --}--subject :: TurtleParser RDFLabel-subject = (Res <$> iriref) <|> blank--{--[11] predicate ::= IRIref --}--predicate :: TurtleParser RDFLabel-predicate = Res <$> iriref--{--[12] object ::= IRIref - | blank - | literal--}--object :: TurtleParser RDFLabel-object = (Res <$> iriref) <|> blank <|> literal--{--[13] literal ::= RDFLiteral - | NumericLiteral - | BooleanLiteral --}--literal :: TurtleParser RDFLabel-literal = lexeme $ rdfLiteral <|> numericLiteral <|> booleanLiteral--{--[14] blank ::= BlankNode - | blankNodePropertyList - | collection --Since both BlankNode and blankNodePropertyList can match '[ ... ]' we pull-that out and treat this as--  blank ::= BLANK_NODE_LABEL-     | "[" (predicateObjectList | WS*) "]"-     | collection--blank :: TurtleParser RDFLabel-blank = lexeme (blankNode <|> blankNodePropertyList <|> collection)---}--blank :: TurtleParser RDFLabel-blank = lexeme (_blankNodeLabel-                <|>-                bracket (char '[') (char ']') handleBlankNode-                <|>-                collection-                )--{--[15] blankNodePropertyList ::= "[" predicateObjectList "]" --We now match the brackets in the parent rule.--blankNodePropertyList :: TurtleParser RDFLabel-blankNodePropertyList = do-  bNode <- newBlankNode-  -- br "[" "]" (predicateObjectList bNode)-  bracket (satisfy (=='['))-    (satisfy (==']'))-    (_manyws *> predicateObjectList bNode *> _manyws)-  -- ignore (optional _manyws) -- TODO: this is a hack-  return bNode---}--handleBlankNode :: TurtleParser RDFLabel-handleBlankNode = do-  bNode <- newBlankNode-  _manyws-  ignore $ optional $ predicateObjectList bNode-  _manyws-  return bNode---{--[16] collection ::= "(" object* ")" --}--collection :: TurtleParser RDFLabel-collection = do-  os <- br "(" ")" (many object)-  eNode <- operatorLabel rdfNil-  case os of-    [] -> return eNode-    -    (x:xs) -> do-      sNode <- newBlankNode-      first <- operatorLabel rdfFirst-      addStatement sNode first x-      lNode <- foldM addElem sNode xs-      rest <- operatorLabel rdfRest-      addStatement lNode rest eNode-      return sNode--    where      -      addElem prevNode curElem = do-        bNode <- newBlankNode-        first <- operatorLabel rdfFirst-        rest <- operatorLabel rdfRest-        addStatement prevNode rest bNode-        addStatement bNode first curElem-        return bNode--{--[17] <BASE> ::= "@base" --}--_base :: TurtleParser ()-_base = atWord "base"--{--[18] <PREFIX> ::= "@prefix" --}--_prefix :: TurtleParser ()-_prefix = atWord "prefix"--{--[19] <UCHAR> ::= ( "\\u" [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] ) - | ( "\\U" [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] - [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] ) ---}--_uchar :: TurtleParser Char-_uchar = char '\\' *> _uchar'-         -_uchar' :: TurtleParser Char-_uchar' = (char 'u' *> hex4) <|> (char 'U' *> hex8)--{--[60s] RDFLiteral ::= String ( LANGTAG | ( "^^" IRIref ) )? --}--rdfLiteral :: TurtleParser RDFLabel-rdfLiteral = do-  lbl <- turtleString-  opt <- optional (_langTag <|> (string "^^" *> iriref))-  return $ Lit (L.toStrict lbl) opt--{--[61s] NumericLiteral ::= NumericLiteralUnsigned - | NumericLiteralPositive - | NumericLiteralNegative --}--numericLiteral :: TurtleParser RDFLabel-numericLiteral = numericLiteralNegative <|> numericLiteralPositive <|> numericLiteralUnsigned--{--[62s] NumericLiteralUnsigned ::= INTEGER - | DECIMAL - | DOUBLE --}--numericLiteralUnsigned :: TurtleParser RDFLabel-numericLiteralUnsigned = -  d2s <$> _double-  <|> -  (mkTypedLit xsdDecimal . L.toStrict <$> _decimal)-  <|> -  (mkTypedLit xsdInteger . L.toStrict <$> _integer)--{--[63s] NumericLiteralPositive ::= INTEGER_POSITIVE - | DECIMAL_POSITIVE - | DOUBLE_POSITIVE --}--numericLiteralPositive :: TurtleParser RDFLabel-numericLiteralPositive =-  d2s <$> _doublePositive-  <|> -  (mkTypedLit xsdDecimal . L.toStrict <$> _decimalPositive)-  <|> -  (mkTypedLit xsdInteger . L.toStrict <$> _integerPositive)--{--[64s] NumericLiteralNegative ::= INTEGER_NEGATIVE - | DECIMAL_NEGATIVE - | DOUBLE_NEGATIVE --}--numericLiteralNegative :: TurtleParser RDFLabel-numericLiteralNegative = -  d2s <$> _doubleNegative-  <|> -  (mkTypedLit xsdDecimal . L.toStrict <$> _decimalNegative)-  <|> -  (mkTypedLit xsdInteger . L.toStrict <$> _integerNegative)-   -{--[65s] BooleanLiteral ::= "true" - | "false"--}--booleanLiteral :: TurtleParser RDFLabel-booleanLiteral = mkTypedLit xsdBoolean . T.pack <$> (string "true" <|> string "false")--{--[66s] String ::= STRING_LITERAL1 - | STRING_LITERAL2 - | STRING_LITERAL_LONG1 - | STRING_LITERAL_LONG2--}--turtleString :: TurtleParser L.Text-turtleString = -  lexeme (-    _stringLiteralLong1 <|> _stringLiteral1 <|>-    _stringLiteralLong2 <|> _stringLiteral2)--{--[67s] IRIref ::= IRI_REF - | PrefixedName --}--iriref :: TurtleParser ScopedName-iriref = lexeme ((makeURIScopedName <$> _iriRef) <|> prefixedName)--{--[68s] PrefixedName ::= PNAME_LN - | PNAME_NS --}--prefixedName :: TurtleParser ScopedName-prefixedName = -  _pnameLN <|> -  flip makeNSScopedName T.empty <$> (_pnameNS >>= findPrefixNamespace)--{--[69s] BlankNode ::= BLANK_NODE_LABEL - | ANON --blankNode :: TurtleParser RDFLabel-blankNode = lexeme (_blankNodeLabel <|> _anon)---}--{--[70s] <IRI_REF> ::= "<" ( [^<>\"{}|^`\\] - [#0000- ] | UCHAR )* ">" --Read [#0000- ] as [#x00-#x20] from-http://lists.w3.org/Archives/Public/public-rdf-comments/2011Aug/0011.html--Unlike N3, whitespace is significant within the surrounding <>.--At present relying on Network.URI to define what characters are valid-in a URI. This is not necessarily ideal.--}--_iriRef :: TurtleParser URI-_iriRef = do-  utxt <- bracket (char '<') (char '>') $ manySatisfy (/= '>') -- TODO: fix-  let ustr = L.unpack utxt-  case parseURIReference ustr of-    Nothing -> fail $ "Unable to convert <" ++ ustr ++ "> to a URI"-    Just uref -> do-      s <- stGet-      either fail return $ appendURIs (baseUri s) uref--{--[71s] <PNAME_NS> ::= (PN_PREFIX)? ":" --}--_pnameNS :: TurtleParser (Maybe L.Text)-_pnameNS = optional _pnPrefix <* char ':'--{--[72s] <PNAME_LN> ::= PNAME_NS PN_LOCAL --}--_pnameLN :: TurtleParser ScopedName-_pnameLN = makeNSScopedName -           <$> (_pnameNS >>= findPrefixNamespace) -           <*> fmap L.toStrict _pnLocal--{--[73s] <BLANK_NODE_LABEL> ::= "_:" PN_LOCAL --}--_blankNodeLabel :: TurtleParser RDFLabel-_blankNodeLabel = (Blank . L.unpack) <$> (string "_:" *> _pnLocal)--{---These are unused in the grammar.--[74s] <VAR1> ::= "?" VARNAME -[75s] <VAR2> ::= "$" VARNAME --}--{--[76s] <LANGTAG> ::= BASE - | PREFIX - | "@" [a-zA-Z]+ ( "-" [a-zA-Z0-9]+ )* --I am ignoring the BASE and PREFIX lines here as they don't make sense to me.--}--_langTag :: TurtleParser ScopedName-_langTag = do-  ichar '@'-  h <- many1Satisfy isaZ-  mt <- optional (L.cons <$> char '-' <*> many1Satisfy isaZ09)-  return $ langName $ L.toStrict $ L.append h (fromMaybe L.empty mt)-  -{--[77s] <INTEGER> ::= [0-9]+ --}--_integer :: TurtleParser L.Text-_integer = many1Satisfy is09--{--[78s] <DECIMAL> ::= [0-9]+ "." [0-9]* - | "." [0-9]+ --We try to produce a canonical form for the-numbers.--}--_decimal :: TurtleParser L.Text-_decimal = -  let dpart = L.cons <$> char '.' <*> (fromMaybe "0" <$> optional _integer)-  in -   (L.append <$> _integer <*> dpart)-   <|>-   (L.append "0." <$> (char '.' *> _integer))--{--[79s] <DOUBLE> ::= [0-9]+ "." [0-9]* EXPONENT - | "." ( [0-9] )+ EXPONENT - | ( [0-9] )+ EXPONENT --Unlike _decimal, the canonical form is enforced-later on, although it could be done here.--}--_double :: TurtleParser L.Text-_double = -  (L.append <$> _decimal <*> _exponent)-  <|>-  (L.append <$> _integer <*> _exponent)-  -{--[80s] <INTEGER_POSITIVE> ::= "+" INTEGER -[81s] <DECIMAL_POSITIVE> ::= "+" DECIMAL -[82s] <DOUBLE_POSITIVE> ::= "+" DOUBLE --}--_integerPositive, _decimalPositive, _doublePositive :: TurtleParser L.Text-_integerPositive = char '+' *> _integer-_decimalPositive = char '+' *> _decimal-_doublePositive = char '+' *> _double--{--[83s] <INTEGER_NEGATIVE> ::= "-" INTEGER -[84s] <DECIMAL_NEGATIVE> ::= "-" DECIMAL -[85s] <DOUBLE_NEGATIVE> ::= "-" DOUBLE --}--_integerNegative, _decimalNegative, _doubleNegative :: TurtleParser L.Text-_integerNegative = L.cons <$> char '-' <*> _integer-_decimalNegative = L.cons <$> char '-' <*> _decimal-_doubleNegative = L.cons <$> char '-' <*> _double--{--[86s] <EXPONENT> ::= [eE] [+-]? [0-9]+ --}--_exponent :: TurtleParser L.Text-_exponent = do-  ignore $ satisfy (`elem` "eE")-  ms <- optional (satisfy (`elem` "+-"))-  e <- _integer-  case ms of-    Just '-' -> return $ L.append "E-" e-    _        -> return $ L.cons 'E' e-  -{--[87s] <STRING_LITERAL1> ::= "'" ( ( [^'\\\n\r] ) | ECHAR | UCHAR )* "'" -[88s] <STRING_LITERAL2> ::= '"' ( ( [^\"\\\n\r] ) | ECHAR | UCHAR )* '"' -[89s] <STRING_LITERAL_LONG1> ::= "'''" ( ( "'" | "''" )? ( [^'\\] | ECHAR | UCHAR ) )* "'''" -[90s] <STRING_LITERAL_LONG2> ::= '"""' ( ( '"' | '""' )? ( [^\"\\] | ECHAR | UCHAR ) )* '"""' --}--_stringLiteral1, _stringLiteral2 :: TurtleParser L.Text-_stringLiteral1 = _stringIt sQuot (_tChars "'\\\n\r")-_stringLiteral2 = _stringIt dQuot (_tChars "\"\\\n\r")--_stringLiteralLong1, _stringLiteralLong2 :: TurtleParser L.Text-_stringLiteralLong1 = _stringItLong sQuot3 (_tCharsLong '\'' "'\\")-_stringLiteralLong2 = _stringItLong dQuot3 (_tCharsLong '"' "\"\\")--_stringIt :: TurtleParser a -> TurtleParser Char -> TurtleParser L.Text-_stringIt sep chars = L.pack <$> bracket sep sep (many chars)--_stringItLong :: TurtleParser a -> TurtleParser L.Text -> TurtleParser L.Text-_stringItLong sep chars = L.concat <$> bracket sep sep (many chars)--_tChars :: String -> TurtleParser Char-_tChars excl = (char '\\' *> (_echar' <|> _uchar'))-               <|> noneOf excl--_tCharsLong :: Char -> String -> TurtleParser L.Text-_tCharsLong c excl = do-  mq <- optional $ oneOrTwo c-  r <- _tChars excl-  return $ L.append (fromMaybe L.empty mq) (L.singleton r)--oneOrTwo :: Char -> TurtleParser L.Text-oneOrTwo c = do-  a <- char c-  mb <- optional (char c)-  case mb of-    Just b -> return $ L.pack [a,b]-    _      -> return $ L.singleton a--{--[91s] <ECHAR> ::= "\\" [tbnrf\\\"'] --}--_echar :: TurtleParser Char-_echar = char '\\' *> _echar'--_echar' :: TurtleParser Char-_echar' = -  (char 't' *> pure '\t') <|>-  (char 'b' *> pure '\b') <|>-  (char 'n' *> pure '\n') <|>-  (char 'r' *> pure '\r') <|>-  (char '\\' *> pure '\\') <|>-  (char '"' *> pure '"') <|>-  (char '\'' *> pure '\'')---{---Unused.--[92s] <NIL> ::= "(" (WS)* ")" --}--{--[93s] <WS> ::= " " - | "\t" - | "\r" - | "\n"--_ws :: TurtleParser ()-_ws = ignore $ satisfy (`elem` " \t\r\n")---}--_manyws :: TurtleParser ()-_manyws = ignore $ manySatisfy (`elem` " \t\r\n")--{--[94s] <ANON> ::= "[" (WS)* "]" --Unused as we do not support the use of ANON in the BlankNode-terminal.--_anon :: TurtleParser RDFLabel-_anon = br "[" "]" _manyws *> newBlankNode---}--{--[95s] <PN_CHARS_BASE> ::= [A-Z] - | [a-z] - | [#00C0-#00D6] - | [#00D8-#00F6] - | [#00F8-#02FF] - | [#0370-#037D] - | [#037F-#1FFF] - | [#200C-#200D] - | [#2070-#218F] - | [#2C00-#2FEF] - | [#3001-#D7FF] - | [#F900-#FDCF] - | [#FDF0-#FFFD] - | [#10000-#EFFFF] - | UCHAR --TODO: may want to make this a Char -> Bool selector for-use with manySatisfy rather than a combinator.--}--_pnCharsBase :: TurtleParser Char-_pnCharsBase = -  let f c = let i = ord c-            in isaZ c || -               match i [(0xc0, 0xd6), (0xd8, 0xf6), (0xf8, 0x2ff),-                        (0x370, 0x37d), (0x37f, 0x1fff), (0x200c, 0x200d),-                        (0x2070, 0x218f), (0x2c00, 0x2fef), (0x3001, 0xd7ff),-                        (0xf900, 0xfdcf), (0xfdf0, 0xfffd), (0x10000, 0xeffff)]-  in satisfy f <|> _uchar--{--[96s] <PN_CHARS_U> ::= PN_CHARS_BASE - | "_"--}--_pnCharsU :: TurtleParser Char-_pnCharsU = _pnCharsBase <|> char '_'--{---Only used in VAR1/2 rules which are themselves unused.--Unused in the grammar (other than-[97s] <VARNAME> ::= ( PN_CHARS_U | [0-9] ) ( PN_CHARS_U | [0-9] | #00B7 | [#0300-#036F] | [# - 203F-#2040] )* --}--{--[98s] <PN_CHARS> ::= PN_CHARS_U - | "-" - | [0-9] - | #00B7 - | [#0300-#036F] - | [#203F-#2040] --}--_pnChars :: TurtleParser Char-_pnChars = -  _pnCharsU -  <|> -  satisfy (\c -> let i = ord c -                 in c == '-' || (c >= '0' && c <= '9') || i == 0xb7 ||-                    match i [(0x0300, 0x036f), (0x203f, 0x2040)])--{--[99s] <PN_PREFIX> ::= PN_CHARS_BASE ( ( PN_CHARS | "." )* PN_CHARS )? --}--_pnPrefix :: TurtleParser L.Text-_pnPrefix = L.cons <$> _pnCharsBase <*> _pnRest-  -{--[100s] <PN_LOCAL> ::= ( PN_CHARS_U | [0-9] ) ( ( PN_CHARS | "." )* PN_CHARS )? --}     --_pnLocal :: TurtleParser L.Text-_pnLocal = L.cons <$> (_pnCharsU <|> satisfy is09) -           <*> _pnRest--{--Extracted from PN_PREFIX and PN_LOCAL is--<PN_REST> :== ( ( PN_CHARS | "." )* PN_CHARS )?--We assume below that the match is only ever done for small strings, so-the cost of the foldr isn't likely to be large. Let's see how well-this assumption holds up.---}--_pnRest :: TurtleParser L.Text-_pnRest = do-  lbl <- many (_pnChars <|> char '.')-  let (nret, lclean) = clean lbl-      -      -- a simple difference list implementation-      edl = id-      snocdl x xs = xs . (x:)-      appenddl = (.)-      replicatedl n x = (replicate n x ++)-  -      -- this started out as a simple automaton/transducer from-      -- http://www.haskell.org/pipermail/haskell-cafe/2011-September/095347.html-      -- but then I decided to complicate it-      -- -      clean :: String -> (Int, String)-      clean = go 0 edl-        where-          go n acc [] = (n, acc [])-          go n acc ('.':xs) = go (n+1) acc xs -          go 0 acc (x:xs) = go 0 (snocdl x acc) xs-          go n acc (x:xs) = go 0 (appenddl acc (snocdl x (replicatedl n '.'))) xs--  reparse $ L.replicate (fromIntegral nret) (L.singleton '.')-  return $ L.pack lclean--{--Original from -- chop = go 0 []-        where-        -- go :: State -> Stack -> String -> String-        go 0 _ [] = []-        go 0 _ (x:xs)-            | isSpace x = go 1 [x] xs-            | otherwise = x : go 0 xs--        go 1 ss [] = []-        go 1 ss (x:xs)-            | isSpace c = go 1 (x:ss) xs-            | otherwise = reverse ss ++ x : go 0 xs---}----------------------------------------------------------------------------------------  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  All rights reserved.------  This file is part of Swish.------  Swish is free software; you can redistribute it and/or modify---  it under the terms of the GNU General Public License as published by---  the Free Software Foundation; either version 2 of the License, or---  (at your option) any later version.------  Swish is distributed in the hope that it will be useful,---  but WITHOUT ANY WARRANTY; without even the implied warranty of---  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the---  GNU General Public License for more details.------  You should have received a copy of the GNU General Public License---  along with Swish; if not, write to:---    The Free Software Foundation, Inc.,---    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA------------------------------------------------------------------------------------
src/Swish/RDF/VarBinding.hs view
@@ -1,430 +1,75 @@-{-# LANGUAGE MultiParamTypeClasses, TypeSynonymInstances, OverloadedStrings #-}-{-# LANGUAGE FlexibleInstances #-} -- needed for ghc 7.12+{-# LANGUAGE OverloadedStrings #-}  -------------------------------------------------------------------------------- --  See end of this file for licence information. -------------------------------------------------------------------------------- -- | --  Module      :  VarBinding---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke --  License     :  GPL V2 -- --  Maintainer  :  Douglas Burke --  Stability   :  experimental---  Portability :  MultiParamTypeClasses, TypeSynonymInstances, OverloadedStrings, FlexibleInstances+--  Portability :  OverloadedStrings -----  This module defines functions for representing and manipulating query---  binding variable sets.  This is the key data that mediates between---  query and back substitution when performing inferences.  A framework---  of query variable modifiers is provided that can be used to---  implement richer inferences, such as filtering of  query results,---  or replacing values based on known relationships.+--  This module instantiates the `VarBinding` types and methods for use+--  with RDF graph labels. -- -------------------------------------------------------------------------------- +--  See module RDFQueryTest for test cases.+ module Swish.RDF.VarBinding-    ( VarBinding(..), nullVarBinding-    , boundVars, subBinding, makeVarBinding-    , applyVarBinding, joinVarBindings, addVarBinding-    , VarBindingModify(..), OpenVarBindingModify-    , vbmCompatibility, vbmCompose-    , composeSequence, findCompositions, findComposition-    , VarBindingFilter(..)-    , makeVarFilterModify-    , makeVarTestFilter, makeVarCompareFilter-    , varBindingId, nullVarBindingModify-    , varFilterDisjunction, varFilterConjunction-    , varFilterEQ, varFilterNE+    ( RDFVarBinding+    , RDFVarBindingModify, RDFOpenVarBindingModify, RDFOpenVarBindingModifyMap+    , RDFVarBindingFilter+    , nullRDFVarBinding+    , rdfVarBindingUriRef, rdfVarBindingBlank+    , rdfVarBindingLiteral+    , rdfVarBindingUntypedLiteral, rdfVarBindingTypedLiteral+    , rdfVarBindingXMLLiteral, rdfVarBindingDatatyped+    , rdfVarBindingMemberProp     ) where -import Swish.Utils.LookupMap-    ( LookupEntryClass(..) -    , makeLookupMap, mapFindMaybe-    )+import Swish.VarBinding (VarBinding(..), VarBindingModify(..), OpenVarBindingModify, VarBindingFilter(..))+import Swish.VarBinding (nullVarBinding, applyVarBinding, makeVarTestFilter) -import Swish.Utils.Namespace (ScopedName, getScopeLocal)+import Swish.RDF.Graph+    ( RDFLabel(..)+    , isLiteral, isUntypedLiteral, isTypedLiteral, isXMLLiteral+    , isDatatyped, isMemberProp, isUri, isBlank+    ) import Swish.RDF.Vocabulary (swishName)-import Swish.Utils.ListHelpers (equiv, subset, flist, headOrNothing, permutations) -import Data.Maybe (mapMaybe, fromMaybe, isJust, fromJust, listToMaybe)--import Data.List (find, intersect, union, (\\), foldl')--import Data.Monoid (mconcat)---- import qualified Data.Text as T+import Data.LookupMap (LookupMap(..))  ---------------------------------------------------------------  Query variable bindings+--  Types for RDF query variable bindings and modifiers ------------------------------------------------------------ --- TODO: is it worth making a Monoid instance of VarBinding?---- |VarBinding is the type of an arbitrary variable bindings---  value, where the type of the bound values is not specified.----data VarBinding a b = VarBinding-    { vbMap  :: a -> Maybe b-    , vbEnum :: [(a,b)]-    , vbNull :: Bool-    }---- |VarBinding is an instance of class Eq, so that variable---  bindings can be compared for equivalence----instance (Eq a, Eq b) => Eq (VarBinding a b) where-    vb1 == vb2 = vbEnum vb1 `equiv` vbEnum vb2---- |VarBinding is an instance of class Show, so that variable---  bindings can be displayed----instance (Show a, Show b) => Show (VarBinding a b) where-    show = show . vbEnum+-- |@RDFVarBinding@ is the specific type type of a variable+--  binding value used with RDF graph queries. +type RDFVarBinding  = VarBinding RDFLabel RDFLabel  -- | maps no query variables.----nullVarBinding :: VarBinding a b-nullVarBinding = VarBinding-    { vbMap  = const Nothing-    , vbEnum = []-    , vbNull = True-    }---- |Return a list of the variables bound by a supplied variable binding----boundVars :: VarBinding a b -> [a]-boundVars = map fst . vbEnum---- |VarBinding subset function, tests to see if one query binding---  is a subset of another;  i.e. every query variable mapping defined---  by one is also defined by the other.----subBinding :: (Eq a, Eq b) => VarBinding a b -> VarBinding a b -> Bool-subBinding vb1 vb2 = vbEnum vb1 `subset` vbEnum vb2---- |Function to make a variable binding from a list of---  pairs of variable and corresponding assigned value.----makeVarBinding :: (Eq a, Show a, Eq b, Show b) => [(a,b)] -> VarBinding a b-makeVarBinding vrbs =-    if null vrbs then nullVarBinding -- (nullVarBinding :: VarBinding a b)-    else VarBinding-        { vbMap  = selectFrom vrbs-        , vbEnum = vrbs-        , vbNull = null vrbs-        }-    where-        selectFrom = flip mapFindMaybe . makeLookupMap-        --  selectFrom bs is the VarBinding lookup function-        {--        selectFrom :: (Eq a) => [(a,b)] -> a -> Maybe b-        selectFrom []         _ = Nothing-        selectFrom ((v,r):bs) l = if l == v then Just r-                                    else selectFrom bs l-        -}---- |Apply query binding to a supplied value, returning the value---  unchanged if no binding is defined----applyVarBinding :: VarBinding a a -> a -> a-applyVarBinding vbind v = fromMaybe v (vbMap vbind v)---- |Join a pair of query bindings, returning a new binding that---  maps all variables recognized by either of the input bindings.---  If the bindings should overlap, such overlap is not detected and---  the value from the first binding provided is used arbitrarily.----joinVarBindings :: (Eq a) => VarBinding a b -> VarBinding a b -> VarBinding a b-joinVarBindings vb1 vb2-    | vbNull vb1 = vb2-    | vbNull vb2 = vb1-    | otherwise  = VarBinding-        { vbMap  = mv12-        , vbEnum = map (\v -> (v,fromJust (mv12 v))) bv12-        , vbNull = False-        }-    where-        -- flist fs a = map ($ a) fs;  see also monad function 'ap'-        mv12 = headOrNothing . filter isJust . flist [ vbMap vb1, vbMap vb2 ]-        bv12 = boundVars vb1 `union` boundVars vb2---- |Add a single new value to a variable binding and return the resulting---  new variable binding.----addVarBinding :: (Eq a, Show a, Eq b, Show b) => a -> b -> VarBinding a b-    -> VarBinding a b-addVarBinding lb val vbind = joinVarBindings vbind $ makeVarBinding [(lb,val)]-----------------------------------------------------------------  Datatypes for variable binding modifiers----------------------------------------------------------------- |Define the type of a function to modify variable bindings in---  forward chaining based on rule antecedent matches.  This---  function is used to implement the \"allocated to\" logic described---  in Appendix B of the RDF semantics document, in which a specific---  blank node is associated with all matches of some specific value---  by applications of the rule on a given graph.---  Use 'id' if no modification of the variable bindings is required.------  This datatype consists of the modifier function itself, which---  operates on a list of variable bindings rather than a single---  variable binding (because some modifications share context across---  a set of bindings), and some additional descriptive information---  that allows possible usage patterns to be analyzed.------  Some usage patterns (see 'vbmUsage' for more details):------  [filter]  all variables are input variables, and the effect---      of the modifier function is to drop variable bindings that---      don't satisfy some criterion.---      Identifiable by an empty element in @vbmUsage@.------  [source]  all variables are output variables:  a raw query---      could be viewed as a source of variable bindings.---      Identifiable by an element of @vbmUsage@ equal to @vbmVocab@.------  [modifier]  for each supplied variable binding, one or more---      new variable bindings may be created that contain the---      input variables bound as supplied plus some additional variables.---      Identifiable by an element of @vbmUsage@ some subset of @vbmVocab@.------  A variety of variable usage patterns may be supported by a given---  modifier:  a modifier may be used to define new variable bindings---  from existing bindings in a number of ways, or simply to check that---  some required relationship between bindings is satisfied.---  (Example, for @a + b = c@, any one variable can be deduced from the---  other two, or all three may be supplied to check that the relationship---  does indeed hold.)----data VarBindingModify a b = VarBindingModify-    { vbmName   :: ScopedName-                            -- ^Name used to identify this variable binding-                            --  modifier when building inference rules.-    , vbmApply  :: [VarBinding a b] -> [VarBinding a b]-                            -- ^Apply variable binding modifier to a-                            --  list of variable bindings, returning a-                            --  new list.  The result list is not-                            --  necessarily the same length as the-                            --  supplied list.-    , vbmVocab  :: [a]      -- ^List of variables used by this modifier.-                            --  All results of applying this modifier contain-                            --  bindings for these variables.-    , vbmUsage  :: [[a]]    -- ^List of binding modifier usage patterns-                            --  supported.  Each pattern is characterized as-                            --  a list of variables for which new bindings-                            --  may be created by some application of this-                            --  modifier, assuming that bindings for all other-                            --  variables in @vbmVocab@ are supplied.-    }---- |Allow a VarBindingModify value to be accessed using a 'LookupMap'.----instance LookupEntryClass-    (VarBindingModify a b) ScopedName (VarBindingModify a b)-    where-        keyVal   vbm     = (vbmName vbm,vbm)-        newEntry (_,vbm) = vbm---- |Type for variable binding modifier that has yet to be instantiated---  with respect to the variables that it operates upon.----type OpenVarBindingModify lb vn = [lb] -> VarBindingModify lb vn---- |Extract variable binding name from @OpenVarBindingModify@ value------  (Because only the name is required, the application to an undefined---  list of variable labels should never be evaluated, as long as the---  name is not dependent on the variable names in any way.)------  NOT QUITE... some of the functions that create @OpenVarBindingModify@---  instances also pattern-match the number of labels provided, forcing---  evaluation of the labels parameter, even though it's not used.----openVbmName :: OpenVarBindingModify lb vn -> ScopedName-openVbmName ovbm = vbmName (ovbm (error "Undefined labels in variable binding"))---- |Allow an @OpenVarBindingModify@ value to be accessed using a @LookupMap@.----instance LookupEntryClass-    (OpenVarBindingModify a b) ScopedName (OpenVarBindingModify a b)-    where-        keyVal   ovbm     = (openVbmName ovbm,ovbm)-        newEntry (_,ovbm) = ovbm---- |Allow an OpenVarBindingModify value to be accessed using a LookupMap.----instance Show (OpenVarBindingModify a b)-    where-        show = show . openVbmName---- |Variable binding modifier compatibility test.------  Given a list of bound variables and a variable binding modifier, return---  a list of new variables that may be bound, or @Nothing@.------  Note:  if the usage pattern component is well-formed (i.e. all---  elements different) then at most one element can be compatible with---  a given input variable set.----vbmCompatibility :: (Eq a) => VarBindingModify a b -> [a] -> Maybe [a]-vbmCompatibility vbm vars = find compat (vbmUsage vbm)-    where-        compat = vbmCompatibleVars vars (vbmVocab vbm)---- |Variable binding usage compatibility test.------  Returns @True@ if the supplied variable bindings can be compatibly---  processed by a variable binding usage with supplied vocabulary and---  usage pattern.----vbmCompatibleVars ::-  (Eq a) -  => [a] -- ^ variables supplied with bindings-  -> [a] -- ^ variables returned with bindings by a modifier-  -> [a] -- ^ variables assigned new bindings by a modifier-  -> Bool-vbmCompatibleVars bvars vocab ovars =-    null (ivars `intersect` ovars) &&       -- ivars and ovars don't overlap-    null ((vocab \\ ovars) \\ ivars)        -- ovars and ivars cover vocab-    where-        ivars = bvars `intersect` vocab---- |Compose variable binding modifiers.------  Returns @Just a@ new variable binding modifier that corresponds to---  applying the first supplied modifier and then applying the second---  one, or @Nothing@ if the two modifiers cannot be compatibly composed.------  NOTE:  this function does not, in general, commute.------  NOTE:  if there are different ways to achieve the same usage, that---  usage is currently repeated in the result returned.----vbmCompose :: (Eq a) => VarBindingModify a b -> VarBindingModify a b-    -> Maybe (VarBindingModify a b)-vbmCompose-    (VarBindingModify nam1 app1 voc1 use1)-    (VarBindingModify nam2 app2 voc2 use2)-    | not (null use12) = Just VarBindingModify-        { vbmName  = swishName $ mconcat ["_", getScopeLocal nam1, "_", getScopeLocal nam2, "_"]-        , vbmApply = app2 . app1-        , vbmVocab = voc1 `union` voc2-        , vbmUsage = use12-        }-    | otherwise = Nothing-    where-        use12 = compatibleUsage voc1 use1 use2---- |Determine compatible ways in which variable binding modifiers may---  be combined.------  The total vocabulary of a modifier is the complete set of variables---  that are used or bound by the modifier.  After the modifier has been---  applied, bindings must exist for all of these variables.------  A usage pattern of a modifier is a set of variables for which new---  bindings may be generated by the modifier.------  The only way in which two variable binding modifiers can be incompatible---  with each other is when they both attempt to create a new binding for---  the same variable.  (Note that this does not mean the composition will---  be compatible with all inputs:  see 'vbmCompatibleVars'.)------  NOTE:  if there are different ways to achieve the same usage, that---  usage is currently repeated in the result returned.----compatibleUsage ::-  (Eq a)-  => [a]   -- ^ the total vocabulary of the first modifier to be applied-  -> [[a]] -- ^ usage patterns for the first modifier-  -> [[a]] -- ^ usage patterns for the second modifier-  -> [[a]] -- ^ a list of possible usage patterns for the composition of-           --  the first modifier with the second modifier, or an empty list if-           --  the modifiers are incompatible.-compatibleUsage voc1 use1 use2 =-    [ u1++u2 | u2 <- use2, null (voc1 `intersect` u2), u1 <- use1 ]---- |Find all compatible compositions of a list of variable binding---  modifiers for a given set of supplied bound variables.-findCompositions :: (Eq a) => [VarBindingModify a b] -> [a]-    -> [VarBindingModify a b]-findCompositions vbms vars =-    mapMaybe (composeCheckSequence vars) (permutations vbms)---- |Compose sequence of variable binding modifiers, and check---  that the result can be used compatibly with a supplied list---  of bound variables, returning @Just (composed modifier)@,---  or @Nothing@.----composeCheckSequence :: (Eq a) => [a] -> [VarBindingModify a b]-    -> Maybe (VarBindingModify a b)-composeCheckSequence vars vbms = useWith vars $ composeSequence vbms-    where-        --  Check that a Maybe modifier is compatible for use with an-        --  indicated set of bound variables, and return (Just modifier)-        --  or Nothing.-        useWith _    Nothing    = Nothing-        useWith vs v@(Just vbm)-            | isJust $ vbmCompatibility vbm vs = v-            | otherwise                        = Nothing---- |Compose sequence of variable binding modifiers.----composeSequence :: (Eq a) => [VarBindingModify a b]-    -> Maybe (VarBindingModify a b)-composeSequence [] = Just varBindingId-composeSequence (vbm:vbms) = foldl' composePair (Just vbm) vbms---- |Compose a pair of variable binding modifiers, returning---  @Just (composed modifier)@, or @Nothing@.----composePair :: (Eq a) => Maybe (VarBindingModify a b) -> VarBindingModify a b-    -> Maybe (VarBindingModify a b)-composePair Nothing     _    = Nothing-composePair (Just vbm1) vbm2 = vbmCompose vbm1 vbm2---- |Return @Just a@ compatible composition of variable binding modifiers---  for a given set of supplied bound variables, or @Nothing@ if there---  is no compatible composition----findComposition :: (Eq a) => [VarBindingModify a b] -> [a]-    -> Maybe (VarBindingModify a b)-findComposition = listToMaybe `c2` findCompositions-    where-        c2 = (.) . (.)  -- compose with function of two arguments+nullRDFVarBinding :: RDFVarBinding+nullRDFVarBinding = nullVarBinding --- |Variable binding modifier that returns exactly those---  variable bindings presented.----varBindingId :: VarBindingModify a b-varBindingId = VarBindingModify-    { vbmName   = swishName "varBindingId"-    , vbmApply  = id-    , vbmVocab  = []-    , vbmUsage  = [[]]-    }+-- |Define type of query binding modifier for RDF graph inference+type RDFVarBindingModify = VarBindingModify RDFLabel RDFLabel --- |Null variable binding modifier------  This is like 'varBindingId' except parameterized by some labels.---  I think this is redundant, and should be eliminated.+-- |Open variable binding modifier that operates on RDFLabel values ---nullVarBindingModify :: OpenVarBindingModify a b-nullVarBindingModify lbs = VarBindingModify-    { vbmName   = swishName "nullVarBindingModify"-    , vbmApply  = id-    , vbmVocab  = lbs-    , vbmUsage  = [[]]-    }+type RDFOpenVarBindingModify = OpenVarBindingModify RDFLabel RDFLabel ----------------------------------------------------------------  Query binding filters-------------------------------------------------------------+-- |Define type for lookup map of open query binding modifiers+type RDFOpenVarBindingModifyMap = LookupMap RDFOpenVarBindingModify --- |VarBindingFilter is a function type that tests to see if---  a query binding satisfies some criterion.+-- |@RDFVarBindingFilter@ is a function type that tests to see if+--  a query binding satisfies some criterion, and is used to+--  create a variable binding modifier that simply filers+--  given variable bindings. -- --  Queries often want to apply some kind of filter or condition --  to the variable bindings that are processed.  In inference rules,@@ -432,92 +77,88 @@ --  the things that are matched. -- --  This function type is used to perform such tests.---  A number of simple implementations are included below.-data VarBindingFilter a b = VarBindingFilter-    { vbfName   :: ScopedName-    , vbfVocab  :: [a]-    , vbfTest   :: VarBinding a b -> Bool-    }+--  A number of simple implementations are included.+--+type RDFVarBindingFilter = VarBindingFilter RDFLabel RDFLabel --- |Make a variable binding modifier from a variable binding filter value.-makeVarFilterModify :: VarBindingFilter a b -> VarBindingModify a b-makeVarFilterModify vbf = VarBindingModify-    { vbmName   = vbfName vbf-    , vbmApply  = filter (vbfTest vbf)-    , vbmVocab  = vbfVocab vbf-    , vbmUsage  = [[]]-    }+------------------------------------------------------------+--  Declare some query binding filters+------------------------------------------------------------ --- |Make a variable test filter for a named variable using a---  supplied value testing function.-makeVarTestFilter ::-    ScopedName -> (b -> Bool) -> a -> VarBindingFilter a b-makeVarTestFilter nam vtest var = VarBindingFilter-    { vbfName   = nam-    , vbfVocab  = [var]-    , vbfTest   = \vb -> case vbMap vb var of-                    Just val  -> vtest val-                    _         -> False-    }+-- |This function generates a query binding filter that ensures+--  an indicated variable is bound to a URI reference.+rdfVarBindingUriRef :: RDFLabel -> RDFVarBindingFilter+rdfVarBindingUriRef =+    makeVarTestFilter (swishName "rdfVarBindingUriRef") isUri --- |Make a variable comparison filter for named variables using---  a supplied value comparison function.-makeVarCompareFilter ::-    ScopedName -> (b -> b -> Bool) -> a -> a -> VarBindingFilter a b-makeVarCompareFilter nam vcomp v1 v2 = VarBindingFilter-    { vbfName   = nam-    , vbfVocab  = [v1,v2]-    , vbfTest   = \vb -> case (vbMap vb v1,vbMap vb v2) of-                    (Just val1, Just val2) -> vcomp val1 val2-                    _                      -> False-    }+-- |This function generates a query binding filter that ensures+--  an indicated variable is bound to a blank node.+rdfVarBindingBlank :: RDFLabel -> RDFVarBindingFilter+rdfVarBindingBlank =+    makeVarTestFilter (swishName "rdfVarBindingBlank") isBlank ----------------------------------------------------------------  Declare some generally useful query binding filters-------------------------------------------------------------+-- |This function generates a query binding filter that ensures+--  an indicated variable is bound to a literal value.+rdfVarBindingLiteral :: RDFLabel -> RDFVarBindingFilter+rdfVarBindingLiteral =+    makeVarTestFilter (swishName "rdfVarBindingLiteral") isLiteral --- |This function generates a query binding filter that ensures that---  two indicated query variables are mapped to the same value.-varFilterEQ :: (Eq b) => a -> a -> VarBindingFilter a b-varFilterEQ =-    makeVarCompareFilter (swishName "varFilterEQ") (==) +-- |This function generates a query binding filter that ensures+--  an indicated variable is bound to an untyped literal value.+rdfVarBindingUntypedLiteral :: RDFLabel -> RDFVarBindingFilter+rdfVarBindingUntypedLiteral =+    makeVarTestFilter (swishName "rdfVarBindingUntypedLiteral") isUntypedLiteral --- |This function generates a query binding filter that ensures that---  two indicated query variables are mapped to different values.-varFilterNE :: (Eq b) => a -> a -> VarBindingFilter a b-varFilterNE =-    makeVarCompareFilter (swishName "varFilterNE") (/=) +-- |This function generates a query binding filter that ensures+--  an indicated variable is bound to a typed literal value.+rdfVarBindingTypedLiteral :: RDFLabel -> RDFVarBindingFilter+rdfVarBindingTypedLiteral =+    makeVarTestFilter (swishName "rdfVarBindingTypedLiteral") isTypedLiteral --- |This function composes a number of query binding filters---  into a composite filter that accepts any query binding that---  satisfies at least one of the component values.-varFilterDisjunction :: (Eq a) => [VarBindingFilter a b]-    -> VarBindingFilter a b-varFilterDisjunction vbfs = VarBindingFilter-    { vbfName   = swishName "varFilterDisjunction"-    , vbfVocab  = foldl1 union (map vbfVocab vbfs)-    , vbfTest   = or . flist (map vbfTest vbfs)-    }+-- |This function generates a query binding filter that ensures+--  an indicated variable is bound to an XML literal value.+rdfVarBindingXMLLiteral :: RDFLabel -> RDFVarBindingFilter+rdfVarBindingXMLLiteral =+    makeVarTestFilter (swishName "rdfVarBindingXMLLiteral") isXMLLiteral --- |This function composes a number of query binding filters---  into a composite filter that accepts any query binding that---  satisfies all of the component values.------  The same function could be achieved by composing the component---  filter-based modifiers, but this function is more convenient---  as it avoids the need to check for modifier compatibility.+-- |This function generates a query binding filter that ensures+--  an indicated variable is bound to container membership property.+rdfVarBindingMemberProp :: RDFLabel -> RDFVarBindingFilter+rdfVarBindingMemberProp =+    makeVarTestFilter (swishName "rdfVarBindingMemberProp") isMemberProp++-- |This function generates a query binding filter that ensures+--  an indicated variable is bound to a literal value with a+--  datatype whose URI is bound to another node ---varFilterConjunction :: (Eq a) => [VarBindingFilter a b]-    -> VarBindingFilter a b-varFilterConjunction vbfs = VarBindingFilter-    { vbfName   = swishName "varFilterConjunction"-    , vbfVocab  = foldl1 union (map vbfVocab vbfs)-    , vbfTest   = and . flist (map vbfTest vbfs)+rdfVarBindingDatatyped ::+  RDFLabel    -- ^ variable bound to the required datatype. +  -> RDFLabel -- ^ variable bound to the literal node to be tested.+  -> RDFVarBindingFilter+rdfVarBindingDatatyped dvar lvar = VarBindingFilter+    { vbfName   = swishName "rdfVarBindingDatatyped"+    , vbfVocab  = [dvar,lvar]+    , vbfTest   = \vb -> testDatatyped vb dvar lvar     } +testDatatyped :: RDFVarBinding -> RDFLabel -> RDFLabel -> Bool+testDatatyped vb dvar lvar = and+        [ isUri dtype+        , isDatatyped dqnam $ applyVarBinding vb lvar+        ]+        where+            dtype = applyVarBinding vb dvar+            -- NOTE: dqnam is not evaluated unless (isUri dtype)+            --       but add in a _ handler to appease -Wall+            -- dqnam = case dtype of { (Res x) -> x }+            dqnam = case dtype of+              Res x -> x+              _ -> error $ "dqnam should not be evaluated with " ++ show dtype+ -------------------------------------------------------------------------------- -----  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke   --  All rights reserved. -- --  This file is part of Swish.
src/Swish/RDF/Vocabulary.hs view
@@ -30,18 +30,27 @@     , namespaceDAML     , namespaceDefault     , namespaceSwish -    , namespaceLang+     -- ** RDF rules                                          -- | The namespaces refer to RDF rules and axioms.                                          , scopeRDF     , scopeRDFS     , scopeRDFD++    -- * Language tags+    --+    -- | Support for language tags that follow RFC 3066.+    -- +    -- This replaces the use of @ScopedName@ and @langName@, @langTag@,+    -- and @isLang@ in versions prior to @0.7.0.0@.+    --+    , LanguageTag+    , toLangTag+    , fromLangTag+    , isBaseLang          -- * Miscellaneous routines-    , langName, langTag, isLang     , swishName-      -    -- * Miscellaneous          , rdfdGeneralRestriction     , rdfdOnProperties, rdfdConstraint, rdfdMaxCardinality     , logImplies@@ -54,16 +63,23 @@     ) where +import Swish.Namespace (Namespace, ScopedName, makeNamespace, makeNSScopedName)+import Swish.QName (LName, getLName)+ import Swish.RDF.Vocabulary.RDF import Swish.RDF.Vocabulary.OWL import Swish.RDF.Vocabulary.XSD -import Swish.Utils.Namespace (Namespace, makeNamespace, ScopedName, getScopeLocal, getScopeNamespace, makeNSScopedName)-+import Data.Char (isDigit, isAsciiLower)+import Data.List (isPrefixOf)+import Data.List.NonEmpty (NonEmpty(..)) import Data.Monoid (mappend, mconcat)-import Data.Maybe (fromMaybe)+import Data.Maybe (fromJust, fromMaybe)+import Data.String (IsString(..))+ import Network.URI (URI, parseURI) +import qualified Data.List.NonEmpty as NE import qualified Data.Text as T  ------------------------------------------------------------@@ -82,12 +98,14 @@  -- | Create a namespace for the datatype family schema used by Swish. namespaceXsdType ::-  T.Text  -- ^ lbl+  LName        -- ^ local name   -> Namespace    -- ^ Namespace has prefix @xsd_lbl@ and   -- URI of @http:\/\/id.ninebynine.org\/2003\/XMLSchema\/lbl#@.-namespaceXsdType dtn = toNS ("xsd_" `mappend` dtn)-                       (mconcat ["http://id.ninebynine.org/2003/XMLSchema/", dtn, "#"])+namespaceXsdType lbl = +    let dtn = getLName lbl+    in toNS ("xsd_" `mappend` dtn)+           (mconcat ["http://id.ninebynine.org/2003/XMLSchema/", dtn, "#"])  -- | Maps @rdfd@ to @http:\/\/id.ninebynine.org\/2003\/rdfext\/rdfd#@. namespaceRDFD :: Namespace@@ -113,26 +131,20 @@ namespaceDefault :: Namespace namespaceDefault = toNSU "default" namespaceDefaultURI --- | Maps @lang@ to @http:\/\/id.ninebynine.org\/2003\/Swish\/Lang\/@.-namespaceLang :: Namespace-namespaceLang    = toNSU "lang"   namespaceLangURI-- tU :: String -> URI tU = fromMaybe (error "Internal error processing namespace URI") . parseURI  namespaceRDFDURI,    namespaceLOGURI,   namespaceSwishURI, -  namespaceLangURI, namespaceDefaultURI :: URI+  namespaceDefaultURI :: URI namespaceRDFDURI  = tU "http://id.ninebynine.org/2003/rdfext/rdfd#" namespaceLOGURI   = tU "http://www.w3.org/2000/10/swap/log#" namespaceSwishURI = tU "http://id.ninebynine.org/2003/Swish/"-namespaceLangURI  = tU "http://id.ninebynine.org/2003/Swish/Lang/" -- To be replaced by urn:ietf:params:lang?   namespaceDefaultURI = tU "http://id.ninebynine.org/default/"  -- | Convert a local name to a scoped name in the @swish@ namespace (`namespaceSwish`).-swishName :: T.Text -> ScopedName+swishName :: LName -> ScopedName swishName = makeNSScopedName namespaceSwish  -----------------------------------------------------------@@ -145,22 +157,103 @@ --  Fortunately, they do not currently need to appear in Notation3 as --  distinct labels (but future developments may change that). --- | Convert the label to a scoped name in the @lang@ namespace (`namespaceLang`).-langName :: -  T.Text  -- ^ The lower-case version of this label is used.-  -> ScopedName-langName = makeNSScopedName namespaceLang . T.toLower+-- | Represent the language tag for a literal string, following+-- RFC 3066 <http://www.ietf.org/rfc/rfc3066.txt>.+--+-- Use 'toLangTag' to create a tag and 'fromLangTag' to+-- convert back. The case is preserved for the tag, although+-- comparison (both the 'Eq' instance and 'compareLangTag')+-- is done using the lower-case form of the tags.+--+-- As an example:+--+-- > Prelude> :set prompt "swish> "+-- > swish> :set -XOverloadedStrings+-- > swish> :m + Swish.RDF.Vocabulary+-- > swish> let en = "en" :: LanguageTag+-- > swish> let us = "en-us" :: LanguageTag+-- > swish> let gb = "en-GB" :: LanguageTag+-- > swish> gb+-- > en-GB+-- > swish> gb == "en-gb"+-- > True+-- > swish> en == us+-- > False+-- > swish> en `isBaseLang` us+-- > True+-- > swish> us `isBaseLang` en+-- > False+-- > swish> us `isBaseLang` gb+-- > False+--+data LanguageTag = +    LanguageTag T.Text (NonEmpty T.Text)+    -- store full value, then the tags --- | Get the name of the language tag (note that the result is--- only guaranteed to be semantically valid if 'isLang' returns @True@--- but that there is no enforcement of this requirement).-langTag :: ScopedName -> T.Text-langTag = getScopeLocal+instance Show LanguageTag where+    show = T.unpack . fromLangTag --- | Is the scoped name in the `namespaceLang` namespace?-isLang :: ScopedName -> Bool-isLang sname = getScopeNamespace sname == namespaceLang+-- | The 'IsString' instance is not total since it will fail+-- given a syntactically-invalid language tag.+instance IsString LanguageTag where+    fromString = fromJust . toLangTag . T.pack +-- | The equality test matches on the full definition, so+-- @en-GB@ does not match @en@. See also 'isBaseLang'.+instance Eq LanguageTag where+    LanguageTag _ t1 == LanguageTag _ t2 = t1 == t2++-- | Create a 'LanguageTag' element from the label.+-- +-- Valid tags follow the ABNF from RCF 3066, which is+--+-- >   Language-Tag = Primary-subtag *( "-" Subtag )+-- >   Primary-subtag = 1*8ALPHA+-- >   Subtag = 1*8(ALPHA / DIGIT)+--+-- There are no checks that the primary or secondary sub tag+-- values are defined in any standard, such as ISO 639,+-- or obey any other syntactical restriction than given above.+-- +toLangTag :: T.Text -> Maybe LanguageTag+toLangTag lbl = +    let tag = T.toLower lbl+        toks = T.split (=='-') tag+    in if all (\s -> let l = T.length s in l > 0 && l < 9) toks+       then let primtag : subtags = toks+            in if T.all isAsciiLower primtag && all (T.all (\c -> isAsciiLower c || isDigit c)) subtags+               then Just $ LanguageTag lbl (NE.fromList toks)+               else Nothing+       else Nothing++-- | Convert a language tag back into text form.+fromLangTag :: LanguageTag -> T.Text+fromLangTag (LanguageTag f _) = f++-- | Compare language tags using the Language-range specification+-- in section 2.5 of RFC 3066.+--+-- 'True' is returned if the comparison tag is the same as, or+-- matches a prefix of, the base tag (where the match must be+-- over complete sub tags).+--+-- Note that +--+-- > l1 `isBaseLang` l2 == l2 `isBaseLang` l1+--+-- only when+--+-- > l1 == l2+--+isBaseLang :: +    LanguageTag     -- ^ base language+    -> LanguageTag  -- ^ comparison language+    -> Bool+isBaseLang (LanguageTag _ (a :| as)) +               (LanguageTag _ (b :| bs))+                   | a == b    = as `isPrefixOf` bs+                   | otherwise = False+ ------------------------------------------------------------ --  Define namespaces for RDF rules, axioms, etc ------------------------------------------------------------@@ -181,7 +274,7 @@ --  Define some common vocabulary terms ------------------------------------------------------------ -toRDFD :: T.Text -> ScopedName+toRDFD :: LName -> ScopedName toRDFD = makeNSScopedName namespaceRDFD  -- | @rdfd:GeneralRestriction@.
src/Swish/RDF/Vocabulary/DublinCore.hs view
@@ -183,7 +183,8 @@            ) where -import Swish.Utils.Namespace (Namespace, makeNamespace, ScopedName, makeNSScopedName)+import Swish.Namespace (Namespace, makeNamespace, ScopedName, makeNSScopedName)+import Swish.QName (LName)  import Data.Maybe (fromMaybe) import Network.URI (parseURI)@@ -217,16 +218,10 @@ --  Terms ------------------------------------------------------------ -toDCT :: T.Text -> ScopedName+toDCT, toDCE, toDCAM, toDCTYPE :: LName -> ScopedName toDCT = makeNSScopedName namespaceDCTERMS--toDCE :: T.Text -> ScopedName toDCE = makeNSScopedName namespaceDCELEM--toDCAM :: T.Text -> ScopedName toDCAM = makeNSScopedName namespaceDCAM--toDCTYPE :: T.Text -> ScopedName toDCTYPE = makeNSScopedName namespaceDCTYPE  -- Classes
src/Swish/RDF/Vocabulary/FOAF.hs view
@@ -112,13 +112,12 @@     ) where -import Swish.Utils.Namespace (Namespace, makeNamespace, ScopedName, makeNSScopedName)+import Swish.Namespace (Namespace, makeNamespace, ScopedName, makeNSScopedName)+import Swish.QName (LName)  import Data.Maybe (fromMaybe) import Network.URI (URI, parseURI) -import qualified Data.Text as T- ------------------------------------------------------------ --  Namespace ------------------------------------------------------------@@ -134,7 +133,7 @@ --  Terms ------------------------------------------------------------ -toF :: T.Text -> ScopedName+toF :: LName -> ScopedName toF  = makeNSScopedName namespaceFOAF  -- Classes
src/Swish/RDF/Vocabulary/Geo.hs view
@@ -35,13 +35,12 @@     ) where -import Swish.Utils.Namespace (Namespace, makeNamespace, ScopedName, makeNSScopedName)+import Swish.Namespace (Namespace, ScopedName, makeNamespace, makeNSScopedName)+import Swish.QName (LName)  import Data.Maybe (fromMaybe) import Network.URI (URI, parseURI) -import qualified Data.Text as T- ------------------------------------------------------------ --  Namespace ------------------------------------------------------------@@ -57,7 +56,7 @@ --  Terms ------------------------------------------------------------ -toG :: T.Text -> ScopedName+toG :: LName -> ScopedName toG  = makeNSScopedName namespaceGEO  -- | @geo:location@.
src/Swish/RDF/Vocabulary/OWL.hs view
@@ -49,13 +49,12 @@     ) where -import Swish.Utils.Namespace (Namespace, makeNamespace, ScopedName, makeNSScopedName)+import Swish.Namespace (Namespace, ScopedName, makeNamespace, makeNSScopedName)+import Swish.QName (LName)  import Data.Maybe (fromMaybe) import Network.URI (URI, parseURI) -import qualified Data.Text as T- ------------------------------------------------------------ --  Namespace ------------------------------------------------------------@@ -71,7 +70,7 @@ --  Terms ------------------------------------------------------------ -toO :: T.Text -> ScopedName+toO :: LName -> ScopedName toO = makeNSScopedName namespaceOWL  -- | @owl:sameAs@.
src/Swish/RDF/Vocabulary/Provenance.hs view
@@ -72,13 +72,12 @@     ) where -import Swish.Utils.Namespace (Namespace, makeNamespace, ScopedName, makeNSScopedName)+import Swish.Namespace (Namespace, makeNamespace, ScopedName, makeNSScopedName)+import Swish.QName (LName)  import Data.Maybe (fromMaybe) import Network.URI (URI, parseURI) -import qualified Data.Text as T- ------------------------------------------------------------ --  Namespace ------------------------------------------------------------@@ -94,7 +93,7 @@ --  Terms ------------------------------------------------------------ -toS :: T.Text -> ScopedName+toS :: LName -> ScopedName toS  = makeNSScopedName namespacePROV  -- Classes
src/Swish/RDF/Vocabulary/RDF.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE OverloadedStrings #-}+{-# Language OverloadedStrings #-}  -------------------------------------------------------------------------------- --  See end of this file for licence information.@@ -101,9 +101,12 @@     ) where -import Swish.Utils.Namespace (Namespace, makeNamespace, ScopedName, makeNSScopedName)+import Swish.Namespace (Namespace, makeNamespace, ScopedName, makeNSScopedName)+import Swish.QName (LName, newLName) -import Data.Maybe (fromMaybe)+import Data.Maybe (fromMaybe, fromJust)+import Data.Word (Word32)+ import Network.URI (URI, parseURI)  import qualified Data.Text as T@@ -128,7 +131,7 @@ --  Terms ------------------------------------------------------------ -toRDF, toRDFS :: T.Text -> ScopedName+toRDF, toRDFS :: LName -> ScopedName toRDF  = makeNSScopedName namespaceRDF toRDFS = makeNSScopedName namespaceRDFS @@ -169,8 +172,13 @@ rdfNodeID = toRDF "nodeID"  -- | Create a @rdf:_n@ entity.-rdfn :: Int -> ScopedName-rdfn = toRDF . T.pack . ("_" ++) . show+--+-- There is no check that the argument is not 0, so it is+-- possible to create the un-defined label @rdf:_0@.+rdfn :: +    Word32+    -> ScopedName+rdfn = toRDF . fromJust . newLName . T.pack . ("_" ++) . show  -- | @rdf:_1@. rdf1 :: ScopedName
src/Swish/RDF/Vocabulary/SIOC.hs view
@@ -107,13 +107,12 @@     ) where -import Swish.Utils.Namespace (Namespace, makeNamespace, ScopedName, makeNSScopedName)+import Swish.Namespace (Namespace, makeNamespace, ScopedName, makeNSScopedName)+import Swish.QName (LName)  import Data.Maybe (fromMaybe) import Network.URI (URI, parseURI) -import qualified Data.Text as T- ------------------------------------------------------------ --  Namespace ------------------------------------------------------------@@ -129,7 +128,7 @@ --  Terms ------------------------------------------------------------ -toS :: T.Text -> ScopedName+toS :: LName -> ScopedName toS  = makeNSScopedName namespaceSIOC  -- Classes
src/Swish/RDF/Vocabulary/XSD.hs view
@@ -62,13 +62,12 @@     ) where -import Swish.Utils.Namespace (Namespace, makeNamespace, ScopedName, makeNSScopedName)+import Swish.Namespace (Namespace, ScopedName, makeNamespace, makeNSScopedName)+import Swish.QName (LName)  import Data.Maybe (fromMaybe) import Network.URI (URI, parseURI) -import qualified Data.Text as T- ------------------------------------------------------------ --  Namespace ------------------------------------------------------------@@ -85,7 +84,7 @@ ------------------------------------------------------------  -- | Create a scoped name for an XSD datatype with the given name.-xsdType :: T.Text -> ScopedName+xsdType :: LName -> ScopedName xsdType = makeNSScopedName namespaceXSD  -- | @xsd:string@ from <http://www.w3.org/TR/xmlschema-2/#string>.
+ src/Swish/Rule.hs view
@@ -0,0 +1,253 @@+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}++--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  Rule+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke+--  License     :  GPL V2+--+--  Maintainer  :  Douglas Burke+--  Stability   :  experimental+--  Portability :  MultiParamTypeClasses, OverloadedStrings+--+--  This module defines a framework for defining inference rules+--  over some expression form.  It is intended to be used with+--  RDF graphs, but the structures aim to be quite generic with+--  respect to the expression forms allowed.+--+--------------------------------------------------------------------------------++module Swish.Rule+       ( Expression(..), Formula(..), Rule(..), RuleMap+       , nullScope, nullSN, nullFormula, nullRule+       , fwdCheckInference, bwdCheckInference+       , showsFormula, showsFormulae, showsWidth+       )+       where++import Swish.Namespace (Namespace, ScopedName)+import Swish.Namespace (makeNamespace, makeNSScopedName)+import Swish.QName (LName)++import Data.LookupMap (LookupEntryClass(..), LookupMap(..))+import Data.Maybe (fromJust)+import Data.String.ShowLines (ShowLines(..))++import Network.URI (URI, parseURI)++------------------------------------------------------------+--  Expressions+------------------------------------------------------------++-- |Expression is a type class for values over which proofs+--  may be constructed.+class (Eq ex) => Expression ex where+    -- |Is expression true in all interpretations?+    --  If so, then its truth is assumed without justification.+    isValid :: ex -> Bool++------------------------------------------------------------+--  Formula:  a named expression+------------------------------------------------------------++-- | A Formula is a named expression.+data Formula ex = Formula+    { formName :: ScopedName        -- ^ Name used for formula in proof chain+    , formExpr :: ex                -- ^ Named formula value+    } deriving Show++-- |Define equality of formulae as equality of formula names+instance Eq (Formula ex) where+    f1 == f2 = formName f1 == formName f2++-- |Define ordering of formulae based on formula names+instance Ord (Formula ex) where+    f1 <= f2 = formName f1 <= formName f2++instance LookupEntryClass (Formula ex) ScopedName (Formula ex)+    where+    newEntry (_,form) = form+    keyVal form = (formName form, form)++-- | The namespace @http:\/\/id.ninebynine.org\/2003\/Ruleset\/null@ with the prefix @null:@.+nullScope :: Namespace+nullScope = makeNamespace (Just "null") nullScopeURI++-- | Create a scoped name with the null namespace.+nullSN :: +  LName -- ^ local name.+  -> ScopedName+nullSN = makeNSScopedName nullScope++tU :: String -> URI+tU = fromJust . parseURI++nullScopeURI :: URI+nullScopeURI = tU "http://id.ninebynine.org/2003/Ruleset/null"++-- | The null formula.+nullFormula :: Formula ex+nullFormula = Formula+    { formName = makeNSScopedName nullScope "nullFormula"+    , formExpr = error "Null formula"+    }++-- testf1 = Formula "f1" ('f',1)+-- testf2 = Formula "f2" ('f',2)++-- |Return a displayable form of a list of labelled formulae+showsFormulae :: +  (ShowLines ex) +  => String        -- ^ newline+  -> [Formula ex]  -- ^ the formulae to show+  -> String        -- ^ text to be placed after the formulae+  -> ShowS+showsFormulae _       []     _     = id+showsFormulae newline [f]    after = showsFormula  newline f .+                                     showString    after+showsFormulae newline (f:fs) after = showsFormula  newline f .+                                     showString    newline .+                                     showsFormulae newline fs after++-- |Create a displayable form of a labelled formula+showsFormula :: +  (ShowLines ex) +  => String      -- ^ newline+  -> Formula ex  -- ^ formula+  -> ShowS+showsFormula newline f =+    showsWidth 16 ("["++show (formName f)++"] ") .+    showls (newline ++ replicate 16 ' ') (formExpr f)++------------------------------------------------------------+--  Rule+------------------------------------------------------------++-- |Rule is a data type for inference rules that can be used+--  to construct a step in a proof.+data Rule ex = Rule+    {+      -- |Name of rule, for use when displaying a proof+      ruleName :: ScopedName,+      +      -- |Forward application of a rule, takes a list of+      --  expressions and returns a list (possibly empty)+      --  of forward applications of the rule to combinations+      --  of the antecedent expressions.+      --  Note that all of the results returned can be assumed to+      --  be (simultaneously) true, given the antecedents provided.+      fwdApply :: [ex] -> [ex],+      +      -- |Backward application of a rule, takes an expression+      --  and returns a list of alternative antecedents, each of+      --  which is a list of expressions that jointly yield the+      --  given consequence through application of the inference+      --  rule.  An empty list is returned if no antecedents+      --  will allow the consequence to be inferred.+      bwdApply :: ex -> [[ex]],+      +      -- |Inference check.  Takes a list of antecedent expressions+      --  and a consequent expression, returning True if the+      --  consequence can be obtained from the antecedents by+      --  application of the rule.  When the antecedents and+      --  consequent are both given, this is generally more efficient+      --  that using either forward or backward chaining.+      --  Also, a particular rule may not fully support either+      --  forward or backward chaining, but all rules are required+      --  to fully support this function.+      --+      --  A default implementation based on forward chaining is+      --  given below.+      checkInference :: [ex] -> ex -> Bool +    }++-- |Define equality of rules as equality of the rule names.+instance Eq (Rule ex) where+    r1 == r2 = ruleName r1 == ruleName r2++-- |Define ordering of rules based on the rule names.+instance Ord (Rule ex) where+    r1 <= r2 = ruleName r1 <= ruleName r2++instance Show (Rule ex) where+    show rl = "Rule "++show (ruleName rl)++instance LookupEntryClass (Rule ex) ScopedName (Rule ex)+    where+    newEntry (_,rule) = rule+    keyVal rule = (ruleName rule, rule)++-- | A 'LookupMap' for a 'Rule'.+type RuleMap ex = LookupMap (Rule ex)++-- | Checks that consequence is a result of+-- applying the rule to the antecedants.+fwdCheckInference :: +  (Eq ex) +  => Rule ex   -- ^ rule+  -> [ex]      -- ^ antecedants+  -> ex        -- ^ consequence+  -> Bool+fwdCheckInference rule ante cons =+    cons `elem` fwdApply rule ante++-- | Checks that the antecedants are all required+-- to create the consequence using the given rule.+bwdCheckInference ::+  (Eq ex) +  => Rule ex   -- ^ rule+  -> [ex]      -- ^ antecedants+  -> ex        -- ^ consequence+  -> Bool+bwdCheckInference rule ante cons = any checkAnts (bwdApply rule cons)+    where+        checkAnts = all (`elem` ante)++-- | The null rule.+nullRule :: Rule ex+nullRule = Rule+    { ruleName = makeNSScopedName nullScope "nullRule"+    , fwdApply = \ _ -> []+    , bwdApply = \ _ -> []+    , checkInference = \ _ _ -> False+    }++------------------------------------------------------------+--  Shows formatting support functions+-----------------------------------------------------------++-- |Show a string left justified in a field of at least the specified+--  number of characters width.+showsWidth :: Int -> String -> ShowS+showsWidth wid str more = str++replicate pad ' '++more+    where+        pad = wid - length str+++--------------------------------------------------------------------------------+--+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke+--  All rights reserved.+--+--  This file is part of Swish.+--+--  Swish is free software; you can redistribute it and/or modify+--  it under the terms of the GNU General Public License as published by+--  the Free Software Foundation; either version 2 of the License, or+--  (at your option) any later version.+--+--  Swish is distributed in the hope that it will be useful,+--  but WITHOUT ANY WARRANTY; without even the implied warranty of+--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+--  GNU General Public License for more details.+--+--  You should have received a copy of the GNU General Public License+--  along with Swish; if not, write to:+--    The Free Software Foundation, Inc.,+--    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+--+--------------------------------------------------------------------------------
+ src/Swish/Ruleset.hs view
@@ -0,0 +1,174 @@+{-# LANGUAGE MultiParamTypeClasses #-}++--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  Ruleset+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke+--  License     :  GPL V2+--+--  Maintainer  :  Douglas Burke+--  Stability   :  experimental+--  Portability :  MultiParamTypeClasses+--+--  This module defines a ruleset data type, used to collect information+--  about a ruleset that may contribute torwards inferences in RDF;+--  e.g. RDF and RDFS are rulesets.+--+--  A 'Ruleset' consists of a namespace, a collection of axioms, and+--  a collection of rules.+--+--------------------------------------------------------------------------------++module Swish.Ruleset+    ( Ruleset(..), RulesetMap+    , makeRuleset, getRulesetNamespace, getRulesetAxioms, getRulesetRules+    , getRulesetAxiom, getRulesetRule+    , getContextAxiom, getMaybeContextAxiom+    , getContextRule,  getMaybeContextRule+    )+where++import Swish.Namespace (Namespace, ScopedName)+import Swish.Rule (Formula(..), Rule(..))++{-+Used for the Show instance of Ruleset, which was+used for debugging but has been removed as not+really needed by the general user.++import Swish.Utils.ShowM (ShowM(..))+import Data.List (intercalate)+-}++import Data.LookupMap (LookupEntryClass(..), LookupMap(..), mapFindMaybe)+import Data.Maybe (fromMaybe, listToMaybe, mapMaybe)++-- | A Rule set.++data Ruleset ex = Ruleset+    { rsNamespace :: Namespace    -- ^ Namespace.+    , rsAxioms    :: [Formula ex] -- ^ Axioms.+    , rsRules     :: [Rule ex]    -- ^ Rules.+    }++{-++Used for debugging.++instance (ShowM ex) => Show (Ruleset ex) where+  show (Ruleset ns axs rls) = +    intercalate "\n" +    [ "Ruleset: " ++ show ns+    , "Axioms:" ]+    ++ (showsFormulae "\n" axs +       (intercalate "\n" ("Rules:" : map show rls))) ""+-}++-- | Ruleset comparisons are based only on their namespace components.+instance Eq (Ruleset ex) where+    r1 == r2 = rsNamespace r1 == rsNamespace r2++instance LookupEntryClass (Ruleset ex) Namespace (Ruleset ex)+    where+        keyVal   r@(Ruleset k _ _) = (k,r)+        newEntry (_,r)             = r++-- | A 'LookupMap' for a 'Ruleset'.+type RulesetMap ex = LookupMap (Ruleset ex)++-- | Create a ruleset.+makeRuleset :: Namespace -> [Formula ex] -> [Rule ex] -> Ruleset ex+makeRuleset nsp fms rls = Ruleset+    { rsNamespace = nsp+    , rsAxioms    = fms+    , rsRules     = rls+    }++-- | Extract the namespace of a ruleset.+getRulesetNamespace :: Ruleset ex -> Namespace+getRulesetNamespace = rsNamespace++-- | Extract the axioms from a ruleset.+getRulesetAxioms :: Ruleset ex -> [Formula ex]+getRulesetAxioms = rsAxioms++-- | Extract the rules from a ruleset.+getRulesetRules :: Ruleset ex -> [Rule ex]+getRulesetRules = rsRules++-- | Find a named axiom in a ruleset.+getRulesetAxiom :: ScopedName -> Ruleset ex -> Maybe (Formula ex)+getRulesetAxiom nam rset =+    mapFindMaybe nam (LookupMap (getRulesetAxioms rset))+    -- listToMaybe $ filter ( (matchName nam) . formName ) $ getRulesetAxioms rset++-- | Find a named rule in a ruleset. +getRulesetRule :: ScopedName -> Ruleset ex -> Maybe (Rule ex)+getRulesetRule nam rset =+    mapFindMaybe nam (LookupMap (getRulesetRules rset))+    -- listToMaybe $ filter ( (matchName nam) . ruleName ) $ getRulesetRules rset++-- | Find a named axiom in a proof context.+getContextAxiom :: +  ScopedName -- ^ Name of axiom.+  -> Formula ex -- ^ Default axiom (used if named component does not exist).+  -> [Ruleset ex] -- ^ Rulesets to search.+  -> Formula ex+getContextAxiom nam def rsets = fromMaybe def (getMaybeContextAxiom nam rsets)+    {-+    foldr (flip fromMaybe) def $ map (getRulesetAxiom nam) rsets+    -}++-- | Find a named axiom in a proof context.+getMaybeContextAxiom ::+  ScopedName -- ^ Name of axiom.+  -> [Ruleset ex] -- ^ Rulesets to search.+  -> Maybe (Formula ex)+getMaybeContextAxiom nam rsets =+    listToMaybe $ mapMaybe (getRulesetAxiom nam) rsets++-- | Find a named rule in a proof context.+getContextRule :: +  ScopedName -- ^ Name of rule.+  -> Rule ex -- ^ Default rule (used if named component does not exist).+  -> [Ruleset ex] -- ^ Rulesets to search.+  -> Rule ex+getContextRule nam def rsets = fromMaybe def (getMaybeContextRule nam rsets)+    {-+    foldr (flip fromMaybe) def $ map (getRulesetRule nam) rsets+    -}++-- | Find a named rule in a proof context.+getMaybeContextRule :: +  ScopedName -- ^ Name of rule.+  -> [Ruleset ex] -- ^ Rulesets to search.+  -> Maybe (Rule ex)+getMaybeContextRule nam rsets =+    listToMaybe $ mapMaybe (getRulesetRule nam) rsets++--------------------------------------------------------------------------------+--+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke+--  All rights reserved.+--+--  This file is part of Swish.+--+--  Swish is free software; you can redistribute it and/or modify+--  it under the terms of the GNU General Public License as published by+--  the Free Software Foundation; either version 2 of the License, or+--  (at your option) any later version.+--+--  Swish is distributed in the hope that it will be useful,+--  but WITHOUT ANY WARRANTY; without even the implied warranty of+--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+--  GNU General Public License for more details.+--+--  You should have received a copy of the GNU General Public License+--  along with Swish; if not, write to:+--    The Free Software Foundation, Inc.,+--    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+--+--------------------------------------------------------------------------------
+ src/Swish/Script.hs view
@@ -0,0 +1,1500 @@+{-# LANGUAGE OverloadedStrings #-}+--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+{- |+Module      :  Script+Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke+License     :  GPL V2++Maintainer  :  Douglas Burke+Stability   :  experimental+Portability :  OverloadedStrings++This module implements the Swish script processor:  it parses a script+from a supplied string, and returns a list of Swish state transformer+functions whose effect, when applied to a state value, is to implement+the supplied script.++-}++module Swish.Script+    ( +      -- * Syntax+      -- $syntax+      +      -- ** Defining a prefix+      -- $prefixLine+      +      -- ** Naming a graph+      -- $nameItem+      +      -- ** Reading and writing graphs+      +      -- $readGraph+      +      -- $writeGraph+      +      -- ** Merging graphs+      -- $mergeGraphs+      +      -- ** Comparing graphs+      +      -- $compareGraphs+      +      -- $assertEquiv+      +      -- $assertMember+      +      -- ** Defining rules+      +      -- $defineRule+      +      -- $defineRuleset+      +      -- $defineConstraints+      +      -- ** Apply a rule+      -- $fwdChain+      +      -- $bwdChain+      +      -- ** Define a proof+      -- $proof+      +      -- * An example script+      -- $exampleScript+      +      -- * Parsing+      +      parseScriptFromText +    )+where++import Swish.Datatype (typeMkRules)+import Swish.Monad ( SwishStateIO, SwishStatus(..), NamedGraph(..))+import Swish.Monad (modGraphs, findGraph, findFormula+                   , modRules, findRule+                   , modRulesets, findRuleset+                   , findOpenVarModify, findDatatype+                   , setInfo, setError, setStatus)+import Swish.Proof (explainProof, showsProof)+import Swish.Rule (Formula(..), Rule(..)) +import Swish.Ruleset (makeRuleset, getRulesetRule, getMaybeContextRule)+import Swish.VarBinding (composeSequence)++import Swish.RDF.Datatype (RDFDatatype)++import Swish.RDF.Ruleset (RDFFormula, RDFRule, RDFRuleset)+import Swish.RDF.Ruleset (makeRDFClosureRule)+import Swish.RDF.Proof (RDFProofStep)+import Swish.RDF.Proof (makeRDFProof, makeRDFProofStep)+import Swish.RDF.VarBinding (RDFVarBindingModify)++import Swish.RDF.GraphShowLines ()++import Swish.RDF.Graph+    ( RDFGraph, RDFLabel(..)+    , NamespaceMap+    , setNamespaces+    , merge, addGraphs+    )++import Swish.RDF.Parser.Utils (whiteSpace, lexeme, symbol, eoln, manyTill)++import Swish.RDF.Parser.N3+    ( parseAnyfromText+    , parseN3      +    , N3Parser, N3State(..)+    , getPrefix+    , subgraph+    , n3symbol -- was uriRef2,+    , quickVariable -- was varid+    , lexUriRef+    , newBlankNode+    )++import Swish.Namespace (ScopedName, getScopeNamespace)+import Swish.QName (QName, qnameFromURI)++import Swish.RDF.Formatter.N3 (formatGraphAsBuilder)++import Swish.Utils.ListHelpers (equiv, flist)++import qualified Data.Text.Lazy as L+import qualified Data.Text.Lazy.Builder as B+import qualified Data.Text.Lazy.IO as LIO+import Text.ParserCombinators.Poly.StateText++import Control.Monad (unless, when, liftM, void)+import Control.Monad.State (modify, gets, lift)++import Data.LookupMap (mapReplace)+import Data.Monoid (Monoid(..))++import Network.URI (URI(..))++import qualified System.IO.Error as IO+import qualified Control.Exception as CE++------------------------------------------------------------+--+--  The parser used to be based on the Notation3 parser, and used many+--  of the same syntax productions, but the top-level productions used+--  are quite different. With the parser re-write it's less clear+--  what is going on.+--+-- NOTE: during the parser re-write we strip out some of this functionality+-- ++-- | Parser for Swish script processor+parseScriptFromText :: +  Maybe QName -- ^ Default base for the script+  -> L.Text   -- ^ Swish script+  -> Either String [SwishStateIO ()]+parseScriptFromText = parseAnyfromText script ++----------------------------------------------------------------------+--  Syntax productions+----------------------------------------------------------------------++between :: Parser s lbr -> Parser s rbr -> Parser s a -> Parser s a+between = bracket++n3SymLex :: N3Parser ScopedName+n3SymLex = lexeme n3symbol++setTo :: N3Parser ()+setTo = isymbol ":-"++semicolon :: N3Parser ()+semicolon = isymbol ";"++comma :: N3Parser ()+comma = isymbol ","++commentText :: N3Parser String+commentText = semicolon *> restOfLine++script :: N3Parser [SwishStateIO ()]+script = do+  whiteSpace+  scs <- many command+  eof+  return scs++isymbol :: String -> N3Parser ()+isymbol = void . symbol++command :: N3Parser (SwishStateIO ())+command =+  prefixLine+  <|> nameItem+  <|> readGraph+  <|> writeGraph+  <|> mergeGraphs+  <|> compareGraphs+  <|> assertEquiv+  <|> assertMember+  <|> defineRule+  <|> defineRuleset+  <|> defineConstraints+  <|> checkProofCmd+  <|> fwdChain+  <|> bwdChain++prefixLine :: N3Parser (SwishStateIO ())+prefixLine = do+  -- try $ isymbol "@prefix"+  isymbol "@prefix"+  getPrefix+  whiteSpace+  isymbol "."+  return $ return ()++--  name :- graph+--  name :- ( graph* )+nameItem :: N3Parser (SwishStateIO ())+nameItem = +  ssAddGraph <$> n3SymLex <*> (symbol ":-" *> graphOrList)+  +maybeURI :: N3Parser (Maybe URI)+maybeURI = (Just <$> lexUriRef) <|> return Nothing++--  @read name  [ <uri> ]+readGraph :: N3Parser (SwishStateIO ())+readGraph = commandName "@read" *> (ssRead <$> n3SymLex <*> maybeURI)++--  @write name [ <uri> ] ; Comment+writeGraph :: N3Parser (SwishStateIO ())+writeGraph =+        do  { commandName "@write"+            ; n <- n3SymLex+            ; let gs = ssGetList n :: SwishStateIO (Either String [RDFGraph])+            ; muri <- maybeURI+            ; c <- commentText+            ; return $ ssWriteList muri gs c+            }++--  @merge ( name* ) => name+mergeGraphs :: N3Parser (SwishStateIO ())+mergeGraphs = do+  commandName "@merge"+  gs <- graphList+  isymbol "=>"+  n <- n3SymLex+  return $ ssMerge n gs++-- @compare  name name+compareGraphs :: N3Parser (SwishStateIO ())+compareGraphs =+  commandName "@compare" *> (ssCompare <$> n3SymLex <*> n3SymLex)+  +-- @<command> name name ; Comment+assertArgs :: (ScopedName -> ScopedName -> String -> SwishStateIO ())+              -> String -> N3Parser (SwishStateIO ())+assertArgs assertFunc cName = do+  commandName $ '@':cName+  assertFunc <$> n3SymLex <*> n3SymLex <*> commentText+      +--  @asserteq name name ; Comment+assertEquiv :: N3Parser (SwishStateIO ())+assertEquiv = assertArgs ssAssertEq "asserteq" +        +--  @assertin name name ; Comment              +assertMember :: N3Parser (SwishStateIO ())+assertMember = assertArgs ssAssertIn "assertin"+  +--  @rule name :- ( name* ) => name [ | ( (name var*)* ) ]               +defineRule :: N3Parser (SwishStateIO ())+defineRule =+        do  { commandName "@rule"+            ; rn <- n3SymLex+            ; setTo+            ; ags <- graphOrList+            ; isymbol "=>"+            ; cg  <- graphExpr+            ; vms <- varModifiers <|> pure []+            ; return $ ssDefineRule rn ags cg vms+            }++--  @ruleset name :- ( name* ) ; ( name* )+defineRuleset :: N3Parser (SwishStateIO ())+defineRuleset =+  commandName "@ruleset" *>      +  (ssDefineRuleset <$> n3SymLex <*> (setTo *> nameList) <*> (semicolon *> nameList))+  +--  @constraints pref :- ( name* ) | ( name* )+defineConstraints :: N3Parser (SwishStateIO ())+defineConstraints =+  commandName "@constraints" *>      +  (ssDefineConstraints <$> n3SymLex <*> (setTo *> graphOrList) <*> (symbol "|" *> nameOrList))+  +--  @proof name ( name* )+--    @input name+--    @step name ( name* ) => name  # rule-name, antecedents, consequent+--    @result name+checkProofCmd :: N3Parser (SwishStateIO ())+checkProofCmd =+        do  { commandName "@proof"+            ; pn  <- n3SymLex+            ; sns <- nameList+            ; commandName "@input"+            ; igf <- formulaExpr+            ; sts <- many checkStep+            ; commandName "@result"+            ; rgf <- formulaExpr+            ; return $ ssCheckProof pn sns igf sts rgf+            }++checkStep ::+    N3Parser (Either String [RDFRuleset]+                -> SwishStateIO (Either String RDFProofStep))+checkStep =+  commandName "@step" *>      +  (ssCheckStep <$> n3SymLex <*> formulaList <*> (symbol "=>" *> formulaExpr))++--  #   ruleset rule (antecedents) => result+--  @fwdchain pref name ( name* ) => name+fwdChain :: N3Parser (SwishStateIO ())+fwdChain =+        do  { commandName "@fwdchain"+            ; sn  <- n3SymLex+            ; rn  <- n3SymLex+            ; ags <- graphOrList+            ; isymbol "=>"+            ; cn  <- n3SymLex+            ; s <- stGet+            ; let prefs = prefixUris s+            ; return $ ssFwdChain sn rn ags cn prefs+            }++--  #   ruleset rule consequent <= (antecedent-alts)+--  @bwdchain pref name graph <= name+bwdChain :: N3Parser (SwishStateIO ())+bwdChain =+        do  { commandName "@bwdchain"+            ; sn  <- n3SymLex+            ; rn  <- n3SymLex+            ; cg  <- graphExpr+            ; isymbol "<="+            ; an  <- n3SymLex+            ; s <- stGet+            ; let prefs = prefixUris s+            ; return $ ssBwdChain sn rn cg an prefs+            }++----------------------------------------------------------------------+--  Syntax clause helpers+----------------------------------------------------------------------++-- TODO: is the loss of identLetter a problem?+commandName :: String -> N3Parser ()+-- commandName cmd = try (string cmd *> notFollowedBy identLetter *> whiteSpace)+commandName cmd = symbol cmd *> pure ()++restOfLine :: N3Parser String+restOfLine = manyTill (satisfy (const True)) eoln <* whiteSpace+  +br :: N3Parser a -> N3Parser a+br = between (symbol "(") (symbol ")")++nameList :: N3Parser [ScopedName]+nameList = br $ many n3SymLex+  +toList :: a -> [a]+toList = (:[])+           +nameOrList :: N3Parser [ScopedName]+nameOrList =+  (toList <$> n3SymLex)      +  <|> nameList+  +graphExpr :: N3Parser (SwishStateIO (Either String RDFGraph))+graphExpr =+        graphOnly+    <|>+        do  { f <- formulaExpr+            ; return $ liftM (liftM formExpr) f+            }++graphOnly :: N3Parser (SwishStateIO (Either String RDFGraph))+graphOnly =+        do  { isymbol "{"+            ; b <- newBlankNode+            ; g <- subgraph b+            ; isymbol "}"+            ; s <- stGet+            ; let gp = setNamespaces (prefixUris s) g+            ; return $ return (Right gp)+            }++graphList :: N3Parser [SwishStateIO (Either String RDFGraph)]+graphList = br (many graphExpr)++graphOrList :: N3Parser [SwishStateIO (Either String RDFGraph)]+graphOrList =+  (toList <$> graphExpr)+  <|> graphList++formulaExpr :: N3Parser (SwishStateIO (Either String RDFFormula))+formulaExpr = n3SymLex >>= namedGraph++namedGraph :: ScopedName -> N3Parser (SwishStateIO (Either String RDFFormula))+namedGraph n =+  (ssAddReturnFormula n <$> (setTo *> graphOnly))+  <|> return (ssGetFormula n)++formulaList :: N3Parser [SwishStateIO (Either String RDFFormula)]+formulaList = between (symbol "(") (symbol ")") (many formulaExpr)++varModifiers :: N3Parser [(ScopedName,[RDFLabel])]+varModifiers = symbol "|" *> varModList++varModList :: N3Parser [(ScopedName,[RDFLabel])]+varModList = +  br (sepBy varMod comma)+  <|> toList <$> lexeme varMod++varMod :: N3Parser (ScopedName,[RDFLabel])+varMod = (,) <$> n3SymLex <*> many (lexeme quickVariable)++----------------------------------------------------------------------+--  SwishState helper functions+----------------------------------------------------------------------+--+--  The functions below operate in the SwishStateIO monad, and are used+--  to assemble an executable version of the parsed script.++-- | Return a message to the user. At present the message begins with '# '+-- but this may be removed.+--+ssReport :: +  String  -- ^ message contents+  -> SwishStateIO ()+-- ssReport msg = lift $ putStrLn $ "# " ++ msg+ssReport msg = modify $ setInfo $ "# " ++ msg++ssReportLabel :: +  String     -- ^ label for the message+  -> String  -- ^ message contents+  -> SwishStateIO ()+ssReportLabel lbl msg = ssReport $ lbl ++ ": " ++ msg++ssAddReturnFormula ::+    ScopedName -> SwishStateIO (Either String RDFGraph)+    -> SwishStateIO (Either String RDFFormula)+ssAddReturnFormula nam gf =+        do  { egr <- gf+            ; ssAddGraph nam [return egr]+            ; return $ liftM (Formula nam) egr+            }++ssAddGraph ::+    ScopedName -> [SwishStateIO (Either String RDFGraph)]+    -> SwishStateIO ()+ssAddGraph nam gf =+    let errmsg = "Graph/list not added: "++show nam++"; "+    in+        do  { esg <- sequence gf        -- [Either String RDFGraph]+            ; let egs = sequence esg    -- Either String [RDFGraph]+            ; let fgs = case egs of+                    Left  er -> setError  (errmsg++er)+                    Right gs -> modGraphs (`mapReplace` NamedGraph nam gs)+            ; modify fgs+            }++ssGetGraph :: ScopedName -> SwishStateIO (Either String RDFGraph)+ssGetGraph nam = liftM head <$> ssGetList nam+  +ssGetFormula :: ScopedName -> SwishStateIO (Either String RDFFormula)+ssGetFormula nam = gets find+    where+        find st = case findFormula nam st of+            Nothing -> Left ("Formula not present: "++show nam)+            Just gr -> Right gr++ssGetList :: ScopedName -> SwishStateIO (Either String [RDFGraph])+ssGetList nam = gets find+    where+        find st = case findGraph nam st of+            Nothing  -> Left ("Graph or list not present: "++show nam)+            Just grs -> Right grs++ssRead :: ScopedName -> Maybe URI -> SwishStateIO ()+ssRead nam muri = ssAddGraph nam [ssReadGraph muri]++ssReadGraph :: Maybe URI -> SwishStateIO (Either String RDFGraph)+ssReadGraph muri = +  let gf inp = case inp of+        Left  es -> Left es+        Right is -> parseN3 is (muri >>= qnameFromURI)+        +  in gf `liftM` getResourceData muri++ssWriteList ::+    Maybe URI -> SwishStateIO (Either String [RDFGraph]) -> String+    -> SwishStateIO ()+ssWriteList muri gf comment = do+  esgs <- gf+  case esgs of+    Left  er   -> modify $ setError ("Cannot write list: "++er)+    Right []   -> putResourceData Nothing (B.fromLazyText (L.concat ["# ", L.pack comment, "\n+ Swish: Writing empty list"]))+    Right [gr] -> ssWriteGraph muri gr comment+    Right grs  -> mapM_ writegr (zip [(0::Int)..] grs)+      where+        writegr (n,gr) = ssWriteGraph (murin muri n) gr+                         ("["++show n++"] "++comment)+        murin Nothing    _ = Nothing+        murin (Just uri) n = +          let rp = reverse $ uriPath uri+              (rLastSet, rRest) = break (=='/') rp+              (before, after) = break (=='.') $ reverse rLastSet+              newPath = reverse rRest ++ "/" ++ before ++ show n ++ after+          in case rLastSet of+            "" -> error $ "Invalid URI (path ends in /): " ++ show uri+            _ -> Just $ uri { uriPath = newPath }+         +  ++{-+ssWrite ::+    Maybe String -> SwishStateIO (Either String RDFGraph) -> String+    -> SwishStateIO ()+ssWrite muri gf comment =+        do  { esg <- gf+            ; case esg of+                Left  er -> modify $ setError ("Cannot write graph: "++er)+                Right gr -> ssWriteGraph muri gr comment+            }+-}++ssWriteGraph :: Maybe URI -> RDFGraph -> String -> SwishStateIO ()+ssWriteGraph muri gr comment =+    putResourceData muri (c `mappend` formatGraphAsBuilder gr)+    where+        c = B.fromLazyText $ L.concat ["# ", L.pack comment, "\n"]++ssMerge ::+    ScopedName -> [SwishStateIO (Either String RDFGraph)]+    -> SwishStateIO ()+ssMerge nam gfs =+    let errmsg = "Graph merge not defined: "++show nam++"; "+    in+        do  { ssReportLabel "Merge" (show nam)+            ; esg <- sequence gfs       -- [Either String RDFGraph]+            ; let egs = sequence esg    -- Either String [RDFGraph]+            ; let fgs = case egs of+                    Left  er -> setError  (errmsg++er)+                    Right [] -> setError  (errmsg++"No graphs to merge")+                    Right gs -> modGraphs (`mapReplace` NamedGraph nam [g])+                            where g = foldl1 merge gs+            ; modify fgs+            }++ssCompare :: ScopedName -> ScopedName -> SwishStateIO ()+ssCompare n1 n2 =+        do  { ssReportLabel "Compare" (show n1 ++ " " ++ show n2)+            ; g1 <- ssGetGraph n1+            ; g2 <- ssGetGraph n2+            ; when (g1 /= g2) (modify $ setStatus SwishGraphCompareError)+            }++ssAssertEq :: ScopedName -> ScopedName -> String -> SwishStateIO ()+ssAssertEq n1 n2 comment =+    let er1 = ":\n  Graph or list compare not performed:  invalid graph/list."+    in+        do  { ssReportLabel "AssertEq" comment+            ; g1 <- ssGetList n1+            ; g2 <- ssGetList n2+            ; case (g1,g2) of+                (Left er,_) -> modify $ setError (comment++er1++"\n  "++er)+                (_,Left er) -> modify $ setError (comment++er1++"\n  "++er)+                (Right gr1,Right gr2) -> +                    unless (equiv gr1 gr2) $ modify $+                      setError (comment++":\n  Graph "++show n1+                                ++" differs from "++show n2++".")+            }++ssAssertIn :: ScopedName -> ScopedName -> String -> SwishStateIO ()+ssAssertIn n1 n2 comment =+    let er1 = ":\n  Membership test not performed:  invalid graph."+        er2 = ":\n  Membership test not performed:  invalid list."+    in+        do  { ssReportLabel "AssertIn" comment+            ; g1 <- ssGetGraph n1+            ; g2 <- ssGetList  n2+            ; case (g1,g2) of+                (Left er,_) -> modify $ setError (comment++er1++"\n  "++er)+                (_,Left er) -> modify $ setError (comment++er2++"\n  "++er)+                (Right gr,Right gs) ->+                    unless (gr `elem` gs) $ modify $+                    setError (comment++":\n  Graph "++show n1+                              ++" not a member of "++show n2)+            }++--  Note:  this is probably incomplete, though it should work in simple cases.+--  A complete solution would have the binding modifiers subject to+--  re-arrangement to suit the actual bound variables encountered.+--  See VarBinding.findCompositions and VarBinding.findComposition+--+--  This code should be adequate if variable bindings are always used+--  in combinations consisting of a single modifier followed by any number+--  of filters.+--+ssDefineRule ::+    ScopedName+    -> [SwishStateIO (Either String RDFGraph)]+    -> SwishStateIO (Either String RDFGraph)+    -> [(ScopedName,[RDFLabel])]+    -> SwishStateIO ()+ssDefineRule rn agfs cgf vmds =+    let errmsg1 = "Rule definition error in antecedent graph(s): "+        errmsg2 = "Rule definition error in consequent graph: "+        errmsg3 = "Rule definition error in variable modifier(s): "+        errmsg4 = "Incompatible variable binding modifier sequence"+    in+        do  { aesg <- sequence agfs     -- [Either String RDFGraph]+            ; let ags = sequence aesg   :: Either String [RDFGraph]+            ; cg <- cgf                 -- Either String RDFGraph+            ; let vmfs = map ssFindVarModify vmds+            ; evms <- sequence vmfs     -- [Either String RDFVarBindingModify]+            ; let vms = sequence evms   :: Either String [RDFVarBindingModify]+            ; let frl = case (ags,cg,vms) of+                    (Left er,_,_) -> setError (errmsg1++er)+                    (_,Left er,_) -> setError (errmsg2++er)+                    (_,_,Left er) -> setError (errmsg3++er)+                    (Right agrs,Right cgr,Right vbms) ->+                        let+                            newRule = makeRDFClosureRule rn agrs cgr+                        in+                        case composeSequence vbms of+                            Just vm -> modRules (`mapReplace` newRule vm)+                            Nothing -> setError errmsg4+            ; modify frl+            }++ssFindVarModify ::+    (ScopedName,[RDFLabel]) -> SwishStateIO (Either String RDFVarBindingModify)+ssFindVarModify (nam,lbs) = gets $ \st ->+  case findOpenVarModify nam st of+    Just ovbm -> Right (ovbm lbs)+    Nothing   -> Left  ("Undefined modifier: "++show nam)++ssDefineRuleset ::+    ScopedName+    -> [ScopedName]+    -> [ScopedName]+    -> SwishStateIO ()+ssDefineRuleset sn ans rns =+    let errmsg1 = "Error in ruleset axiom(s): "+        errmsg2 = "Error in ruleset rule(s): "+    in+        do  { let agfs = mapM ssGetFormula ans+                                        :: SwishStateIO [Either String RDFFormula]+            ; aesg <- agfs              -- [Either String RDFFormula]+            ; let eags = sequence aesg  :: Either String [RDFFormula]+            ; let erlf = mapM ssFindRule rns+                                        :: SwishStateIO [Either String RDFRule]+            ; rles <- erlf              -- [Either String RDFRule]+            ; let erls = sequence rles  :: Either String [RDFRule]+            ; let frs = case (eags,erls) of+                    (Left er,_) -> setError (errmsg1++er)+                    (_,Left er) -> setError (errmsg2++er)+                    (Right ags,Right rls) ->+                        modRulesets (`mapReplace` rs)+                        where+                            rs = makeRuleset (getScopeNamespace sn) ags rls+            ; modify frs+            }++ssFindRule :: ScopedName -> SwishStateIO (Either String RDFRule)+ssFindRule nam = gets find+    where+        find st = case findRule nam st of+            Nothing -> Left ("Rule not found: "++show nam)+            Just rl -> Right rl++ssDefineConstraints  ::+    ScopedName+    -> [SwishStateIO (Either String RDFGraph)]+    -> [ScopedName]+    -> SwishStateIO ()+ssDefineConstraints  sn cgfs dtns =+    let errmsg1 = "Error in constraint graph(s): "+        errmsg2 = "Error in datatype(s): "+    in+        do  { cges <- sequence cgfs     -- [Either String RDFGraph]+            ; let ecgs = sequence cges  :: Either String [RDFGraph]+            ; let ecgr = case ecgs of+                    Left er   -> Left er+                    Right []  -> Right mempty+                    Right grs -> Right $ foldl1 merge grs+            ; edtf <- mapM ssFindDatatype dtns+                                        -- [Either String RDFDatatype]+            ; let edts = sequence edtf   :: Either String [RDFDatatype]+            ; let frs = case (ecgr,edts) of+                    (Left er,_) -> setError (errmsg1++er)+                    (_,Left er) -> setError (errmsg2++er)+                    (Right cgr,Right dts) ->+                        modRulesets (`mapReplace` rs)+                        where+                            rs  = makeRuleset (getScopeNamespace sn) [] rls+                            rls = concatMap (`typeMkRules` cgr) dts+            ; modify frs+            }++ssFindDatatype :: ScopedName -> SwishStateIO (Either String RDFDatatype)+ssFindDatatype nam = gets find+    where+        find st = case findDatatype nam st of+            Nothing -> Left ("Datatype not found: "++show nam)+            Just dt -> Right dt+++ssCheckProof ::+    ScopedName                                      -- proof name+    -> [ScopedName]                                 -- ruleset names+    -> SwishStateIO (Either String RDFFormula)      -- input formula+    -> [Either String [RDFRuleset]                  -- proof step from rulesets+        -> SwishStateIO (Either String RDFProofStep)]+    -> SwishStateIO (Either String RDFFormula)      -- result formula+    -> SwishStateIO ()+ssCheckProof pn sns igf stfs rgf =+    let+        infmsg1 = "Proof satisfied: "+        errmsg1 = "Error in proof ruleset(s): "+        errmsg2 = "Error in proof input: "+        errmsg3 = "Error in proof step(s): "+        errmsg4 = "Error in proof goal: "+        errmsg5 = "Proof not satisfied: "+        proofname = " (Proof "++show pn++")"+    in+        do  { let rs1 = map ssFindRuleset sns       :: [SwishStateIO (Either String RDFRuleset)]+            ; rs2 <- sequence rs1                   -- [Either String RDFRuleset]+            ; let erss = sequence rs2               :: Either String [RDFRuleset]+            ; eig <- igf                            -- Either String RDFFormula+            ; let st1  = sequence $ flist stfs erss :: SwishStateIO [Either String RDFProofStep]+            ; st2 <- st1                            -- [Either String RDFProofStep]+            ; let ests = sequence st2               :: Either String [RDFProofStep]+            ; erg  <- rgf                           -- Either String RDFFormula+            ; let proof = case (erss,eig,ests,erg) of+                    (Left er,_,_,_) -> Left (errmsg1++er++proofname)+                    (_,Left er,_,_) -> Left (errmsg2++er++proofname)+                    (_,_,Left er,_) -> Left (errmsg3++er++proofname)+                    (_,_,_,Left er) -> Left (errmsg4++er++proofname)+                    (Right rss, Right ig, Right sts, Right rg) ->+                        Right (makeRDFProof rss ig rg sts)+            ; when False $ case proof of+                    (Left  _)  -> return ()+                    (Right pr) -> putResourceData Nothing $+                                    B.fromLazyText (L.concat ["Proof ", L.pack (show pn), "\n"])+                                    `mappend`+                                    B.fromString (showsProof "\n" pr "\n")+                                    -- TODO: clean up+            ; let checkproof = case proof of+                    (Left  er) -> setError er+                    (Right pr) ->+                        case explainProof pr of+                            Nothing -> setInfo (infmsg1++show pn)+                            Just ex -> setError (errmsg5++show pn++", "++ex)+                        {-+                        if not $ checkProof pr then+                            setError (errmsg5++show pn)+                        else+                            setInfo (infmsg1++show pn)+                        -}+            ; modify checkproof+            }++ssCheckStep ::+    ScopedName                                      -- rule name+    -> [SwishStateIO (Either String RDFFormula)]    -- antecedent graph formulae+    -> SwishStateIO (Either String RDFFormula)      -- consequent graph formula+    -> Either String [RDFRuleset]                   -- rulesets+    -> SwishStateIO (Either String RDFProofStep)    -- resulting proof step+ssCheckStep _  _    _    (Left  er)  = return $ Left er+ssCheckStep rn eagf ecgf (Right rss) =+    let+        errmsg1 = "Rule not in proof step ruleset(s): "+        errmsg2 = "Error in proof step antecedent graph(s): "+        errmsg3 = "Error in proof step consequent graph: "+    in+        do  { let mrul = getMaybeContextRule rn rss :: Maybe RDFRule+            ; esag <- sequence eagf                 -- [Either String RDFFormula]]+            ; let eags = sequence esag              :: Either String [RDFFormula]+            ; ecg  <- ecgf                          -- Either String RDFFormula+            ; let est = case (mrul,eags,ecg) of+                    (Nothing,_,_) -> Left (errmsg1++show rn)+                    (_,Left er,_) -> Left (errmsg2++er)+                    (_,_,Left er) -> Left (errmsg3++er)+                    (Just rul,Right ags,Right cg) ->+                        Right $ makeRDFProofStep rul ags cg+            ; return est+            }++ssFwdChain ::+    ScopedName                                      -- ruleset name+    -> ScopedName                                   -- rule name+    -> [SwishStateIO (Either String RDFGraph)]      -- antecedent graphs+    -> ScopedName                                   -- consequent graph name+    -> NamespaceMap                                 -- prefixes for new graph+    -> SwishStateIO ()+ssFwdChain sn rn agfs cn prefs =+    let+        errmsg1 = "FwdChain rule error: "+        errmsg2 = "FwdChain antecedent error: "+    in+        do  { erl  <- ssFindRulesetRule sn rn+            ; aesg <- sequence agfs     -- [Either String RDFGraph]+            ; let eags = sequence aesg   :: Either String [RDFGraph]+            ; let fcr = case (erl,eags) of+                    (Left er,_) -> setError (errmsg1++er)+                    (_,Left er) -> setError (errmsg2++er)+                    (Right rl,Right ags) ->+                        modGraphs (`mapReplace` NamedGraph cn [cg])+                        where+                            cg = case fwdApply rl ags of+                                []  -> mempty+                                grs -> setNamespaces prefs $ foldl1 addGraphs grs+            ; modify fcr+            }++ssFindRulesetRule ::+    ScopedName -> ScopedName -> SwishStateIO (Either String RDFRule)+ssFindRulesetRule sn rn = gets find+    where+        find st = case findRuleset sn st of+            Nothing -> Left ("Ruleset not found: "++show sn)+            Just rs -> find1 rs+        find1 rs = case getRulesetRule rn rs of+            Nothing -> Left ("Rule not in ruleset: "++show sn++": "++show rn)+            Just rl -> Right rl++ssFindRuleset ::+    ScopedName -> SwishStateIO (Either String RDFRuleset)+ssFindRuleset sn = gets find+    where+        find st = case findRuleset sn st of+            Nothing -> Left ("Ruleset not found: "++show sn)+            Just rs -> Right rs++ssBwdChain ::+    ScopedName                                      -- ruleset name+    -> ScopedName                                   -- rule name+    -> SwishStateIO (Either String RDFGraph)        -- consequent graphs+    -> ScopedName                                   -- antecedent alts name+    -> NamespaceMap                                 -- prefixes for new graphs+    -> SwishStateIO ()+ssBwdChain sn rn cgf an prefs =+    let+        errmsg1 = "BwdChain rule error: "+        errmsg2 = "BwdChain goal error: "+    in+        do  { erl <- ssFindRulesetRule sn rn+            ; ecg <- cgf                -- Either String RDFGraph+            ; let fcr = case (erl,ecg) of+                    (Left er,_) -> setError (errmsg1++er)+                    (_,Left er) -> setError (errmsg2++er)+                    (Right rl,Right cg) ->+                        modGraphs (`mapReplace` NamedGraph an ags)+                        where+                            ags  = map mergegr (bwdApply rl cg)+                            mergegr grs = case grs of+                                [] -> mempty+                                _  -> setNamespaces prefs $ foldl1 addGraphs grs+            ; modify fcr+            }++--  Temporary implementation:  just read local file WNH     +--  (Add logic to separate filenames from URIs, and+--  attempt HTTP GET, or similar.)+getResourceData :: Maybe URI -> SwishStateIO (Either String L.Text)+getResourceData muri =+    case muri of+        Nothing  -> fromStdin+        Just uri -> fromUri uri+    where+    fromStdin =+        do  { dat <- lift LIO.getContents+            ; return $ Right dat+            }+    fromUri = fromFile+    fromFile uri | uriScheme uri == "file:" = Right `fmap` lift (LIO.readFile $ uriPath uri)+                 | otherwise = error $ "Unsupported file name for read: " ++ show uri+                               +--  Temporary implementation:  just write local file+--  (Need to add logic to separate filenames from URIs, and+--  attempt HTTP PUT, or similar.)+putResourceData :: Maybe URI -> B.Builder -> SwishStateIO ()+putResourceData muri gsh = do+    ios <- lift . CE.try $ maybe toStdout toUri muri+    case ios of+      Left ioe -> modify $ setError+                    ("Error writing graph: "+++                    IO.ioeGetErrorString ioe)+      Right _   -> return ()++    where+        toStdout  = LIO.putStrLn gstr+        toUri uri | uriScheme uri == "file:" = LIO.writeFile (uriPath uri) gstr+                  | otherwise                = error $ "Unsupported scheme for write: " ++ show uri+        gstr = B.toLazyText gsh++{- $syntax++The script syntax is based loosely on Notation3, and the script parser is an+extension of the Notation3 parser in the module "Swish.RDF.Parser.N3".+The comment character is @#@ and white space is ignored.++> script            := command *+> command           := prefixLine        |+>                      nameItem          |+>                      readGraph         |+>                      writeGraph        |+>                      mergeGraphs       |+>                      compareGraphs     |+>                      assertEquiv       |+>                      assertMember      |+>                      defineRule        |+>                      defineRuleset     |+>                      defineConstraints |+>                      checkProofCmd     |+>                      fwdChain          |+>                      bwdChain ++-}++{- $prefixLine++> prefixLine        := @prefix [<prefix>]: <uri> .++Define a namespace prefix and URI. ++The prefix thus defined is available for use in any subsequent script+command, and also in any graphs contained within the script file. (So,+prefix declarations do not need to be repeated for each graph+contained within the script.)++Graphs read from external files must contain their own prefix+declarations.++Example:++  > @prefix gex: <http://example1.com/graphs/>.+  > @prefix :    <http://example2.com/id/>.++-}++{- $nameItem++> nameItem          := name :- graph     |+>                      name :- ( graph* )++Graphs or lists of graphs can be given a name for use in other+statements.  A name is a qname (prefix:local) or a URI enclosed in+angle++Example:++> @prefix ex1: <http://example.com/graphs/> .+> @prefix ex2: <http://example.com/statements/> .+>+> ex1:gr1 :- { +>     ex2:foo a ex2:Foo .+>     ex2:bar a ex2:Bar .+>     ex2:Bar rdfs:subClassOf ex2:Foo .+> }++-}++{- $readGraph++> readGraph         := @read name [<uri>]++The @\@read@ command reads in the contents of the given URI+- which at present only supports reading local files, so+no HTTP access - and stores it under the given name.++If no URI is given then the file is read from standard input.++Example:++  > @prefix ex: <http://example.com/> .+  > @read ex:foo <foo.n3>++-}++{- $writeGraph++> writeGraph        := @write name [<uri>] ; comment++The @\@write@ command writes out the contents of the given graph+- which at present only supports writing local files, so+no HTTP access. The comment text is written as a comment line+preceeding the graph contents.++If no URI is given then the file is written to the standard output.++Example:++  > @prefix ex: <http://example.com/> .+  > @read ex:gr1 <graph1.n3>+  > @read ex:gr2 <graph2.n3>+  > @merge (ex:gr1 ex:gr2) => ex:gr3+  > @write ex:gr3 ; the merged data+  > @write ex:gr3 <merged.n3> ; merge of graph1.n3 and graph2.n3++-}++{- $mergeGraphs++> mergeGraphs       := @merge ( name* ) => name++Create a new named graph that is the merge two or more graphs,+renaming bnodes as required to avoid node-merging.++When the merge command is run, the message++  > # Merge: <output graph name>++will be created on the standard output channel.++Example:++  > @prefix gex: <http://example.com/graph/>.+  > @prefix ex: <http://example.com/statements/>.+  > gex:gr1 :- { ex:foo ex:bar _:b1 . }+  > gex:gr2 :- { _:b1 ex:foobar 23. }+  > @merge (gex:gr1 gex:gr2) => gex:gr3+  > @write gex:gr3 ; merged graphs++When run in Swish, this creates the following output (along with+several other namespace declarations):++ > # merged graphs+ > @prefix ex: <http://example.com/statements/> .+ > ex:foo ex:bar [] .+ > [+ >  ex:foobar "23"^^xsd:integer+ > ] .++-}++{- $compareGraphs++> compareGraphs     := @compare name name++Compare two graphs for isomorphism, setting the Swish exit status to+reflect the result.++When the compare command is run, the message++  > # Compare: <graph1> <graph2>++will be created on the standard output channel.++Example:++  > @prefix gex: <http://example.com/graphs/>.+  > @read gex:gr1 <graph1.n3>+  > @read gex:gr2 <graph2.n3>+  > @compare gex:gr1 gex:gr2++-}++{- $assertEquiv++> assertEquiv       := @asserteq name name ; comment++Test two graphs or lists of graphs for isomorphism, reporting if they+differ. The comment text is included with any report generated.++When the command is run, the message++  > # AssertEq: <comment>++will be created on the standard output channel.++Example:++  > @prefix ex:  <http://id.ninebynine.org/wip/2003/swishtest/> .+  >+  > # Set up the graphs for the rules+  > ex:Rule01Ant :- { ?p ex:son ?o . }+  > ex:Rule01Con :- { ?o a ex:Male ; ex:parent ?p . }+  >+  > # Create a rule and a ruleset+  > @rule ex:Rule01 :- ( ex:Rule01Ant ) => ex:Rule01Con+  > @ruleset ex:rules :- (ex:TomSonDick ex:TomSonHarry) ; (ex:Rule01)+  >+  > # Apply the rule+  > @fwdchain ex:rules ex:Rule01 { :Tom ex:son :Charles . } => ex:Rule01fwd+  >+  > # Compare the results to the expected value+  > ex:ExpectedRule01fwd :- { :Charles a ex:Male ; ex:parent :Tom . }  +  > @asserteq ex:Rule01fwd ex:ExpectedRule01fwd+  >    ; Infer that Charles is male and has parent Tom++-}++{- $assertMember++> assertMember      := @assertin name name ; comment++Test if a graph is isomorphic to a member of a list of graphs,+reporting if no match is found. The comment text is included with any+report generated.++Example:++> @bwdchain pv:rules :PassengerVehicle ex:Test01Inp <= :t1b+> +> @assertin ex:Test01Bwd0 :t1b ; Backward chain component test (0)+> @assertin ex:Test01Bwd1 :t1b ; Backward chain component test (1)++-}++{- $defineRule++> defineRule        := @rule name :- ( name* ) => name+> defineRule        := @rule name :- ( name* ) => name+>                       | ( (name var*)* )++Define a named Horn-style rule. ++The list of names preceding and following @=>@ are the antecedent and consequent+graphs, respectivelu. Both sets may contain variable nodes of the form +@?var@.++The optional part, after the @|@ separator, is a list of variable+binding modifiers, each of which consists of a name and a list of+variables (@?var@) to which the modifier is applied. Variable binding+modifiers are built in to Swish, and are used to incorporate datatype+value inferences into a rule.  ++-}++{- $defineRuleset++> defineRuleset     := @ruleset name :- ( name* ) ; ( name* ) ++Define a named ruleset (a collection of axioms and rules). The first+list of names are the axioms that are part of the ruleset, and the+second list are the rules.++-}++{- $defineConstraints++> defineConstraints := @constraints pref :- ( name* ) | [ name | ( name* ) ]++Define a named ruleset containing class-restriction rules based on a+datatype value constraint. The first list of+names is a list of graphs that together comprise the class-restriction+definitions (rule names are the names of the corresponding restriction+classes). The second list of names is a list of datatypes whose+datatype relations are referenced by the class restriction+definitions.++-}++{- $fwdChain++> fwdChain          := @fwdchain pref name ( name* ) => name++Define a new graph obtained by forward-chaining a rule. The first name+is the ruleset to be used. The second name is the rule name. The list+of names are the antecedent graphs to which the rule is applied. The+name following the @=>@ names a new graph that is the result of formward+chaining from the given antecedents using the indicated rule.++-}++{- $bwdChain++> bwdChain          := @bwdchain pref name graph <= name++Define a new list of alternative graphs obtained by backward-chaining+a rule. The first name is the ruleset to be used. The second name is+the rule name. The third name (before the @<=@) is the name of a goal graph+from which to backward chain. The final name (after the @<=@) names a new+list of graphs, each of which is an alternative antecedent from which+the given goal can be deduced using the indicated rule.+++-}++{- $proof++> checkProofCmd     := proofLine nl+>                      inputLine nl+>                      (stepLine nl)*+>                      resultLine+> proofLine         := @proof name ( name* )++Check a proof, reporting the step that fails, if any.++The @\@proof@ line names the proof and specifies a list rulesets+(proof context) used.  The remaining lines specify the input+expression (@\@input@), proof steps (@\@step@) and the final result+(@\@result@) that is demonstrated by the proof.++> inputLine         := @input name++In a proof, indicates an input expression upon which the proof is+based. Exactly one of these immediately follows the @\@proof@ command.++> stepLine          := @step name ( name* ) => name++This defines a step of the proof; any number of these immediately+follow the @\@input@ command.++It indicates the name of the rule applied for this step, a list of+antecedent graphs, and a named graph that is deduced by this step.+For convenience, the deduced graph may introduce a new named graph+using an expression of the form:++  > name :- { statements }++> resultLine        := @result name++This defines the goal of the proof, and completes a proof+definition. Exactly one of these immediately follows the @\@step@+commands.  For convenience, the result statement may introduce a new+named graph using an expression of the form:++  > name :- { statements }++-}++{- $exampleScript++This is the example script taken from+<http://www.ninebynine.org/Software/swish-0.2.1.html#sec-script-example>+with the proof step adjusted so that it passes.++> # -- Example Swish script --+> #+> # Comment lines start with a '#'+> #+> # The script syntax is loosely based on Notation3, but it is a quite +> # different language, except that embedded graphs (enclosed in {...})+> # are encoded using Notation3 syntax.+> #+> # -- Prefix declarations --+> #+> # As well as being used for all labels defined and used by the script+> # itself, these are applied to all graph expressions within the script +> # file, and to graphs created by scripted inferences, +> # but are not applied to any graphs read in from an external source.+> +> @prefix ex:  <http://id.ninebynine.org/wip/2003/swishtest/> .+> @prefix pv:  <http://id.ninebynine.org/wip/2003/swishtest/pv/> .+> @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .+> @prefix xsd_integer: <http://id.ninebynine.org/2003/XMLSchema/integer#> .+> @prefix rs_rdf:  <http://id.ninebynine.org/2003/Ruleset/rdf#> .+> @prefix rs_rdfs: <http://id.ninebynine.org/2003/Ruleset/rdfs#> .+> @prefix :   <http://id.ninebynine.org/default/> .+> +> # Additionally, prefix declarations are provided automatically for:+> #    @prefix rdf:   <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .+> #    @prefix rdfs:  <file://www.w3.org/2000/01/rdf-schema#> .+> #    @prefix rdfd:  <http://id.ninebynine.org/2003/rdfext/rdfd#> .+> #    @prefix rdfo:  <http://id.ninebynine.org/2003/rdfext/rdfo#> .+> #    @prefix owl:   <http://www.w3.org/2002/07/owl#> .+> +> # -- Simple named graph declarations --+> +> ex:Rule01Ant :- { ?p ex:son ?o . }+> +> ex:Rule01Con :- { ?o a ex:Male ; ex:parent ?p . }+> +> ex:TomSonDick :- { :Tom ex:son :Dick . }+> ex:TomSonHarry :- { :Tom ex:son :Harry . }+> +> # -- Named rule definition --+> +> @rule ex:Rule01 :- ( ex:Rule01Ant ) => ex:Rule01Con+> +> # -- Named ruleset definition --+> #+> # A 'ruleset' is a collection of axioms and rules.+> #+> # Currently, the ruleset is identified using the namespace alone;+> # i.e. the 'rules' in 'ex:rules' below is not used.  +> # This is under review.+> +> @ruleset ex:rules :- (ex:TomSonDick ex:TomSonHarry) ; (ex:Rule01)+> +> # -- Forward application of rule --+> #+> # The rule is identified here by ruleset and a name within the ruleset.+> +> @fwdchain ex:rules ex:Rule01 { :Tom ex:son :Charles . } => ex:Rule01fwd+> +> # -- Compare graphs --+> #+> # Compare result of inference with expected result.+> # This is a graph isomorphism test rather than strict equality, +> # to allow for bnode renaming.+> # If the graphs are not equal, a message is generated, which+> # includes the comment (';' to end of line)+> +> ex:ExpectedRule01fwd :- { :Charles a ex:Male ; ex:parent :Tom . }  +> +> @asserteq ex:Rule01fwd ex:ExpectedRule01fwd+>    ; Infer that Charles is male and has parent Tom+> +> # -- Display graph (to screen and a file) --+> #+> # The comment is included in the output.+> +> @write ex:Rule01fwd ; Charles is male and has parent Tom+> @write ex:Rule01fwd <Example1.n3> ; Charles is male and has parent Tom+> +> # -- Read graph from file --+> #+> # Creates a new named graph in the Swish environment.+> +> @read ex:Rule01inp <Example1.n3>+> +> # -- Proof check --+> #+> # This proof uses the built-in RDF and RDFS rulesets, +> # which are the RDF- and RDFS- entailment rules described in the RDF+> # formal semantics document.+> #+> # To prove:+> #     ex:foo ex:prop "a" .+> # RDFS-entails+> #     ex:foo ex:prop _:x .+> #     _:x rdf:type rdfs:Resource .+> #+> # If the proof is not valid according to the axioms and rules of the +> # ruleset(s) used and antecedents given, then an error is reported +> # indicating the failed proof step.+> +> ex:Input  :- { ex:foo ex:prop "a" . }+> ex:Result :- { ex:foo ex:prop _:a . _:a rdf:type rdfs:Resource . }+> +> @proof ex:Proof ( rs_rdf:rules rs_rdfs:rules )+>   @input  ex:Input+>   @step   rs_rdfs:r3 ( rs_rdfs:a10 rs_rdfs:a39 )+>           => ex:Stepa :- { rdfs:Literal rdf:type rdfs:Class . }+>   @step   rs_rdfs:r8 ( ex:Stepa )+>           => ex:Stepb :- { rdfs:Literal rdfs:subClassOf rdfs:Resource . }+>   @step   rs_rdf:lg ( ex:Input )+>           => ex:Stepc :- { ex:foo ex:prop _:a . _:a rdf:_allocatedTo "a" . }+>   @step   rs_rdfs:r1 ( ex:Stepc )+>           => ex:Stepd :- { _:a rdf:type rdfs:Literal . }+>   @step   rs_rdfs:r9 ( ex:Stepb ex:Stepd )+>           => ex:Stepe :- { _:a rdf:type rdfs:Resource . }+>   @step   rs_rdf:se  ( ex:Stepc ex:Stepd ex:Stepe )+>           => ex:Result+>   @result ex:Result+> +> # -- Restriction based datatype inferencing --+> #+> # Datatype inferencing based on a general class restriction and+> # a predefined relation (per idea noted by Pan and Horrocks).+> +> ex:VehicleRule :-+>   { :PassengerVehicle a rdfd:GeneralRestriction ;+>       rdfd:onProperties (:totalCapacity :seatedCapacity :standingCapacity) ;+>       rdfd:constraint xsd_integer:sum ;+>       rdfd:maxCardinality "1"^^xsd:nonNegativeInteger . }+> +> # Define a new ruleset based on a declaration of a constraint class+> # and reference to built-in datatype.+> # The datatype constraint xsd_integer:sum is part of the definition +> # of datatype xsd:integer that is cited in the constraint ruleset+> # declaration.  It relates named properties of a class instance.+> +> @constraints pv:rules :- ( ex:VehicleRule ) | xsd:integer+> +> # Input data for test cases:+> +> ex:Test01Inp :-+>   { _:a1 a :PassengerVehicle ;+>       :seatedCapacity "30"^^xsd:integer ;+>       :standingCapacity "20"^^xsd:integer . }+> +> # Forward chaining test case:+> +> ex:Test01Fwd :- { _:a1 :totalCapacity "50"^^xsd:integer . }+> +> @fwdchain pv:rules :PassengerVehicle ex:Test01Inp => :t1f+> @asserteq :t1f ex:Test01Fwd  ; Forward chain test+> +> # Backward chaining test case:+> #+> # Note that the result of backward chaining is a list of alternatives,+> # any one of which is sufficient to derive the given conclusion.+> +> ex:Test01Bwd0 :-+>   { _:a1 a :PassengerVehicle .+>     _:a1 :totalCapacity "50"^^xsd:integer .+>     _:a1 :seatedCapacity "30"^^xsd:integer . }+> +> ex:Test01Bwd1 :-+>   { _:a1 a :PassengerVehicle .+>     _:a1 :totalCapacity "50"^^xsd:integer .+>     _:a1 :standingCapacity "20"^^xsd:integer . }+> +> # Declare list of graphs:+> +> ex:Test01Bwd :- ( ex:Test01Bwd0 ex:Test01Bwd1 )+> +> @bwdchain pv:rules :PassengerVehicle ex:Test01Inp <= :t1b+> @asserteq :t1b ex:Test01Bwd  ; Backward chain test+> +> # Can test for graph membership in a list+> +> @assertin ex:Test01Bwd0 :t1b ; Backward chain component test (0)+> @assertin ex:Test01Bwd1 :t1b ; Backward chain component test (1)+> +> # -- Merge graphs --+> #+> # Merging renames bnodes to avoid collisions.+> +> @merge ( ex:Test01Bwd0 ex:Test01Bwd1 ) => ex:Merged+> +> # This form of comparison sets the Swish exit status based on the result.+> +> ex:ExpectedMerged :-+>   { _:a1 a :PassengerVehicle .+>     _:a1 :totalCapacity "50"^^xsd:integer .+>     _:a1 :seatedCapacity "30"^^xsd:integer .+>     _:a2 a :PassengerVehicle .+>     _:a2 :totalCapacity "50"^^xsd:integer .+>     _:a2 :standingCapacity "20"^^xsd:integer . }+> +> @compare ex:Merged ex:ExpectedMerged+> +> # End of example script++If saved in the file example.ss, then it can be evaluated by saying+either of:++> % Swish -s=example.ss++or, from @ghci@:++> Prelude> :set prompt "Swish> "+> Swish> :m + Swish+> Swish> runSwish "-s=example.ss"++and the output is++> # AssertEq: Infer that Charles is male and has parent Tom+> # Charles is male and has parent Tom+> @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .+> @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .+> @prefix rdfd: <http://id.ninebynine.org/2003/rdfext/rdfd#> .+> @prefix owl: <http://www.w3.org/2002/07/owl#> .+> @prefix log: <http://www.w3.org/2000/10/swap/log#> .+> @prefix : <http://id.ninebynine.org/default/> .+> @prefix ex: <http://id.ninebynine.org/wip/2003/swishtest/> .+> @prefix pv: <http://id.ninebynine.org/wip/2003/swishtest/pv/> .+> @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .+> @prefix xsd_integer: <http://id.ninebynine.org/2003/XMLSchema/integer#> .+> @prefix rs_rdf: <http://id.ninebynine.org/2003/Ruleset/rdf#> .+> @prefix rs_rdfs: <http://id.ninebynine.org/2003/Ruleset/rdfs#> .+> :Charles ex:parent :Tom ;+>          a ex:Male .+> +> Proof satisfied: ex:Proof+> # AssertEq: Forward chain test+> # AssertEq: Backward chain test+> # AssertIn: Backward chain component test (0)+> # AssertIn: Backward chain component test (1)+> # Merge: ex:Merged+> # Compare: ex:Merged ex:ExpectedMerged++-}++--------------------------------------------------------------------------------+--+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke  +--  All rights reserved.+--+--  This file is part of Swish.+--+--  Swish is free software; you can redistribute it and/or modify+--  it under the terms of the GNU General Public License as published by+--  the Free Software Foundation; either version 2 of the License, or+--  (at your option) any later version.+--+--  Swish is distributed in the hope that it will be useful,+--  but WITHOUT ANY WARRANTY; without even the implied warranty of+--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+--  GNU General Public License for more details.+--+--  You should have received a copy of the GNU General Public License+--  along with Swish; if not, write to:+--    The Free Software Foundation, Inc.,+--    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+--+--------------------------------------------------------------------------------
src/Swish/Utils/ListHelpers.hs view
@@ -3,86 +3,29 @@ -------------------------------------------------------------------------------- -- | --  Module      :  ListHelpers---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke --  License     :  GPL V2 -- --  Maintainer  :  Douglas Burke --  Stability   :  experimental --  Portability :  H98 -----  This module defines some generic list and related helper functions. +--  This module defines some generic list and related helper functions. The plan+--  is to move to using functionality from other modules (e.g. containers) where+--  possible. -- -------------------------------------------------------------------------------- -{--TODO:--The plan is to use modules such as Data.Set where appropriate.---}- module Swish.Utils.ListHelpers-       ( -- list of Swish.RDF.xxx modules the routine is used in-         select  -- GraphMatch-       , deleteIndex -- Datatype-       , subset -- Proof, RDFProof, VarBinding [also defined in Utils.PartOrderedCollection]-       , equiv -- GraphMatch, RDFRuleset, SwishScript, VarBinding, Utils.LookupMap-       , addSetElem -- RDFGraph-       , headOrNothing -- VarBinding-       , pairUngroup -- GraphMatch-       , pairSort -- GraphMatch-       , pairGroup -- GraphMatch-       , breakAll -- SwishMain-       , powerSet -- ClassRestrictionRule, RDFProof-       , permutations -- VarBinding-       , listProduct -- RDFQuery-       , powerSequencesLen -- RDFProof-       , flist -- Datatype, RDFProof, RDFRuleset, SwishScript, VarBinding-       , allp -- RDFQuery-       , anyp -- RDFQuery+       ( -- list of modules the routine is used in+         subset -- Proof, RDF.Proof, VarBinding [also defined in Data.Ord.Partial]+       , equiv -- GraphMatch, RDF.Ruleset, Script, VarBinding, Data.LookupMap+       , flist -- Datatype, RDF.Proof, RDF.Ruleset, Script, VarBinding, ...                ) where-  -import Data.Ord (comparing)  -import Data.List (sortBy, groupBy)  ---------------------------------------------------------------  Generic helpers----------------------------------------------------------------- |Select is like filter, except that it tests one list to select---  elements from a second list.-select :: ( a -> Bool ) -> [a] -> [b] -> [b]-select _ [] []           = []-select f (e1:l1) (e2:l2)-    | f e1      = e2 : select f l1 l2-    | otherwise = select f l1 l2-select _ _ _    = error "select supplied with different length lists"---- |Delete the n'th element of a list, returning the result------  If the list doesn't have an n'th element, return the list unchanged.----deleteIndex :: [a] -> Int -> [a]-deleteIndex [] _ = []-deleteIndex xxs@(x:xs) n-    | n <  0    = xxs-    | n == 0    = xs-    | otherwise = x:deleteIndex xs (n-1)--{--testdi1 = deleteIndex [1,2,3,4] 0    == [2,3,4]-testdi2 = deleteIndex [1,2,3,4] 1    == [1,3,4]-testdi3 = deleteIndex [1,2,3,4] 2    == [1,2,4]-testdi4 = deleteIndex [1,2,3,4] 3    == [1,2,3]-testdi5 = deleteIndex [1,2,3,4] 4    == [1,2,3,4]-testdi6 = deleteIndex [1,2,3,4] (-1) == [1,2,3,4]-testdi = and-    [ testdi1, testdi2, testdi3, testdi4, testdi5, testdi6 ]--}-------------------------------------------------------------- --  Set functions -- --  NOTE: to change to Data.Set then Eq a constraint will @@ -99,182 +42,7 @@ equiv           :: (Eq a) => [a] -> [a] -> Bool a `equiv` b     = a `subset` b && b `subset` a --- |Add element to set--addSetElem :: (Eq a) => a -> [a] -> [a]-addSetElem e es = if e `elem` es then es else e:es- ---------------------------------------------------------------  Lists and Maybes----------------------------------------------------------------- |Return head of a list of @Maybe@'s, or @Nothing@ if list is empty------  Use with @filter isJust@ to select a non-Nothing value from a---  list when such a value is present.----headOrNothing :: [Maybe a] -> Maybe a-headOrNothing []    = Nothing-headOrNothing (a:_) = a-----------------------------------------------------------------  Filter, ungroup, sort and group pairs by first member---------------------------------------------------------------{--pairSelect :: ((a,b) -> Bool) -> ((a,b) -> c) -> [(a,b)] -> [c]-pairSelect p f as = map f (filter p as)--}--pairUngroup :: (a,[b]) -> [(a,b)]-pairUngroup (a,bs) = [ (a,b) | b <- bs ]--pairSort :: (Ord a) => [(a,b)] -> [(a,b)]-pairSort = sortBy (comparing fst)--pairGroup :: (Ord a) => [(a,b)] -> [(a,[b])]-pairGroup = map (factor . unzip) . groupBy eqFirst . pairSort -    where-      -- as is not [] by construction, but would be nice to have-      -- this enforced by the types-      factor (as, bs) = (head as,bs)-      eqFirst a b     = fst a == fst b-----------------------------------------------------------------  Separate list into sublists----------------------------------------------------------------- |Break list into a list of sublists, separated by element---  satisfying supplied condition.-breakAll :: (a -> Bool) -> [a] -> [[a]]-breakAll _ [] = []-breakAll p s  = let (h,s') = break p s-                    in h : breakAll p (drop 1 s')-----------------------------------------------------------------  Powerset-----------------------------------------------------------------  [[[TBD... there's a much better implementation in my email,---     from Christopher Hendrie.  This is the raw code.]]]-{-->ranked_powerset :: [a] -> [[[a]]]->ranked_powerset = takeWhile (not . null) . foldr next_powerset ([[]] :-repeat [])->->next_powerset :: a -> [[[a]]] -> [[[a]]]->next_powerset x r = zipWith (++) ([] : map (map (x:)) r) r->->powerset :: [a] -> [[a]]->powerset = tail . concat . ranked_powerset--}---- |Powerset of a list, in ascending order of size.---  Assumes the supplied list has no duplicate elements.-powerSet :: [a] -> [[a]]-powerSet as =-    concatMap (`combinations` as) [1..length as]---- |Combinations of n elements from a list, each being returned in the---  order that they appear in the list.-combinations :: Int -> [a] -> [[a]]-combinations _ []       = []        -- Don't include empty combinations-combinations n as@(ah:at)-    | n <= 0            = [[]]-    | n >  length as    = []-    | n == length as    = [as]-    | otherwise         = map (ah:) (combinations (n-1) at) ++-                          combinations n at--{---- |Return list of integers from lo to hi.-intRange :: Int -> Int -> [Int]-intRange lo hi = take (hi-lo+1) (iterate (+1) 1)--}--{---- Tests-testcomb0 = combinations 0 "abcd" -- []-testcomb1 = combinations 1 "abcd" -- ["a","b","c","d"]-testcomb2 = combinations 2 "abcd" -- ["ab","ac","ad","bc","bd","cd"]-testcomb3 = combinations 3 "abcd" -- ["abc","abd","acd","bcd"]-testcomb4 = combinations 4 "abcd" -- ["abcd"]-testcomb5 = combinations 5 "abcd" -- []-testpower = powerSet "abc"        -- ["a","b","c","ab","ac","bc","abc"]--}-----------------------------------------------------------------  Permutations of a list-----------------------------------------------------------------  This algorithm is copied from an email by S.D.Mechveliani---  http://www.dcs.gla.ac.uk/mail-www/haskell/msg01936.html-permutations :: [a] -> [[a]]-permutations    []     = [[]]-permutations    (j:js) = addOne $ permutations js-    where-        addOne = foldr ((++) . ao) []--        ao []           = [[j]]-        ao (k:ks)       = (j:k:ks) : map (k:) (ao ks)--{--testperm = permutations [1,2,3] ==-    [[1,2,3],[2,1,3],[2,3,1],[1,3,2],[3,1,2],[3,2,1]]--}-----------------------------------------------------------------  List product----------------------------------------------------------------- |Given a list of lists, construct a new list of lists where---  each member of the new list is the same length as the original---  list, and each member corresponds to a different choice of---  one element from each of the original members in the---  corresponding position.  Thus:------  > listProduct [[a1,a2],[b1],[c1,c2]] =---  >      [ [a1,b1,c1], [a1,b1,c2], [a2,b1,c1], [a2,b1,c2] ]------  Note:  The length of the resulting list is the prodicty of---  lengths of the components of the original list.  Thus, if---  any member of the original list is empty then so is the---  resulting list:------  > listProduct [[a1,a2],[],[c1,c2]] = []------  NOTE:  this is subsumed by 'sequence'----listProduct :: [[a]] -> [[a]]-listProduct []       = [[]]-listProduct (as:ass) = concat [ map (a:) (listProduct ass) | a <- as ]--{--test1 = listProduct [["a1","a2"],["b1"],["c1","c2"]]-test2 = listProduct [["a1","a2"],[],["c1","c2"]]--lp []       = [[]]-lp (as:ass) = concatMap (\a -> (map (a:) (lp ass))) as--}-----------------------------------------------------------------  Powersequence (?) -- all sequences from some base values----------------------------------------------------------------- |Construct list of lists of sequences of increasing length-powerSeqBylen :: [a] -> [[a]] -> [[[a]]]-powerSeqBylen rs ps = ps : powerSeqBylen rs (powerSeqNext rs ps)---- |Return sequences of length n+1 given original sequence---  and list of all sequences of length n-powerSeqNext :: [a] -> [[a]] -> [[a]]-powerSeqNext rs rss = [ h:t | t <- rss, h <- rs ]---- |Return all powersequences of a given length-powerSequencesLen :: Int -> [a] -> [[a]]-powerSequencesLen len rs = powerSeqBylen rs [[]] !! len-------------------------------------------------------------- --  Functions, lists and monads ------------------------------------------------------------ @@ -307,36 +75,10 @@ fmonadtest = fmonad [(1*),(2*),(3*)] 3 -- [3,6,9] -} --- |Test if a value satisfies all predicates in a list----allp :: [a->Bool] -> a -> Bool-allp ps a = and (flist ps a)--{--allptest0 = allp [(>=1),(>=2),(>=3)] 0     -- False-allptest1 = allp [(>=1),(>=2),(>=3)] 1     -- False-allptest2 = allp [(>=1),(>=2),(>=3)] 2     -- False-allptest3 = allp [(>=1),(>=2),(>=3)] 3     -- True-allptest  = and [not allptest0,not allptest1,not allptest2,allptest3]--}---- |Test if a value satisfies any predicate in a list----anyp :: [a->Bool] -> a -> Bool-anyp ps a = or (flist ps a)--{--anyptest0 = anyp [(>=1),(>=2),(>=3)] 0     -- False-anyptest1 = anyp [(>=1),(>=2),(>=3)] 1     -- True-anyptest2 = anyp [(>=1),(>=2),(>=3)] 2     -- True-anyptest3 = anyp [(>=1),(>=2),(>=3)] 3     -- True-anyptest  = and [not anyptest0,anyptest1,anyptest2,anyptest3]--}-- -------------------------------------------------------------------------------- -----  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke --  All rights reserved. -- --  This file is part of Swish.
− src/Swish/Utils/LookupMap.hs
@@ -1,454 +0,0 @@-{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}-{-# LANGUAGE FlexibleContexts, FlexibleInstances, TypeSynonymInstances #-}-{-# LANGUAGE DeriveFunctor, DeriveFoldable, DeriveTraversable #-}------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  LookupMap---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  License     :  GPL V2------  Maintainer  :  Douglas Burke---  Stability   :  experimental---  Portability :  A lot of LANGUAGE extensions...------  This module defines a lookup table format and associated functions---  used by the graph matching code.-----------------------------------------------------------------------------------------------------------------------------------------------------  Generic list-of-pairs lookup functions---------------------------------------------------------------module Swish.Utils.LookupMap-    ( LookupEntryClass(..), LookupMap(..)-    , emptyLookupMap, makeLookupMap, listLookupMap-    , reverseLookupMap-    , keyOrder-    , mapFind, mapFindMaybe, mapContains-    , mapReplace, mapReplaceOrAdd, mapReplaceAll, mapReplaceMap-    , mapAdd, mapAddIfNew-    , mapDelete, mapDeleteAll-    , mapApplyToAll, mapTranslate-    , mapEq, mapKeys, mapVals-    , mapSelect, mapMerge-    , mapTranslateKeys, mapTranslateVals-    , mapTranslateEntries, mapTranslateEntriesM--    )-    where--import qualified Data.Foldable as F-import qualified Data.Traversable as T-import qualified Data.List as L--import Control.Arrow (first, second)--import Data.Ord (comparing)--import Swish.Utils.ListHelpers (equiv)---- this is in Data.Tuple in base 4.3-swap :: (a,b) -> (b,a)-swap (a,b) = (b,a)-----------------------------------------------------------------  Class for lookup map entries----------------------------------------------------------------- |@LookupEntryClass@ defines essential functions of any datatype---  that can be used to make a 'LookupMap'.------  Minimal definition: @newEntry@ and @keyVal@----class (Eq k, Show k) => LookupEntryClass a k v | a -> k, a -> v-    where-        newEntry    :: (k,v) -> a-        keyVal      :: a -> (k,v)-        -        entryKey    :: a -> k-        entryKey = fst . keyVal-        -        entryVal    :: a -> v-        entryVal = snd . keyVal-                             -        entryEq     :: (Eq v) => a -> a -> Bool-        entryEq e1 e2 = keyVal e1 == keyVal e2-        -        entryShow   :: (Show v) => a -> String-        entryShow e = show k ++ ":" ++ show v where (k,v) = keyVal e-                                                    -        kmap :: (LookupEntryClass a2 k2 v) => (k -> k2) -> a -> a2-        kmap f = newEntry . first f . keyVal-        -        vmap :: (LookupEntryClass a2 k v2) => (v -> v2) -> a -> a2-        vmap f = newEntry . second f . keyVal---- |Predefine a pair of appropriate values as a valid lookup table entry---  (i.e. an instance of LookupEntryClass).----instance (Eq k, Show k) => LookupEntryClass (k,v) k v where-    newEntry = id-    keyVal   = id---- |Define a lookup map based on a list of values.------  Note:  the class constraint that a is an instance of 'LookupEntryClass'---  is not defined here, for good reasons (which I forget right now, but---  something to do with the method dictionary being superfluous on---  an algebraic data type).----data LookupMap a = LookupMap [a]-  deriving (Functor, F.Foldable, T.Traversable)--{- -TODO: could add--instance Monoid (LookupMap a) where-    mempty = LookupMap []-    mappend = mapMerge--but may need constraints on a, do not-want to add instances at this time, and is-it really useful? ---}--gLM :: LookupMap a -> [a]-gLM (LookupMap es) = es---- TODO:  See also 'mapEq'---  (why not just use that for the Eq instance?  I don't know:  it's probably historic.)------- |Define equality of 'LookupMap' values based on equality of entries.------  (This is possibly a poor definition, as it is dependent on ordering---  of list members.  But it passes all current test cases, and is used---  only for testing.)----instance (Eq a) => Eq (LookupMap a) where-    LookupMap es1 == LookupMap es2 = es1 == es2---- |Define Show instance for LookupMap based on Showing the--- list of entries.----instance (Show a ) => Show (LookupMap a) where-    show (LookupMap es) = "LookupMap " ++ show es--{--TODO: should the LookupEntryClass constraint be removed from-emptyLookupMap and makeLookupMap?--I guess not since LookupMap is exported, so users can use-that if they do not need the constraint.--}---- |Empty lookup map of arbitrary (i.e. polymorphic) type.----emptyLookupMap :: (LookupEntryClass a k v) => LookupMap a-emptyLookupMap = LookupMap []---- |Function to create a `LookupMap` from a list of entries.------  Currently, this is trivial but future versions could be---  more substantial.----makeLookupMap :: (LookupEntryClass a k v) => [a] -> LookupMap a-makeLookupMap = LookupMap---- |Return list of lookup map entries.------  Currently, this is trivial but future versions could be---  more substantial.----listLookupMap :: (LookupEntryClass a k v) => LookupMap a -> [a]-listLookupMap = gLM---- |Given a lookup map entry, return a new entry that can be used---  in the reverse direction of lookup.  This is used to construct---  a reverse LookupMap.----reverseEntry :: (LookupEntryClass a1 k v, LookupEntryClass a2 v k)-    => a1 -> a2-reverseEntry = newEntry . swap . keyVal---- |Given a lookup map, return a new map that can be used---  in the opposite direction of lookup.----reverseLookupMap :: (LookupEntryClass a1 b c, LookupEntryClass a2 c b)-    => LookupMap a1 -> LookupMap a2-reverseLookupMap = fmap reverseEntry---- |Given a pair of lookup entry values, return the ordering of their---  key values.----keyOrder :: (LookupEntryClass a k v, Ord k)-    =>  a -> a -> Ordering-keyOrder = comparing entryKey----  Local helper function to build a new LookupMap from---  a new entry and an exiting map.----mapCons :: a -> LookupMap a -> LookupMap a-mapCons e (LookupMap es) = LookupMap (e:es)---- |Find key in lookup map and return corresponding value,---  otherwise return default supplied.----mapFind :: (LookupEntryClass a k v) => v -> k -> LookupMap a -> v-mapFind def key (LookupMap es) = foldr match def es where-    match ent alt-        | key == entryKey ent   = entryVal ent-        | otherwise             = alt---- |Find key in lookup map and return Just the corresponding value,---  otherwise return Nothing.----mapFindMaybe :: (LookupEntryClass a k v) => k -> LookupMap a -> Maybe v-mapFindMaybe key (LookupMap es) = foldr match Nothing es where-    match ent alt-        | key ==  entryKey ent  = Just (entryVal ent)-        | otherwise             = alt---- |Test to see if key is present in the supplied map----mapContains :: (LookupEntryClass a k v) =>-    LookupMap a -> k -> Bool-mapContains (LookupMap es) key  = any match es where-    match ent = key == entryKey ent---- |Replace an existing occurrence of a key a with a new key-value pair.---    ---  The resulting lookup map has the same form as the original in all---  other respects.  Assumes exactly one occurrence of the supplied key.----mapReplace :: (LookupEntryClass a k v) =>-    LookupMap a -> a -> LookupMap a-mapReplace (LookupMap (e:es)) newe-    | entryKey e == entryKey newe       = LookupMap (newe:es)-    | otherwise                         = mapAdd more e where-        more = mapReplace (LookupMap es) newe-mapReplace _ newe =-    error ("mapReplace: Key value not found in lookup table: "++-           Prelude.show (entryKey newe))---- |Replace an existing occurrence of a key a with a new key-value pair,---  or add a new key-value pair if the supplied key is not already present.----mapReplaceOrAdd :: (LookupEntryClass a k v) =>-    a -> LookupMap a -> LookupMap a-mapReplaceOrAdd newe (LookupMap (e:es))-    | entryKey e == entryKey newe       = LookupMap (newe:es)-    | otherwise                         = mapCons e more where-        more = mapReplaceOrAdd newe (LookupMap es)-mapReplaceOrAdd newe (LookupMap [])     = LookupMap [newe]---- |Replace any occurrence of a key a with a new key-value pair.------  The resulting lookup map has the same form as the original in all---  other respects.----mapReplaceAll :: (LookupEntryClass a k v) =>-    LookupMap a -> a -> LookupMap a-mapReplaceAll (LookupMap (e:es)) newe   = mapCons e' more where-    more = mapReplaceAll (LookupMap es) newe-    e'   = if entryKey e == entryKey newe then newe else e-mapReplaceAll (LookupMap []) _          = LookupMap []---- |Replace any occurrence of a key in the first argument with a---  corresponding key-value pair from the second argument, if present.------  This could be implemented by multiple applications of 'mapReplaceAll',---  but is arranged differently so that only one new @LookupMap@ value is---  created.------  Note:  keys in the new map that are not present in the old map---  are not included in the result map----mapReplaceMap :: (LookupEntryClass a k v) =>-    LookupMap a -> LookupMap a -> LookupMap a-mapReplaceMap (LookupMap (e:es)) newmap = mapCons e' more where-    more  = mapReplaceMap (LookupMap es) newmap-    e'    = newEntry (k, mapFind v k newmap)-    (k,v) = keyVal e-mapReplaceMap (LookupMap []) _ = LookupMap []---- |Add supplied key-value pair to the lookup map.------  This is effectively an optimized case of 'mapReplaceOrAdd' or 'mapAddIfNew',---  where the caller guarantees to avoid duplicate key values.----mapAdd :: LookupMap a -> a -> LookupMap a-mapAdd emap e = mapCons e emap---- |Add supplied key-value pair to the lookup map,---  only if the key value is not already present.----mapAddIfNew :: (LookupEntryClass a k v) =>-    LookupMap a -> a -> LookupMap a-mapAddIfNew emap e = if mapContains emap (entryKey e)-                        then emap-                        else mapCons e emap---- |Delete supplied key value from the lookup map.------  This function assumes exactly one occurrence.----mapDelete :: (LookupEntryClass a k v) =>-    LookupMap a -> k -> LookupMap a-mapDelete (LookupMap (e:es)) k-    | k == entryKey e   = LookupMap es-    | otherwise         = mapCons e more where-        more = mapDelete (LookupMap es) k-mapDelete _ k =-    error ("mapDelete: Key value not found in lookup table: " ++ Prelude.show k)---- |Delete any occurrence of a supplied key value from the lookup map.----mapDeleteAll :: (LookupEntryClass a k v) =>-    LookupMap a -> k -> LookupMap a-mapDeleteAll (LookupMap (e:es)) k =-    if entryKey e == k then more else mapCons e more where-        more = mapDeleteAll (LookupMap es) k-mapDeleteAll (LookupMap []) _ = LookupMap []---- |Return a list of values obtained by applying a function to each key---  in the map.  Creates an alternative set of values that can be---  retrieved using mapTranslate.----mapApplyToAll :: (LookupEntryClass a k v) =>-    LookupMap a -> (k -> w) -> [w]-mapApplyToAll es f = gLM $ fmap (f . entryKey) es---- |Find a node in a lookup map list, and returns the---  corresponding value from a supplied list.  The appropriate ordering---  of the list is not specified here, but an appropriately ordered list---  may be obtained by 'mapApplyToAll'.----mapTranslate :: (LookupEntryClass a k v) =>-    LookupMap a -> [w] -> k -> w -> w-mapTranslate (LookupMap (e:es)) (w:ws) k def-    | k == entryKey e   = w-    | otherwise         = mapTranslate (LookupMap es) ws k def-mapTranslate _ _ _ def = def---- |Compare two lookup maps for equality.------  Two maps are equal if they have the same set of keys, and if---  each key maps to an equivalent value.----mapEq :: (LookupEntryClass a k v, Eq v) =>-    LookupMap a -> LookupMap a -> Bool-mapEq es1 es2 =-    ks1 `equiv` ks2 &&-    and [ mapFindMaybe k es1 == mapFindMaybe k es2 | k <- ks1 ]-    where-        ks1 = mapKeys es1-        ks2 = mapKeys es2---- |Return the list of keys in a supplied LookupMap----mapKeys :: (LookupEntryClass a k v) =>-    LookupMap a -> [k]-mapKeys (LookupMap es) = L.nub $ map entryKey es---- |Return list of distinct values in a supplied LookupMap----mapVals :: (Eq v, LookupEntryClass a k v) =>-    LookupMap a -> [v]-mapVals (LookupMap es) = L.nub $ map entryVal es---- |Select portion of a lookup map that corresponds to---  a supplied list of keys----mapSelect :: (LookupEntryClass a k v) =>-    LookupMap a -> [k] -> LookupMap a-mapSelect (LookupMap es) ks =-    LookupMap $ filter (keyIn ks) es-    where-        keyIn iks e = entryKey e `elem` iks---- |Merge two lookup maps, ensuring that if the same key appears---  in both maps it is associated with the same value.----mapMerge :: (LookupEntryClass a k v, Eq a, Show a, Ord k) =>-    LookupMap a -> LookupMap a -> LookupMap a-mapMerge (LookupMap s1) (LookupMap s2) =-    LookupMap $ merge (L.sortBy keyOrder s1) (L.sortBy keyOrder s2)-    where-        merge es1 [] = es1-        merge [] es2 = es2-        merge es1@(e1:et1) es2@(e2:et2) =-            case keyOrder e1 e2 of-                LT -> e1 : merge et1 es2-                GT -> e2 : merge es1 et2-                EQ -> if e1 /= e2-                        then error ("mapMerge key conflict: " ++ show e1-                                    ++ " with " ++ show e2)-                        else e1 : merge et1 et2---- |An fmap-like function that returns a new lookup map that is a---  copy of the supplied map with entry keys replaced according to---  a supplied function.----mapTranslateKeys :: (LookupEntryClass a1 k1 v, LookupEntryClass a2 k2 v) =>-    (k1 -> k2) -> LookupMap a1 -> LookupMap a2-mapTranslateKeys f = fmap (kmap f)---- |An fmap-like function that returns a new lookup map that is a---  copy of the supplied map with entry values replaced according to---  a supplied function.----mapTranslateVals :: (LookupEntryClass a1 k v1, LookupEntryClass a2 k v2) =>-    (v1 -> v2) -> LookupMap a1 -> LookupMap a2-mapTranslateVals f = fmap (vmap f)---- |A function that returns a new lookup map that is a copy of the---  supplied map with complete entries replaced according to---  a supplied function.----mapTranslateEntries :: (a1 -> a2) -> LookupMap a1 -> LookupMap a2-mapTranslateEntries = fmap---- |A monadic form of `mapTranslateEntries` which is--- the same as `Data.Traversable.mapM`.------ Since `LookupMap` now has a `Data.Traversable.Traversable` instance--- this is just `T.mapM`.----mapTranslateEntriesM :: (Monad m)-    => (a1 -> m a2) -> LookupMap a1 -> m (LookupMap a2)-mapTranslateEntriesM = T.mapM -{--mapTranslateEntriesM f (LookupMap es) =-    do  { m2 <- mapM f es-        ; return $ LookupMap m2-        }--}----------------------------------------------------------------------------------------  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  All rights reserved.------  This file is part of Swish.------  Swish is free software; you can redistribute it and/or modify---  it under the terms of the GNU General Public License as published by---  the Free Software Foundation; either version 2 of the License, or---  (at your option) any later version.------  Swish is distributed in the hope that it will be useful,---  but WITHOUT ANY WARRANTY; without even the implied warranty of---  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the---  GNU General Public License for more details.------  You should have received a copy of the GNU General Public License---  along with Swish; if not, write to:---    The Free Software Foundation, Inc.,---    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA------------------------------------------------------------------------------------
− src/Swish/Utils/MiscHelpers.hs
@@ -1,69 +0,0 @@------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  MiscHelpers---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  License     :  GPL V2------  Maintainer  :  Douglas Burke---  Stability   :  experimental---  Portability :  H98------  This module defines a random set of helper functions---  used by the graph handling code.------  This module is *deprecated* and will be removed at the next possible---  release.--------------------------------------------------------------------------------------module Swish.Utils.MiscHelpers-      ( hash-      , hashModulus-      )-where-----------------------------------------------------------------  Hash function and values-------------------------------------------------------------------  Simple hash function based on Sedgewick, Algorithms in C, p 233---  (choose mx*cm+255 < maxBound)---  'seed' is an additional parameter that allows the function---  to be varied for re-hashing.--hashModulus :: Int-hashModulus = 16000001--hash :: Int -> String -> Int-hash seed = hash1 seed (64+seed) hashModulus --hash1 :: Int -> Int -> Int -> String -> Int-hash1 sofar cm mx (c:str) = hash1 (( sofar*cm + fromEnum c ) `rem` mx) cm mx str-hash1 sofar _ _ []        = sofar-----------------------------------------------------------------------------------------  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  All rights reserved.------  This file is part of Swish.------  Swish is free software; you can redistribute it and/or modify---  it under the terms of the GNU General Public License as published by---  the Free Software Foundation; either version 2 of the License, or---  (at your option) any later version.------  Swish is distributed in the hope that it will be useful,---  but WITHOUT ANY WARRANTY; without even the implied warranty of---  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the---  GNU General Public License for more details.------  You should have received a copy of the GNU General Public License---  along with Swish; if not, write to:---    The Free Software Foundation, Inc.,---    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA------------------------------------------------------------------------------------
− src/Swish/Utils/Namespace.hs
@@ -1,222 +0,0 @@-{-# LANGUAGE TypeSynonymInstances, MultiParamTypeClasses, FlexibleInstances #-}-{-# LANGUAGE OverloadedStrings #-}-------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  Namespace---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  License     :  GPL V2------  Maintainer  :  Douglas Burke---  Stability   :  experimental---  Portability :  TypeSynonymInstances, MultiParamTypeClasses, FlexibleInstances, OverloadedStrings------  This module defines algebraic datatypes for namespaces and scoped names.------  For these purposes, a namespace is a prefix and URI used to identify---  a namespace (cf. XML namespaces), and a scoped name is a name that---  is scoped by a specified namespace.--------------------------------------------------------------------------------------module Swish.Utils.Namespace-    ( Namespace-    , makeNamespace, makeNamespaceQName-      , getNamespacePrefix, getNamespaceURI, getNamespaceTuple-    -- , nullNamespace-    , ScopedName-    , getScopeNamespace, getScopeLocal-    , getScopePrefix, getScopeURI-    , getQName, getScopedNameURI-    , matchName-    , makeScopedName-    , makeQNameScopedName-    , makeURIScopedName-    , makeNSScopedName-    , nullScopedName-    , namespaceToBuilder-    )-    where--import Swish.Utils.QName (QName, newQName, getQNameURI, getNamespace, getLocalName)-import Swish.Utils.LookupMap (LookupEntryClass(..))--import Data.Monoid (Monoid(..))-import Data.String (IsString(..))-import Data.Maybe (fromMaybe)--import Network.URI (URI(..), parseURIReference, nullURI)--import qualified Data.Text as T-import qualified Data.Text.Lazy.Builder as B-----------------------------------------------------------------  Namespace, having a prefix and a URI----------------------------------------------------------------- |A NameSpace value consists of an optional prefix and a corresponding URI.-----data Namespace = Namespace (Maybe T.Text) URI--- data Namespace = Namespace (Maybe T.Text) !URI-                 -{-                 -                 {-                   nsPrefix :: Maybe T.Text-                 , nsURI :: URI-                 }--}-                 -getNamespacePrefix :: Namespace -> Maybe T.Text-getNamespacePrefix (Namespace p _) = p--getNamespaceURI :: Namespace -> URI-getNamespaceURI (Namespace _ u) = u--getNamespaceTuple :: Namespace -> (Maybe T.Text, URI)-getNamespaceTuple (Namespace p u) = (p, u)---- | Equality is defined by the URI, not by the prefix--- (so the same URI with different prefixes will be--- considered to be equal).-instance Eq Namespace where-  (Namespace _ u1) == (Namespace _ u2) = u1 == u2--instance Show Namespace where-    show (Namespace (Just p) u) = show p ++ ":<" ++ show u ++ ">"-    show (Namespace _ u)        = "<" ++ show u ++ ">"--instance LookupEntryClass Namespace (Maybe T.Text) URI where-    keyVal   (Namespace pre uri) = (pre,uri)-    newEntry (pre,uri)           = Namespace pre uri--makeNamespace :: Maybe T.Text -> URI -> Namespace-makeNamespace = Namespace--makeNamespaceQName :: Namespace -> T.Text -> QName-makeNamespaceQName (Namespace _ uri) = newQName uri--{--nullNamespace :: Namespace-nullNamespace = Namespace Nothing ""--}---- | Utility routine to create a \@prefix line (matching N3/Turtle)---   grammar for this namespace.----namespaceToBuilder :: Namespace -> B.Builder-namespaceToBuilder (Namespace pre uri) =-  mconcat $ map B.fromText -  [ "@prefix ", fromMaybe "" pre, ": <", T.pack (show uri), "> .\n"]-----------------------------------------------------------------  ScopedName, made from a namespace and a local name----------------------------------------------------------------- | A full ScopedName value has a QName prefix, namespace URI---  and a local part.  ScopedName values may omit the prefix---  (see 'Namespace') or the local part.------  Some applications may handle null namespace URIs as meaning---  the local part is relative to some base URI.----data ScopedName = ScopedName !QName Namespace T.Text---- | Returns the local part.-getScopeLocal :: ScopedName -> T.Text-getScopeLocal (ScopedName _ _ l) = l---- | Returns the namespace.-getScopeNamespace :: ScopedName -> Namespace-getScopeNamespace (ScopedName _ ns _) = ns---- | Returns the prefix of the namespace, if set.-getScopePrefix :: ScopedName -> Maybe T.Text-getScopePrefix = getNamespacePrefix . getScopeNamespace---- | Returns the URI of the namespace.-getScopeURI :: ScopedName -> URI-getScopeURI = getNamespaceURI . getScopeNamespace---- | This is not total since it will fail if the input string is not a valid URI.-instance IsString ScopedName where-  fromString s =-    maybe (error ("Unable to convert " ++ s ++ " into a ScopedName"))-          makeURIScopedName (parseURIReference s)-    --- | Scoped names are equal if their corresponding QNames are equal-instance Eq ScopedName where-  (ScopedName qn1 _ _) == (ScopedName qn2 _ _) = qn1 == qn2---- | Scoped names are ordered by their QNames-instance Ord ScopedName where-  (ScopedName qn1 _ _) <= (ScopedName qn2 _ _) = qn1 <= qn2---- | If there is a namespace associated then the Show instance--- uses @prefix:local@, otherwise @<url>@.-instance Show ScopedName where-    show (ScopedName qn n l) = case getNamespacePrefix n of-      Just pre -> T.unpack $ mconcat [pre, ":", l]-      _        -> show qn -- "<" ++ show (getNamespaceURI n) ++ T.unpack l ++ ">"---- |Get the QName corresponding to a scoped name.-getQName :: ScopedName -> QName-getQName (ScopedName qn _ _) = qn---- |Get URI corresponding to a scoped name (using RDF conventions).-getScopedNameURI :: ScopedName -> URI-getScopedNameURI = getQNameURI . getQName---- for the moment leave matchName using String rather than Text---- |Test if supplied string matches the display form of a---  scoped name.-matchName :: String -> ScopedName -> Bool-matchName str nam = str == show nam---- |Construct a ScopedName from prefix, URI and local name-makeScopedName :: Maybe T.Text -> URI -> T.Text -> ScopedName-makeScopedName pre nsuri local =-  ScopedName (newQName nsuri local) (Namespace pre nsuri) local---- |Construct a ScopedName from a QName.-makeQNameScopedName :: Maybe T.Text -> QName -> ScopedName-makeQNameScopedName pre qn = ScopedName qn (Namespace pre (getNamespace qn)) (getLocalName qn)---- | Construct a ScopedName for a bare URI (the label is set to \"\").-makeURIScopedName :: URI -> ScopedName-makeURIScopedName uri = makeScopedName Nothing uri ""---- | Construct a ScopedName from a Namespace and local component-makeNSScopedName :: Namespace -> T.Text -> ScopedName-makeNSScopedName ns local = ScopedName (newQName (getNamespaceURI ns) local) ns local---- | This should never appear as a valid name-nullScopedName :: ScopedName-nullScopedName = makeURIScopedName nullURI----------------------------------------------------------------------------------------  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  All rights reserved.------  This file is part of Swish.------  Swish is free software; you can redistribute it and/or modify---  it under the terms of the GNU General Public License as published by---  the Free Software Foundation; either version 2 of the License, or---  (at your option) any later version.------  Swish is distributed in the hope that it will be useful,---  but WITHOUT ANY WARRANTY; without even the implied warranty of---  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the---  GNU General Public License for more details.------  You should have received a copy of the GNU General Public License---  along with Swish; if not, write to:---    The Free Software Foundation, Inc.,---    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA------------------------------------------------------------------------------------
− src/Swish/Utils/PartOrderedCollection.hs
@@ -1,386 +0,0 @@------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  PartOrderedCollection---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  License     :  GPL V2------  Maintainer  :  Douglas Burke---  Stability   :  experimental---  Portability :  H98------  This module provides methods to support operations on partially ordered---  collections.  The partial ordering relationship is represented by---  `Maybe Ordering`.------  Thanks to members of the haskell-cafe mailing list ----    Robert (rvollmert-lists\@gmx.net) and---    Tom Pledger (Tom.Pledger\@peace.com) ----  who suggested key ideas on which some of the code in this module is based.---------------------------------------------------------------------------------------- at present the only user of this module is Swish.RDF.ClassRestrictionRule--module Swish.Utils.PartOrderedCollection-    ( PartCompare-    , minima-    , maxima-    , partCompareEq-    , partComparePair-    , partCompareListMaybe-    , partCompareListSubset-    , partCompareListPartOrd-    , partCompareMaybe-        -      -- the following are currently unused-    , partCompareOrd-    , partCompareListOrd -    )-    where--import Data.List (foldl')-----------------------------------------------------------------  Type of partial compare function---------------------------------------------------------------type PartCompare a = a -> a -> Maybe Ordering-----------------------------------------------------------------  Functions for minima and maxima of a part-ordered list----------------------------------------------------------------- |This function finds the maxima in a list of partially---  ordered values, preserving the sequence of retained---  values from the supplied list.------  It returns all those values in the supplied list---  for which there is no larger element in the list.----maxima :: PartCompare a -> [a] -> [a]-maxima cmp = foldl' add [] -    where-        add []     e = [e]-        add ms@(m:mr) e = case cmp m e of-            Nothing -> m : add mr e-            Just GT -> ms-            Just EQ -> ms-            Just LT -> add mr e---- |This function finds the minima in a list of partially---  ordered values, preserving the sequence of retained---  values from the supplied list.------  It returns all those values in the supplied list---  for which there is no smaller element in the list.----minima :: PartCompare a -> [a] -> [a]-minima cmp = maxima (flip cmp)-----------------------------------------------------------------  Partial ordering comparison functions----------------------------------------------------------------- |Partial ordering for Eq values-partCompareEq :: (Eq a) => a -> a -> Maybe Ordering-partCompareEq a1 a2 = if a1 == a2 then Just EQ else Nothing---- |Partial ordering for Ord values-partCompareOrd :: (Ord a) => a -> a -> Maybe Ordering-partCompareOrd a1 a2 = Just $ compare a1 a2---- |Part-ordering comparison on pairs of values,---  where each has a part-ordering relationship-partComparePair ::-    (a->a->Maybe Ordering) -> (b->b->Maybe Ordering) -> (a,b) -> (a,b)-    -> Maybe Ordering-partComparePair cmpa cmpb (a1,b1) (a2,b2) = case (cmpa a1 a2,cmpb b1 b2) of-    (_,Nothing)       -> Nothing-    (jc1,Just EQ)     -> jc1-    (Nothing,_)       -> Nothing-    (Just EQ,jc2)     -> jc2-    (Just c1,Just c2) -> if c1 == c2 then Just c1 else Nothing---- |Part-ordering comparison on lists of partially ordered values, where:------  [@as==bs@]  if members of as are all equal to corresponding members of bs---    ---  [@as<=bs@]  if members of as are all less than or equal to corresponding---          members of bs---    ---  [@as>=bs@]  if members of as are all greater than or equal to corresponding---          members of bs---    ---  [otherwise] as and bs are unrelated------  The comparison is restricted to the common elements in the two lists.----partCompareListPartOrd :: PartCompare a -> [a] -> [a] -> Maybe Ordering-partCompareListPartOrd cmp a1s b1s = pcomp a1s b1s EQ-    where-        pcomp (a:as) (b:bs) ordp = case cmp a b of-            Just rel  -> pcomp1 as bs rel ordp-            _         -> Nothing-        pcomp _      _      ordp = Just ordp-        -- pcomp []     []     ordp = Just ordp-        -        pcomp1 as bs ordn EQ   = pcomp as bs ordn-        pcomp1 as bs EQ   ordp = pcomp as bs ordp-        pcomp1 as bs ordn ordp =-            if ordn == ordp then pcomp as bs ordp else Nothing-                                                       --- |Part-ordering comparison on lists of Ord values, where:------  [@as==bs@]  if members of as are all equal to corresponding members of bs---    ---  [@as<=bs@]  if members of as are all less than or equal to corresponding---          members of bs---    ---  [@as>=bs@]  if members of as are all greater than or equal to corresponding---          members of bs---    ---  [otherwise] as and bs are unrelated----partCompareListOrd :: (Ord a) => [a] -> [a] -> Maybe Ordering-partCompareListOrd = partCompareListPartOrd (Just `c2` compare)-    where c2 = (.) . (.)---- |Part-ordering comparison for Maybe values.-partCompareMaybe :: (Eq a) => Maybe a -> Maybe a -> Maybe Ordering-partCompareMaybe Nothing  Nothing  = Just EQ-partCompareMaybe (Just _) Nothing  = Just GT-partCompareMaybe Nothing  (Just _) = Just LT-partCompareMaybe (Just a) (Just b) = if a == b then Just EQ else Nothing---- |Part-ordering comparison on lists of Maybe values.-partCompareListMaybe :: (Eq a) => [Maybe a] -> [Maybe a] -> Maybe Ordering-partCompareListMaybe = partCompareListPartOrd partCompareMaybe---- |Part-ordering comparison on lists based on subset relationship-partCompareListSubset :: (Eq a) => [a] -> [a] -> Maybe Ordering-partCompareListSubset a b-    | aeqvb     = Just EQ-    | asubb     = Just LT-    | bsuba     = Just GT-    | otherwise = Nothing-    where-        asubb = a `subset` b-        bsuba = b `subset` a-        aeqvb = asubb && bsuba-        x `subset` y = and [ ma `elem` y | ma <- x ]-----------------------------------------------------------------  Test cases---------------------------------------------------------------{---notTrueFalse  = Nothing :: Maybe Bool---- partCompareListOrd-test01 = partCompareListOrd [1,2,3] [1,2,3] == Just EQ-test02 = partCompareListOrd [1,2,3] [2,3,4] == Just LT-test03 = partCompareListOrd [1,2,4] [1,2,3] == Just GT-test04 = partCompareListOrd [1,2,3] [2,1,3] == Nothing---- partCompareMaybe-test11 = partCompareMaybe (Just True)  (Just True)  == Just EQ-test12 = partCompareMaybe (Just True)  (Just False) == Nothing-test13 = partCompareMaybe notTrueFalse (Just False) == Just LT-test14 = partCompareMaybe (Just True)  notTrueFalse == Just GT-test15 = partCompareMaybe notTrueFalse notTrueFalse == Just EQ---- partCompareListMaybe-test21 = partCompareListMaybe [Just True,Just False]-                              [Just True,Just False]-            == Just EQ-test22 = partCompareListMaybe [Just True,Just False]-                              [Just True,Just True]-            == Nothing-test23 = partCompareListMaybe [Just False,Just True]-                              [Just False,Just True]-            == Just EQ-test24 = partCompareListMaybe [Nothing,   Just True]-                              [Just False,Just True]-            == Just LT-test25 = partCompareListMaybe [Just False,Just True]-                              [Just False,Nothing]-            == Just GT-test26 = partCompareListMaybe [Nothing,   Just True]-                              [Just False,Nothing]-            == Nothing-test27 = partCompareListMaybe [Nothing,Just True]-                              [Nothing,Nothing]-            == Just GT-test28 = partCompareListMaybe [notTrueFalse,notTrueFalse]-                              [notTrueFalse,notTrueFalse]-            == Just EQ----  minima, maxima-test31a = maxima partCompareListMaybe ds1a == ds1b-test31b = minima partCompareListMaybe ds1a == ds1c-ds1a =-    [ [Just 'a',Just 'b',Just 'c']-    , [Just 'a',Just 'b',Nothing ]-    , [Just 'a',Nothing ,Just 'c']-    , [Just 'a',Nothing ,Nothing ]-    , [Nothing ,Just 'b',Just 'c']-    , [Nothing ,Just 'b',Nothing ]-    , [Nothing ,Nothing ,Just 'c']-    , [Nothing ,Nothing ,Nothing ]-    ]-ds1b =-    [ [Just 'a',Just 'b',Just 'c']-    ]-ds1c =-    [ [Nothing ,Nothing ,Nothing ]-    ]--test32a = maxima partCompareListMaybe ds2a == ds2b-test32b = minima partCompareListMaybe ds2a == ds2c-ds2a =-    [ [Just 'a',Just 'b',Nothing ]-    , [Just 'a',Nothing ,Just 'c']-    , [Just 'a',Nothing ,Nothing ]-    , [Nothing ,Just 'b',Just 'c']-    , [Nothing ,Just 'b',Nothing ]-    , [Nothing ,Nothing ,Just 'c']-    ]-ds2b =-    [ [Just 'a',Just 'b',Nothing ]-    , [Just 'a',Nothing ,Just 'c']-    , [Nothing ,Just 'b',Just 'c']-    ]-ds2c =-    [ [Just 'a',Nothing ,Nothing ]-    , [Nothing ,Just 'b',Nothing ]-    , [Nothing ,Nothing ,Just 'c']-    ]--test33a = maxima partCompareListMaybe ds3a == ds3b-test33b = minima partCompareListMaybe ds3a == ds3c-ds3a =-    [ [Just "a1",Just "b1",Just "c1"]-    , [Just "a2",Just "b2",Nothing  ]-    , [Just "a3",Nothing  ,Just "c3"]-    , [Just "a4",Nothing  ,Nothing  ]-    , [Nothing  ,Just "b5",Just "c5"]-    , [Nothing  ,Just "b6",Nothing  ]-    , [Nothing  ,Nothing  ,Just "c7"]-    ]-ds3b =-    [ [Just "a1",Just "b1",Just "c1"]-    , [Just "a2",Just "b2",Nothing  ]-    , [Just "a3",Nothing  ,Just "c3"]-    , [Just "a4",Nothing  ,Nothing  ]-    , [Nothing  ,Just "b5",Just "c5"]-    , [Nothing  ,Just "b6",Nothing  ]-    , [Nothing  ,Nothing  ,Just "c7"]-    ]-ds3c =-    [ [Just "a1",Just "b1",Just "c1"]-    , [Just "a2",Just "b2",Nothing  ]-    , [Just "a3",Nothing  ,Just "c3"]-    , [Just "a4",Nothing  ,Nothing  ]-    , [Nothing  ,Just "b5",Just "c5"]-    , [Nothing  ,Just "b6",Nothing  ]-    , [Nothing  ,Nothing  ,Just "c7"]-    ]---test34a = maxima partCompareListMaybe ds4a == ds4b-test34b = minima partCompareListMaybe ds4a == ds4c-ds4a =-    [ [Just 1, Just 1 ]-    , [Just 2, Nothing]-    , [Nothing,Just 3 ]-    , [Nothing,Nothing]-    ]-ds4b =-    [ [Just 1, Just 1 ]-    , [Just 2, Nothing]-    , [Nothing,Just 3 ]-    ]-ds4c =-    [ [Nothing,Nothing]-    ]---- Check handling of equal values-test35a = maxima partCompareListMaybe ds5a == ds5b-test35b = minima partCompareListMaybe ds5a == ds5c-ds5a =-    [ [Just 1, Just 1 ]-    , [Just 2, Nothing]-    , [Nothing,Just 3 ]-    , [Nothing,Nothing]-    , [Just 1, Just 1 ]-    , [Just 2, Nothing]-    , [Nothing,Just 3 ]-    , [Nothing,Nothing]-    ]-ds5b =-    [ [Just 1, Just 1 ]-    , [Just 2, Nothing]-    , [Nothing,Just 3 ]-    ]-ds5c =-    [ [Nothing,Nothing]-    ]---- test case 32 with different ordering of values-test36a = maxima partCompareListMaybe ds6a == ds6b-test36b = minima partCompareListMaybe ds6a == ds6c-ds6a =-    [ [Just 'a',Just 'b',Nothing ]-    , [Nothing ,Nothing ,Just 'c']-    , [Nothing ,Just 'b',Nothing ]-    , [Nothing ,Just 'b',Just 'c']-    , [Just 'a',Nothing ,Nothing ]-    , [Just 'a',Nothing ,Just 'c']-    ]-ds6b =-    [ [Just 'a',Just 'b',Nothing ]-    , [Nothing ,Just 'b',Just 'c']-    , [Just 'a',Nothing ,Just 'c']-    ]-ds6c =-    [ [Nothing ,Nothing ,Just 'c']-    , [Nothing ,Just 'b',Nothing ]-    , [Just 'a',Nothing ,Nothing ]-    ]--test = and-    [ test01, test02, test03, test04-    , test11, test12, test13, test14, test15-    , test21, test22, test23, test24, test25, test26, test27, test28-    , test31a, test31b, test32a, test32b, test33a, test33b-    , test34a, test34b, test35a, test35b, test36a, test36b-    ]---}----------------------------------------------------------------------------------------  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  All rights reserved.------  This file is part of Swish.------  Swish is free software; you can redistribute it and/or modify---  it under the terms of the GNU General Public License as published by---  the Free Software Foundation; either version 2 of the License, or---  (at your option) any later version.------  Swish is distributed in the hope that it will be useful,---  but WITHOUT ANY WARRANTY; without even the implied warranty of---  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the---  GNU General Public License for more details.------  You should have received a copy of the GNU General Public License---  along with Swish; if not, write to:---    The Free Software Foundation, Inc.,---    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA------------------------------------------------------------------------------------
− src/Swish/Utils/QName.hs
@@ -1,277 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  QName---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  License     :  GPL V2------  Maintainer  :  Douglas Burke---  Stability   :  experimental---  Portability :  OverloadedStrings------  This module defines an algebraic datatype for qualified names (QNames).---------------------------------------------------------------------------------------- At present we support using URI references rather than forcing an absolute--- URI. This is partly to support the existing tests (too lazy to resolve whether--- the tests really should be using relative URIs in this case).--module Swish.Utils.QName-    ( QName-    , newQName-    , qnameFromURI-    , getNamespace-    , getLocalName-    , getQNameURI-    , qnameFromFilePath-    )-    where--import System.Directory (canonicalizePath)--import Network.URI (URI(..), URIAuth(..)-                    , parseURIReference)--import Data.String (IsString(..))-import Data.Maybe (fromMaybe)--import Data.Interned (intern, unintern)-import Data.Interned.URI (InternedURI)--import qualified Data.Text as T-----------------------------------------------------------------  Qualified name-------------------------------------------------------------------  cf. http://www.w3.org/TR/REC-xml-names/--{-| --A qualified name, consisting of a namespace URI-and the local part of the identifier.---}--{--For now I have added in storing the actual URI-as well as the namespace component. This may or-may not be a good idea (space vs time saving).--}--data QName = QName !InternedURI URI T.Text---- | This is not total since it will fail if the input string is not a valid URI.-instance IsString QName where-  fromString = qnameFromString-               --- | Equality is determined by a case sensitive comparison of the               --- URI.-instance Eq QName where-  -- see qnEq comments below-  (QName u1 _ _) == (QName u2 _ _) = u1 == u2---- ugly, use show instance OR switch to the ordering of InternedURI-    -instance Ord QName where-  {--    (QName u1 l1) <= (QName u2 l2) =-        if up1 /= up2 then up1 <= up2 else (ur1++l1) <= (ur2++l2)-        where-            n   = min (length u1) (length u2)-            (up1,ur1) = splitAt n u1-            (up2,ur2) = splitAt n u2-  -}-  -  -- TODO: which is faster?-  -- Now we have changed to InternedURI, we could use the-  -- Ord instance of it, but it is unclear to me what the-  -- ordering means in that case, and whether the semantics-  -- matter here?-  (QName u1 _ _) <= (QName u2 _ _) = show u1 <= show u2-  -  {--  (QName _ uri1 l1) <= (QName _ uri2 l2) =-    if up1 /= up2 then up1 <= up2 else (ur1 ++ T.unpack l1) <= (ur2 ++ T.unpack l2)-      where-        u1 = show uri1-        u2 = show uri2-        -        n   = min (length u1) (length u2)-        (up1,ur1) = splitAt n u1-        (up2,ur2) = splitAt n u2-  -}-  --- | The format used to display the URI is @<uri>@.-instance Show QName where-    show (QName u _ _) = "<" ++ show u ++ ">"--{--Should this be clever and ensure that local doesn't-contain /, say?--We could also me more clever, and safer, when constructing-the overall uri.--}-newQName :: URI -> T.Text -> QName-newQName ns local = -  let l   = T.unpack local-      uristr = show ns ++ l-      uri = fromMaybe (error ("Unable to parse URI from: '" ++ show ns ++ "' + '" ++ l ++ "'")) (parseURIReference uristr)-  -  {- the following does not work since the semantics of relativeTo do not match the required-     behavior here.  It may well be better to do something like the following, writing a replacement for-     relativeTo, but leave that for a later date.-  -  let l   = T.unpack local-      luri = fromMaybe (error ("Unable to parse local name as a URI reference: '" ++ l ++ "'")) (parseRelativeReference l)-      uri = fromMaybe (error ("Unable to combine " ++ show ns ++ " with " ++ l)) $ luri `relativeTo` ns-  -}-      -  in QName (intern uri) ns local--{---old behavior-- splitQname "http://example.org/aaa#bbb" = ("http://example.org/aaa#","bbb")- splitQname "http://example.org/aaa/bbb" = ("http://example.org/aaa/","bbb")- splitQname "http://example.org/aaa/"    = ("http://example.org/aaa/","")--Should "urn:foo:bar" have a local name of "" or "foo:bar"? For now go-with the first option.---}--qnameFromURI :: URI -> QName-qnameFromURI uri =-  let uf = uriFragment uri-      up = uriPath uri-      q0 = QName iuri uri ""-      iuri = intern uri-  in case uf of-    "#"    -> q0-    '#':xs -> QName iuri (uri { uriFragment = "#" }) (T.pack xs)-    ""     -> case break (=='/') (reverse up) of-      ("",_) -> q0 -- path ends in / or is empty-      (_,"") -> q0 -- path contains no /-      (rlname,rpath) -> QName iuri (uri {uriPath = reverse rpath}) (T.pack (reverse rlname))-      -    e -> error $ "Unexpected: uri=" ++ show uri ++ " has fragment='" ++ show e ++ "'" --qnameFromString :: String -> QName-qnameFromString s =   -  maybe (error ("Unable to convert '" ++ s ++ "' into a QName"))-  qnameFromURI (parseURIReference s)---- | Return the URI of the namespace stored in the QName.--- This does not contain the local component.----getNamespace :: QName -> URI-getNamespace (QName _ ns _) = ns---- | Return the local component of the QName.-getLocalName :: QName -> T.Text-getLocalName (QName _ _ l) = l---- | Returns the full URI of the QName (ie the combination of the--- namespace and local components).-getQNameURI :: QName -> URI-getQNameURI (QName u _ _) = unintern u--{--Original used comparison of concatenated strings,-but that was very inefficient.  The longer version below-does the comparison without constructing new values but is-no longer valid with the namespace being stored as a URI,-so for now just compare the overall URIs and we can-optimize this at a later date if needed.-qnEq :: QName -> QName -> Bool-qnEq (QName u1 _ _) (QName u2 _ _) = u1 == u2--qnEq (QName _ n1 l1) (QName _ n2 l2) = qnEq1 n1 n2 l1 l2-  where-    qnEq1 (c1:ns1) (c2:ns2)  ln1 ln2   = c1==c2 && qnEq1 ns1 ns2 ln1 ln2-    qnEq1 []  ns2  ln1@(_:_) ln2       = qnEq1 ln1 ns2 []  ln2-    qnEq1 ns1 []   ln1       ln2@(_:_) = qnEq1 ns1 ln2 ln1 []-    qnEq1 []  []   []        []        = True-    qnEq1 _   _    _         _         = False--}--{-|-Convert a filepath to a file: URI stored in a QName. If the-input file path is relative then the working directory is used-to convert it into an absolute path.--If the input represents a directory then it *must* end in -the directory separator - so for Posix systems use -@\"\/foo\/bar\/\"@ rather than -@\"\/foo\/bar\"@.--This has not been tested on Windows.--}--{--NOTE: not sure what I say directories should end in the path-seperator since--ghci> System.Directory.canonicalizePath "/Users/dburke/haskell/swish-text"-"/Users/dburke/haskell/swish-text"-ghci> System.Directory.canonicalizePath "/Users/dburke/haskell/swish-text/"-"/Users/dburke/haskell/swish-text"---}---- since we build up the URI manually we could--- create the QName directly, but leave that --- for now.--qnameFromFilePath :: FilePath -> IO QName-qnameFromFilePath = fmap qnameFromURI . filePathToURI-  -emptyAuth :: Maybe URIAuth-emptyAuth = Just $ URIAuth "" "" ""--filePathToURI :: FilePath -> IO URI-filePathToURI fname = do-  ipath <- canonicalizePath fname-  -  {--  let paths = splitDirectories ipath-      txt = intercalate "/" $ case paths of-        "/":rs -> rs-        _      -> paths-  -}-  -  -- Is manually creating the URI sensible?-  -- return $ fromJust $ parseURI $ "file:///" ++ txt-  -- return $ URI "file:" emptyAuth txt "" ""-  return $ URI "file:" emptyAuth ipath "" ""----------------------------------------------------------------------------------------  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  All rights reserved.------  This file is part of Swish.------  Swish is free software; you can redistribute it and/or modify---  it under the terms of the GNU General Public License as published by---  the Free Software Foundation; either version 2 of the License, or---  (at your option) any later version.------  Swish is distributed in the hope that it will be useful,---  but WITHOUT ANY WARRANTY; without even the implied warranty of---  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the---  GNU General Public License for more details.------  You should have received a copy of the GNU General Public License---  along with Swish; if not, write to:---    The Free Software Foundation, Inc.,---    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA------------------------------------------------------------------------------------
− src/Swish/Utils/ShowM.hs
@@ -1,75 +0,0 @@------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  ShowM---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  License     :  GPL V2------  Maintainer  :  Douglas Burke---  Stability   :  experimental---  Portability :  H98------  This module defines an extension of the Show class for displaying---  multi-line values.  It serves the following purposes:------  (1) provides a method with greater layout control of multiline values,------  (2) provides a possibility to override the default Show behaviour---      for programs that use the extended ShowM interface, and------  (3) uses a ShowS intermediate value to avoid unnecessary---      concatenation of long strings.--------------------------------------------------------------------------------------module Swish.Utils.ShowM (ShowM(..)) where-----------------------------------------------------------------  ShowM framework----------------------------------------------------------------- |ShowM is a type class for values that may be formatted in---  multi-line displays.-class (Show sh) => ShowM sh where-    -- |Multi-line value display method-    --  Create a multiline displayable form of a value, returned-    --  as a 'ShowS' value.  The default implementation behaves just-    --  like a normal instance of 'Show'.-    ---    --  This function is intended to allow the calling function some control-    --  of multiline displays by providing:-    ---    --  (1) the first line of the value is not preceded by any text, so-    --      it may be appended to some preceding text on the same line,-    ---    --  (2) the supplied line break string is used to separate lines of the-    --      formatted text, and may include any desired indentation, and-    ---    --  (3) no newline is output following the final line of text.-    showms :: String -> sh -> ShowS-    showms _ = shows----------------------------------------------------------------------------------------  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  All rights reserved.------  This file is part of Swish.------  Swish is free software; you can redistribute it and/or modify---  it under the terms of the GNU General Public License as published by---  the Free Software Foundation; either version 2 of the License, or---  (at your option) any later version.------  Swish is distributed in the hope that it will be useful,---  but WITHOUT ANY WARRANTY; without even the implied warranty of---  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the---  GNU General Public License for more details.------  You should have received a copy of the GNU General Public License---  along with Swish; if not, write to:---    The Free Software Foundation, Inc.,---    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA------------------------------------------------------------------------------------
+ src/Swish/VarBinding.hs view
@@ -0,0 +1,544 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeSynonymInstances #-}++--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  VarBinding+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke+--  License     :  GPL V2+--+--  Maintainer  :  Douglas Burke+--  Stability   :  experimental+--  Portability :  FlexibleInstances, MultiParamTypeClasses, OverloadedStrings, TypeSynonymInstances+--+--  This module defines functions for representing and manipulating query+--  binding variable sets.  This is the key data that mediates between+--  query and back substitution when performing inferences.  A framework+--  of query variable modifiers is provided that can be used to+--  implement richer inferences, such as filtering of  query results,+--  or replacing values based on known relationships.+--+--------------------------------------------------------------------------------++module Swish.VarBinding+    ( VarBinding(..), nullVarBinding+    , boundVars, subBinding, makeVarBinding+    , applyVarBinding, joinVarBindings, addVarBinding+    , VarBindingModify(..), OpenVarBindingModify+    , vbmCompatibility, vbmCompose+    , composeSequence, findCompositions, findComposition+    , VarBindingFilter(..)+    , makeVarFilterModify+    , makeVarTestFilter, makeVarCompareFilter+    , varBindingId, nullVarBindingModify+    , varFilterDisjunction, varFilterConjunction+    , varFilterEQ, varFilterNE+    )+where++import Swish.Namespace (ScopedName, getScopeLocal)+import Swish.QName (newLName, getLName)++import Swish.RDF.Vocabulary (swishName)++import Swish.Utils.ListHelpers (equiv, subset, flist)++import Data.List (find, intersect, union, (\\), foldl', permutations)+import Data.LookupMap (LookupEntryClass(..), makeLookupMap, mapFindMaybe)+import Data.Maybe (mapMaybe, fromMaybe, isJust, fromJust, listToMaybe)+import Data.Monoid (mconcat)++------------------------------------------------------------+--  Query variable bindings+------------------------------------------------------------++-- TODO: is it worth making a Monoid instance of VarBinding?++-- |VarBinding is the type of an arbitrary variable bindings+--  value, where the type of the bound values is not specified.+--+data VarBinding a b = VarBinding+    { vbMap  :: a -> Maybe b+    , vbEnum :: [(a,b)]+    , vbNull :: Bool+    }++-- |VarBinding is an instance of class Eq, so that variable+--  bindings can be compared for equivalence+--+instance (Eq a, Eq b) => Eq (VarBinding a b) where+    vb1 == vb2 = vbEnum vb1 `equiv` vbEnum vb2++-- |VarBinding is an instance of class Show, so that variable+--  bindings can be displayed+--+instance (Show a, Show b) => Show (VarBinding a b) where+    show = show . vbEnum++-- | maps no query variables.+--+nullVarBinding :: VarBinding a b+nullVarBinding = VarBinding+    { vbMap  = const Nothing+    , vbEnum = []+    , vbNull = True+    }++-- |Return a list of the variables bound by a supplied variable binding+--+boundVars :: VarBinding a b -> [a]+boundVars = map fst . vbEnum++-- |VarBinding subset function, tests to see if one query binding+--  is a subset of another;  i.e. every query variable mapping defined+--  by one is also defined by the other.+--+subBinding :: (Eq a, Eq b) => VarBinding a b -> VarBinding a b -> Bool+subBinding vb1 vb2 = vbEnum vb1 `subset` vbEnum vb2++-- |Function to make a variable binding from a list of+--  pairs of variable and corresponding assigned value.+--+makeVarBinding :: (Eq a, Show a, Eq b, Show b) => [(a,b)] -> VarBinding a b+makeVarBinding [] = nullVarBinding+makeVarBinding vrbs =+    let selectFrom = flip mapFindMaybe . makeLookupMap+    in VarBinding+           { vbMap  = selectFrom vrbs+           , vbEnum = vrbs+           , vbNull = False+           }++-- |Apply query binding to a supplied value, returning the value+--  unchanged if no binding is defined+--+applyVarBinding :: VarBinding a a -> a -> a+applyVarBinding vbind v = fromMaybe v (vbMap vbind v)++-- |Join a pair of query bindings, returning a new binding that+--  maps all variables recognized by either of the input bindings.+--  If the bindings should overlap, such overlap is not detected and+--  the value from the first binding provided is used arbitrarily.+--+joinVarBindings :: (Eq a) => VarBinding a b -> VarBinding a b -> VarBinding a b+joinVarBindings vb1 vb2+    | vbNull vb1 = vb2+    | vbNull vb2 = vb1+    | otherwise  = VarBinding+        { vbMap  = mv12+        , vbEnum = map (\v -> (v,fromJust (mv12 v))) bv12+        , vbNull = False+        }+    where+        mv12 = headOrNothing . filter isJust . flist [ vbMap vb1, vbMap vb2 ]+        bv12 = boundVars vb1 `union` boundVars vb2++-- |Return head of a list of @Maybe@'s, or @Nothing@ if list is empty+--+--  Use with @filter isJust@ to select a non-Nothing value from a+--  list when such a value is present.+--+headOrNothing :: [Maybe a] -> Maybe a+headOrNothing []    = Nothing+headOrNothing (a:_) = a++-- |Add a single new value to a variable binding and return the resulting+--  new variable binding.+--+addVarBinding :: (Eq a, Show a, Eq b, Show b) => a -> b -> VarBinding a b+    -> VarBinding a b+addVarBinding lb val vbind = joinVarBindings vbind $ makeVarBinding [(lb,val)]++------------------------------------------------------------+--  Datatypes for variable binding modifiers+------------------------------------------------------------++-- |Define the type of a function to modify variable bindings in+--  forward chaining based on rule antecedent matches.  This+--  function is used to implement the \"allocated to\" logic described+--  in Appendix B of the RDF semantics document, in which a specific+--  blank node is associated with all matches of some specific value+--  by applications of the rule on a given graph.+--  Use 'id' if no modification of the variable bindings is required.+--+--  This datatype consists of the modifier function itself, which+--  operates on a list of variable bindings rather than a single+--  variable binding (because some modifications share context across+--  a set of bindings), and some additional descriptive information+--  that allows possible usage patterns to be analyzed.+--+--  Some usage patterns (see 'vbmUsage' for more details):+--+--  [filter]  all variables are input variables, and the effect+--      of the modifier function is to drop variable bindings that+--      don't satisfy some criterion.+--      Identifiable by an empty element in @vbmUsage@.+--+--  [source]  all variables are output variables:  a raw query+--      could be viewed as a source of variable bindings.+--      Identifiable by an element of @vbmUsage@ equal to @vbmVocab@.+--+--  [modifier]  for each supplied variable binding, one or more+--      new variable bindings may be created that contain the+--      input variables bound as supplied plus some additional variables.+--      Identifiable by an element of @vbmUsage@ some subset of @vbmVocab@.+--+--  A variety of variable usage patterns may be supported by a given+--  modifier:  a modifier may be used to define new variable bindings+--  from existing bindings in a number of ways, or simply to check that+--  some required relationship between bindings is satisfied.+--  (Example, for @a + b = c@, any one variable can be deduced from the+--  other two, or all three may be supplied to check that the relationship+--  does indeed hold.)+--+data VarBindingModify a b = VarBindingModify+    { vbmName   :: ScopedName+                            -- ^Name used to identify this variable binding+                            --  modifier when building inference rules.+    , vbmApply  :: [VarBinding a b] -> [VarBinding a b]+                            -- ^Apply variable binding modifier to a+                            --  list of variable bindings, returning a+                            --  new list.  The result list is not+                            --  necessarily the same length as the+                            --  supplied list.+    , vbmVocab  :: [a]      -- ^List of variables used by this modifier.+                            --  All results of applying this modifier contain+                            --  bindings for these variables.+    , vbmUsage  :: [[a]]    -- ^List of binding modifier usage patterns+                            --  supported.  Each pattern is characterized as+                            --  a list of variables for which new bindings+                            --  may be created by some application of this+                            --  modifier, assuming that bindings for all other+                            --  variables in @vbmVocab@ are supplied.+    }++-- |Allow a VarBindingModify value to be accessed using a 'LookupMap'.+--+instance LookupEntryClass+    (VarBindingModify a b) ScopedName (VarBindingModify a b)+    where+        keyVal   vbm     = (vbmName vbm,vbm)+        newEntry (_,vbm) = vbm++-- |Type for variable binding modifier that has yet to be instantiated+--  with respect to the variables that it operates upon.+--+type OpenVarBindingModify lb vn = [lb] -> VarBindingModify lb vn++-- |Extract variable binding name from @OpenVarBindingModify@ value+--+--  (Because only the name is required, the application to an undefined+--  list of variable labels should never be evaluated, as long as the+--  name is not dependent on the variable names in any way.)+--+--  NOT QUITE... some of the functions that create @OpenVarBindingModify@+--  instances also pattern-match the number of labels provided, forcing+--  evaluation of the labels parameter, even though it's not used.+--+openVbmName :: OpenVarBindingModify lb vn -> ScopedName+openVbmName ovbm = vbmName (ovbm (error "Undefined labels in variable binding"))++-- |Allow an @OpenVarBindingModify@ value to be accessed using a @LookupMap@.+--+instance LookupEntryClass+    (OpenVarBindingModify a b) ScopedName (OpenVarBindingModify a b)+    where+        keyVal   ovbm     = (openVbmName ovbm,ovbm)+        newEntry (_,ovbm) = ovbm++-- |Allow an OpenVarBindingModify value to be accessed using a LookupMap.+--+instance Show (OpenVarBindingModify a b)+    where+        show = show . openVbmName++-- |Variable binding modifier compatibility test.+--+--  Given a list of bound variables and a variable binding modifier, return+--  a list of new variables that may be bound, or @Nothing@.+--+--  Note:  if the usage pattern component is well-formed (i.e. all+--  elements different) then at most one element can be compatible with+--  a given input variable set.+--+vbmCompatibility :: (Eq a) => VarBindingModify a b -> [a] -> Maybe [a]+vbmCompatibility vbm vars = find compat (vbmUsage vbm)+    where+        compat = vbmCompatibleVars vars (vbmVocab vbm)++-- |Variable binding usage compatibility test.+--+--  Returns @True@ if the supplied variable bindings can be compatibly+--  processed by a variable binding usage with supplied vocabulary and+--  usage pattern.+--+vbmCompatibleVars ::+  (Eq a) +  => [a] -- ^ variables supplied with bindings+  -> [a] -- ^ variables returned with bindings by a modifier+  -> [a] -- ^ variables assigned new bindings by a modifier+  -> Bool+vbmCompatibleVars bvars vocab ovars =+    null (ivars `intersect` ovars) &&       -- ivars and ovars don't overlap+    null ((vocab \\ ovars) \\ ivars)        -- ovars and ivars cover vocab+    where+        ivars = bvars `intersect` vocab++-- |Compose variable binding modifiers.+--+--  Returns @Just a@ new variable binding modifier that corresponds to+--  applying the first supplied modifier and then applying the second+--  one, or @Nothing@ if the two modifiers cannot be compatibly composed.+--+--  NOTE:  this function does not, in general, commute.+--+--  NOTE:  if there are different ways to achieve the same usage, that+--  usage is currently repeated in the result returned.+--+vbmCompose :: (Eq a) => VarBindingModify a b -> VarBindingModify a b+    -> Maybe (VarBindingModify a b)+vbmCompose+    (VarBindingModify nam1 app1 voc1 use1)+    (VarBindingModify nam2 app2 voc2 use2)+    | not (null use12) = Just VarBindingModify+        { vbmName  = name+        , vbmApply = app2 . app1+        , vbmVocab = voc1 `union` voc2+        , vbmUsage = use12+        }+    | otherwise = Nothing+    where+        use12 = compatibleUsage voc1 use1 use2+        getName = getLName . getScopeLocal+        -- since _ is a valid LName component then we know the mconcat output+        -- is a valid LName and so can use fromJust+        name = swishName $ fromJust $ newLName $ mconcat ["_", getName nam1, "_", getName nam2, "_"]++-- |Determine compatible ways in which variable binding modifiers may+--  be combined.+--+--  The total vocabulary of a modifier is the complete set of variables+--  that are used or bound by the modifier.  After the modifier has been+--  applied, bindings must exist for all of these variables.+--+--  A usage pattern of a modifier is a set of variables for which new+--  bindings may be generated by the modifier.+--+--  The only way in which two variable binding modifiers can be incompatible+--  with each other is when they both attempt to create a new binding for+--  the same variable.  (Note that this does not mean the composition will+--  be compatible with all inputs:  see @vbmCompatibleVars@.)+--+--  NOTE:  if there are different ways to achieve the same usage, that+--  usage is currently repeated in the result returned.+--+compatibleUsage ::+  (Eq a)+  => [a]   -- ^ the total vocabulary of the first modifier to be applied+  -> [[a]] -- ^ usage patterns for the first modifier+  -> [[a]] -- ^ usage patterns for the second modifier+  -> [[a]] -- ^ a list of possible usage patterns for the composition of+           --  the first modifier with the second modifier, or an empty list if+           --  the modifiers are incompatible.+compatibleUsage voc1 use1 use2 =+    [ u1++u2 | u2 <- use2, null (voc1 `intersect` u2), u1 <- use1 ]++-- |Find all compatible compositions of a list of variable binding+--  modifiers for a given set of supplied bound variables.+findCompositions :: +    (Eq a) => [VarBindingModify a b] +    -> [a]+    -> [VarBindingModify a b]+findCompositions vbms vars =+    mapMaybe (composeCheckSequence vars) (permutations vbms)++-- |Compose sequence of variable binding modifiers, and check+--  that the result can be used compatibly with a supplied list+--  of bound variables, returning @Just (composed modifier)@,+--  or @Nothing@.+--+composeCheckSequence :: (Eq a) => [a] -> [VarBindingModify a b]+    -> Maybe (VarBindingModify a b)+composeCheckSequence vars vbms = useWith vars $ composeSequence vbms+    where+        --  Check that a Maybe modifier is compatible for use with an+        --  indicated set of bound variables, and return (Just modifier)+        --  or Nothing.+        useWith _    Nothing    = Nothing+        useWith vs v@(Just vbm)+            | isJust $ vbmCompatibility vbm vs = v+            | otherwise                        = Nothing++-- |Compose sequence of variable binding modifiers.+--+composeSequence :: (Eq a) => [VarBindingModify a b]+    -> Maybe (VarBindingModify a b)+composeSequence [] = Just varBindingId+composeSequence (vbm:vbms) = foldl' composePair (Just vbm) vbms++-- |Compose a pair of variable binding modifiers, returning+--  @Just (composed modifier)@, or @Nothing@.+--+composePair :: (Eq a) => Maybe (VarBindingModify a b) -> VarBindingModify a b+    -> Maybe (VarBindingModify a b)+composePair Nothing     _    = Nothing+composePair (Just vbm1) vbm2 = vbmCompose vbm1 vbm2++-- |Return @Just a@ compatible composition of variable binding modifiers+--  for a given set of supplied bound variables, or @Nothing@ if there+--  is no compatible composition+--+findComposition :: (Eq a) => [VarBindingModify a b] -> [a]+    -> Maybe (VarBindingModify a b)+findComposition = listToMaybe `c2` findCompositions+    where+        c2 = (.) . (.)  -- compose with function of two arguments++-- |Variable binding modifier that returns exactly those+--  variable bindings presented.+--+varBindingId :: VarBindingModify a b+varBindingId = VarBindingModify+    { vbmName   = swishName "varBindingId"+    , vbmApply  = id+    , vbmVocab  = []+    , vbmUsage  = [[]]+    }++-- |Null variable binding modifier+--+--  This is like 'varBindingId' except parameterized by some labels.+--  I think this is redundant, and should be eliminated.+--+nullVarBindingModify :: OpenVarBindingModify a b+nullVarBindingModify lbs = VarBindingModify+    { vbmName   = swishName "nullVarBindingModify"+    , vbmApply  = id+    , vbmVocab  = lbs+    , vbmUsage  = [[]]+    }++------------------------------------------------------------+--  Query binding filters+------------------------------------------------------------++-- |VarBindingFilter is a function type that tests to see if+--  a query binding satisfies some criterion.+--+--  Queries often want to apply some kind of filter or condition+--  to the variable bindings that are processed.  In inference rules,+--  it sometimes seems desirable to stipulate additional conditions on+--  the things that are matched.+--+--  This function type is used to perform such tests.+--  A number of simple implementations are included below.+data VarBindingFilter a b = VarBindingFilter+    { vbfName   :: ScopedName+    , vbfVocab  :: [a]+    , vbfTest   :: VarBinding a b -> Bool+    }++-- |Make a variable binding modifier from a variable binding filter value.+makeVarFilterModify :: VarBindingFilter a b -> VarBindingModify a b+makeVarFilterModify vbf = VarBindingModify+    { vbmName   = vbfName vbf+    , vbmApply  = filter (vbfTest vbf)+    , vbmVocab  = vbfVocab vbf+    , vbmUsage  = [[]]+    }++-- |Make a variable test filter for a named variable using a+--  supplied value testing function.+makeVarTestFilter ::+    ScopedName -> (b -> Bool) -> a -> VarBindingFilter a b+makeVarTestFilter nam vtest var = VarBindingFilter+    { vbfName   = nam+    , vbfVocab  = [var]+    , vbfTest   = \vb -> case vbMap vb var of+                    Just val  -> vtest val+                    _         -> False+    }++-- |Make a variable comparison filter for named variables using+--  a supplied value comparison function.+makeVarCompareFilter ::+    ScopedName -> (b -> b -> Bool) -> a -> a -> VarBindingFilter a b+makeVarCompareFilter nam vcomp v1 v2 = VarBindingFilter+    { vbfName   = nam+    , vbfVocab  = [v1,v2]+    , vbfTest   = \vb -> case (vbMap vb v1,vbMap vb v2) of+                    (Just val1, Just val2) -> vcomp val1 val2+                    _                      -> False+    }++------------------------------------------------------------+--  Declare some generally useful query binding filters+------------------------------------------------------------++-- |This function generates a query binding filter that ensures that+--  two indicated query variables are mapped to the same value.+varFilterEQ :: (Eq b) => a -> a -> VarBindingFilter a b+varFilterEQ =+    makeVarCompareFilter (swishName "varFilterEQ") (==) ++-- |This function generates a query binding filter that ensures that+--  two indicated query variables are mapped to different values.+varFilterNE :: (Eq b) => a -> a -> VarBindingFilter a b+varFilterNE =+    makeVarCompareFilter (swishName "varFilterNE") (/=) ++-- |This function composes a number of query binding filters+--  into a composite filter that accepts any query binding that+--  satisfies at least one of the component values.+varFilterDisjunction :: (Eq a) => [VarBindingFilter a b]+    -> VarBindingFilter a b+varFilterDisjunction vbfs = VarBindingFilter+    { vbfName   = swishName "varFilterDisjunction"+    , vbfVocab  = foldl1 union (map vbfVocab vbfs)+    , vbfTest   = or . flist (map vbfTest vbfs)+    }++-- |This function composes a number of query binding filters+--  into a composite filter that accepts any query binding that+--  satisfies all of the component values.+--+--  The same function could be achieved by composing the component+--  filter-based modifiers, but this function is more convenient+--  as it avoids the need to check for modifier compatibility.+--+varFilterConjunction :: (Eq a) => [VarBindingFilter a b]+    -> VarBindingFilter a b+varFilterConjunction vbfs = VarBindingFilter+    { vbfName   = swishName "varFilterConjunction"+    , vbfVocab  = foldl1 union (map vbfVocab vbfs)+    , vbfTest   = and . flist (map vbfTest vbfs)+    }++--------------------------------------------------------------------------------+--+--  (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke+--  All rights reserved.+--+--  This file is part of Swish.+--+--  Swish is free software; you can redistribute it and/or modify+--  it under the terms of the GNU General Public License as published by+--  the Free Software Foundation; either version 2 of the License, or+--  (at your option) any later version.+--+--  Swish is distributed in the hope that it will be useful,+--  but WITHOUT ANY WARRANTY; without even the implied warranty of+--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+--  GNU General Public License for more details.+--+--  You should have received a copy of the GNU General Public License+--  along with Swish; if not, write to:+--    The Free Software Foundation, Inc.,+--    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+--+--------------------------------------------------------------------------------
swish.cabal view
@@ -1,5 +1,5 @@ Name:               swish-Version:            0.6.5.2+Version:            0.7.0.0 Stability:          experimental License:            LGPL License-file:       LICENSE @@ -9,7 +9,7 @@ Category:           Semantic Web Synopsis:           A semantic web toolkit.  -Tested-With:        GHC==7.0.4+Tested-With:        GHC==7.4.2 Cabal-Version:      >= 1.9.2 Homepage:           https://bitbucket.org/doug_burke/swish/wiki/Home Bug-reports:        https://bitbucket.org/doug_burke/swish/issues@@ -44,139 +44,78 @@   .   * Complete, ready-to-run, command-line and script-driven programs.   .-  Changes:-  .-  [Version 0.6.5.2] Update cabal to allow @polyparse@ version @1.8@-  and hopefully to build with ghc 7.4. The tests and application now-  build against the library rather than re-compiling (internal-  change only, thanks to a hint seen on haskell-cafe a while back).-  .-  [Version 0.6.5.1] Haddock fixes for @0.6.5.0@.-  .-  [Version 0.6.5.0] Export "Swish.RDF.TurtleFormatter.parseText".-  Updated @filepath@ dependency and removed unused @array@ one.-  .-  [Version 0.6.4.0] Added support for xsd:decimal with "Swish.RDF.RDFDatatypeXsdDecimal"-  and "Swish.RDF.MapXsdDecimal" thanks to William Waites <https://bitbucket.org/ww>.-  Added "Swish.RDF.Vocabulary.Provenance".-  .-  [Version 0.6.3.0] Added "Swish.RDF.Vocabulary.SIOC".-  .-  [Version 0.6.2.1] Hackage did not want to upload @0.6.2.0@, so re-try by -  disabling the @hpc@ and @developer@ flags for the tests to -  work around what appears to be <http://hackage.haskell.org/trac/hackage/ticket/811>.-  .-  [Version 0.6.2.0] Updated "Swish.RDF.Vocabulary" and "Swish.RDF.RDFGraph" to-  include more common RDF terms. Added "Swish.RDF.Vocabulary.DublinCore",-  "Swish.RDF.Vocabulary.FOAF", and "Swish.RDF.Vocabulary.Geo" modules,-  "Swish.RDF.Vocabulary.OWL", "Swish.RDF.Vocabulary.RDF", and -  "Swish.RDF.Vocabulary.XSD" modules (re-exported from "Swish.RDF.Vocabulary"-  as necessary). The test suite has been updated to take advantage of the-  support in recent Cabal versions which means that the @tests@ flag has been-  removed and the minimum Cabal version increased to @1.9.2@. A change was-  made to the internal labelling of the RDFS container-property axioms in-  "Swish.RDF.RDFProofContext".+  Changes in version @0.7.0.0@:   .-  [Version 0.6.1.2] Corrected minimum mtl constraint from 1 to 2 and updated-  the maximum time version to 1.4 from 1.3.+  For code that uses the Swish script language, the main change is to import @Swish@ rather+  than @Swish.RDF.SwishMain@, and to note that the other @Swish.RDF.Swish*@ modules are+  now called @Swish.*@.   .-  [Version 0.6.1.1] Minor improvement to the error message produced by the-  Turtle, Ntriples, and N3 parsers: a fragment of the remaining text is-  included to provide some context (still lacking compared to the information-  provided in version 0.3.2.1).+  For code that uses the graph library, the main changes are that @Swish.RDF.RDFGraph@+  is now called @Swish.RDF.Graph@, the @Lit@ constructor of the @RDFLabel@ has been split+  into three (@Lit@, @LangLit@, and @TypedLit@) and a new @LanguageTag@ type introduced,+  local names now use the @LName@ type (previously they were just @Text@ values), and the+  parsers and formatters have renamed to+  @Swish.RDF.Parser.*@ and @Swish.RDF.Formatter.*@.   .-  [Version 0.6.1.0] Added support for Turtle format (added the-  "Swish.RDF.TurtleFormatter" and "Swish.RDF.TurtleParser" modules).+  * Moved a number of modules around: generic code directly into @Swish@+  and the @Swish.RDF.RDF*@ forms renamed to @Swish.RDF.*@. Some modules+  have been moved out of the @Swish.Utils.*@ namespace. Generic modules+  have been placed into the @Data.*@ namespace. The @Swish.RDF.Swish*@+  modules have been moved to @Swish.*@ and @Swish.RDF.SwishMain@ has+  been removed; use @Swish@ instead.   .-  [Version 0.6.0.2] Minor internal changes.+  * Parsing modules are now in the @Swish.RDF.Parser@ hierarchy and+  @Swish.RDF.RDFParser@ has been renamed to @Swish.RDF.Parser.Utils@.   .-  [Version 0.6.0.1] Moved to using hashing routine using the @Data.Hashable@-  interface rather than "Swish.Utils.MiscHelpers", which is deprecated.+  * Formatting modules are now in the @Swish.RDF.Formatter@ hierarchy.   .-  [Version 0.6.0.0] Add "Data.Interned.URI" and use it to speed up the 'QName'-  equality check.+  * RDF literals are now stored using the @Lit@, @LangLit@, or @TypedLit@ constructors+  (@RDFLabel@) rather than using just @Lit@. Language codes are now represented+  by @Swish.RDF.Vocabulary.LanguageTag@ rather than as a @ScopedName@.   .-  [Version 0.5.0.3] Didn't get all the required @FlexibleInstances@.+  * Local names are now represented by the @Swish.QName.LName@ type +  rather than as a @Text@ value. A few routines now return a @Maybe@ value+  rather than error-ing out on invalid input.   .-  [Version 0.5.0.2] HUnit constraint is only added when the @tests@ flag-  is used. Removed random and bytestring constraints. Add @FlexibleInstances@-  pragma for ghc 7.2 compatability.+  * Make use of @Data.List.NonEmpty@ in a few cases.   .-  [Version 0.5.0.1] Update bounds on package constraints to try and get a-  successful build on ghc 7.2; removed parallel constraint as not used.+  * Removed @mkTypedLit@ from @Swish.RDF.RDFParser@; use+  @Swish.RDF.RDFDatatype.makeDataTypedLiteral@ instead.   .-  [Version 0.5.0.0] The constructors for @ScopedName@ and @QName@ have been-  removed to hide some experimental optimisations (partly added in 0.4.0.0);-  @Namespace@ has seen a similar change but no optimisation. Output speed-  should be improved but no systematic analysis has been performed.+  * Removed @asubj@, @apred@ and @aobj@ from @Swish.RDF.GraphClass@ and+  @Swish.RDF.RDFGraph@; use @arcSubj@, @arcPred@ or @arcObj@ instead.   .-  [Version 0.4.0.0] Moving to using polyparse for parsing and @Text@ rather than-  @String@ where appropriate. Use of @URI@ and @Maybe Text@ rather than @String@ in the @Namespace@-  type. Removed the Swish.Utils.DateTime and Swish.Utils.TraceHelpers-  modules. Symbols have been removed from the export lists of the following modules:-  Swish.Utils.LookupMap, Swish.Utils.ListHelpers, Swish.Utils.MiscHelpers,-  Swish.Utils.ShowM. Some significant improvements to parsing speed, but no-  concerted effort or checks made yet.+  * Removed un-used @containedIn@ element of the @LDGraph@ type class.+  The arguments to @setArcs@ have been flipped, @replaceArcs@ removed,+  @add@ renamed to @addGraphs@, and @emptyGraph@ added.   .-  [Version 0.3.2.1] Marked a number of routines from the Swish.Utils modules-  as deprecated. Use foldl' rather than foldl.+  * Removed un-used exports from @Swish.Utils.PartOrderedCollection@: +  @partCompareOrd@, @partCompareMaybe@, @partCompareListOrd@, and+  @partCompareListPartOrd@.   .-  [Version 0.3.2.0] The N3 parser no longer assumes a set of pre-defined namespaces.-  There is no API change worthy of a bump to the minor version number, but it-  is a large-enough change in behaviour that I felt the need for the update.+  * Removed the @Swish.Utils.MiscHelpers@ module and moved single-use functionality+  out of @Swish.Utils.ListHelpers@.   .-  [Version 0.3.1.2] 'Swish.RDF.RDFGraph.toRDFGraph' now sets up the-  namespace map of the graph based on the input labels (previously it-  left the map empty).+  * Removed various exported symbols from a range of modules as they were+  unused.   .-  [Version 0.3.1.1] Bug fixes for N3 format: strings ending in a -  double quote character are now written out correctly and-  @xsd:double@ values are not written using XSD canonical form/capital-  @E@ but with a lower-case @e@. On input of N3,-  literals that match @xsd:double@ are converted to XSD canonical form-  (as stored in 'RDFLabel'), which can make simple textual comparison-  of literals fail. The 'Eq' instance of 'RDFLabel' now ignores the-  case of the language tag for literals and the 'Show' instance -  uses XSD canonical form for @xsd:boolean@, @xsd:integer@,-  @xsd:decimal@ and @xsd:double@ literals. -  Noted that the 'ToRDFLable' and 'FromRDFLabel' classes replicate-  existing functionality in the "Swish.RDF.RDFDatatype" module.+  * Use @Word32@ rather than @Int@ for label indexes (@Swish.GraphMatch.LabelIndex@)+  and in the bnode counts when formatting to N3/Turtle.   .-  [Version 0.3.1.0] Added the `Swish.RDF.RDFGraph.ToRDFLabel` and-  `Swish.RDF.RDFGraph.FromRDFLabel` classes and the -  `Swish.RDF.RDFGraph.toRDFTriple` and `Swish.RDF.RDFGraph.fromRDFTriple`-  functions.-  Added instances: @IsString RDFLabel@, @IsString QName@, @IsString ScopedName@-  and @Monoid NSGraph@.-  The modules "Swish" and "Swish.RDF" have been introduced to provide-  documentation. The module "Swish.Utils.DateTime" is deprecated and-  will be removed in a later release.-  The N3 formatter now writes out literals with @xsd:boolean@, @xsd:integer@, -  @xsd:decimal@ and @xsd:double@ types as literals rather than as a typed string.+  * Minor clean up of the @LookupMap@ module: @mergeReplaceOrAdd@ and @mergeReplace@+  are now combined into @mergeReplace@; @mapSelect@, @mapApplyToAll@, and+  @mapTranslate*@ have been removed; documentation slightly improved;+  and a few minor internal clean ups.   .-  [Version 0.3.0.3] Changed @scripts/SwishExample.ss@ script so that the-  proof succeeds. Some documentation improvements, including a discussion-  of the Swish script format (see "Swish.RDF.SwishScript"). Very minor-  changes to behavior of Swish in several edge cases.+  * Clarified that @Swish.RDF.RDFDatatypeXsdDecimal@ is for @xsd:decimal@ rather+  than @xsd:double@.   .-  [Version 0.3.0.2] Bugfix: stop losing triples with a bnode subject when-  using the N3Formatter; this also makes the @scripts/SwishTest.ss@ test-  pass again. Several commands in Swish scripts now create screen-  output (mainly to check what it is doing). Added the @developer@-  flag for building.+  * Support using versions 0.8 or 0.9 of the @intern@ package and version 0.5 of+  @containers@.   .-  [Version 0.3.0.1] updates the Swish script parser to work with the-  changes in 0.3.0.0 (reported by Rick Murphy).-  Several example scripts are installed in the-  @scripts/@ directory, although only @VehicleCapacity.ss@ works-  with this release.+  * Switch to @Control.Exception.try@ to avoid deprecation warnings from @System.IO.Error.try@.   .-  [Version 0.3.0.0] is an attempt to update -  version 0.2.1 (<http://hackage.haskell.org/package/swish-0.2.1/>)-  to build against-  a recent ghc install, with some clean ups - including support for-  the current N3 specification - and the addition of the-  NTriples format. It has not been tested against ghc7.+  Changes in previous versions can be found at <https://bitbucket.org/doug_burke/swish/src/tip/CHANGES>.   .   References:   .@@ -207,64 +146,65 @@    Build-Depends:       base >=3 && < 5,       binary == 0.5.*,-      containers >= 0.3 && < 0.5,+      containers >= 0.3 && < 0.6,       directory >= 1.0 && < 1.2,       filepath >= 1.1 && < 1.4,       hashable == 1.1.*,-      -- intern == 0.8,       mtl >= 2 && < 3,       network >= 2.2 && < 2.4,       old-locale == 1.0.*,        polyparse >= 1.6 && < 1.9,+      semigroups >= 0.5 && < 0.9,       text == 0.11.*,       time >= 1.1 && < 1.5     if impl(ghc < 7.4.0)       Build-Depends:   intern == 0.8    if impl(ghc >= 7.4.0)-      Build-Depends:   intern == 0.8.*+      Build-Depends:   intern >= 0.8 && < 1.0     Hs-Source-Dirs: src/     Exposed-Modules:+      Data.Interned.URI+      Data.LookupMap+      Data.Ord.Partial+      Data.String.ShowLines       Swish+      Swish.Commands+      Swish.Datatype+      Swish.GraphClass+      Swish.GraphMatch+      Swish.GraphMem+      Swish.GraphPartition+      Swish.Monad+      Swish.Namespace+      Swish.Proof+      Swish.QName       Swish.RDF-      Swish.RDF.BuiltInDatatypes-      Swish.RDF.BuiltInMap-      Swish.RDF.BuiltInRules+      Swish.RDF.BuiltIn+      Swish.RDF.BuiltIn.Datatypes+      Swish.RDF.BuiltIn.Rules       Swish.RDF.ClassRestrictionRule       Swish.RDF.Datatype-      Swish.RDF.GraphClass-      Swish.RDF.GraphMatch-      Swish.RDF.GraphMem-      Swish.RDF.GraphPartition-      Swish.RDF.MapXsdDecimal-      Swish.RDF.MapXsdInteger-      Swish.RDF.NTFormatter-      Swish.RDF.NTParser-      Swish.RDF.N3Formatter-      Swish.RDF.N3Parser+      Swish.RDF.Datatype.XSD.Decimal+      Swish.RDF.Datatype.XSD.Integer+      Swish.RDF.Datatype.XSD.MapDecimal+      Swish.RDF.Datatype.XSD.MapInteger+      Swish.RDF.Datatype.XSD.String+      Swish.RDF.Formatter.NTriples+      Swish.RDF.Formatter.N3+      Swish.RDF.Formatter.Turtle+      Swish.RDF.Graph+      Swish.RDF.GraphShowLines+      Swish.RDF.Parser.NTriples+      Swish.RDF.Parser.N3+      Swish.RDF.Parser.Turtle+      Swish.RDF.Parser.Utils       Swish.RDF.Proof-      Swish.RDF.RDFDatatype-      Swish.RDF.RDFDatatypeXsdDecimal-      Swish.RDF.RDFDatatypeXsdInteger-      Swish.RDF.RDFDatatypeXsdString-      Swish.RDF.RDFGraph-      Swish.RDF.RDFGraphShowM-      Swish.RDF.RDFParser-      Swish.RDF.RDFProof-      Swish.RDF.RDFProofContext-      Swish.RDF.RDFQuery-      Swish.RDF.RDFRuleset-      Swish.RDF.RDFVarBinding-      Swish.RDF.Rule+      Swish.RDF.ProofContext+      Swish.RDF.Query       Swish.RDF.Ruleset-      Swish.RDF.SwishCommands-      Swish.RDF.SwishMain-      Swish.RDF.SwishMonad-      Swish.RDF.SwishScript-      Swish.RDF.TurtleFormatter-      Swish.RDF.TurtleParser       Swish.RDF.VarBinding       Swish.RDF.Vocabulary       Swish.RDF.Vocabulary.DublinCore@@ -275,14 +215,11 @@       Swish.RDF.Vocabulary.RDF       Swish.RDF.Vocabulary.SIOC       Swish.RDF.Vocabulary.XSD+      Swish.Rule+      Swish.Ruleset+      Swish.Script       Swish.Utils.ListHelpers-      Swish.Utils.LookupMap-      Swish.Utils.MiscHelpers-      Swish.Utils.Namespace-      Swish.Utils.PartOrderedCollection-      Swish.Utils.QName-      Swish.Utils.ShowM-      Data.Interned.URI+      Swish.VarBinding     ghc-options:       -Wall -fno-warn-orphans@@ -317,6 +254,7 @@    Build-Depends:       base >=3 && < 5,       HUnit == 1.2.*,+      semigroups >= 0.5 && < 0.9,       swish  Test-Suite test-graph
tests/BuiltInMapTest.hs view
@@ -5,7 +5,7 @@ -------------------------------------------------------------------------------- -- | --  Module      :  BuiltInMapTest---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke --  License     :  GPL V2 -- --  Maintainer  :  Douglas Burke@@ -19,23 +19,17 @@  module Main where -import Swish.RDF.BuiltInMap+import Swish.Namespace (makeNSScopedName)+import Swish.Ruleset (getMaybeContextAxiom, getMaybeContextRule)++import Swish.RDF.BuiltIn     ( findRDFOpenVarBindingModifier     , findRDFDatatype     , rdfRulesetMap     , allRulesets     ) -import Swish.RDF.RDFDatatypeXsdInteger-    ( typeNameXsdInteger, namespaceXsdInteger-    )--import Swish.RDF.Ruleset-    ( getMaybeContextAxiom-    , getMaybeContextRule-    )--import Swish.Utils.Namespace (makeNSScopedName)+import Swish.RDF.Datatype.XSD.Integer (typeNameXsdInteger, namespaceXsdInteger)  import Swish.RDF.Vocabulary     ( swishName@@ -45,7 +39,7 @@     , namespaceXsdType     ) -import Swish.Utils.LookupMap (mapFindMaybe)+import Data.LookupMap (mapFindMaybe)  import Test.HUnit     ( Test(TestCase,TestList)@@ -261,7 +255,8 @@  -------------------------------------------------------------------------------- -----  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke --  All rights reserved. -- --  This file is part of Swish.
tests/GraphPartitionTest.hs view
@@ -3,7 +3,7 @@ -------------------------------------------------------------------------------- -- | --  Module      :  GraphPartitionTest---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke --  License     :  GPL V2 -- --  Maintainer  :  Douglas Burke@@ -16,18 +16,19 @@  module Main where -import Swish.RDF.GraphPartition+import Swish.GraphPartition     ( PartitionedGraph(..), getArcs     , GraphPartition(..), node     , partitionGraph, comparePartitions     ) -import Swish.RDF.GraphClass (Arc(..))--import Swish.RDF.GraphMem (LabelMem(..))+import Swish.GraphClass (Arc(..))+import Swish.GraphMem (LabelMem(..))  import Swish.Utils.ListHelpers (equiv) +import Data.List.NonEmpty (fromList)+ import Test.HUnit (Test(TestCase, TestList),                    assertEqual, assertBool) @@ -89,24 +90,36 @@ gp4 = PartitionedGraph [ p11, p14 ] gp5 = PartitionedGraph [ p11, p12, p15 ] +toPF :: +    String +    -> [(LabelMem, GraphPartition LabelMem)] +    -> GraphPartition LabelMem+toPF l xs = PartSub (LF l) (fromList xs)++toPV :: +    String +    -> [(LabelMem, GraphPartition LabelMem)] +    -> GraphPartition LabelMem+toPV l xs = PartSub (LV l) (fromList xs)+ p11, p12, p13, p14, p15 :: GraphPartition LabelMem -p11 = PartSub (LF "s1") [ (LF "p11",PartObj (LF "o11")) ]-p12 = PartSub (LF "s2") [ (LF "p21",PartObj (LF "o21"))-                        , (LF "p22",PartObj (LF "o22"))-                        ]-p13 = PartSub (LF "s3") [ (LF "p31",PartObj (LF "o31"))-                        , (LF "p32",p12)-                        , (LF "p33",PartObj (LF "s3"))-                        ]-p14 = PartSub (LF "s3") [ (LF "p31",PartObj (LF "o31"))-                        , (LF "p33",PartObj (LF "s3"))-                        , (LF "p32",p12)-                        ]-p15 = PartSub (LF "s3") [ (LF "p31",PartObj (LF "o31"))-                        , (LF "p32",PartObj (LF "s2"))-                        , (LF "p33",PartObj (LF "s3"))-                        ]+p11 = toPF "s1" [ (LF "p11",PartObj (LF "o11")) ]+p12 = toPF "s2" [ (LF "p21",PartObj (LF "o21"))+                , (LF "p22",PartObj (LF "o22"))+                ]+p13 = toPF "s3" [ (LF "p31",PartObj (LF "o31"))+                , (LF "p32",p12)+                , (LF "p33",PartObj (LF "s3"))+                ]+p14 = toPF "s3" [ (LF "p31",PartObj (LF "o31"))+                , (LF "p33",PartObj (LF "s3"))+                , (LF "p32",p12)+                ]+p15 = toPF "s3" [ (LF "p31",PartObj (LF "o31"))+                , (LF "p32",PartObj (LF "s2"))+                , (LF "p33",PartObj (LF "s3"))+                ]  ga1, ga2, ga3, ga4, ga5 :: [Arc LabelMem] @@ -327,77 +340,77 @@ ps1, ps2f, ps2r, pb3f, pb3r, pb3af, pb3ar,   pb4af, pb4ar :: GraphPartition LabelMem -ps1  = PartSub (LF "s1") [ (LF "p",PartObj (LF "o11")) ]-ps2f = PartSub (LF "s2") [ (LF "p1",PartObj (LF "o21"))-                         , (LF "p2",PartObj (LF "o22"))-                         ]-ps2r = PartSub (LF "s2") [ (LF "p2",PartObj (LF "o22"))-                         , (LF "p1",PartObj (LF "o21"))-                         ]-pb3f = PartSub (LV "b3") [ (LF "p",PartObj (LF "o31"))-                         , (LF "p",PartObj (LF "s2"))-                         , (LF "p",PartObj (LV "b3"))-                         ]-pb3r = PartSub (LV "b3") [ (LF "p",PartObj (LV "b3"))-                         , (LF "p",PartObj (LF "s2"))-                         , (LF "p",PartObj (LF "o31"))-                         ]+ps1  = toPF "s1" [ (LF "p",PartObj (LF "o11")) ]+ps2f = toPF "s2" [ (LF "p1",PartObj (LF "o21"))+                 , (LF "p2",PartObj (LF "o22"))+                 ]+ps2r = toPF "s2" [ (LF "p2",PartObj (LF "o22"))+                 , (LF "p1",PartObj (LF "o21"))+                 ]+pb3f = toPV "b3" [ (LF "p",PartObj (LF "o31"))+                 , (LF "p",PartObj (LF "s2"))+                 , (LF "p",PartObj (LV "b3"))+                 ]+pb3r = toPV "b3" [ (LF "p",PartObj (LV "b3"))+                 , (LF "p",PartObj (LF "s2"))+                 , (LF "p",PartObj (LF "o31"))+                 ] -pb3af = PartSub (LV "b3") [ (LF "p",PartObj (LF "o31"))-                          , (LF "p",PartObj (LF "s2"))-                          , (LF "p",PartObj (LV "b3"))-                          , (LF "p",pb4af)-                          ]-pb3ar = PartSub (LV "b3") [ (LF "p",pb4ar)-                          , (LF "p",PartObj (LV "b3"))-                          , (LF "p",PartObj (LF "s2"))-                          , (LF "p",PartObj (LF "o31"))-                          ]-pb4af = PartSub (LV "b4") [ (LF "p",PartObj (LF "s2"))-                          , (LF "p",PartObj (LV "b3"))-                          ]-pb4ar = PartSub (LV "b4") [ (LF "p",PartObj (LV "b3"))-                          , (LF "p",PartObj (LF "s2"))-                          ]+pb3af = toPV "b3" [ (LF "p",PartObj (LF "o31"))+                  , (LF "p",PartObj (LF "s2"))+                  , (LF "p",PartObj (LV "b3"))+                  , (LF "p",pb4af)+                  ]+pb3ar = toPV "b3" [ (LF "p",pb4ar)+                  , (LF "p",PartObj (LV "b3"))+                  , (LF "p",PartObj (LF "s2"))+                  , (LF "p",PartObj (LF "o31"))+                  ]+pb4af = toPV "b4" [ (LF "p",PartObj (LF "s2"))+                  , (LF "p",PartObj (LV "b3"))+                  ]+pb4ar = toPV "b4" [ (LF "p",PartObj (LV "b3"))+                  , (LF "p",PartObj (LF "s2"))+                  ]          pb5a1, pb5b1, pb5c1 :: GraphPartition LabelMem -pb5a1 = PartSub (LV "b5a") [ (LF "p",pb5b1) ]-pb5b1 = PartSub (LV "b5b") [ (LF "p",pb5c1) ]-pb5c1 = PartSub (LV "b5c") [ (LF "p",PartObj (LV "b5a")) ]+pb5a1 = toPV "b5a" [ (LF "p",pb5b1) ]+pb5b1 = toPV "b5b" [ (LF "p",pb5c1) ]+pb5c1 = toPV "b5c" [ (LF "p",PartObj (LV "b5a")) ]  pb3bf, pb3br, pb4bf, pb4br :: GraphPartition LabelMem -pb3bf = PartSub (LV "b3") [ (LF "p",PartObj (LF "o31"))-                          , (LF "p",PartObj (LF "s2"))-                          , (LF "p",PartObj (LV "b3"))-                          , (LF "p",pb4bf)-                          ]-pb3br = PartSub (LV "b3") [ (LF "p",pb4br)-                          , (LF "p",PartObj (LV "b3"))-                          , (LF "p",PartObj (LF "s2"))-                          , (LF "p",PartObj (LF "o31"))-                          ]-pb4bf = PartSub (LV "b4") [ (LF "p",PartObj (LF "s2"))-                          , (LF "p",PartObj (LV "b3"))-                          , (LF "p",PartObj (LV "b5b"))-                          ]-pb4br = PartSub (LV "b4") [ (LF "p",PartObj (LV "b5b"))-                          , (LF "p",PartObj (LV "b3"))-                          , (LF "p",PartObj (LF "s2"))-                          ]+pb3bf = toPV "b3" [ (LF "p",PartObj (LF "o31"))+                  , (LF "p",PartObj (LF "s2"))+                  , (LF "p",PartObj (LV "b3"))+                  , (LF "p",pb4bf)+                  ]+pb3br = toPV "b3" [ (LF "p",pb4br)+                  , (LF "p",PartObj (LV "b3"))+                  , (LF "p",PartObj (LF "s2"))+                  , (LF "p",PartObj (LF "o31"))+                  ]+pb4bf = toPV "b4" [ (LF "p",PartObj (LF "s2"))+                  , (LF "p",PartObj (LV "b3"))+                  , (LF "p",PartObj (LV "b5b"))+                  ]+pb4br = toPV "b4" [ (LF "p",PartObj (LV "b5b"))+                  , (LF "p",PartObj (LV "b3"))+                  , (LF "p",PartObj (LF "s2"))+                  ]  pb5a2, pb5b2, pb5c2 :: GraphPartition LabelMem -pb5a2 = PartSub (LV "b5a") [ (LF "p",PartObj (LV "b5b")) ]-pb5b2 = PartSub (LV "b5b") [ (LF "p",pb5c2) ]-pb5c2 = PartSub (LV "b5c") [ (LF "p",pb5a2) ]+pb5a2 = toPV "b5a" [ (LF "p",PartObj (LV "b5b")) ]+pb5b2 = toPV "b5b" [ (LF "p",pb5c2) ]+pb5c2 = toPV "b5c" [ (LF "p",pb5a2) ]  pb5a3, pb5b3, pb5c3 :: GraphPartition LabelMem -pb5a3 = PartSub (LV "b5a") [ (LF "p",pb5b3) ]-pb5b3 = PartSub (LV "b5b") [ (LF "p",PartObj (LV "b5c")) ]-pb5c3 = PartSub (LV "b5c") [ (LF "p",pb5a3) ]+pb5a3 = toPV "b5a" [ (LF "p",pb5b3) ]+pb5b3 = toPV "b5b" [ (LF "p",PartObj (LV "b5c")) ]+pb5c3 = toPV "b5c" [ (LF "p",pb5a3) ]  testPartition11, testPartition12, testPartition13, testPartition14, testPartition15,    testPartition16 :: Test@@ -449,13 +462,13 @@ testPartition55 = testEqv "testPartition55" []   (comparePartitions pp5f pp5r) testPartition56 = testEqv "testPartition56" []   (comparePartitions pp6f pp6r) testPartition57 = testEqv "testPartition57"-        [(Nothing,Just $ PartSub (LV "b3") [(LF "p",pb4af)])]+        [(Nothing,Just $ toPV "b3" [(LF "p",pb4af)])]         (comparePartitions pp3f pp4f) testPartition58 = testEqv "testPartition58"         [(Nothing,Just pb5a1)]         (comparePartitions pp4f pp5f) testPartition59 = testEqv "testPartition59"-        [(Nothing,Just $ PartSub (LV "b4") [(LF "p",PartObj (LV "b5b"))])]+        [(Nothing,Just $ toPV "b4" [(LF "p",PartObj (LV "b5b"))])]         (comparePartitions pp5f pp6f)  testPartitionSuite :: Test@@ -507,19 +520,19 @@  c11, c12a, c12b, c13a, c13b :: GraphPartition LabelMem -c11  = PartSub (LF "s1") [ (LF "p11",PartObj (LF "o11")) ]-c12a = PartSub (LF "s2") [ (LF "p21",c13a)+c11  = toPF "s1" [ (LF "p11",PartObj (LF "o11")) ]+c12a = toPF "s2" [ (LF "p21",c13a)                          , (LF "p22",PartObj (LF "o22"))                          ]-c12b = PartSub (LF "s2") [ (LF "p22",PartObj (LF "o22"))+c12b = toPF "s2" [ (LF "p22",PartObj (LF "o22"))                          , (LF "p21",c13b)                          ]-c13a = PartSub (LV "b3") [ (LF "p31",PartObj (LF "o31"))-                         , (LF "p33",PartObj (LF "o33a"))-                         ]-c13b = PartSub (LV "b3") [ (LF "p31",PartObj (LF "o31"))-                         , (LF "p33",PartObj (LF "o33b"))-                         ]+c13a = toPV "b3" [ (LF "p31",PartObj (LF "o31"))+                 , (LF "p33",PartObj (LF "o33a"))+                 ]+c13b = toPV "b3" [ (LF "p31",PartObj (LF "o31"))+                 , (LF "p33",PartObj (LF "o33b"))+                 ]         testCompare01 :: Test testCompare01 = testEqv "testCompare01"@@ -556,7 +569,8 @@  -------------------------------------------------------------------------------- -----  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, +--    2011, 2012 Douglas Burke --  All rights reserved. -- --  This file is part of Swish.
tests/GraphTest.hs view
@@ -3,7 +3,7 @@ -------------------------------------------------------------------------------- -- | --  Module      :  GraphTest---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke --  License     :  GPL V2 -- --  Maintainer  :  Douglas Burke@@ -29,28 +29,31 @@       ( Test(TestCase,TestList,TestLabel),         assertEqual, assertBool ) -import Data.List (sort, elemIndex)-import Data.Maybe (fromJust)-import Data.Ord (comparing)--import Swish.Utils.ListHelpers-import Swish.RDF.GraphClass (Arc(..), LDGraph(..),-                             Label(..), arc,-                             arcFromTriple,arcToTriple)-import Swish.RDF.GraphMem-import Swish.RDF.GraphMatch+import Swish.GraphClass (Arc(..), LDGraph(..), Label(..))+import Swish.GraphClass (arc, arcFromTriple, arcToTriple)+import Swish.GraphMem+import Swish.GraphMatch       ( LabelMap, GenLabelMap(..), LabelEntry,          EquivalenceClass,         ScopedLabel(..), makeScopedLabel, makeScopedArc,         LabelIndex, nullLabelVal, emptyMap,         mapLabelIndex, {-mapLabelList,-} setLabelHash, newLabelMap,-        graphLabels, assignLabelMap, newGenerationMap,-        graphMatch1, equivalenceClasses+        graphLabels, assignLabelMap, newGenerationMap+        -- graphMatch1  only used with pairSort+	, equivalenceClasses       )-import Swish.Utils.LookupMap (LookupEntryClass(..), makeLookupMap) +import Swish.Utils.ListHelpers (subset)++import Data.LookupMap (LookupEntryClass(..), makeLookupMap)+ import TestHelpers (runTestSuite, testEq, testEqv) +import Data.List (sort, elemIndex)+import Data.Maybe (fromJust)+import Data.Ord (comparing)+import Data.Word (Word32)+ default ( Int )  ------------------------------------------------------------@@ -72,7 +75,7 @@  setArcsT :: (LDGraph lg lb) =>             [(lb, lb, lb)] -> lg lb -> lg lb-setArcsT a = setArcs $ map arcFromTriple a+setArcsT a g = setArcs g $ map arcFromTriple a  getArcsT :: (LDGraph lg lb) =>             lg lb -> [(lb, lb, lb)]@@ -100,7 +103,7 @@ --  Label map and entry creation helpers ------------------------------------------------------------ -tstLabelMap :: (Label lb) => Int -> [(lb,LabelIndex)] -> LabelMap lb+tstLabelMap :: (Label lb) => Word32 -> [(lb,LabelIndex)] -> LabelMap lb tstLabelMap gen lvs = LabelMap gen (makeLookupMap $ makeEntries lvs)  makeEntries :: (Label lb) => [(lb,LabelIndex)] -> [LabelEntry lb]@@ -110,8 +113,11 @@ --  Graph helper function tests ------------------------------------------------------------ --- select+{- +-- select; no longer exported so need to check that+-- other tests still test this routine+ testSelect :: String -> String -> String -> Test testSelect lab = testeq ("Select"++lab ) @@ -136,6 +142,8 @@     testSelect01, testSelect02, testSelect03, testSelect04     ] +-}+ -- subset  testSubset :: String -> Bool -> [Int] -> [Int] -> Test@@ -263,12 +271,12 @@ gr1a = setArcsT ga1 gr1 gr2a = setArcsT ga2 gr2 gr3a = setArcsT ga3 gr3-gr4a = add gr2a gr3a-gr4b = add gr3a gr2a+gr4a = addGraphs gr2a gr3a+gr4b = addGraphs gr3a gr2a gr4c = delete gr2a gr4a gr4d = delete gr3a gr4a gr4e = extract gs4 gr4a-gr4g = add gr2a gr4a+gr4g = addGraphs gr2a gr4a  gl4f :: [LabelMem] gl4f = labels gr4a@@ -665,30 +673,30 @@                        (b3,(1,262143)),(b4,(1,262143))] -} -bhash :: Int+bhash :: Word32 bhash = 23 -l1hash, l4hash, l10hash :: Int+l1hash, l4hash, l10hash :: Word32 l1hash = 2524 l4hash = -1302210307 l10hash = 10836024   -l1hash2, l4hash2, l10hash2 :: Int+l1hash2, l4hash2, l10hash2 :: Word32 l1hash2 = 2524 l4hash2 = -1302210307 l10hash2 = 10836024   -o1hash, o2hash, o3hash :: Int+o1hash, o2hash, o3hash :: Word32 o1hash = 2623 o2hash = 2620 o3hash = 2621 -p1hash, p2hash, p3hash :: Int+p1hash, p2hash, p3hash :: Word32 p1hash = 2624 p2hash = 2627 p3hash = 2626 -s1hash, s2hash, s3hash :: Int+s1hash, s2hash, s3hash :: Word32 s1hash = 2723 s2hash = 2720 s3hash = 2721@@ -1082,7 +1090,7 @@ testEqAssignMap32 = testeq "EqAssignMap32" eq3ltst eq3lmap  type EquivClass = EquivalenceClass (ScopedLabel LabelMem)-type EquivArgs  = ((Int, Int), [ScopedLabel LabelMem])+type EquivArgs  = ((Word32, Word32), [ScopedLabel LabelMem])  ec31 :: [EquivClass] ec31     = equivalenceClasses eq3lmap (graphLabels as11)@@ -1099,9 +1107,7 @@  ec32test :: [EquivArgs] ec32test =-    [ -      ((1,-1302210307),[l4_2])-    , ((1,2524),[l1_2])+    [ ((1,2524),[l1_2])     , ((1,2620),[o2_2])     , ((1,2621),[o3_2])     , ((1,2623),[o1_2])@@ -1110,18 +1116,23 @@     , ((1,2721),[s3_2])     , ((1,2723),[s1_2])     , ((1,10836024),[l10_2])+    , ((1,2992756989),[l4_2])     ]    testEquivClass33_1, testEquivClass33_2 :: Test testEquivClass33_1 = testeq "EquivClass33_1" ec31test ec31 testEquivClass33_2 = testeq "EquivClass33_2" ec32test ec32 +{- as pairSOrt is no-longer exported need to check this code gets tested+ -- This value is nonsense for this test, -- but a parameter is needed for graphMatch1 (below)  ec3pairs :: [(EquivClass, EquivClass)] ec3pairs = zip (pairSort ec31) (pairSort ec32) +-}+ {-  This is a pointless test in this case  ec3test :: [(EquivClass, EquivClass)]@@ -1134,9 +1145,13 @@ testEquivClass33_3 = testeq "EquivClass33_3" ec3test ec3pairs -} +{- pairSort is no longer exported+ eq3lmap1 :: (Bool, LabelMap (ScopedLabel LabelMem)) eq3lmap1 = graphMatch1 False matchable eq3hs1 eq3hs2 eq3lmap ec3pairs +-}+ eq3ltst1 :: LabelMap (ScopedLabel LabelMem) eq3ltst1 = tstLabelMap 2     [ (o1_1,(1,142321))@@ -1156,8 +1171,10 @@ -- testEqAssignMap34 = testeq "EqAssignMap34" (Just eq3ltst1) eq3lmap1 -- testEqAssignMap34 = testeq "EqAssignMap34" Nothing eq3lmap1 +{- pairSort is not exported testEqAssignMap34 :: Test testEqAssignMap34 = testeq "EqAssignMap34" False (fst eq3lmap1)+-}  {- eq3rc1      = reclassify eq3hs1 eq3lmap@@ -1184,7 +1201,7 @@   , testEqGraphMap31_1, testEqGraphMap31_2   , testEqAssignMap32   , testEquivClass33_1, testEquivClass33_2 -- , testEquivClass33_3-  , testEqAssignMap34+  -- , testEqAssignMap34    pairSort is not exported   -- , testEqReclassify35_1, testEqReclassify35_2   ] @@ -1839,8 +1856,8 @@  allTests :: Test allTests = TestList-  [ testSelectSuite-  , testSubsetSuite+  [ -- testSelectSuite+    testSubsetSuite   , testLabSuite   , testGraphSuite   , testLabelEqSuite@@ -1879,7 +1896,8 @@  -------------------------------------------------------------------------------- -----  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke --  All rights reserved. -- --  This file is part of Swish.
tests/LookupMapTest.hs view
@@ -17,19 +17,16 @@  module Main where -import Swish.Utils.LookupMap+import Data.LookupMap     ( LookupEntryClass(..), LookupMap(..)     , makeLookupMap     , reverseLookupMap     , mapFind, mapContains-    , mapReplace, mapReplaceOrAdd, mapReplaceAll, mapReplaceMap+    , mapReplace, mapReplaceAll, mapReplaceMap     , mapAdd, mapAddIfNew     , mapDelete, mapDeleteAll-    , mapApplyToAll, mapTranslate     , mapEq, mapKeys, mapVals-    , mapSelect, mapMerge-    , mapTranslateKeys, mapTranslateVals-    , mapTranslateEntries, mapTranslateEntriesM+    , mapMerge     )  import Data.List ( sort )@@ -109,23 +106,14 @@ lm08 = mapDelete lm07 3 lm09 = mapDeleteAll lm08 2 -la10 :: [String]-la10 = mapApplyToAll lm03 (`replicate` '*')--lt11, lt12, lt13, lt14 :: String-lt11 = mapTranslate lm03 la10 1 "****"-lt12 = mapTranslate lm03 la10 2 "****"-lt13 = mapTranslate lm03 la10 3 "****"-lt14 = mapTranslate lm03 la10 4 "****"- lm20, lm21, lm22, lm33, lm34, lm35, lm36 :: TestMap lm20 = mapReplaceMap lm05 $ newMap [(2,"bbb20"),(3,"ccc20")] lm21 = mapReplaceMap lm05 $ newMap [] lm22 = mapReplaceMap lm05 $ newMap [(9,"zzz22"),(1,"aaa22")] lm33 = mapAddIfNew lm22 $ newEntry (1,"aaa33") lm34 = mapAddIfNew lm22 $ newEntry (4,"ddd34")-lm35 = mapReplaceOrAdd (newEntry (1,"aaa35")) lm22-lm36 = mapReplaceOrAdd (newEntry (4,"ddd36")) lm22+lm35 = mapReplace lm22 (newEntry (1,"aaa35"))+lm36 = mapReplace lm22 (newEntry (4,"ddd36"))  testLookupMapSuite :: Test testLookupMapSuite = @@ -151,11 +139,6 @@   , testLookupMapFind "08" lm08 2 "bbb"   , testLookupMap     "09" lm09 [(1,"aaa")]   , testLookupMapFind "09" lm09 2 ""-  , testeq "LookupMapApplyToAll10" ["***","**","*"] la10-  , testeq "LookupMapTranslate11" "*"   lt11-  , testeq "LookupMapTranslate12" "**"  lt12-  , testeq "LookupMapTranslate13" "***" lt13-  , testeq "LookupMapTranslate14" "****" lt14   , testLookupMap     "20" lm20 [(2,"bbb20"),(3,"ccc20"),(2,"bbb20"),(1,"aaa")]   , testLookupMapFind "20" lm20 2 "bbb20"   , testLookupMap     "21" lm21 [(2,"bbb1"),(3,"ccc"),(2,"bbb1"),(1,"aaa")]@@ -347,19 +330,16 @@  lm101, lm102, lm103, lm104 :: TestMap lm101 = mapAdd lm03 $ newEntry (4,"ddd")+{- lm102 = mapSelect lm101 [1,3] lm103 = mapSelect lm101 [2,4] lm104 = mapSelect lm101 [2,3]+-} -mapSelectSuite :: Test-mapSelectSuite = -  TestList-  [ testLookupMap "101" lm101 [(4,"ddd"),(3,"ccc"),(2,"bbb"),(1,"aaa")]-  , testLookupMap "102" lm102 [(3,"ccc"),(1,"aaa")]-  , testLookupMap "103" lm103 [(4,"ddd"),(2,"bbb")]-  , testLookupMap "104" lm104 [(3,"ccc"),(2,"bbb")]-  ]-  +lm102 = mapAdd (mapAdd (newMap []) (newEntry (1,"aaa"))) $ newEntry (3,"ccc")+lm103 = mapAdd (mapAdd (newMap []) (newEntry (2,"bbb"))) $ newEntry (4,"ddd")+lm104 = mapAdd (mapAdd (newMap []) (newEntry (2,"bbb"))) $ newEntry (3,"ccc")+ lm105, lm106, lm107, lm108 :: TestMap lm105 = mapMerge lm102 lm103 lm106 = mapMerge lm102 lm104@@ -369,82 +349,14 @@ mapMergeSuite :: Test mapMergeSuite =   TestList-  [ testLookupMap "105" lm105 [(1,"aaa"),(2,"bbb"),(3,"ccc"),(4,"ddd")]+  [ testLookupMap "101" lm101 [(4,"ddd"),(3,"ccc"),(2,"bbb"),(1,"aaa")]+  , testLookupMap "105" lm105 [(1,"aaa"),(2,"bbb"),(3,"ccc"),(4,"ddd")]   , testLookupMap "106" lm106 [(1,"aaa"),(2,"bbb"),(3,"ccc")]   , testLookupMap "107" lm107 [(2,"bbb"),(3,"ccc"),(4,"ddd")]   , testLookupMap "108" lm108 [(1,"aaa"),(2,"bbb"),(3,"ccc"),(4,"ddd")]   ]     ---------------------------------------------------------------  Tranlation tests----------------------------------------------------------------- Rather late in the day, generic versions of the testing functions used earlier-type TestMapG a b = LookupMap (GenMapEntry a b)--newMapG :: (Eq a, Show a, Eq b, Show b) => [(a,b)] -> TestMapG a b-newMapG es = makeLookupMap (map newEntry es)--testLookupMapG :: (Eq a, Show a, Eq b, Show b) => String -> TestMapG a b -> [(a,b)] -> Test-testLookupMapG lab m1 m2 = testeq ("LookupMapG"++lab ) (newMapG m2) m1-testLookupMapM ::-    (Eq a, Show a, Eq b, Show b, Monad m,-     Eq (m (TestMapG a b)), Show (m (TestMapG a b)))-    => String -> m (TestMapG a b) -> m (TestMapG a b) -> Test-testLookupMapM lab m1 m2 = testeq ("LookupMapM"++lab ) m2 m1--tm101 :: TestMap-tm101 = newMap [(1,"a"),(2,"bb"),(3,"ccc"),(4,"dddd")]--tf102 :: Int -> String-tf102 = flip replicate '*'--tm102 :: StrTestMap-tm102 = mapTranslateKeys tf102 tm101--tm103 :: RevTestMap-tm103 = mapTranslateVals length tm102--tf104 :: (LookupEntryClass a Int [b],-          LookupEntryClass c String Int) =>-         a -> c-tf104 e = newEntry (replicate k '#', 5 - length v) where (k,v) = keyVal e--tm104 :: RevTestMap-tm104 = mapTranslateEntries tf104 tm101---- Test monadic translation, using Maybe monad--- (Note that if Nothing is generated at any step,--- it propagates to the result)----tf105 :: (LookupEntryClass a Int [b],-          LookupEntryClass c String Int) =>-         a -> Maybe c-tf105 e = Just $ tf104 e--tm105 :: MayTestMap-tm105 = mapTranslateEntriesM tf105 tm101--tf106 :: (LookupEntryClass a Int [b],-          LookupEntryClass c String Int) =>-         a -> Maybe c-tf106 e = if k == 2 then Nothing else tf105 e where (k,_) = keyVal e--tm106 :: MayTestMap-tm106 = mapTranslateEntriesM tf106 tm101--mapTranslateSuite :: Test-mapTranslateSuite = -  TestList-  [ testLookupMapG "tm101" tm101 [(1,"a"),(2,"bb"),(3,"ccc"),(4,"dddd")]-  , testLookupMapG "tm102" tm102 [("*","a"),("**","bb"),("***","ccc"),("****","dddd")]-  , testLookupMapG "tm103" tm103 [("*",1),("**",2),("***",3),("****",4)]-  , testLookupMapG "tm104" tm104 [("#",4),("##",3),("###",2),("####",1)]-  , testLookupMapM "tm105" tm105 (Just tm104)-  , testLookupMapM "tm106" tm106 Nothing-  ] -  ------------------------------------------------------------- --  All tests ------------------------------------------------------------ @@ -455,9 +367,7 @@   , testMapKeysSuite   , testMapValsSuite   , testMapEqSuite-  , mapSelectSuite   , mapMergeSuite-  , mapTranslateSuite   ]  main :: IO ()
tests/N3FormatterTest.hs view
@@ -5,7 +5,7 @@ -------------------------------------------------------------------------------- -- | --  Module      :  N3FormatterTest---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke --  License     :  GPL V2 -- --  Maintainer  :  Douglas Burke@@ -18,13 +18,14 @@  module Main where -import Swish.RDF.N3Formatter-    ( formatGraphAsLazyText-    , formatGraphDiag )+import Swish.GraphClass (Arc, arc)+import Swish.Namespace (Namespace, makeNamespace, makeNSScopedName, namespaceToBuilder)+import Swish.QName (LName) -import Swish.RDF.N3Parser (parseN3)+import Swish.RDF.Formatter.N3 (formatGraphAsLazyText, formatGraphDiag)+import Swish.RDF.Parser.N3 (parseN3) -import Swish.RDF.RDFGraph+import Swish.RDF.Graph     ( RDFGraph, RDFTriple     , RDFLabel(..), ToRDFLabel     , NSGraph(..)@@ -35,16 +36,9 @@     , resOwlSameAs     ) -import Swish.Utils.Namespace (Namespace, makeNamespace, makeNSScopedName, namespaceToBuilder)--import Swish.Utils.LookupMap-    ( LookupMap(..)-    , emptyLookupMap-    , makeLookupMap)--import Swish.RDF.GraphClass (Arc, arc)+import Data.LookupMap (LookupMap(..), emptyLookupMap, makeLookupMap) -import Swish.RDF.Vocabulary (langName, namespaceRDF, namespaceXSD)+import Swish.RDF.Vocabulary (toLangTag, namespaceRDF, namespaceXSD)  import Network.URI (URI, parseURI) @@ -81,7 +75,7 @@ toNS :: T.Text -> String -> Namespace toNS p = makeNamespace (Just p) . toURI -toRes :: Namespace -> T.Text -> RDFLabel+toRes :: Namespace -> LName -> RDFLabel toRes ns = Res . makeNSScopedName ns  base1, base2, base3, base4, basef, baseu, basem :: Namespace@@ -150,7 +144,7 @@ l14txt = "lx14"  toL :: B.Builder -> RDFLabel-toL = flip Lit Nothing . L.toStrict . B.toLazyText+toL = Lit . L.toStrict . B.toLazyText  l1, l2, l3, l11, l12, l13, l14 :: RDFLabel l1  = toL l1txt@@ -162,8 +156,8 @@ l14 = toL l14txt  lfr, lfoobar :: RDFLabel-lfr = Lit "chat et chien" (Just (langName "fr"))-lfoobar = Lit "foo bar" (Just (makeNSScopedName base1 "o1"))+lfr = LangLit "chat et chien" (fromJust $ toLangTag "fr")+lfoobar = TypedLit "foo bar" (makeNSScopedName base1 "o1")    f1, f2 :: RDFLabel f1 = Res $ makeNSScopedName base1 "f1"@@ -694,7 +688,7 @@   in toRDFGraph arcs     graph_l4 = toGraph [ toRDFTriple s1 p1 ("A string with \"quotes\"" :: RDFLabel)-                   , toRDFTriple s2 p2 (Lit "A typed string with \"quotes\"" (Just (fromString "urn:a#b")))+                   , toRDFTriple s2 p2 (TypedLit "A typed string with \"quotes\"" (fromString "urn:a#b"))                    ]                                          ------------------------------------------------------------@@ -1493,7 +1487,8 @@  -------------------------------------------------------------------------------- -----  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke --  All rights reserved. -- --  This file is part of Swish.
tests/N3ParserTest.hs view
@@ -5,7 +5,7 @@ -------------------------------------------------------------------------------- -- | --  Module      :  N3ParserTest---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke --  License     :  GPL V2 -- --  Maintainer  :  Douglas Burke@@ -18,7 +18,19 @@  module Main where -import Swish.RDF.N3Parser+import Swish.GraphClass (Arc, arc) +import Swish.Namespace (+  Namespace, makeNamespace, getNamespaceURI+  , ScopedName+  , makeScopedName+  , makeNSScopedName+  , nullScopedName+  -- , makeUriScopedName+  , namespaceToBuilder+  )+import Swish.QName (QName, qnameFromURI)++import Swish.RDF.Parser.N3     ( parseN3     , parseTextFromText, parseAltFromText     , parseNameFromText -- , parsePrefixFromText@@ -26,7 +38,7 @@     , parseURIref2FromText     ) -import Swish.RDF.RDFGraph+import Swish.RDF.Graph     ( RDFGraph, RDFLabel(..), NSGraph(..)     , LookupFormula(..)     , emptyRDFGraph, toRDFGraph@@ -34,19 +46,9 @@     , resOwlSameAs, resLogImplies     ) -import Swish.Utils.Namespace (-  Namespace, makeNamespace, getNamespaceURI-  , ScopedName-  , makeScopedName-  , makeNSScopedName-  , nullScopedName-  -- , makeUriScopedName-  , namespaceToBuilder-  )- import Swish.RDF.Vocabulary     ( namespaceRDF-    , langName+    , toLangTag     , rdfXMLLiteral     , xsdBoolean      , xsdInteger@@ -54,17 +56,14 @@     , xsdDouble      ) -import Swish.RDF.GraphClass (Arc, arc) --import Swish.Utils.QName (QName, qnameFromURI)-import Swish.Utils.LookupMap (LookupMap(..))+import Data.LookupMap (LookupMap(..))  import Test.HUnit (Test(TestCase,TestList), assertEqual)  import Network.URI (URI, nullURI, parseURIReference)  import Data.Monoid (Monoid(..))-import Data.Maybe (fromMaybe)+import Data.Maybe (fromJust, fromMaybe) import Data.List (intercalate)  import qualified Data.Text as T@@ -242,7 +241,7 @@ baseFile = "file:///dev/null"  dqn :: QName-dqn = (qnameFromURI . toURI) baseFile+dqn = (fromJust . qnameFromURI . toURI) baseFile  toNS :: T.Text -> String -> Namespace toNS p = makeNamespace (Just p) . toURI@@ -313,18 +312,18 @@ oa = Res $ makeNSScopedName basea "c"  l1, l2, l3 :: RDFLabel-l1 = Lit "l1"  Nothing-l2 = Lit "l2-'\"line1\"'\n\nl2-'\"\"line2\"\"'" Nothing-l3 = Lit "l3--\r\"'\\--\x0020\&--\x00A0\&--" Nothing+l1 = Lit "l1"+l2 = Lit "l2-'\"line1\"'\n\nl2-'\"\"line2\"\"'"+l3 = Lit "l3--\r\"'\\--\x0020\&--\x00A0\&--"  lfr, lxml, lfrxml :: RDFLabel-lfr    = Lit "chat"          $ Just $ langName "fr"-lxml   = Lit "<br/>"         $ Just rdfXMLLiteral-lfrxml = Lit "<em>chat</em>" $ Just rdfXMLLiteral+lfr    = LangLit "chat"           $ fromJust $ toLangTag "fr"+lxml   = TypedLit "<br/>"         rdfXMLLiteral+lfrxml = TypedLit "<em>chat</em>" rdfXMLLiteral  bTrue, bFalse :: RDFLabel-bTrue  = Lit "true"  $ Just xsdBoolean-bFalse = Lit "false" $ Just xsdBoolean+bTrue  = TypedLit "true"  xsdBoolean+bFalse = TypedLit "false" xsdBoolean  f1, f2 :: RDFLabel f1 = Res $ makeNSScopedName base1 "f1"@@ -1258,10 +1257,10 @@ lit_g2 = lit_g1 { namespaces = xnslist }  bOne, b20, b221, b23e4 :: RDFLabel-bOne  = Lit "1" $ Just xsdInteger-b20   = Lit "2.0" $ Just xsdDecimal-b221  = Lit "-2.21" $ Just xsdDecimal-b23e4 = Lit "-2.3E-4" $ Just xsdDouble+bOne  = TypedLit "1"       xsdInteger+b20   = TypedLit "2.0"     xsdDecimal+b221  = TypedLit "-2.21"   xsdDecimal+b23e4 = TypedLit "-2.3E-4" xsdDouble  lit_g4 :: RDFGraph lit_g4 = mempty {@@ -1506,7 +1505,8 @@  -------------------------------------------------------------------------------- -----  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke --  All rights reserved. -- --  This file is part of Swish.
tests/NTTest.hs view
@@ -5,34 +5,31 @@ -------------------------------------------------------------------------------- -- | --  Module      :  NTTest---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke --  License     :  GPL V2 -- --  Maintainer  :  Douglas Burke --  Stability   :  experimental --  Portability :  OverloadedString -----  This Module contains test cases for the NTriples modules: ---  "NTParser" and "NTFormatter".+--  This Module contains test cases for the NTriples parsing and formatting modules. -- --------------------------------------------------------------------------------  module Main where -import Swish.RDF.NTParser (parseNT)-import Swish.RDF.NTFormatter (formatGraphAsLazyText)+import Swish.GraphClass (arc) -import Swish.RDF.RDFGraph+import Swish.RDF.Parser.NTriples (parseNT)+import Swish.RDF.Formatter.NTriples (formatGraphAsLazyText)++import Swish.RDF.Graph   ( RDFGraph, RDFLabel(..)     , emptyRDFGraph      , toRDFGraph     ) --- import Swish.Utils.Namespace (makeURIScopedName)--import Swish.RDF.Vocabulary (langName, rdfXMLLiteral)--import Swish.RDF.GraphClass (arc)+import Swish.RDF.Vocabulary (toLangTag, rdfXMLLiteral)  import Test.HUnit     ( Test(TestCase,TestList)@@ -40,6 +37,7 @@  import qualified Data.Text.Lazy as T +import Data.Maybe (fromJust) import TestHelpers (runTestSuite)  ------------------------------------------------------------@@ -114,17 +112,17 @@ -}  l0, l1, l2, l3, l4 :: RDFLabel-l0 = Lit "" Nothing-l1 = Lit "l1"  Nothing-l2 = Lit "l2-'\"line1\"'\n\nl2-'\"\"line2\"\"'" Nothing-l3 = Lit "l3--\r\"'\\--\x20&--\x17A&--" Nothing-l4 = Lit "l4 \\" Nothing+l0 = Lit ""+l1 = Lit "l1"+l2 = Lit "l2-'\"line1\"'\n\nl2-'\"\"line2\"\"'"+l3 = Lit "l3--\r\"'\\--\x20&--\x17A&--"+l4 = Lit "l4 \\"  lfr, lgben, lxml1, lxml2 :: RDFLabel-lfr    = Lit "chat"          (Just $ langName "fr")-lgben  = Lit "football"      (Just $ langName "en-gb")-lxml1  = Lit "<br/>"         (Just rdfXMLLiteral)-lxml2  = Lit "<em>chat</em>" (Just rdfXMLLiteral)+lfr    = LangLit "chat"           $ fromJust $ toLangTag "fr"+lgben  = LangLit "football"       $ fromJust $ toLangTag "en-gb"+lxml1  = TypedLit "<br/>"         rdfXMLLiteral+lxml2  = TypedLit "<em>chat</em>" rdfXMLLiteral  b1 , b2 :: RDFLabel b1 = Blank "x1"@@ -262,7 +260,8 @@  -------------------------------------------------------------------------------- -----  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke --  All rights reserved. -- --  This file is part of Swish.
tests/QNameTest.hs view
@@ -19,16 +19,18 @@  module Main where -import Swish.Utils.QName+import Swish.Namespace (makeQNameScopedName, getQName, getScopedNameURI)+import Swish.QName     ( QName+    , LName     , newQName     , qnameFromURI     , getNamespace     , getLocalName     , getQNameURI+    , getLName     ) -import Swish.Utils.Namespace (makeQNameScopedName, getQName, getScopedNameURI) import Test.HUnit (Test(TestList))  import Network.URI (URI, parseURIReference)@@ -120,9 +122,12 @@ nq1 = newQName base1 "s1" nq2 = newQName base1 "s2" +toQN :: String -> QName+toQN = fromJust . qnameFromURI . toURI+ qu1, qu2, qu3, qu4, qu5, qu6, qu7 :: QName-qu1 = qnameFromURI (toURI "http://id.ninebynine.org/wip/2003/test/graph1/node#s1")-qu2 = qnameFromURI (toURI "http://id.ninebynine.org/wip/2003/test/graph2/node/s2")+qu1 = toQN "http://id.ninebynine.org/wip/2003/test/graph1/node#s1"+qu2 = toQN "http://id.ninebynine.org/wip/2003/test/graph2/node/s2" qu3 = "http://id.ninebynine.org/wip/2003/test/graph3/node" qu4 = "http://id.ninebynine.org/wip/2003/test/graph5/" qu5 = "http://id.ninebynine.org/wip/2003/test/graph5/s5"@@ -172,13 +177,13 @@         "http://id.ninebynine.org/wip/2003/test/graph3/node"         (getNamespace qb3)   , testTextEq "testGetLocalName01"-        "s1" (getLocalName qb1s1)+        "s1" (getLName (getLocalName qb1s1))   , testTextEq "testGetLocalName02"-        "s2" (getLocalName qb2s2)+        "s2" (getLName (getLocalName qb2s2))   , testTextEq "testGetLocalName03"-      "s3" (getLocalName qb3s3)+      "s3" (getLName (getLocalName qb3s3))   , testTextEq "testGetLocalName04"-      "" (getLocalName qb3)+      "" (getLName (getLocalName qb3))   , testURIEq "testGetQNameURI01"       "http://id.ninebynine.org/wip/2003/test/graph1/node#s1"       (getQNameURI qb1s1)@@ -277,12 +282,12 @@ URI combination done by newQName (may be tested elsewhere). -} -testSplitURI :: String -> String -> (String,T.Text) -> Test+testSplitURI :: String -> String -> (String, LName) -> Test testSplitURI lbl input (a,b) =   let qn = newQName (toURI a) b   in     TestList-   [ testCompare lbl ":split" qn ((qnameFromURI . toURI) input)+   [ testCompare lbl ":split" qn ((fromJust . qnameFromURI . toURI) input)    , testCompare lbl ":show"  input (show (getQNameURI qn))    ] @@ -334,7 +339,7 @@ testSQRoundTrip :: String -> String -> Test testSQRoundTrip lbl uri =    let u = (fromJust . parseURIReference) uri-      qn = qnameFromURI u+      qn = (fromJust . qnameFromURI) u       sn = makeQNameScopedName Nothing qn   in TestList      [ testCompare "SQ:URI"   lbl u  (getScopedNameURI sn)
tests/RDFDatatypeXsdIntegerTest.hs view
@@ -5,7 +5,7 @@ -------------------------------------------------------------------------------- -- | --  Module      :  RDFDatatypeXsdIntegerTest---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke --  License     :  GPL V2 -- --  Maintainer  :  Douglas Burke@@ -19,26 +19,7 @@  module Main where -import Swish.RDF.RDFDatatypeXsdInteger-    ( rdfDatatypeXsdInteger-    , rdfDatatypeValXsdInteger-    , typeNameXsdInteger, namespaceXsdInteger-    , axiomsXsdInteger, rulesXsdInteger-    , prefixXsdInteger-    )--import Swish.RDF.RDFVarBinding (RDFVarBinding)--import Swish.RDF.RDFRuleset-    ( RDFRule -    , makeRDFGraphFromN3Builder-    )--import Swish.RDF.RDFDatatype (RDFDatatypeMod, applyRDFDatatypeMod)-import Swish.RDF.RDFGraph (RDFLabel(..), RDFGraph)-import Swish.RDF.ClassRestrictionRule (falseGraphStr)--import Swish.RDF.Datatype+import Swish.Datatype     ( typeName, typeRules, typeMkRules     , getTypeAxiom, getTypeRule     , DatatypeVal(..)@@ -47,14 +28,31 @@     , DatatypeMod(..)     , nullDatatypeMod     )+import Swish.Namespace (ScopedName, getNamespaceURI, makeScopedName, makeNSScopedName, namespaceToBuilder)+import Swish.QName (LName)+import Swish.Rule    (Formula(..), Rule(..), nullFormula, nullRule)+import Swish.Ruleset (Ruleset(..), getRulesetRule)+import Swish.VarBinding (makeVarBinding) -import Swish.RDF.Ruleset (Ruleset(..), getRulesetRule)-import Swish.RDF.Rule    (Formula(..), Rule(..), nullFormula, nullRule)-import Swish.RDF.VarBinding (makeVarBinding)-import Swish.Utils.Namespace (getNamespaceURI, ScopedName, makeScopedName, makeNSScopedName)-import Swish.RDF.Vocabulary (namespaceDefault)-import Swish.Utils.LookupMap (LookupMap(..), mapFindMaybe)+import Swish.RDF.ClassRestrictionRule (falseGraphStr)+import Swish.RDF.Datatype (RDFDatatypeMod, applyRDFDatatypeMod) +import Swish.RDF.Datatype.XSD.Integer+    ( rdfDatatypeXsdInteger+    , rdfDatatypeValXsdInteger+    , typeNameXsdInteger, namespaceXsdInteger+    , axiomsXsdInteger, rulesXsdInteger+    )++import Swish.RDF.VarBinding (RDFVarBinding)++import Swish.RDF.Ruleset (RDFRule, makeRDFGraphFromN3Builder)+import Swish.RDF.Graph (RDFLabel(..), RDFGraph)++import Swish.RDF.Vocabulary (namespaceDefault, namespaceRDF, namespaceRDFD, namespaceXSD)++import Data.LookupMap (LookupMap(..), mapFindMaybe)+ import Test.HUnit     ( Test(TestCase,TestList)       , assertFailure@@ -79,7 +77,7 @@ toURI :: String -> URI toURI = fromJust . parseURI -xsdIntName :: T.Text -> ScopedName+xsdIntName :: LName -> ScopedName xsdIntName = makeNSScopedName namespaceXsdInteger   axiomXsdIntegerDT :: ScopedName@@ -221,10 +219,10 @@ rdfVB (v,b) = (Var v,Blank b)                   -- (Variable,Blank)  rdfVL :: (String, String) -> (RDFLabel, RDFLabel)-rdfVL (v,l) = (Var v,Lit (T.pack l) Nothing)             -- (Variable,Untyped literal)+rdfVL (v,l) = (Var v, Lit (T.pack l))             -- (Variable,Untyped literal)  rdfVI :: (String, String) -> (RDFLabel, RDFLabel)-rdfVI (v,l) = (Var v,Lit (T.pack l) (Just typeNameXsdInteger))+rdfVI (v,l) = (Var v, TypedLit (T.pack l) typeNameXsdInteger)                                                 -- (Variable,Integer literal)  makeBVR :: [(String,ScopedName)] -> RDFVarBinding@@ -744,11 +742,19 @@ --  Test rules defined for datatype ------------------------------------------------------------ +prefixes :: B.Builder+prefixes = mconcat $ map namespaceToBuilder+           [ namespaceRDF+           , namespaceRDFD+           , namespaceXSD+           , namespaceXsdInteger+           ]+ mkGraph :: B.Builder -> RDFGraph mkGraph gr =    let base = "@prefix : <" `mappend` (ns `mappend` "> . \n")       ns = B.fromString $ show $ getNamespaceURI namespaceDefault-  in makeRDFGraphFromN3Builder (prefixXsdInteger `mappend` (base `mappend` gr))+  in makeRDFGraphFromN3Builder (prefixes `mappend` (base `mappend` gr))  testRuleFwd :: String -> Maybe (Rule RDFGraph) -> B.Builder -> [B.Builder] -> Test testRuleFwd lab (Just rule) antstr constrs =@@ -1352,7 +1358,8 @@  -------------------------------------------------------------------------------- -----  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke --  All rights reserved. -- --  This file is part of Swish.
tests/RDFGraphTest.hs view
@@ -5,7 +5,7 @@ -------------------------------------------------------------------------------- -- | --  Module      :  RDFGraphTest---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke --  License     :  GPL V2 -- --  Maintainer  :  Douglas Burke@@ -18,18 +18,16 @@  module Main where -import Swish.Utils.LookupMap-    ( LookupMap(..), LookupEntryClass(..)-    , mapFindMaybe )+import Swish.Namespace (Namespace, makeNamespace, getNamespaceURI, ScopedName, makeNSScopedName, nullScopedName)+import Swish.QName (QName, qnameFromURI) -import Swish.Utils.ListHelpers (equiv)+import Data.LookupMap (LookupMap(..), LookupEntryClass(..), mapFindMaybe) -import Swish.RDF.GraphClass (Label(..), arc)+import Swish.Utils.ListHelpers (equiv) -import Swish.Utils.Namespace (Namespace, makeNamespace, getNamespaceURI, ScopedName, makeNSScopedName, nullScopedName)-import Swish.Utils.QName (QName, qnameFromURI)+import Swish.GraphClass (Label(..), arc) -import Swish.RDF.RDFGraph+import Swish.RDF.Graph   ( RDFTriple, toRDFTriple, fromRDFTriple   , RDFGraph    , RDFLabel(..), ToRDFLabel(..), FromRDFLabel(..)@@ -46,7 +44,8 @@  import Swish.RDF.Vocabulary   ( namespaceRDF-  , langName +  , LanguageTag+  , toLangTag    , rdfXMLLiteral   , xsdBoolean   , xsdInteger@@ -84,19 +83,22 @@ --  Test language tag comparisons ------------------------------------------------------------ -type Lang = Maybe ScopedName+type Lang = Maybe LanguageTag +toLT :: T.Text -> LanguageTag+toLT = fromJust . toLangTag+ lt0, lt1, lt2, lt3, lt4, lt5, lt6,   lt7, lt8 :: Lang lt0 = Nothing-lt1 = Just (langName "en")-lt2 = Just (langName "EN")-lt3 = Just (langName "fr")-lt4 = Just (langName "FR")-lt5 = Just (langName "en-us")-lt6 = Just (langName "en-US")-lt7 = Just (langName "EN-us")-lt8 = Just (langName "EN-US")+lt1 = Just (toLT "en")+lt2 = Just (toLT "EN")+lt3 = Just (toLT "fr")+lt4 = Just (toLT "FR")+lt5 = Just (toLT "en-us")+lt6 = Just (toLT "en-US")+lt7 = Just (toLT "EN-us")+lt8 = Just (toLT "EN-US")  langlist :: [(String, Lang)] langlist =@@ -156,7 +158,7 @@ base4 = toNS "base4" "http://id.ninebynine.org/wip/2003/test/graph3/nodebase"  qn1s1 :: QName-qn1s1 = qnameFromURI $ toURI $ base1Str ++ "s1"+qn1s1 = fromJust $ qnameFromURI $ toURI $ base1Str ++ "s1"  qu1s1 :: URI qu1s1 = toURI $ base1Str ++ "s1"@@ -242,30 +244,30 @@ l1, l2, l2gb, l3, l4, l5, l6, l7, l8,   l9, l10, l11, l12 :: RDFLabel l1   = "l1" -- use IsString instance-l2   = Lit "l2"  (Just (langName "en")) -l2gb = Lit "l2"  (Just (langName "en-gb")) -l3   = Lit "l2"  (Just (langName "fr")) -l4   = Lit "l4"  (Just qb1t1)           -l5   = Lit "l4"  (Just qb1t1)           -l6   = Lit "l4"  (Just qb1t1)           -l7   = Lit "l4"  (Just qb1t2)           -l8   = Lit "l4"  (Just qb1t2)           -l9   = Lit "l4"  (Just qb1t2)           -l10  = Lit "l10" (Just rdfXMLLiteral)  --- l11  = Lit "l11" (Just rdfXMLLiteral)  --- l12  = Lit "l12" (Just rdfXMLLiteral)  -l11  = Lit "l10" (Just rdfXMLLiteral)   -- are these meant to both be l10?-l12  = Lit "l10" (Just rdfXMLLiteral)   -- if you change them some tests fail+l2   = LangLit "l2"  (toLT "en")+l2gb = LangLit "l2"  (toLT "en-gb")+l3   = LangLit "l2"  (toLT "fr")+l4   = TypedLit "l4"  qb1t1    +l5   = TypedLit "l4"  qb1t1           +l6   = TypedLit "l4"  qb1t1           +l7   = TypedLit "l4"  qb1t2           +l8   = TypedLit "l4"  qb1t2           +l9   = TypedLit "l4"  qb1t2           +l10  = TypedLit "l10" rdfXMLLiteral+-- l11  = TypedLit "l11" (Just rdfXMLLiteral)  +-- l12  = TypedLit "l12" (Just rdfXMLLiteral)  +l11  = TypedLit "l10"  rdfXMLLiteral   -- are these meant to both be l10?+l12  = TypedLit "l10"  rdfXMLLiteral   -- if you change them some tests fail  nanF, infF, ninfF :: RDFLabel-nanF  = Lit "NaN" (Just xsdFloat)-infF  = Lit "INF" (Just xsdFloat)-ninfF = Lit "-INF" (Just xsdFloat)+nanF  = TypedLit "NaN"  xsdFloat+infF  = TypedLit "INF"  xsdFloat+ninfF = TypedLit "-INF" xsdFloat  nanD, infD, ninfD :: RDFLabel-nanD  = Lit "NaN" (Just xsdDouble)-infD  = Lit "INF" (Just xsdDouble)-ninfD = Lit "-INF" (Just xsdDouble)+nanD  = TypedLit "NaN"  xsdDouble+infD  = TypedLit "INF"  xsdDouble+ninfD = TypedLit "-INF" xsdDouble  v1, v2, v3, v4, vb3, vb4 :: RDFLabel v1  = Var "v1"  @@ -333,20 +335,22 @@  -- test ToRDFLabel/FromRDFlabel/IsString instances --++toLbl :: T.Text -> Maybe ScopedName -> RDFLabel+toLbl sVal (Just dtype) = TypedLit sVal dtype+toLbl sVal _            = Lit sVal      testToConv ::    (ToRDFLabel a, Eq a, Show a)    => String -> T.Text -> Maybe ScopedName -> a -> Test testToConv lbl sVal dtype hVal = -  let rdfVal = Lit sVal dtype-  in testEq (":tconv:" ++ lbl) rdfVal (toRDFLabel hVal)+    testEq (":tconv:" ++ lbl) (toLbl sVal dtype) $ toRDFLabel hVal    testFrConv ::    (FromRDFLabel a, Eq a, Show a)    => String -> T.Text -> Maybe ScopedName -> a -> Test testFrConv lbl sVal dtype hVal = -  let rdfVal = Lit sVal dtype-  in testEq (":fconv:" ++ lbl) (Just hVal)  (fromRDFLabel rdfVal)+    testEq (":fconv:" ++ lbl) (Just hVal) $ fromRDFLabel (toLbl sVal dtype)    testConv ::    (ToRDFLabel a, FromRDFLabel a, Eq a, Show a) @@ -367,24 +371,24 @@     -- failure case     testEq "fconv:fail chr1"    (Nothing :: Maybe Char)   (fromRDFLabel l1)   , testEq "fconv:fail chr2"    (Nothing :: Maybe Char)   (fromRDFLabel s1)-  , testEq "fconv:fail str1"    (Nothing :: Maybe String) (fromRDFLabel (Lit "1.23" (Just xsdFloat)))+  , testEq "fconv:fail str1"    (Nothing :: Maybe String) (fromRDFLabel (TypedLit "1.23" xsdFloat))   , testEq "fconv:fail bool1"   (Nothing :: Maybe Bool)  (fromRDFLabel l1)-  , testEq "fconv:fail bool2"   (Nothing :: Maybe Bool)  (fromRDFLabel (Lit "True" (Just xsdBoolean))) -- should we just let this be valid?-  , testEq "fconv:fail bool3"   (Nothing :: Maybe Bool)  (fromRDFLabel (Lit "true" (Just xsdFloat)))+  , testEq "fconv:fail bool2"   (Nothing :: Maybe Bool)  (fromRDFLabel (TypedLit "True" xsdBoolean)) -- should we just let this be valid?+  , testEq "fconv:fail bool3"   (Nothing :: Maybe Bool)  (fromRDFLabel (TypedLit "true" xsdFloat))   , testEq "fconv:fail int1"    (Nothing :: Maybe Int)  (fromRDFLabel l1)-  , testEq "fconv:fail int2"    (Nothing :: Maybe Int)  (fromRDFLabel (Lit "123456789012345" (Just xsdInteger))) +  , testEq "fconv:fail int2"    (Nothing :: Maybe Int)  (fromRDFLabel (TypedLit "123456789012345" xsdInteger))    , testEq "fconv:fail float1"  (Nothing :: Maybe Float)  (fromRDFLabel l1)-  , testEq "fconv:fail float2"  (Nothing :: Maybe Float)  (fromRDFLabel (Lit "1.234e101" (Just xsdFloat))) -- invalid input -  , testEq "fconv:fail float3"  (Nothing :: Maybe Float)  (fromRDFLabel (Lit "-1.234e101" (Just xsdFloat))) -- invalid input -  , testEq "fconv:fail float4"  (Nothing :: Maybe Float)  (fromRDFLabel (Lit "NaNs" (Just xsdFloat))) -- invalid input -  , testEq "fconv:fail dbl1"    (Nothing :: Maybe Double)  (fromRDFLabel (Lit "1.23" (Just xsdFloat))) -- invalid input +  , testEq "fconv:fail float2"  (Nothing :: Maybe Float)  (fromRDFLabel (TypedLit "1.234e101" xsdFloat)) -- invalid input +  , testEq "fconv:fail float3"  (Nothing :: Maybe Float)  (fromRDFLabel (TypedLit "-1.234e101" xsdFloat)) -- invalid input +  , testEq "fconv:fail float4"  (Nothing :: Maybe Float)  (fromRDFLabel (TypedLit "NaNs" xsdFloat)) -- invalid input +  , testEq "fconv:fail dbl1"    (Nothing :: Maybe Double)  (fromRDFLabel (TypedLit "1.23" xsdFloat)) -- invalid input    , testEq "fconv:fail sn1"     (Nothing :: Maybe ScopedName) (fromRDFLabel l1)   , testEq "fconv:fail qn1"     (Nothing :: Maybe QName)      (fromRDFLabel l1)   , testEq "fconv:fail qu1"     (Nothing :: Maybe URI)        (fromRDFLabel l1)   , testEq "fconv:fail triple"  (Nothing :: Maybe (ScopedName, ScopedName, Int)) (fromRDFTriple t01)                                          -- basic string tests-  , testEq     "tconv:emptystring1"  (Lit "" Nothing)    ""       -- want to try out IsString so do not use testToConv+  , testEq     "tconv:emptystring1"  (Lit "")    ""       -- want to try out IsString so do not use testToConv   , testConv   "emptystring2"        ""                  Nothing    (""::String)   , testConv   "char"                "x"                 Nothing    'x'   , testToConv "l1-1"                "l1"                Nothing    l1@@ -1589,7 +1593,8 @@  -------------------------------------------------------------------------------- -----  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke  +--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke   --  All rights reserved. -- --  This file is part of Swish.
tests/RDFProofContextTest.hs view
@@ -5,7 +5,7 @@ -------------------------------------------------------------------------------- -- | --  Module      :  RDFProofContextTest---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke --  License     :  GPL V2 -- --  Maintainer  :  Douglas Burke@@ -19,23 +19,23 @@  module Main where -import Swish.RDF.BuiltInMap (rdfRulesetMap, allRulesets)-import Swish.RDF.RDFProofContext (rulesetRDF, rulesetRDFS, rulesetRDFD)-import Swish.RDF.RDFProof (RDFProof, RDFProofStep, makeRDFProof, makeRDFProofStep )+import Swish.Namespace (Namespace, makeNamespace, ScopedName, makeNSScopedName, namespaceToBuilder)+import Swish.QName (LName, newLName)+import Swish.Proof (Step(..), checkProof, checkStep, explainProof)+import Swish.Rule (Formula(..), Rule(..), nullFormula, nullRule)+import Swish.Ruleset (getContextAxiom, getContextRule) -import Swish.RDF.RDFRuleset+import Swish.RDF.BuiltIn (rdfRulesetMap, allRulesets)++import Swish.RDF.ProofContext (rulesetRDF, rulesetRDFS, rulesetRDFD)+import Swish.RDF.Proof (RDFProof, RDFProofStep, makeRDFProof, makeRDFProofStep )+import Swish.RDF.Ruleset     ( RDFFormula, RDFRule, RDFRuleset     , nullRDFFormula     , makeRDFFormula ) -import Swish.RDF.RDFGraph (RDFGraph)-import Swish.RDF.RDFGraphShowM ()-import Swish.RDF.Proof (Step(..), checkProof, checkStep, explainProof)-import Swish.RDF.Ruleset (getContextAxiom, getContextRule)-import Swish.RDF.Rule (Formula(..), Rule(..), nullFormula, nullRule)--import Swish.Utils.Namespace (Namespace, makeNamespace, ScopedName, makeNSScopedName, namespaceToBuilder)-import Swish.Utils.LookupMap (mapFindMaybe)+import Swish.RDF.Graph (RDFGraph)+-- import Swish.RDF.GraphShowLines ()  import Swish.RDF.Vocabulary     ( namespaceRDF@@ -48,6 +48,8 @@     , scopeRDFD     ) +import Data.LookupMap (mapFindMaybe)+ import Test.HUnit     ( Test(TestCase,TestList)     , assertBool, assertEqual )@@ -91,7 +93,7 @@  --  Various support methods -makeFormula :: Namespace -> T.Text -> B.Builder -> RDFFormula+makeFormula :: Namespace -> LName -> B.Builder -> RDFFormula makeFormula scope local gr =     makeRDFFormula scope local (prefix `mappend` gr) @@ -103,7 +105,7 @@ getAxiom nam = getContextAxiom (makeSName nam) nullRDFFormula rdfdContext  makeSName :: String -> ScopedName-makeSName nam = makeNSScopedName ns (T.pack loc)+makeSName nam = makeNSScopedName ns (fromJust (newLName (T.pack loc)))     where         (pre,_:loc) = break (==':') nam         ns = case pre of@@ -678,7 +680,8 @@  -------------------------------------------------------------------------------- -----  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke --  All rights reserved. -- --  This file is part of Swish.
tests/RDFProofTest.hs view
@@ -19,16 +19,17 @@  module Main where -import Swish.RDF.RDFProof+import Swish.Namespace (Namespace, makeNamespace, ScopedName, makeNSScopedName)+import Swish.Rule (Rule(..))+import Swish.VarBinding (VarBinding(..), VarBindingModify(..))+import Swish.VarBinding (makeVarFilterModify, varBindingId, varFilterNE)++import Swish.RDF.Proof     ( makeRdfInstanceEntailmentRule     , makeRdfSubgraphEntailmentRule     , makeRdfSimpleEntailmentRule     )--import Swish.RDF.RDFQuery (rdfQueryFind, rdfQuerySubs)-import Swish.RDF.RDFVarBinding (RDFVarBinding, RDFVarBindingModify)--import Swish.RDF.RDFRuleset+import Swish.RDF.Ruleset     ( RDFRule     , makeRDFGraphFromN3Builder     , makeN3ClosureAllocatorRule@@ -37,20 +38,12 @@     , makeNodeAllocTo     ) -import Swish.RDF.RDFGraph-    ( Label(..), RDFLabel(..), RDFGraph-    , add, allLabels, allNodes )--import Swish.RDF.VarBinding-    ( VarBinding(..) -    , VarBindingModify(..)-    , makeVarFilterModify-    , varBindingId -- , varFilterDisjunction, varFilterConjunction-    , varFilterNE-    )+import Swish.RDF.Query (rdfQueryFind, rdfQuerySubs)+import Swish.RDF.VarBinding (RDFVarBinding, RDFVarBindingModify) -import Swish.RDF.Rule (Rule(..))-import Swish.Utils.Namespace (Namespace, makeNamespace, ScopedName, makeNSScopedName)+import Swish.RDF.Graph+    ( Label(..), RDFLabel(..), RDFGraph+    , addGraphs, allLabels, allNodes )  import Test.HUnit ( Test(TestList) ) @@ -143,7 +136,7 @@ fwd11 = fwdApply rul11 [graph1]  bwd11 :: [[RDFGraph]]-bwd11 = bwdApply rul11 (add result11b result11c)+bwd11 = bwdApply rul11 (addGraphs result11b result11c)  test1 :: Test test1 = 
tests/RDFQueryTest.hs view
@@ -5,7 +5,7 @@ -------------------------------------------------------------------------------- -- | --  Module      :  RDFQueryTest---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke --  License     :  GPL V2 -- --  Maintainer  :  Douglas Burke@@ -22,17 +22,26 @@  module Main where -import Swish.RDF.RDFQuery+import Swish.Namespace (getNamespaceURI, ScopedName, makeScopedName)+import Swish.VarBinding+    ( VarBinding(..)+    , makeVarBinding+    , joinVarBindings+    , VarBindingModify(..)+    , makeVarFilterModify+    , varBindingId+    , varFilterNE+    )+import Swish.RDF.Query     ( rdfQueryFind, rdfQueryFilter     , rdfQueryBack, rdfQueryBackFilter, rdfQueryBackModify     , rdfQueryInstance     , rdfQuerySubs, rdfQueryBackSubs     , rdfQuerySubsAll     , rdfQuerySubsBlank, rdfQueryBackSubsBlank-    -- debug     ) -import Swish.RDF.RDFVarBinding+import Swish.RDF.VarBinding     ( RDFVarBinding     , RDFVarBindingFilter     , rdfVarBindingUriRef, rdfVarBindingBlank@@ -42,20 +51,10 @@     , rdfVarBindingMemberProp     ) -import Swish.RDF.RDFGraph (RDFGraph, RDFLabel(..), merge)-import Swish.RDF.VarBinding-    ( VarBinding(..)-    , makeVarBinding-    , joinVarBindings-    , VarBindingModify(..)-    , makeVarFilterModify-    , varBindingId-    , varFilterNE-    )+import Swish.RDF.Graph (RDFGraph, RDFLabel(..), merge) -import Swish.Utils.Namespace (getNamespaceURI, ScopedName, makeScopedName)-import Swish.RDF.Vocabulary (namespaceRDF, langName, swishName, rdfType, rdfXMLLiteral)-import Swish.RDF.N3Parser (parseN3)+import Swish.RDF.Vocabulary (namespaceRDF, toLangTag, swishName, rdfType, rdfXMLLiteral)+import Swish.RDF.Parser.N3 (parseN3)  import Test.HUnit ( Test(TestList) ) @@ -1287,11 +1286,11 @@ u_dt  = Res q_dattyp  l_1, l_2, l_3, l_4, l_5 :: RDFLabel-l_1   = Lit "l1" Nothing-l_2   = Lit "l2" (Just $ langName "fr")-l_3   = Lit "l3" (Just q_dattyp)-l_4   = Lit "l4" (Just q_dattyp) -- was: (Lang "fr")-l_5   = Lit "l5" (Just rdfXMLLiteral)+l_1   = Lit "l1"+l_2   = LangLit "l2" $ fromJust $ toLangTag "fr"+l_3   = TypedLit "l3" q_dattyp+l_4   = TypedLit "l4" q_dattyp+l_5   = TypedLit "l5" rdfXMLLiteral  b_1, b_2, b_3, b_l1, b_l2 :: RDFLabel b_1   = Blank "1"@@ -1666,7 +1665,8 @@  -------------------------------------------------------------------------------- -----  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke  +--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke   --  All rights reserved. -- --  This file is part of Swish.
tests/RDFRulesetTest.hs view
@@ -5,7 +5,7 @@ -------------------------------------------------------------------------------- -- | --  Module      :  RDFRulesetTest---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011, 2012 Douglas Burke --  License     :  GPL V2 -- --  Maintainer  :  Douglas Burke@@ -20,27 +20,33 @@  module Main where -import Swish.RDF.RDFRuleset+import Swish.Namespace (Namespace, makeNamespace, getNamespaceTuple, getNamespaceURI, ScopedName, makeScopedName, makeNSScopedName, namespaceToBuilder)+import Swish.QName (LName)+import Swish.Rule (Formula(..), Rule(..), fwdCheckInference )+import Swish.Ruleset (makeRuleset, getRulesetNamespace, getRulesetAxioms)+import Swish.Ruleset (getRulesetRules, getRulesetAxiom, getRulesetRule)+import Swish.VarBinding (makeVarBinding, vbmCompose, makeVarFilterModify)++import Swish.RDF.Ruleset     ( RDFFormula, RDFRule, RDFClosure, RDFRuleset     , GraphClosure(..)     , makeRDFGraphFromN3Builder     , makeRDFFormula     , makeN3ClosureSimpleRule     , makeNodeAllocTo-    -- for debugging     , graphClosureFwdApply, graphClosureBwdApply     ) -import Swish.RDF.RDFQuery (rdfQueryBack, rdfQueryBackModify)+import Swish.RDF.Query (rdfQueryBack, rdfQueryBackModify) -import Swish.RDF.RDFVarBinding+import Swish.RDF.VarBinding     ( RDFVarBinding     , RDFVarBindingModify     , RDFVarBindingFilter     , rdfVarBindingXMLLiteral     ) -import Swish.RDF.RDFGraph+import Swish.RDF.Graph     ( Label (..), RDFLabel(..), RDFGraph     , Arc(..)     , getArcs@@ -48,14 +54,7 @@     , toRDFGraph     )     -import Swish.RDF.VarBinding (makeVarBinding, vbmCompose, makeVarFilterModify)-import Swish.RDF.Ruleset-    ( makeRuleset, getRulesetNamespace, getRulesetAxioms, getRulesetRules-    , getRulesetAxiom, getRulesetRule )--import Swish.RDF.Rule (Formula(..), Rule(..), fwdCheckInference ) import Swish.RDF.Vocabulary (namespaceRDF, namespaceRDFS, namespaceOWL, scopeRDF)-import Swish.Utils.Namespace (Namespace, makeNamespace, getNamespaceTuple, getNamespaceURI, ScopedName, makeScopedName, makeNSScopedName, namespaceToBuilder)  import Test.HUnit     ( Test(TestCase,TestList)@@ -268,7 +267,7 @@ scopeex :: Namespace scopeex = toNS (Just "ex") "http://id.ninebynine.org/wip/2003/RDFProofCheck#" -makeFormula :: Namespace -> T.Text -> B.Builder -> RDFFormula+makeFormula :: Namespace -> LName -> B.Builder -> RDFFormula makeFormula scope local gr =     makeRDFFormula scope local $ prefix `mappend` gr @@ -403,7 +402,8 @@  -------------------------------------------------------------------------------- -----  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke+--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,+--    2011, 2012 Douglas Burke --  All rights reserved. -- --  This file is part of Swish.
tests/VarBindingTest.hs view
@@ -19,7 +19,7 @@  module Main where -import Swish.RDF.VarBinding+import Swish.VarBinding     ( VarBinding(..)     , subBinding, nullVarBinding, makeVarBinding     , boundVars, subBinding, makeVarBinding