All Downloads are FREE. Search and download functionalities are using the official Maven repository.

function.datashape2.ppathparser.rq Maven / Gradle / Ivy

Go to download

Corese is a Semantic Web Factory (triple store and SPARQL endpoint) implementing RDF, RDFS, SPARQL 1.1 Query and Update.

The newest version!
#
# Data Shape Property Path Interpreter
# PP expressions are searched in graph ?shape 
#
# Olivier Corby - Wimmics Inria I3S - 2016-2019
#
prefix sh:    
prefix xsh:   
prefix jc:   
prefix h:    
#
# SHACL Path Compiler from RDF graph to list expression
#

#
# Rewrite Property Path shape expression as a LDScript list
#
# ([sh:zeroOrMorePath rdf:rest] rdf:first)
# ->
# (sh:sequence ((sh:zeroOrMorePath rdf:rest) rdf:first))
# URI may be URI of a subpath such as:
# [sh:path ex:parent] .
# ex:parent sh:alternativePath (ex:father ex:mother)
# sh:path (ai:location [ xsh:service ( rdf:type ) )

@import 


function sh:pathparser(exp) {
    let (path = sh:path(sh:shaclGraph(), exp)) {
        path
    }
}

function sh:pathparser(dt:graph shape, exp) {
    sh:path(shape, exp)
}


function sh:path(dt:graph shape, exp) {
  if (isURI(exp)) {
    return (exp)
  }
  else if (isExtension(exp)) {
        # exp is a list
        letdyn (shacl = shape) {
            let (list = maplist(lambda(term) { 
                    sh:path(shacl, term) }, exp)) {
                return (sh:sequence(list))
            }
        }
  }
  else if (isLiteral(exp)) { 
     return (exp)
  }
  else {
    let (select ?shape ?exp ?q ?path  where {
            graph ?shape { 
                ?exp ?q ?path   filter (?q not in (rdf:first, owl:sameAs))
            }
        } ) {
        
        if (! bound(q)) {
            return(error())   
        } else if (q = rdf:rest)  { 
            return (sh:sequence(sh:listsh(shape, exp))) 
        } else if (sh:extension(exp, q)) {
            return (sh:extension(shape, q, path)) 
        } else { 
            let (res = sh:path(shape, path)) {
                if (q = sh:alternativePath && xt:size(res) = 2 && xt:get(res, 0) = sh:sequencePath) {
                    return (xt:list(q, xt:get(res, 1)))
                }
                else {
                    return (xt:list(q, res))
                }
            }
        }
    }
  }
}

function sh:sequence(list) {
    return (xt:list(sh:sequencePath, list))
}


# [ (rdf:type)]
# test isBlank because a path element may be an URI which may have properties
# focus on local bnode 
function sh:extension(exp, q) {
    return (sh:statement(q) || sh:operator(q) || (isBlank(exp) && sh:usernamespace(q)))
}

function sh:statement(oper) {
    oper in (xsh:service, xsh:ldpath)
}

function sh:operator(oper) {
    sh:isFunction(oper)
}


function sh:isFunction(oper) {
    oper = xsh:function || sh:isFunctionName(oper)
}

function sh:isFunctionName(oper) {
     xt:member(oper, sh:defShapePathExtension()) || strstarts(oper, sx:)
}

function sh:usernamespace2(oper) {
    ! mapany(rq:strstarts, oper, xt:list(rdf:, rdfs:, owl:, sh:))
}

function sh:usernamespace(oper) {
    for (pref in xt:list(rdf:, rdfs:, owl:, sh:, xsh:)) {
        if (strstarts(oper, pref)) {
            return (false)
        }
    } ;
    return (true)
}

#
# (us:location [sh:pattern 'dbpedia'] [ (rdf:type) ] ) 
#
function sh:extension(shape, oper, path) {
         if (sh:statement(oper))     { return (xt:list(oper,   sh:listsh(shape, path))) }
    else if (sh:operator(oper))      { return (sh:parseOperator(shape, oper, path)) }
    else if (sh:usernamespace(oper)) { return (xt:list(xsh:service, xt:cons(oper, sh:listsh(shape, path)))) }
}



# return [xsh:function [ xsh:name ( arg )]]
function sh:parseOperator(shape, oper, path) {
    if (sh:isFunction(oper), sh:parseFunction(shape, oper, path),
    xt:list())
}

# extended path for triple
# [xsh:node xsh:subject] [xsh:triple xsh:predicate]
# oper = xsh:node|xsh:triple ; path = xsh:subject|xsh:predicate|xsh:object
# oper and path can be switched
#
function sh:parsePathTriple(shape, oper, path) {
    if (oper in (xsh:nodePath, xsh:triplePath),
        xt:list(sh:pathtriple, xt:list(oper, sh:pathfun(path))),
        xt:list(sh:pathtriple, xt:list(path, sh:pathfun(oper))))
}

# [xsh:node path]
#
function sh:pathfun(path) {
    if (path = xsh:subject,   xt:subject,
    if (path = xsh:object,    xt:object, 
    if (path = xsh:predicate, xt:predicate,
    if (path = xsh:graph,     xt:graph,
    if (isBlank(path),        maplist(sh:pathfun, sh:tolist(path)),     
    xsh:undef)))))
}

# [ xsh:function [ us:fun (arg) ] ]
# [ xsh:function us:fun ]
# us:fun (arg) where oper = us:fun ; fun = (arg)
# 
function sh:parseFunction(shape, oper, fun) {
    let (select * where {
        graph ?shape { 
            {
                [] xsh:function ?fun 
                optional { ?fun ?pred ?param filter (?pred != owl:sameAs) }             
                bind (coalesce(?pred, ?fun) as ?name)
                bind (coalesce(?param, xt:list()) as ?value)
                bind (sh:getExtValueList(?value) as ?arg)
            }
            union {
                [] ?oper ?fun 
                filter (?oper not in (owl:sameAs, xsh:function))
                bind (?oper as ?name)
                bind (sh:getExtValueList(?fun) as ?arg)
            }
            filter (sh:isFunctionName(name) || xt:isFunction(name, 3)) 
        }}){
        sh:defineFunction(name, arg)
    }
}


function sh:defineFunction(name, arg) {
    if (name = xsh:triplePath, sh:defineFunctionTriple(name, arg),
    if (name = xsh:extension,  sh:defineExtension(name, arg),
        sh:funpath(name, arg)))
}

# place holder to test extension that generate new path code
# [xsh:extension (xsh:filter sh:hasValue h:hasParent) ]
# [xsh:extension (xsh:property pathExpression sh:hasValue h:hasParent) ]
#
function sh:defineExtension(fun, exp) {
    let ((name oper value) = exp) {
        if (name = xsh:filter, 
            sh:defFilter(sh:defConstraint(oper, value)),
        if (name in (sh:and, sh:or, sh:xone), 
            sh:defFilter(sh:extBoolean(fun, exp)),
        if (name = sh:not,  
            sh:defFilter(sh:extNot(fun, exp)),
        if (name = xsh:property, 
            sh:defFilter(sh:defProperty(fun, exp)), 
        xt:list()))))
    }   
}


# [xsh:extension (xsh:property pathExpression sh:hasValue h:hasParent) ]
#
function sh:defProperty(fun, exp) {
    let ((name path oper value) = exp) {
        sh:defPathConstraint(path, oper, value)
    }
}

#
# [xsh:extension (sh:not (sh:hasValue h:hasParent)) ]
function sh:extNot(fun, exp) {
    let ((name exp1) = exp,
         (op1 val1) = exp1,
         e1 = sh:defConstraint(op1, val1)){
        sh:defBoolean(name, e1)
    }
}

#
# [xsh:extension (sh:or (sh:hasValue h:hasParent)(sh:hasValue h:hasChild)) ]
function sh:extBoolean(fun, exp) {
    let ((name exp1 exp2) = exp,
         (op1 val1) = exp1,
         (op2 val2) = exp2,
         e1 = sh:defConstraint(op1, val1),
         e2 = sh:defConstraint(op2, val2)) {
        sh:defBoolean(name, xt:list(e1, e2))
    }
}


function sh:funpath(name, arg) {
    xt:list(xsh:function, xt:list(name, arg))
}

# xsh:tripleExtension((xsh:preceding xsh:subject)) OR xsh:triplePath(xsh:subject)  
#
function sh:getFunctionTriple(name, arg) {
    if (mapany(sh:isExtensionTriple, arg),
        sh:tripleExtension, sh:triplePath)
}

# xsh:tripleExtension((xsh:preceding xsh:subject)) OR xsh:triplePath(xsh:subject)  
#
function sh:getFunctionTripleBasic(name, arg) {
    let ((subject predicate object) = arg) {
        if (coalesce(sh:isExtensionTriple(subject) || sh:isExtensionTriple(object), false),
            sh:tripleExtension, sh:triplePath)
    }
}

# node = (xsh:preceding xsh:subject) || (xsh:exist (exp))
#
function sh:isExtensionTriple(node) {
    isExtension(node)
}

# predicate is a nested constraint
#
function sh:isNestedPredicate(name, arg) {
    let ((subject predicate) = arg) {
       coalesce(sh:isExtensionTriple(predicate) && xt:first(predicate) in  (xsh:exist, xsh:filter), false)
    }
}

# name = xsh:triplePath
#
function sh:defineFunctionTriple(name, arg) {
    if (sh:isNestedPredicate(name, arg),
        # predicate nested constraint = (xsh:exist (subPropertyOf* hasAncestor))
        sh:nest(name, arg),
        sh:funpath(sh:getFunctionTriple(name, arg), arg))
}



#
# predicate is a nested constraint: subPropertyOf* hasAncestor
# name = xsh:triplePath ; 
# arg = (xsh:subject (xsh:exist ([sh:zeroOrMorePath rdfs:subPropertyOf] [xsh:object h:hasAncestor])))
# predicate = (xsh:exist  ([sh:zeroOrMorePath rdfs:subPropertyOf] [xsh:object h:hasAncestor]))
# predicate = (xsh:filter ([sh:pattern rdf:]))
# compile and move nested constraint after xsh:triplePath
# compiled as:
#  (xsh:triplePath (xsh:subject)) 
#  (xsh:exist  ((xsh:nodePath xsh:predicate)(rdfs:subPropertyOf*)(xsh:object h:hasAncestor)))
#
function sh:nest(name, arg) {
    let ((subject predicate object) = arg, 
          (oper value) = predicate,
          exist   = (oper = xsh:exist),
          aname   = sh:getFunctionTripleBasic(name, arg),
          atriple = sh:funpath(aname, xt:list(subject, bnode(), coalesce(object, bnode()))),
          proj    = sh:funpath(xsh:nodePath,   xt:list(xsh:predicate)),
          pred    = if (exist, sh:path(sh:shaclGraph(), value), value),
          test    = if (exist, xt:cons(proj, xt:get(pred, 1)), xt:list(proj, sh:funpath(oper, value))),
          path    = sh:sequence(test)
          ) {
        # record exist path has already compiled
        sh:setConstraint(sh:path, path, path);
        sh:sequence(xt:list(atriple, sh:funpath(xsh:exist, xt:list(path))))
    }
}


function sh:getExtValueList(value) {
    if (isBlank(value)) {
       coalesce(sh:reclist(value), value)
    }
    else {
        return(xt:list(value))
    }
}

#
# Rewrite recursively RDF path list ?exp as a list
#
function dt:list sh:listsh(dt:graph ?shape, ?exp){
   let (select ?shape ?exp (aggregate(sh:path(?shape, ?e)) as ?l)  
        where { graph ?shape { ?exp rdf:rest*/rdf:first ?e }}) {
        return (?l)
    }
}

function dt:list sh:tolist(exp){
     sh:tolist(sh:shaclGraph(), exp)   
}

function dt:list sh:tolist(dt:graph shape, exp){
   let (select ?shape ?exp (aggregate(?e) as ?l)  
        where { graph ?shape { ?exp rdf:rest*/rdf:first ?e }}) {
        return(?l)
    }
}


#
# rewrite PP once as a list and record it
#
function sh:getPPath(p){
   return (if (sh:hasConstraint(sh:path, p), 
     sh:getConstraint(sh:path, p), 
     sh:setConstraint(sh:path, p, sh:pathparser(p))))
}

#
# rewrite PP once as a list and record it
#
function sh:getPath(dt:graph shape, p){
   return (if (sh:hasConstraint(sh:path, p), 
     sh:getConstraint(sh:path, p), 
     sh:setConstraint(sh:path, p, sh:path(shape, p))))
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy