swish-0.2.1: Swish/HaskellRDF/N3Parser.hs
--------------------------------------------------------------------------------
-- $Id: N3Parser.hs,v 1.36 2004/01/22 19:52:41 graham Exp $
--
-- Copyright (c) 2003, G. KLYNE. All rights reserved.
-- See end of this file for licence information.
--------------------------------------------------------------------------------
-- |
-- Module : N3Parser
-- Copyright : (c) 2003, Graham Klyne
-- License : GPL V2
--
-- Maintainer : Graham Klyne
-- Stability : provisional
-- Portability : H98
--
-- This Module implements a Notation 3 parser (see [1], [2]), returning a
-- new RDFGraph consisting of triples and namespace information parsed from
-- the supplied N3 input string, or an error indication.
--
-- Uses the Parsec monadic parser library.
--
--
-- REFERENCES:
--
-- [1] 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
--
--------------------------------------------------------------------------------
module Swish.HaskellRDF.N3Parser
( ParseResult
, parseN3fromString
, parseAnyfromString
, parseTextFromString, parseAltFromString
, parseNameFromString, parsePrefixFromString
, parseAbsURIrefFromString, parseLexURIrefFromString, parseURIref2FromString
-- Exports for parsers that embed Notation3 in a bigger syntax
, N3Parser, N3State(..)
, whiteSpace, symbol, lexeme, eof, identStart, identLetter
, defaultPrefix, namedPrefix
, document, subgraph, uriRef2, varid, lexUriRef
, newBlankNode
)
where
import Swish.HaskellRDF.RDFGraph
( RDFGraph, RDFLabel(..)
, NamespaceMap
, LookupFormula(..)
, addArc
, setFormula
, setNamespaces
, emptyRDFGraph
)
import Swish.HaskellRDF.GraphClass
( arc )
import Swish.HaskellUtils.LookupMap
( LookupMap(..)
, mapFind, mapReplace, mapReplaceOrAdd )
import Swish.HaskellUtils.Namespace
( Namespace(..)
, ScopedName(..)
, getScopePrefix
, getScopedNameURI
, makeScopedName, makeUriScopedName
, nullScopedName
)
import Swish.HaskellRDF.Vocabulary
( namespaceRDF
, namespaceRDFS
, namespaceRDFD
, namespaceRDFO
, namespaceOWL
, langName
, rdf_type
, rdf_first, rdf_rest, rdf_nil
, owl_sameAs
, operator_plus, operator_minus, operator_slash, operator_star
, default_base
)
import Swish.HaskellUtils.ProcessURI
( isAbsoluteURIRef, isValidURIRef, absoluteUriPart )
import Swish.HaskellUtils.ErrorM
( ErrorM(Error,Result) )
import Swish.HaskellRDF.ParsecLanguage
( emptyDef )
import Text.ParserCombinators.Parsec
import qualified Text.ParserCombinators.Parsec.Token as P
import Data.Char
( chr, digitToInt )
----------------------------------------------------------------------
-- Set up token parsers
----------------------------------------------------------------------
pythonStyle =
emptyDef
{ P.commentStart = ""
, P.commentEnd = ""
, P.commentLine = "#"
, P.nestedComments = True
, P.identStart = letter <|> char '_' -- oneOf "_"
, P.identLetter = alphaNum <|> char '_'
, P.reservedNames = []
, P.reservedOpNames= []
, P.caseSensitive = True
}
lexer :: P.TokenParser N3State
lexer = P.makeTokenParser pythonStyle
whiteSpace = P.whiteSpace lexer
symbol = P.symbol lexer
lexeme = P.lexeme lexer
identStart = P.identStart pythonStyle
identLetter = P.identLetter pythonStyle
----------------------------------------------------------------------
-- 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
}
-- Type for special name lookup table
type SpecialMap = LookupMap (String,ScopedName)
-- Functions to update N3State vector (use with Parsec updateState)
setPrefix :: String -> String -> N3State -> N3State
setPrefix pre uri st = st { prefixUris=p' }
where
p' = mapReplaceOrAdd (Namespace 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 -> String -> N3State -> N3State
setSUri nam suri = setSName nam (makeScopedName "" suri "")
-- Get name for special syntax element, default null
getSName :: N3State -> String -> ScopedName
getSName st nam = mapFind nullScopedName nam (syntaxUris st)
getSUri :: N3State -> String -> String
getSUri st nam = getScopedNameURI $ getSName st nam
-- Lookup prefix in table and return URI or 'prefix:'
mapPrefix :: NamespaceMap -> String -> String
mapPrefix ps pre = mapFind (pre++":") pre ps
-- Functions to access state:
-- Map prefix to namespace
getPrefixNs :: N3State -> String -> Namespace
getPrefixNs st pre = Namespace pre (mapPrefix (prefixUris st) pre)
-- Map ScopedName using prefix table
-- (Ignore URI in supplied ScopedName)
getPrefixScopedName :: N3State -> ScopedName -> ScopedName
getPrefixScopedName st snam = ScopedName (getPrefixNs st pre) loc
where
pre = getScopePrefix snam
loc = snLocal snam
-- Return function to update graph in N3 parser state,
-- using the supplied function of a graph
-- (use returned function with Parsec updateState)
updateGraph :: ( RDFGraph -> RDFGraph ) -> ( N3State -> N3State )
updateGraph f s = s { graphState = f (graphState s) }
-- Define default table of namespaces
prefixTable = [ namespaceRDF
, namespaceRDFS
, namespaceRDFD -- datatypes
, namespaceRDFO -- operators
, namespaceOWL
]
-- Define default special-URI table
specialTable :: [(String,ScopedName)]
specialTable = [ ( "a", rdf_type ),
( "equals", owl_sameAs ),
( "listfirst", rdf_first ),
( "listrest", rdf_rest ),
( "listnull", rdf_nil ),
( "plus", operator_plus ),
( "minus", operator_minus ),
( "slash", operator_slash ),
( "star", operator_star ),
( "base", default_base ) ]
----------------------------------------------------------------------
-- Define top-level parser function:
-- accepts a string and returns a graph or error
----------------------------------------------------------------------
type N3Parser a = GenParser Char N3State a
type ParseResult = ErrorM RDFGraph -- PResult RDFGraph | PError String
parseN3fromString :: String -> ParseResult
parseN3fromString input =
case parseAnyfromString document Nothing input of
Left err -> Error err
Right gr -> Result gr
-- Function to supply initial context and parse supplied term
--
-- parser is parser to apply
-- base is base URI of the input, or Nothing to use default base value
-- input is the input to be parsed
--
parseAnyfromString :: N3Parser a -> Maybe String -> String -> (Either String a)
parseAnyfromString parser base input =
let
pmap = LookupMap prefixTable
smap = LookupMap specialTable
bmap = case base of
Nothing -> smap
Just bs -> mapReplace smap ("base",makeUriScopedName bs)
pstate = N3State
{ graphState = emptyRDFGraph
, thisNode = NoNode
, prefixUris = pmap
, syntaxUris = bmap
, nodeGen = 0
}
result = runParser parser pstate "" input
in
case result of
Left err -> Left (show err)
Right res -> Right res
newBlankNode :: N3Parser RDFLabel
newBlankNode =
do { s <- getState
; let n = (nodeGen s) + 1
; setState ( s { nodeGen = n } )
; return (Blank (show n))
}
-- Test functions for selected element parsing
parseTextFromString :: String -> String -> (Either String String)
parseTextFromString s =
parseAnyfromString (string s) Nothing
parseAltFromString :: String -> String -> String -> (Either String String)
parseAltFromString s1 s2 =
parseAnyfromString ( (string s1) <|> (string s2) ) Nothing
parseNameFromString :: String -> (Either String String)
parseNameFromString =
parseAnyfromString name Nothing
parsePrefixFromString :: String -> (Either String Namespace)
parsePrefixFromString =
parseAnyfromString prefix Nothing
parseAbsURIrefFromString :: String -> (Either String String)
parseAbsURIrefFromString =
parseAnyfromString absUriRef Nothing
parseLexURIrefFromString :: String -> (Either String String)
parseLexURIrefFromString =
parseAnyfromString lexUriRef Nothing
parseURIref2FromString :: String -> (Either String ScopedName)
parseURIref2FromString =
parseAnyfromString uriRef2 Nothing
----------------------------------------------------------------------
-- Syntax productions
----------------------------------------------------------------------
-- document = directive* statement-list
document :: N3Parser RDFGraph
document =
do { whiteSpace
; many directive
; statements
; eof
; s <- getState
; return $ setNamespaces (prefixUris s) (graphState s)
}
-- directive = "@prefix" prefix ":" uriRef2 "." // Namespace declaration
-- | "@prefix" ":" uriRef2 "." // Default namespace
-- | "@equivalence" uriRef2 "." // Alternative to daml:equivalent
-- | "@listfirst" uriRef2 "." // Alternative to n3:first
-- | "@listrest" uriRef2 "." // Alternative to n3:rest
-- | "@listnull" uriRef2 "." // Alternative to n3:null
-- | "@plus" uriRef2 "." // Alternative to operator:plus
-- | "@minus" uriRef2 "." // Alternative to operator:minus
-- | "@slash" uriRef2 "." // Alternative to operator:slash
-- | "@star" uriRef2 "." // Alternative to operator:star
-- | "@base" uriRef2 "." // Base URI for relative URIs.
directive :: N3Parser ()
directive =
do { try $ symbol "@prefix"
; ( defaultPrefix <|> namedPrefix )
}
<|>
do { string "@" -- not lexeme
; syntaxUri
}
<?>
"directive"
defaultPrefix :: N3Parser ()
defaultPrefix =
do { symbol ":"
; u <- uriRef2
; symbol "."
; updateState $ setPrefix "" (getScopedNameURI u)
}
namedPrefix :: N3Parser ()
namedPrefix =
do { n <- name
; symbol ":"
; u <- uriRef2
; symbol "."
; updateState $ setPrefix n (getScopedNameURI u)
}
syntaxUri :: N3Parser ()
syntaxUri =
do { s <- uriName
; u <- uriRef2
; symbol "."
; updateState $ setSUri s (getScopedNameURI u)
}
uriName :: N3Parser String
uriName =
(try $ symbol "equivalence")
<|> (try $ symbol "listfirst")
<|> (try $ symbol "listrest")
<|> (try $ symbol "listnull")
<|> (try $ symbol "plus")
<|> (try $ symbol "minus")
<|> (try $ symbol "slash")
<|> (try $ symbol "star")
<|> (try $ symbol "base")
<?> "special URI directive"
-- statements = [ statement ( "." statement )* ]
--
-- statement = subject property-list
--
-- properties = [ property ( ";" property )* ]
--
-- New statements are added to the user state graph
statements :: N3Parser ()
statements =
do { sepEndBy1 statement (symbol ".")
; return ()
}
statement :: N3Parser ()
statement =
do { subj <- subject
; optional $ properties subj
}
properties :: RDFLabel -> N3Parser ()
properties subj =
do { sepBy1 (property subj) (symbol ";")
; return ()
}
-- property = verb object-list
-- | ":-" anon-node // Creates anon-node aongside the current node
-- verb = ">-" prop "->" // has 'prop' of
-- | "<-" prop "<-" // is 'prop' of
-- | operator // has operator:'operator' of (???)
-- | prop // has 'prop' of -- shorthand
-- | "has" prop "of" // has 'prop' of
-- | "is" prop "of" // is 'prop' of
-- | "a" // has rdf:type of
-- | "=" // has daml:equivalent of
--
-- subj is the subject node for these properties.
--
-- New statements are added to the graph in the parser's user state.
property :: RDFLabel -> N3Parser ()
property subj =
do { (prop,swap) <- verb
; objects subj prop swap
}
<|>
do { symbol ":-"
; anonNode subj
; return ()
}
verb :: N3Parser (RDFLabel,Bool)
verb = do { p <- prop ; return (p,False) }
<|> do { p <- operator ; return (p,False) }
<|> do { symbol ">-" ; p <- prop ; symbol "->" ; return (p,False) }
<|> do { symbol "<-" ; p <- prop ; symbol "<-" ; return (p,True) }
<|> do { symbol "has" ; p <- prop ; symbol "of" ; return (p,False) }
<|> do { symbol "is" ; p <- prop ; symbol "of" ; return (p,True) }
<|> do { symbol "a"
; lab <- operatorLabel rdf_type
; return (lab,False)
}
<|> do { symbol "="
; lab <- operatorLabel owl_sameAs
; return (lab,False)
}
<?> "property"
-- objects = object
-- | object "," object-list
--
-- subj is the subject node for the new statements,
-- prop is the property node for the new statements.
-- swap is true if the subject/object values in the resulting statement
-- are to be swapped (for "is <prop> of", etc.)
--
-- New statements are added to the graph in the parser's user state
objects :: RDFLabel -> RDFLabel -> Bool -> N3Parser ()
objects subj prop swap =
do { sepBy1 (object subj prop swap) (symbol ",")
; return ()
}
-- anonNode = "[" property-list "]" // Something with given properties
-- | "{" statement-list "}" // List of statements as resource
-- | "(" node-list ")" // Construct list with
-- // rdf:first, rdf:rest, rdf:nil
--
-- subj is the subject node with which the new anonymous node is equated,
--
-- The anonymous node value is returned by this parser (which is often the same
-- as the supplied subject node, but not always).
--
-- New statements are added to the graph in the parser's user state
-- (in the case of a formula, a new graph and parser are created, and
-- the graph arcs are added to this new graph).
anonNode :: RDFLabel -> N3Parser RDFLabel
anonNode subj =
do { symbol "[" ; properties subj ; symbol "]" ; return subj }
<|> do { symbol "{" ; form <- formula subj ; symbol "}" ; return form }
<|> do { symbol "(" ; list <- nodeList subj ; symbol ")" ; return list }
<?> "anon node (\"[\", \"(\" or \"{\")"
-- This method allows a statement list to be parsed as a subgraph
-- whose value is associated with the supplied node of the current
-- graph.
formula :: RDFLabel -> N3Parser RDFLabel
formula subj =
do { subgr <- subgraph subj
; updateState $ updateGraph
$ setFormula (Formula subj subgr)
; return subj
}
subgraph :: RDFLabel -> N3Parser RDFGraph
subgraph this =
do { pstate <- getState
; let fstate = pstate { graphState = emptyRDFGraph, thisNode = this }
; setState fstate -- switch new state into parser
; statements -- parse statements of formula
; fstate' <- getState
; let nstate = pstate { nodeGen = (nodeGen fstate') }
; setState nstate -- swap back state, with updated nodeGen
; return (graphState fstate')
}
-- prop = uri-ref2
-- | varid
--
-- Returns URI value as a Node
prop :: N3Parser RDFLabel
prop = nodeid <|> varid <|> uriNode
-- operator = "+" // >- operator:plus ->
-- | "-" // >- operator:minus ->
-- | "/" // >- operator:slash ->
-- | "*" // >- operator:star->
--
-- If matched, the operator is returned as a node value.
operator :: N3Parser RDFLabel
operator =
do { symbol "+" ; operatorLabel operator_plus }
<|> do { symbol "-" ; operatorLabel operator_minus }
<|> do { symbol "*" ; operatorLabel operator_star }
<|> do { symbol "/" ; operatorLabel operator_slash }
<?> ""
operatorLabel :: ScopedName -> N3Parser RDFLabel
operatorLabel snam =
do { s <- getState
; return $ Res (getPrefixScopedName s snam)
}
-- subject = node
subject :: N3Parser RDFLabel
subject = node
-- object = litNode
--
-- This production adds a new triple to the graph state,
-- using the supplied subject and propert values.
-- If swap is True, the subject and object positions are
-- swapped.
object :: RDFLabel -> RDFLabel -> Bool -> N3Parser ()
object subj prop swap =
do { o <- litNode
; if swap then (addStatement o prop subj)
else (addStatement subj prop o)
}
-- Add statement to graph in N3 parser state
addStatement :: RDFLabel -> RDFLabel -> RDFLabel -> N3Parser ()
addStatement s p o = updateState (updateGraph (addArc (arc s p o) ))
-- nodeList = litNode*
--
-- subj is the node from which the list is linked.
--
-- Returns the supplied head of list or Nil node allocated.
--
-- Link first element of link to list head, scan rest of list,
-- and return the list head; otherwise return a node rdf_null.
--
-- This slightly convoluted pattern is to deal with two different
-- occurrences of a list node:
-- <node> :- ( l1, l2, ... )
-- Here, <node> (the supplied subj) is the listhead.
-- <node> prop ( l1, l2, ... )
-- Here, the a new blank is supplied as subj to be the listhead.
-- In either case, if the list is non-empty, the supplied subj
-- is returned. But if the list is empty, a rdf_null node is returned.
-- In the second case, the invoking production must use the returned
-- value.
nodeList :: RDFLabel -> N3Parser RDFLabel
nodeList subj =
do { val <- litNode
; first <- operatorLabel rdf_first
; addStatement subj first val
; nodeList1 subj
; return subj
}
<|> do { nil <- operatorLabel rdf_nil
; return nil
}
<?> "Node or ')'"
nodeList1 :: RDFLabel -> N3Parser ()
nodeList1 prev =
do { val <- litNode
; lnk <- newBlankNode
; first <- operatorLabel rdf_first
; rest <- operatorLabel rdf_rest
; addStatement lnk first val
; addStatement prev rest lnk
; nodeList1 lnk
}
<|> do { nil <- operatorLabel rdf_nil
; rest <- operatorLabel rdf_rest
; addStatement prev rest nil
}
<?> "Node or ')'"
-- lit-node = node
-- | str-node [ "@" lang ] [ "^^" uriRef2 ]
-- str-node = '"' constant-value '"'
-- | '"""' constant value '"""' // Including single or double occurences of
-- // quotes and/or newlines
--
-- Returns a new node value.
litNode :: N3Parser RDFLabel
litNode =
node
<|> do { s <- strNode
; t <- litTypeOrLang
; return $ Lit s t
}
<?> "URI, blank node or literal"
strNode :: N3Parser String
strNode =
tripleQuoteString
<|> singleQuoteString
litTypeOrLang :: N3Parser (Maybe ScopedName)
litTypeOrLang =
langTag
<|> typeUri
<|> return Nothing
<?> "'@tag' (language tag) or '^^name' (datatype URI)"
langTag :: N3Parser (Maybe ScopedName)
langTag =
do { string "@"
; l <- name -- name1 letter
; return $ Just (langName l)
}
<?> "'@tag' (language tag)"
typeUri :: N3Parser (Maybe ScopedName)
typeUri =
do { string "^^"
; u <- uriRef2
; return $ Just u
}
<?> "'^^name' (datatype URI)"
-- node = nodeid
-- | varid
-- | uri-ref2
-- | anon-node
--
-- nodeid = "_:" name
--
-- varid = "?" name
--
-- Returns a new node value.
node :: N3Parser RDFLabel
node = nodeid
<|> varid
<|> uriNode
<|> do { n <- newBlankNode
; anonNode n
}
<?> "URI or blank node"
-- Identified blank node in input
--
-- Note that automatically generated blank node identifiers start with
-- a digit, where input node identifiers start with a letter, so there
-- can be no clash. Care is needed when serializing a graph to ensure
-- that future clashes are avoided.
nodeid :: N3Parser RDFLabel
-- nodeid = lexeme nodeid1
nodeid =
do { string "_:"
; n <- name
; return (Blank n)
}
-- variable identifier
varid :: N3Parser RDFLabel
varid = do { string "?"
; n <- name
; return (Var n)
}
-- uriNode = qname
-- | "<" URI-reference ">"
-- | "this"
uriNode :: N3Parser RDFLabel
uriNode =
do { sn <- uriRef2 ;
; return ( Res sn )
}
<|>
do { string "this"
; s <- getState
; return ( thisNode s )
}
<?> "URI node"
-- uriRef2 = qname
-- | ":" local-name
-- | "<" URI-reference ">"
-- qname = prefix ":" local-name
--
-- prefix = name // Namespace prefix
--
-- local-name = name // Local name (namespace qualified)
--
-- name = alpha alphanumeric*
--
-- alpha = "a"-"z"
-- | "A"-"Z"
-- | "_"
--
-- alphanumeric = alpha
-- | "0"-"9"
--
-- URI-reference = (conforming to syntax in RFC2396)
--
-- uriRef2 returns a ScopedName.
uriRef2 :: N3Parser ScopedName
uriRef2 = ( lexeme $ try uriRef2a )
<?> "URI or QName"
uriRef2a =
do { ns <- prefix
; string ":"
; local <- localname
; return $ ScopedName ns local
}
<|>
do { string ":"
; ns <- defaultprefix
; local <- localname
; return $ ScopedName ns local
}
<|>
do { u <- absUriRef
; return $ makeUriScopedName u
}
<?> "URI or QName"
prefix :: N3Parser Namespace
prefix =
do { pref <- prefixname
; st <- getState
; return (getPrefixNs st pref) -- map prefix to namespace
}
defaultprefix :: N3Parser Namespace
defaultprefix =
do { st <- getState
; return (getPrefixNs st "")
}
name :: N3Parser String
name = lexeme $ name1 identStart
prefixname :: N3Parser String
prefixname = name1 identStart
localname :: N3Parser String
localname = lexeme $ name1 identLetter
-- 'name1' is a name without following whitespace
-- initChar is a parser for the first character
name1 :: N3Parser Char -> N3Parser String
name1 initChar =
do { c <- initChar
; cs <- many identLetter
; return (c:cs)
}
<?> "identifier"
----------------------------------------------------------------------
-- Lexical support
----------------------------------------------------------------------
--
-- The following code adapted from ParsecToken,
-- modified to handle different escape conventions and triple-quoted strings
-- \c
-- \uhhhh
-- \Uhhhhhhhh
--
-- Regular single-quoted string -- cannot be split over line breaks
singleQuoteString :: N3Parser String
singleQuoteString =
lexeme
( between (char '"') (char '"' <?> "end of string (\")") anyStringChars
<?> "literal string" )
anyStringChars =
do { str <- many stringChar
; return (foldr (maybe id (:)) "" str)
}
-- Triple-quoted string -- may include line breaks, '"' or '""'.
tripleQuoteString :: N3Parser String
tripleQuoteString =
lexeme
( do { str <- between (try $ string "\"\"\"")
(string "\"\"\"" <?> "end of string (\"\"\")")
(many tripleQuoteSubstring)
; return (foldr (++) "" str)
}
<?> "triple-quoted literal string" )
-- Match non-quote substring or one or two quote characters
tripleQuoteSubstring :: N3Parser String
tripleQuoteSubstring =
tripleQuoteSubstring1
<|> try sqTripleQuoteSubstring1
<|> try dqTripleQuoteSubstring1
dqTripleQuoteSubstring1 =
do { string "\"\""
; s <- tripleQuoteSubstring1
; return $ "\"\""++s
}
sqTripleQuoteSubstring1 =
do { char '"'
; s <- tripleQuoteSubstring1
; return $ "\""++s
}
-- match at least one non-quote character in a triple-quoted string
tripleQuoteSubstring1 :: N3Parser String
tripleQuoteSubstring1 =
do { str <- many1 tripleQuoteStringChar
; return $ foldr (maybe id (:)) "" str
}
tripleQuoteStringChar :: CharParser st (Maybe Char)
tripleQuoteStringChar =
stringChar
<|> do { (string "\n")
; return $ Just '\n'
}
stringChar :: CharParser st (Maybe Char)
stringChar =
do { c <- stringLetter
; return $ Just c
}
<|> stringEscape
<?> "string character"
stringLetter = satisfy (\c -> (c /= '"') && (c /= '\\') && (c >= '\032'))
stringEscape =
do { char '\\'
; do { esc <- escapeCode; return (Just esc) }
}
-- escape codes
escapeCode = charEsc <|> charUCS2 <|> charUCS4 <?> "escape code"
-- \c
charEsc = choice (map parseEsc escMap)
where
parseEsc (c,code) = do { char c; return code }
escMap = zip ("nrt\\\"\'") ("\n\r\t\\\"\'")
-- \uhhhh
charUCS2 =
do { char 'u'
; n <- numberFW 16 hexDigit 4 0
; return $ chr n
}
-- \Uhhhhhhhh
charUCS4 =
do { char 'U'
; n <- numberFW 16 hexDigit 8 0
; return $ chr n
}
-- parse fixed-width number:
numberFW :: Int -> CharParser st Char -> Int -> Int -> CharParser st Int
numberFW base baseDigit 0 val = return val
numberFW base baseDigit width val =
do { d <- baseDigit
; numberFW base baseDigit (width-1) ((val*base) + (digitToInt d))
}
----------------------------------------------------------------------
-- Parse a URI reference from the input
-- The result returned has absolute form; relative URIs are resolved
-- relative to the current base prefix (set using "@base").
--
-- [[[TODO: rework the URI parser to use the Parsec library]]]
-- lexeme version
lexUriRef :: N3Parser String
lexUriRef = lexeme absUriRef
absUriRef :: N3Parser String
absUriRef =
do { u <- between (char '<') (char '>' <?> "end of URI '>'") anyUriChars
; if (isAbsoluteURIRef u)
then (return u)
else
if (isValidURIRef u)
then
do { s <- getState
; (return $ absoluteUriPart (getSUri s "base") u)
}
else (fail ("Invalid URI: <"++u++">"))
}
anyUriChars :: N3Parser String
anyUriChars = many uriChar
uriChar :: N3Parser Char
uriChar =
alphaNum
<|> oneOf "[];?:@&=+$,-_.!~*'()%//#"
<?> "URI character"
--------------------------------------------------------------------------------
--
-- Copyright (c) 2003, G. KLYNE. 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
--
--------------------------------------------------------------------------------
-- $Source: /file/cvsdev/HaskellRDF/N3Parser.hs,v $
-- $Author: graham $
-- $Revision: 1.36 $
-- $Log: N3Parser.hs,v $
-- Revision 1.36 2004/01/22 19:52:41 graham
-- Rename module URI to avoid awkward clash with Haskell libraries
--
-- Revision 1.35 2004/01/09 11:23:54 graham
-- Fix up N3Parser so that the final statement-terminating '.' in a formula
-- or file is optional.
--
-- Revision 1.34 2004/01/07 19:49:12 graham
-- Reorganized RDFLabel details to eliminate separate language field,
-- and to use ScopedName rather than QName.
-- Removed some duplicated functions from module Namespace.
--
-- Revision 1.33 2003/12/20 12:53:40 graham
-- Fix up code to compile and test with GHC 5.04.3
--
-- Revision 1.32 2003/12/10 03:48:57 graham
-- SwishScript nearly complete: BwdChain and PrrofCheck to do.
--
-- Revision 1.31 2003/12/08 23:55:36 graham
-- Various enhancements to variable bindings and proof structure.
-- New module BuiltInMap coded and tested.
-- Script processor is yet to be completed.
--
-- Revision 1.30 2003/12/05 02:31:32 graham
-- Script parsing complete.
-- Some Swish script functions run successfully.
-- Command execution to be completed.
--
-- Revision 1.29 2003/12/04 02:53:27 graham
-- More changes to LookupMap functions.
-- SwishScript logic part complete, type-checks OK.
--
-- Revision 1.28 2003/12/03 17:07:23 graham
-- Replace occurrences of QName in N3Parser with ScopedName.
--
-- Revision 1.27 2003/12/03 15:57:00 graham
-- Use common parser wrapper function for all parsing.
-- (This will also be used for Swish script parsing.)
--
-- Revision 1.26 2003/12/03 15:42:09 graham
-- Eliminate special return type in favour of ErrorM
--
-- Revision 1.25 2003/11/24 17:20:34 graham
-- Separate module Vocabulary from module Namespace.
--
-- Revision 1.24 2003/11/24 15:46:04 graham
-- Rationalize N3Parser and N3Formatter to use revised vocabulary
-- terms defined in Namespace.hs
--
-- Revision 1.23 2003/10/24 21:02:42 graham
-- Changed kind-structure of LookupMap type classes.
--
-- Revision 1.22 2003/10/09 16:26:31 graham
-- Added parser support for literal language tags and datatypes.
-- (Language tags are names, not strictly per RFC3066)
--
-- Revision 1.21 2003/09/24 18:50:52 graham
-- Revised module format to be Haddock compatible.
--
-- Revision 1.20 2003/09/24 13:36:42 graham
-- QName handling separated from RDFGraph module, and
-- QName splitting moved from URI module to QName module.
--
-- Revision 1.19 2003/07/01 14:18:57 graham
-- Allow blank node in predicate position.
-- Add parser and formatter test case for this.
--
-- Revision 1.18 2003/06/19 19:48:03 graham
-- Allow variable id in predicate position
--
-- Revision 1.17 2003/06/12 00:47:56 graham
-- Allowed variable node (?v) and bare anonymous nodes in N3 parser.
--
-- Revision 1.16 2003/06/03 19:24:13 graham
-- Updated all source modules to cite GNU Public Licence
--
-- Revision 1.15 2003/05/29 00:57:37 graham
-- Resolved swish performance problem, which turned out to an inefficient
-- method used by the parser to add arcs to a graph.
--
-- Revision 1.14 2003/05/22 15:16:39 graham
-- Added additional parser test cases for lists
--
-- Revision 1.13 2003/05/21 13:55:13 graham
-- N3 parser now handles relative URIs and default prefixes.
-- (Still need to figure better default base URI handling; i.e. current document)
--
-- Revision 1.12 2003/05/21 13:34:13 graham
-- Various N3 parser bug fixes.
-- Need to fix handling of :name terms.
--
-- Revision 1.11 2003/05/20 23:35:28 graham
-- Modified code to compile with GHC hierarchical libraries
--
-- Revision 1.10 2003/05/08 18:55:36 graham
-- Updated graph matching module to deal consistently
-- with graphs containing formulae. All graph tests now
-- run OK, but the GraphMatch module is a mess and
-- desperately needs restructuring. Also, graph matching
-- performance needs to be improved.
--
-- Revision 1.9 2003/05/07 23:58:09 graham
-- More restructuring.
-- RDFGraphTest runs OK.
-- N3ParserTest needs to be updated to use new structure for formulae.
--
-- Revision 1.8 2003/04/17 00:35:38 graham
-- Added module N3ParserTest
-- N3parser is mostly working
-- Formulae remain to test
--
-- Revision 1.7 2003/04/15 21:40:54 graham
-- N3Parser compiles
-- Some small changes to RDFGraph
-- Added some QName methods
--
-- Revision 1.6 2003/04/11 17:38:34 graham
-- Rename GraphLookupMap to LookupMap
--
-- Revision 1.5 2003/04/10 20:08:39 graham
-- Reorganized RDFGraph naming (RDFGraphTest OK)
-- Progressing N3Parser
--
-- Revision 1.4 2003/03/12 23:00:43 graham
-- Graph model coded and working, except for graph isomorphism test.
--
-- Revision 1.3 2003/03/12 13:41:59 graham
-- N3 parser initial coding done.
-- Graph not yet implemented.
--
-- Revision 1.2 2003/03/08 17:28:45 graham
-- Added string literal parsing code
--
-- Revision 1.1 2003/03/07 22:53:38 graham
-- Started on N3 parser in Haskell