diff --git a/src/main/java/fr/inria/corese/core/next/query/impl/sparql/bridge/AstBackedExpr.java b/src/main/java/fr/inria/corese/core/next/query/impl/sparql/bridge/AstBackedExpr.java
index 24c61c731..6a516a797 100644
--- a/src/main/java/fr/inria/corese/core/next/query/impl/sparql/bridge/AstBackedExpr.java
+++ b/src/main/java/fr/inria/corese/core/next/query/impl/sparql/bridge/AstBackedExpr.java
@@ -138,7 +138,7 @@ public DatatypeValue getValue() {
SparqlTermResolver resolver = whereCompiler == null
? new SparqlTermResolver(null)
: whereCompiler.termResolver();
- return CoreseAstQueryBuilder.toNode(source, resolver).getDatatypeValue();
+ return resolver.toNode(source).getDatatypeValue();
}
return null;
}
diff --git a/src/main/java/fr/inria/corese/core/next/query/impl/sparql/bridge/CoreseAstQueryBuilder.java b/src/main/java/fr/inria/corese/core/next/query/impl/sparql/bridge/CoreseAstQueryBuilder.java
index 807979405..a3ae1706a 100644
--- a/src/main/java/fr/inria/corese/core/next/query/impl/sparql/bridge/CoreseAstQueryBuilder.java
+++ b/src/main/java/fr/inria/corese/core/next/query/impl/sparql/bridge/CoreseAstQueryBuilder.java
@@ -3,8 +3,6 @@
import fr.inria.corese.core.next.query.api.exception.UnsupportedQueryFeatureException;
import fr.inria.corese.core.next.query.impl.sparql.ast.*;
-import fr.inria.corese.core.next.query.impl.sparql.ast.path.PathAst;
-import fr.inria.corese.core.next.query.impl.sparql.ast.path.PredicatePathAst;
import fr.inria.corese.core.next.query.impl.engine.model.ExpType.Type;
import fr.inria.corese.core.next.query.impl.engine.model.Filter;
import fr.inria.corese.core.next.query.impl.engine.model.Node;
@@ -172,41 +170,6 @@ public Filter toNextFilter(ConstraintAst filterExpression) {
return new AstBackedExpr(filterExpression, whereCompiler).getFilter();
}
- /**
- * Converts a query term used as subject, predicate, object, or variable reference
- * into a runtime {@link Node}.
- *
- *
This helper is package-visible because both the query builder and the
- * {@link WhereCompiler} need a single shared term-to-node conversion rule.
- */
- static Node toNode(TermAst term) {
- return toNode(term, new SparqlTermResolver(null));
- }
-
- static Node toNode(TermAst term, SparqlTermResolver resolver) {
- return switch (term) {
- case VarAst(String name) -> NodeImpl.forVariable(name);
- case IriAst(String raw) when raw.startsWith("_:") -> NodeImpl.forBlank(raw.substring(2));
- case IriAst(String raw) -> NodeImpl.forIRI(resolver.resolveIri(raw));
- case LiteralAst(String lexical, String lang, String datatype) -> NodeImpl.forLiteral(
- resolver.unquoteLexical(lexical),
- resolver.normalizeDatatypeIri(datatype),
- lang);
- default -> throw new IllegalArgumentException(
- "A query term must be a variable, IRI or literal, got: "
- + term.getClass().getSimpleName());
- };
- }
-
- static TermAst simplePredicate(PathAst path) {
- if (path instanceof PredicatePathAst(TermAst predicate)) {
- return predicate;
- }
- throw new UnsupportedQueryFeatureException(
- "Property path bridge compilation is not supported yet by the next pipeline for: "
- + path.getClass().getSimpleName());
- }
-
/**
* Rejects unsupported clauses for {@code ASK} queries.
*
@@ -318,7 +281,7 @@ private void applyDataset(Query query, DatasetClauseAst datasetClause, WhereComp
private List toNodeList(Iterable iris, WhereCompiler compiler) {
List nodes = new ArrayList<>();
for (IriAst iri : iris) {
- nodes.add(toNode(iri, compiler.termResolver()));
+ nodes.add(compiler.termResolver().toNode(iri));
}
return nodes;
}
@@ -371,7 +334,7 @@ private List describeNodes(
}
nodes.add(node);
} else {
- nodes.add(toNode(term, compiler.termResolver()));
+ nodes.add(compiler.termResolver().toNode(term));
}
}
return nodes;
@@ -540,7 +503,8 @@ private Exp compileConstructTemplate(
Exp bgp = Exp.create(Type.BGP);
for (TriplePatternAst triple : template.triplePatternAsts()) {
Node subject = constructNode(query, triple.subject(), compiler);
- Node predicate = constructNode(query, simplePredicate(triple.predicate()), compiler);
+ Node predicate = constructNode(
+ query, WhereCompiler.simplePredicate(triple.predicate()), compiler);
Node object = constructNode(query, triple.object(), compiler);
bgp.add(new AstBackedEdge(subject, predicate, object));
}
@@ -556,8 +520,8 @@ private Exp compileConstructTemplate(
private Node constructNode(Query query, TermAst term, WhereCompiler compiler) {
if (term instanceof VarAst(String name)) {
Node bound = visibleBodyNode(query, name);
- return bound != null ? bound : toNode(term, compiler.termResolver());
+ return bound != null ? bound : compiler.termResolver().toNode(term);
}
- return toNode(term, compiler.termResolver());
+ return compiler.termResolver().toNode(term);
}
}
diff --git a/src/main/java/fr/inria/corese/core/next/query/impl/sparql/bridge/NativeEvaluationContext.java b/src/main/java/fr/inria/corese/core/next/query/impl/sparql/bridge/NativeEvaluationContext.java
index efe5d5e4f..5cc19693f 100644
--- a/src/main/java/fr/inria/corese/core/next/query/impl/sparql/bridge/NativeEvaluationContext.java
+++ b/src/main/java/fr/inria/corese/core/next/query/impl/sparql/bridge/NativeEvaluationContext.java
@@ -60,7 +60,7 @@ DatatypeValue variable(String name) {
}
DatatypeValue constant(TermAst expression) {
- return CoreseAstQueryBuilder.toNode(expression, termResolver()).getDatatypeValue();
+ return termResolver().toNode(expression).getDatatypeValue();
}
DatatypeValue required(TermAst expression) {
diff --git a/src/main/java/fr/inria/corese/core/next/query/impl/sparql/bridge/NextFilterFromAst.java b/src/main/java/fr/inria/corese/core/next/query/impl/sparql/bridge/NextFilterFromAst.java
index 2893e6904..743bbe6e5 100644
--- a/src/main/java/fr/inria/corese/core/next/query/impl/sparql/bridge/NextFilterFromAst.java
+++ b/src/main/java/fr/inria/corese/core/next/query/impl/sparql/bridge/NextFilterFromAst.java
@@ -1,6 +1,5 @@
package fr.inria.corese.core.next.query.impl.sparql.bridge;
-
import fr.inria.corese.core.next.query.impl.sparql.ast.TermAst;
import fr.inria.corese.core.next.query.impl.engine.model.Expr;
import fr.inria.corese.core.next.query.impl.engine.model.Filter;
@@ -10,9 +9,7 @@
import java.util.List;
import java.util.Optional;
-/**
- * {@link Filter} view exposing native AST metadata through the Corese-next {@link Expr} API.
- */
+/** Filter view exposing native AST metadata through the Corese-next expression API. */
public final class NextFilterFromAst implements Filter {
private final AstBackedExpr owner;
@@ -71,4 +68,4 @@ public boolean isRecExist() {
public Optional coreseNextSource() {
return owner.sourceAst();
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/fr/inria/corese/core/next/query/impl/sparql/bridge/SparqlTermResolver.java b/src/main/java/fr/inria/corese/core/next/query/impl/sparql/bridge/SparqlTermResolver.java
index af3a65b9c..9ae6af494 100644
--- a/src/main/java/fr/inria/corese/core/next/query/impl/sparql/bridge/SparqlTermResolver.java
+++ b/src/main/java/fr/inria/corese/core/next/query/impl/sparql/bridge/SparqlTermResolver.java
@@ -5,10 +5,14 @@
import fr.inria.corese.core.next.data.impl.namespace.PrefixHandler;
import fr.inria.corese.core.next.data.spi.io.IOConstants;
import fr.inria.corese.core.next.data.spi.term.IRIUtils;
+import fr.inria.corese.core.next.query.impl.engine.model.Node;
+import fr.inria.corese.core.next.query.impl.engine.model.NodeImpl;
+import fr.inria.corese.core.next.query.impl.sparql.ast.IriAst;
+import fr.inria.corese.core.next.query.impl.sparql.ast.LiteralAst;
import fr.inria.corese.core.next.query.impl.sparql.ast.PrefixDeclarationAst;
import fr.inria.corese.core.next.query.impl.sparql.ast.QueryPrologueAst;
-
-import java.util.Objects;
+import fr.inria.corese.core.next.query.impl.sparql.ast.TermAst;
+import fr.inria.corese.core.next.query.impl.sparql.ast.VarAst;
/** Resolves SPARQL terms against one immutable query-prologue snapshot. */
public final class SparqlTermResolver {
@@ -45,6 +49,20 @@ public String resolveIri(String raw) {
return resolvePrefixedIri(raw);
}
+ public Node toNode(TermAst term) {
+ return switch (term) {
+ case VarAst(String name) -> NodeImpl.forVariable(name);
+ case IriAst(String raw) when raw.startsWith(IOConstants.BLANK_NODE_PREFIX) ->
+ NodeImpl.forBlank(raw.substring(IOConstants.BLANK_NODE_PREFIX.length()));
+ case IriAst(String raw) -> NodeImpl.forIRI(resolveIri(raw));
+ case LiteralAst(String lexical, String lang, String datatype) -> NodeImpl.forLiteral(
+ unquoteLexical(lexical), normalizeDatatypeIri(datatype), lang);
+ default -> throw new IllegalArgumentException(
+ "A query term must be a variable, IRI or literal, got: "
+ + term.getClass().getSimpleName());
+ };
+ }
+
public String normalizeDatatypeIri(String datatype) {
if (datatype == null || datatype.isEmpty()) {
return null;
diff --git a/src/main/java/fr/inria/corese/core/next/query/impl/sparql/bridge/WhereCompiler.java b/src/main/java/fr/inria/corese/core/next/query/impl/sparql/bridge/WhereCompiler.java
index 4e5a9568b..82f268795 100644
--- a/src/main/java/fr/inria/corese/core/next/query/impl/sparql/bridge/WhereCompiler.java
+++ b/src/main/java/fr/inria/corese/core/next/query/impl/sparql/bridge/WhereCompiler.java
@@ -11,8 +11,11 @@
import fr.inria.corese.core.next.query.impl.sparql.ast.PatternAst;
import fr.inria.corese.core.next.query.impl.sparql.ast.QueryPrologueAst;
import fr.inria.corese.core.next.query.impl.sparql.ast.ServiceAst;
+import fr.inria.corese.core.next.query.impl.sparql.ast.TermAst;
import fr.inria.corese.core.next.query.impl.sparql.ast.TriplePatternAst;
import fr.inria.corese.core.next.query.impl.sparql.ast.UnionAst;
+import fr.inria.corese.core.next.query.impl.sparql.ast.path.PathAst;
+import fr.inria.corese.core.next.query.impl.sparql.ast.path.PredicatePathAst;
import fr.inria.corese.core.next.query.impl.engine.model.Edge;
import fr.inria.corese.core.next.query.impl.engine.model.ExpType.Type;
import fr.inria.corese.core.next.query.impl.engine.model.Filter;
@@ -120,13 +123,22 @@ private Exp compileBgp(BgpAst bgp) {
}
private Edge toEdge(TriplePatternAst triple) {
- Node subject = CoreseAstQueryBuilder.toNode(triple.subject(), termResolver);
- Node predicate = CoreseAstQueryBuilder.toNode(
- CoreseAstQueryBuilder.simplePredicate(triple.predicate()), termResolver);
- Node object = CoreseAstQueryBuilder.toNode(triple.object(), termResolver);
+ Node subject = termResolver.toNode(triple.subject());
+ Node predicate = termResolver.toNode(
+ simplePredicate(triple.predicate()));
+ Node object = termResolver.toNode(triple.object());
return new AstBackedEdge(subject, predicate, object);
}
+ static TermAst simplePredicate(PathAst path) {
+ if (path instanceof PredicatePathAst(TermAst predicate)) {
+ return predicate;
+ }
+ throw new UnsupportedQueryFeatureException(
+ "Property path bridge compilation is not supported yet by the next pipeline for: "
+ + path.getClass().getSimpleName());
+ }
+
private Exp compileFilter(FilterAst filter) {
Filter nextFilter = new AstBackedExpr(filter.operator(), this).getFilter();
return Exp.create(Type.FILTER, nextFilter);
@@ -165,7 +177,7 @@ private Exp compileMinus(MinusAst minus) {
*/
private Exp compileBind(BindAst bind) {
Filter filter = new AstBackedExpr(bind.expression(), this).getFilter();
- Node variable = CoreseAstQueryBuilder.toNode(bind.variable(), termResolver);
+ Node variable = termResolver.toNode(bind.variable());
Exp exp = Exp.create(Type.BIND);
exp.setFilter(filter);
exp.setFunctional(filter.isFunctional());
@@ -177,7 +189,7 @@ private Exp compileBind(BindAst bind) {
* Compiles {@code SERVICE { ... }} into a KGRAM {@link Exp}.
*/
private Exp compileService(ServiceAst service) {
- Node endpoint = CoreseAstQueryBuilder.toNode(service.endpoint(), termResolver);
+ Node endpoint = termResolver.toNode(service.endpoint());
Exp endpointNode = Exp.create(Type.NODE, endpoint);
Query body = Query.create(compile(service.pattern()));
body.setService(true);
diff --git a/src/test/java/fr/inria/corese/core/next/query/impl/sparql/bridge/AstBackedExprTest.java b/src/test/java/fr/inria/corese/core/next/query/impl/sparql/bridge/AstBackedExprTest.java
new file mode 100644
index 000000000..af39c82a3
--- /dev/null
+++ b/src/test/java/fr/inria/corese/core/next/query/impl/sparql/bridge/AstBackedExprTest.java
@@ -0,0 +1,80 @@
+package fr.inria.corese.core.next.query.impl.sparql.bridge;
+
+import fr.inria.corese.core.next.query.impl.sparql.ast.IriAst;
+import fr.inria.corese.core.next.query.impl.sparql.ast.LiteralAst;
+import fr.inria.corese.core.next.query.impl.sparql.ast.PrefixDeclarationAst;
+import fr.inria.corese.core.next.query.impl.sparql.ast.QueryPrologueAst;
+import fr.inria.corese.core.next.query.impl.sparql.ast.VarAst;
+import fr.inria.corese.core.next.query.impl.sparql.ast.constraint.BoundAst;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+@DisplayName("AstBackedExpr: AST wrapping, value resolution, and filter view")
+class AstBackedExprTest {
+
+ @Test
+ void resolvesConstantUsingCompilerPrologue() {
+ QueryPrologueAst prologue = new QueryPrologueAst(
+ List.of(new PrefixDeclarationAst("ex:", new IriAst("http://example.org/"))),
+ null);
+ WhereCompiler compiler = new WhereCompiler().withPrologue(prologue);
+ AstBackedExpr expression = new AstBackedExpr(new IriAst("ex:name"), compiler);
+
+ assertEquals("http://example.org/name", expression.getValue().stringValue());
+ assertTrue(expression.isConstant());
+ assertFalse(expression.isVariable());
+ }
+
+ @Test
+ void resolvesLiteralValue() {
+ WhereCompiler compiler = new WhereCompiler();
+ AstBackedExpr stringExpr = new AstBackedExpr(new LiteralAst("\"hello\"", null, null), compiler);
+ assertEquals("hello", stringExpr.getValue().stringValue());
+
+ AstBackedExpr intExpr = new AstBackedExpr(new LiteralAst("42", null, "http://www.w3.org/2001/XMLSchema#integer"), compiler);
+ assertEquals("42", intExpr.getValue().stringValue());
+ }
+
+ @Test
+ void variableExpressionMetadata() {
+ WhereCompiler compiler = new WhereCompiler();
+ AstBackedExpr varExpr = new AstBackedExpr(new VarAst("x"), compiler);
+
+ assertEquals("x", varExpr.getLabel());
+ assertTrue(varExpr.isVariable());
+ assertFalse(varExpr.isConstant());
+ assertTrue(varExpr.getExpList().isEmpty());
+ assertNull(varExpr.getArg());
+ }
+
+ @Test
+ void boundExpressionAndFilterView() {
+ WhereCompiler compiler = new WhereCompiler();
+ BoundAst boundAst = new BoundAst(List.of(new VarAst("y")));
+ AstBackedExpr boundExpr = new AstBackedExpr(boundAst, compiler);
+
+ assertTrue(boundExpr.isBound());
+ assertNotNull(boundExpr.getFilter());
+ assertTrue(boundExpr.getFilter().isBound());
+ assertEquals(List.of("y"), boundExpr.getFilter().getVariables());
+ assertEquals(boundAst, boundExpr.getFilter().getFilterExpression());
+ }
+
+ @Test
+ void immutableExpressionsThrowOnModification() {
+ WhereCompiler compiler = new WhereCompiler();
+ AstBackedExpr expr = new AstBackedExpr(new VarAst("z"), compiler);
+
+ assertThrows(UnsupportedOperationException.class, () -> expr.setExp(0, expr));
+ assertThrows(UnsupportedOperationException.class, () -> expr.setArg(expr));
+ }
+}