packages feed

swish 0.6.1.2 → 0.6.2.1

raw patch · 116 files changed

+22685/−20121 lines, 116 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- Swish.RDF.RDFGraph: resRdfType, resLogImplies, resOwlSameAs, resRdfdMaxCardinality, resRdfdConstraint, resRdfdOnProperties, resRdfdGeneralRestriction, resRdfsMember, resRdfNil, resRdfRest, resRdfFirst :: RDFLabel
- Swish.RDF.Vocabulary: namespaceOWL :: Namespace
- Swish.RDF.Vocabulary: namespaceRDF :: Namespace
- Swish.RDF.Vocabulary: namespaceRDFS :: Namespace
- Swish.RDF.Vocabulary: namespaceXSD :: Namespace
- Swish.RDF.Vocabulary: owlSameAs :: ScopedName
- Swish.RDF.Vocabulary: rdfAbout :: ScopedName
- Swish.RDF.Vocabulary: rdfDatatype :: ScopedName
- Swish.RDF.Vocabulary: rdfFirst :: ScopedName
- Swish.RDF.Vocabulary: rdfID :: ScopedName
- Swish.RDF.Vocabulary: rdfNil :: ScopedName
- Swish.RDF.Vocabulary: rdfResource :: ScopedName
- Swish.RDF.Vocabulary: rdfRest :: ScopedName
- Swish.RDF.Vocabulary: rdfType :: ScopedName
- Swish.RDF.Vocabulary: rdfXMLLiteral :: ScopedName
- Swish.RDF.Vocabulary: rdfsMember :: ScopedName
- Swish.RDF.Vocabulary: scopeRDF, scopeRDFD, scopeRDFS :: Namespace
- Swish.RDF.Vocabulary: xsdBoolean :: ScopedName
- Swish.RDF.Vocabulary: xsdDate, xsdDateTime :: ScopedName
- Swish.RDF.Vocabulary: xsdDecimal :: ScopedName
- Swish.RDF.Vocabulary: xsdDouble :: ScopedName
- Swish.RDF.Vocabulary: xsdFloat :: ScopedName
- Swish.RDF.Vocabulary: xsdInteger :: ScopedName
- Swish.RDF.Vocabulary: xsdNegInteger :: ScopedName
- Swish.RDF.Vocabulary: xsdNonNegInteger :: ScopedName
- Swish.RDF.Vocabulary: xsdNonPosInteger :: ScopedName
- Swish.RDF.Vocabulary: xsdPosInteger :: ScopedName
- Swish.RDF.Vocabulary: xsdString :: ScopedName
- Swish.RDF.Vocabulary: xsdType :: Text -> ScopedName
+ 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.SwishMain: instance Show SwishAction
+ Swish.RDF.Vocabulary: scopeRDF :: Namespace
+ Swish.RDF.Vocabulary: scopeRDFD :: Namespace
+ Swish.RDF.Vocabulary: scopeRDFS :: Namespace
+ Swish.RDF.Vocabulary.DublinCore: dcamVocabularyEncodingScheme :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dcammemberOf :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dcelemcontributor :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dcelemcoverage :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dcelemcreator :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dcelemdate :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dcelemdescription :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dcelemformat :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dcelemidentifier :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dcelemlanguage :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dcelempublisher :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dcelemrelation :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dcelemrights :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dcelemsource :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dcelemsubject :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dcelemtitle :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dcelemtype :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctAgent :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctAgentClass :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctBibliographicResource :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctBox :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctDCMIType :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctDDC :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctFileFormat :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctFrequency :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctIMT :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctISO3166 :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctISO639_2 :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctISO639_3 :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctJurisdiction :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctLCC :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctLCSH :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctLicenseDocument :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctLinguisticSystem :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctLocation :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctLocationPeriodOrJurisdiction :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctMESH :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctMediaType :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctMediaTypeOrExtent :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctMethodOfAccrual :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctMethodOfInstruction :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctNLM :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctPeriod :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctPeriodOfTime :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctPhysicalMedium :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctPhysicalResource :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctPoint :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctPolicy :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctProvenanceStatement :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctRFC1766 :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctRFC3066 :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctRFC4646 :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctRFC5646 :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctRightsStatement :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctSizeOrDuration :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctStandard :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctTGN :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctUDC :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctURI :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctW3CDTF :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctabstract :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctaccessRights :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctaccrualMethod :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctaccrualPeriodicity :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctaccrualPolicy :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctalternative :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctaudience :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctavailable :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctbibliographicCitation :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctconformsTo :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctcontributor :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctcoverage :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctcreated :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctcreator :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctdate :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctdateAccepted :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctdateCopyrighted :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctdateSubmitted :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctdescription :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dcteducationLevel :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctextent :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctformat :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dcthasFormat :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dcthasPart :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dcthasVersion :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctidentifier :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctinstructionalMethod :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctisFormatOf :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctisPartOf :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctisReferencedBy :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctisReplacedBy :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctisRequiredBy :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctisVersionOf :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctissued :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctlanguage :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctlicense :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctmediator :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctmedium :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctmodified :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctprovenance :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctpublisher :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctreferences :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctrelation :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctreplaces :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctrequires :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctrights :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctrightsHolder :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctsource :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctspatial :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctsubject :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dcttableOfContents :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dcttemporal :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dcttitle :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dcttype :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctvalid :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctypeCollection :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctypeDataset :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctypeEvent :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctypeImage :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctypeInteractiveResource :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctypeMovingImage :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctypePhysicalObject :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctypeService :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctypeSoftware :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctypeSound :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctypeStillImage :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: dctypeText :: ScopedName
+ Swish.RDF.Vocabulary.DublinCore: namespaceDCAM :: Namespace
+ Swish.RDF.Vocabulary.DublinCore: namespaceDCELEM :: Namespace
+ Swish.RDF.Vocabulary.DublinCore: namespaceDCTERMS :: Namespace
+ Swish.RDF.Vocabulary.DublinCore: namespaceDCTYPE :: Namespace
+ Swish.RDF.Vocabulary.FOAF: foafAgent :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafDocument :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafGroup :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafImage :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafLabelProperty :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafOnlineAccount :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafOnlineChatAccount :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafOnlineEcommerceAccount :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafOnlineGamingAccount :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafOrganization :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafPerson :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafPersonalProfileDocument :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafProject :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafaccount :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafaccountName :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafaccountServiceHomepage :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafage :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafaimChatID :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafbased_near :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafbirthday :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafcurrentProject :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafdepiction :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafdepicts :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafdnaChecksum :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foaffamilyName :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foaffamily_name :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foaffirstName :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foaffocus :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foaffundedBy :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafgeekcode :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafgender :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafgivenName :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafgivenname :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafholdsAccount :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafhomepage :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foaficqChatID :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafimg :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafinterest :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafisPrimaryTopicOf :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafjabberID :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafknows :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foaflastName :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foaflogo :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafmade :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafmaker :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafmbox :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafmbox_sha1sum :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafmember :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafmembershipClass :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafmsnChatID :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafmyersBriggs :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafname :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafnick :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafopenid :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafpage :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafpastProject :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafphone :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafplan :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafprimaryTopic :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafpublications :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafschoolHomepage :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafsha1 :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafskypeID :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafstatus :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafsurname :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foaftheme :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafthumbnail :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foaftipjar :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foaftitle :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foaftopic :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foaftopic_interest :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafweblog :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafworkInfoHomepage :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafworkplaceHomepage :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: foafyahooChatID :: ScopedName
+ Swish.RDF.Vocabulary.FOAF: namespaceFOAF :: Namespace
+ Swish.RDF.Vocabulary.Geo: geoPoint :: ScopedName
+ Swish.RDF.Vocabulary.Geo: geoSpatialThing :: ScopedName
+ Swish.RDF.Vocabulary.Geo: geolat :: ScopedName
+ Swish.RDF.Vocabulary.Geo: geolat_long :: ScopedName
+ Swish.RDF.Vocabulary.Geo: geolocation :: ScopedName
+ Swish.RDF.Vocabulary.Geo: geolong :: ScopedName
+ Swish.RDF.Vocabulary.Geo: namespaceGEO :: Namespace
+ Swish.RDF.Vocabulary.OWL: namespaceOWL :: Namespace
+ Swish.RDF.Vocabulary.OWL: owlAnnotationProperty :: ScopedName
+ Swish.RDF.Vocabulary.OWL: owlClass :: ScopedName
+ Swish.RDF.Vocabulary.OWL: owlDatatypeProperty :: ScopedName
+ Swish.RDF.Vocabulary.OWL: owlNamedIndividual :: ScopedName
+ Swish.RDF.Vocabulary.OWL: owlNothing :: ScopedName
+ Swish.RDF.Vocabulary.OWL: owlObjectProperty :: ScopedName
+ Swish.RDF.Vocabulary.OWL: owlOntology :: ScopedName
+ Swish.RDF.Vocabulary.OWL: owlSameAs :: ScopedName
+ Swish.RDF.Vocabulary.OWL: owlThing :: ScopedName
+ Swish.RDF.Vocabulary.OWL: owlbackwardCompatibleWith :: ScopedName
+ Swish.RDF.Vocabulary.OWL: owldeprecated :: ScopedName
+ Swish.RDF.Vocabulary.OWL: owlequivalentClass :: ScopedName
+ Swish.RDF.Vocabulary.OWL: owlequivalentProperty :: ScopedName
+ Swish.RDF.Vocabulary.OWL: owlimports :: ScopedName
+ Swish.RDF.Vocabulary.OWL: owlincompatibleWith :: ScopedName
+ Swish.RDF.Vocabulary.OWL: owlpriorVersion :: ScopedName
+ Swish.RDF.Vocabulary.OWL: owlrational :: ScopedName
+ Swish.RDF.Vocabulary.OWL: owlreal :: ScopedName
+ Swish.RDF.Vocabulary.OWL: owlversionInfo :: ScopedName
+ Swish.RDF.Vocabulary.RDF: namespaceRDF :: Namespace
+ Swish.RDF.Vocabulary.RDF: namespaceRDFS :: Namespace
+ Swish.RDF.Vocabulary.RDF: rdf1 :: ScopedName
+ Swish.RDF.Vocabulary.RDF: rdf2 :: ScopedName
+ Swish.RDF.Vocabulary.RDF: rdfAbout :: ScopedName
+ Swish.RDF.Vocabulary.RDF: rdfAlt :: ScopedName
+ Swish.RDF.Vocabulary.RDF: rdfBag :: ScopedName
+ Swish.RDF.Vocabulary.RDF: rdfDatatype :: ScopedName
+ Swish.RDF.Vocabulary.RDF: rdfDescription :: ScopedName
+ Swish.RDF.Vocabulary.RDF: rdfFirst :: ScopedName
+ Swish.RDF.Vocabulary.RDF: rdfID :: ScopedName
+ Swish.RDF.Vocabulary.RDF: rdfLi :: ScopedName
+ Swish.RDF.Vocabulary.RDF: rdfList :: ScopedName
+ Swish.RDF.Vocabulary.RDF: rdfNil :: ScopedName
+ Swish.RDF.Vocabulary.RDF: rdfNodeID :: ScopedName
+ Swish.RDF.Vocabulary.RDF: rdfObject :: ScopedName
+ Swish.RDF.Vocabulary.RDF: rdfParseType :: ScopedName
+ Swish.RDF.Vocabulary.RDF: rdfPredicate :: ScopedName
+ Swish.RDF.Vocabulary.RDF: rdfProperty :: ScopedName
+ Swish.RDF.Vocabulary.RDF: rdfRDF :: ScopedName
+ Swish.RDF.Vocabulary.RDF: rdfResource :: ScopedName
+ Swish.RDF.Vocabulary.RDF: rdfRest :: ScopedName
+ Swish.RDF.Vocabulary.RDF: rdfSeq :: ScopedName
+ Swish.RDF.Vocabulary.RDF: rdfStatement :: ScopedName
+ Swish.RDF.Vocabulary.RDF: rdfSubject :: ScopedName
+ Swish.RDF.Vocabulary.RDF: rdfType :: ScopedName
+ Swish.RDF.Vocabulary.RDF: rdfValue :: ScopedName
+ Swish.RDF.Vocabulary.RDF: rdfXMLLiteral :: ScopedName
+ Swish.RDF.Vocabulary.RDF: rdfn :: Int -> ScopedName
+ Swish.RDF.Vocabulary.RDF: rdfsClass :: ScopedName
+ Swish.RDF.Vocabulary.RDF: rdfsComment :: ScopedName
+ Swish.RDF.Vocabulary.RDF: rdfsContainer :: ScopedName
+ Swish.RDF.Vocabulary.RDF: rdfsContainerMembershipProperty :: ScopedName
+ Swish.RDF.Vocabulary.RDF: rdfsDatatype :: ScopedName
+ Swish.RDF.Vocabulary.RDF: rdfsDomain :: ScopedName
+ Swish.RDF.Vocabulary.RDF: rdfsIsDefinedBy :: ScopedName
+ Swish.RDF.Vocabulary.RDF: rdfsLabel :: ScopedName
+ Swish.RDF.Vocabulary.RDF: rdfsLiteral :: ScopedName
+ Swish.RDF.Vocabulary.RDF: rdfsMember :: ScopedName
+ Swish.RDF.Vocabulary.RDF: rdfsRange :: ScopedName
+ Swish.RDF.Vocabulary.RDF: rdfsResource :: ScopedName
+ Swish.RDF.Vocabulary.RDF: rdfsSeeAlso :: ScopedName
+ Swish.RDF.Vocabulary.RDF: rdfsSubClassOf :: ScopedName
+ Swish.RDF.Vocabulary.RDF: rdfsSubPropertyOf :: ScopedName
+ Swish.RDF.Vocabulary.XSD: namespaceXSD :: Namespace
+ Swish.RDF.Vocabulary.XSD: xsdAnyURI :: ScopedName
+ Swish.RDF.Vocabulary.XSD: xsdBoolean :: ScopedName
+ Swish.RDF.Vocabulary.XSD: xsdByte :: ScopedName
+ Swish.RDF.Vocabulary.XSD: xsdDate :: ScopedName
+ Swish.RDF.Vocabulary.XSD: xsdDateTime :: ScopedName
+ Swish.RDF.Vocabulary.XSD: xsdDecimal :: ScopedName
+ Swish.RDF.Vocabulary.XSD: xsdDouble :: ScopedName
+ Swish.RDF.Vocabulary.XSD: xsdFloat :: ScopedName
+ Swish.RDF.Vocabulary.XSD: xsdInt :: ScopedName
+ Swish.RDF.Vocabulary.XSD: xsdInteger :: ScopedName
+ Swish.RDF.Vocabulary.XSD: xsdLong :: ScopedName
+ Swish.RDF.Vocabulary.XSD: xsdNegInteger :: ScopedName
+ Swish.RDF.Vocabulary.XSD: xsdNonNegInteger :: ScopedName
+ Swish.RDF.Vocabulary.XSD: xsdNonPosInteger :: ScopedName
+ Swish.RDF.Vocabulary.XSD: xsdPosInteger :: ScopedName
+ Swish.RDF.Vocabulary.XSD: xsdShort :: ScopedName
+ Swish.RDF.Vocabulary.XSD: xsdString :: ScopedName
+ Swish.RDF.Vocabulary.XSD: xsdTime :: ScopedName
+ Swish.RDF.Vocabulary.XSD: xsdType :: Text -> ScopedName
+ Swish.RDF.Vocabulary.XSD: xsdUnsignedByte :: ScopedName
+ Swish.RDF.Vocabulary.XSD: xsdUnsignedInt :: ScopedName
+ Swish.RDF.Vocabulary.XSD: xsdUnsignedLong :: ScopedName
+ Swish.RDF.Vocabulary.XSD: xsdUnsignedShort :: ScopedName

Files

− Data/Interned/URI.hs
@@ -1,91 +0,0 @@-{-# LANGUAGE TypeFamilies, FlexibleInstances #-}-------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  URI---  Copyright   :  (c) 2011 Douglas Burke---  License     :  GPL V2------  Maintainer  :  Douglas Burke---  Stability   :  experimental---  Portability :  TypeFamilies, FlexibleInstances------  Support interning URIs.--------------------------------------------------------------------------------------module Data.Interned.URI-       ( InternedURI-       ) where--import Data.String (IsString(..))-import Data.Hashable-import Data.Interned-import Data.Maybe (fromMaybe)--import Network.URI---- Could look at adding UNPACK statements before each component-data InternedURI = InternedURI !Int !URI--instance IsString InternedURI where-  fromString = intern .-               fromMaybe (error "Error: unable to create a URI.") .-               parseURIReference-              --instance Eq InternedURI where-  InternedURI a _ == InternedURI b _ = a == b--instance Ord InternedURI where-  compare (InternedURI a _) (InternedURI b _) = compare a b--instance Show InternedURI where-  showsPrec d (InternedURI _ b) = showsPrec d b--instance Interned InternedURI where-  type Uninterned InternedURI = URI-  data Description InternedURI = DU !URI deriving (Eq) -- DU {-# UNPACK #-} !URI deriving (Eq) -  describe = DU-  identify = InternedURI-  identity (InternedURI i _) = i-  cache = iuCache--instance Uninternable InternedURI where-  unintern (InternedURI _ b) = b ---- Rather than access the URI components, just use the reverse of the--- string representation of the URI.-instance Hashable (Description InternedURI) where-  hash = hashWithSalt 5381 -- use the stringSalt value from Data.Hashable-  hashWithSalt salt (DU u) = hashWithSalt salt ((reverse . show) u)--iuCache :: Cache InternedURI-iuCache = mkCache-{-# NOINLINE iuCache #-}----------------------------------------------------------------------------------------  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------------------------------------------------------------------------------------
− Swish.hs
@@ -1,79 +0,0 @@------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  Swish---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  License     :  GPL V2------  Maintainer  :  Douglas Burke---  Stability   :  experimental---  Portability :  H98------  Swish:  Semantic Web Inference Scripting in Haskell------  This program is a simple skeleton for constructing Semantic Web [1]---  inference tools in Haskell, using the RDF graph and several RDF---  parsers (at present Notation 3 and NTriples).------  It might be viewed as a kind of embroyonic CWM [2] in Haskell,---  except that the intent is that Haskell will be used as a primary---  language for defining inferences.  As such, Swish is an open-ended---  toolkit for constructing new special-purpose Semantic Web---  applications rather than a closed, self-contained general-purpose---  SW application.  As such, it is part of another experiment along---  the lines described in [3].------  The script format used by Swish is described in---  "Swish.RDF.SwishScript".------  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".------  (1) Semantic web: <http://www.w3.org/2001/sw/>------  (2) CWM:          <http://www.w3.org/2000/10/swap/doc/cwm.html>------  (3) Motivation:   <http://www.w3.org/2000/10/swap/doc/Motivation.html>------  (4) Notation 3:   <http://www.w3.org/TeamSubmission/2008/SUBM-n3-20080114/>------  (5) Turtle:       <http://www.w3.org/TR/turtle/>------  (6) RDF:          <http://www.w3.org/RDF/>--------------------------------------------------------------------------------------module Swish-       (-         module Swish.RDF.SwishMain-       ) where--import Swish.RDF.SwishMain--- should this re-export Swish.RDF.SwishMain or other components?-       ---------------------------------------------------------------------------------------  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------------------------------------------------------------------------------------
− Swish/RDF.hs
@@ -1,44 +0,0 @@------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  RDF---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  License     :  GPL V2------  Maintainer  :  Douglas Burke---  Stability   :  experimental---  Portability :  FlexibleInstances, MultiParamTypeClasses, TypeSynonymInstances------  This module provides an in-memory RDF Graph (it re-exports---  "Swish.RDF.RDFGraph").--------------------------------------------------------------------------------------module Swish.RDF (module Swish.RDF.RDFGraph) where--import Swish.RDF.RDFGraph----------------------------------------------------------------------------------------  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------------------------------------------------------------------------------------
− Swish/RDF/BuiltInDatatypes.hs
@@ -1,74 +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)-----------------------------------------------------------------  Declare datatype map---------------------------------------------------------------allDatatypes :: [RDFDatatype]-allDatatypes =-    [ rdfDatatypeXsdString-    , rdfDatatypeXsdInteger-    ]--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------------------------------------------------------------------------------------
− 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------------------------------------------------------------------------------------
− 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------------------------------------------------------------------------------------
− Swish/RDF/ClassRestrictionRule.hs
@@ -1,546 +0,0 @@-{-# LANGUAGE MultiParamTypeClasses, OverloadedStrings #-}-------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  ClassRestrictionRule---  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 implements an inference rule based on a restruction on class---  membership of one or more values.--------------------------------------------------------------------------------------module Swish.RDF.ClassRestrictionRule-       ( ClassRestriction(..), ClassRestrictionFn-       , makeDatatypeRestriction, makeDatatypeRestrictionFn-       , makeRDFClassRestrictionRules-       , makeRDFDatatypeRestrictionRules-       , falseGraph, falseGraphStr       -       )-       where--import Swish.RDF.RDFGraph-    ( RDFLabel(..)-    , getScopedName-    , RDFGraph-    , getArcs-    , merge-    , toRDFGraph, emptyRDFGraph-    , Arc(..)-    , resRdfType-    , resRdfdMaxCardinality-    )--import Swish.RDF.RDFRuleset (RDFRule, makeRDFGraphFromN3Builder)-import Swish.RDF.RDFDatatype (RDFDatatypeVal, fromRDFLabel, toRDFLabel)--import Swish.RDF.RDFQuery-    ( 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.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.Maybe (fromJust, fromMaybe, mapMaybe)-import Data.List (delete, nub, (\\))--import qualified Data.Text.Lazy.Builder as B-----------------------------------------------------------------  Class restriction data type----------------------------------------------------------------- |Type of function that evaluates missing node values in a---  restriction from those supplied.-type ClassRestrictionFn = [Maybe RDFLabel] -> Maybe [[RDFLabel]]---- |Datatype for named class restriction-data ClassRestriction = ClassRestriction-    { crName    :: ScopedName-    , crFunc    :: ClassRestrictionFn-    }--instance Eq ClassRestriction where-    cr1 == cr2  =  crName cr1 == crName cr2--instance Show ClassRestriction where-    show cr = "ClassRestriction:" ++ show (crName cr)--instance LookupEntryClass ClassRestriction ScopedName ClassRestriction-    where-    newEntry (_,fn) = fn-    keyVal cr = (crName cr, cr)-----------------------------------------------------------------  Instantiate a class restriction from a datatype relation----------------------------------------------------------------- |Make a class restriction from a datatype relation.------  This lifts application of the datatype relation to operate---  on 'RDFLabel' values, which are presumed to contain appropriately---  datatyped values.----makeDatatypeRestriction ::-    RDFDatatypeVal vt -> DatatypeRel vt -> ClassRestriction-makeDatatypeRestriction dtv dtrel = ClassRestriction-    { crName = dtRelName dtrel-    , crFunc = makeDatatypeRestrictionFn dtv (dtRelFunc dtrel)-    }----  The core logic below is something like @(map toLabels . dtrelfn . map frLabel)@---  but the extra lifting and catMaybes are needed to get the final result---  type in the right form.---- |Make a class restriction function from a datatype relation function.----makeDatatypeRestrictionFn ::-    RDFDatatypeVal vt -> DatatypeRelFn vt -> ClassRestrictionFn-makeDatatypeRestrictionFn dtv dtrelfn =-    liftM (mapMaybe toLabels) . dtrelfn . map frLabel-    where-        frLabel Nothing  = Nothing-        frLabel (Just l) = fromRDFLabel dtv l-        toLabels         = mapM toLabel   -- Maybe [RDFLabel]-        toLabel          = toRDFLabel dtv-----------------------------------------------------------------  Make rules from supplied class restrictions and graph---------------------------------------------------------------mkPrefix :: Namespace -> B.Builder-mkPrefix = namespaceToBuilder--ruleQuery :: RDFGraph-ruleQuery = makeRDFGraphFromN3Builder $-            mconcat-            [ mkPrefix namespaceRDFD-            , " ?c a rdfd:GeneralRestriction ; "-            , "    rdfd:onProperties ?p ; "     -            , "    rdfd:constraint   ?r . "-            ]-            ---  Placeholder false graph for now.-falseGraph :: RDFGraph-falseGraph = makeRDFGraphFromN3Builder $-             mkPrefix namespaceRDFD `mappend` falseGraphStr--falseGraphStr :: B.Builder-falseGraphStr = "_:a rdfd:false _:b . "---- |Make a list of class restriction rules given a list of class restriction---  values and a graph containing one or more class restriction definitions.----makeRDFClassRestrictionRules :: [ClassRestriction] -> RDFGraph -> [RDFRule]-makeRDFClassRestrictionRules crs gr =-    mapMaybe constructRule (queryForRules gr)-    where-        queryForRules = rdfQueryFind ruleQuery-        constructRule = makeRestrictionRule1 crs gr--makeRestrictionRule1 ::-    [ClassRestriction] -> RDFGraph -> RDFVarBinding -> Maybe RDFRule-makeRestrictionRule1 crs gr vb =-    makeRestrictionRule2 rn c ps cs-    where-        c  = fromMaybe NoNode $ vbMap vb (Var "c")-        p  = fromMaybe NoNode $ vbMap vb (Var "p")-        r  = fromMaybe NoNode $ vbMap vb (Var "r")-        cs = filter (>0) $ map fromInteger $-             rdfFindPredInt c resRdfdMaxCardinality gr-        ps = rdfFindList gr p-        rn = mapFindMaybe (getScopedName r) (LookupMap crs)--makeRestrictionRule2 ::-    Maybe ClassRestriction -> RDFLabel -> [RDFLabel] -> [Int]-    -> Maybe RDFRule-makeRestrictionRule2 (Just restriction) cls@(Res cname) props cs =-    Just restrictionRule-    where-        restrictionRule = Rule-            { ruleName = cname-              -- fwdApply :: [ex] -> [ex]-            , fwdApply = fwdApplyRestriction restriction cls props cs-              -- bwdApply :: ex -> [[ex]]-            , bwdApply = bwdApplyRestriction restriction cls props cs-            , checkInference = bwdCheckInference restrictionRule-            }-makeRestrictionRule2 _ _ _ _ = Nothing-    -- trace "\nmakeRestrictionRule: missing class restriction"----  Forward apply class restriction.-fwdApplyRestriction ::-    ClassRestriction -> RDFLabel -> [RDFLabel] -> [Int] -> [RDFGraph]-    -> [RDFGraph]-fwdApplyRestriction restriction cls props cs antgrs =-    maybe [falseGraph] concat newgrs-      where-        -- Instances of the named class in the graph:-        ris = nub $ rdfFindValSubj resRdfType cls antgr-        --  Merge antecedent graphs into one (with bnode renaming):-        --  (Uses 'if' and 'foldl1' to avoid merging in the common case-        --  of just one graph supplied.)-        antgr = if null antgrs then emptyRDFGraph else foldl1 merge antgrs-        --  Apply class restriction to single instance of the restricted class-        newgr :: RDFLabel -> Maybe [RDFGraph]-        newgr ri = fwdApplyRestriction1 restriction ri props cs antgr-        newgrs :: Maybe [[RDFGraph]]-        newgrs = mapM newgr ris----  Forward apply class restriction to single class instance (ci).---  Return single set of inferred results, for each combination of---  property values, or an empty list, or Nothing if the supplied values---  are inconsistent with the restriction.-fwdApplyRestriction1 ::-    ClassRestriction -> RDFLabel -> [RDFLabel] -> [Int] -> RDFGraph-    -> Maybe [RDFGraph]-fwdApplyRestriction1 restriction ci props cs antgr =-    if grConsistent then Just newgrs else Nothing-    where-        --  Apply restriction to graph-        (grConsistent,_,_,sts) = applyRestriction restriction ci props cs antgr-        --  Select results, eliminate those with unknowns-        nts :: [[RDFLabel]]-        nts = mapMaybe sequence sts-        --  Make new graph from results, including only newly generated arcs-        newarcs = nub [Arc ci p v | vs <- nts, (p,v) <- zip props vs ]-                  \\ getArcs antgr-        newgrs  = if null newarcs then [] else [toRDFGraph newarcs]----  Backward apply class restriction.------  Returns a list of alternatives, any one of which is sufficient to---  satisfy the given consequent.----bwdApplyRestriction ::-    ClassRestriction -> RDFLabel -> [RDFLabel] -> [Int] -> RDFGraph-    -> [[RDFGraph]]-bwdApplyRestriction restriction cls props cs congr =-    fromMaybe [[falseGraph]] newgrs-    where-        -- Instances of the named class in the graph:-        ris = rdfFindValSubj resRdfType cls congr-        --  Apply class restriction to single instance of the restricted class-        newgr :: RDFLabel -> Maybe [[RDFGraph]]-        newgr ri = bwdApplyRestriction1 restriction cls ri props cs congr-        --  'map newgr ris' is conjunction of disjunctions, where-        --  each disjunction is itself a conjunction of conjunctions.-        --  'sequence' distributes the conjunction over the disjunction,-        --  yielding an equivalent disjunction of conjunctions-        --  map concat flattens the conjunctions of conjuctions-        newgrs :: Maybe [[RDFGraph]]-        newgrs = liftM (map concat . sequence) $ mapM newgr ris----  Backward apply a class restriction to single class instance (ci).---  Return one or more sets of antecedent results from which the consequence---  can be derived in the defined relation, an empty list if the supplied---  consequence cannot be inferred, or Nothing if the consequence is---  inconsistent with the restriction.-bwdApplyRestriction1 ::-    ClassRestriction -> RDFLabel -> RDFLabel -> [RDFLabel] -> [Int] -> RDFGraph-    -> Maybe [[RDFGraph]]-bwdApplyRestriction1 restriction cls ci props cs congr =-    if grConsistent then Just grss else Nothing-    where-        --  Apply restriction to graph-        (grConsistent,pvs,cts,_) =-            applyRestriction restriction ci props cs congr-        --  Build list of all full tuples consistent with the values supplied-        fts :: [[RDFLabel]]-        fts = concatMap snd cts-        --  Construct partial tuples from members of fts from which at least-        --  one of the supplied values can be derived-        pts :: [([Maybe RDFLabel],[RDFLabel])]-        pts = concatMap (deriveTuple restriction) fts-        --  Select combinations of members of pts from which all the-        --  supplied values can be derived-        dtss :: [[[Maybe RDFLabel]]]-        dtss = coverSets pvs pts-        --  Filter members of dtss that fully cover the values-        --  obtained from the consequence graph.-        ftss :: [[[Maybe RDFLabel]]]-        ftss = filter (not . (\t -> coversVals deleteMaybe t pvs)) dtss-        --  Make new graphs for all alternatives-        grss :: [[RDFGraph]]-        grss = map ( makeGraphs . newArcs ) ftss-        --  Collect arcs for one alternative-        newArcs dts =-            [ Arc ci p v | mvs <- dts, (p,Just v) <- zip props mvs ]-        --  Make graphs for one alternative-        makeGraphs = map (toRDFGraph . (:[])) . (Arc ci resRdfType cls :)----  Helper function to select sub-tuples from which some of a set of---  values can be derived using a class restriction.------  restriction is the restriction being evaluated.---  ft          is a full tuple of values known to be consistent with---              the restriction------  The result returned is a list of pairs, whose first member is a partial---  tuples from which the full tuple supplied can be derived, and the second---  is the supplied tuple calculated from that input.----deriveTuple ::-    ClassRestriction -> [RDFLabel]-    -> [([Maybe RDFLabel],[RDFLabel])]-deriveTuple restriction ft =-    map (tosnd ft) $ minima partCompareListMaybe $ filter derives partials-    where-        partials = mapM (\x -> [Nothing,Just x]) ft-        derives  = ([ft]==) . fromJust . crFunc restriction-        tosnd    = flip (,)----  Helper function to apply a restriction to selected information from---  a supplied graph, and returns a tuple containing:---  (a) an indication of whether the graph is consistent with the---      restriction---  (b) a list of values specified in the graph for each property---  (c) a complete list of tuples that use combinations of values from---      the graph and are consistent with the restriction.---      Each member is a pair consisting of some combination of input---      values, and a list of complete tuple values that can be---      calculated from those inputs, or an empty list if there is---      insufficient information.---  (d) a set of tuples that are consistent with the restriction and use---      as much information from the graph as possible.  This set is---      minimal in the sense that they must all correspond to different---      complete input tuples satisfying the restriction.------  This function factors out logic that is common to forward and---  backward chaining of a class restriction.------  restriction is the class restriction being applied---  ci          is the identifier of a graph node to be tested---  props       is a list of properties of the graph noode whose values---              are constrained by the class restriction.---  cs          is a list of max cardinality constraints on the restriction,---              the minimum of which is used as the cardinality constraint---              on the restriction.  If the list is null, no cardinality---              constraint is applied.---  gr          is the graph from which property values are extracted.----applyRestriction ::-    ClassRestriction -> RDFLabel -> [RDFLabel] -> [Int] -> RDFGraph-    -> ( Bool-       , [[RDFLabel]]-       , [([Maybe RDFLabel],[[RDFLabel]])]-       , [[Maybe RDFLabel]]-       )-applyRestriction restriction ci props cs gr =-    (coversVals deleteMaybe sts pvs && cardinalityOK, pvs, cts, sts )-    where-        --  Extract from the antecedent graph all specified values of the-        --  restricted properties (constructs inner list for each property)-        pvs :: [[RDFLabel]]-        pvs = [ rdfFindPredVal ci p gr | p <- props ]-        --  Convert tuple of alternatives to list of alternative tuples-        --  (Each tuple is an inner list)-        pts :: [[Maybe RDFLabel]]-        pts = mapM allJustAndNothing pvs-        --  Try class restriction calculation for each tuple-        --  For each, result may be:-        --    Nothing  (inconsistent)-        --    Just []  (underspecified)-        --    Just [t] (single tuple of values derived from given values)-        --    Just ts  (alternative tuples derived from given values)-        rts :: [Maybe [[RDFLabel]]]-        rts = map (crFunc restriction) pts-        -        --  Extract list of consistent tuples of given values-        cts :: [([Maybe RDFLabel],[[RDFLabel]])]-        cts = mapMaybe tupleConv (zip pts rts)-        -        --  TODO: be more idiomatic?-        tupleConv :: (a, Maybe b) -> Maybe (a,b)-        tupleConv (a, Just b)  = Just (a,b)-        tupleConv _            = Nothing-        -        --  Build list of consistent tuples with maximum information-        --  based on that supplied and available-        -- mts = concatMap mostValues cts-        mts = map mostOneValue cts-        --  Eliminate consistent results subsumed by others.-        --  This results in a mimimal possible set of consistent inputs,-        --  because if any pair could be consistently unified then their-        --  common subsumer would still be in the list, and both would be-        --  thereby eliminated.-        sts :: [[Maybe RDFLabel]]-        sts = maxima partCompareListMaybe mts-        --  Check the cardinality constraint-        cardinalityOK = null cs || length sts <= minimum cs-        ---  Map a non-empty list of values to a list of Just values,---  preceding each with a Nothing element.------  Nothing corresponds to an unknown value.  This logic is used---  as part of constructing a list of alternative tuples of known---  data values (either supplied or calculated from the class---  restriction).----allJustAndNothing :: [a] -> [Maybe a]-allJustAndNothing as = Nothing:map Just as--{----  Get maximum information about possible tuple values from a---  given pair of input tuple, which is known to be consistent with---  the restriction, and calculated result tuples.  Where the result---  tuple is not exactly calculated, return the input tuple.------  imvs    tuple of Maybe element values, with Nothing for---          unspecified values---  movss   Maybe list of possible fully-specified result tuples,---          an empty list if no result tuples can be computed---          based on the input tuple, or Nothing if the input---          tuple is inconsistent.----mostValues :: ([Maybe a],[[a]]) -> [[Maybe a]]-mostValues (imvs,([])) = [imvs]-mostValues (_,movss) = map (map Just) movss--}----  Get maximum information about possible tuple values from a---  given pair of input and possible result tuples, which is---  known to be consistent with the restriction.  If the result---  tuple is not exactly calculated, return the input tuple.------  This is a variant of mostValues that returns a single vector.---  Multiple possible values are considered to be equivalent to---  Just [], i.e. unknown result.------  imvs    tuple of Maybe element values, with Nothing for---          unspecified values---  movss   Maybe list of possible fully-specified result tuples,---          or an empty list if no result tuples can be computed---          based on the input tuple.----mostOneValue :: ([Maybe a],[[a]]) -> [Maybe a]-mostOneValue (_,[movs]) = map Just movs-mostOneValue (imvs,_)   = imvs----  Helper function that returns subsets of dts that "cover" the indicated---  values;  i.e. from which all of the supplied values can be deduced---  by the enumerated function results.  The minima of all such subsets is---  returned, as each of these corresponds to some minimum information needed---  to deduce all of the given values.------  pvs     is a list of lists of values to be covered.  The inner list---          contains multiple values for each member of a tuple.---  dts     is an enumerated list of function values from some subset of---          the tuple space to complete tuples.  Each member is a pair---          containing the partial tuple (using Nothing for unspecified---          values) and the full tuple calculated from it.------  The return value is a disjunction of conjunctions of partial tuples---  that cover the indicated parameter values.------  NOTE:---  The result minimization is not perfect (cf. test2 below), but I believe---  it is adequate for the practical situations I envisage, and in any---  case will not result in incorrect values.  It's significance is for---  search-tree pruning.  A perfect minimization might be achieved by---  using a more subtle partial ordering that takes account of both subsets---  and the partial ordering of set members in place of 'partCompareListSubset'.----coverSets  :: (Eq a) => [[a]] -> [([Maybe a],[a])] -> [[[Maybe a]]]-coverSets pvs dts =-    minima partCompareListSubset $ map (map fst) ctss-    where-        ctss = filter coverspvs $ powerSet cts-        cts  = minima (partComparePair partCompareListMaybe partCompareEq) dts-        coverspvs cs = coversVals delete (map snd cs) pvs----  Does a supplied list of tuples cover a list of possible alternative---  values for each tuple member?----coversVals :: (a->[b]->[b]) -> [[a]] -> [[b]] -> Bool-coversVals dropVal ts vss =-    -- all null (foldr dropUsed vss ts)-    any (all null) (scanr dropUsed vss ts)-    where-        --  Remove single tuple values from the list of supplied values:-        dropUsed []       []     = []-        dropUsed (a:as) (bs:bss) = dropVal a bs : dropUsed as bss-        dropUsed _ _ = error "coversVals.dropUsed: list length mismatch"--{----  Does a supplied list of possible alternative values for each---  element of a tuple cover every tuple in a supplied list?------  (See module spike-coverVals.hs for test cases)----coversAll :: ([a]->b->Bool) -> [[a]] -> [[b]] -> Bool-coversAll matchElem vss ts = all (invss vss) ts-    where-        --  Test if a given tuple is covered by vss-        invss vss t = and $ zipWith matchElem vss t----  Test if the value in a Maybe is contained in a list.-maybeElem :: (Eq a) => Maybe a -> [a] -> Bool-maybeElem Nothing  = const True-maybeElem (Just t) = elem t--}---- |Delete a Maybe value from a list-deleteMaybe :: (Eq a) => Maybe a -> [a] -> [a]-deleteMaybe Nothing  as = as-deleteMaybe (Just a) as = delete a as-----------------------------------------------------------------  Make restriction rules from supplied datatype and graph---------------------------------------------------------------makeRDFDatatypeRestrictionRules :: RDFDatatypeVal vt -> RDFGraph -> [RDFRule]-makeRDFDatatypeRestrictionRules dtval gr =-    makeRDFClassRestrictionRules dcrs gr-    where-        dcrs = map (makeDatatypeRestriction dtval) (tvalRel dtval)----------------------------------------------------------------------------------------  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------------------------------------------------------------------------------------
− Swish/RDF/Datatype.hs
@@ -1,1064 +0,0 @@-{-# 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---  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/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------------------------------------------------------------------------------------
− 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 gs =-    map (sigCalc . arcToTriple) gs-    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------------------------------------------------------------------------------------
− 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------------------------------------------------------------------------------------
− 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------------------------------------------------------------------------------------
− 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------------------------------------------------------------------------------------
− 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------------------------------------------------------------------------------------
− 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------------------------------------------------------------------------------------
− 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 v = go [] v-  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------------------------------------------------------------------------------------
− 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------------------------------------------------------------------------------------
− Swish/RDF/Proof.hs
@@ -1,296 +0,0 @@------------------------------------------------------------------------------------  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.RDF.Proof-    ( Proof(..), Step(..)-    , checkProof, explainProof, checkStep, showProof, showsProof, showsFormula )-where--import Swish.RDF.Ruleset (Ruleset(..))--import Swish.RDF.Rule-    ( Expression(..), Formula(..), Rule(..)-    , showsFormula, showsFormulae )--import Swish.Utils.ShowM (ShowM(..))--import Swish.Utils.ListHelpers (subset)--import Data.List (union, intersect, intercalate, foldl')-import Data.Maybe (catMaybes, isNothing)-----------------------------------------------------------------  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 :: -  (ShowM 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 :: -  (ShowM 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 :: (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---- |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++")")-    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------------------------------------------------------------------------------------
− 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------------------------------------------------------------------------------------
− 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------------------------------------------------------------------------------------
− 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------------------------------------------------------------------------------------
− Swish/RDF/RDFGraph.hs
@@ -1,1141 +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-      -      -- * Export selected RDFLabel values-    , resRdfType, resRdfFirst, resRdfRest, resRdfNil-    , resRdfsMember-    , resRdfdGeneralRestriction-    , resRdfdOnProperties, resRdfdConstraint, resRdfdMaxCardinality-    , resOwlSameAs, 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------------------------------------------------------------resRdfType, resRdfFirst, resRdfRest, resRdfNil,-  resRdfsMember, resRdfdGeneralRestriction,-  resRdfdOnProperties, resRdfdConstraint,-  resRdfdMaxCardinality, resOwlSameAs, resLogImplies-  :: RDFLabel--resRdfType               = Res rdfType -resRdfFirst               = Res rdfFirst -resRdfRest                = Res rdfRest-resRdfNil                 = Res rdfNil-resRdfsMember             = Res rdfsMember-resRdfdGeneralRestriction = Res rdfdGeneralRestriction-resRdfdOnProperties       = Res rdfdOnProperties-resRdfdConstraint         = Res rdfdConstraint-resRdfdMaxCardinality     = Res rdfdMaxCardinality-resOwlSameAs              = Res owlSameAs-resLogImplies             = Res logImplies--------------------------------------------------------------  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 emoty 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------------------------------------------------------------------------------------
− 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------------------------------------------------------------------------------------
− 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------------------------------------------------------------------------------------
− 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 gr = null $ getArcs gr-----------------------------------------------------------------  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------------------------------------------------------------------------------------
− 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-    ]----  Define 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 "cp1"-            "?x  ?c ?y . "-            "?c rdf:type rdfs:ContainerMembershipProperty ."-            (makeVarFilterModify $ isMemberPropV "c")--rdfscp12 :: RDFRule-rdfscp12 = makeN3ClosureRule scopeRDFS "cp1"-            "?c  ?p ?y . "-            "?c rdf:type rdfs:ContainerMembershipProperty ."-            (makeVarFilterModify $ isMemberPropV "c")--rdfscp13 :: RDFRule-rdfscp13 = makeN3ClosureRule scopeRDFS "cp1"-            "?x  ?p ?c . "-            "?c rdf:type rdfs:ContainerMembershipProperty ."-            (makeVarFilterModify $ isMemberPropV "c")--rdfscp21 :: RDFRule-rdfscp21 = makeN3ClosureRule scopeRDFS "cp1"-            "?x  ?c ?y . "-            "?c rdfs:domain rdfs:Resource ."-            (makeVarFilterModify $ isMemberPropV "c")--rdfscp22 :: RDFRule-rdfscp22 = makeN3ClosureRule scopeRDFS "cp1"-            "?c  ?p ?y . "-            "?c rdfs:domain rdfs:Resource ."-            (makeVarFilterModify $ isMemberPropV "c")--rdfscp23 :: RDFRule-rdfscp23 = makeN3ClosureRule scopeRDFS "cp1"-            "?x  ?p ?c . "-            "?c rdfs:domain rdfs:Resource ."-            (makeVarFilterModify $ isMemberPropV "c")--rdfscp31 :: RDFRule-rdfscp31 = makeN3ClosureRule scopeRDFS "cp1"-            "?x  ?c ?y . "-            "?c rdfs:range rdfs:Resource ."-            (makeVarFilterModify $ isMemberPropV "c")--rdfscp32 :: RDFRule-rdfscp32 = makeN3ClosureRule scopeRDFS "cp1"-            "?c  ?p ?y . "-            "?c rdfs:range rdfs:Resource ."-            (makeVarFilterModify $ isMemberPropV "c")--rdfscp33 :: RDFRule-rdfscp33 = makeN3ClosureRule scopeRDFS "cp1"-            "?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-    ]----  Define 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-    ]----  Define ruleset for RDFD 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------------------------------------------------------------------------------------
− Swish/RDF/RDFQuery.hs
@@ -1,610 +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.--------------------------------------------------------------------------------------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.------  A very basic form of graph query, a query graph and---  a target graph, and returns a list of 'RDFVarBinding'---  values, each of which corresponds to a set of variable---  bindings that make the query graph a subgraph of the---  target graph, or @[]@ if the query cannot be matched.------  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 -> RDFGraph -> [RDFVarBinding]-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]]]---- |rdfFindArcs is the main function here:  it takes 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:---          @rdfQuerySimple (rdfSubjEq s)@------  * find all statements with given property:---          @rdfQuerySimple (rdfPredEq p)@------  * find all statements with given object:---          @rdfQuerySimple (rdfObjEq  o)@------  * find all statements matching conjunction of these conditions:---          @rdfQuerySimple ('allp' [...])@------  * find all statements matching disjunction of these conditions:---          @rdfQuerySimple ('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 -> RDFLabel -> 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 -> RDFLabel -> 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 have a of given value for for a given predicate-rdfFindValSubj :: RDFLabel -> RDFLabel -> 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 some 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------------------------------------------------------------------------------------
− 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------------------------------------------------------------------------------------
− 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------------------------------------------------------------------------------------
− Swish/RDF/Rule.hs
@@ -1,244 +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)-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@-nullScope :: Namespace-nullScope = makeNamespace (Just "null") nullScopeURI--nullSN :: T.Text -> ScopedName-nullSN = makeScopedName (Just "null") nullScopeURI--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 rule names-instance Eq (Rule ex) where-    r1 == r2 = ruleName r1 == ruleName r2---- |Define ordering of rules based on 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------------------------------------------------------------------------------------
− Swish/RDF/Ruleset.hs
@@ -1,153 +0,0 @@-{-# LANGUAGE MultiParamTypeClasses #-}------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  Ruleset---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 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.RDF.Ruleset-    ( Ruleset(..), RulesetMap-    , makeRuleset, getRulesetNamespace, getRulesetAxioms, getRulesetRules-    , getRulesetAxiom, getRulesetRule-    , getContextAxiom, getMaybeContextAxiom-    , getContextRule,  getMaybeContextRule-    )-where--import Swish.Utils.Namespace (Namespace, ScopedName)-import Swish.RDF.Rule (Formula(..), Rule(..))--import Swish.Utils.LookupMap-    ( LookupEntryClass(..), LookupMap(..)-    , mapFindMaybe-    )--{--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.Maybe (fromMaybe, listToMaybe, mapMaybe)---- | Ruleset, having namespace, axioms and rules--data Ruleset ex = Ruleset-    { rsNamespace :: Namespace-    , rsAxioms    :: [Formula ex]-    , rsRules     :: [Rule ex]-    }--{---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))) ""--}--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--type RulesetMap ex = LookupMap (Ruleset ex)--makeRuleset :: Namespace -> [Formula ex] -> [Rule ex] -> Ruleset ex-makeRuleset nsp fms rls = Ruleset-    { rsNamespace = nsp-    , rsAxioms    = fms-    , rsRules     = rls-    }--getRulesetNamespace :: Ruleset ex -> Namespace-getRulesetNamespace = rsNamespace--getRulesetAxioms :: Ruleset ex -> [Formula ex]-getRulesetAxioms = rsAxioms--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 or rule in a proof context.-getContextAxiom :: ScopedName -> Formula ex -> [Ruleset ex] -> Formula ex-getContextAxiom nam def rsets = fromMaybe def (getMaybeContextAxiom nam rsets)-    {--    foldr (flip fromMaybe) def $ map (getRulesetAxiom nam) rsets-    -}--getMaybeContextAxiom :: ScopedName -> [Ruleset ex] -> Maybe (Formula ex)-getMaybeContextAxiom nam rsets =-    listToMaybe $ mapMaybe (getRulesetAxiom nam) rsets--getContextRule :: ScopedName -> Rule ex -> [Ruleset ex] -> Rule ex-getContextRule nam def rsets = fromMaybe def (getMaybeContextRule nam rsets)-    {--    foldr (flip fromMaybe) def $ map (getRulesetRule nam) rsets-    -}--getMaybeContextRule :: ScopedName -> [Ruleset ex] -> Maybe (Rule ex)-getMaybeContextRule nam rsets =-    listToMaybe $ mapMaybe (getRulesetRule nam) rsets----------------------------------------------------------------------------------------  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------------------------------------------------------------------------------------
− 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------------------------------------------------------------------------------------
− Swish/RDF/SwishMain.hs
@@ -1,263 +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).------  This module is likely to be moved to the Swish module.----------------------------------------------------------------------------------------  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.--- -newtype SwishAction = SA (SwishStateIO ())---- | 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 $ 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 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 :: Maybe String -> Either (String, SwishStatus) SwishAction-validateBase Nothing  = Right $ SA $ swishBase Nothing Nothing-validateBase (Just b) =-  case fmap qnameFromURI (parseURI b) of-    j@(Just _) -> Right $ SA $ 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------------------------------------------------------------------------------------
− 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------------------------------------------------------------------------------------
− 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------------------------------------------------------------------------------------
− Swish/RDF/TurtleFormatter.hs
@@ -1,885 +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 (see [1])---  for an RDFGraph value.------ 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/>)--------------------------------------------------------------------------------------{--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-    )-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--{--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--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------------------------------------------------------------------------------------
− 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------------------------------------------------------------------------------------
− Swish/RDF/VarBinding.hs
@@ -1,540 +0,0 @@-{-# LANGUAGE MultiParamTypeClasses, TypeSynonymInstances, OverloadedStrings #-}-{-# LANGUAGE FlexibleInstances #-} -- needed for ghc 7.12-------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  VarBinding---  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke---  License     :  GPL V2------  Maintainer  :  Douglas Burke---  Stability   :  experimental---  Portability :  MultiParamTypeClasses, TypeSynonymInstances, OverloadedStrings, FlexibleInstances------  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.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-    )-where--import Swish.Utils.LookupMap-    ( LookupEntryClass(..) -    , makeLookupMap, mapFindMaybe-    )--import Swish.Utils.Namespace (ScopedName, getScopeLocal)-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-----------------------------------------------------------------  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 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---- |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 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/RDF/Vocabulary.hs
@@ -1,264 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  Vocabulary---  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 commonly used vocabulary terms,---  using the 'Namespace' and 'ScopedName' data types.--------------------------------------------------------------------------------------module Swish.RDF.Vocabulary-    ( namespaceRDF-    , namespaceRDFS-    , namespaceRDFD-    , namespaceOWL-    , namespaceXSD-    , namespaceXsdType-    , namespaceMATH-    , namespaceLOG-    , namespaceDAML-    , namespaceDefault-    , namespaceSwish -    , swishName-    , namespaceLang,  langName, langTag, isLang-    , scopeRDF-    , scopeRDFS-    , scopeRDFD-    , rdfDatatype, rdfResource, rdfAbout, rdfID-    , rdfType-    , rdfFirst, rdfRest, rdfNil, rdfXMLLiteral-    , rdfsMember-    , rdfdGeneralRestriction-    , rdfdOnProperties, rdfdConstraint, rdfdMaxCardinality-    , owlSameAs, logImplies-    , xsdType, xsdString, xsdBoolean-    , xsdDecimal, xsdInteger-    , xsdNonNegInteger, xsdNonPosInteger, xsdPosInteger, xsdNegInteger-    , xsdFloat, xsdDouble-    , xsdDate, xsdDateTime-    , defaultBase-    )-where--import Swish.Utils.Namespace (Namespace, makeNamespace, ScopedName, getScopeLocal, getScopeNamespace, makeScopedName)--import Data.Monoid (mappend, mconcat)-import Data.Maybe (fromMaybe)-import Network.URI (URI, parseURI)--import qualified Data.Text as T-----------------------------------------------------------------  Define some common namespace values---------------------------------------------------------------toNS :: T.Text -> T.Text -> Namespace-toNS p utxt = -  let ustr = T.unpack utxt-      uri = fromMaybe (error ("Unable to convert " ++ ustr ++ " to a URI")) $-            parseURI ustr-  in makeNamespace (Just p) uri--toNSU :: T.Text -> URI -> Namespace-toNSU p = makeNamespace (Just p)--namespaceXsdType :: T.Text -> Namespace-namespaceXsdType dtn = toNS ("xsd_" `mappend` dtn)-                       (mconcat ["http://id.ninebynine.org/2003/XMLSchema/", dtn, "#"])--namespaceRDF :: Namespace-namespaceRDFS :: Namespace-namespaceRDFD :: Namespace-namespaceOWL :: Namespace-namespaceXSD :: Namespace-namespaceMATH :: Namespace-namespaceLOG :: Namespace-namespaceDAML :: Namespace-namespaceSwish :: Namespace-namespaceDefault :: Namespace-namespaceLang :: Namespace--namespaceRDF     = toNSU "rdf"    namespaceRDFURI-namespaceRDFS    = toNSU "rdfs"   namespaceRDFSURI-namespaceRDFD    = toNSU "rdfd"   namespaceRDFDURI-namespaceOWL     = toNSU "owl"    namespaceOWLURI-namespaceXSD     = toNSU "xsd"    namespaceXSDURI-namespaceMATH    = toNS "math"    "http://www.w3.org/2000/10/swap/math#"-namespaceLOG     = toNSU "log"    namespaceLOGURI-namespaceDAML    = toNS "daml"    "http://www.daml.org/2000/10/daml-ont#"-namespaceSwish   = toNSU "swish"  namespaceSwishURI-namespaceLang    = toNSU "lang"   namespaceLangURI-namespaceDefault = toNSU "default" namespaceDefaultURI--tU :: String -> URI-tU = fromMaybe (error "Internal error processing namespace URI") . parseURI--namespaceRDFURI, namespaceRDFSURI, namespaceRDFDURI, -  namespaceXSDURI, namespaceOWLURI, namespaceLOGURI,-  namespaceSwishURI, -  namespaceLangURI, namespaceDefaultURI :: URI-namespaceRDFURI   = tU "http://www.w3.org/1999/02/22-rdf-syntax-ns#"-namespaceRDFSURI  = tU "http://www.w3.org/2000/01/rdf-schema#"-namespaceRDFDURI  = tU "http://id.ninebynine.org/2003/rdfext/rdfd#"-namespaceOWLURI   = tU "http://www.w3.org/2002/07/owl#"-namespaceXSDURI   = tU "http://www.w3.org/2001/XMLSchema#"-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/"--swishName :: T.Text -> ScopedName--- swishName = ScopedName namespaceSwish-swishName = makeScopedName (Just "swish") namespaceSwishURI----------------------------------------------------------------  Language tags-------------------------------------------------------------------  Note:  simple language tag URIs may be abbreviated as lang:tag,---  but if the tag contains a hyphen, this would not be valid QName---  form in Notation3, even though it is a valid QName component.---  Fortunately, they do not currently need to appear in Notation3 as---  distinct labels (but future developments may change that).--langName :: T.Text -> ScopedName--- langName = ScopedName namespaceLang . T.toLower-langName = makeScopedName (Just "lang") namespaceLangURI . T.toLower--langTag :: ScopedName -> T.Text-langTag = getScopeLocal--isLang :: ScopedName -> Bool-isLang sname = getScopeNamespace sname == namespaceLang-----------------------------------------------------------------  Define namespaces for RDF rules, axioms, etc---------------------------------------------------------------scopeRDF, scopeRDFS, scopeRDFD :: Namespace--scopeRDF  = toNS "rs_rdf"   "http://id.ninebynine.org/2003/Ruleset/rdf#"-scopeRDFS = toNS "rs_rdfs"  "http://id.ninebynine.org/2003/Ruleset/rdfs#"-scopeRDFD = toNS "rs_rdfd"  "http://id.ninebynine.org/2003/Ruleset/rdfd#"-----------------------------------------------------------------  Define some common vocabulary terms---------------------------------------------------------------toRDF, toRDFS, toRDFD :: T.Text -> ScopedName-toRDF  = makeScopedName (Just "rdf")  namespaceRDFURI-toRDFS = makeScopedName (Just "rdfs") namespaceRDFSURI-toRDFD = makeScopedName (Just "rdfd") namespaceRDFDURI--rdfDatatype   :: ScopedName-rdfResource   :: ScopedName-rdfAbout      :: ScopedName-rdfID         :: ScopedName-rdfType       :: ScopedName-rdfFirst      :: ScopedName-rdfRest       :: ScopedName-rdfNil        :: ScopedName-rdfXMLLiteral :: ScopedName--rdfDatatype   = toRDF "datatype"-rdfResource   = toRDF "resource"-rdfAbout      = toRDF "about"-rdfID         = toRDF "ID"-rdfType       = toRDF "type"-rdfFirst      = toRDF "first"-rdfRest       = toRDF "rest"-rdfNil        = toRDF "nil"-rdfXMLLiteral = toRDF "XMLLiteral"--rdfsMember    :: ScopedName-rdfsMember    = toRDFS "member"--rdfdGeneralRestriction :: ScopedName-rdfdOnProperties       :: ScopedName-rdfdConstraint         :: ScopedName-rdfdMaxCardinality     :: ScopedName--rdfdGeneralRestriction = toRDFD "GeneralRestriction"-rdfdOnProperties       = toRDFD "onProperties"-rdfdConstraint         = toRDFD "constraint"-rdfdMaxCardinality     = toRDFD "maxCardinality"--xsdType             :: T.Text -> ScopedName-xsdType             = makeScopedName (Just "xsd") namespaceXSDURI--xsdString           :: ScopedName-xsdString           = xsdType "string"--xsdBoolean          :: ScopedName-xsdBoolean          = xsdType "boolean"--xsdDecimal          :: ScopedName-xsdDecimal          = xsdType "decimal"--xsdInteger          :: ScopedName-xsdInteger          = xsdType "integer"--xsdNonNegInteger   :: ScopedName-xsdNonNegInteger   = xsdType "nonNegativeInteger"--xsdNonPosInteger   :: ScopedName-xsdNonPosInteger   = xsdType "nonPositiveInteger"--xsdPosInteger      :: ScopedName-xsdPosInteger      = xsdType "positiveInteger"--xsdNegInteger      :: ScopedName-xsdNegInteger      = xsdType "negativeInteger"--xsdFloat            :: ScopedName-xsdFloat            = xsdType "float"--xsdDouble           :: ScopedName-xsdDouble           = xsdType "double"--xsdDate, xsdDateTime :: ScopedName-xsdDate = xsdType "date"-xsdDateTime = xsdType "dateTime"--owlSameAs   :: ScopedName-owlSameAs   = makeScopedName (Just "owl") namespaceOWLURI "sameAs"--logImplies  :: ScopedName-logImplies  = makeScopedName (Just "log") namespaceLOGURI "implies"--defaultBase :: ScopedName-defaultBase = makeScopedName (Just "default") namespaceDefaultURI "base"----------------------------------------------------------------------------------------  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------------------------------------------------------------------------------------
− Swish/Utils/ListHelpers.hs
@@ -1,359 +0,0 @@------------------------------------------------------------------------------------  See end of this file for licence information.------------------------------------------------------------------------------------ |---  Module      :  ListHelpers---  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 some generic list and related helper functions. --------------------------------------------------------------------------------------{--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-        -      )-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 ---        likely need changing to Ord a----------------------------------------------------------------- |Subset test--subset          :: (Eq a) => [a] -> [a] -> Bool-a `subset` b    = and [ ma `elem` b | ma <- a ]---- |Set equivalence test--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----------------------------------------------------------------- |Apply list of functions to some value, returning list of results.---  It's kind of like an converse map.------  This is similar to the 'ap' function in the Monad library.----flist :: [a->b] -> a -> [b]-flist fs a = map ($ a) fs--{--flisttest = flist [(1*),(2*),(3*)] 5 -- [5,10,15]--}--{----- |A more generalized form of flist that works with arbitrary Monads.---  (Suggested by Derek Elkin.)--fmonad :: Monad m => m (a->b) -> a -> m b-fmonad fm a =-    do  { f <- fm-        ; return $ f a-        }---}--{--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---  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/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------------------------------------------------------------------------------------
− 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------------------------------------------------------------------------------------
− 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------------------------------------------------------------------------------------
− 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 as = foldl' add [] as-    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------------------------------------------------------------------------------------
− 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------------------------------------------------------------------------------------
− 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------------------------------------------------------------------------------------
+ app/SwishApp.hs view
@@ -0,0 +1,90 @@+--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  SwishApp+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke+--  License     :  GPL V2+--+--  Maintainer  :  Douglas Burke+--  Stability   :  experimental+--  Portability :  H98+--+--  This module is a wrapper for the main program of Swish.+--+--------------------------------------------------------------------------------++module Main where++import Paths_swish (version)+import Data.Version (showVersion)++import System.Environment (getArgs)+import System.Exit (ExitCode(ExitSuccess,ExitFailure), exitWith)++import Control.Monad (unless)++import System.IO (stderr, hPutStrLn)++import Swish++------------------------------------------------------------+--  Swish main program+------------------------------------------------------------+--+--  This is a minimal wrapper for the real main program, to facilitate+--  interactive execution (e.g. in HUGS) of different command lines.+--+--  execStateT runs the monad with a supplied initial state,+--  then separates the resulting state from the IO monad.++main :: IO ()+main = do+  args <- getArgs+  let (flags, cmds) = splitArguments args+      doHelp = "-h" `elem` flags+      doVersion = "-v" `elem` flags+      doQuiet = "-q" `elem` flags+      +  if doHelp || doVersion+    then if doHelp then displaySwishHelp else displayVersion >> exitWith ExitSuccess+    else do+      unless doQuiet $ displayVersion >> putStrLn "\n"+      case validateCommands cmds of+        Left (emsg, ecode) -> do+          hPutStrLn stderr $ "Swish: " ++ emsg+          exitWith $ ExitFailure $ fromEnum ecode+          +        Right acts -> do+          code <- runSwishActions acts+          case code of+            SwishSuccess -> exitWith ExitSuccess+            _ -> hPutStrLn stderr ("Swish: "++show code)+                 >> exitWith (ExitFailure $ fromEnum code)+  +displayVersion :: IO ()+displayVersion = putStrLn $ "Swish " ++ showVersion version +  +--------------------------------------------------------------------------------+--+--  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/Data/Interned/URI.hs view
@@ -0,0 +1,91 @@+{-# LANGUAGE TypeFamilies, FlexibleInstances #-}++--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  URI+--  Copyright   :  (c) 2011 Douglas Burke+--  License     :  GPL V2+--+--  Maintainer  :  Douglas Burke+--  Stability   :  experimental+--  Portability :  TypeFamilies, FlexibleInstances+--+--  Support interning URIs.+--+--------------------------------------------------------------------------------++module Data.Interned.URI+       ( InternedURI+       ) where++import Data.String (IsString(..))+import Data.Hashable+import Data.Interned+import Data.Maybe (fromMaybe)++import Network.URI++-- Could look at adding UNPACK statements before each component+data InternedURI = InternedURI !Int !URI++instance IsString InternedURI where+  fromString = intern .+               fromMaybe (error "Error: unable to create a URI.") .+               parseURIReference+              ++instance Eq InternedURI where+  InternedURI a _ == InternedURI b _ = a == b++instance Ord InternedURI where+  compare (InternedURI a _) (InternedURI b _) = compare a b++instance Show InternedURI where+  showsPrec d (InternedURI _ b) = showsPrec d b++instance Interned InternedURI where+  type Uninterned InternedURI = URI+  data Description InternedURI = DU !URI deriving (Eq) -- DU {-# UNPACK #-} !URI deriving (Eq) +  describe = DU+  identify = InternedURI+  identity (InternedURI i _) = i+  cache = iuCache++instance Uninternable InternedURI where+  unintern (InternedURI _ b) = b ++-- Rather than access the URI components, just use the reverse of the+-- string representation of the URI.+instance Hashable (Description InternedURI) where+  hash = hashWithSalt 5381 -- use the stringSalt value from Data.Hashable+  hashWithSalt salt (DU u) = hashWithSalt salt ((reverse . show) u)++iuCache :: Cache InternedURI+iuCache = mkCache+{-# NOINLINE iuCache #-}++--------------------------------------------------------------------------------+--+--  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.hs view
@@ -10,64 +10,53 @@ --  Stability   :  experimental --  Portability :  H98 -----  This module is a wrapper for the main program of Swish.+--  Swish:  Semantic Web Inference Scripting in Haskell -------------------------------------------------------------------------------------module Main where--import Paths_swish (version)-import Data.Version (showVersion)--import System.Environment (getArgs)-import System.Exit (ExitCode(ExitSuccess,ExitFailure), exitWith)--import Control.Monad (unless)--import System.IO (stderr, hPutStrLn)--import Swish-----------------------------------------------------------------  Swish main program-------------------------------------------------------------+--  This program is a simple skeleton for constructing Semantic Web [1]+--  inference tools in Haskell, using the RDF graph and several RDF+--  parsers (at present Notation 3 and NTriples). -----  This is a minimal wrapper for the real main program, to facilitate---  interactive execution (e.g. in HUGS) of different command lines.+--  It might be viewed as a kind of embroyonic CWM [2] in Haskell,+--  except that the intent is that Haskell will be used as a primary+--  language for defining inferences.  As such, Swish is an open-ended+--  toolkit for constructing new special-purpose Semantic Web+--  applications rather than a closed, self-contained general-purpose+--  SW application.  As such, it is part of another experiment along+--  the lines described in [3]. -----  execStateT runs the monad with a supplied initial state,---  then separates the resulting state from the IO monad.+--  The script format used by Swish is described in+--  "Swish.RDF.SwishScript".+--+--  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".+--+--  (1) Semantic web: <http://www.w3.org/2001/sw/>+--+--  (2) CWM:          <http://www.w3.org/2000/10/swap/doc/cwm.html>+--+--  (3) Motivation:   <http://www.w3.org/2000/10/swap/doc/Motivation.html>+--+--  (4) Notation 3:   <http://www.w3.org/TeamSubmission/2008/SUBM-n3-20080114/>+--+--  (5) Turtle:       <http://www.w3.org/TR/turtle/>+--+--  (6) RDF:          <http://www.w3.org/RDF/>+--+-------------------------------------------------------------------------------- -main :: IO ()-main = do-  args <- getArgs-  let (flags, cmds) = splitArguments args-      doHelp = "-h" `elem` flags-      doVersion = "-v" `elem` flags-      doQuiet = "-q" `elem` flags-      -  if doHelp || doVersion-    then if doHelp then displaySwishHelp else displayVersion >> exitWith ExitSuccess-    else do-      unless doQuiet $ displayVersion >> putStrLn "\n"-      case validateCommands cmds of-        Left (emsg, ecode) -> do-          hPutStrLn stderr $ "Swish: " ++ emsg-          exitWith $ ExitFailure $ fromEnum ecode-          -        Right acts -> do-          code <- runSwishActions acts-          case code of-            SwishSuccess -> exitWith ExitSuccess-            _ -> hPutStrLn stderr ("Swish: "++show code)-                 >> exitWith (ExitFailure $ fromEnum code)-  -displayVersion :: IO ()-displayVersion = putStrLn $ "Swish " ++ showVersion version -  +module Swish+       (+         module Swish.RDF.SwishMain+       ) where++import Swish.RDF.SwishMain+-- should this re-export Swish.RDF.SwishMain or other components?+        -------------------------------------------------------------------------------- -----  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke  +--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke --  All rights reserved. -- --  This file is part of Swish.
+ src/Swish/RDF.hs view
@@ -0,0 +1,44 @@+--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  RDF+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke+--  License     :  GPL V2+--+--  Maintainer  :  Douglas Burke+--  Stability   :  experimental+--  Portability :  FlexibleInstances, MultiParamTypeClasses, TypeSynonymInstances+--+--  This module provides an in-memory RDF Graph (it re-exports+--  "Swish.RDF.RDFGraph").+--+--------------------------------------------------------------------------------++module Swish.RDF (module Swish.RDF.RDFGraph) where++import Swish.RDF.RDFGraph++--------------------------------------------------------------------------------+--+--  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/BuiltInDatatypes.hs view
@@ -0,0 +1,74 @@+--------------------------------------------------------------------------------+--  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)++------------------------------------------------------------+--  Declare datatype map+------------------------------------------------------------++allDatatypes :: [RDFDatatype]+allDatatypes =+    [ rdfDatatypeXsdString+    , rdfDatatypeXsdInteger+    ]++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 view
@@ -0,0 +1,52 @@+--------------------------------------------------------------------------------+--  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 view
@@ -0,0 +1,157 @@+--------------------------------------------------------------------------------+--  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
@@ -0,0 +1,546 @@+{-# LANGUAGE MultiParamTypeClasses, OverloadedStrings #-}++--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  ClassRestrictionRule+--  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 implements an inference rule based on a restruction on class+--  membership of one or more values.+--+--------------------------------------------------------------------------------++module Swish.RDF.ClassRestrictionRule+       ( ClassRestriction(..), ClassRestrictionFn+       , makeDatatypeRestriction, makeDatatypeRestrictionFn+       , makeRDFClassRestrictionRules+       , makeRDFDatatypeRestrictionRules+       , falseGraph, falseGraphStr       +       )+       where++import Swish.RDF.RDFGraph+    ( RDFLabel(..)+    , getScopedName+    , RDFGraph+    , getArcs+    , merge+    , toRDFGraph, emptyRDFGraph+    , Arc(..)+    , resRdfType+    , resRdfdMaxCardinality+    )++import Swish.RDF.RDFRuleset (RDFRule, makeRDFGraphFromN3Builder)+import Swish.RDF.RDFDatatype (RDFDatatypeVal, fromRDFLabel, toRDFLabel)++import Swish.RDF.RDFQuery+    ( 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.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.Maybe (fromJust, fromMaybe, mapMaybe)+import Data.List (delete, nub, (\\))++import qualified Data.Text.Lazy.Builder as B++------------------------------------------------------------+--  Class restriction data type+------------------------------------------------------------++-- |Type of function that evaluates missing node values in a+--  restriction from those supplied.+type ClassRestrictionFn = [Maybe RDFLabel] -> Maybe [[RDFLabel]]++-- |Datatype for named class restriction+data ClassRestriction = ClassRestriction+    { crName    :: ScopedName+    , crFunc    :: ClassRestrictionFn+    }++instance Eq ClassRestriction where+    cr1 == cr2  =  crName cr1 == crName cr2++instance Show ClassRestriction where+    show cr = "ClassRestriction:" ++ show (crName cr)++instance LookupEntryClass ClassRestriction ScopedName ClassRestriction+    where+    newEntry (_,fn) = fn+    keyVal cr = (crName cr, cr)++------------------------------------------------------------+--  Instantiate a class restriction from a datatype relation+------------------------------------------------------------++-- |Make a class restriction from a datatype relation.+--+--  This lifts application of the datatype relation to operate+--  on 'RDFLabel' values, which are presumed to contain appropriately+--  datatyped values.+--+makeDatatypeRestriction ::+    RDFDatatypeVal vt -> DatatypeRel vt -> ClassRestriction+makeDatatypeRestriction dtv dtrel = ClassRestriction+    { crName = dtRelName dtrel+    , crFunc = makeDatatypeRestrictionFn dtv (dtRelFunc dtrel)+    }++--  The core logic below is something like @(map toLabels . dtrelfn . map frLabel)@+--  but the extra lifting and catMaybes are needed to get the final result+--  type in the right form.++-- |Make a class restriction function from a datatype relation function.+--+makeDatatypeRestrictionFn ::+    RDFDatatypeVal vt -> DatatypeRelFn vt -> ClassRestrictionFn+makeDatatypeRestrictionFn dtv dtrelfn =+    liftM (mapMaybe toLabels) . dtrelfn . map frLabel+    where+        frLabel Nothing  = Nothing+        frLabel (Just l) = fromRDFLabel dtv l+        toLabels         = mapM toLabel   -- Maybe [RDFLabel]+        toLabel          = toRDFLabel dtv++------------------------------------------------------------+--  Make rules from supplied class restrictions and graph+------------------------------------------------------------++mkPrefix :: Namespace -> B.Builder+mkPrefix = namespaceToBuilder++ruleQuery :: RDFGraph+ruleQuery = makeRDFGraphFromN3Builder $+            mconcat+            [ mkPrefix namespaceRDFD+            , " ?c a rdfd:GeneralRestriction ; "+            , "    rdfd:onProperties ?p ; "     +            , "    rdfd:constraint   ?r . "+            ]+            +--  Placeholder false graph for now.+falseGraph :: RDFGraph+falseGraph = makeRDFGraphFromN3Builder $+             mkPrefix namespaceRDFD `mappend` falseGraphStr++falseGraphStr :: B.Builder+falseGraphStr = "_:a rdfd:false _:b . "++-- |Make a list of class restriction rules given a list of class restriction+--  values and a graph containing one or more class restriction definitions.+--+makeRDFClassRestrictionRules :: [ClassRestriction] -> RDFGraph -> [RDFRule]+makeRDFClassRestrictionRules crs gr =+    mapMaybe constructRule (queryForRules gr)+    where+        queryForRules = rdfQueryFind ruleQuery+        constructRule = makeRestrictionRule1 crs gr++makeRestrictionRule1 ::+    [ClassRestriction] -> RDFGraph -> RDFVarBinding -> Maybe RDFRule+makeRestrictionRule1 crs gr vb =+    makeRestrictionRule2 rn c ps cs+    where+        c  = fromMaybe NoNode $ vbMap vb (Var "c")+        p  = fromMaybe NoNode $ vbMap vb (Var "p")+        r  = fromMaybe NoNode $ vbMap vb (Var "r")+        cs = filter (>0) $ map fromInteger $+             rdfFindPredInt c resRdfdMaxCardinality gr+        ps = rdfFindList gr p+        rn = mapFindMaybe (getScopedName r) (LookupMap crs)++makeRestrictionRule2 ::+    Maybe ClassRestriction -> RDFLabel -> [RDFLabel] -> [Int]+    -> Maybe RDFRule+makeRestrictionRule2 (Just restriction) cls@(Res cname) props cs =+    Just restrictionRule+    where+        restrictionRule = Rule+            { ruleName = cname+              -- fwdApply :: [ex] -> [ex]+            , fwdApply = fwdApplyRestriction restriction cls props cs+              -- bwdApply :: ex -> [[ex]]+            , bwdApply = bwdApplyRestriction restriction cls props cs+            , checkInference = bwdCheckInference restrictionRule+            }+makeRestrictionRule2 _ _ _ _ = Nothing+    -- trace "\nmakeRestrictionRule: missing class restriction"++--  Forward apply class restriction.+fwdApplyRestriction ::+    ClassRestriction -> RDFLabel -> [RDFLabel] -> [Int] -> [RDFGraph]+    -> [RDFGraph]+fwdApplyRestriction restriction cls props cs antgrs =+    maybe [falseGraph] concat newgrs+      where+        -- Instances of the named class in the graph:+        ris = nub $ rdfFindValSubj resRdfType cls antgr+        --  Merge antecedent graphs into one (with bnode renaming):+        --  (Uses 'if' and 'foldl1' to avoid merging in the common case+        --  of just one graph supplied.)+        antgr = if null antgrs then emptyRDFGraph else foldl1 merge antgrs+        --  Apply class restriction to single instance of the restricted class+        newgr :: RDFLabel -> Maybe [RDFGraph]+        newgr ri = fwdApplyRestriction1 restriction ri props cs antgr+        newgrs :: Maybe [[RDFGraph]]+        newgrs = mapM newgr ris++--  Forward apply class restriction to single class instance (ci).+--  Return single set of inferred results, for each combination of+--  property values, or an empty list, or Nothing if the supplied values+--  are inconsistent with the restriction.+fwdApplyRestriction1 ::+    ClassRestriction -> RDFLabel -> [RDFLabel] -> [Int] -> RDFGraph+    -> Maybe [RDFGraph]+fwdApplyRestriction1 restriction ci props cs antgr =+    if grConsistent then Just newgrs else Nothing+    where+        --  Apply restriction to graph+        (grConsistent,_,_,sts) = applyRestriction restriction ci props cs antgr+        --  Select results, eliminate those with unknowns+        nts :: [[RDFLabel]]+        nts = mapMaybe sequence sts+        --  Make new graph from results, including only newly generated arcs+        newarcs = nub [Arc ci p v | vs <- nts, (p,v) <- zip props vs ]+                  \\ getArcs antgr+        newgrs  = if null newarcs then [] else [toRDFGraph newarcs]++--  Backward apply class restriction.+--+--  Returns a list of alternatives, any one of which is sufficient to+--  satisfy the given consequent.+--+bwdApplyRestriction ::+    ClassRestriction -> RDFLabel -> [RDFLabel] -> [Int] -> RDFGraph+    -> [[RDFGraph]]+bwdApplyRestriction restriction cls props cs congr =+    fromMaybe [[falseGraph]] newgrs+    where+        -- Instances of the named class in the graph:+        ris = rdfFindValSubj resRdfType cls congr+        --  Apply class restriction to single instance of the restricted class+        newgr :: RDFLabel -> Maybe [[RDFGraph]]+        newgr ri = bwdApplyRestriction1 restriction cls ri props cs congr+        --  'map newgr ris' is conjunction of disjunctions, where+        --  each disjunction is itself a conjunction of conjunctions.+        --  'sequence' distributes the conjunction over the disjunction,+        --  yielding an equivalent disjunction of conjunctions+        --  map concat flattens the conjunctions of conjuctions+        newgrs :: Maybe [[RDFGraph]]+        newgrs = liftM (map concat . sequence) $ mapM newgr ris++--  Backward apply a class restriction to single class instance (ci).+--  Return one or more sets of antecedent results from which the consequence+--  can be derived in the defined relation, an empty list if the supplied+--  consequence cannot be inferred, or Nothing if the consequence is+--  inconsistent with the restriction.+bwdApplyRestriction1 ::+    ClassRestriction -> RDFLabel -> RDFLabel -> [RDFLabel] -> [Int] -> RDFGraph+    -> Maybe [[RDFGraph]]+bwdApplyRestriction1 restriction cls ci props cs congr =+    if grConsistent then Just grss else Nothing+    where+        --  Apply restriction to graph+        (grConsistent,pvs,cts,_) =+            applyRestriction restriction ci props cs congr+        --  Build list of all full tuples consistent with the values supplied+        fts :: [[RDFLabel]]+        fts = concatMap snd cts+        --  Construct partial tuples from members of fts from which at least+        --  one of the supplied values can be derived+        pts :: [([Maybe RDFLabel],[RDFLabel])]+        pts = concatMap (deriveTuple restriction) fts+        --  Select combinations of members of pts from which all the+        --  supplied values can be derived+        dtss :: [[[Maybe RDFLabel]]]+        dtss = coverSets pvs pts+        --  Filter members of dtss that fully cover the values+        --  obtained from the consequence graph.+        ftss :: [[[Maybe RDFLabel]]]+        ftss = filter (not . (\t -> coversVals deleteMaybe t pvs)) dtss+        --  Make new graphs for all alternatives+        grss :: [[RDFGraph]]+        grss = map ( makeGraphs . newArcs ) ftss+        --  Collect arcs for one alternative+        newArcs dts =+            [ Arc ci p v | mvs <- dts, (p,Just v) <- zip props mvs ]+        --  Make graphs for one alternative+        makeGraphs = map (toRDFGraph . (:[])) . (Arc ci resRdfType cls :)++--  Helper function to select sub-tuples from which some of a set of+--  values can be derived using a class restriction.+--+--  restriction is the restriction being evaluated.+--  ft          is a full tuple of values known to be consistent with+--              the restriction+--+--  The result returned is a list of pairs, whose first member is a partial+--  tuples from which the full tuple supplied can be derived, and the second+--  is the supplied tuple calculated from that input.+--+deriveTuple ::+    ClassRestriction -> [RDFLabel]+    -> [([Maybe RDFLabel],[RDFLabel])]+deriveTuple restriction ft =+    map (tosnd ft) $ minima partCompareListMaybe $ filter derives partials+    where+        partials = mapM (\x -> [Nothing,Just x]) ft+        derives  = ([ft]==) . fromJust . crFunc restriction+        tosnd    = flip (,)++--  Helper function to apply a restriction to selected information from+--  a supplied graph, and returns a tuple containing:+--  (a) an indication of whether the graph is consistent with the+--      restriction+--  (b) a list of values specified in the graph for each property+--  (c) a complete list of tuples that use combinations of values from+--      the graph and are consistent with the restriction.+--      Each member is a pair consisting of some combination of input+--      values, and a list of complete tuple values that can be+--      calculated from those inputs, or an empty list if there is+--      insufficient information.+--  (d) a set of tuples that are consistent with the restriction and use+--      as much information from the graph as possible.  This set is+--      minimal in the sense that they must all correspond to different+--      complete input tuples satisfying the restriction.+--+--  This function factors out logic that is common to forward and+--  backward chaining of a class restriction.+--+--  restriction is the class restriction being applied+--  ci          is the identifier of a graph node to be tested+--  props       is a list of properties of the graph noode whose values+--              are constrained by the class restriction.+--  cs          is a list of max cardinality constraints on the restriction,+--              the minimum of which is used as the cardinality constraint+--              on the restriction.  If the list is null, no cardinality+--              constraint is applied.+--  gr          is the graph from which property values are extracted.+--+applyRestriction ::+    ClassRestriction -> RDFLabel -> [RDFLabel] -> [Int] -> RDFGraph+    -> ( Bool+       , [[RDFLabel]]+       , [([Maybe RDFLabel],[[RDFLabel]])]+       , [[Maybe RDFLabel]]+       )+applyRestriction restriction ci props cs gr =+    (coversVals deleteMaybe sts pvs && cardinalityOK, pvs, cts, sts )+    where+        --  Extract from the antecedent graph all specified values of the+        --  restricted properties (constructs inner list for each property)+        pvs :: [[RDFLabel]]+        pvs = [ rdfFindPredVal ci p gr | p <- props ]+        --  Convert tuple of alternatives to list of alternative tuples+        --  (Each tuple is an inner list)+        pts :: [[Maybe RDFLabel]]+        pts = mapM allJustAndNothing pvs+        --  Try class restriction calculation for each tuple+        --  For each, result may be:+        --    Nothing  (inconsistent)+        --    Just []  (underspecified)+        --    Just [t] (single tuple of values derived from given values)+        --    Just ts  (alternative tuples derived from given values)+        rts :: [Maybe [[RDFLabel]]]+        rts = map (crFunc restriction) pts+        +        --  Extract list of consistent tuples of given values+        cts :: [([Maybe RDFLabel],[[RDFLabel]])]+        cts = mapMaybe tupleConv (zip pts rts)+        +        --  TODO: be more idiomatic?+        tupleConv :: (a, Maybe b) -> Maybe (a,b)+        tupleConv (a, Just b)  = Just (a,b)+        tupleConv _            = Nothing+        +        --  Build list of consistent tuples with maximum information+        --  based on that supplied and available+        -- mts = concatMap mostValues cts+        mts = map mostOneValue cts+        --  Eliminate consistent results subsumed by others.+        --  This results in a mimimal possible set of consistent inputs,+        --  because if any pair could be consistently unified then their+        --  common subsumer would still be in the list, and both would be+        --  thereby eliminated.+        sts :: [[Maybe RDFLabel]]+        sts = maxima partCompareListMaybe mts+        --  Check the cardinality constraint+        cardinalityOK = null cs || length sts <= minimum cs+        +--  Map a non-empty list of values to a list of Just values,+--  preceding each with a Nothing element.+--+--  Nothing corresponds to an unknown value.  This logic is used+--  as part of constructing a list of alternative tuples of known+--  data values (either supplied or calculated from the class+--  restriction).+--+allJustAndNothing :: [a] -> [Maybe a]+allJustAndNothing as = Nothing:map Just as++{-+--  Get maximum information about possible tuple values from a+--  given pair of input tuple, which is known to be consistent with+--  the restriction, and calculated result tuples.  Where the result+--  tuple is not exactly calculated, return the input tuple.+--+--  imvs    tuple of Maybe element values, with Nothing for+--          unspecified values+--  movss   Maybe list of possible fully-specified result tuples,+--          an empty list if no result tuples can be computed+--          based on the input tuple, or Nothing if the input+--          tuple is inconsistent.+--+mostValues :: ([Maybe a],[[a]]) -> [[Maybe a]]+mostValues (imvs,([])) = [imvs]+mostValues (_,movss) = map (map Just) movss+-}++--  Get maximum information about possible tuple values from a+--  given pair of input and possible result tuples, which is+--  known to be consistent with the restriction.  If the result+--  tuple is not exactly calculated, return the input tuple.+--+--  This is a variant of mostValues that returns a single vector.+--  Multiple possible values are considered to be equivalent to+--  Just [], i.e. unknown result.+--+--  imvs    tuple of Maybe element values, with Nothing for+--          unspecified values+--  movss   Maybe list of possible fully-specified result tuples,+--          or an empty list if no result tuples can be computed+--          based on the input tuple.+--+mostOneValue :: ([Maybe a],[[a]]) -> [Maybe a]+mostOneValue (_,[movs]) = map Just movs+mostOneValue (imvs,_)   = imvs++--  Helper function that returns subsets of dts that "cover" the indicated+--  values;  i.e. from which all of the supplied values can be deduced+--  by the enumerated function results.  The minima of all such subsets is+--  returned, as each of these corresponds to some minimum information needed+--  to deduce all of the given values.+--+--  pvs     is a list of lists of values to be covered.  The inner list+--          contains multiple values for each member of a tuple.+--  dts     is an enumerated list of function values from some subset of+--          the tuple space to complete tuples.  Each member is a pair+--          containing the partial tuple (using Nothing for unspecified+--          values) and the full tuple calculated from it.+--+--  The return value is a disjunction of conjunctions of partial tuples+--  that cover the indicated parameter values.+--+--  NOTE:+--  The result minimization is not perfect (cf. test2 below), but I believe+--  it is adequate for the practical situations I envisage, and in any+--  case will not result in incorrect values.  It's significance is for+--  search-tree pruning.  A perfect minimization might be achieved by+--  using a more subtle partial ordering that takes account of both subsets+--  and the partial ordering of set members in place of 'partCompareListSubset'.+--+coverSets  :: (Eq a) => [[a]] -> [([Maybe a],[a])] -> [[[Maybe a]]]+coverSets pvs dts =+    minima partCompareListSubset $ map (map fst) ctss+    where+        ctss = filter coverspvs $ powerSet cts+        cts  = minima (partComparePair partCompareListMaybe partCompareEq) dts+        coverspvs cs = coversVals delete (map snd cs) pvs++--  Does a supplied list of tuples cover a list of possible alternative+--  values for each tuple member?+--+coversVals :: (a->[b]->[b]) -> [[a]] -> [[b]] -> Bool+coversVals dropVal ts vss =+    -- all null (foldr dropUsed vss ts)+    any (all null) (scanr dropUsed vss ts)+    where+        --  Remove single tuple values from the list of supplied values:+        dropUsed []       []     = []+        dropUsed (a:as) (bs:bss) = dropVal a bs : dropUsed as bss+        dropUsed _ _ = error "coversVals.dropUsed: list length mismatch"++{-+--  Does a supplied list of possible alternative values for each+--  element of a tuple cover every tuple in a supplied list?+--+--  (See module spike-coverVals.hs for test cases)+--+coversAll :: ([a]->b->Bool) -> [[a]] -> [[b]] -> Bool+coversAll matchElem vss ts = all (invss vss) ts+    where+        --  Test if a given tuple is covered by vss+        invss vss t = and $ zipWith matchElem vss t++--  Test if the value in a Maybe is contained in a list.+maybeElem :: (Eq a) => Maybe a -> [a] -> Bool+maybeElem Nothing  = const True+maybeElem (Just t) = elem t+-}++-- |Delete a Maybe value from a list+deleteMaybe :: (Eq a) => Maybe a -> [a] -> [a]+deleteMaybe Nothing  as = as+deleteMaybe (Just a) as = delete a as++------------------------------------------------------------+--  Make restriction rules from supplied datatype and graph+------------------------------------------------------------++makeRDFDatatypeRestrictionRules :: RDFDatatypeVal vt -> RDFGraph -> [RDFRule]+makeRDFDatatypeRestrictionRules dtval gr =+    makeRDFClassRestrictionRules dcrs gr+    where+        dcrs = map (makeDatatypeRestriction dtval) (tvalRel dtval)++--------------------------------------------------------------------------------+--+--  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/Datatype.hs view
@@ -0,0 +1,1064 @@+{-# 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+--  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 view
@@ -0,0 +1,248 @@+{-# 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 view
@@ -0,0 +1,622 @@+{-# 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 gs =+    map (sigCalc . arcToTriple) gs+    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 view
@@ -0,0 +1,166 @@+{-# 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 view
@@ -0,0 +1,570 @@+--------------------------------------------------------------------------------+--  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/MapXsdInteger.hs view
@@ -0,0 +1,69 @@+{-# 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 view
@@ -0,0 +1,941 @@+{-# 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 view
@@ -0,0 +1,1145 @@+{-# 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 view
@@ -0,0 +1,247 @@+{-# 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 v = go [] v+  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 view
@@ -0,0 +1,427 @@+--------------------------------------------------------------------------------+--  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/Proof.hs view
@@ -0,0 +1,296 @@+--------------------------------------------------------------------------------+--  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.RDF.Proof+    ( Proof(..), Step(..)+    , checkProof, explainProof, checkStep, showProof, showsProof, showsFormula )+where++import Swish.RDF.Ruleset (Ruleset(..))++import Swish.RDF.Rule+    ( Expression(..), Formula(..), Rule(..)+    , showsFormula, showsFormulae )++import Swish.Utils.ShowM (ShowM(..))++import Swish.Utils.ListHelpers (subset)++import Data.List (union, intersect, intercalate, foldl')+import Data.Maybe (catMaybes, isNothing)++------------------------------------------------------------+--  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 :: +  (ShowM 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 :: +  (ShowM 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 :: (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++-- |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++")")+    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/RDF/RDFDatatype.hs view
@@ -0,0 +1,202 @@+--------------------------------------------------------------------------------+--  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/RDFDatatypeXsdInteger.hs view
@@ -0,0 +1,539 @@+{-# 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 view
@@ -0,0 +1,333 @@+{-# 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 view
@@ -0,0 +1,1396 @@+{-# 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 emoty 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 view
@@ -0,0 +1,57 @@+{-# 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 view
@@ -0,0 +1,353 @@+{-# 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 view
@@ -0,0 +1,380 @@+{-# 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 view
@@ -0,0 +1,848 @@+{-# 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 view
@@ -0,0 +1,636 @@+--------------------------------------------------------------------------------+--  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 view
@@ -0,0 +1,546 @@+{-# 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 view
@@ -0,0 +1,187 @@+{-# 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 view
@@ -0,0 +1,247 @@+{-# 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
@@ -0,0 +1,175 @@+{-# LANGUAGE MultiParamTypeClasses #-}+--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  Ruleset+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 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.RDF.Ruleset+    ( Ruleset(..), RulesetMap+    , makeRuleset, getRulesetNamespace, getRulesetAxioms, getRulesetRules+    , getRulesetAxiom, getRulesetRule+    , getContextAxiom, getMaybeContextAxiom+    , getContextRule,  getMaybeContextRule+    )+where++import Swish.Utils.Namespace (Namespace, ScopedName)+import Swish.RDF.Rule (Formula(..), Rule(..))++import Swish.Utils.LookupMap+    ( LookupEntryClass(..), LookupMap(..)+    , mapFindMaybe+    )++{-+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.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++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 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/SwishCommands.hs view
@@ -0,0 +1,422 @@+{-# 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 view
@@ -0,0 +1,291 @@+--------------------------------------------------------------------------------+--  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 view
@@ -0,0 +1,270 @@+{-# 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 view
@@ -0,0 +1,1512 @@+{-# 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 view
@@ -0,0 +1,885 @@+{-# 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 (see [1])+--  for an RDFGraph value.+--+-- 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/>)+--+--------------------------------------------------------------------------------++{-+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+    )+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++{-+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++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 view
@@ -0,0 +1,1062 @@+{-# 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
@@ -0,0 +1,540 @@+{-# LANGUAGE MultiParamTypeClasses, TypeSynonymInstances, OverloadedStrings #-}+{-# LANGUAGE FlexibleInstances #-} -- needed for ghc 7.12++--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  VarBinding+--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke+--  License     :  GPL V2+--+--  Maintainer  :  Douglas Burke+--  Stability   :  experimental+--  Portability :  MultiParamTypeClasses, TypeSynonymInstances, OverloadedStrings, FlexibleInstances+--+--  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.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+    )+where++import Swish.Utils.LookupMap+    ( LookupEntryClass(..) +    , makeLookupMap, mapFindMaybe+    )++import Swish.Utils.Namespace (ScopedName, getScopeLocal)+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++------------------------------------------------------------+--  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 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++-- |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 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/Vocabulary.hs view
@@ -0,0 +1,233 @@+{-# LANGUAGE OverloadedStrings #-}++--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  Vocabulary+--  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 commonly used vocabulary terms,+--  using the 'Namespace' and 'ScopedName' data types. Additional vocabularies+--  are available in the set of @Swish.RDF.Vocabulary.*@ modules, parts of+--  which are re-exported by this module+--+--------------------------------------------------------------------------------++module Swish.RDF.Vocabulary+    ( +      -- * Namespaces+      +      namespaceRDFD+    , namespaceXsdType+    , namespaceMATH+    , namespaceLOG+    , namespaceDAML+    , namespaceDefault+    , namespaceSwish +    , namespaceLang+    -- ** RDF rules                                     +    -- | The namespaces refer to RDF rules and axioms.                                     +    , scopeRDF+    , scopeRDFS+    , scopeRDFD+    +    -- * Miscellaneous routines+    , langName, langTag, isLang+    , swishName+      +    -- * Miscellaneous     +    , rdfdGeneralRestriction+    , rdfdOnProperties, rdfdConstraint, rdfdMaxCardinality+    , logImplies+    , defaultBase+      +    -- * Re-exported modules  +    , module Swish.RDF.Vocabulary.RDF+    , module Swish.RDF.Vocabulary.OWL+    , module Swish.RDF.Vocabulary.XSD  +    )+where++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.Monoid (mappend, mconcat)+import Data.Maybe (fromMaybe)+import Network.URI (URI, parseURI)++import qualified Data.Text as T++------------------------------------------------------------+--  Define some common namespace values+------------------------------------------------------------++toNS :: T.Text -> T.Text -> Namespace+toNS p utxt = +  let ustr = T.unpack utxt+      uri = fromMaybe (error ("Unable to convert " ++ ustr ++ " to a URI")) $+            parseURI ustr+  in makeNamespace (Just p) uri++toNSU :: T.Text -> URI -> Namespace+toNSU p = makeNamespace (Just p)++-- | Create a namespace for the datatype family schema used by Swish.+namespaceXsdType ::+  T.Text  -- ^ lbl+  -> 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, "#"])++-- | Maps @rdfd@ to @http:\/\/id.ninebynine.org\/2003\/rdfext\/rdfd#@.+namespaceRDFD :: Namespace+namespaceRDFD    = toNSU "rdfd"   namespaceRDFDURI++-- | Maps @math@ to <http://www.w3.org/2000/10/swap/math#>.+namespaceMATH :: Namespace+namespaceMATH    = toNS "math"    "http://www.w3.org/2000/10/swap/math#"++-- | Maps @log@ to <http://www.w3.org/2000/10/swap/log#>.+namespaceLOG :: Namespace+namespaceLOG     = toNSU "log"    namespaceLOGURI++-- | Maps @daml@ to <http://www.daml.org/2000/10/daml-ont#>.+namespaceDAML :: Namespace+namespaceDAML    = toNS "daml"    "http://www.daml.org/2000/10/daml-ont#"++-- | Maps @swish@ to @http:\/\/id.ninebynine.org\/2003\/Swish\/@.+namespaceSwish :: Namespace+namespaceSwish   = toNSU "swish"  namespaceSwishURI++-- | Maps @default@ to @http:\/\/id.ninebynine.org\/default\/@.+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+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 = makeNSScopedName namespaceSwish++-----------------------------------------------------------+--  Language tags+------------------------------------------------------------+--+--  Note:  simple language tag URIs may be abbreviated as lang:tag,+--  but if the tag contains a hyphen, this would not be valid QName+--  form in Notation3, even though it is a valid QName component.+--  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++-- | 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++-- | Is the scoped name in the `namespaceLang` namespace?+isLang :: ScopedName -> Bool+isLang sname = getScopeNamespace sname == namespaceLang++------------------------------------------------------------+--  Define namespaces for RDF rules, axioms, etc+------------------------------------------------------------++-- | Maps @rs_rdf@ to @http:\/\/id.ninebynine.org\/2003\/Ruleset\/rdf#@.+scopeRDF :: Namespace+scopeRDF  = toNS "rs_rdf"   "http://id.ninebynine.org/2003/Ruleset/rdf#"++-- | Maps @rs_rdfs@ to @http:\/\/id.ninebynine.org\/2003\/Ruleset\/rdfs#@.+scopeRDFS :: Namespace+scopeRDFS = toNS "rs_rdfs"  "http://id.ninebynine.org/2003/Ruleset/rdfs#"++-- | Maps @rs_rdfd@ to @http:\/\/id.ninebynine.org\/2003\/Ruleset\/rdfd#@.+scopeRDFD :: Namespace+scopeRDFD = toNS "rs_rdfd"  "http://id.ninebynine.org/2003/Ruleset/rdfd#"++------------------------------------------------------------+--  Define some common vocabulary terms+------------------------------------------------------------++toRDFD :: T.Text -> ScopedName+toRDFD = makeNSScopedName namespaceRDFD++-- | @rdfd:GeneralRestriction@.+rdfdGeneralRestriction :: ScopedName+rdfdGeneralRestriction = toRDFD "GeneralRestriction"++-- | @rdfd:onProperties@.+rdfdOnProperties :: ScopedName+rdfdOnProperties = toRDFD "onProperties"++-- | @rdfd:constraint@.+rdfdConstraint :: ScopedName+rdfdConstraint = toRDFD "constraint"++-- | @rdfd:maxCardinality@.+rdfdMaxCardinality :: ScopedName+rdfdMaxCardinality = toRDFD "maxCardinality"++-- | @log:implies@.+logImplies  :: ScopedName+logImplies  = makeNSScopedName namespaceLOG "implies"++-- | @default:base@.+defaultBase :: ScopedName+defaultBase = makeNSScopedName namespaceDefault "base"++--------------------------------------------------------------------------------+--+--  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/Vocabulary/DublinCore.hs view
@@ -0,0 +1,770 @@+{-# LANGUAGE OverloadedStrings #-}++--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  Swish.RDF.Vocabulary.DublinCore+--  Copyright   :  (c) 2011 Douglas Burke+--  License     :  GPL V2+--+--  Maintainer  :  Douglas Burke+--  Stability   :  experimental+--  Portability :  OverloadedStrings+--+--  This module defines some commonly used vocabulary terms from the Dublin Core+--  vocabularies (<http://dublincore.org/documents/dcmi-terms/>).+--+--------------------------------------------------------------------------------++module Swish.RDF.Vocabulary.DublinCore+    ( +      namespaceDCTERMS+    , namespaceDCELEM+    , namespaceDCAM+    , namespaceDCTYPE+        +    -- * Classes+    -- | See the \"Classes\" section at <http://dublincore.org/documents/dcmi-terms/#H6>.+    , dctAgent+    , dctAgentClass +    , dctBibliographicResource +    , dctFileFormat +    , dctFrequency +    , dctJurisdiction +    , dctLicenseDocument +    , dctLinguisticSystem +    , dctLocation +    , dctLocationPeriodOrJurisdiction +    , dctMediaType +    , dctMediaTypeOrExtent +    , dctMethodOfAccrual +    , dctMethodOfInstruction +    , dctPeriodOfTime +    , dctPhysicalMedium +    , dctPhysicalResource +    , dctPolicy +    , dctProvenanceStatement +    , dctRightsStatement +    , dctSizeOrDuration +    , dctStandard+      +    -- * Properties  +    -- | See the \"Properties\" section at <http://dublincore.org/documents/dcmi-terms/#H2>.+    , dctabstract +    , dctaccessRights +    , dctaccrualMethod +    , dctaccrualPeriodicity +    , dctaccrualPolicy +    , dctalternative +    , dctaudience +    , dctavailable +    , dctbibliographicCitation +    , dctconformsTo +    , dctcontributor +    , dctcoverage +    , dctcreated +    , dctcreator +    , dctdate +    , dctdateAccepted +    , dctdateCopyrighted +    , dctdateSubmitted +    , dctdescription +    , dcteducationLevel +    , dctextent +    , dctformat +    , dcthasFormat +    , dcthasPart +    , dcthasVersion +    , dctidentifier +    , dctinstructionalMethod +    , dctisFormatOf +    , dctisPartOf +    , dctisReferencedBy +    , dctisReplacedBy +    , dctisRequiredBy +    , dctissued +    , dctisVersionOf +    , dctlanguage +    , dctlicense +    , dctmediator +    , dctmedium +    , dctmodified +    , dctprovenance +    , dctpublisher +    , dctreferences +    , dctrelation +    , dctreplaces +    , dctrequires +    , dctrights +    , dctrightsHolder +    , dctsource +    , dctspatial +    , dctsubject +    , dcttableOfContents +    , dcttemporal +    , dcttitle +    , dcttype +    , dctvalid+      +    -- * Legacy Properties +    --  +    -- | The following properties are from the legacy /elements/ vocabulary +    -- (@http:\/\/purl.org\/dc\/elements\/1.1\/contributor\/@). See+    -- <http://dublincore.org/documents/dcmi-terms/#H3>.+      +    , dcelemcontributor+    , dcelemcoverage+    , dcelemcreator+    , dcelemdate+    , dcelemdescription+    , dcelemformat+    , dcelemidentifier+    , dcelemlanguage+    , dcelempublisher+    , dcelemrelation+    , dcelemrights+    , dcelemsource+    , dcelemsubject+    , dcelemtitle+    , dcelemtype+      +    -- * Encoding+    -- | See the \"Vocabulary Encoding Schemes\" section at <http://dublincore.org/documents/dcmi-terms/#H4>.+      +    , dctLCSH+    , dctMESH+    , dctDDC+    , dctLCC+    , dctUDC+    , dctDCMIType+    , dctIMT+    , dctTGN+    , dctNLM++    -- * Datatypes+    -- | See the \"Syntax Encoding Schemes\" section at <http://dublincore.org/documents/dcmi-terms/#H5>.++    , dctBox+    , dctISO3166+    , dctISO639_2+    , dctISO639_3+    , dctPeriod+    , dctPoint+    , dctRFC1766+    , dctRFC3066+    , dctRFC4646+    , dctRFC5646+    , dctURI+    , dctW3CDTF+      +    -- * Types+    -- | See the \"DCMI Type Vocabulary\" section at <http://dublincore.org/documents/dcmi-terms/#H7>.+    +    , dctypeCollection+    , dctypeDataset+    , dctypeEvent+    , dctypeImage+    , dctypeInteractiveResource+    , dctypeService+    , dctypeSoftware+    , dctypeSound+    , dctypeText+    , dctypePhysicalObject+    , dctypeStillImage+    , dctypeMovingImage+      +    -- * DCMI Abstract Model   +    --+    -- | Terms from the DCMI Abstract Model (<http://dublincore.org/documents/dcmi-terms/#H8>).          ++    , dcammemberOf+    , dcamVocabularyEncodingScheme+      +    ) where++import Swish.Utils.Namespace (Namespace, makeNamespace, ScopedName, makeNSScopedName)++import Data.Maybe (fromMaybe)+import Network.URI (parseURI)++import qualified Data.Text as T++------------------------------------------------------------+--  Namespace+------------------------------------------------------------++toNS :: T.Text -> String -> Namespace+toNS p = makeNamespace (Just p) . fromMaybe (error "Internal error processing DC URI") . parseURI++-- | Maps @dcterms@ to <http://purl.org/dc/terms/>.+namespaceDCTERMS :: Namespace+namespaceDCTERMS = toNS "dcterms" "http://purl.org/dc/terms/"++-- | Maps @dcelem@ to the legacy namespace <http://purl.org/dc/elements/1.1/>.+namespaceDCELEM :: Namespace+namespaceDCELEM = toNS "dcelem" "http://purl.org/dc/elements/1.1/"++-- | Maps @dcam@ to <http://purl.org/dc/dcam/>.+namespaceDCAM :: Namespace+namespaceDCAM = toNS "dcam" "http://purl.org/dc/dcam/"++-- | Maps @dctype@ to <http://purl.org/dc/dcmitype/>.+namespaceDCTYPE :: Namespace+namespaceDCTYPE = toNS "dctype" "http://purl.org/dc/dcmitype/"++------------------------------------------------------------+--  Terms+------------------------------------------------------------++toDCT :: T.Text -> 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++-- | @dcterms:Agent@ from <http://dublincore.org/documents/dcmi-terms/#classes-Agent>.+dctAgent :: ScopedName+dctAgent = toDCT "Agent"++-- | @dcterms:AgentClass@ from <http://dublincore.org/documents/dcmi-terms/#classes-AgentClass>.+dctAgentClass :: ScopedName+dctAgentClass = toDCT "AgentClass"++-- | @dcterms:BibliographicResource@ from <http://dublincore.org/documents/dcmi-terms/#classes-BibliographicResource>.+dctBibliographicResource :: ScopedName+dctBibliographicResource = toDCT "BibliographicResource"++-- | @dcterms:FileFormat@ from <http://dublincore.org/documents/dcmi-terms/#classes-FileFormat>.+dctFileFormat :: ScopedName+dctFileFormat = toDCT "FileFormat"++-- | @dcterms:Frequency@ from <http://dublincore.org/documents/dcmi-terms/#classes-Frequency>.+dctFrequency :: ScopedName+dctFrequency = toDCT "Frequency"++-- | @dcterms:Jurisdiction@ from <http://dublincore.org/documents/dcmi-terms/#classes-Jurisdiction>.+dctJurisdiction :: ScopedName+dctJurisdiction = toDCT "Jurisdiction"++-- | @dcterms:LicenseDocument@ from <http://dublincore.org/documents/dcmi-terms/#classes-LicenseDocument>.+dctLicenseDocument :: ScopedName+dctLicenseDocument = toDCT "LicenseDocument"++-- | @dcterms:LinguisticSystem@ from <http://dublincore.org/documents/dcmi-terms/#classes-LinguisticSystem>.+dctLinguisticSystem :: ScopedName+dctLinguisticSystem = toDCT "LinguisticSystem"++-- | @dcterms:Location@ from <http://dublincore.org/documents/dcmi-terms/#classes-Location>.+dctLocation :: ScopedName+dctLocation = toDCT "Location"++-- | @dcterms:LocationPeriodOrJurisdiction@ from <http://dublincore.org/documents/dcmi-terms/#classes-LocationPeriodOrJurisdiction>.+dctLocationPeriodOrJurisdiction :: ScopedName+dctLocationPeriodOrJurisdiction = toDCT "LocationPeriodOrJurisdiction"++-- | @dcterms:MediaType@ from <http://dublincore.org/documents/dcmi-terms/#classes-MediaType>.+dctMediaType :: ScopedName+dctMediaType = toDCT "MediaType"++-- | @dcterms:MediaTypeOrExtent@ from <http://dublincore.org/documents/dcmi-terms/#classes-MediaTypeOrExtent>.+dctMediaTypeOrExtent :: ScopedName+dctMediaTypeOrExtent = toDCT "MediaTypeOrExtent"++-- | @dcterms:MethodOfAccrual@ from <http://dublincore.org/documents/dcmi-terms/#classes-MethodOfAccrual>.+dctMethodOfAccrual :: ScopedName+dctMethodOfAccrual = toDCT "MethodOfAccrual"++-- | @dcterms:MethodOfInstruction@ from <http://dublincore.org/documents/dcmi-terms/#classes-MethodOfInstruction>.+dctMethodOfInstruction :: ScopedName+dctMethodOfInstruction = toDCT "MethodOfInstruction"++-- | @dcterms:PeriodOfTime@ from <http://dublincore.org/documents/dcmi-terms/#classes-PeriodOfTime>.+dctPeriodOfTime :: ScopedName+dctPeriodOfTime = toDCT "PeriodOfTime"++-- | @dcterms:PhysicalMedium@ from <http://dublincore.org/documents/dcmi-terms/#classes-PhysicalMedium>.+dctPhysicalMedium :: ScopedName+dctPhysicalMedium = toDCT "PhysicalMedium"++-- | @dcterms:PhysicalResource@ from <http://dublincore.org/documents/dcmi-terms/#classes-PhysicalResource>.+dctPhysicalResource :: ScopedName+dctPhysicalResource = toDCT "PhysicalResource"++-- | @dcterms:Policy@ from <http://dublincore.org/documents/dcmi-terms/#classes-Policy>.+dctPolicy :: ScopedName+dctPolicy = toDCT "Policy"++-- | @dcterms:ProvenanceStatement@ from <http://dublincore.org/documents/dcmi-terms/#classes-ProvenanceStatement>.+dctProvenanceStatement :: ScopedName+dctProvenanceStatement = toDCT "ProvenanceStatement"++-- | @dcterms:RightsStatement@ from <http://dublincore.org/documents/dcmi-terms/#classes-RightsStatement>.+dctRightsStatement :: ScopedName+dctRightsStatement = toDCT "RightsStatement"++-- | @dcterms:SizeOrDuration@ from <http://dublincore.org/documents/dcmi-terms/#classes-SizeOrDuration>.+dctSizeOrDuration :: ScopedName+dctSizeOrDuration = toDCT "SizeOrDuration"++-- | @dcterms:Standard@ from <http://dublincore.org/documents/dcmi-terms/#classes-Standard>.+dctStandard :: ScopedName+dctStandard = toDCT "Standard"++-- Properties++-- | @dcterms:abstract@ from <http://dublincore.org/documents/dcmi-terms/#terms-abstract>. +dctabstract :: ScopedName+dctabstract = toDCT "abstract"++-- | @dcterms:accessRights@ from <http://dublincore.org/documents/dcmi-terms/#terms-accessRights>. +dctaccessRights :: ScopedName+dctaccessRights = toDCT "accessRights"++-- | @dcterms:accrualMethod@ from <http://dublincore.org/documents/dcmi-terms/#terms-accrualMethod>. +dctaccrualMethod :: ScopedName+dctaccrualMethod = toDCT "accrualMethod"++-- | @dcterms:accrualPeriodicity@ from <http://dublincore.org/documents/dcmi-terms/#terms-accrualPeriodicity>. +dctaccrualPeriodicity :: ScopedName+dctaccrualPeriodicity = toDCT "accrualPeriodicity"++-- | @dcterms:accrualPolicy@ from <http://dublincore.org/documents/dcmi-terms/#terms-accrualPolicy>. +dctaccrualPolicy :: ScopedName+dctaccrualPolicy = toDCT "accrualPolicy"++-- | @dcterms:alternative@ from <http://dublincore.org/documents/dcmi-terms/#terms-alternative>. +dctalternative :: ScopedName+dctalternative = toDCT "alternative"++-- | @dcterms:audience@ from <http://dublincore.org/documents/dcmi-terms/#terms-audience>. +dctaudience :: ScopedName+dctaudience = toDCT "audience"++-- | @dcterms:available@ from <http://dublincore.org/documents/dcmi-terms/#terms-available>. +dctavailable :: ScopedName+dctavailable = toDCT "available"++-- | @dcterms:bibliographicCitation@ from <http://dublincore.org/documents/dcmi-terms/#terms-bibliographicCitation>. +dctbibliographicCitation :: ScopedName+dctbibliographicCitation = toDCT "bibliographicCitation"++-- | @dcterms:conformsTo@ from <http://dublincore.org/documents/dcmi-terms/#terms-conformsTo>. +dctconformsTo :: ScopedName+dctconformsTo = toDCT "conformsTo"++-- | @dcterms:contributor@ from <http://dublincore.org/documents/dcmi-terms/#terms-contributor>. +dctcontributor :: ScopedName+dctcontributor = toDCT "contributor"++-- | @dcterms:coverage@ from <http://dublincore.org/documents/dcmi-terms/#terms-coverage>. +dctcoverage :: ScopedName+dctcoverage = toDCT "coverage"++-- | @dcterms:created@ from <http://dublincore.org/documents/dcmi-terms/#terms-created>. +dctcreated :: ScopedName+dctcreated = toDCT "created"++-- | @dcterms:creator@ from <http://dublincore.org/documents/dcmi-terms/#terms-creator>. +dctcreator :: ScopedName+dctcreator = toDCT "creator"++-- | @dcterms:date@ from <http://dublincore.org/documents/dcmi-terms/#terms-date>. +dctdate :: ScopedName+dctdate = toDCT "date"++-- | @dcterms:dateAccepted@ from <http://dublincore.org/documents/dcmi-terms/#terms-dateAccepted>. +dctdateAccepted :: ScopedName+dctdateAccepted = toDCT "dateAccepted"++-- | @dcterms:dateCopyrighted@ from <http://dublincore.org/documents/dcmi-terms/#terms-dateCopyrighted>. +dctdateCopyrighted :: ScopedName+dctdateCopyrighted = toDCT "dateCopyrighted"++-- | @dcterms:dateSubmitted@ from <http://dublincore.org/documents/dcmi-terms/#terms-dateSubmitted>. +dctdateSubmitted :: ScopedName+dctdateSubmitted = toDCT "dateSubmitted"++-- | @dcterms:description@ from <http://dublincore.org/documents/dcmi-terms/#terms-description>. +dctdescription :: ScopedName+dctdescription = toDCT "description"++-- | @dcterms:educationLevel@ from <http://dublincore.org/documents/dcmi-terms/#terms-educationLevel>. +dcteducationLevel :: ScopedName+dcteducationLevel = toDCT "educationLevel"++-- | @dcterms:extent@ from <http://dublincore.org/documents/dcmi-terms/#terms-extent>. +dctextent :: ScopedName+dctextent = toDCT "extent"++-- | @dcterms:format@ from <http://dublincore.org/documents/dcmi-terms/#terms-format>. +dctformat :: ScopedName+dctformat = toDCT "format"++-- | @dcterms:hasFormat@ from <http://dublincore.org/documents/dcmi-terms/#terms-hasFormat>. +dcthasFormat :: ScopedName+dcthasFormat = toDCT "hasFormat"++-- | @dcterms:hasPart@ from <http://dublincore.org/documents/dcmi-terms/#terms-hasPart>. +dcthasPart :: ScopedName+dcthasPart = toDCT "hasPart"++-- | @dcterms:hasVersion@ from <http://dublincore.org/documents/dcmi-terms/#terms-hasVersion>. +dcthasVersion :: ScopedName+dcthasVersion = toDCT "hasVersion"++-- | @dcterms:identifier@ from <http://dublincore.org/documents/dcmi-terms/#terms-identifier>. +dctidentifier :: ScopedName+dctidentifier = toDCT "identifier"++-- | @dcterms:instructionalMethod@ from <http://dublincore.org/documents/dcmi-terms/#terms-instructionalMethod>. +dctinstructionalMethod :: ScopedName+dctinstructionalMethod = toDCT "instructionalMethod"++-- | @dcterms:isFormatOf@ from <http://dublincore.org/documents/dcmi-terms/#terms-isFormatOf>. +dctisFormatOf :: ScopedName+dctisFormatOf = toDCT "isFormatOf"++-- | @dcterms:isPartOf@ from <http://dublincore.org/documents/dcmi-terms/#terms-isPartOf>. +dctisPartOf :: ScopedName+dctisPartOf = toDCT "isPartOf"++-- | @dcterms:isReferencedBy@ from <http://dublincore.org/documents/dcmi-terms/#terms-isReferencedBy>. +dctisReferencedBy :: ScopedName+dctisReferencedBy = toDCT "isReferencedBy"++-- | @dcterms:isReplacedBy@ from <http://dublincore.org/documents/dcmi-terms/#terms-isReplacedBy>. +dctisReplacedBy :: ScopedName+dctisReplacedBy = toDCT "isReplacedBy"++-- | @dcterms:isRequiredBy@ from <http://dublincore.org/documents/dcmi-terms/#terms-isRequiredBy>. +dctisRequiredBy :: ScopedName+dctisRequiredBy = toDCT "isRequiredBy"++-- | @dcterms:issued@ from <http://dublincore.org/documents/dcmi-terms/#terms-issued>. +dctissued :: ScopedName+dctissued = toDCT "issued"++-- | @dcterms:isVersionOf@ from <http://dublincore.org/documents/dcmi-terms/#terms-isVersionOf>. +dctisVersionOf :: ScopedName+dctisVersionOf = toDCT "isVersionOf"++-- | @dcterms:language@ from <http://dublincore.org/documents/dcmi-terms/#terms-language>. +dctlanguage :: ScopedName+dctlanguage = toDCT "language"++-- | @dcterms:license@ from <http://dublincore.org/documents/dcmi-terms/#terms-license>. +dctlicense :: ScopedName+dctlicense = toDCT "license"++-- | @dcterms:mediator@ from <http://dublincore.org/documents/dcmi-terms/#terms-mediator>. +dctmediator :: ScopedName+dctmediator = toDCT "mediator"++-- | @dcterms:medium@ from <http://dublincore.org/documents/dcmi-terms/#terms-medium>. +dctmedium :: ScopedName+dctmedium = toDCT "medium"++-- | @dcterms:modified@ from <http://dublincore.org/documents/dcmi-terms/#terms-modified>. +dctmodified :: ScopedName+dctmodified = toDCT "modified"++-- | @dcterms:provenance@ from <http://dublincore.org/documents/dcmi-terms/#terms-provenance>. +dctprovenance :: ScopedName+dctprovenance = toDCT "provenance"++-- | @dcterms:publisher@ from <http://dublincore.org/documents/dcmi-terms/#terms-publisher>. +dctpublisher :: ScopedName+dctpublisher = toDCT "publisher"++-- | @dcterms:references@ from <http://dublincore.org/documents/dcmi-terms/#terms-references>. +dctreferences :: ScopedName+dctreferences = toDCT "references"++-- | @dcterms:relation@ from <http://dublincore.org/documents/dcmi-terms/#terms-relation>. +dctrelation :: ScopedName+dctrelation = toDCT "relation"++-- | @dcterms:replaces@ from <http://dublincore.org/documents/dcmi-terms/#terms-replaces>. +dctreplaces :: ScopedName+dctreplaces = toDCT "replaces"++-- | @dcterms:requires@ from <http://dublincore.org/documents/dcmi-terms/#terms-requires>. +dctrequires :: ScopedName+dctrequires = toDCT "requires"++-- | @dcterms:rights@ from <http://dublincore.org/documents/dcmi-terms/#terms-rights>. +dctrights :: ScopedName+dctrights = toDCT "rights"++-- | @dcterms:rightsHolder@ from <http://dublincore.org/documents/dcmi-terms/#terms-rightsHolder>. +dctrightsHolder :: ScopedName+dctrightsHolder = toDCT "rightsHolder"++-- | @dcterms:source@ from <http://dublincore.org/documents/dcmi-terms/#terms-source>. +dctsource :: ScopedName+dctsource = toDCT "source"++-- | @dcterms:spatial@ from <http://dublincore.org/documents/dcmi-terms/#terms-spatial>. +dctspatial :: ScopedName+dctspatial = toDCT "spatial"++-- | @dcterms:subject@ from <http://dublincore.org/documents/dcmi-terms/#terms-subject>. +dctsubject :: ScopedName+dctsubject = toDCT "subject"++-- | @dcterms:tableOfContents@ from <http://dublincore.org/documents/dcmi-terms/#terms-tableOfContents>. +dcttableOfContents :: ScopedName+dcttableOfContents = toDCT "tableOfContents"++-- | @dcterms:temporal@ from <http://dublincore.org/documents/dcmi-terms/#terms-temporal>. +dcttemporal :: ScopedName+dcttemporal = toDCT "temporal"++-- | @dcterms:title@ from <http://dublincore.org/documents/dcmi-terms/#terms-title>. +dcttitle :: ScopedName+dcttitle = toDCT "title"++-- | @dcterms:type@ from <http://dublincore.org/documents/dcmi-terms/#terms-type>. +dcttype :: ScopedName+dcttype = toDCT "type"++-- | @dcterms:valid@ from <http://dublincore.org/documents/dcmi-terms/#terms-valid>. +dctvalid :: ScopedName+dctvalid = toDCT "valid"++-- legacy elements vocabulary: properties++-- | @dcelem:contributor@ from <http://dublincore.org/documents/dcmi-terms/#elements-contributor>.+dcelemcontributor :: ScopedName+dcelemcontributor = toDCE "contributor"++-- | @dcelem:coverage@ from <http://dublincore.org/documents/dcmi-terms/#elements-coverage>.+dcelemcoverage :: ScopedName+dcelemcoverage = toDCE "coverage"++-- | @dcelem:creator@ from <http://dublincore.org/documents/dcmi-terms/#elements-creator>.+dcelemcreator :: ScopedName+dcelemcreator = toDCE "creator"++-- | @dcelem:date@ from <http://dublincore.org/documents/dcmi-terms/#elements-date>.+dcelemdate :: ScopedName+dcelemdate = toDCE "date"++-- | @dcelem:description@ from <http://dublincore.org/documents/dcmi-terms/#elements-description>.+dcelemdescription :: ScopedName+dcelemdescription = toDCE "description"++-- | @dcelem:format@ from <http://dublincore.org/documents/dcmi-terms/#elements-format>.+dcelemformat :: ScopedName+dcelemformat = toDCE "format"++-- | @dcelem:identifier@ from <http://dublincore.org/documents/dcmi-terms/#elements-identifier>.+dcelemidentifier :: ScopedName+dcelemidentifier = toDCE "identifier"++-- | @dcelem:language@ from <http://dublincore.org/documents/dcmi-terms/#elements-language>.+dcelemlanguage :: ScopedName+dcelemlanguage = toDCE "language"++-- | @dcelem:publisher@ from <http://dublincore.org/documents/dcmi-terms/#elements-publisher>.+dcelempublisher :: ScopedName+dcelempublisher = toDCE "publisher"++-- | @dcelem:relation@ from <http://dublincore.org/documents/dcmi-terms/#elements-relation>.+dcelemrelation :: ScopedName+dcelemrelation = toDCE "relation"++-- | @dcelem:rights@ from <http://dublincore.org/documents/dcmi-terms/#elements-rights>.+dcelemrights :: ScopedName+dcelemrights = toDCE "rights"++-- | @dcelem:source@ from <http://dublincore.org/documents/dcmi-terms/#elements-source>.+dcelemsource :: ScopedName+dcelemsource = toDCE "source"++-- | @dcelem:subject@ from <http://dublincore.org/documents/dcmi-terms/#elements-subject>.+dcelemsubject :: ScopedName+dcelemsubject = toDCE "subject"++-- | @dcelem:title@ from <http://dublincore.org/documents/dcmi-terms/#elements-title>.+dcelemtitle :: ScopedName+dcelemtitle = toDCE "title"++-- | @dcelem:type@ from <http://dublincore.org/documents/dcmi-terms/#elements-type>.+dcelemtype :: ScopedName+dcelemtype = toDCE "type"++-- Datatypes++-- | @dcterms:Box@ from <http://dublincore.org/documents/dcmi-terms/#ses-Box>.+dctBox :: ScopedName+dctBox = toDCT "Box"++-- | @dcterms:ISO3166@ from <http://dublincore.org/documents/dcmi-terms/#ses-ISO3166>.+dctISO3166 :: ScopedName+dctISO3166 = toDCT "ISO3166"++-- | @dcterms:ISO639-2@ from <http://dublincore.org/documents/dcmi-terms/#ses-ISO639-2>.+dctISO639_2 :: ScopedName+dctISO639_2 = toDCT "ISO639-2"++-- | @dcterms:ISO639-3@ from <http://dublincore.org/documents/dcmi-terms/#ses-ISO639-3>.+dctISO639_3 :: ScopedName+dctISO639_3 = toDCT "ISO639-3"++-- | @dcterms:Period@ from <http://dublincore.org/documents/dcmi-terms/#ses-Period>.+dctPeriod :: ScopedName+dctPeriod = toDCT "Period"++-- | @dcterms:Point@ from <http://dublincore.org/documents/dcmi-terms/#ses-Point>.+dctPoint :: ScopedName+dctPoint = toDCT "Point"++-- | @dcterms:RFC1766@ from <http://dublincore.org/documents/dcmi-terms/#ses-RFC1766>.+dctRFC1766 :: ScopedName+dctRFC1766 = toDCT "RFC1766"++-- | @dcterms:RFC3066@ from <http://dublincore.org/documents/dcmi-terms/#ses-RFC3066>.+dctRFC3066 :: ScopedName+dctRFC3066 = toDCT "RFC3066"++-- | @dcterms:RFC4646@ from <http://dublincore.org/documents/dcmi-terms/#ses-RFC4646>.+dctRFC4646 :: ScopedName+dctRFC4646 = toDCT "RFC4646"++-- | @dcterms:RFC5646@ from <http://dublincore.org/documents/dcmi-terms/#ses-RFC5646>.+dctRFC5646 :: ScopedName+dctRFC5646 = toDCT "RFC5646"++-- | @dcterms:URI@ from <http://dublincore.org/documents/dcmi-terms/#ses-URI>.+dctURI :: ScopedName+dctURI = toDCT "URI"++-- | @dcterms:W3CDTF@ from <http://dublincore.org/documents/dcmi-terms/#ses-W3CDTF>.+dctW3CDTF :: ScopedName+dctW3CDTF = toDCT "W3CDTF"++-- | @dcam:memberOf@ from <http://dublincore.org/documents/dcmi-terms/#dcam-memberOf>.+dcammemberOf :: ScopedName+dcammemberOf = toDCAM "memberOf"++-- | @dcam:memberOf@ from <http://dublincore.org/documents/dcmi-terms/#dcam-memberOf>.+dcamVocabularyEncodingScheme :: ScopedName+dcamVocabularyEncodingScheme = toDCAM "VocabularyEncodingScheme"++-- | @dctype:Collection@ from <http://dublincore.org/documents/dcmi-terms/#dcmitype-Collection>.+dctypeCollection :: ScopedName+dctypeCollection = toDCTYPE "Collection"++-- | @dctype:Dataset@ from <http://dublincore.org/documents/dcmi-terms/#dcmitype-Dataset>.+dctypeDataset :: ScopedName+dctypeDataset = toDCTYPE "Dataset"++-- | @dctype:Event@ from <http://dublincore.org/documents/dcmi-terms/#dcmitype-Event>.+dctypeEvent :: ScopedName+dctypeEvent = toDCTYPE "Event"++-- | @dctype:Image@ from <http://dublincore.org/documents/dcmi-terms/#dcmitype-Image>.+dctypeImage :: ScopedName+dctypeImage = toDCTYPE "Image"++-- | @dctype:InteractiveResource@ from <http://dublincore.org/documents/dcmi-terms/#dcmitype-InteractiveResource>.+dctypeInteractiveResource :: ScopedName+dctypeInteractiveResource = toDCTYPE "InteractiveResource"++-- | @dctype:Service@ from <http://dublincore.org/documents/dcmi-terms/#dcmitype-Service>.+dctypeService :: ScopedName+dctypeService = toDCTYPE "Service"++-- | @dctype:Software@ from <http://dublincore.org/documents/dcmi-terms/#dcmitype-Software>.+dctypeSoftware :: ScopedName+dctypeSoftware = toDCTYPE "Software"++-- | @dctype:Sound@ from <http://dublincore.org/documents/dcmi-terms/#dcmitype-Sound>.+dctypeSound :: ScopedName+dctypeSound = toDCTYPE "Sound"++-- | @dctype:Text@ from <http://dublincore.org/documents/dcmi-terms/#dcmitype-Text>.+dctypeText :: ScopedName+dctypeText = toDCTYPE "Text"++-- | @dctype:PhysicalObject@ from <http://dublincore.org/documents/dcmi-terms/#dcmitype-PhysicalObject>.+dctypePhysicalObject :: ScopedName+dctypePhysicalObject = toDCTYPE "PhysicalObject"++-- | @dctype:StillImage@ from <http://dublincore.org/documents/dcmi-terms/#dcmitype-StillImage>.+dctypeStillImage :: ScopedName+dctypeStillImage = toDCTYPE "StillImage"++-- | @dctype:MovingImage@ from <http://dublincore.org/documents/dcmi-terms/#dcmitype-MovingImage>.+dctypeMovingImage :: ScopedName+dctypeMovingImage = toDCTYPE "MovingImage"++-- | @dcterms:LCSH@ from <http://dublincore.org/documents/dcmi-terms/#ves-LCSH>.+dctLCSH :: ScopedName+dctLCSH = toDCT "LCSH"++-- | @dcterms:MESH@ from <http://dublincore.org/documents/dcmi-terms/#ves-MESH>.+dctMESH :: ScopedName+dctMESH = toDCT "MESH"++-- | @dcterms:DDC@ from <http://dublincore.org/documents/dcmi-terms/#ves-DDC>.+dctDDC :: ScopedName+dctDDC = toDCT "DDC"++-- | @dcterms:LCC@ from <http://dublincore.org/documents/dcmi-terms/#ves-LCC>.+dctLCC :: ScopedName+dctLCC = toDCT "LCC"++-- | @dcterms:UDC@ from <http://dublincore.org/documents/dcmi-terms/#ves-UDC>.+dctUDC :: ScopedName+dctUDC = toDCT "UDC"++-- | @dcterms:DCMIType@ from <http://dublincore.org/documents/dcmi-terms/#ves-DCMIType>.+dctDCMIType :: ScopedName+dctDCMIType = toDCT "DCMIType"++-- | @dcterms:IMT@ from <http://dublincore.org/documents/dcmi-terms/#ves-IMT>.+dctIMT :: ScopedName+dctIMT = toDCT "IMT"++-- | @dcterms:TGN@ from <http://dublincore.org/documents/dcmi-terms/#ves-TGN>.+dctTGN :: ScopedName+dctTGN = toDCT "TGN"++-- | @dcterms:NLM@ from <http://dublincore.org/documents/dcmi-terms/#ves-NLM>.+dctNLM :: ScopedName+dctNLM = toDCT "NLM"++--------------------------------------------------------------------------------+--+--  Copyright (c) 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/Vocabulary/FOAF.hs view
@@ -0,0 +1,466 @@+{-# LANGUAGE OverloadedStrings #-}++--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  Swish.RDF.Vocabulary.FOAF+--  Copyright   :  (c) 2011 Douglas Burke+--  License     :  GPL V2+--+--  Maintainer  :  Douglas Burke+--  Stability   :  experimental+--  Portability :  OverloadedStrings+--+--  This module defines some commonly used vocabulary terms from the FOAF+--  vocabulary (<http://xmlns.com/foaf/spec/>).+--+--  Note that unlike some of the existing vocabularies in Swish, the FOAF+--  one matches the case and spelling of the RDF terms; so we+--  use 'foafbased_near'+--  rather than @foafBasedNear@. This is partly because some terms would+--  end up with the same Haskell label if a conversion to camel-case wer+--  used.+--+--------------------------------------------------------------------------------++module Swish.RDF.Vocabulary.FOAF+    ( +      -- | The version used for this module is +      -- \"FOAF Vocabulary Specification 0.98 Namespace Document 9 August 2010 - /Marco Polo Edition/\",+      -- <http://xmlns.com/foaf/spec/20100809.html>.+      namespaceFOAF+      +      -- * Classes+      , foafAgent+      , foafDocument+      , foafGroup+      , foafImage+      , foafLabelProperty+      , foafOnlineAccount+      , foafOnlineChatAccount+      , foafOnlineEcommerceAccount+      , foafOnlineGamingAccount+      , foafOrganization+      , foafPerson+      , foafPersonalProfileDocument+      , foafProject+        +      -- * Properties+      , foafaccount+      , foafaccountName+      , foafaccountServiceHomepage+      , foafage+      , foafaimChatID+      , foafbased_near+      , foafbirthday+      , foafcurrentProject+      , foafdepiction+      , foafdepicts+      , foafdnaChecksum+      , foaffamilyName+      , foaffamily_name+      , foaffirstName+      , foaffocus+      , foaffundedBy+      , foafgeekcode+      , foafgender+      , foafgivenName+      , foafgivenname+      , foafholdsAccount+      , foafhomepage+      , foaficqChatID+      , foafimg+      , foafinterest+      , foafisPrimaryTopicOf+      , foafjabberID+      , foafknows+      , foaflastName+      , foaflogo+      , foafmade+      , foafmaker+      , foafmbox+      , foafmbox_sha1sum+      , foafmember+      , foafmembershipClass+      , foafmsnChatID+      , foafmyersBriggs+      , foafname+      , foafnick+      , foafopenid+      , foafpage+      , foafpastProject+      , foafphone+      , foafplan+      , foafprimaryTopic+      , foafpublications+      , foafschoolHomepage+      , foafsha1+      , foafskypeID+      , foafstatus+      , foafsurname+      , foaftheme+      , foafthumbnail+      , foaftipjar+      , foaftitle+      , foaftopic+      , foaftopic_interest+      , foafweblog+      , foafworkInfoHomepage+      , foafworkplaceHomepage+      , foafyahooChatID+    )+where++import Swish.Utils.Namespace (Namespace, makeNamespace, ScopedName, makeNSScopedName)++import Data.Maybe (fromMaybe)+import Network.URI (URI, parseURI)++import qualified Data.Text as T++------------------------------------------------------------+--  Namespace+------------------------------------------------------------++foafURI :: URI+foafURI = fromMaybe (error "Internal error processing FOAF URI") $ parseURI "http://xmlns.com/foaf/0.1/"++-- | Maps @foaf@ to <http://xmlns.com/foaf/0.1/>.+namespaceFOAF :: Namespace+namespaceFOAF = makeNamespace (Just "foaf") foafURI++------------------------------------------------------------+--  Terms+------------------------------------------------------------++toF :: T.Text -> ScopedName+toF  = makeNSScopedName namespaceFOAF++-- Classes++-- | @foaf:Agent@ from <http://xmlns.com/foaf/spec/#term_Agent>.+foafAgent :: ScopedName+foafAgent = toF "Agent"++-- | @foaf:Document@ from <http://xmlns.com/foaf/spec/#term_Document>.+foafDocument :: ScopedName+foafDocument = toF "Document"++-- | @foaf:Group@ from <http://xmlns.com/foaf/spec/#term_Group>.+foafGroup :: ScopedName+foafGroup = toF "Group"++-- | @foaf:Image@ from <http://xmlns.com/foaf/spec/#term_Image>.+foafImage :: ScopedName+foafImage = toF "Image"++-- | @foaf:LabelProperty@ from <http://xmlns.com/foaf/spec/#term_LabelProperty>.+foafLabelProperty :: ScopedName+foafLabelProperty = toF "LabelProperty"++-- | @foaf:OnlineAccount@ from <http://xmlns.com/foaf/spec/#term_OnlineAccount>.+foafOnlineAccount :: ScopedName+foafOnlineAccount = toF "OnlineAccount"++-- | @foaf:OnlineChatAccount@ from <http://xmlns.com/foaf/spec/#term_OnlineChatAccount>.+foafOnlineChatAccount :: ScopedName+foafOnlineChatAccount = toF "OnlineChatAccount"++-- | @foaf:OnlineEcommerceAccount@ from <http://xmlns.com/foaf/spec/#term_OnlineEcommerceAccount>.+foafOnlineEcommerceAccount :: ScopedName+foafOnlineEcommerceAccount = toF "OnlineEcommerceAccount"++-- | @foaf:OnlineGamingAccount@ from <http://xmlns.com/foaf/spec/#term_OnlineGamingAccount>.+foafOnlineGamingAccount :: ScopedName+foafOnlineGamingAccount = toF "OnlineGamingAccount"++-- | @foaf:Organization@ from <http://xmlns.com/foaf/spec/#term_Organization>.+foafOrganization :: ScopedName+foafOrganization = toF "Organization"++-- | @foaf:Person@ from <http://xmlns.com/foaf/spec/#term_Person>.+foafPerson :: ScopedName+foafPerson = toF "Person"++-- | @foaf:PersonalProfileDocument@ from <http://xmlns.com/foaf/spec/#term_PersonalProfileDocument>.+foafPersonalProfileDocument :: ScopedName+foafPersonalProfileDocument = toF "PersonalProfileDocument"++-- | @foaf:Project@ from <http://xmlns.com/foaf/spec/#term_Project>.+foafProject :: ScopedName+foafProject = toF "Project"++-- Properties++-- | @foaf:account@ from <http://xmlns.com/foaf/spec/#term_account>. +foafaccount :: ScopedName+foafaccount = toF "account"+  +-- | @foaf:accountName@ from <http://xmlns.com/foaf/spec/#term_accountName>. +foafaccountName :: ScopedName+foafaccountName = toF "accountName"+  +-- | @foaf:accountServiceHomepage@ from <http://xmlns.com/foaf/spec/#term_accountServiceHomepage>. +foafaccountServiceHomepage :: ScopedName+foafaccountServiceHomepage = toF "accountServiceHomepage"+  +-- | @foaf:age@ from <http://xmlns.com/foaf/spec/#term_age>. +foafage :: ScopedName+foafage = toF "age"+  +-- | @foaf:aimChatID@ from <http://xmlns.com/foaf/spec/#term_aimChatID>. +foafaimChatID :: ScopedName+foafaimChatID = toF "aimChatID"+  +-- | @foaf:based_near@ from <http://xmlns.com/foaf/spec/#term_based_near>. +foafbased_near :: ScopedName+foafbased_near = toF "based_near"+  +-- | @foaf:birthday@ from <http://xmlns.com/foaf/spec/#term_birthday>. +foafbirthday :: ScopedName+foafbirthday = toF "birthday"+  +-- | @foaf:currentProject@ from <http://xmlns.com/foaf/spec/#term_currentProject>. +foafcurrentProject :: ScopedName+foafcurrentProject = toF "currentProject"+  +-- | @foaf:depiction@ from <http://xmlns.com/foaf/spec/#term_depiction>. +foafdepiction :: ScopedName+foafdepiction = toF "depiction"+  +-- | @foaf:depicts@ from <http://xmlns.com/foaf/spec/#term_depicts>. +foafdepicts :: ScopedName+foafdepicts = toF "depicts"+  +-- | @foaf:dnaChecksum@ from <http://xmlns.com/foaf/spec/#term_dnaChecksum>. +foafdnaChecksum :: ScopedName+foafdnaChecksum = toF "dnaChecksum"+  +-- | @foaf:familyName@ from <http://xmlns.com/foaf/spec/#term_familyName>. +foaffamilyName :: ScopedName+foaffamilyName = toF "familyName"+  +-- | @foaf:family_name@ from <http://xmlns.com/foaf/spec/#term_family_name>. +foaffamily_name :: ScopedName+foaffamily_name = toF "family_name"+  +-- | @foaf:firstName@ from <http://xmlns.com/foaf/spec/#term_firstName>. +foaffirstName :: ScopedName+foaffirstName = toF "firstName"+  +-- | @foaf:focus@ from <http://xmlns.com/foaf/spec/#term_focus>. +foaffocus :: ScopedName+foaffocus = toF "focus"+  +-- | @foaf:fundedBy@ from <http://xmlns.com/foaf/spec/#term_fundedBy>. +foaffundedBy :: ScopedName+foaffundedBy = toF "fundedBy"+  +-- | @foaf:geekcode@ from <http://xmlns.com/foaf/spec/#term_geekcode>. +foafgeekcode :: ScopedName+foafgeekcode = toF "geekcode"+  +-- | @foaf:gender@ from <http://xmlns.com/foaf/spec/#term_gender>. +foafgender :: ScopedName+foafgender = toF "gender"+  +-- | @foaf:givenName@ from <http://xmlns.com/foaf/spec/#term_givenName>. +foafgivenName :: ScopedName+foafgivenName = toF "givenName"+  +-- | @foaf:givenname@ from <http://xmlns.com/foaf/spec/#term_givenname>. +foafgivenname :: ScopedName+foafgivenname = toF "givenname"+  +-- | @foaf:holdsAccount@ from <http://xmlns.com/foaf/spec/#term_holdsAccount>. +foafholdsAccount :: ScopedName+foafholdsAccount = toF "holdsAccount"+  +-- | @foaf:homepage@ from <http://xmlns.com/foaf/spec/#term_homepage>. +foafhomepage :: ScopedName+foafhomepage = toF "homepage"+  +-- | @foaf:icqChatID@ from <http://xmlns.com/foaf/spec/#term_icqChatID>. +foaficqChatID :: ScopedName+foaficqChatID = toF "icqChatID"+  +-- | @foaf:img@ from <http://xmlns.com/foaf/spec/#term_img>. +foafimg :: ScopedName+foafimg = toF "img"+  +-- | @foaf:interest@ from <http://xmlns.com/foaf/spec/#term_interest>. +foafinterest :: ScopedName+foafinterest = toF "interest"+  +-- | @foaf:isPrimaryTopicOf@ from <http://xmlns.com/foaf/spec/#term_isPrimaryTopicOf>. +foafisPrimaryTopicOf :: ScopedName+foafisPrimaryTopicOf = toF "isPrimaryTopicOf"+  +-- | @foaf:jabberID@ from <http://xmlns.com/foaf/spec/#term_jabberID>. +foafjabberID :: ScopedName+foafjabberID = toF "jabberID"+  +-- | @foaf:knows@ from <http://xmlns.com/foaf/spec/#term_knows>. +foafknows :: ScopedName+foafknows = toF "knows"+  +-- | @foaf:lastName@ from <http://xmlns.com/foaf/spec/#term_lastName>. +foaflastName :: ScopedName+foaflastName = toF "lastName"+  +-- | @foaf:logo@ from <http://xmlns.com/foaf/spec/#term_logo>. +foaflogo :: ScopedName+foaflogo = toF "logo"+  +-- | @foaf:made@ from <http://xmlns.com/foaf/spec/#term_made>. +foafmade :: ScopedName+foafmade = toF "made"+  +-- | @foaf:maker@ from <http://xmlns.com/foaf/spec/#term_maker>. +foafmaker :: ScopedName+foafmaker = toF "maker"+  +-- | @foaf:mbox@ from <http://xmlns.com/foaf/spec/#term_mbox>. +foafmbox :: ScopedName+foafmbox = toF "mbox"+  +-- | @foaf:mbox_sha1sum@ from <http://xmlns.com/foaf/spec/#term_mbox_sha1sum>. +foafmbox_sha1sum :: ScopedName+foafmbox_sha1sum = toF "mbox_sha1sum"+  +-- | @foaf:member@ from <http://xmlns.com/foaf/spec/#term_member>. +foafmember :: ScopedName+foafmember = toF "member"+  +-- | @foaf:membershipClass@ from <http://xmlns.com/foaf/spec/#term_membershipClass>. +foafmembershipClass :: ScopedName+foafmembershipClass = toF "membershipClass"++-- | @foaf:msnChatID@ from <http://xmlns.com/foaf/spec/#term_msnChatID>.  +foafmsnChatID :: ScopedName+foafmsnChatID = toF "msnChatID"++-- | @foaf:myersBriggs@ from <http://xmlns.com/foaf/spec/#term_myersBriggs>. +foafmyersBriggs :: ScopedName+foafmyersBriggs = toF "myersBriggs"++-- | @foaf:name@ from <http://xmlns.com/foaf/spec/#term_name>. +foafname :: ScopedName+foafname = toF "name"++-- | @foaf:nick@ from <http://xmlns.com/foaf/spec/#term_nick>. +foafnick :: ScopedName+foafnick = toF "nick"++-- | @foaf:openid@ from <http://xmlns.com/foaf/spec/#term_openid>. +foafopenid :: ScopedName+foafopenid = toF "openid"+ +-- | @foaf:page@ from <http://xmlns.com/foaf/spec/#term_page>. +foafpage :: ScopedName+foafpage = toF "page"+  +-- | @foaf:pastProject@ from <http://xmlns.com/foaf/spec/#term_pastProject>. +foafpastProject :: ScopedName+foafpastProject = toF "pastProject"+  +-- | @foaf:phone@ from <http://xmlns.com/foaf/spec/#term_phone>. +foafphone :: ScopedName+foafphone = toF "phone"+  +-- | @foaf:plan@ from <http://xmlns.com/foaf/spec/#term_plan>. +foafplan :: ScopedName+foafplan = toF "plan"++-- | @foaf:primaryTopic@ from <http://xmlns.com/foaf/spec/#term_primaryTopic>.  +foafprimaryTopic :: ScopedName+foafprimaryTopic = toF "primaryTopic"++-- | @foaf:publications@ from <http://xmlns.com/foaf/spec/#term_publications>. +foafpublications :: ScopedName+foafpublications = toF "publications"++-- | @foaf:schoolHomepage@ from <http://xmlns.com/foaf/spec/#term_schoolHomepage>. +foafschoolHomepage :: ScopedName+foafschoolHomepage = toF "schoolHomepage"++-- | @foaf:sha1@ from <http://xmlns.com/foaf/spec/#term_sha1>. +foafsha1 :: ScopedName+foafsha1 = toF "sha1"++-- | @foaf:skypeID@ from <http://xmlns.com/foaf/spec/#term_skypeID>. +foafskypeID :: ScopedName+foafskypeID = toF "skypeID"+ +-- | @foaf:status@ from <http://xmlns.com/foaf/spec/#term_status>. +foafstatus :: ScopedName+foafstatus = toF "status"+  +-- | @foaf:surname@ from <http://xmlns.com/foaf/spec/#term_surname>. +foafsurname :: ScopedName+foafsurname = toF "surname"+  +-- | @foaf:theme@ from <http://xmlns.com/foaf/spec/#term_theme>. +foaftheme :: ScopedName+foaftheme = toF "theme"+  +-- | @foaf:thumbnail@ from <http://xmlns.com/foaf/spec/#term_thumbnail>. +foafthumbnail :: ScopedName+foafthumbnail = toF "thumbnail"++-- | @foaf:tipjar@ from <http://xmlns.com/foaf/spec/#term_tipjar>.  +foaftipjar :: ScopedName+foaftipjar = toF "tipjar"++-- | @foaf:title@ from <http://xmlns.com/foaf/spec/#term_title>. +foaftitle :: ScopedName+foaftitle = toF "title"++-- | @foaf:topic@ from <http://xmlns.com/foaf/spec/#term_topic>. +foaftopic :: ScopedName+foaftopic = toF "topic"+ +-- | @foaf:topic_interest@ from <http://xmlns.com/foaf/spec/#term_topic_interest>. +foaftopic_interest :: ScopedName+foaftopic_interest = toF "topic_interest"+  +-- | @foaf:weblog@ from <http://xmlns.com/foaf/spec/#term_weblog>. +foafweblog :: ScopedName+foafweblog = toF "weblog"+  +-- | @foaf:workInfoHomepage@ from <http://xmlns.com/foaf/spec/#term_workInfoHomepage>. +foafworkInfoHomepage :: ScopedName+foafworkInfoHomepage = toF "workInfoHomepage"+  +-- | @foaf:workplaceHomepage@ from <http://xmlns.com/foaf/spec/#term_workplaceHomepage>. +foafworkplaceHomepage :: ScopedName+foafworkplaceHomepage = toF "workplaceHomepage"+  +-- | @foaf:yahooChatID@ from <http://xmlns.com/foaf/spec/#term_yahooChatID>. +foafyahooChatID :: ScopedName+foafyahooChatID = toF "yahooChatID"+  +--------------------------------------------------------------------------------+--+--  Copyright (c) 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/Vocabulary/Geo.hs view
@@ -0,0 +1,109 @@+{-# LANGUAGE OverloadedStrings #-}++--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  Swish.RDF.Vocabulary.Geo+--  Copyright   :  (c) 2011 Douglas Burke+--  License     :  GPL V2+--+--  Maintainer  :  Douglas Burke+--  Stability   :  experimental+--  Portability :  OverloadedStrings+--+--  This module defines some commonly used vocabulary terms from the Geo+--  vocabulary (<http://www.w3.org/2003/01/geo/>, WGS84 Geo Positioning: an RDF vocabulary).+--+--  Note that unlike some of the existing vocabularies in Swish, the Geo+--  one matches the case and spelling of the RDF terms; so we+--  use 'geolat' rather than @geoLat@.+--+--------------------------------------------------------------------------------++module Swish.RDF.Vocabulary.Geo+    ( +      namespaceGEO+      -- * Classes+      , geoSpatialThing+      , geoPoint+      -- * Properties+      , geolocation+      , geolat+      , geolong+      , geolat_long+    )+where++import Swish.Utils.Namespace (Namespace, makeNamespace, ScopedName, makeNSScopedName)++import Data.Maybe (fromMaybe)+import Network.URI (URI, parseURI)++import qualified Data.Text as T++------------------------------------------------------------+--  Namespace+------------------------------------------------------------++geoURI :: URI+geoURI = fromMaybe (error "Internal error processing Geo URI") $ parseURI "http://www.w3.org/2003/01/geo/wgs84_pos#"++-- | Maps @geo@ to <http://www.w3.org/2003/01/geo/wgs84_pos#>.+namespaceGEO :: Namespace+namespaceGEO = makeNamespace (Just "geo") geoURI++------------------------------------------------------------+--  Terms+------------------------------------------------------------++toG :: T.Text -> ScopedName+toG  = makeNSScopedName namespaceGEO++-- | @geo:location@.+geolocation :: ScopedName+geolocation = toG "location"++-- | @geo:lat@.+geolat :: ScopedName+geolat = toG "lat"++-- | @geo:long@.+geolong :: ScopedName+geolong = toG "long"++-- | @geo:lat_long@ (it is suggested that this not be used when generating RDF statements).+geolat_long :: ScopedName+geolat_long = toG "lat_long"++-- | @geo:SpatialThing@.+geoSpatialThing :: ScopedName+geoSpatialThing = toG "SpatialThing"++-- | @geo:Point@.+geoPoint :: ScopedName+geoPoint = toG "Point"++--------------------------------------------------------------------------------+--+--  Copyright (c) 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/Vocabulary/OWL.hs view
@@ -0,0 +1,175 @@+{-# LANGUAGE OverloadedStrings #-}++--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  Swish.RDF.Vocabulary.OWL+--  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 vocabulary terms from the OWL vocabulary. Note that there+--  is an unfortunate mixture of styles for property names - e.g. 'owlSameAs'+--  and 'owlequivalentClass'. At present there is no systematic attempt to+--  include terms from the vocabulary.+--+--------------------------------------------------------------------------------++module Swish.RDF.Vocabulary.OWL+    ( +      namespaceOWL+      +    , owlOntology+    , owlimports+    , owlversionInfo+    , owldeprecated+    , owlpriorVersion+    , owlbackwardCompatibleWith+    , owlincompatibleWith+        +    , owlClass+    , owlThing+    , owlNothing+    , owlNamedIndividual+      +    , owlSameAs+    , owlequivalentClass+    , owlequivalentProperty+      +    , owlObjectProperty+    , owlDatatypeProperty+    , owlAnnotationProperty+      +    , owlrational+    , owlreal+    )+where++import Swish.Utils.Namespace (Namespace, makeNamespace, ScopedName, makeNSScopedName)++import Data.Maybe (fromMaybe)+import Network.URI (URI, parseURI)++import qualified Data.Text as T++------------------------------------------------------------+--  Namespace+------------------------------------------------------------++owlURI :: URI+owlURI = fromMaybe (error "Internal error processing OWL URI") $ parseURI "http://www.w3.org/2002/07/owl#"++-- | Maps @owl@ to <http://www.w3.org/2002/07/owl#>.+namespaceOWL :: Namespace+namespaceOWL = makeNamespace (Just "owl") owlURI++------------------------------------------------------------+--  Terms+------------------------------------------------------------++toO :: T.Text -> ScopedName+toO = makeNSScopedName namespaceOWL++-- | @owl:sameAs@.+owlSameAs   :: ScopedName+owlSameAs = toO "sameAs"++-- | @owl:equivalentClass@.+owlequivalentClass :: ScopedName+owlequivalentClass = toO "equivalentClass"++-- | @owl:equivalentProperty@.+owlequivalentProperty :: ScopedName+owlequivalentProperty = toO "equivalentPropery"++-- | @owl:Ontology@.+owlOntology :: ScopedName+owlOntology = toO "Ontology"++-- | @owl:imports@.+owlimports :: ScopedName+owlimports = toO "imports"++-- | @owl:versionInfo@.+owlversionInfo :: ScopedName+owlversionInfo = toO "versionInfo"++-- | @owl:deprecated@.+owldeprecated :: ScopedName+owldeprecated = toO "deprecated"++-- | @owl:priorVersion@.+owlpriorVersion :: ScopedName+owlpriorVersion = toO "priorVersion"++-- | @owl:backwartCompatibleWith@.+owlbackwardCompatibleWith :: ScopedName+owlbackwardCompatibleWith = toO "backwardCompatibleWith"++-- | @owl:incompatibleWith@.+owlincompatibleWith :: ScopedName+owlincompatibleWith = toO "incompatibleWith"++-- | @owl:Class@.+owlClass :: ScopedName+owlClass = toO "Class"++-- | @owl:ObjectProperty@.+owlObjectProperty :: ScopedName+owlObjectProperty = toO "ObjectProperty"++-- | @owl:DatatypeProperty@.+owlDatatypeProperty :: ScopedName+owlDatatypeProperty = toO "DatatypeProperty"++-- | @owl:AnnotationProperty@.+owlAnnotationProperty :: ScopedName+owlAnnotationProperty = toO "AnnotationProperty"++-- | @owl:NamedIndividual@.+owlNamedIndividual :: ScopedName+owlNamedIndividual = toO "NamedIndividual"++-- | @owl:Thing@.+owlThing :: ScopedName+owlThing = toO "Thing"++-- | @owl:Thing@.+owlNothing :: ScopedName+owlNothing = toO "Nothing"++-- | @owl:rational@.+owlrational :: ScopedName+owlrational = toO "rational"++-- | @owl:real@.+owlreal :: ScopedName+owlreal = toO "real"++--------------------------------------------------------------------------------+--+--  Copyright (c) 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/Vocabulary/RDF.hs view
@@ -0,0 +1,325 @@+{-# LANGUAGE OverloadedStrings #-}++--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  Swish.RDF.Vocabulary.RDF+--  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 commonly used vocabulary terms from the+--  RDF (<http://www.w3.org/TR/rdf-syntax-grammar/>) and+--  RDF Schema (<http://www.w3.org/TR/rdf-schema/>) documents.+--+--------------------------------------------------------------------------------++module Swish.RDF.Vocabulary.RDF+    ( +      -- * Namespaces+      +      namespaceRDF+    , namespaceRDFS+    +    -- * 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.                                          +    --                                          +    , rdfRDF                                          +    , rdfDescription      +    , rdfID+    , rdfAbout+    , rdfParseType+    , rdfResource+    , rdfLi+    , rdfNodeID+    , rdfDatatype+    , rdf1, rdf2, rdfn+    -- * 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.+    , rdfsResource+    , rdfsClass+    , rdfsLiteral+    , rdfsDatatype+    , rdfXMLLiteral+    , rdfProperty+    +    -- ** Properties+    --                                 +    -- | See the \"Properties\" section at <http://www.w3.org/TR/rdf-schema/#ch_classes> for more information.+    , rdfsRange+    , rdfsDomain+    , rdfType+    , rdfsSubClassOf+    , rdfsSubPropertyOf+    , rdfsLabel+    , rdfsComment+    -- ** Containers+    --+    -- | See the \"Container Classes and Properties\" section at <http://www.w3.org/TR/rdf-schema/#ch_containervocab>.+    , rdfsContainer+    , rdfBag+    , rdfSeq                                 +    , rdfAlt  +    , rdfsContainerMembershipProperty+    , rdfsMember+    -- ** Collections+    --+    -- | See the \"Collections\" section at <http://www.w3.org/TR/rdf-schema/#ch_collectionvocab>.+    , rdfList    +    , rdfFirst+    , rdfRest +    , rdfNil +    -- ** Reification Vocabulary +    --  +    -- | See the \"Reification Vocabulary\" section at <http://www.w3.org/TR/rdf-schema/#ch_reificationvocab>.+    , rdfStatement  +    , rdfSubject  +    , rdfPredicate  +    , rdfObject  +    -- ** Utility Properties +    --  +    -- | See the \"Utility Properties\" section at <http://www.w3.org/TR/rdf-schema/#ch_utilvocab>.+    , rdfsSeeAlso+    , rdfsIsDefinedBy+    , rdfValue  +      +    )+where++import Swish.Utils.Namespace (Namespace, makeNamespace, ScopedName, makeNSScopedName)++import Data.Maybe (fromMaybe)+import Network.URI (URI, parseURI)++import qualified Data.Text as T++------------------------------------------------------------+--  Namespaces+------------------------------------------------------------++rdfURI, rdfsURI :: URI+rdfURI = fromMaybe (error "Internal error processing RDF URI") $ parseURI "http://www.w3.org/1999/02/22-rdf-syntax-ns#"+rdfsURI = fromMaybe (error "Internal error processing RDFS URI") $ parseURI "http://www.w3.org/2000/01/rdf-schema#"++-- | Maps @rdf@ to <http://www.w3.org/1999/02/22-rdf-syntax-ns#>.+namespaceRDF :: Namespace+namespaceRDF = makeNamespace (Just "rdf") rdfURI++-- | Maps @rdfs@ to <http://www.w3.org/2000/01/rdf-schema#>.+namespaceRDFS :: Namespace+namespaceRDFS = makeNamespace (Just "rdfs") rdfsURI++------------------------------------------------------------+--  Terms+------------------------------------------------------------++toRDF, toRDFS :: T.Text -> ScopedName+toRDF  = makeNSScopedName namespaceRDF+toRDFS = makeNSScopedName namespaceRDFS++-- | @rdf:RDF@.+rdfRDF :: ScopedName+rdfRDF = toRDF "RDF"++-- | @rdf:Description@.+rdfDescription :: ScopedName+rdfDescription = toRDF "Description"+  +-- | @rdf:datatype@.+rdfDatatype   :: ScopedName+rdfDatatype   = toRDF "datatype"++-- | @rdf:resource@.+rdfResource   :: ScopedName+rdfResource   = toRDF "resource"++-- | @rdf:about@.+rdfAbout      :: ScopedName+rdfAbout      = toRDF "about"++-- | @rdf:ID@.+rdfID         :: ScopedName+rdfID         = toRDF "ID"++-- | @rdf:parseType@.+rdfParseType :: ScopedName+rdfParseType = toRDF "parseType"++-- | @rdf:li@.+rdfLi :: ScopedName+rdfLi = toRDF "li"++-- | @rdf:nodeID@.+rdfNodeID :: ScopedName+rdfNodeID = toRDF "nodeID"++-- | Create a @rdf:_n@ entity.+rdfn :: Int -> ScopedName+rdfn = toRDF . T.pack . ("_" ++) . show++-- | @rdf:_1@.+rdf1 :: ScopedName+rdf1 = toRDF "_1"++-- | @rdf:_2@.+rdf2 :: ScopedName+rdf2 = toRDF "_2"++-- | @rdf:first@ from <http://www.w3.org/TR/rdf-schema/#ch_first>.+rdfFirst      :: ScopedName+rdfFirst      = toRDF "first"++-- | @rdf:rest@ from <http://www.w3.org/TR/rdf-schema/#ch_rest>.+rdfRest       :: ScopedName+rdfRest       = toRDF "rest"++-- | @rdf:nil@ from <http://www.w3.org/TR/rdf-schema/#ch_nil>.+rdfNil        :: ScopedName+rdfNil        = toRDF "nil"++-- | @rdf:type@ from <http://www.w3.org/TR/rdf-schema/#ch_type>.+rdfType       :: ScopedName+rdfType       = toRDF "type"++-- | @rdf:Property@ from <http://www.w3.org/TR/rdf-schema/#ch_property>.+rdfProperty   :: ScopedName+rdfProperty   = toRDF "Property"++-- | @rdf:XMLLiteral@ from <http://www.w3.org/TR/rdf-schema/#ch_xmlliteral>.+rdfXMLLiteral :: ScopedName+rdfXMLLiteral = toRDF "XMLLiteral"++-- | @rdfs:Resource@ from <http://www.w3.org/TR/rdf-schema/#ch_resource>.+rdfsResource :: ScopedName+rdfsResource = toRDFS "Resource"++-- | @rdfs:Class@ from <http://www.w3.org/TR/rdf-schema/#ch_class>.+rdfsClass :: ScopedName+rdfsClass = toRDFS "Class"++-- | @rdfs:Literal@ from <http://www.w3.org/TR/rdf-schema/#ch_literal>.+rdfsLiteral :: ScopedName+rdfsLiteral = toRDFS "Literal"++-- | @rdfs:Datatype@ from <http://www.w3.org/TR/rdf-schema/#ch_datatype>.+rdfsDatatype :: ScopedName+rdfsDatatype = toRDFS "Datatype"++-- | @rdfs:label@ from <http://www.w3.org/TR/rdf-schema/#ch_label>.+rdfsLabel :: ScopedName+rdfsLabel = toRDFS "label"++-- | @rdfs:comment@ from <http://www.w3.org/TR/rdf-schema/#ch_comment>.+rdfsComment :: ScopedName+rdfsComment = toRDFS "comment"++-- | @rdfs:range@ from <http://www.w3.org/TR/rdf-schema/#ch_range>.+rdfsRange :: ScopedName+rdfsRange = toRDFS "range"++-- | @rdfs:domain@ from <http://www.w3.org/TR/rdf-schema/#ch_domain>.+rdfsDomain :: ScopedName+rdfsDomain = toRDFS "domain"++-- | @rdfs:subClassOf@ from <http://www.w3.org/TR/rdf-schema/#ch_subclassof>.+rdfsSubClassOf :: ScopedName+rdfsSubClassOf = toRDFS "subClassOf"++-- | @rdfs:subPropertyOf@ from <http://www.w3.org/TR/rdf-schema/#ch_subpropertyof>.+rdfsSubPropertyOf :: ScopedName+rdfsSubPropertyOf = toRDFS "subPropertyOf"++-- | @rdfs:Container@ from <http://www.w3.org/TR/rdf-schema/#ch_container>.+rdfsContainer :: ScopedName+rdfsContainer = toRDFS "Container"++-- | @rdf:Bag@ from <http://www.w3.org/TR/rdf-schema/#ch_bag>.+rdfBag :: ScopedName+rdfBag = toRDF "Bag"++-- | @rdf:Seq@ from <http://www.w3.org/TR/rdf-schema/#ch_seq>.+rdfSeq :: ScopedName+rdfSeq = toRDF "Seq"++-- | @rdf:Alt@ from <http://www.w3.org/TR/rdf-schema/#ch_alt>.+rdfAlt :: ScopedName+rdfAlt = toRDF "Alt"++-- | @rdfs:ContainerMembershipProperty@ from <http://www.w3.org/TR/rdf-schema/#ch_containermembershipproperty>.+rdfsContainerMembershipProperty :: ScopedName+rdfsContainerMembershipProperty = toRDFS "ContainerMembershipProperty"++-- | @rdfs:member@ from <http://www.w3.org/TR/rdf-schema/#ch_member>.+rdfsMember :: ScopedName+rdfsMember    = toRDFS "member"++-- | @rdf:List@ from <http://www.w3.org/TR/rdf-schema/#ch_list>.+rdfList :: ScopedName+rdfList = toRDF "List"++-- | @rdf:Statement@ from <http://www.w3.org/TR/rdf-schema/#ch_statement>.+rdfStatement :: ScopedName+rdfStatement = toRDF "Statement"++-- | @rdf:subject@ from <http://www.w3.org/TR/rdf-schema/#ch_subject>.+rdfSubject :: ScopedName+rdfSubject = toRDF "subject"++-- | @rdf:predicate@ from <http://www.w3.org/TR/rdf-schema/#ch_predicate>.+rdfPredicate :: ScopedName+rdfPredicate = toRDF "subject"++-- | @rdf:object@ from <http://www.w3.org/TR/rdf-schema/#ch_object>.+rdfObject :: ScopedName+rdfObject = toRDF "object"++-- | @rdfs:seeAlso@ from <http://www.w3.org/TR/rdf-schema/#ch_seealso>.+rdfsSeeAlso :: ScopedName+rdfsSeeAlso = toRDFS "seeAlso"++-- | @rdfs:isDefinedBy@ from <http://www.w3.org/TR/rdf-schema/#ch_isdefinedby>.+rdfsIsDefinedBy :: ScopedName+rdfsIsDefinedBy = toRDFS "isDefinedBy"++-- | @rdf:value@ from <http://www.w3.org/TR/rdf-schema/#ch_value>.+rdfValue :: ScopedName+rdfValue = toRDF "value"++--------------------------------------------------------------------------------+--+--  Copyright (c) 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/Vocabulary/XSD.hs view
@@ -0,0 +1,201 @@+{-# LANGUAGE OverloadedStrings #-}++--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  Swish.RDF.Vocabulary.XSD+--  Copyright   :  (c) 2011 Douglas Burke+--  License     :  GPL V2+--+--  Maintainer  :  Douglas Burke+--  Stability   :  experimental+--  Portability :  OverloadedStrings+--+--  This module defines vocabulary terms from the XSD document.+--+--------------------------------------------------------------------------------++module Swish.RDF.Vocabulary.XSD+    ( +      namespaceXSD+      +    -- * XSD data types+    --  +    -- | See the XSD Schema Part 2 documentation at <http://www.w3.org/TR/xmlschema-2/>;+    -- the version used is \"W3C Recommendation 28 October 2004\",  +    -- <http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/>.  +    , xsdType +    +    -- ** Primitive datatypes  +    --  +    -- | See the section \"Primitive datatypes\" at  +    -- <http://www.w3.org/TR/xmlschema-2/#built-in-primitive-datatypes>.  +    , xsdString+    , xsdBoolean+    , xsdDecimal+    , xsdFloat+    , xsdDouble+    , xsdDateTime+    , xsdTime+    , xsdDate+    , xsdAnyURI  +      +    -- ** Derived datatypes  +    --  +    -- | See the section \"Derived datatypes\" at  +    -- <http://www.w3.org/TR/xmlschema-2/#built-in-derived>.  +    , xsdInteger+    , xsdNonPosInteger+    , xsdNegInteger+    , xsdLong+    , xsdInt+    , xsdShort+    , xsdByte+    , xsdNonNegInteger+    , xsdUnsignedLong+    , xsdUnsignedInt+    , xsdUnsignedShort+    , xsdUnsignedByte+    , xsdPosInteger++    )+where++import Swish.Utils.Namespace (Namespace, makeNamespace, ScopedName, makeNSScopedName)++import Data.Maybe (fromMaybe)+import Network.URI (URI, parseURI)++import qualified Data.Text as T++------------------------------------------------------------+--  Namespace+------------------------------------------------------------++xsdURI :: URI+xsdURI = fromMaybe (error "Internal error processing XSD URI") $ parseURI "http://www.w3.org/2001/XMLSchema#"++-- | Maps @xsd@ to <http://www.w3.org/2001/XMLSchema#>.+namespaceXSD :: Namespace+namespaceXSD = makeNamespace (Just "xsd") xsdURI++------------------------------------------------------------+--  Terms+------------------------------------------------------------++-- | Create a scoped name for an XSD datatype with the given name.+xsdType :: T.Text -> ScopedName+xsdType = makeNSScopedName namespaceXSD++-- | @xsd:string@ from <http://www.w3.org/TR/xmlschema-2/#string>.+xsdString           :: ScopedName+xsdString           = xsdType "string"++-- | @xsd:boolean@ from <http://www.w3.org/TR/xmlschema-2/#boolean>.+xsdBoolean          :: ScopedName+xsdBoolean          = xsdType "boolean"++-- | @xsd:decimal@ from <http://www.w3.org/TR/xmlschema-2/#decimal>.+xsdDecimal          :: ScopedName+xsdDecimal          = xsdType "decimal"++-- | @xsd:integer@ from <http://www.w3.org/TR/xmlschema-2/#integer>.+xsdInteger          :: ScopedName+xsdInteger          = xsdType "integer"++-- | @xsd:nonNegativeInteger@ from <http://www.w3.org/TR/xmlschema-2/#nonNegativeInteger>.+xsdNonNegInteger   :: ScopedName+xsdNonNegInteger   = xsdType "nonNegativeInteger"++-- | @xsd:nonPositiveInteger@ from <http://www.w3.org/TR/xmlschema-2/#nonPositiveInteger>.+xsdNonPosInteger   :: ScopedName+xsdNonPosInteger   = xsdType "nonPositiveInteger"++-- | @xsd:positiveInteger@ from <http://www.w3.org/TR/xmlschema-2/#positiveInteger>.+xsdPosInteger      :: ScopedName+xsdPosInteger      = xsdType "positiveInteger"++-- | @xsd:negativeInteger@ from <http://www.w3.org/TR/xmlschema-2/#negativeInteger>.+xsdNegInteger      :: ScopedName+xsdNegInteger      = xsdType "negativeInteger"++-- | @xsd:float@ from <http://www.w3.org/TR/xmlschema-2/#float>.+xsdFloat            :: ScopedName+xsdFloat            = xsdType "float"++-- | @xsd:double@ from <http://www.w3.org/TR/xmlschema-2/#double>.+xsdDouble           :: ScopedName+xsdDouble           = xsdType "double"++-- | @xsd:long@ from <http://www.w3.org/TR/xmlschema-2/#long>.+xsdLong :: ScopedName+xsdLong = xsdType "long"++-- | @xsd:int@ from <http://www.w3.org/TR/xmlschema-2/#int>.+xsdInt :: ScopedName+xsdInt = xsdType "int"++-- | @xsd:short@ from <http://www.w3.org/TR/xmlschema-2/#short>.+xsdShort :: ScopedName+xsdShort = xsdType "short"++-- | @xsd:byte@ from <http://www.w3.org/TR/xmlschema-2/#byte>.+xsdByte :: ScopedName+xsdByte = xsdType "byte"++-- | @xsd:unsignedLong@ from <http://www.w3.org/TR/xmlschema-2/#unsignedLong>.+xsdUnsignedLong :: ScopedName+xsdUnsignedLong = xsdType "unsignedLong"++-- | @xsd:unsignedInt@ from <http://www.w3.org/TR/xmlschema-2/#unsignedInt>.+xsdUnsignedInt :: ScopedName+xsdUnsignedInt = xsdType "unsignedInt"++-- | @xsd:unsignedShort@ from <http://www.w3.org/TR/xmlschema-2/#unsignedShort>.+xsdUnsignedShort :: ScopedName+xsdUnsignedShort = xsdType "unsignedShort"++-- | @xsd:unsignedByte@ from <http://www.w3.org/TR/xmlschema-2/#unsignedByte>.+xsdUnsignedByte :: ScopedName+xsdUnsignedByte = xsdType "unsignedByte"++-- | @xsd:date@ from <http://www.w3.org/TR/xmlschema-2/#date>.+xsdDate :: ScopedName+xsdDate = xsdType "date"++-- | @xsd:dateTime@ from <http://www.w3.org/TR/xmlschema-2/#dateTime>.+xsdDateTime :: ScopedName+xsdDateTime = xsdType "dateTime"++-- | @xsd:time@ from <http://www.w3.org/TR/xmlschema-2/#time>.+xsdTime :: ScopedName+xsdTime = xsdType "time"++-- | @xsd:anyURI@ from <http://www.w3.org/TR/xmlschema-2/#anyURI>.+xsdAnyURI :: ScopedName+xsdAnyURI = xsdType "anyURI"++--------------------------------------------------------------------------------+--+--  Copyright (c) 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/ListHelpers.hs view
@@ -0,0 +1,359 @@+--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  ListHelpers+--  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 some generic list and related helper functions. +--+--------------------------------------------------------------------------------++{-+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+        +      )+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 +--        likely need changing to Ord a+------------------------------------------------------------++-- |Subset test++subset          :: (Eq a) => [a] -> [a] -> Bool+a `subset` b    = and [ ma `elem` b | ma <- a ]++-- |Set equivalence test++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+------------------------------------------------------------++-- |Apply list of functions to some value, returning list of results.+--  It's kind of like an converse map.+--+--  This is similar to the 'ap' function in the Monad library.+--+flist :: [a->b] -> a -> [b]+flist fs a = map ($ a) fs++{-+flisttest = flist [(1*),(2*),(3*)] 5 -- [5,10,15]+-}++{-++-- |A more generalized form of flist that works with arbitrary Monads.+--  (Suggested by Derek Elkin.)++fmonad :: Monad m => m (a->b) -> a -> m b+fmonad fm a =+    do  { f <- fm+        ; return $ f a+        }++-}++{-+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+--  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/LookupMap.hs view
@@ -0,0 +1,454 @@+{-# 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 view
@@ -0,0 +1,69 @@+--------------------------------------------------------------------------------+--  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 view
@@ -0,0 +1,222 @@+{-# 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 view
@@ -0,0 +1,386 @@+--------------------------------------------------------------------------------+--  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 as = foldl' add [] as+    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 view
@@ -0,0 +1,277 @@+{-# 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 view
@@ -0,0 +1,75 @@+--------------------------------------------------------------------------------+--  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+--+--------------------------------------------------------------------------------
swish.cabal view
@@ -1,5 +1,5 @@ Name:               swish-Version:            0.6.1.2+Version:            0.6.2.1 Stability:          experimental License:            LGPL License-file:       LICENSE @@ -9,8 +9,8 @@ Category:           Semantic Web Synopsis:           A semantic web toolkit.  -Tested-With:        GHC==6.12.3-Cabal-Version:      >= 1.6+Tested-With:        GHC==7.0.4+Cabal-Version:      >= 1.9.2 Homepage:           https://bitbucket.org/doug_burke/swish/wiki/Home Bug-reports:        https://bitbucket.org/doug_burke/swish/issues @@ -46,6 +46,21 @@   .   Changes:   .+  [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".+  .   [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.   .@@ -55,14 +70,14 @@   provided in version 0.3.2.1).   .   [Version 0.6.1.0] Added support for Turtle format (added the-  Swish.RDF.TurtleFormatter and Swish.RDF.TurtleParser modules).+  "Swish.RDF.TurtleFormatter" and "Swish.RDF.TurtleParser" modules).   .   [Version 0.6.0.2] Minor internal changes.   .-  [Version 0.6.0.1] Moved to using hashing routine using the Data.Hashable-  interface rather than Swish.Utils.MiscHelpers which is deprecated.+  [Version 0.6.0.1] Moved to using hashing routine using the @Data.Hashable@+  interface rather than "Swish.Utils.MiscHelpers", which is deprecated.   .-  [Version 0.6.0.0] Add Data.Interned.URI and use it to speed up the QName+  [Version 0.6.0.0] Add "Data.Interned.URI" and use it to speed up the 'QName'   equality check.   .   [Version 0.5.0.3] Didn't get all the required @FlexibleInstances@.@@ -168,13 +183,9 @@   Description: Turn on developer flags   Default:     False -Flag  tests-  Description: Build the test programs.-  Default:     False--Flag  hpc-  Description: Use Hpc for the tests-  Default:     False+-- Flag  hpc+--   Description: Use Hpc for the tests+--   Default:     False  Library    Build-Depends:@@ -195,6 +206,8 @@       hashable == 1.1.*,       intern == 0.8.* +   Hs-Source-Dirs: src/+    Exposed-Modules:       Swish       Swish.RDF@@ -234,6 +247,12 @@       Swish.RDF.TurtleParser       Swish.RDF.VarBinding       Swish.RDF.Vocabulary+      Swish.RDF.Vocabulary.DublinCore+      Swish.RDF.Vocabulary.FOAF+      Swish.RDF.Vocabulary.Geo+      Swish.RDF.Vocabulary.OWL+      Swish.RDF.Vocabulary.RDF+      Swish.RDF.Vocabulary.XSD       Swish.Utils.ListHelpers       Swish.Utils.LookupMap       Swish.Utils.MiscHelpers@@ -253,298 +272,416 @@       ghc-options: -Werror       ghc-prof-options: -auto-all -Executable         BuiltInMapTest-   Main-Is:        tests/BuiltInMapTest.hs-   if !flag(tests)-      buildable: False--   if flag(tests)-      Build-Depends: -          HUnit == 1.2.*--   if flag(hpc)-      ghc-options: -fhpc+Test-Suite test-builtinmap+   type:       exitcode-stdio-1.0+   Hs-Source-Dirs: tests/ src/+   Main-Is:    BuiltInMapTest.hs+   Other-Modules:  TestHelpers -   if flag(developer)-      ghc-options: -Werror-      ghc-prof-options: -auto-all+   -- if flag(hpc)+   --    ghc-options: -fhpc+   -- +   -- if flag(developer)+   --    ghc-options: -Werror+   --    ghc-prof-options: -auto-all     ghc-options:       -Wall -fno-warn-orphans -Executable         GraphPartitionTest-   Main-Is:        tests/GraphPartitionTest.hs--   if !flag(tests)-      buildable: False--   if flag(tests)-      Build-Depends: -          HUnit == 1.2.*+   Build-Depends:+      HUnit == 1.2.*,+      base >=3 && < 5,+      text == 0.11.*,+      old-locale == 1.0.*, +      network >= 2.2 && < 2.4,+      hashable == 1.1.*,+      intern == 0.8.*,+      directory >= 1.0 && < 1.2,+      mtl >= 2 && < 3,+      polyparse >= 1.6 && < 1.8,+      time >= 1.1 && < 1.5 -   if flag(hpc)-      ghc-options: -fhpc+Test-Suite test-graphpartition+   type:       exitcode-stdio-1.0+   Hs-Source-Dirs: tests/ src/+   Main-Is:        GraphPartitionTest.hs+   Other-Modules:  TestHelpers -   if flag(developer)-      ghc-options: -Werror-      ghc-prof-options: -auto-all+   -- if flag(hpc)+   --    ghc-options: -fhpc+   -- +   -- if flag(developer)+   --    ghc-options: -Werror+   --    ghc-prof-options: -auto-all     ghc-options:       -Wall -fno-warn-orphans -Executable         GraphTest-   Main-Is:        tests/GraphTest.hs-   if !flag(tests)-      buildable: False--   if flag(tests)-      Build-Depends: -          HUnit == 1.2.*+   Build-Depends:+      HUnit == 1.2.*,+      base >=3 && < 5,+      hashable == 1.1.*,+      mtl >= 2 && < 3 -   if flag(hpc)-      ghc-options: -fhpc+Test-Suite test-graph+   type:       exitcode-stdio-1.0+   Hs-Source-Dirs: tests/ src/+   Main-Is:        GraphTest.hs+   Other-Modules:  TestHelpers -   if flag(developer)-      ghc-options: -Werror-      ghc-prof-options: -auto-all+   -- if flag(hpc)+   --    ghc-options: -fhpc+   -- +   -- if flag(developer)+   --    ghc-options: -Werror+   --    ghc-prof-options: -auto-all     ghc-options:       -Wall -fno-warn-orphans -Executable         NTTest-   Main-Is:        tests/NTTest.hs-   if !flag(tests)-      buildable: False--   if flag(tests)-      Build-Depends: -          HUnit == 1.2.*+   Build-Depends:+      HUnit == 1.2.*,+      base >=3 && < 5,+      hashable == 1.1.*,+      mtl >= 2 && < 3 -   if flag(hpc)-      ghc-options: -fhpc+Test-Suite test-nt+   type:       exitcode-stdio-1.0+   Hs-Source-Dirs: tests/ src/+   Main-Is:        NTTest.hs+   Other-Modules:  TestHelpers -   if flag(developer)-      ghc-options: -Werror-      ghc-prof-options: -auto-all+   -- if flag(hpc)+   --    ghc-options: -fhpc+   -- +   -- if flag(developer)+   --    ghc-options: -Werror+   --    ghc-prof-options: -auto-all     ghc-options:       -Wall -fno-warn-orphans -Executable         N3ParserTest-   Main-Is:        tests/N3ParserTest.hs-   if !flag(tests)-      buildable: False--   if flag(tests)-      Build-Depends: -          HUnit == 1.2.*+   Build-Depends:+      HUnit == 1.2.*,+      base >=3 && < 5,+      hashable == 1.1.*,+      mtl >= 2 && < 3,+      text == 0.11.*,+      network >= 2.2 && < 2.4,+      intern == 0.8.*,+      directory >= 1.0 && < 1.2,+      old-locale == 1.0.*,+      time >= 1.1 && < 1.5,+      polyparse >= 1.6 && < 1.8 -   if flag(hpc)-      ghc-options: -fhpc+Test-Suite test-n3parser+   type:       exitcode-stdio-1.0+   Hs-Source-Dirs: tests/ src/+   Main-Is:        N3ParserTest.hs+   Other-Modules:  TestHelpers -   if flag(developer)-      ghc-options: -Werror-      ghc-prof-options: -auto-all+   -- if flag(hpc)+   --    ghc-options: -fhpc+   -- +   -- if flag(developer)+   --    ghc-options: -Werror+   --    ghc-prof-options: -auto-all     ghc-options:       -Wall -fno-warn-orphans -Executable         N3FormatterTest-   Main-Is:        tests/N3FormatterTest.hs-   if !flag(tests)-      buildable: False--   if flag(tests)-      Build-Depends: -          HUnit == 1.2.*+   Build-Depends:+      HUnit == 1.2.*,+      base >=3 && < 5,+      hashable == 1.1.*,+      mtl >= 2 && < 3,+      text == 0.11.*,+      network >= 2.2 && < 2.4,+      intern == 0.8.*,+      directory >= 1.0 && < 1.2,+      old-locale == 1.0.*,+      time >= 1.1 && < 1.5,+      polyparse >= 1.6 && < 1.8 -   if flag(hpc)-      ghc-options: -fhpc+Test-Suite test-n3formatter+   type:       exitcode-stdio-1.0+   Hs-Source-Dirs: tests/ src/+   Main-Is:        N3FormatterTest.hs+   Other-Modules:  TestHelpers -   if flag(developer)-      ghc-options: -Werror-      ghc-prof-options: -auto-all+   -- if flag(hpc)+   --    ghc-options: -fhpc+   -- +   -- if flag(developer)+   --    ghc-options: -Werror+   --    ghc-prof-options: -auto-all     ghc-options:       -Wall -fno-warn-orphans -Executable         RDFDatatypeXsdIntegerTest-   Main-Is:        tests/RDFDatatypeXsdIntegerTest.hs-   if !flag(tests)-      buildable: False--   if flag(tests)-      Build-Depends: -          HUnit == 1.2.*+   Build-Depends:+      HUnit == 1.2.*,+      base >=3 && < 5,+      hashable == 1.1.*,+      mtl >= 2 && < 3,+      text == 0.11.*,+      network >= 2.2 && < 2.4,+      intern == 0.8.*,+      directory >= 1.0 && < 1.2,+      old-locale == 1.0.*,+      time >= 1.1 && < 1.5,+      polyparse >= 1.6 && < 1.8 -   if flag(hpc)-      ghc-options: -fhpc+Test-Suite test-rdfdatatypexsdinteger+   type:       exitcode-stdio-1.0+   Hs-Source-Dirs: tests/ src/+   Main-Is:        RDFDatatypeXsdIntegerTest.hs+   Other-Modules:  TestHelpers -   if flag(developer)-      ghc-options: -Werror-      ghc-prof-options: -auto-all+   -- if flag(hpc)+   --    ghc-options: -fhpc+   -- +   -- if flag(developer)+   --    ghc-options: -Werror+   --    ghc-prof-options: -auto-all     ghc-options:       -Wall -fno-warn-orphans -Executable         RDFGraphTest-   Main-Is:        tests/RDFGraphTest.hs-   if !flag(tests)-      buildable: False--   if flag(tests)-      Build-Depends: -          HUnit == 1.2.*+   Build-Depends:+      HUnit == 1.2.*,+      base >=3 && < 5,+      hashable == 1.1.*,+      mtl >= 2 && < 3,+      text == 0.11.*,+      network >= 2.2 && < 2.4,+      intern == 0.8.*,+      directory >= 1.0 && < 1.2,+      old-locale == 1.0.*,+      time >= 1.1 && < 1.5,+      polyparse >= 1.6 && < 1.8 -   if flag(hpc)-      ghc-options: -fhpc+Test-Suite test-rdfgraph+   type:       exitcode-stdio-1.0+   Hs-Source-Dirs: tests/ src/+   Main-Is:        RDFGraphTest.hs+   Other-Modules:  TestHelpers -   if flag(developer)-      ghc-options: -Werror-      ghc-prof-options: -auto-all+   -- if flag(hpc)+   --    ghc-options: -fhpc+   -- +   -- if flag(developer)+   --    ghc-options: -Werror+   --    ghc-prof-options: -auto-all     ghc-options:       -Wall -fno-warn-orphans -Executable         RDFProofContextTest-   Main-Is:        tests/RDFProofContextTest.hs-   if !flag(tests)-      buildable: False-                    -   if flag(tests)-      Build-Depends: -          HUnit == 1.2.*+   Build-Depends:+      HUnit == 1.2.*,+      base >=3 && < 5,+      hashable == 1.1.*,+      mtl >= 2 && < 3,+      text == 0.11.*,+      network >= 2.2 && < 2.4,+      time >= 1.1 && < 1.5,+      old-locale == 1.0.*,+      intern == 0.8.*,+      directory >= 1.0 && < 1.2 -   if flag(hpc)-      ghc-options: -fhpc+Test-Suite test-rdfproofcontext+   type:       exitcode-stdio-1.0+   Hs-Source-Dirs: tests/ src/+   Main-Is:        RDFProofContextTest.hs+   Other-Modules:  TestHelpers -   if flag(developer)-      ghc-options: -Werror-      ghc-prof-options: -auto-all+   -- if flag(hpc)+   --    ghc-options: -fhpc+   -- +   -- if flag(developer)+   --    ghc-options: -Werror+   --    ghc-prof-options: -auto-all     ghc-options:       -Wall -fno-warn-orphans -Executable         RDFProofTest-   Main-Is:        tests/RDFProofTest.hs-   if !flag(tests)-      buildable: False-                     -   if flag(tests)-      Build-Depends: -          HUnit == 1.2.*+   Build-Depends:+      HUnit == 1.2.*,+      base >=3 && < 5,+      hashable == 1.1.*,+      mtl >= 2 && < 3,+      text == 0.11.*,+      network >= 2.2 && < 2.4,+      time >= 1.1 && < 1.5,+      intern == 0.8.*,+      directory >= 1.0 && < 1.2,+      old-locale == 1.0.*,+      polyparse >= 1.6 && < 1.8 -   if flag(hpc)-      ghc-options: -fhpc+Test-Suite test-rdfproof+   type:       exitcode-stdio-1.0+   Hs-Source-Dirs: tests/ src/+   Main-Is:        RDFProofTest.hs+   Other-Modules:  TestHelpers -   if flag(developer)-      ghc-options: -Werror-      ghc-prof-options: -auto-all+   -- if flag(hpc)+   --    ghc-options: -fhpc+   -- +   -- if flag(developer)+   --    ghc-options: -Werror+   --    ghc-prof-options: -auto-all     ghc-options:       -Wall -fno-warn-orphans -Executable         RDFQueryTest-   Main-Is:        tests/RDFQueryTest.hs-   if !flag(tests)-      buildable: False--   if flag(tests)-      Build-Depends: -          HUnit == 1.2.*+   Build-Depends:+      HUnit == 1.2.*,+      base >=3 && < 5,+      hashable == 1.1.*,+      mtl >= 2 && < 3,+      text == 0.11.*,+      network >= 2.2 && < 2.4,+      time >= 1.1 && < 1.5,+      intern == 0.8.*,+      directory >= 1.0 && < 1.2,+      old-locale == 1.0.*,+      polyparse >= 1.6 && < 1.8 -   if flag(hpc)-      ghc-options: -fhpc+Test-Suite test-rdfquery+   type:       exitcode-stdio-1.0+   Hs-Source-Dirs: tests/ src/+   Main-Is:        RDFQueryTest.hs+   Other-Modules:  TestHelpers -   if flag(developer)-      ghc-options: -Werror-      ghc-prof-options: -auto-all+   -- if flag(hpc)+   --    ghc-options: -fhpc+   -- +   -- if flag(developer)+   --    ghc-options: -Werror+   --    ghc-prof-options: -auto-all     ghc-options:       -Wall -fno-warn-orphans -Executable         RDFRulesetTest-   Main-Is:        tests/RDFRulesetTest.hs-   if !flag(tests)-      buildable: False--   if flag(tests)-      Build-Depends: -          HUnit == 1.2.*+   Build-Depends:+      HUnit == 1.2.*,+      base >=3 && < 5,+      hashable == 1.1.*,+      mtl >= 2 && < 3,+      text == 0.11.*,+      network >= 2.2 && < 2.4,+      time >= 1.1 && < 1.5,+      intern == 0.8.*,+      directory >= 1.0 && < 1.2,+      old-locale == 1.0.*,+      polyparse >= 1.6 && < 1.8 -   if flag(hpc)-      ghc-options: -fhpc+Test-Suite test-rdfruleset+   type:       exitcode-stdio-1.0+   Hs-Source-Dirs: tests/ src/+   Main-Is:        RDFRulesetTest.hs+   Other-Modules:  TestHelpers -   if flag(developer)-      ghc-options: -Werror-      ghc-prof-options: -auto-all+   -- if flag(hpc)+   --    ghc-options: -fhpc+   -- +   -- if flag(developer)+   --    ghc-options: -Werror+   --    ghc-prof-options: -auto-all     ghc-options:       -Wall -fno-warn-orphans -Executable         VarBindingTest-   Main-Is:        tests/VarBindingTest.hs-   if !flag(tests)-      buildable: False--   if flag(tests)-      Build-Depends: -          HUnit == 1.2.*+   Build-Depends:+      HUnit == 1.2.*,+      base >=3 && < 5,+      hashable == 1.1.*,+      mtl >= 2 && < 3,+      text == 0.11.*,+      network >= 2.2 && < 2.4,+      time >= 1.1 && < 1.5,+      intern == 0.8.*,+      directory >= 1.0 && < 1.2,+      old-locale == 1.0.*,+      polyparse >= 1.6 && < 1.8 -   if flag(hpc)-      ghc-options: -fhpc+Test-Suite test-varbinding+   type:       exitcode-stdio-1.0+   Hs-Source-Dirs: tests/ src/+   Main-Is:        VarBindingTest.hs+   Other-Modules:  TestHelpers -   if flag(developer)-      ghc-options: -Werror-      ghc-prof-options: -auto-all+   -- if flag(hpc)+   --    ghc-options: -fhpc+   -- +   -- if flag(developer)+   --    ghc-options: -Werror+   --    ghc-prof-options: -auto-all     ghc-options:       -Wall -fno-warn-orphans -Executable         LookupMapTest-   Main-Is:        tests/LookupMapTest.hs-   if !flag(tests)-      buildable: False--   if flag(tests)-      Build-Depends: -          HUnit == 1.2.*+   Build-Depends:+      HUnit == 1.2.*,+      base >=3 && < 5,+      text == 0.11.*,+      network >= 2.2 && < 2.4,+      intern == 0.8.*,+      hashable == 1.1.*,+      directory >= 1.0 && < 1.2 -   if flag(hpc)-      ghc-options: -fhpc+Test-Suite test-lookupmap+   type:       exitcode-stdio-1.0+   Hs-Source-Dirs: tests/ src/+   Main-Is:        LookupMapTest.hs+   Other-Modules:  TestHelpers -   if flag(developer)-      ghc-options: -Werror-      ghc-prof-options: -auto-all+   -- if flag(hpc)+   --    ghc-options: -fhpc+   -- +   -- if flag(developer)+   --    ghc-options: -Werror+   --    ghc-prof-options: -auto-all     ghc-options:       -Wall -fno-warn-orphans -Executable         QNameTest-   Main-Is:        tests/QNameTest.hs-   if !flag(tests)-      buildable: False--   if flag(tests)-      Build-Depends: -          HUnit == 1.2.*+   Build-Depends:+      HUnit == 1.2.*,+      base >=3 && < 5 -   if flag(hpc)-      ghc-options: -fhpc+Test-Suite test-qname+   type:       exitcode-stdio-1.0+   Hs-Source-Dirs: tests/ src/+   Main-Is:        QNameTest.hs+   Other-Modules:  TestHelpers -   if flag(developer)-      ghc-options: -Werror-      ghc-prof-options: -auto-all+   -- if flag(hpc)+   --    ghc-options: -fhpc+   -- +   -- if flag(developer)+   --    ghc-options: -Werror+   --    ghc-prof-options: -auto-all     ghc-options:       -Wall -fno-warn-orphans +   Build-Depends:+      HUnit == 1.2.*,+      base >=3 && < 5,+      text == 0.11.*,+      network >= 2.2 && < 2.4,+      intern == 0.8.*,+      hashable == 1.1.*,+      directory >= 1.0 && < 1.2+  -- we do not have the data files to run this test  Executable         SwishTest    Main-Is:        tests/SwishTest.hs  Executable         Swish-   Main-Is:        src/Swish.hs+   Main-Is:        SwishApp.hs+   Hs-Source-Dirs: app/ src/    Other-Modules:  Paths_swish     ghc-options:@@ -553,4 +690,20 @@    if flag(developer)       ghc-options: -Werror       ghc-prof-options: -auto-all++   Build-Depends:+      base >=3 && < 5,+      text == 0.11.*,+      binary == 0.5.*,+      containers >= 0.3 && < 0.5,+      array == 0.3.*,+      polyparse >= 1.6 && < 1.8,+      old-locale == 1.0.*, +      time >= 1.1 && < 1.5,+      mtl >= 2 && < 3,+      network >= 2.2 && < 2.4,+      directory >= 1.0 && < 1.2,+      filepath >= 1.1 && < 1.3,+      hashable == 1.1.*,+      intern == 0.8.* 
tests/BuiltInMapTest.hs view
@@ -45,26 +45,22 @@     , namespaceXsdType     ) -import Swish.Utils.LookupMap-    ( mapFindMaybe )--import Swish.Utils.ListHelpers-    ( equiv )+import Swish.Utils.LookupMap (mapFindMaybe)  import Test.HUnit     ( Test(TestCase,TestList)-    , Assertion-    , assertBool, assertEqual, assertFailure-    , runTestTT+    , assertEqual     ) -import Control.Monad (unless)-import Data.Maybe (isJust, isNothing)+import TestHelpers (runTestSuite+                   , testJust+                   , testEqv2)  ------------------------------------------------------------ --  Test case helpers ------------------------------------------------------------ +{- assertMember :: (Eq a, Show a) => String -> a -> [a] -> Assertion assertMember preface expected actual =   unless (expected `elem` actual ) (assertFailure msg)@@ -124,9 +120,13 @@ testEqv :: (Eq a, Show a) => String -> [a] -> [a] -> Test testEqv = testMaker ListTest "Eqv" +-}+ testEqvEqv :: (Eq a, Show a) => String -> [[a]] -> [[a]] -> Test-testEqvEqv = testMaker (ListTest . map ListTest) "EqvEqv"+testEqvEqv = testEqv2+-- testEqvEqv = testMaker (ListTest . map ListTest) "EqvEqv" +{- testHasEqv :: (Eq a, Show a) => String -> [a] -> [[a]] -> Test testHasEqv lab a1 a2 =     TestCase ( assertMember ("testHasEqv:"++lab) ma1 ma2 )@@ -136,6 +136,7 @@  testMaybeEqv :: (Eq a, Show a) => String -> Maybe [a] -> Maybe [a] -> Test testMaybeEqv = testMaker MaybeListTest "MaybeEqv"+-}  ------------------------------------------------------------ --  Test finding built-in variable binding modifiers@@ -245,7 +246,7 @@     ]  main :: IO ()-main = runTestTT allTests >> return ()+main = runTestSuite allTests  {- runTestFile :: Test -> IO ()
tests/GraphPartitionTest.hs view
@@ -29,13 +29,17 @@ import Swish.Utils.ListHelpers (equiv)  import Test.HUnit (Test(TestCase, TestList),-                   assertEqual, assertBool,-                   runTestTT)+                   assertEqual, assertBool) +import TestHelpers (runTestSuite+                    , testEq, testNe+                    )+ ------------------------------------------------------------ --  Test case helpers ------------------------------------------------------------ +{- testEq :: (Eq a, Show a) => String -> a -> a -> Test testEq lab a1 a2 =     TestCase ( assertEqual ("testEq:"++lab) a1 a2 )@@ -43,6 +47,7 @@ testNe :: (Eq a, Show a) => String -> a -> a -> Test testNe lab a1 a2 =     TestCase ( assertBool ("testNe:"++lab) (a1 /= a2) )+-}  -- Compare lists and lists of lists and Maybe lists for set equivalence: @@ -538,7 +543,7 @@     ]  main :: IO ()-main = runTestTT allTests >> return ()+main = runTestSuite allTests  {- runTestFile t = do
tests/GraphTest.hs view
@@ -27,8 +27,7 @@  import Test.HUnit       ( Test(TestCase,TestList,TestLabel),-        assertEqual, assertBool,-        runTestTT )+        assertEqual, assertBool )  import Data.List (sort, elemIndex) import Data.Maybe (fromJust)@@ -50,6 +49,8 @@       ) import Swish.Utils.LookupMap (LookupEntryClass(..), makeLookupMap) +import TestHelpers (runTestSuite, testEq, testEqv)+ default ( Int )  ------------------------------------------------------------@@ -82,12 +83,18 @@ ------------------------------------------------------------  testeq :: (Show a, Eq a) => String -> a -> a -> Test+testeq = testEq+{- testeq lab req got =     TestCase ( assertEqual ("test"++lab) req got )+-}  testeqv :: (Show a, Eq a) => String -> [a] -> [a] -> Test+testeqv = testEqv+{- testeqv lab req got =     TestCase ( assertEqual ("test"++lab) True (req `equiv` got) )+-}  ------------------------------------------------------------ --  Label map and entry creation helpers@@ -379,11 +386,11 @@  testLabelEqSuite :: Test testLabelEqSuite = TestList-  [ testLabelEq (testLab a b) (testEq  a b) n1 n2+  [ testLabelEq (testLab a b) (nodeTest a b) n1 n2       | (a,n1) <- nodelist , (b,n2) <- nodelist ]     where     testLab a b = a ++ "-" ++ b-    testEq  a b = (a == b)        ||+    nodeTest  a b = (a == b)        ||             (a,b) `elem` nodeeqlist ||             (b,a) `elem` nodeeqlist @@ -418,11 +425,11 @@     where     testLab a b = a ++ "-" ++ b     testOrd a b-      | testEq a b = EQ+      | nodeTest a b = EQ       | otherwise  = comparing fromJust                      (elemIndex a nodeorder)                      (elemIndex b nodeorder)-    testEq  a b = (a == b)        ||+    nodeTest a b = (a == b)        ||             (a,b) `elem` nodeeqlist ||             (b,a) `elem` nodeeqlist @@ -484,11 +491,11 @@  testStmtEqSuite :: Test testStmtEqSuite = TestList-  [ testStmtEq (testLab a b) (testEq  a b) t1 t2+  [ testStmtEq (testLab a b) (nodeTest  a b) t1 t2       | (a,t1) <- tlist , (b,t2) <- tlist ]     where     testLab a b = a ++ "-" ++ b-    testEq  a b = (a == b)        ||+    nodeTest  a b = (a == b)        ||             (a,b) `elem` stmteqlist ||             (b,a) `elem` stmteqlist @@ -1230,11 +1237,11 @@  testGraphEqSuite :: Test testGraphEqSuite = TestLabel "testGraphEqSuite" $ TestList-  [ testGraphEq (testLab ll1 ll2) (testEq ll1 ll2) gg1 gg2+  [ testGraphEq (testLab ll1 ll2) (nodeTest ll1 ll2) gg1 gg2       | (ll1,gg1) <- glist , (ll2,gg2) <- glist ]     where     testLab ll1 ll2 = ll1 ++ "-" ++ ll2-    testEq  ll1 ll2 = (ll1 == ll2)        ||+    nodeTest  ll1 ll2 = (ll1 == ll2)        ||             (ll1,ll2) `elem` grapheqlist ||             (ll2,ll1) `elem` grapheqlist @@ -1851,7 +1858,7 @@   ]  main :: IO ()-main = runTestTT allTests >> return ()+main = runTestSuite allTests  {- runTestFile t = do
tests/LookupMapTest.hs view
@@ -32,15 +32,15 @@     , mapTranslateEntries, mapTranslateEntriesM     ) -import Swish.Utils.ListHelpers-    ( equiv )- import Data.List ( sort ) -import Test.HUnit-    ( Test(TestCase,TestList)-    , assertEqual, runTestTT )+import Test.HUnit ( Test(TestList) ) +import TestHelpers (runTestSuite+                    , testEq+                    , testEqv+                    )+   ------------------------------------------------------------ --  Declare lookup entry for testing ------------------------------------------------------------@@ -70,12 +70,18 @@ ------------------------------------------------------------  testeq :: (Show a, Eq a) => String -> a -> a -> Test+testeq = testEq+{- testeq lab req got =     TestCase ( assertEqual ("test"++lab) req got )+-}  testeqv :: (Show a, Eq a) => String -> [a] -> [a] -> Test+testeqv = testEqv+{- testeqv lab req got =     TestCase ( assertEqual ("test"++lab) True (req `equiv` got) )+-}  ------------------------------------------------------------ --  LookupMap functions@@ -327,11 +333,11 @@  testMapEqSuite :: Test testMapEqSuite = TestList-  [ testMapEq (testLab l1 l2) (testEq l1 l2) m1 m2+  [ testMapEq (testLab l1 l2) (nodeTest l1 l2) m1 m2       | (l1,m1) <- maplist , (l2,m2) <- maplist ]     where     testLab l1 l2 = l1 ++ "-" ++ l2-    testEq  l1 l2 = (l1 == l2)       ||+    nodeTest  l1 l2 = (l1 == l2)       ||             (l1,l2) `elem` mapeqlist ||             (l2,l1) `elem` mapeqlist @@ -455,7 +461,7 @@   ]  main :: IO ()-main = runTestTT allTests >> return ()+main = runTestSuite allTests  {- runTestFile t = do
tests/N3FormatterTest.hs view
@@ -59,19 +59,17 @@  import Test.HUnit     ( Test(TestCase,TestList)-    , assertEqual, runTestTT )+    , assertEqual ) ----------------------------------------------------------------  Common test wrappers-------------------------------------------------------------+import TestHelpers (runTestSuite, testCompareEq) +-- Specialized equality comparisons+ testLabelEq :: String -> Bool -> RDFLabel -> RDFLabel -> Test-testLabelEq lab eq n1 n2 =-    TestCase ( assertEqual ("testLabelEq:"++lab) eq (n1==n2) )+testLabelEq = testCompareEq "testLabelEq:"  testGraphEq :: String -> Bool -> RDFGraph -> RDFGraph -> Test-testGraphEq lab eq gg1 gg2 =-    TestCase ( assertEqual ("testGraphEq:"++lab) eq (gg1==gg2) )+testGraphEq = testCompareEq "testGraphEq:"  ------------------------------------------------------------ --  Define some common values@@ -1482,7 +1480,7 @@   ]  main :: IO ()-main = runTestTT allTests >> return ()+main = runTestSuite allTests  {- runTestFile t = do
tests/N3ParserTest.hs view
@@ -59,7 +59,7 @@ import Swish.Utils.QName (QName, qnameFromURI) import Swish.Utils.LookupMap (LookupMap(..)) -import Test.HUnit (Test(TestCase,TestList), assertEqual, runTestTT)+import Test.HUnit (Test(TestCase,TestList), assertEqual)  import Network.URI (URI, nullURI, parseURIReference) @@ -71,6 +71,16 @@ import qualified Data.Text.Lazy as L import qualified Data.Text.Lazy.Builder as B +import TestHelpers (runTestSuite, testCompareEq)++-- Specialized equality comparisons++testLabelEq :: String -> Bool -> RDFLabel -> RDFLabel -> Test+testLabelEq = testCompareEq "testLabelEq:"++testGraphEq :: String -> Bool -> RDFGraph -> RDFGraph -> Test+testGraphEq = testCompareEq "testGraphEq:"+ toURI :: String -> URI toURI s = fromMaybe (error ("Internal error: invalid uri=" ++ s)) (parseURIReference s) @@ -101,14 +111,6 @@ --  Common test wrappers ------------------------------------------------------------ -testLabelEq :: String -> Bool -> RDFLabel -> RDFLabel -> Test-testLabelEq lab eq n1 n2 =-    TestCase ( assertEqual ("testLabelEq:"++lab) eq (n1==n2) )--testGraphEq :: String -> Bool -> RDFGraph -> RDFGraph -> Test-testGraphEq lab eq gg1 gg2 =-    TestCase ( assertEqual ("testGraphEq:"++lab) eq (gg1==gg2) )- parseTestBase :: String -> Maybe QName -> String -> B.Builder -> RDFGraph -> String -> Test parseTestBase lbl1 mbase lbl2 inp gr er =     TestList@@ -1500,7 +1502,7 @@   ]  main :: IO ()-main = runTestTT allTests >> return ()+main = runTestSuite allTests  -------------------------------------------------------------------------------- --
tests/NTTest.hs view
@@ -36,10 +36,12 @@  import Test.HUnit     ( Test(TestCase,TestList)-    , assertEqual, runTestTT )+    , assertEqual )  import qualified Data.Text.Lazy as T +import TestHelpers (runTestSuite)+ ------------------------------------------------------------ --  Parser tests ------------------------------------------------------------@@ -256,7 +258,7 @@   ]    main :: IO ()  -main = runTestTT allTests >> return ()+main = runTestSuite allTests  -------------------------------------------------------------------------------- --
tests/QNameTest.hs view
@@ -29,13 +29,18 @@     )  import Swish.Utils.Namespace (makeQNameScopedName, getQName, getScopedNameURI)-import Test.HUnit (Test(TestCase,TestList), assertEqual, runTestTT)+import Test.HUnit (Test(TestList))  import Network.URI (URI, parseURIReference) import Data.Maybe (fromJust)  import qualified Data.Text as T +import TestHelpers (runTestSuite+                    , testCompare+                    , testCompareEq+                   )+ ------------------------------------------------------------ --  Define some common values ------------------------------------------------------------@@ -69,15 +74,13 @@ qb2st2 = newQName base2 "st2" qb3st3 = newQName base3 "st3" -testIsEq :: (Show a, Eq a) => String -> String -> a -> a -> Test-testIsEq lbl1 lbl2 a b = TestCase (assertEqual (lbl1++":"++lbl2) a b)- ------------------------------------------------------------ --  QName equality tests ------------------------------------------------------------  testQNameEq :: String -> Bool -> QName -> QName -> Test-testQNameEq lbl eq n1 n2 = testIsEq "QNameEq" lbl eq (n1==n2)+testQNameEq = testCompareEq "QNameEq"+-- testQNameEq lbl eq n1 n2 = testIsEq "QNameEq" lbl eq (n1==n2)  qnlist :: [(String, QName)] qnlist =@@ -145,13 +148,13 @@ ------------------------------------------------------------  testStringEq :: String -> String -> String -> Test-testStringEq = testIsEq "StringEq"+testStringEq = testCompare "StringEq"  testTextEq :: String -> T.Text -> T.Text -> Test-testTextEq = testIsEq "TextEq"+testTextEq = testCompare "TextEq"  testURIEq :: String -> String -> URI -> Test-testURIEq lbl uri = testIsEq "URIEq" lbl (toURI uri)+testURIEq lbl uri = testCompare "URIEq" lbl (toURI uri)  testPartQNameSuite :: Test testPartQNameSuite = @@ -195,7 +198,7 @@ ------------------------------------------------------------  testMaybeQNameEq :: String -> Bool -> Maybe QName -> Maybe QName -> Test-testMaybeQNameEq lbl eq n1 n2 = testIsEq "MaybeQName" lbl eq (n1==n2)+testMaybeQNameEq lbl eq n1 n2 = testCompareEq "MaybeQName" lbl eq n1 n2  testMaybeQNameEqSuite :: Test testMaybeQNameEqSuite = @@ -217,7 +220,7 @@ ------------------------------------------------------------  testQNameLe :: String -> Bool -> QName -> QName -> Test-testQNameLe lbl le n1 n2 = testIsEq "QNameLE" lbl le (n1 <= n2)+testQNameLe lbl le n1 n2 = testCompare "QNameLE" lbl le (n1 <= n2)  testQNameLeSuite :: Test testQNameLeSuite = @@ -279,8 +282,8 @@   let qn = newQName (toURI a) b   in     TestList-   [ testIsEq lbl ":split" qn ((qnameFromURI . toURI) input)-   , testIsEq lbl ":show"  input (show (getQNameURI qn))+   [ testCompare lbl ":split" qn ((qnameFromURI . toURI) input)+   , testCompare lbl ":show"  input (show (getQNameURI qn))    ]  testSplitURISuite :: Test@@ -334,8 +337,8 @@       qn = qnameFromURI u       sn = makeQNameScopedName Nothing qn   in TestList-     [ testIsEq "SQ:URI"   lbl u  (getScopedNameURI sn)-     , testIsEq "SQ:Qname" lbl qn (getQName sn)+     [ testCompare "SQ:URI"   lbl u  (getScopedNameURI sn)+     , testCompare "SQ:Qname" lbl qn (getQName sn)      ]  testSNameTTSuite :: Test@@ -377,7 +380,7 @@   ]  main :: IO ()-main = runTestTT allTests >> return ()+main = runTestSuite allTests  {- runTestFile t = do
tests/RDFDatatypeXsdIntegerTest.hs view
@@ -54,105 +54,23 @@ import Swish.Utils.Namespace (getNamespaceURI, ScopedName, makeScopedName, makeNSScopedName) import Swish.RDF.Vocabulary (namespaceDefault) import Swish.Utils.LookupMap (LookupMap(..), mapFindMaybe)-import Swish.Utils.ListHelpers (equiv)  import Test.HUnit     ( Test(TestCase,TestList)-    , Assertion-    , assertBool, assertEqual, assertFailure-    , runTestTT+      , assertFailure     )  import Network.URI (URI, parseURI) -import Control.Monad (unless) import Data.Monoid (Monoid(..))-import Data.Maybe (isJust, isNothing, fromMaybe, fromJust)+import Data.Maybe (fromMaybe, fromJust) import Data.List (intersperse)  import qualified Data.Text as T import qualified Data.Text.Lazy.Builder as B ----------------------------------------------------------------  Test case helpers---------------------------------------------------------------assertMember :: (Eq a, Show a) => String -> a -> [a] -> Assertion-assertMember preface expected actual =-  unless (expected `elem` actual ) (assertFailure msg)-  where msg = (if null preface then "" else preface ++ "\n") ++-             "expected: " ++ show expected ++ "\nbut got: " ++ show actual--test :: String -> Bool -> Test-test lab bv =-    TestCase ( assertBool ("test:"++lab) bv )--testEq :: (Eq a, Show a) => String -> a -> a -> Test-testEq lab a1 a2 =-    TestCase ( assertEqual ("testEq:"++lab) a1 a2 )--testElem :: (Eq a, Show a) => String -> a -> [a] -> Test-testElem lab a1 as =-    TestCase ( assertMember ("testElem:"++lab) a1 as )--testLe :: (Ord a, Show a) => String -> Bool -> a -> a -> Test-testLe lab eq a1 a2 =-    TestCase ( assertEqual ("testLe:"++lab) eq (a1<=a2) )---- Test for Just x or Nothing--testJust :: String -> Maybe a -> Test-testJust lab av =-    TestCase ( assertBool ("testJust:"++lab) (isJust av) )--testNothing :: String -> Maybe a -> Test-testNothing lab av =-    TestCase ( assertBool ("testJust:"++lab) (isNothing av) )---- Compare lists and lists of lists and Maybe lists for set equivalence:--data ListTest a = ListTest [a]--instance (Eq a) => Eq (ListTest a) where-    (ListTest a1) == (ListTest a2) = a1 `equiv` a2--instance (Show a) => Show (ListTest a) where-    show (ListTest a) = show a--data MaybeListTest a = MaybeListTest (Maybe [a])--instance (Eq a) => Eq (MaybeListTest a) where-    MaybeListTest (Just a1) == MaybeListTest (Just a2) = a1 `equiv` a2-    MaybeListTest Nothing   == MaybeListTest Nothing   = True-    _                       == _                       = False--instance (Show a) => Show (MaybeListTest a) where-    show (MaybeListTest a) = show a--testEqv :: (Eq a, Show a) => String -> [a] -> [a] -> Test-testEqv lab a1 a2 =-    TestCase ( assertEqual ("testEqv:"++lab) (ListTest a1) (ListTest a2) )--testEqvEqv :: (Eq a, Show a) => String -> [[a]] -> [[a]] -> Test-testEqvEqv lab a1 a2 =-    TestCase ( assertEqual ("testEqvEqv:"++lab) ma1 ma2 )-    where-        ma1 = ListTest $ map ListTest a1-        ma2 = ListTest $ map ListTest a2--testHasEqv :: (Eq a, Show a) => String -> [a] -> [[a]] -> Test-testHasEqv lab a1 a2 =-    TestCase ( assertMember ("testHasEqv:"++lab) ma1 ma2 )-    where-        ma1 = ListTest a1-        ma2 = map ListTest a2--testMaybeEqv :: (Eq a, Show a) => String -> Maybe [a] -> Maybe [a] -> Test-testMaybeEqv lab a1 a2 =-    TestCase ( assertEqual ("testMaybeEqv:"++lab) ma1 ma2 )-    where-        ma1 = MaybeListTest a1-        ma2 = MaybeListTest a2+import TestHelpers (runTestSuite+                   , testEq, testElem, testEqv, testEqv2)  ------------------------------------------------------------ --  Misc values@@ -848,7 +766,7 @@         antgr   = mkGraph antstr         pregrss = map (map mkGraph) prestrss     in-        testEqvEqv lab pregrss $ bwdApply rule antgr+        testEqv2 lab pregrss $ bwdApply rule antgr testRuleBwd lab Nothing _ _ = TestCase $     assertFailure $ "testRuleBwd:"++lab++", null rule supplied" @@ -1419,7 +1337,7 @@     ]  main :: IO ()-main = runTestTT allTests >> return ()+main = runTestSuite allTests  {- trules = runTestTT testDatatypeRuleSuite
tests/RDFGraphTest.hs view
@@ -73,23 +73,12 @@ import Test.HUnit     ( Test(TestCase,TestList,TestLabel)     , Assertion-    , assertBool, assertEqual-    , runTestTT )-----------------------------------------------------------------  Common definitions---------------------------------------------------------------testCompare :: (Eq a, Show a) => String -> String -> a -> a -> Test-testCompare typ lab a1 a2 =-    TestCase ( assertEqual (typ++lab) a1 a2 )--testCompareEq :: (Eq a, Show a) => String -> String -> Bool -> a -> a -> Test-testCompareEq typ lab eq a1 a2 =-    TestCase ( assertEqual (typ++lab) eq (a1==a2) )+    , assertBool, assertEqual ) -testEq :: (Eq a, Show a) => String -> a -> a -> Test-testEq = testCompare "testEq"+import TestHelpers ( runTestSuite+                     , testEq+                     , testCompare+                     , testCompareEq)  ------------------------------------------------------------ --  Test language tag comparisons@@ -1581,7 +1570,7 @@   ]  main :: IO ()-main = runTestTT allTests >> return ()+main = runTestSuite allTests  {- runTestFile t = do
tests/RDFProofContextTest.hs view
@@ -50,43 +50,27 @@  import Test.HUnit     ( Test(TestCase,TestList)-    , assertBool, assertEqual-    , runTestTT )+    , assertBool, assertEqual )  import Network.URI (URI, parseURI)  import Data.Monoid (Monoid(..))-import Data.Maybe (isJust, isNothing, fromJust, fromMaybe)+import Data.Maybe (isNothing, fromJust, fromMaybe)  import qualified Data.Text as T import qualified Data.Text.Lazy.Builder as B ---  misc helpers--test :: String -> Bool -> Test-test lab tst = TestCase $ assertBool lab tst--testEq :: (Eq a, Show a) => String -> a -> a -> Test-testEq lab e a = TestCase $ assertEqual lab e a--testJe :: (Eq a, Show a) => String -> a -> Maybe a -> Test-testJe lab e a = TestList-    [ TestCase $ assertBool  lab (isJust a)-    , TestCase $ assertEqual lab e (fromJust a)-    ]--testJl :: (Eq a, Show a) => String -> Int -> Maybe [a] -> Test-testJl lab e a = TestList-    [ TestCase $ assertBool  lab   (isJust a)-    , TestCase $ assertEqual lab e (length (fromJust a))-    ]+import TestHelpers ( runTestSuite+                     , test+                     , testEq+                     , testElem+                     ) -testNo :: (Eq a, Show a) => String -> [[a]] -> Test-testNo lab a =-    TestCase $ assertBool  lab   (null a)+--  misc helpers  testGr :: String -> RDFGraph -> [RDFGraph] -> Test-testGr lab eg a = TestCase $ assertBool lab (eg `elem` a)+testGr = testElem+-- testGr lab eg a = TestCase $ assertBool lab (eg `elem` a)  -- testProof "rdfProof01" True rdfProof01 testProof :: String -> Bool -> RDFProof -> Test@@ -499,7 +483,7 @@                 "ex:s1 rdf:_123 ex:o ."                  rdfStep08a :: RDFProofStep                -rdfStep08a = makeRDFProofStep (getRule "rs_rdfs:cp1") [rdfBase08]  rdfCons08a+rdfStep08a = makeRDFProofStep (getRule "rs_rdfs:cp11") [rdfBase08]  rdfCons08a  rdfCons08a :: RDFFormula rdfCons08a = makeFormula scopeex "rdfCons08a" @@ -675,7 +659,7 @@   ]  main :: IO ()-main = runTestTT allTests >> return ()+main = runTestSuite allTests  {- runTestFile t = do
tests/RDFProofTest.hs view
@@ -52,45 +52,26 @@ import Swish.RDF.Rule (Rule(..)) import Swish.Utils.Namespace (Namespace, makeNamespace, ScopedName, makeNSScopedName) -import Test.HUnit-    ( Test(TestCase,TestList)-    , assertBool, assertEqual-    , runTestTT )+import Test.HUnit ( Test(TestList) )  import Network.URI (URI, parseURI)  import Data.Monoid (Monoid(..))-import Data.Maybe (isJust, fromJust)+import Data.Maybe (fromJust)  import qualified Data.Text as T import qualified Data.Text.Lazy.Builder as B ---  misc helpers--test :: String -> Bool -> Test-test lab tst = TestCase $ assertBool lab tst--testEq :: (Eq a, Show a) => String -> a -> a -> Test-testEq lab e a = TestCase $ assertEqual lab e a--testJe :: (Eq a, Show a) => String -> a -> Maybe a -> Test-testJe lab e a = TestList-    [ TestCase $ assertBool  lab (isJust a)-    , TestCase $ assertEqual lab e (fromJust a)-    ]--testJl :: (Eq a, Show a) => String -> Int -> Maybe [a] -> Test-testJl lab e a = TestList-    [ TestCase $ assertBool  lab   (isJust a)-    , TestCase $ assertEqual lab e (length (fromJust a))-    ]+import TestHelpers ( runTestSuite+                     , test+                     , testEq, testElem+                     , testNo+                   ) -testNo :: (Eq a, Show a) => String -> [[a]] -> Test-testNo lab a =-    TestCase $ assertBool  lab   (null a)+--  misc helpers  testIn :: (Eq a, Show a) => String -> a -> [a] -> Test-testIn lab eg a = TestCase $ assertBool lab (eg `elem` a)+testIn = testElem -- lab eg a = TestCase $ assertBool lab (eg `elem` a)  mkGr :: B.Builder -> [B.Builder] -> RDFGraph mkGr pr bdy = makeRDFGraphFromN3Builder $ mconcat (pr : bdy)@@ -1066,7 +1047,7 @@   ]  main :: IO ()-main = runTestTT allTests >> return ()+main = runTestSuite allTests  {- runTestFile t = do
tests/RDFQueryTest.hs view
@@ -56,12 +56,8 @@ import Swish.Utils.Namespace (getNamespaceURI, ScopedName, makeScopedName) import Swish.RDF.Vocabulary (namespaceRDF, langName, swishName, rdfType, rdfXMLLiteral) import Swish.RDF.N3Parser (parseN3)-import Swish.Utils.ListHelpers (equiv) -import Test.HUnit-    ( Test(TestCase,TestList)-    , assertBool, assertEqual-    , runTestTT )+import Test.HUnit ( Test(TestList) )  import qualified Data.Text.Lazy.Builder as B @@ -70,27 +66,22 @@ import Data.Monoid (Monoid(..)) import Data.Maybe (fromJust) +import TestHelpers (runTestSuite+                   , test+                   , testEq+                   , testElem+                   , testEqv+                   )+ ------------------------------------------------------------ --  misc helpers ------------------------------------------------------------ -newtype Set a = Set [a] deriving Show--instance (Eq a) => Eq (Set a) where-    Set v1 == Set v2 = v1 `equiv` v2--test :: String -> Bool -> Test-test lab tst = TestCase $ assertBool lab tst--testEq :: (Eq a, Show a) => String -> a -> a -> Test-testEq lab e a = TestCase $ assertEqual lab e a- testLs :: (Eq a, Show a) => String -> [a] -> [a] -> Test-testLs lab e a = TestCase $ assertEqual lab (Set e) (Set a)+testLs = testEqv  testGr :: String -> B.Builder -> [RDFGraph] -> Test-testGr lab e a = TestCase $ assertBool lab (eg `elem` a)-    where eg = graphFromBuilder mempty e+testGr lab e = testElem lab (graphFromBuilder mempty e)  graphFromBuilder :: B.Builder -> B.Builder -> RDFGraph graphFromBuilder prefix body = @@ -99,20 +90,6 @@     Right gr -> gr     Left msg -> error msg     --- Compare lists for set equivalence:--data ListTest a = ListTest [a]--instance (Eq a) => Eq (ListTest a) where-    (ListTest a1) == (ListTest a2) = a1 `equiv` a2--instance (Show a) => Show (ListTest a) where-    show (ListTest a) = show a--testEqv :: (Eq a, Show a) => String -> [a] -> [a] -> Test-testEqv lab a1 a2 =-    TestCase ( assertEqual ("testEqv:"++lab) (ListTest a1) (ListTest a2) )- ------------------------------------------------------------ --  test1:  simple query qith URI, literal and blank nodes. ------------------------------------------------------------@@ -1674,7 +1651,7 @@   ]  main :: IO ()-main = runTestTT allTests >> return ()+main = runTestSuite allTests  {- runTestFile t = do
tests/RDFRulesetTest.hs view
@@ -59,8 +59,7 @@  import Test.HUnit     ( Test(TestCase,TestList)-    , assertBool, assertEqual-    , runTestTT+    , assertBool     )  import Network.URI (URI, parseURI)@@ -72,44 +71,69 @@ import qualified Data.Text as T import qualified Data.Text.Lazy.Builder as B +import TestHelpers (runTestSuite+                   , test+                   , testLe+                   , testCompare+                   , testCompareEq+                   , testMaker+                   )+import qualified TestHelpers as T+ ------------------------------------------------------------ --  Test case helpers ------------------------------------------------------------ -test :: String -> Bool -> Test-test lab bv =-    TestCase ( assertBool ("test:"++lab) bv )- testVal :: (Eq a, Show a) => String -> a -> a -> Test+testVal = testCompare "testVal:"+{- testVal lab a1 a2 =     TestCase ( assertEqual ("testVal:"++lab) a1 a2 )+-}  testEq :: (Eq a, Show a) => String -> Bool -> a -> a -> Test+testEq = testCompareEq "testIsEq:"+{- testEq lab eq a1 a2 =     TestCase ( assertEqual ("testEq:"++lab) eq (a1==a2) )+-}  testEqual :: (Eq a, Show a) => String -> a -> a -> Test+testEqual = T.testEq+{- testEqual lab a1 a2 =     TestCase ( assertEqual ("testEq:"++lab) a1 a2 )+-} +{- testLe :: (Ord a, Show a) => String -> Bool -> a -> a -> Test testLe lab eq a1 a2 =     TestCase ( assertEqual ("testLe:"++lab) eq (a1<=a2) )+-}  testStringEq :: String -> String -> String -> Test+testStringEq = testCompare "testStringEq:"+{- testStringEq lab s1 s2 =     TestCase ( assertEqual ("testStringEq:"++lab) s1 s2 )+-}  testSameNamespace :: String -> Namespace -> Namespace -> Test+testSameNamespace = testMaker getNamespaceTuple "testSameNamespace:"+{- testSameNamespace lab n1 n2 =     TestCase ( assertBool ("testSameNamespace:"++lab) ((p1==p2)&&(u1==u2)) )     where       (p1, u1) = getNamespaceTuple n1       (p2, u2) = getNamespaceTuple n2+-}  testScopedNameEq :: String -> Bool -> ScopedName -> ScopedName -> Test+testScopedNameEq = testCompareEq "testScopedNameEq:"+{- testScopedNameEq lab eq n1 n2 =     TestCase ( assertEqual ("testScopedNameEq:"++lab) eq (n1==n2) )+-}  {- testQNameEq :: String -> Bool -> QName -> QName -> Test@@ -366,7 +390,7 @@   ]  main :: IO ()-main = runTestTT allTests >> return ()+main = runTestSuite allTests  {- runTestFile t = do
+ tests/TestHelpers.hs view
@@ -0,0 +1,245 @@+--------------------------------------------------------------------------------+--  See end of this file for licence information.+--------------------------------------------------------------------------------+-- |+--  Module      :  TestHelpers+--  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 test case helper functions, providing a range of+--  commonly-used test cases.+--+--------------------------------------------------------------------------------++module TestHelpers+       ( +         runTestSuite+         , test+         , testCompare, testCompareEq+         , testEq, testNe, testLe, testGe+         , testElem+         , testJust, testNothing +         , testJe, testJl, testNo+         , testEqv, testEqv2, testHasEqv, testMaybeEqv+         , testMaker                                 +         )+       where++import System.Exit (exitSuccess, exitFailure)++import Test.HUnit (Test(TestCase, TestList)+                   , runTestTT+                   , Assertion+                   , assertBool, assertEqual, assertFailure+                   )+import Test.HUnit.Base (Counts(..))++import Control.Monad (unless)++import Data.Maybe (isJust, isNothing, fromJust)++import Swish.Utils.ListHelpers (equiv)++-- | Run the test suite and exit the process with either +-- @exitSuccess@ or @exitFailure@.+runTestSuite :: Test -> IO ()+runTestSuite tests = do+  Counts ncases ntried nerrors nfailures <- runTestTT tests+  -- It's not clear whether all these checks are needed but leave in+  if ntried == ncases && nerrors == nfailures && nerrors == 0+    then exitSuccess+    else exitFailure++------------------------------------------------------------+--  Test case helpers+------------------------------------------------------------++assertMember :: (Eq a, Show a) => String -> a -> [a] -> Assertion+assertMember preface expected actual =+  unless (expected `elem` actual ) (assertFailure msg)+  where msg = (if null preface then "" else preface ++ "\n") +++             "expected: " ++ show expected ++ "\nbut got: " ++ show actual++test :: String -> Bool -> Test+test lab = TestCase . assertBool ("test:"++lab)++testCompare :: (Eq a, Show a) => String -> String -> a -> a -> Test+testCompare typ lab a1 a2 =+    TestCase ( assertEqual (typ++lab) a1 a2 )++testCompareEq :: (Eq a, Show a) => String -> String -> Bool -> a -> a -> Test+testCompareEq typ lab eq a1 a2 =+    TestCase ( assertEqual (typ++lab) eq (a1==a2) )++testMaker :: (Show b, Eq b) => (a -> b) -> String -> String -> a -> a -> Test+testMaker conv l1 l2 x y =+  TestCase (assertEqual (l1 ++ ":" ++ l2) (conv x) (conv y))++testEq :: (Eq a, Show a) => String -> a -> a -> Test+testEq = testCompare "testEq:"++testNe :: (Eq a, Show a) => String -> a -> a -> Test+testNe lab a1 a2 =+    TestCase ( assertBool ("testNe:"++lab) (a1 /= a2) )++testLe :: (Ord a, Show a) => String -> Bool -> a -> a -> Test+testLe lab eq a1 a2 =+    TestCase ( assertEqual ("testLe:"++lab) eq (a1<=a2) )++testGe :: (Ord a, Show a) => String -> Bool -> a -> a -> Test+testGe lab eq a1 a2 =+    TestCase ( assertEqual ("testGe:"++lab) eq (a1>=a2) )++-- Test for Just x or Nothing++testJust :: String -> Maybe a -> Test+testJust lab = TestCase . assertBool ("testJust:"++lab) . isJust++testNothing :: String -> Maybe a -> Test+testNothing lab = TestCase . assertBool ("testJust:"++lab) . isNothing++testJe :: (Eq a, Show a) => String -> a -> Maybe a -> Test+testJe lab e a = TestList+    [ TestCase $ assertBool  lab (isJust a)+    , TestCase $ assertEqual lab e (fromJust a)+    ]++testJl :: (Eq a, Show a) => String -> Int -> Maybe [a] -> Test+testJl lab e a = TestList+    [ TestCase $ assertBool  lab   (isJust a)+    , TestCase $ assertEqual lab e (length (fromJust a))+    ]++testNo :: (Eq a, Show a) => String -> [[a]] -> Test+testNo lab a =+    TestCase $ assertBool  lab   (null a)++-- Test for list membership++testElem :: (Eq a, Show a) => String -> a -> [a] -> Test+testElem lab a1 as = TestCase ( assertMember ("testElem:"++lab) a1 as )++-- Compare lists and lists of lists and Maybe lists for set equivalence:++data ListTest a = ListTest [a]++instance (Eq a) => Eq (ListTest a) where+    (ListTest a1) == (ListTest a2) = a1 `equiv` a2++instance (Show a) => Show (ListTest a) where+    show (ListTest a) = show a++data MaybeListTest a = MaybeListTest (Maybe [a])++instance (Eq a) => Eq (MaybeListTest a) where+    MaybeListTest (Just a1) == MaybeListTest (Just a2) = a1 `equiv` a2+    MaybeListTest Nothing   == MaybeListTest Nothing   = True+    _                       == _                       = False++instance (Show a) => Show (MaybeListTest a) where+    show (MaybeListTest a) = show a++testEqv :: (Eq a, Show a) => String -> [a] -> [a] -> Test+testEqv = testMaker ListTest "testEqv" ++testEqv2 :: (Eq a, Show a) => String -> [[a]] -> [[a]] -> Test+testEqv2 = testMaker (ListTest . map ListTest) "testEqv2"++testHasEqv :: (Eq a, Show a) => String -> [a] -> [[a]] -> Test+testHasEqv lab a1 a2 =+    TestCase ( assertMember ("testHasEqv:"++lab) ma1 ma2 )+    where+        ma1 = ListTest a1+        ma2 = map ListTest a2++testMaybeEqv :: (Eq a, Show a) => String -> Maybe [a] -> Maybe [a] -> Test+testMaybeEqv = testMaker MaybeListTest "testMaybeEqv"++{-++------------------------------------------------------------+--  Test suites for the above+------------------------------------------------------------++testSuccessSuite = TestList+    [ test          "01" True+    , testEq        "02" 2 2+    , testLe        "03" 1 2+    , testLe        "04" 2 2+    , testGe        "05" 3 2+    , testGe        "07" 2 2+    , testJust      "08" (Just "08")+    , testNothing   "09" (Nothing :: Maybe String)+    , testElem      "10" 'b' "abc"+    , testEqv       "11" "abc" "bca"+    , testEqv       "12" "abc" "bbccaa"+    , testEqv2      "13" ["abc","def","ghi"] ["fed","ghi","bca"]+    , testHasEqv    "14" "abc"               ["fed","ghi","bca"]+    , testHasEqv    "15" "ghi"               ["fed","ghi","bca"]+    , testHasEqv    "16" "def"               ["fed","ghi","bca"]+    , testMaybeEqv  "17" (Just "abc") (Just "bca")+    , testMaybeEqv  "18" Nothing      (Nothing :: Maybe String)+    ]++-- All of these tests should be failures:+-- Look for number of failures == total number of tests+testFailureSuite = TestList+    [ test          "01" False+    , testEq        "02" 2 22+    , testLe        "03" 2 1+    , testGe        "04" 2 3+    , testJust      "05" (Nothing :: Maybe String)+    , testNothing   "06" (Just "09")+    , testElem      "07" 'd' "abc"+    , testEqv       "08" "abd" "bca"+    , testEqv2      "09" ["abd","def","ghi"] ["fed","ghi","bca"]+    , testHasEqv    "10" "abd"               ["fed","ghi","bca"]+    , testMaybeEqv  "11" (Just "abc") (Just "bda")+    , testMaybeEqv  "12" Nothing      (Just "bda")+    ]+-}++------------------------------------------------------------+--  All tests+------------------------------------------------------------++{-+allSuccessTests = TestList+    [ testSuccessSuite+    ]++allFailureTests = TestList+    [ testFailureSuite+    ]++mainS = runTestTT allSuccessTests+mainF = runTestTT allFailureTests+-}++--------------------------------------------------------------------------------+--+--  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+--+--------------------------------------------------------------------------------
tests/VarBindingTest.hs view
@@ -33,101 +33,21 @@     , varFilterEQ, varFilterNE     ) -import Swish.RDF.Vocabulary-    ( swishName )--import Swish.Utils.ListHelpers-    ( equiv )+import Swish.RDF.Vocabulary (swishName) -import Test.HUnit-    ( Test(TestCase,TestList)-    , Assertion-    , assertBool, assertEqual, assertFailure-    , runTestTT-    )+import Test.HUnit (Test(TestList)) -import Control.Monad (unless) import Data.List (union, intersect) import Data.Maybe (isJust, isNothing, fromJust)  -- import qualified Data.Text as T ----------------------------------------------------------------  Test case helpers---------------------------------------------------------------assertMember :: (Eq a, Show a) => String -> a -> [a] -> Assertion-assertMember preface expected actual =-  unless (expected `elem` actual ) (assertFailure msg)-  where msg = (if null preface then "" else preface ++ "\n") ++-             "expected: " ++ show expected ++ "\nbut got: " ++ show actual--test :: String -> Bool -> Test-test lab bv =-    TestCase ( assertBool ("test:"++lab) bv )--testEq :: (Eq a, Show a) => String -> a -> a -> Test-testEq lab a1 a2 =-    TestCase ( assertEqual ("testEq:"++lab) a1 a2 )--testLe :: (Ord a, Show a) => String -> Bool -> a -> a -> Test-testLe lab eq a1 a2 =-    TestCase ( assertEqual ("testLe:"++lab) eq (a1<=a2) )---- Test for Just x or Nothing--testJust :: String -> Maybe a -> Test-testJust lab av =-    TestCase ( assertBool ("testJust:"++lab) (isJust av) )--testNothing :: String -> Maybe a -> Test-testNothing lab av =-    TestCase ( assertBool ("testJust:"++lab) (isNothing av) )---- Compare lists and lists of lists and Maybe lists for set equivalence:--data ListTest a = ListTest [a]--instance (Eq a) => Eq (ListTest a) where-    (ListTest a1) == (ListTest a2) = a1 `equiv` a2--instance (Show a) => Show (ListTest a) where-    show (ListTest a) = show a--data MaybeListTest a = MaybeListTest (Maybe [a])--instance (Eq a) => Eq (MaybeListTest a) where-    MaybeListTest (Just a1) == MaybeListTest (Just a2) = a1 `equiv` a2-    MaybeListTest Nothing   == MaybeListTest Nothing   = True-    _                       == _                       = False--instance (Show a) => Show (MaybeListTest a) where-    show (MaybeListTest a) = show a--testEqv :: (Eq a, Show a) => String -> [a] -> [a] -> Test-testEqv lab a1 a2 =-    TestCase ( assertEqual ("testEqv:"++lab) (ListTest a1) (ListTest a2) )--testEqvEqv :: (Eq a, Show a) => String -> [[a]] -> [[a]] -> Test-testEqvEqv lab a1 a2 =-    TestCase ( assertEqual ("testEqvEqv:"++lab) ma1 ma2 )-    where-        ma1 = ListTest $ map ListTest a1-        ma2 = ListTest $ map ListTest a2--testHasEqv :: (Eq a, Show a) => String -> [a] -> [[a]] -> Test-testHasEqv lab a1 a2 =-    TestCase ( assertMember ("testHasEqv:"++lab) ma1 ma2 )-    where-        ma1 = ListTest a1-        ma2 = map ListTest a2--testMaybeEqv :: (Eq a, Show a) => String -> Maybe [a] -> Maybe [a] -> Test-testMaybeEqv lab a1 a2 =-    TestCase ( assertEqual ("testMaybeEqv:"++lab) ma1 ma2 )-    where-        ma1 = MaybeListTest a1-        ma2 = MaybeListTest a2+import TestHelpers (runTestSuite+                    , test+                    , testEq +                    , testEqv, testEqv2, testHasEqv, testMaybeEqv+                    , testJust, testNothing  +                    )  ------------------------------------------------------------ --  Define and variable bindings@@ -499,7 +419,7 @@                         vbmName (fromJust jvbmid1)    , testEqv "testVarCompose01" vbm34vocab $ vbmVocab vbm34-  , testEqvEqv "testVarCompose02" vbm34usage $ vbmUsage vbm34+  , testEqv2 "testVarCompose02" vbm34usage $ vbmUsage vbm34   , testMaybeEqv "testVarCompose03" (Just ["c","d"]) $      vbmCompatibility vbm34 ["a","b"]   , testMaybeEqv "testVarCompose04" (Just ["b","d"]) $@@ -528,7 +448,7 @@      vbmApply vbm34 [vbad,vbbd,vbcd]    , testEqv "testVarCompose21" vbm43vocab $ vbmVocab vbm43-  , testEqvEqv "testVarCompose22" vbm43usage $ vbmUsage vbm43+  , testEqv2 "testVarCompose22" vbm43usage $ vbmUsage vbm43   , testMaybeEqv "testVarCompose23" Nothing $      vbmCompatibility vbm43 ["a","b"]   , testMaybeEqv "testVarCompose24" (Just ["b","d"]) $@@ -560,7 +480,7 @@   , test   "testVarCompose42" (isNothing vbm65)   , test   "testVarCompose43" (isNothing vbm78)   , test   "testVarCompose44" (isJust    vbm87)-  , testEqvEqv "testVarCompose45" vbm87usage $+  , testEqv2 "testVarCompose45" vbm87usage $      vbmUsage (fromJust vbm87)    , test   "testVarCompose51" $ isJust jvbm1id@@ -870,7 +790,7 @@     ]  main :: IO ()-main = runTestTT allTests >> return ()+main = runTestSuite allTests  {- runTestFile t = do