You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jena.apache.org by an...@apache.org on 2024/08/28 09:15:44 UTC

(jena) branch gh2518-cdt updated: Adds support for SPARQL CDTs (lists and maps as literals) (#2501)

This is an automated email from the ASF dual-hosted git repository.

andy pushed a commit to branch gh2518-cdt
in repository https://gitbox.apache.org/repos/asf/jena.git


The following commit(s) were added to refs/heads/gh2518-cdt by this push:
     new 037eb9b79f Adds support for SPARQL CDTs (lists and maps as literals) (#2501)
037eb9b79f is described below

commit 037eb9b79fa3217d379e3793d1b0e3b1c200242c
Author: Olaf Hartig <oh...@amazon.com>
AuthorDate: Wed Aug 28 11:15:38 2024 +0200

    Adds support for SPARQL CDTs (lists and maps as literals) (#2501)
    
    GH-2518: SPARQL CDTs (lists and maps as literals)
---
 jena-arq/Grammar/CDTs/cdt_literals.jj              |  370 ++
 jena-arq/Grammar/CDTs/generate                     |   65 +
 jena-arq/Grammar/arq.jj                            |   56 +
 jena-arq/Grammar/main.jj                           |   74 +
 .../main/java/org/apache/jena/cdt/CDTFactory.java  |   64 +
 .../src/main/java/org/apache/jena/cdt/CDTKey.java  |   66 +
 .../CDTLiteralParseException.java}                 |   24 +-
 .../org/apache/jena/cdt/CDTLiteralParserBase.java  |   51 +
 .../main/java/org/apache/jena/cdt/CDTValue.java    |  144 +
 .../org/apache/jena/cdt/CompositeDatatypeBase.java |   60 +
 .../org/apache/jena/cdt/CompositeDatatypeList.java |  353 ++
 .../org/apache/jena/cdt/CompositeDatatypeMap.java  |  413 ++
 .../org/apache/jena/cdt/ParserForCDTLiterals.java  |  115 +
 .../apache/jena/cdt/parser/CDTLiteralParser.java   |  560 ++
 .../jena/cdt/parser/CDTLiteralParserConstants.java |  147 +
 .../cdt/parser/CDTLiteralParserTokenManager.java   | 1045 ++++
 .../sparql_11 => cdt/parser}/JavaCharStream.java   |  240 +-
 .../lang/arq => cdt/parser}/ParseException.java    |   68 +-
 .../{sparql/lang/arq => cdt/parser}/Token.java     |   27 +-
 .../lang/arq => cdt/parser}/TokenMgrError.java     |   56 +-
 .../src/main/java/org/apache/jena/query/ARQ.java   |    7 +
 .../main/java/org/apache/jena/riot/RDFParser.java  |    2 +-
 .../jena/riot/system/CDTAwareParserProfile.java    |  132 +
 .../java/org/apache/jena/riot/system/RiotLib.java  |    4 +-
 .../java/org/apache/jena/sparql/ARQConstants.java  |    3 +
 .../jena/sparql/algebra/AlgebraGenerator.java      |    4 +
 .../org/apache/jena/sparql/algebra/OpAsQuery.java  |    9 +
 .../org/apache/jena/sparql/algebra/OpVars.java     |   21 +
 .../org/apache/jena/sparql/algebra/OpVisitor.java  |    1 +
 .../apache/jena/sparql/algebra/OpVisitorBase.java  |    2 +
 .../jena/sparql/algebra/OpVisitorByType.java       |    4 +
 .../org/apache/jena/sparql/algebra/Transform.java  |    1 +
 .../apache/jena/sparql/algebra/TransformCopy.java  |    2 +
 .../jena/sparql/algebra/TransformSingle.java       |    2 +
 .../jena/sparql/algebra/TransformWrapper.java      |    2 +
 .../apache/jena/sparql/algebra/op/OpUnfold.java    |  102 +
 .../algebra/optimize/VariableUsageVisitor.java     |   12 +
 .../algebra/walker/ApplyTransformVisitor.java      |    9 +
 .../sparql/algebra/walker/OpVisitorByType.java     |    5 +
 .../algebra/walker/OpVisitorByTypeAndExpr.java     |    6 +
 .../jena/sparql/algebra/walker/WalkerVisitor.java  |    9 +
 .../sparql/engine/iterator/QueryIterUnfold.java    |  346 ++
 .../jena/sparql/engine/main/ExecutionDispatch.java |    7 +
 .../apache/jena/sparql/engine/main/OpExecutor.java |    6 +
 .../apache/jena/sparql/engine/main/VarFinder.java  |   14 +-
 .../apache/jena/sparql/engine/ref/Evaluator.java   |    2 +
 .../jena/sparql/engine/ref/EvaluatorDispatch.java  |    7 +
 .../jena/sparql/engine/ref/EvaluatorSimple.java    |    8 +
 .../org/apache/jena/sparql/expr/NodeValueCmp.java  |   57 +-
 .../org/apache/jena/sparql/expr/ValueSpace.java    |   11 +
 .../sparql/expr/aggregate/AccumulatorExpr.java     |    2 +-
 .../jena/sparql/expr/aggregate/AggFoldList.java    |  279 +
 .../jena/sparql/expr/aggregate/AggFoldMap.java     |  285 +
 .../jena/sparql/expr/aggregate/AggregatorBase.java |    3 +
 .../sparql/expr/aggregate/AggregatorFactory.java   |   10 +
 .../apache/jena/sparql/function/ARQFunctions.java  |    2 +
 .../library/cdt/CDTLiteralFunctionUtils.java       |  153 +
 .../function/library/cdt/CDTLiteralFunctions.java  |   43 +
 .../sparql/function/library/cdt/ConcatFct.java     |   61 +
 .../sparql/function/library/cdt/ContainsFct.java   |   52 +
 .../function/library/cdt/ContainsKeyFct.java       |   53 +
 .../function/library/cdt/ContainsTermFct.java      |   52 +
 .../cdt/FunctionBase1List.java}                    |   29 +-
 .../jena/sparql/function/library/cdt/GetFct.java   |  102 +
 .../cdt/HeadFct.java}                              |   39 +-
 .../cdt/KeysFct.java}                              |   39 +-
 .../jena/sparql/function/library/cdt/ListFct.java  |   65 +
 .../jena/sparql/function/library/cdt/MapFct.java   |  102 +
 .../cdt/MergeFct.java}                             |   41 +-
 .../jena/sparql/function/library/cdt/PutFct.java   |  112 +
 .../sparql/function/library/cdt/RemoveFct.java     |   58 +
 .../cdt/ReverseFct.java}                           |   38 +-
 .../jena/sparql/function/library/cdt/SizeFct.java  |   48 +
 .../sparql/function/library/cdt/SubSeqFct.java     |  101 +
 .../cdt/TailFct.java}                              |   31 +-
 .../apache/jena/sparql/lang/SyntaxVarScope.java    |   17 +
 .../org/apache/jena/sparql/lang/arq/ARQParser.java | 5775 ++++++++++----------
 .../apache/jena/sparql/lang/arq/ARQParserBase.java |   52 +
 .../jena/sparql/lang/arq/ARQParserConstants.java   |  374 +-
 .../sparql/lang/arq/ARQParserTokenManager.java     | 2514 +++++----
 .../jena/sparql/lang/arq/ParseException.java       |   48 +-
 .../jena/sparql/lang/arq/SimpleCharStream.java     |   21 +-
 .../org/apache/jena/sparql/lang/arq/Token.java     |    7 +-
 .../apache/jena/sparql/lang/arq/TokenMgrError.java |   36 +-
 .../jena/sparql/lang/sparql_11/JavaCharStream.java |    2 +-
 .../jena/sparql/serializer/FormatterElement.java   |   12 +
 .../main/java/org/apache/jena/sparql/sse/Tags.java |    1 +
 .../apache/jena/sparql/sse/writers/WriterOp.java   |   19 +
 .../apache/jena/sparql/syntax/ElementUnfold.java   |   85 +
 .../apache/jena/sparql/syntax/ElementVisitor.java  |    1 +
 .../jena/sparql/syntax/ElementVisitorBase.java     |    3 +
 .../apache/jena/sparql/syntax/ElementWalker.java   |    8 +
 .../jena/sparql/syntax/PatternVarsVisitor.java     |    7 +
 .../ApplyElementTransformVisitor.java              |   12 +
 .../syntax/syntaxtransform/ElementTransform.java   |    1 +
 .../ElementTransformCleanGroupsOfOne.java          |    1 +
 .../syntaxtransform/ElementTransformCopyBase.java  |    7 +
 .../syntaxtransform/ElementTransformIdentity.java  |    2 +
 .../apache/jena/external/Scripts_SPARQLCDTs.java}  |   29 +-
 jena-arq/testing/README.txt                        |    3 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-sparql-01.rq |   13 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-sparql-02.rq |   13 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-sparql-03.rq |   13 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-sparql-04.rq |   13 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-sparql-05.rq |   14 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-sparql-06.rq |   14 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-sparql-07.rq |   14 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-sparql-08.rq |   14 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-sparql-09.rq |   14 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-sparql-11.rq |   14 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-sparql-12.rq |   14 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-sparql-13.rq |   14 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-sparql-14.rq |   14 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-sparql-15.rq |   15 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-sparql-16.rq |   15 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-sparql-17.rq |   15 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-sparql-18.rq |   15 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-sparql-21.rq |   14 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-sparql-22.rq |   14 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-sparql-23.rq |   14 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-sparql-24.rq |   14 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-sparql-25.rq |   15 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-sparql-26.rq |   15 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-sparql-27.rq |   15 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-sparql-28.rq |   15 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-turtle-01.rq |   13 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-01.ttl        |    7 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-turtle-02.rq |   13 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-02.ttl        |    7 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-turtle-03.rq |   13 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-03.ttl        |    7 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-turtle-04.rq |   13 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-04.ttl        |    7 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-turtle-05.rq |   12 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-05.ttl        |    7 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-turtle-06.rq |   12 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-06.ttl        |    7 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-turtle-07.rq |   12 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-07.ttl        |    7 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-turtle-08.rq |   12 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-08.ttl        |    7 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-turtle-09.rq |   14 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-09.ttl        |    8 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-turtle-10.rq |   14 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-10.ttl        |    8 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-turtle-11.rq |   14 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-11.ttl        |    8 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-turtle-12.rq |   14 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-12.ttl        |    8 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-turtle-13.rq |   14 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-13.ttl        |    8 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-turtle-14.rq |   14 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-14.ttl        |    8 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-turtle-15.rq |   14 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-15a.ttl       |    7 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-15b.ttl       |    7 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-turtle-16.rq |   14 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-16a.ttl       |    7 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-16b.ttl       |    7 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-turtle-17.rq |   13 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-17a.ttl       |    7 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-17b.ttl       |    7 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-turtle-18.rq |   13 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-18a.ttl       |    7 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-18b.ttl       |    7 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-turtle-19.rq |   14 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-19a.ttl       |    7 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-19b.ttl       |    7 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-turtle-21.rq |   14 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-21.ttl        |    7 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-turtle-22.rq |   14 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-22.ttl        |    7 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-turtle-23.rq |   14 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-23.ttl        |    7 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-turtle-24.rq |   14 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-24.ttl        |    7 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-turtle-25.rq |   13 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-25.ttl        |    7 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-turtle-26.rq |   13 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-26.ttl        |    7 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-turtle-27.rq |   13 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-27.ttl        |    7 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-turtle-28.rq |   13 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-28.ttl        |    7 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-turtle-29.rq |   15 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-29.ttl        |    8 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-turtle-30.rq |   15 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-30.ttl        |    8 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-turtle-31.rq |   15 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-31.ttl        |    8 +
 .../testing/SPARQL-CDTs/bnodes/bnodes-turtle-32.rq |   15 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-32.ttl        |    8 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-41.ttl        |    7 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-42.ttl        |    7 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-43.ttl        |    7 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-44.ttl        |    7 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-45.ttl        |    7 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-46.ttl        |    7 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-47.ttl        |    7 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-48.ttl        |    7 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-49.ttl        |    8 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-50.ttl        |    8 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-51.ttl        |    8 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-52.ttl        |    8 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-sparql-01.rq  |   14 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-sparql-01.ttl |    7 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-sparql-02.rq  |   14 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-sparql-02.ttl |    7 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-sparql-03.rq  |   13 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-sparql-03.ttl |    7 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-sparql-04.rq  |   13 +
 .../SPARQL-CDTs/bnodes/bnodes-turtle-sparql-04.ttl |    7 +
 jena-arq/testing/SPARQL-CDTs/bnodes/empty.ttl      |    0
 jena-arq/testing/SPARQL-CDTs/bnodes/manifest.ttl   |  749 +++
 jena-arq/testing/SPARQL-CDTs/bnodes/true.srx       |    5 +
 jena-arq/testing/SPARQL-CDTs/fold/empty.ttl        |    0
 jena-arq/testing/SPARQL-CDTs/fold/fold-list-01.rq  |   11 +
 jena-arq/testing/SPARQL-CDTs/fold/fold-list-02.rq  |   11 +
 jena-arq/testing/SPARQL-CDTs/fold/fold-list-03.rq  |   15 +
 jena-arq/testing/SPARQL-CDTs/fold/fold-list-04.rq  |   21 +
 jena-arq/testing/SPARQL-CDTs/fold/fold-list-05.rq  |   24 +
 .../SPARQL-CDTs/fold/fold-list-distinct-01.rq      |   11 +
 .../SPARQL-CDTs/fold/fold-list-distinct-02.rq      |   18 +
 .../SPARQL-CDTs/fold/fold-list-distinct-03.rq      |   18 +
 .../SPARQL-CDTs/fold/fold-list-distinct-04.rq      |   21 +
 .../SPARQL-CDTs/fold/fold-list-distinct-05.rq      |   21 +
 .../SPARQL-CDTs/fold/fold-list-distinct-06.rq      |   21 +
 .../SPARQL-CDTs/fold/fold-list-distinct-07.rq      |   22 +
 .../SPARQL-CDTs/fold/fold-list-distinct-08.rq      |   22 +
 .../fold/fold-list-distinct-orderby-01.rq          |   31 +
 .../fold/fold-list-distinct-orderby-02.rq          |   22 +
 .../fold/fold-list-distinct-orderby-03.rq          |   40 +
 .../SPARQL-CDTs/fold/fold-list-orderby-01.rq       |   11 +
 .../SPARQL-CDTs/fold/fold-list-orderby-02.rq       |   11 +
 .../SPARQL-CDTs/fold/fold-list-orderby-03.rq       |   15 +
 .../SPARQL-CDTs/fold/fold-list-orderby-04.rq       |   18 +
 .../SPARQL-CDTs/fold/fold-list-orderby-05.rq       |   15 +
 .../SPARQL-CDTs/fold/fold-list-orderby-06.rq       |   16 +
 jena-arq/testing/SPARQL-CDTs/fold/fold-map-01.rq   |   11 +
 jena-arq/testing/SPARQL-CDTs/fold/fold-map-02.rq   |   15 +
 jena-arq/testing/SPARQL-CDTs/fold/fold-map-03.rq   |   19 +
 jena-arq/testing/SPARQL-CDTs/fold/fold-map-04.rq   |   15 +
 jena-arq/testing/SPARQL-CDTs/fold/fold-map-05.rq   |   19 +
 jena-arq/testing/SPARQL-CDTs/fold/fold-map-06.rq   |   15 +
 .../SPARQL-CDTs/fold/fold-map-orderby-01.rq        |   16 +
 .../SPARQL-CDTs/fold/fold-map-orderby-02.rq        |   16 +
 jena-arq/testing/SPARQL-CDTs/fold/manifest.ttl     |  354 ++
 jena-arq/testing/SPARQL-CDTs/fold/true.srx         |    5 +
 .../SPARQL-CDTs/list-functions/concat-01.rq        |    8 +
 .../SPARQL-CDTs/list-functions/concat-02.rq        |   10 +
 .../SPARQL-CDTs/list-functions/concat-03.rq        |   10 +
 .../SPARQL-CDTs/list-functions/concat-04.rq        |    9 +
 .../SPARQL-CDTs/list-functions/concat-05.rq        |    9 +
 .../SPARQL-CDTs/list-functions/concat-06.rq        |   10 +
 .../SPARQL-CDTs/list-functions/concat-07.rq        |   10 +
 .../SPARQL-CDTs/list-functions/concat-08.rq        |    8 +
 .../SPARQL-CDTs/list-functions/concat-09.rq        |    9 +
 .../SPARQL-CDTs/list-functions/concat-10.rq        |   10 +
 .../SPARQL-CDTs/list-functions/concat-error-01.rq  |    8 +
 .../SPARQL-CDTs/list-functions/concat-error-02.rq  |    7 +
 .../SPARQL-CDTs/list-functions/concat-null-01.rq   |   14 +
 .../SPARQL-CDTs/list-functions/contains-01.rq      |    8 +
 .../SPARQL-CDTs/list-functions/contains-02.rq      |    8 +
 .../SPARQL-CDTs/list-functions/contains-03.rq      |   22 +
 .../SPARQL-CDTs/list-functions/contains-04.rq      |    8 +
 .../SPARQL-CDTs/list-functions/contains-05.rq      |   11 +
 .../SPARQL-CDTs/list-functions/contains-06.rq      |   11 +
 .../SPARQL-CDTs/list-functions/contains-07.rq      |   11 +
 .../SPARQL-CDTs/list-functions/contains-08.rq      |   11 +
 .../SPARQL-CDTs/list-functions/contains-09.rq      |   12 +
 .../SPARQL-CDTs/list-functions/contains-10.rq      |   12 +
 .../list-functions/contains-error-01.rq            |    8 +
 .../SPARQL-CDTs/list-functions/contains-null-01.rq |    9 +
 .../testing/SPARQL-CDTs/list-functions/empty.ttl   |    0
 .../testing/SPARQL-CDTs/list-functions/get-01.rq   |    7 +
 .../testing/SPARQL-CDTs/list-functions/get-02.rq   |    8 +
 .../testing/SPARQL-CDTs/list-functions/get-03.rq   |    7 +
 .../testing/SPARQL-CDTs/list-functions/get-04.rq   |    8 +
 .../testing/SPARQL-CDTs/list-functions/get-05.rq   |    8 +
 .../testing/SPARQL-CDTs/list-functions/get-06.rq   |   11 +
 .../testing/SPARQL-CDTs/list-functions/get-07.rq   |    9 +
 .../testing/SPARQL-CDTs/list-functions/get-08.rq   |   10 +
 .../testing/SPARQL-CDTs/list-functions/get-09.rq   |    9 +
 .../testing/SPARQL-CDTs/list-functions/get-10.rq   |    9 +
 .../testing/SPARQL-CDTs/list-functions/get-11.rq   |   12 +
 .../testing/SPARQL-CDTs/list-functions/get-12.rq   |   12 +
 .../testing/SPARQL-CDTs/list-functions/get-13.rq   |   12 +
 .../SPARQL-CDTs/list-functions/get-error-01.rq     |    7 +
 .../SPARQL-CDTs/list-functions/get-error-02.rq     |    7 +
 .../SPARQL-CDTs/list-functions/get-error-03.rq     |    7 +
 .../SPARQL-CDTs/list-functions/get-error-04.rq     |    7 +
 .../SPARQL-CDTs/list-functions/get-error-05.rq     |    7 +
 .../SPARQL-CDTs/list-functions/get-error-06.rq     |    7 +
 .../SPARQL-CDTs/list-functions/get-null-01.rq      |    7 +
 .../SPARQL-CDTs/list-functions/get-null-02.rq      |    7 +
 .../testing/SPARQL-CDTs/list-functions/head-01.rq  |    8 +
 .../testing/SPARQL-CDTs/list-functions/head-02.rq  |    7 +
 .../testing/SPARQL-CDTs/list-functions/head-03.rq  |    7 +
 .../testing/SPARQL-CDTs/list-functions/head-04.rq  |    7 +
 .../testing/SPARQL-CDTs/list-functions/head-05.rq  |    7 +
 .../testing/SPARQL-CDTs/list-functions/head-06.rq  |    7 +
 .../testing/SPARQL-CDTs/list-functions/head-07.rq  |    8 +
 .../testing/SPARQL-CDTs/list-functions/head-08.rq  |    7 +
 .../testing/SPARQL-CDTs/list-functions/head-09.rq  |    7 +
 .../testing/SPARQL-CDTs/list-functions/head-10.rq  |    8 +
 .../testing/SPARQL-CDTs/list-functions/head-11.rq  |    8 +
 .../testing/SPARQL-CDTs/list-functions/head-12.rq  |   11 +
 .../SPARQL-CDTs/list-functions/head-error-01.rq    |    8 +
 .../SPARQL-CDTs/list-functions/head-null-01.rq     |    7 +
 .../SPARQL-CDTs/list-functions/head-null-02.rq     |    7 +
 .../list-functions/list-constructor-01.rq          |    7 +
 .../list-functions/list-constructor-02.rq          |    8 +
 .../list-functions/list-constructor-03.rq          |    8 +
 .../list-functions/list-constructor-04.rq          |    8 +
 .../list-functions/list-constructor-05.rq          |    8 +
 .../list-functions/list-constructor-06.rq          |    8 +
 .../list-functions/list-constructor-09.rq          |    8 +
 .../list-functions/list-constructor-10.rq          |    8 +
 .../list-functions/list-constructor-11.rq          |    8 +
 .../list-functions/list-constructor-12.rq          |   12 +
 .../list-functions/list-constructor-13.rq          |    9 +
 .../list-functions/list-constructor-14.rq          |    8 +
 .../list-functions/list-constructor-15.rq          |    9 +
 .../list-functions/list-constructor-16.rq          |   12 +
 .../list-functions/list-constructor-null-01.rq     |   12 +
 .../list-functions/list-constructor-null-02.rq     |   12 +
 .../SPARQL-CDTs/list-functions/list-equals-01.rq   |    6 +
 .../SPARQL-CDTs/list-functions/list-equals-02.rq   |    6 +
 .../SPARQL-CDTs/list-functions/list-equals-03.rq   |    6 +
 .../SPARQL-CDTs/list-functions/list-equals-04.rq   |    6 +
 .../SPARQL-CDTs/list-functions/list-equals-05.rq   |    6 +
 .../SPARQL-CDTs/list-functions/list-equals-06.rq   |    7 +
 .../SPARQL-CDTs/list-functions/list-equals-07.rq   |    7 +
 .../SPARQL-CDTs/list-functions/list-equals-08.rq   |    9 +
 .../SPARQL-CDTs/list-functions/list-equals-09.rq   |    8 +
 .../list-functions/list-equals-null-01.rq          |    7 +
 .../list-functions/list-equals-null-02.rq          |    7 +
 .../list-functions/list-greater-equal-01.rq        |    8 +
 .../list-functions/list-greater-equal-02.rq        |    8 +
 .../list-functions/list-greater-equal-03.rq        |    8 +
 .../list-functions/list-greater-equal-04.rq        |    8 +
 .../list-functions/list-greater-equal-05.rq        |    8 +
 .../list-functions/list-greater-equal-06.rq        |    8 +
 .../list-functions/list-greater-equal-07.rq        |    8 +
 .../list-functions/list-greater-equal-08.rq        |    8 +
 .../list-functions/list-greater-equal-09.rq        |    8 +
 .../list-functions/list-greater-equal-10.rq        |    8 +
 .../list-functions/list-greater-equal-11.rq        |    8 +
 .../list-functions/list-greater-equal-12.rq        |    8 +
 .../list-functions/list-greater-equal-13.rq        |    8 +
 .../list-functions/list-greater-equal-14.rq        |    8 +
 .../list-functions/list-greater-equal-15.rq        |    8 +
 .../list-functions/list-greater-equal-16.rq        |    8 +
 .../list-functions/list-greater-equal-19.rq        |    8 +
 .../list-functions/list-greater-equal-20.rq        |    8 +
 .../list-functions/list-greater-equal-21.rq        |    8 +
 .../list-functions/list-greater-equal-22.rq        |    8 +
 .../list-functions/list-greater-equal-23.rq        |    8 +
 .../list-functions/list-greater-equal-24.rq        |    8 +
 .../list-functions/list-greater-equal-25.rq        |    8 +
 .../list-functions/list-greater-equal-26.rq        |    8 +
 .../list-functions/list-greater-equal-27.rq        |    9 +
 .../list-functions/list-greater-equal-28.rq        |    7 +
 .../list-functions/list-greater-equal-29.rq        |    8 +
 .../list-functions/list-greater-equal-31.rq        |    8 +
 .../list-functions/list-greater-equal-error-03.rq  |    8 +
 .../list-functions/list-greater-equal-error-04.rq  |    8 +
 .../list-functions/list-greater-equal-null-01.rq   |    8 +
 .../list-functions/list-greater-equal-null-02.rq   |    8 +
 .../list-functions/list-greater-equal-null-03.rq   |    7 +
 .../list-functions/list-greater-equal-null-04.rq   |   11 +
 .../list-functions/list-greater-equal-null-05.rq   |    8 +
 .../list-functions/list-greater-equal-null-06.rq   |    9 +
 .../list-functions/list-greater-than-01.rq         |    8 +
 .../list-functions/list-greater-than-02.rq         |    8 +
 .../list-functions/list-greater-than-03.rq         |    8 +
 .../list-functions/list-greater-than-04.rq         |    8 +
 .../list-functions/list-greater-than-05.rq         |    8 +
 .../list-functions/list-greater-than-06.rq         |    8 +
 .../list-functions/list-greater-than-07.rq         |    8 +
 .../list-functions/list-greater-than-08.rq         |    8 +
 .../list-functions/list-greater-than-09.rq         |    8 +
 .../list-functions/list-greater-than-10.rq         |    8 +
 .../list-functions/list-greater-than-11.rq         |    8 +
 .../list-functions/list-greater-than-12.rq         |    8 +
 .../list-functions/list-greater-than-13.rq         |    8 +
 .../list-functions/list-greater-than-14.rq         |    8 +
 .../list-functions/list-greater-than-15.rq         |    8 +
 .../list-functions/list-greater-than-16.rq         |    8 +
 .../list-functions/list-greater-than-19.rq         |    8 +
 .../list-functions/list-greater-than-20.rq         |    8 +
 .../list-functions/list-greater-than-21.rq         |    8 +
 .../list-functions/list-greater-than-22.rq         |    8 +
 .../list-functions/list-greater-than-23.rq         |    8 +
 .../list-functions/list-greater-than-24.rq         |    8 +
 .../list-functions/list-greater-than-25.rq         |    8 +
 .../list-functions/list-greater-than-26.rq         |    8 +
 .../list-functions/list-greater-than-27.rq         |    9 +
 .../list-functions/list-greater-than-28.rq         |    7 +
 .../list-functions/list-greater-than-29.rq         |    8 +
 .../list-functions/list-greater-than-31.rq         |    8 +
 .../list-functions/list-greater-than-error-03.rq   |    8 +
 .../list-functions/list-greater-than-error-04.rq   |    8 +
 .../list-functions/list-greater-than-null-01.rq    |    8 +
 .../list-functions/list-greater-than-null-02.rq    |    8 +
 .../list-functions/list-greater-than-null-03.rq    |    9 +
 .../list-functions/list-greater-than-null-04.rq    |   11 +
 .../list-functions/list-greater-than-null-05.rq    |    8 +
 .../list-functions/list-greater-than-null-06.rq    |    9 +
 .../list-functions/list-less-equal-01.rq           |    8 +
 .../list-functions/list-less-equal-02.rq           |    8 +
 .../list-functions/list-less-equal-03.rq           |    8 +
 .../list-functions/list-less-equal-04.rq           |    8 +
 .../list-functions/list-less-equal-05.rq           |    8 +
 .../list-functions/list-less-equal-06.rq           |    8 +
 .../list-functions/list-less-equal-07.rq           |    8 +
 .../list-functions/list-less-equal-08.rq           |    8 +
 .../list-functions/list-less-equal-09.rq           |    8 +
 .../list-functions/list-less-equal-10.rq           |    8 +
 .../list-functions/list-less-equal-11.rq           |    8 +
 .../list-functions/list-less-equal-12.rq           |    8 +
 .../list-functions/list-less-equal-13.rq           |    8 +
 .../list-functions/list-less-equal-14.rq           |    8 +
 .../list-functions/list-less-equal-15.rq           |    8 +
 .../list-functions/list-less-equal-16.rq           |    8 +
 .../list-functions/list-less-equal-19.rq           |    8 +
 .../list-functions/list-less-equal-20.rq           |    8 +
 .../list-functions/list-less-equal-21.rq           |    8 +
 .../list-functions/list-less-equal-22.rq           |    8 +
 .../list-functions/list-less-equal-23.rq           |    8 +
 .../list-functions/list-less-equal-24.rq           |    8 +
 .../list-functions/list-less-equal-25.rq           |    8 +
 .../list-functions/list-less-equal-26.rq           |    8 +
 .../list-functions/list-less-equal-27.rq           |    9 +
 .../list-functions/list-less-equal-28.rq           |    7 +
 .../list-functions/list-less-equal-29.rq           |    8 +
 .../list-functions/list-less-equal-31.rq           |    8 +
 .../list-functions/list-less-equal-error-03.rq     |    8 +
 .../list-functions/list-less-equal-error-04.rq     |    8 +
 .../list-functions/list-less-equal-null-01.rq      |    8 +
 .../list-functions/list-less-equal-null-02.rq      |    8 +
 .../list-functions/list-less-equal-null-03.rq      |    9 +
 .../list-functions/list-less-equal-null-04.rq      |   11 +
 .../list-functions/list-less-equal-null-05.rq      |    8 +
 .../list-functions/list-less-equal-null-06.rq      |    9 +
 .../list-functions/list-less-than-01.rq            |    8 +
 .../list-functions/list-less-than-02.rq            |    8 +
 .../list-functions/list-less-than-03.rq            |    8 +
 .../list-functions/list-less-than-04.rq            |    8 +
 .../list-functions/list-less-than-05.rq            |    8 +
 .../list-functions/list-less-than-06.rq            |    8 +
 .../list-functions/list-less-than-07.rq            |    8 +
 .../list-functions/list-less-than-08.rq            |    8 +
 .../list-functions/list-less-than-09.rq            |    8 +
 .../list-functions/list-less-than-10.rq            |    8 +
 .../list-functions/list-less-than-11.rq            |    8 +
 .../list-functions/list-less-than-12.rq            |    8 +
 .../list-functions/list-less-than-13.rq            |    8 +
 .../list-functions/list-less-than-14.rq            |    8 +
 .../list-functions/list-less-than-15.rq            |    8 +
 .../list-functions/list-less-than-16.rq            |    8 +
 .../list-functions/list-less-than-19.rq            |    8 +
 .../list-functions/list-less-than-20.rq            |    8 +
 .../list-functions/list-less-than-21.rq            |    8 +
 .../list-functions/list-less-than-22.rq            |    8 +
 .../list-functions/list-less-than-23.rq            |    8 +
 .../list-functions/list-less-than-24.rq            |    8 +
 .../list-functions/list-less-than-25.rq            |    8 +
 .../list-functions/list-less-than-26.rq            |    8 +
 .../list-functions/list-less-than-27.rq            |    9 +
 .../list-functions/list-less-than-28.rq            |    7 +
 .../list-functions/list-less-than-29.rq            |    8 +
 .../list-functions/list-less-than-31.rq            |    8 +
 .../list-functions/list-less-than-error-03.rq      |    8 +
 .../list-functions/list-less-than-error-04.rq      |    8 +
 .../list-functions/list-less-than-null-01.rq       |    8 +
 .../list-functions/list-less-than-null-02.rq       |    8 +
 .../list-functions/list-less-than-null-03.rq       |    9 +
 .../list-functions/list-less-than-null-04.rq       |   11 +
 .../list-functions/list-less-than-null-05.rq       |    8 +
 .../list-functions/list-less-than-null-06.rq       |    9 +
 .../SPARQL-CDTs/list-functions/manifest.ttl        | 3226 +++++++++++
 .../SPARQL-CDTs/list-functions/reverse-01.rq       |    7 +
 .../SPARQL-CDTs/list-functions/reverse-02.rq       |    7 +
 .../SPARQL-CDTs/list-functions/reverse-03.rq       |    7 +
 .../SPARQL-CDTs/list-functions/reverse-04.rq       |    7 +
 .../SPARQL-CDTs/list-functions/reverse-05.rq       |    7 +
 .../SPARQL-CDTs/list-functions/reverse-06.rq       |    7 +
 .../SPARQL-CDTs/list-functions/reverse-07.rq       |    9 +
 .../SPARQL-CDTs/list-functions/reverse-08.rq       |    7 +
 .../SPARQL-CDTs/list-functions/reverse-09.rq       |    7 +
 .../SPARQL-CDTs/list-functions/reverse-10.rq       |    9 +
 .../SPARQL-CDTs/list-functions/reverse-error-01.rq |    8 +
 .../SPARQL-CDTs/list-functions/sameterm-01.rq      |    6 +
 .../SPARQL-CDTs/list-functions/sameterm-02.rq      |    6 +
 .../SPARQL-CDTs/list-functions/sameterm-03.rq      |    6 +
 .../SPARQL-CDTs/list-functions/sameterm-04.rq      |    6 +
 .../SPARQL-CDTs/list-functions/sameterm-null-01.rq |    6 +
 .../testing/SPARQL-CDTs/list-functions/size-01.rq  |    7 +
 .../testing/SPARQL-CDTs/list-functions/size-02.rq  |    7 +
 .../testing/SPARQL-CDTs/list-functions/size-03.rq  |    7 +
 .../testing/SPARQL-CDTs/list-functions/size-04.rq  |    7 +
 .../testing/SPARQL-CDTs/list-functions/size-05.rq  |    7 +
 .../testing/SPARQL-CDTs/list-functions/size-06.rq  |    7 +
 .../testing/SPARQL-CDTs/list-functions/size-07.rq  |    7 +
 .../testing/SPARQL-CDTs/list-functions/size-08.rq  |    7 +
 .../testing/SPARQL-CDTs/list-functions/size-09.rq  |    7 +
 .../testing/SPARQL-CDTs/list-functions/size-10.rq  |    7 +
 .../testing/SPARQL-CDTs/list-functions/size-11.rq  |    7 +
 .../testing/SPARQL-CDTs/list-functions/size-12.rq  |    7 +
 .../SPARQL-CDTs/list-functions/size-error-01.rq    |    8 +
 .../SPARQL-CDTs/list-functions/subseq-01.rq        |    7 +
 .../SPARQL-CDTs/list-functions/subseq-02.rq        |    7 +
 .../SPARQL-CDTs/list-functions/subseq-03.rq        |    7 +
 .../SPARQL-CDTs/list-functions/subseq-04.rq        |    7 +
 .../SPARQL-CDTs/list-functions/subseq-05.rq        |    7 +
 .../SPARQL-CDTs/list-functions/subseq-06.rq        |    7 +
 .../SPARQL-CDTs/list-functions/subseq-07.rq        |    7 +
 .../SPARQL-CDTs/list-functions/subseq-08.rq        |    8 +
 .../SPARQL-CDTs/list-functions/subseq-09.rq        |    8 +
 .../SPARQL-CDTs/list-functions/subseq-10.rq        |    8 +
 .../SPARQL-CDTs/list-functions/subseq-11.rq        |    8 +
 .../SPARQL-CDTs/list-functions/subseq-12.rq        |    8 +
 .../SPARQL-CDTs/list-functions/subseq-13.rq        |    8 +
 .../SPARQL-CDTs/list-functions/subseq-error-01.rq  |    8 +
 .../SPARQL-CDTs/list-functions/subseq-error-02.rq  |    8 +
 .../testing/SPARQL-CDTs/list-functions/tail-01.rq  |    8 +
 .../testing/SPARQL-CDTs/list-functions/tail-02.rq  |    7 +
 .../testing/SPARQL-CDTs/list-functions/tail-03.rq  |    7 +
 .../testing/SPARQL-CDTs/list-functions/tail-04.rq  |    7 +
 .../testing/SPARQL-CDTs/list-functions/tail-05.rq  |    7 +
 .../testing/SPARQL-CDTs/list-functions/tail-06.rq  |    7 +
 .../testing/SPARQL-CDTs/list-functions/tail-07.rq  |    7 +
 .../testing/SPARQL-CDTs/list-functions/tail-08.rq  |    7 +
 .../testing/SPARQL-CDTs/list-functions/tail-09.rq  |    7 +
 .../testing/SPARQL-CDTs/list-functions/tail-10.rq  |    7 +
 .../SPARQL-CDTs/list-functions/tail-error-01.rq    |    8 +
 .../testing/SPARQL-CDTs/list-functions/true.srx    |    5 +
 jena-arq/testing/SPARQL-CDTs/manifest-all.ttl      |   16 +
 .../SPARQL-CDTs/map-functions/containsKey-01.rq    |    9 +
 .../SPARQL-CDTs/map-functions/containsKey-02.rq    |    9 +
 .../SPARQL-CDTs/map-functions/containsKey-03.rq    |   11 +
 .../testing/SPARQL-CDTs/map-functions/empty.ttl    |    0
 .../testing/SPARQL-CDTs/map-functions/get-01.rq    |    9 +
 .../testing/SPARQL-CDTs/map-functions/get-02.rq    |    9 +
 .../testing/SPARQL-CDTs/map-functions/get-03.rq    |   12 +
 .../testing/SPARQL-CDTs/map-functions/get-04.rq    |   12 +
 .../SPARQL-CDTs/map-functions/get-error-01.rq      |    8 +
 .../SPARQL-CDTs/map-functions/get-null-01.rq       |    8 +
 .../testing/SPARQL-CDTs/map-functions/keys-01.rq   |    7 +
 .../testing/SPARQL-CDTs/map-functions/keys-02.rq   |    7 +
 .../testing/SPARQL-CDTs/map-functions/keys-03.rq   |   10 +
 .../testing/SPARQL-CDTs/map-functions/manifest.ttl | 2081 +++++++
 .../map-functions/map-constructor-01.rq            |    7 +
 .../map-functions/map-constructor-02.rq            |    7 +
 .../map-functions/map-constructor-03.rq            |    7 +
 .../map-functions/map-constructor-04.rq            |    7 +
 .../map-functions/map-constructor-05.rq            |    7 +
 .../map-functions/map-constructor-06.rq            |    7 +
 .../map-functions/map-constructor-07.rq            |    7 +
 .../map-functions/map-constructor-08.rq            |    7 +
 .../map-functions/map-constructor-09.rq            |    7 +
 .../map-functions/map-constructor-10.rq            |   15 +
 .../map-functions/map-constructor-11.rq            |   17 +
 .../SPARQL-CDTs/map-functions/map-equals-01.rq     |    6 +
 .../SPARQL-CDTs/map-functions/map-equals-02.rq     |    6 +
 .../SPARQL-CDTs/map-functions/map-equals-03.rq     |    6 +
 .../SPARQL-CDTs/map-functions/map-equals-04.rq     |    7 +
 .../SPARQL-CDTs/map-functions/map-equals-05.rq     |    6 +
 .../SPARQL-CDTs/map-functions/map-equals-06.rq     |    7 +
 .../SPARQL-CDTs/map-functions/map-equals-07.rq     |    7 +
 .../SPARQL-CDTs/map-functions/map-equals-08.rq     |    9 +
 .../SPARQL-CDTs/map-functions/map-equals-09.rq     |    8 +
 .../map-functions/map-equals-null-01.rq            |    9 +
 .../map-functions/map-equals-null-02.rq            |    9 +
 .../map-functions/map-greater-equal-01.rq          |    8 +
 .../map-functions/map-greater-equal-02.rq          |    8 +
 .../map-functions/map-greater-equal-03.rq          |    8 +
 .../map-functions/map-greater-equal-04.rq          |    8 +
 .../map-functions/map-greater-equal-05.rq          |    8 +
 .../map-functions/map-greater-equal-06.rq          |    8 +
 .../map-functions/map-greater-equal-07.rq          |    8 +
 .../map-functions/map-greater-equal-08.rq          |    8 +
 .../map-functions/map-greater-equal-09.rq          |    8 +
 .../map-functions/map-greater-equal-10.rq          |    8 +
 .../map-functions/map-greater-equal-11.rq          |    8 +
 .../map-functions/map-greater-equal-12.rq          |    8 +
 .../map-functions/map-greater-equal-13.rq          |    8 +
 .../map-functions/map-greater-equal-14.rq          |    8 +
 .../map-functions/map-greater-equal-15.rq          |   10 +
 .../map-functions/map-greater-equal-16.rq          |   10 +
 .../map-functions/map-greater-equal-17.rq          |   10 +
 .../map-functions/map-greater-equal-18.rq          |   10 +
 .../map-functions/map-greater-equal-19.rq          |   10 +
 .../map-functions/map-greater-equal-20.rq          |   10 +
 .../map-functions/map-greater-equal-21.rq          |   10 +
 .../map-functions/map-greater-equal-error-01.rq    |   10 +
 .../map-functions/map-greater-equal-error-02.rq    |   10 +
 .../map-functions/map-greater-equal-null-01.rq     |    7 +
 .../map-functions/map-greater-equal-null-02.rq     |    8 +
 .../map-functions/map-greater-equal-null-03.rq     |    9 +
 .../map-functions/map-greater-equal-null-04.rq     |    9 +
 .../map-functions/map-greater-than-01.rq           |    8 +
 .../map-functions/map-greater-than-02.rq           |    8 +
 .../map-functions/map-greater-than-03.rq           |    8 +
 .../map-functions/map-greater-than-04.rq           |    8 +
 .../map-functions/map-greater-than-05.rq           |    8 +
 .../map-functions/map-greater-than-06.rq           |    8 +
 .../map-functions/map-greater-than-07.rq           |    8 +
 .../map-functions/map-greater-than-08.rq           |    8 +
 .../map-functions/map-greater-than-09.rq           |    8 +
 .../map-functions/map-greater-than-10.rq           |    8 +
 .../map-functions/map-greater-than-11.rq           |    8 +
 .../map-functions/map-greater-than-12.rq           |    8 +
 .../map-functions/map-greater-than-13.rq           |    8 +
 .../map-functions/map-greater-than-14.rq           |    8 +
 .../map-functions/map-greater-than-15.rq           |   10 +
 .../map-functions/map-greater-than-16.rq           |   10 +
 .../map-functions/map-greater-than-17.rq           |   10 +
 .../map-functions/map-greater-than-18.rq           |   10 +
 .../map-functions/map-greater-than-19.rq           |   10 +
 .../map-functions/map-greater-than-20.rq           |   10 +
 .../map-functions/map-greater-than-21.rq           |   10 +
 .../map-functions/map-greater-than-error-01.rq     |   10 +
 .../map-functions/map-greater-than-error-02.rq     |   10 +
 .../map-functions/map-greater-than-null-01.rq      |    7 +
 .../map-functions/map-greater-than-null-02.rq      |    8 +
 .../map-functions/map-greater-than-null-03.rq      |    9 +
 .../map-functions/map-greater-than-null-04.rq      |    9 +
 .../SPARQL-CDTs/map-functions/map-less-equal-01.rq |    8 +
 .../SPARQL-CDTs/map-functions/map-less-equal-02.rq |    8 +
 .../SPARQL-CDTs/map-functions/map-less-equal-03.rq |    8 +
 .../SPARQL-CDTs/map-functions/map-less-equal-04.rq |    8 +
 .../SPARQL-CDTs/map-functions/map-less-equal-05.rq |    8 +
 .../SPARQL-CDTs/map-functions/map-less-equal-06.rq |    8 +
 .../SPARQL-CDTs/map-functions/map-less-equal-07.rq |    8 +
 .../SPARQL-CDTs/map-functions/map-less-equal-08.rq |    8 +
 .../SPARQL-CDTs/map-functions/map-less-equal-09.rq |    8 +
 .../SPARQL-CDTs/map-functions/map-less-equal-10.rq |    8 +
 .../SPARQL-CDTs/map-functions/map-less-equal-11.rq |    8 +
 .../SPARQL-CDTs/map-functions/map-less-equal-12.rq |    8 +
 .../SPARQL-CDTs/map-functions/map-less-equal-13.rq |    8 +
 .../SPARQL-CDTs/map-functions/map-less-equal-14.rq |    8 +
 .../SPARQL-CDTs/map-functions/map-less-equal-15.rq |   10 +
 .../SPARQL-CDTs/map-functions/map-less-equal-16.rq |   10 +
 .../SPARQL-CDTs/map-functions/map-less-equal-17.rq |   10 +
 .../SPARQL-CDTs/map-functions/map-less-equal-18.rq |   10 +
 .../SPARQL-CDTs/map-functions/map-less-equal-19.rq |   10 +
 .../SPARQL-CDTs/map-functions/map-less-equal-20.rq |   10 +
 .../SPARQL-CDTs/map-functions/map-less-equal-21.rq |   10 +
 .../map-functions/map-less-equal-error-01.rq       |   10 +
 .../map-functions/map-less-equal-error-02.rq       |   10 +
 .../map-functions/map-less-equal-null-01.rq        |    7 +
 .../map-functions/map-less-equal-null-02.rq        |    8 +
 .../map-functions/map-less-equal-null-03.rq        |    9 +
 .../map-functions/map-less-equal-null-04.rq        |    9 +
 .../SPARQL-CDTs/map-functions/map-less-than-01.rq  |    8 +
 .../SPARQL-CDTs/map-functions/map-less-than-02.rq  |    8 +
 .../SPARQL-CDTs/map-functions/map-less-than-03.rq  |    8 +
 .../SPARQL-CDTs/map-functions/map-less-than-04.rq  |    8 +
 .../SPARQL-CDTs/map-functions/map-less-than-05.rq  |    8 +
 .../SPARQL-CDTs/map-functions/map-less-than-06.rq  |    8 +
 .../SPARQL-CDTs/map-functions/map-less-than-07.rq  |    8 +
 .../SPARQL-CDTs/map-functions/map-less-than-08.rq  |    8 +
 .../SPARQL-CDTs/map-functions/map-less-than-09.rq  |    8 +
 .../SPARQL-CDTs/map-functions/map-less-than-10.rq  |    8 +
 .../SPARQL-CDTs/map-functions/map-less-than-11.rq  |    8 +
 .../SPARQL-CDTs/map-functions/map-less-than-12.rq  |    8 +
 .../SPARQL-CDTs/map-functions/map-less-than-13.rq  |    8 +
 .../SPARQL-CDTs/map-functions/map-less-than-14.rq  |    8 +
 .../SPARQL-CDTs/map-functions/map-less-than-15.rq  |   10 +
 .../SPARQL-CDTs/map-functions/map-less-than-16.rq  |   10 +
 .../SPARQL-CDTs/map-functions/map-less-than-17.rq  |   10 +
 .../SPARQL-CDTs/map-functions/map-less-than-18.rq  |   10 +
 .../SPARQL-CDTs/map-functions/map-less-than-19.rq  |   10 +
 .../SPARQL-CDTs/map-functions/map-less-than-20.rq  |   10 +
 .../SPARQL-CDTs/map-functions/map-less-than-21.rq  |   10 +
 .../map-functions/map-less-than-error-01.rq        |   10 +
 .../map-functions/map-less-than-error-02.rq        |   10 +
 .../map-functions/map-less-than-null-01.rq         |    7 +
 .../map-functions/map-less-than-null-02.rq         |    8 +
 .../map-functions/map-less-than-null-03.rq         |    9 +
 .../map-functions/map-less-than-null-04.rq         |    9 +
 .../testing/SPARQL-CDTs/map-functions/merge-01.rq  |   10 +
 .../testing/SPARQL-CDTs/map-functions/merge-02.rq  |   10 +
 .../testing/SPARQL-CDTs/map-functions/merge-03.rq  |   10 +
 .../testing/SPARQL-CDTs/map-functions/merge-04.rq  |   10 +
 .../testing/SPARQL-CDTs/map-functions/merge-05.rq  |   10 +
 .../testing/SPARQL-CDTs/map-functions/merge-06.rq  |   10 +
 .../testing/SPARQL-CDTs/map-functions/merge-07.rq  |   10 +
 .../testing/SPARQL-CDTs/map-functions/merge-08.rq  |   10 +
 .../SPARQL-CDTs/map-functions/merge-null-01.rq     |   18 +
 .../SPARQL-CDTs/map-functions/merge-null-02.rq     |   18 +
 .../SPARQL-CDTs/map-functions/merge-null-03.rq     |   14 +
 .../SPARQL-CDTs/map-functions/merge-null-04.rq     |   10 +
 .../testing/SPARQL-CDTs/map-functions/put-01.rq    |    9 +
 .../testing/SPARQL-CDTs/map-functions/put-02.rq    |   12 +
 .../testing/SPARQL-CDTs/map-functions/put-03.rq    |   12 +
 .../testing/SPARQL-CDTs/map-functions/put-04.rq    |    9 +
 .../testing/SPARQL-CDTs/map-functions/put-05.rq    |    9 +
 .../testing/SPARQL-CDTs/map-functions/put-06.rq    |    9 +
 .../testing/SPARQL-CDTs/map-functions/put-07.rq    |   15 +
 .../testing/SPARQL-CDTs/map-functions/put-08.rq    |   15 +
 .../testing/SPARQL-CDTs/map-functions/put-09.rq    |    9 +
 .../testing/SPARQL-CDTs/map-functions/put-10.rq    |   15 +
 .../testing/SPARQL-CDTs/map-functions/put-11.rq    |   15 +
 .../testing/SPARQL-CDTs/map-functions/put-12.rq    |   10 +
 .../testing/SPARQL-CDTs/map-functions/put-13.rq    |    9 +
 .../testing/SPARQL-CDTs/map-functions/put-14.rq    |    9 +
 .../testing/SPARQL-CDTs/map-functions/put-15.rq    |    9 +
 .../SPARQL-CDTs/map-functions/put-error-01.rq      |    8 +
 .../SPARQL-CDTs/map-functions/put-error-02.rq      |    8 +
 .../SPARQL-CDTs/map-functions/put-error-03.rq      |    8 +
 .../SPARQL-CDTs/map-functions/put-error-04.rq      |    8 +
 .../SPARQL-CDTs/map-functions/put-error-05.rq      |    7 +
 .../testing/SPARQL-CDTs/map-functions/remove-01.rq |   11 +
 .../testing/SPARQL-CDTs/map-functions/remove-02.rq |    9 +
 .../testing/SPARQL-CDTs/map-functions/remove-03.rq |    9 +
 .../testing/SPARQL-CDTs/map-functions/remove-04.rq |    9 +
 .../testing/SPARQL-CDTs/map-functions/remove-05.rq |   10 +
 .../testing/SPARQL-CDTs/map-functions/remove-06.rq |   10 +
 .../testing/SPARQL-CDTs/map-functions/remove-07.rq |   10 +
 .../testing/SPARQL-CDTs/map-functions/remove-08.rq |    9 +
 .../testing/SPARQL-CDTs/map-functions/remove-09.rq |    9 +
 .../testing/SPARQL-CDTs/map-functions/remove-10.rq |    9 +
 .../testing/SPARQL-CDTs/map-functions/remove-11.rq |    9 +
 .../SPARQL-CDTs/map-functions/sameterm-01.rq       |    6 +
 .../SPARQL-CDTs/map-functions/sameterm-02.rq       |    6 +
 .../SPARQL-CDTs/map-functions/sameterm-03.rq       |    6 +
 .../SPARQL-CDTs/map-functions/sameterm-04.rq       |    6 +
 .../SPARQL-CDTs/map-functions/sameterm-05.rq       |    6 +
 .../SPARQL-CDTs/map-functions/sameterm-null-01.rq  |    6 +
 .../testing/SPARQL-CDTs/map-functions/size-01.rq   |    7 +
 .../testing/SPARQL-CDTs/map-functions/size-02.rq   |    7 +
 .../testing/SPARQL-CDTs/map-functions/size-03.rq   |    7 +
 .../testing/SPARQL-CDTs/map-functions/size-04.rq   |    7 +
 .../testing/SPARQL-CDTs/map-functions/size-05.rq   |    7 +
 .../testing/SPARQL-CDTs/map-functions/true.srx     |    5 +
 jena-arq/testing/SPARQL-CDTs/orderby/empty.ttl     |    0
 jena-arq/testing/SPARQL-CDTs/orderby/manifest.ttl  |  291 +
 .../testing/SPARQL-CDTs/orderby/order-list-03.rq   |   15 +
 .../testing/SPARQL-CDTs/orderby/order-list-04.rq   |   15 +
 .../testing/SPARQL-CDTs/orderby/order-list-05.rq   |   18 +
 .../testing/SPARQL-CDTs/orderby/order-list-06.rq   |   15 +
 .../testing/SPARQL-CDTs/orderby/order-list-07.rq   |   15 +
 .../testing/SPARQL-CDTs/orderby/order-list-08.rq   |   15 +
 .../testing/SPARQL-CDTs/orderby/order-list-09.rq   |   15 +
 .../testing/SPARQL-CDTs/orderby/order-list-10.rq   |   15 +
 .../SPARQL-CDTs/orderby/order-list-null-01.rq      |   15 +
 .../SPARQL-CDTs/orderby/order-list-null-02.rq      |   16 +
 .../testing/SPARQL-CDTs/orderby/order-map-03.rq    |   15 +
 .../testing/SPARQL-CDTs/orderby/order-map-04.rq    |   15 +
 .../testing/SPARQL-CDTs/orderby/order-map-05.rq    |   15 +
 .../testing/SPARQL-CDTs/orderby/order-map-06.rq    |   15 +
 .../testing/SPARQL-CDTs/orderby/order-map-07.rq    |   15 +
 .../testing/SPARQL-CDTs/orderby/order-map-08.rq    |   15 +
 .../testing/SPARQL-CDTs/orderby/order-map-09.rq    |   15 +
 .../testing/SPARQL-CDTs/orderby/order-map-10.rq    |   15 +
 .../testing/SPARQL-CDTs/orderby/order-map-11.rq    |   15 +
 .../testing/SPARQL-CDTs/orderby/order-map-12.rq    |   18 +
 .../testing/SPARQL-CDTs/orderby/order-map-13.rq    |   15 +
 .../testing/SPARQL-CDTs/orderby/order-map-14.rq    |   15 +
 .../testing/SPARQL-CDTs/orderby/order-map-15.rq    |   15 +
 .../testing/SPARQL-CDTs/orderby/order-map-16.rq    |   15 +
 .../testing/SPARQL-CDTs/orderby/order-map-17.rq    |   15 +
 .../SPARQL-CDTs/orderby/order-map-null-01.rq       |   15 +
 .../SPARQL-CDTs/orderby/order-map-null-02.rq       |   16 +
 jena-arq/testing/SPARQL-CDTs/orderby/true.srx      |    5 +
 jena-arq/testing/SPARQL-CDTs/unfold/empty.ttl      |    0
 jena-arq/testing/SPARQL-CDTs/unfold/manifest.ttl   |  483 ++
 jena-arq/testing/SPARQL-CDTs/unfold/true.srx       |    5 +
 .../SPARQL-CDTs/unfold/unfold-get-list-1var-01.rq  |    7 +
 .../SPARQL-CDTs/unfold/unfold-get-list-1var-02.rq  |    7 +
 .../SPARQL-CDTs/unfold/unfold-get-list-1var-03.rq  |    9 +
 .../SPARQL-CDTs/unfold/unfold-get-list-1var-04.rq  |    9 +
 .../SPARQL-CDTs/unfold/unfold-get-list-2vars-01.rq |    7 +
 .../SPARQL-CDTs/unfold/unfold-get-list-2vars-02.rq |    7 +
 .../SPARQL-CDTs/unfold/unfold-get-list-2vars-03.rq |    9 +
 .../SPARQL-CDTs/unfold/unfold-get-list-2vars-04.rq |    9 +
 .../SPARQL-CDTs/unfold/unfold-get-list-2vars-05.rq |    7 +
 .../SPARQL-CDTs/unfold/unfold-get-list-2vars-06.rq |   10 +
 .../SPARQL-CDTs/unfold/unfold-list-1var-01.rq      |    6 +
 .../SPARQL-CDTs/unfold/unfold-list-1var-01.srj     |   14 +
 .../SPARQL-CDTs/unfold/unfold-list-1var-02.rq      |    6 +
 .../SPARQL-CDTs/unfold/unfold-list-1var-02.srj     |   17 +
 .../SPARQL-CDTs/unfold/unfold-list-1var-03.rq      |    6 +
 .../SPARQL-CDTs/unfold/unfold-list-1var-03.srj     |   14 +
 .../SPARQL-CDTs/unfold/unfold-list-1var-04.rq      |    6 +
 .../SPARQL-CDTs/unfold/unfold-list-1var-04.srj     |   14 +
 .../SPARQL-CDTs/unfold/unfold-list-1var-05.rq      |    6 +
 .../SPARQL-CDTs/unfold/unfold-list-1var-05.srj     |   14 +
 .../SPARQL-CDTs/unfold/unfold-list-1var-06.rq      |    6 +
 .../SPARQL-CDTs/unfold/unfold-list-1var-06.srj     |   14 +
 .../SPARQL-CDTs/unfold/unfold-list-1var-07.rq      |    6 +
 .../SPARQL-CDTs/unfold/unfold-list-1var-07.srj     |   17 +
 .../SPARQL-CDTs/unfold/unfold-list-1var-08.rq      |    6 +
 .../SPARQL-CDTs/unfold/unfold-list-1var-08.srj     |   17 +
 .../SPARQL-CDTs/unfold/unfold-list-1var-09.rq      |    6 +
 .../SPARQL-CDTs/unfold/unfold-list-1var-09.srj     |   13 +
 .../SPARQL-CDTs/unfold/unfold-list-1var-10.rq      |    6 +
 .../SPARQL-CDTs/unfold/unfold-list-1var-10.srj     |   15 +
 .../SPARQL-CDTs/unfold/unfold-list-2vars-01.rq     |    6 +
 .../SPARQL-CDTs/unfold/unfold-list-2vars-01.srj    |   16 +
 .../SPARQL-CDTs/unfold/unfold-list-2vars-02.rq     |    6 +
 .../SPARQL-CDTs/unfold/unfold-list-2vars-02.srj    |   20 +
 .../SPARQL-CDTs/unfold/unfold-list-2vars-03.rq     |    6 +
 .../SPARQL-CDTs/unfold/unfold-list-2vars-03.srj    |   16 +
 .../SPARQL-CDTs/unfold/unfold-list-2vars-04.rq     |    6 +
 .../SPARQL-CDTs/unfold/unfold-list-2vars-04.srj    |   16 +
 .../SPARQL-CDTs/unfold/unfold-list-2vars-05.rq     |    6 +
 .../SPARQL-CDTs/unfold/unfold-list-2vars-05.srj    |   16 +
 .../SPARQL-CDTs/unfold/unfold-list-2vars-06.rq     |    6 +
 .../SPARQL-CDTs/unfold/unfold-list-2vars-06.srj    |   16 +
 .../SPARQL-CDTs/unfold/unfold-list-2vars-07.rq     |    6 +
 .../SPARQL-CDTs/unfold/unfold-list-2vars-07.srj    |   20 +
 .../SPARQL-CDTs/unfold/unfold-list-2vars-08.rq     |    6 +
 .../SPARQL-CDTs/unfold/unfold-list-2vars-08.srj    |   20 +
 .../SPARQL-CDTs/unfold/unfold-list-2vars-09.rq     |    6 +
 .../SPARQL-CDTs/unfold/unfold-list-2vars-09.srj    |   15 +
 .../SPARQL-CDTs/unfold/unfold-list-2vars-10.rq     |    6 +
 .../SPARQL-CDTs/unfold/unfold-list-2vars-10.srj    |   18 +
 .../SPARQL-CDTs/unfold/unfold-map-1var-01.rq       |    6 +
 .../SPARQL-CDTs/unfold/unfold-map-1var-01.srj      |   14 +
 .../SPARQL-CDTs/unfold/unfold-map-1var-02.rq       |    6 +
 .../SPARQL-CDTs/unfold/unfold-map-1var-02.srj      |   14 +
 .../SPARQL-CDTs/unfold/unfold-map-1var-03.rq       |    6 +
 .../SPARQL-CDTs/unfold/unfold-map-1var-03.srj      |   14 +
 .../SPARQL-CDTs/unfold/unfold-map-1var-04.rq       |    6 +
 .../SPARQL-CDTs/unfold/unfold-map-1var-04.srj      |   14 +
 .../SPARQL-CDTs/unfold/unfold-map-2vars-01.rq      |    6 +
 .../SPARQL-CDTs/unfold/unfold-map-2vars-01.srj     |   16 +
 .../SPARQL-CDTs/unfold/unfold-map-2vars-02.rq      |    6 +
 .../SPARQL-CDTs/unfold/unfold-map-2vars-02.srj     |   16 +
 .../SPARQL-CDTs/unfold/unfold-map-2vars-03.rq      |    6 +
 .../SPARQL-CDTs/unfold/unfold-map-2vars-03.srj     |   16 +
 .../SPARQL-CDTs/unfold/unfold-map-2vars-04.rq      |    6 +
 .../SPARQL-CDTs/unfold/unfold-map-2vars-04.srj     |   16 +
 .../SPARQL-CDTs/unfold/unfold-map-2vars-05.rq      |    6 +
 .../SPARQL-CDTs/unfold/unfold-map-2vars-05.srj     |   16 +
 .../SPARQL-CDTs/unfold/unfold-map-2vars-06.rq      |    6 +
 .../SPARQL-CDTs/unfold/unfold-map-2vars-06.srj     |   16 +
 .../SPARQL-CDTs/unfold/unfold-map-2vars-07.rq      |    6 +
 .../SPARQL-CDTs/unfold/unfold-map-2vars-07.srj     |   16 +
 .../SPARQL-CDTs/unfold/unfold-map-2vars-08.rq      |    6 +
 .../SPARQL-CDTs/unfold/unfold-map-2vars-08.srj     |   15 +
 .../org/apache/jena/graph/impl/LiteralLabel.java   |   22 +
 .../jena/graph/impl/LiteralLabelFactory.java       |   15 +
 .../rewriters/BuildElementVisitor.java             |    8 +-
 .../querybuilder/rewriters/ElementRewriter.java    |   20 +-
 .../arq/querybuilder/rewriters/OpRewriter.java     |    8 +-
 .../updatebuilder/QuadIteratorBuilder.java         |    8 +-
 .../jena/arq/querybuilder/WhereValidator.java      |    8 +-
 .../permissions/query/rewriter/OpRewriter.java     |   13 +
 853 files changed, 25199 insertions(+), 4780 deletions(-)

diff --git a/jena-arq/Grammar/CDTs/cdt_literals.jj b/jena-arq/Grammar/CDTs/cdt_literals.jj
new file mode 100644
index 0000000000..d2e098b906
--- /dev/null
+++ b/jena-arq/Grammar/CDTs/cdt_literals.jj
@@ -0,0 +1,370 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+options
+{
+  // Use \ u escapes in streams AND use a reader for the query
+  // => get both raw and escaped unicode
+
+   JAVA_UNICODE_ESCAPE   = true ;
+   UNICODE_INPUT         = false ;
+
+  STATIC                = false ;
+//   DEBUG_PARSER          = true ;
+//   DEBUG_TOKEN_MANAGER   = true ;
+}
+
+PARSER_BEGIN(CDTLiteralParser)
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.cdt.parser;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.jena.cdt.*;
+import org.apache.jena.graph.Node;
+
+import static org.apache.jena.riot.lang.extra.LangParserLib.*;
+
+public class CDTLiteralParser extends CDTLiteralParserBase
+{
+}
+PARSER_END(CDTLiteralParser)
+
+// --- Entry point for cdt:List literals
+
+List<CDTValue> List() : { List<CDTValue> l = new ArrayList<CDTValue>(); }
+{
+  <LBRACKET>
+
+  ( NonEmptyListContent(l) )?
+
+  <RBRACKET>
+
+  { return l; }
+}
+
+void NonEmptyListContent(List<CDTValue> l) : { }
+{
+  ListElement(l)
+
+  (  <COMMA> ListElement(l)  )*
+}
+
+void ListElement(List<CDTValue> l) : { String iri; Node n; List<CDTValue> subList; Map<CDTKey,CDTValue> m; }
+{
+  iri = IRI_REF()       { n = createURI(iri, token.beginLine, token.beginColumn);  l.add( CDTFactory.createValue(n) ); }
+|
+  n = BlankNode()       { l.add( CDTFactory.createValue(n) ); }
+| 
+  n = RDFLiteral()      { l.add( CDTFactory.createValue(n) ); }
+|
+  n = NumericLiteral()  { l.add( CDTFactory.createValue(n) ); }
+|
+  n = BooleanLiteral()  { l.add( CDTFactory.createValue(n) ); }
+|
+  <NULL>                { l.add( CDTFactory.getNullValue() ); }
+|
+  subList = List()      { l.add( CDTFactory.createValue(subList) ); }
+|
+  m = Map()             { l.add( CDTFactory.createValue(m) ); }
+}
+
+// --- Entry point for cdt:Map literals
+
+Map<CDTKey,CDTValue> Map() : { Map<CDTKey,CDTValue> m = new HashMap<CDTKey,CDTValue>(); }
+{
+  <LBRACE>
+
+  ( NonEmptyMapContent(m) )?
+
+  <RBRACE>
+
+  { return m; }
+}
+
+void NonEmptyMapContent(Map<CDTKey,CDTValue> m) : { }
+{
+  MapEntry(m)
+
+  ( <COMMA> MapEntry(m) )*
+}
+
+void MapEntry(Map<CDTKey,CDTValue> m) : { CDTKey key; CDTValue value; }
+{
+  key = MapKey()
+
+  <COLON>
+
+  value = MapValue()
+
+  {
+    final CDTValue oldValue = m.put(key, value);
+    if ( oldValue != null ) throw new ParseException("map with non-unique key (" + key.toString() + ")");
+  }
+}
+
+CDTKey MapKey() : { String iri; Node n; }
+{
+  iri = IRI_REF()       { n = createURI(iri, token.beginLine, token.beginColumn);  return CDTFactory.createKey(n); }
+|
+  n = RDFLiteral()      { return CDTFactory.createKey(n); }
+|
+  n = NumericLiteral()  { return CDTFactory.createKey(n); }
+|
+  n = BooleanLiteral()  { return CDTFactory.createKey(n); }
+}
+
+CDTValue MapValue() : { String iri; Node n; List<CDTValue> subList; Map<CDTKey,CDTValue> m; }
+{
+  iri = IRI_REF()       { n = createURI(iri, token.beginLine, token.beginColumn);  return CDTFactory.createValue(n); }
+|
+  n = BlankNode()       { return CDTFactory.createValue(n); }
+| 
+  n = RDFLiteral()      { return CDTFactory.createValue(n); }
+|
+  n = NumericLiteral()  { return CDTFactory.createValue(n); }
+|
+  n = BooleanLiteral()  { return CDTFactory.createValue(n); }
+|
+  <NULL>                { return CDTFactory.getNullValue(); }
+|
+  subList = List()      { return CDTFactory.createValue(subList); }
+|
+  m = Map()             { return CDTFactory.createValue(m); }
+}
+
+
+// ---- Basic terms
+
+String IRI_REF() : { Token t ; }
+{
+  t = <IRIref>
+  { return resolveQuotedIRI(t.image, t.beginLine, t.beginColumn) ; }
+}
+
+Node BlankNode() :  { Token t = null ; }
+{
+  t = <BLANK_NODE_LABEL>
+
+  {
+    return createBNode(t.image, t.beginLine, t.beginColumn);
+  }
+}
+
+Node NumericLiteral() : { Token t ; }
+{
+  t = <INTEGER> { return createLiteralInteger(t.image, t.beginLine, t.beginColumn); }
+| t = <DECIMAL> { return createLiteralDecimal(t.image, t.beginLine, t.beginColumn); }
+| t = <DOUBLE>  { return createLiteralDouble(t.image, t.beginLine, t.beginColumn); }
+}
+
+Node BooleanLiteral() : {}
+{
+   <TRUE> { return XSD_TRUE; }
+  |
+   <FALSE> { return XSD_FALSE; }
+}
+
+Node RDFLiteral() : { String lex = null ; }
+{
+  lex = String()
+  // Optional lang tag and datatype.
+  { String lang = null ; String dt = null ; }
+  (
+    lang = Langtag()
+  |
+    ( <DATATYPE> dt = IRI_REF() )  // divergence from the Turtle parser here; instead of IRIref(), we only permit IRI_REF()
+  )?
+
+  {
+    return createLiteral(lex, lang, dt, token.beginLine, token.beginColumn);
+  }
+} 
+
+String Langtag() : { Token t ; }
+{
+  t = <LANGTAG> // divergence from the Turtle parser here, which also permits AnyDirective() at this point
+  { String lang = stripChars(t.image, 1) ; return lang ; }
+}
+
+String String() : { Token t ;  String lex ; }
+{
+  ( t = <STRING_LITERAL1> { lex = stripQuotes(t.image) ; }
+  | t = <STRING_LITERAL2> { lex = stripQuotes(t.image) ; }
+  | t = <STRING_LITERAL_LONG1> { lex = stripQuotes3(t.image) ; }
+  | t = <STRING_LITERAL_LONG2> { lex = stripQuotes3(t.image) ; }
+  )
+    { lex = unescapeStr(lex,  t.beginLine, t.beginColumn) ;
+      return lex ;
+    }
+}
+
+
+// ------------------------------------------
+// Tokens
+
+// Comments and whitespace
+
+SKIP : { " " | "\t" | "\n" | "\r" | "\f" }
+
+TOKEN: { <#WS: " " | "\t" | "\n" | "\r" | "\f"> }
+
+
+// -------------------------------------------------
+// Keywords
+
+TOKEN [IGNORE_CASE] :
+{
+  < TRUE:        "true" >
+| < FALSE:       "false" >
+
+// -------------------------------------------------
+
+|   < INTEGER: (["-","+"])? <DIGITS> >
+|
+   < DECIMAL: (["-","+"])? 
+              ((<DIGITS>)+ "." (<DIGITS>)* | "." (<DIGITS>)+)
+   >
+      // Required exponent.
+|  < DOUBLE:
+      (["+","-"])?
+      ( 
+        (["0"-"9"])+ "." (["0"-"9"])* <EXPONENT>
+        | "." (["0"-"9"])+ (<EXPONENT>)
+        | (["0"-"9"])+ <EXPONENT>
+      )
+      >
+| < #EXPONENT: ["e","E"] (["+","-"])? (["0"-"9"])+ >
+| < #QUOTE_3D: "\"\"\"">
+| < #QUOTE_3S: "'''">
+// "u" done by javacc input stream.  
+// "U" escapes not supported yet for Java strings
+| <ECHAR: "\\" ("t"|"b"|"n"|"r"|"f"|"\\"|"\""|"'")>
+
+| < STRING_LITERAL1: 
+      // Single quoted string
+      "'" ( (~["'","\\","\n","\r"]) | <ECHAR> )* "'" > 
+
+| < STRING_LITERAL2:
+    // Double quoted string
+      "\"" ( (~["\"","\\","\n","\r"]) | <ECHAR> )* "\"" >
+
+| < STRING_LITERAL_LONG1:
+     <QUOTE_3S> 
+      ( ~["'","\\"] | <ECHAR> | ("'" ~["'"]) | ("''" ~["'"]))*
+     <QUOTE_3S> >
+
+| < STRING_LITERAL_LONG2: 
+     <QUOTE_3D> 
+      ( ~["\"","\\"] | <ECHAR> | ("\"" ~["\""]) | ("\"\"" ~["\""]))*
+     <QUOTE_3D> >
+| < DIGITS: (["0"-"9"])+>
+// | <HEX: ["0"-"9"] | ["A"-"F"] | ["a"-"f"]>
+}
+
+TOKEN:
+{
+   // Includes # for relative URIs
+   <IRIref: "<" (~[ ">","<", "\"", "{", "}", "^", "\\", "|", "`",
+                      "\u0000"-"\u0020"])* ">" >
+|  <BLANK_NODE_LABEL: "_:" <PN_LOCAL> >
+|  <LANGTAG: <AT> (<A2Z>)+("-" (<A2ZN>)+)* >
+|  <#A2Z: ["a"-"z","A"-"Z"]>
+|  <#A2ZN: ["a"-"z","A"-"Z","0"-"9"]>
+}
+
+
+TOKEN :
+{
+  < NULL:      "null" >
+
+| < LBRACE:    "{" >
+| < RBRACE:    "}" >
+
+| < LBRACKET:  "[" >
+| < RBRACKET:  "]" >
+
+| < COMMA:     "," >
+| < COLON:     ":" >
+}
+
+// Operator
+
+TOKEN :
+{
+  < DATATYPE: "^^">
+| < AT: "@">
+}
+
+TOKEN:
+{
+  <#PN_CHARS_BASE:
+          ["A"-"Z"] | ["a"-"z"] |
+          ["\u00C0"-"\u00D6"] | ["\u00D8"-"\u00F6"] | ["\u00F8"-"\u02FF"] |
+          ["\u0370"-"\u037D"] | ["\u037F"-"\u1FFF"] |
+          ["\u200C"-"\u200D"] | ["\u2070"-"\u218F"] | ["\u2C00"-"\u2FEF"] |
+          ["\u3001"-"\uD7FF"] | ["\uF900"-"\uFFFD"]
+          >
+          // [#x10000-#xEFFFF]
+|
+  <#PN_CHARS_U: <PN_CHARS_BASE> | "_" >
+|
+// No DOT
+  <#PN_CHARS: (<PN_CHARS_U> | "-" | ["0"-"9"] | "\u00B7" |
+              ["\u0300"-"\u036F"] | ["\u203F"-"\u2040"] ) >
+|
+  // With a leading "_", no dot at end of local name.
+  <#PN_LOCAL: (<PN_CHARS_U> | ["0"-"9"]) ((<PN_CHARS>|".")* <PN_CHARS>)? >
+}
+
+// Catch-all tokens.  Must be last.  
+// Any non-whitespace.  Causes a parser exception, rather than a
+// token manager error (with hidden line numbers).
+// Only bad IRIs (e.g. spaces) now give unhelpful parse errors.
+TOKEN:
+{
+  <#UNKNOWN: (~[" ","\t","\n","\r","\f" ])+ >
+}
+
+/*
+# Local Variables:
+# tab-width: 4
+# indent-tabs-mode: nil
+# comment-default-style: "//"
+# End:
+*/
diff --git a/jena-arq/Grammar/CDTs/generate b/jena-arq/Grammar/CDTs/generate
new file mode 100755
index 0000000000..687b81f053
--- /dev/null
+++ b/jena-arq/Grammar/CDTs/generate
@@ -0,0 +1,65 @@
+#!/bin/bash
+## Licensed to the Apache Software Foundation (ASF) under one
+## or more contributor license agreements.  See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership.  The ASF licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License.  You may obtain a copy of the License at
+##
+##     http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+
+# Parser builder
+
+DIR="../../src/main/java/org/apache/jena/cdt/parser"
+FILE=cdt_literals.jj
+CLASS=CDTLiteralParser
+
+(cd "$DIR" ; rm -f *.java )
+
+echo "---- Process grammar ----"
+
+javacc -OUTPUT_DIRECTORY=$DIR -JDK_VERSION=1.8 "$FILE"
+
+RC=$?
+[ "$RC" = 0 ] || return
+
+echo "---- Create text form ----"
+jjdoc -TEXT=true -OUTPUT_FILE=${FILE%%.jj}.txt "${FILE}"
+
+echo "---- Fixing Java warnings in TokenMgrError"
+F="$DIR/TokenMgrError.java"
+if [ -e "$F" ]
+then
+    sed -e 's/public class TokenMgrError/\n@SuppressWarnings("all")\npublic class TokenMgrError/' < $F > F 
+    mv F $F
+fi
+
+## JavaCharStream -- SimpleCharStream is OK.
+echo "---- Fixing Java warnings in JavaCharStream..."
+F="$DIR/JavaCharStream.java"
+if [ -e "$F" ]
+then
+    sed -e 's/public/\n@SuppressWarnings("all")\npublic/' < $F > F 
+    mv F $F
+fi
+    
+echo "---- Fixing Java warnings in ${CLASS} ..."
+F="$DIR/${CLASS}.java"
+sed -e 's/@SuppressWarnings("serial")//' \
+    < $F > F
+mv F $F
+
+echo "---- Create HTML form ----"
+../jj2html ${FILE%%.jj}.txt '../tokens.txt' > ${FILE%%.jj}-1.html
+../grammarExtracts < ${FILE%%.jj}-1.html > ${FILE%%.jj}-2.html
+
+echo "Check X and Y for IRI_REF because \" became '"
+
+echo "---- Done"
diff --git a/jena-arq/Grammar/arq.jj b/jena-arq/Grammar/arq.jj
index 2432ac5085..24f421a5d6 100644
--- a/jena-arq/Grammar/arq.jj
+++ b/jena-arq/Grammar/arq.jj
@@ -300,6 +300,26 @@ void OrderCondition() :
       else
           getQuery().addOrderBy(v, direction) ; }
 }
+SortCondition OrderConditionForAggregationFunction() :
+{ int direction = 0 ; Expr expr = null ; Node v = null ; }
+{
+  { direction = Query.ORDER_DEFAULT ; }
+  (
+    (
+      ( <ASC> { direction = Query.ORDER_ASCENDING ; }
+      | <DESC> { direction = Query.ORDER_DESCENDING ; } )
+      expr = BrackettedExpression()
+    )
+  |
+    ( expr = Constraint()
+    | v = Var()
+    )
+  )
+  { if ( v == null )
+          return new SortCondition(expr, direction) ;
+      else
+          return new SortCondition(v, direction) ; }
+}
 void LimitOffsetClauses() : { }
 {
   (
@@ -634,6 +654,8 @@ Element GraphPatternNotTriples() : { Element el = null ; }
    el = ExistsElt()
  |
    el = NotExistsElt()
+ |
+   el = Unfold()
  )
  { return el ; }
 }
@@ -758,6 +780,17 @@ Element NotExistsElt() : { Element el ; }
     el = GroupGraphPattern()
     { return new ElementNotExists(el) ; }
 }
+Element Unfold() : { Var v1 ; Var v2 = null ; Expr expr ; }
+{
+  <UNFOLD>
+  <LPAREN>
+  expr = Expression()
+  <AS>
+  v1 = Var()
+  (<COMMA> v2 = Var() { ; } )?
+  <RPAREN>
+  { return new ElementUnfold(expr, v1, v2) ; }
+}
 Element MinusGraphPattern() : { Element el ; }
 {
     <MINUS_P>
@@ -1611,6 +1644,26 @@ Expr Aggregate() : { Aggregator agg = null ; String sep = null ;
     { agg = AggregatorFactory.createCustom(AggURI.var_samp, distinct, expr) ; }
   | t = <VAR_POP> <LPAREN> ( <DISTINCT> { distinct = true ; } )? expr = Expression() <RPAREN>
     { agg = AggregatorFactory.createCustom(AggURI.var_pop, distinct, expr) ; }
+  | t = <FOLD>
+    { java.util.List<SortCondition> scs = null ;
+      SortCondition sc = null ; }
+    <LPAREN>
+    ( <DISTINCT> { distinct = true ; } )?
+    expr = Expression()
+    ( <COMMA> expr2 = Expression() )?
+    (
+       <ORDER> <BY>
+       (
+         sc = OrderConditionForAggregationFunction()
+         {
+           if ( scs == null )
+             scs = new java.util.ArrayList<SortCondition>();
+           scs.add(sc);
+         }
+       )+
+    )?
+    <RPAREN>
+    { agg = AggregatorFactory.createFold(distinct, expr, expr2, scs) ; }
   | t = <AGG>
     { String iri ; }
     iri = iri()
@@ -1785,11 +1838,13 @@ TOKEN [IGNORE_CASE] :
 | < SERVICE: "service" >
 | < LET: "LET" >
 | < LATERAL: "LATERAL" >
+| < UNFOLD: "unfold" >
 | < TRIPLE: "TRIPLE" >
 | < IS_TRIPLE: "isTRIPLE" >
 | < SUBJECT: "SUBJECT" >
 | < PREDICATE: "PREDICATE" >
 | < OBJECT: "OBJECT" >
+| < UNFOLD: "unfold" >
 | < EXISTS: "exists" >
 | < NOT: "not" >
 | < AS: "as" >
@@ -1810,6 +1865,7 @@ TOKEN [IGNORE_CASE] :
 | < VARIANCE: "variance" >
 | < VAR_SAMP: "var_samp" >
 | < VAR_POP: "var_pop" >
+| < FOLD: "fold" >
 | < SAMPLE: "sample" >
 | < GROUP_CONCAT: "group_concat" >
 | < FILTER: "filter" >
diff --git a/jena-arq/Grammar/main.jj b/jena-arq/Grammar/main.jj
index 757e0055e5..46ff197b3b 100644
--- a/jena-arq/Grammar/main.jj
+++ b/jena-arq/Grammar/main.jj
@@ -480,6 +480,27 @@ void OrderCondition() :
           getQuery().addOrderBy(v, direction) ; }
 }
 
+SortCondition OrderConditionForAggregationFunction() :
+{ int direction = 0 ; Expr expr = null ; Node v = null ; }
+{
+  { direction = Query.ORDER_DEFAULT ; }
+  (
+    ( // These are for clarity in the HTML
+      ( <ASC>  { direction = Query.ORDER_ASCENDING ; }
+      | <DESC> { direction = Query.ORDER_DESCENDING ; } ) 
+      expr = BrackettedExpression() 
+    )
+  |
+    ( expr = Constraint()
+    | v = Var() //{ expr = asExpr(v) ; } 
+    ) 
+  )
+  { if ( v == null )
+          return new SortCondition(expr, direction) ;
+      else
+          return new SortCondition(v, direction) ; }
+}
+
 void LimitOffsetClauses() : { }
 {
   // SPARQL does not care about the order here.
@@ -902,10 +923,14 @@ Element GraphPatternNotTriples() : { Element el = null ; }
 #ifdef ARQ
  |
    el = Assignment()
+ |
+   el = Unfold()
  |
    el = ExistsElt()
  |
    el = NotExistsElt()
+ |
+   el = Unfold()
 #endif
  )
  { return el ; }
@@ -1038,6 +1063,18 @@ Element Assignment() : { Var v ; Expr expr ; }
     { return new ElementAssign(v, expr) ; }
 }
 
+Element Unfold() : {  Var v1 ; Var v2 = null ; Expr expr ; }
+{
+  <UNFOLD>
+  <LPAREN>
+  expr = Expression()
+  <AS>
+  v1 = Var()
+  (<COMMA> v2 = Var() { ; } )?
+  <RPAREN>
+  { return new ElementUnfold(expr, v1, v2) ; }
+}
+
 Element ExistsElt() : { Element el ; }
 {
     <EXISTS>
@@ -1051,6 +1088,18 @@ Element NotExistsElt() : { Element el ; }
     el = GroupGraphPattern()
     { return new ElementNotExists(el) ; }
 }
+
+Element Unfold() : {  Var v1 ; Var v2 = null ; Expr expr ; }
+{
+  <UNFOLD>
+  <LPAREN>
+  expr = Expression()
+  <AS>
+  v1 = Var()
+  (<COMMA> v2 = Var() { ; } )?
+  <RPAREN>
+  { return new ElementUnfold(expr, v1, v2) ; }
+}
 #endif
 
 Element MinusGraphPattern() : { Element el ; }
@@ -2234,6 +2283,27 @@ Expr Aggregate() : { Aggregator agg = null ; String sep = null ;
   | t = <VAR_POP> <LPAREN> ( <DISTINCT> { distinct = true ; } )? expr = Expression() <RPAREN>
     { agg = AggregatorFactory.createCustom(AggURI.var_pop, distinct, expr) ; }
 
+  | t = <FOLD>
+    { java.util.List<SortCondition> scs = null ;
+      SortCondition sc = null ; }
+    <LPAREN>
+    ( <DISTINCT> { distinct = true ; } )?
+    expr = Expression()
+    ( <COMMA> expr2 = Expression() )?
+    (
+       <ORDER> <BY>
+       (
+         sc = OrderConditionForAggregationFunction()
+         {
+           if ( scs == null )
+             scs = new java.util.ArrayList<SortCondition>();
+           scs.add(sc);
+         }
+       )+
+    )?
+    <RPAREN>
+    { agg = AggregatorFactory.createFold(distinct, expr, expr2, scs) ; }
+
     /* Explicit syntax (aggregate even if not registered) */
   | t = <AGG>
     { String iri ; }
@@ -2499,6 +2569,7 @@ TOKEN [IGNORE_CASE] :
 #ifdef ARQ
 |  < LET:         "LET" >
 |  < LATERAL:     "LATERAL" >
+|  < UNFOLD:      "unfold" >
 #endif
 
 |  < TRIPLE:      "TRIPLE" >
@@ -2529,6 +2600,9 @@ TOKEN [IGNORE_CASE] :
 |  < VARIANCE:    "variance" >
 |  < VAR_SAMP:    "var_samp" >
 |  < VAR_POP:     "var_pop" >
+#ifdef ARQ
+|  < FOLD:        "fold" >
+#endif
 
 |  < SAMPLE:      "sample" >
 |  < GROUP_CONCAT: "group_concat" >
diff --git a/jena-arq/src/main/java/org/apache/jena/cdt/CDTFactory.java b/jena-arq/src/main/java/org/apache/jena/cdt/CDTFactory.java
new file mode 100644
index 0000000000..9af154467a
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/cdt/CDTFactory.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.cdt;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.jena.graph.Node;
+import org.apache.jena.graph.NodeFactory;
+
+public class CDTFactory
+{
+	public static CDTKey createKey( final Node n ) {
+		return new CDTKey() {
+			@Override public Node asNode() { return n; }
+		};
+	}
+
+	public static CDTValue createValue( final Node n ) {
+		return new CDTValue() {
+			@Override public boolean isNode() { return true; }
+			@Override public Node asNode() { return n; }
+		};
+	}
+
+	public static CDTValue createValue( final List<CDTValue> l ) {
+		final Node n = NodeFactory.createLiteralByValue(l, CompositeDatatypeList.type);
+		return createValue(n);
+	}
+
+	public static CDTValue createValue( final Map<CDTKey,CDTValue> m ) {
+		final Node n = NodeFactory.createLiteralByValue(m, CompositeDatatypeMap.type);
+		return createValue(n);
+	}
+
+	public static CDTValue getNullValue() {
+		if ( nullValue == null ) {
+			return new CDTValue() {
+				@Override public boolean isNull() { return true; }
+			};
+		}
+
+		return nullValue;
+	}
+
+	private static CDTValue nullValue = null;
+
+}
diff --git a/jena-arq/src/main/java/org/apache/jena/cdt/CDTKey.java b/jena-arq/src/main/java/org/apache/jena/cdt/CDTKey.java
new file mode 100644
index 0000000000..7631565e88
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/cdt/CDTKey.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.cdt;
+
+import org.apache.jena.graph.Node;
+import org.apache.jena.riot.out.NodeFmtLib;
+
+public abstract class CDTKey
+{
+	/**
+	 * Returns this object as an RDF term (i.e., an IRI, a literal,
+	 * or a blank node), assuming it is one. If it is not, then an
+	 * {@link UnsupportedOperationException} is thrown.
+	 */
+	public Node asNode() {
+		throw new UnsupportedOperationException( this + " is not an RDF term" );
+	}
+
+	@Override
+	public boolean equals( final Object other ) {
+		if ( other instanceof CDTKey ) {
+			final CDTKey otherKey = (CDTKey) other;
+
+			final Node n1 = asNode();
+			final Node n2 = otherKey.asNode();
+			return n1.equals(n2);
+		}
+
+		return false;
+	}
+
+	@Override
+	public int hashCode() {
+		return asNode().hashCode();
+	}
+
+	@Override
+	public String toString() {
+		return asNode().toString();
+	}
+
+	/**
+	 * Returns a string representation of this element
+	 * to be included in the lexical form of literals.
+	 */
+	public String asLexicalForm() {
+		return NodeFmtLib.strTTL( asNode() );
+	}
+
+}
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/ARQFunctions.java b/jena-arq/src/main/java/org/apache/jena/cdt/CDTLiteralParseException.java
similarity index 58%
copy from jena-arq/src/main/java/org/apache/jena/sparql/function/ARQFunctions.java
copy to jena-arq/src/main/java/org/apache/jena/cdt/CDTLiteralParseException.java
index a7452a464a..01325265fc 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/function/ARQFunctions.java
+++ b/jena-arq/src/main/java/org/apache/jena/cdt/CDTLiteralParseException.java
@@ -16,22 +16,16 @@
  * limitations under the License.
  */
 
-package org.apache.jena.sparql.function;
+package org.apache.jena.cdt;
 
-import org.apache.jena.sparql.ARQConstants;
-import org.apache.jena.sparql.function.library.triple.TripleTermFunctions;
+import org.apache.jena.shared.JenaException;
 
-/** Load ARQ functions ().
- * The one in package {@link org.apache.jena.sparql.function.library}
- * can be invoked by afn:name without registration because afn:-&gt;java: is built-in
- * in MappedLoader.
- *
- * @see ARQConstants#ARQFunctionLibraryURI
- * @see ARQConstants#ARQFunctionLibrary
- */
-public class ARQFunctions {
+public class CDTLiteralParseException  extends JenaException
+{
+	private static final long serialVersionUID = -6244204787118953600L;
 
-    public static void load(FunctionRegistry reg) {
-        TripleTermFunctions.register(reg);
-    }
+	public CDTLiteralParseException() { super(); }
+	public CDTLiteralParseException(Throwable cause) { super(cause); }
+	public CDTLiteralParseException(String msg) { super(msg); }
+	public CDTLiteralParseException(String msg, Throwable cause) { super(msg, cause); }
 }
diff --git a/jena-arq/src/main/java/org/apache/jena/cdt/CDTLiteralParserBase.java b/jena-arq/src/main/java/org/apache/jena/cdt/CDTLiteralParserBase.java
new file mode 100644
index 0000000000..7e26446c20
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/cdt/CDTLiteralParserBase.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.cdt;
+
+import org.apache.jena.graph.Node;
+import org.apache.jena.riot.lang.extra.LangParserBase;
+
+public class CDTLiteralParserBase extends LangParserBase
+{
+	@Override
+	protected Node createBNode( final String label, final int line, final int column ) {
+		// We need to cut away the leading '_:' of the given blank node label.
+		// This is necessary because the Turtle parser does the same. If we
+		// would not do it here for the blank node labels obtained from the
+		// lexical forms of CDT literals, then the label-to-bnode mapping of
+		// the shared parser state fails to map the same blank node identifiers
+		// inside and outside of CDT literals to the same blank node.
+		final String lbl = label.startsWith("_:") ? label.substring(2) : label;
+		return super.createBNode(lbl, line, column);
+	}
+
+	@Override
+	protected Node createLiteral( final String lex, final String langTag, final String datatypeURI, final int line, final int column ) {
+		if ( CompositeDatatypeList.uri.equals(datatypeURI) ) {
+			return profile.createTypedLiteral(lex, CompositeDatatypeList.type, 0L, 0L);
+		}
+
+		if ( CompositeDatatypeMap.uri.equals(datatypeURI) ) {
+			return profile.createTypedLiteral(lex, CompositeDatatypeMap.type, 0L, 0L);
+		}
+
+		return super.createLiteral(lex, langTag, datatypeURI, line, column);
+	}
+
+}
diff --git a/jena-arq/src/main/java/org/apache/jena/cdt/CDTValue.java b/jena-arq/src/main/java/org/apache/jena/cdt/CDTValue.java
new file mode 100644
index 0000000000..3baed568f3
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/cdt/CDTValue.java
@@ -0,0 +1,144 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.cdt;
+
+import org.apache.jena.graph.Node;
+import org.apache.jena.riot.out.NodeFmtLib;
+import org.apache.jena.sparql.expr.ExprEvalException;
+
+public abstract class CDTValue
+{
+	/**
+	 * Returns true if this is a null value (in which
+	 * case {@link #isNode()} must return false).
+	 */
+	public boolean isNull() {
+		return false;
+	}
+
+	/**
+	 * Returns true if this object is an RDF term (i.e., an IRI, a literal,
+	 * or a blank node). In that case, {@link #asNode()} can be used to get
+	 * a corresponding {@link Node} representation of this RDF term.
+	 */
+	public boolean isNode() {
+		return false;
+	}
+
+	/**
+	 * Returns this object as an RDF term (i.e., an IRI, a literal,
+	 * or a blank node), assuming it is one. If it is not, then an
+	 * {@link UnsupportedOperationException} is thrown.
+	 */
+	public Node asNode() {
+		throw new UnsupportedOperationException( this + " is not an RDF term" );
+	}
+
+	@Override
+	public String toString() {
+		if ( isNode() )
+			return asNode().toString();
+
+		if ( isNull() )
+			return "null";
+
+		throw new IllegalArgumentException( "not null and not a Node (" + this.getClass().getName() + ")" );
+	}
+
+	@Override
+	public int hashCode() {
+		if ( isNode() )
+			return asNode().hashCode();;
+
+		if ( isNull() )
+			return 1;
+
+		throw new IllegalArgumentException( "not null and not a Node (" + this.getClass().getName() + ")" );
+	}
+
+	@Override
+	public boolean equals( final Object other ) {
+		try {
+			return sameAs(other);
+		}
+		catch ( final ExprEvalException ex ) {
+			return false;
+		}
+	}
+
+	public boolean sameAs( final Object other ) throws ExprEvalException {
+		if ( isNull() ) {
+			throw new ExprEvalException("nulls cannot be compared");
+		}
+
+		if ( other instanceof CDTValue ) {
+			final CDTValue otherValue = (CDTValue) other;
+
+			if ( otherValue.isNull() ) {
+				throw new ExprEvalException("nulls cannot be compared");
+			}
+
+			if ( isNode() ) {
+				return otherValue.isNode() && asNode().sameValueAs( otherValue.asNode() );
+			}
+
+			throw new IllegalStateException( "unexpected type of CDTValue: " + this.getClass().getName() );
+		}
+
+		return false;
+	}
+
+	public final boolean sameAs( final CDTValue otherValue ) throws ExprEvalException {
+		if ( isNull() ) {
+			throw new ExprEvalException("nulls cannot be compared");
+		}
+
+		if ( otherValue.isNull() ) {
+			throw new ExprEvalException("nulls cannot be compared");
+		}
+
+		if ( isNode() ) {
+			return otherValue.isNode() && asNode().sameValueAs( otherValue.asNode() );
+		}
+
+		throw new IllegalStateException( "unexpected type of CDTValue: " + this.getClass().getName() );
+	}
+
+	public String asLexicalForm() {
+		if ( isNull() ) {
+			return "null";
+		}
+
+		if ( isNode() ) {
+			final Node n = asNode();
+			if ( CompositeDatatypeList.isListLiteral(n) ) {
+				return n.getLiteralLexicalForm();
+			}
+			else if ( CompositeDatatypeMap.isMapLiteral(n) ) {
+				return n.getLiteralLexicalForm();
+			}
+			else {
+				return NodeFmtLib.strTTL(n);
+			}
+		}
+
+		throw new IllegalStateException( "unexpected type of CDTValue: " + this.getClass().getName() );
+	}
+
+}
diff --git a/jena-arq/src/main/java/org/apache/jena/cdt/CompositeDatatypeBase.java b/jena-arq/src/main/java/org/apache/jena/cdt/CompositeDatatypeBase.java
new file mode 100644
index 0000000000..ffeff44314
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/cdt/CompositeDatatypeBase.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.cdt;
+
+import org.apache.jena.datatypes.DatatypeFormatException;
+import org.apache.jena.datatypes.RDFDatatype;
+import org.apache.jena.graph.impl.LiteralLabel;
+import org.apache.jena.sparql.expr.Expr;
+
+public abstract class CompositeDatatypeBase<T> implements RDFDatatype
+{
+	@Override
+	public Class<?> getJavaClass() {
+		return null;
+	}
+
+	@Override
+	public Object cannonicalise( final Object value ) {
+		return value;
+	}
+
+	@Override
+	public Object extendedTypeDefinition() {
+		return null;
+	}
+
+	@Override
+	public RDFDatatype normalizeSubType( final Object value, final RDFDatatype dt ) {
+		return this;
+	}
+
+	@Override
+	public abstract T parse( final String lexicalForm ) throws DatatypeFormatException;
+
+	public abstract String unparseValue( final T value );
+
+	// helper for the compare function in each of the subclasses
+	protected static int compareByLexicalForms( final LiteralLabel value1, final LiteralLabel value2 ) {
+		final int lexCmp = value1.getLexicalForm().compareTo( value2.getLexicalForm() );
+		if ( lexCmp < 0 ) return Expr.CMP_LESS;
+		if ( lexCmp > 0 ) return Expr.CMP_GREATER;
+		return Expr.CMP_EQUAL;
+	}
+}
diff --git a/jena-arq/src/main/java/org/apache/jena/cdt/CompositeDatatypeList.java b/jena-arq/src/main/java/org/apache/jena/cdt/CompositeDatatypeList.java
new file mode 100644
index 0000000000..c377b607d4
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/cdt/CompositeDatatypeList.java
@@ -0,0 +1,353 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.cdt;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.jena.datatypes.DatatypeFormatException;
+import org.apache.jena.graph.Node;
+import org.apache.jena.graph.impl.LiteralLabel;
+import org.apache.jena.sparql.expr.Expr;
+import org.apache.jena.sparql.expr.ExprEvalException;
+import org.apache.jena.sparql.expr.ExprNotComparableException;
+import org.apache.jena.sparql.expr.NodeValue;
+
+public class CompositeDatatypeList extends CompositeDatatypeBase<List<CDTValue>>
+{
+	public final static String uri = "http://w3id.org/awslabs/neptune/SPARQL-CDTs/List";
+	public final static CompositeDatatypeList type = new CompositeDatatypeList();
+
+	protected CompositeDatatypeList() {}
+
+	@Override
+	public String getURI() {
+		return uri;
+	}
+
+	@Override
+	public boolean isValidValue( final Object value ) {
+		if ( !(value instanceof List<?>) ) {
+			return false;
+		}
+
+		final List<?> l = (List<?>) value;
+		for ( final Object e : l ) {
+			if ( !(e instanceof CDTValue) ) {
+				return false;
+			}
+		}
+
+		return true; 
+	}
+
+	@Override
+	public boolean isValidLiteral( final LiteralLabel lit ) {
+		final String dtURI = lit.getDatatypeURI();
+		if ( dtURI == null || ! dtURI.equals(uri) ) {
+			return false;
+		}
+
+		final String lang = lit.language();
+		if ( lang != null && ! lang.isEmpty() ) {
+			return false;
+		}
+
+		final String lex = lit.getLexicalForm();
+		return isValid(lex);
+	}
+
+	@Override
+	public boolean isValid( final String lexicalForm ) {
+		try {
+			ParserForCDTLiterals.parseListLiteral(lexicalForm);
+			return true;
+		}
+		catch ( final Exception ex ) {
+			return false;
+		}
+	}
+
+	@Override
+	public List<CDTValue> parse( final String lexicalForm ) throws DatatypeFormatException {
+		try {
+			return ParserForCDTLiterals.parseListLiteral(lexicalForm);
+		}
+		catch ( final Exception ex ) {
+			throw new DatatypeFormatException(lexicalForm, type, ex);
+		}
+	}
+
+	@Override
+	public String unparse( final Object value ) {
+		if ( !(value instanceof List<?>) ) {
+			throw new IllegalArgumentException();
+		}
+
+		@SuppressWarnings("unchecked")
+		final List<CDTValue> list = (List<CDTValue>) value;
+
+		return unparseValue(list);
+	}
+
+	@Override
+	public String unparseValue( final List<CDTValue> list ) {
+		final StringBuilder sb = new StringBuilder();
+		sb.append("[");
+		if ( ! list.isEmpty() ) {
+			final Iterator<CDTValue> it = list.iterator();
+			final CDTValue firstElmt = it.next();
+			final String firstElmtAsString = unparseListElement(firstElmt);
+			sb.append(firstElmtAsString);
+			while ( it.hasNext() ) {
+				final CDTValue nextElmt = it.next();
+				final String nextElmtAsString = unparseListElement(nextElmt);
+				sb.append(", ");
+				sb.append(nextElmtAsString);
+			}
+		}
+		sb.append("]");
+		return sb.toString();
+	}
+
+	protected String unparseListElement( final CDTValue elmt ) {
+		return elmt.asLexicalForm();
+	}
+
+	@Override
+	public int getHashCode( final LiteralLabel lit ) {
+		return lit.hashCode();
+	}
+
+	@Override
+	public boolean isEqual( final LiteralLabel value1, final LiteralLabel value2 ) {
+		if ( ! isListLiteral(value1) || ! isListLiteral(value2) ) {
+			return false;
+		}
+
+		final List<CDTValue> list1 = getValue(value1);
+		final List<CDTValue> list2 = getValue(value2);
+
+		if ( list1.size() != list2.size() ) return false;
+		if ( list1.isEmpty() ) return true;
+
+		final Iterator<CDTValue> it1 = list1.iterator();
+		final Iterator<CDTValue> it2 = list2.iterator();
+		while ( it1.hasNext() ) {
+			final CDTValue v1 = it1.next();
+			final CDTValue v2 = it2.next();
+
+			if ( v1.isNull() || v2.isNull() ) {
+				if ( ! v1.isNull() || ! v2.isNull() ) {
+					return false;
+				}
+			}
+			else {
+				final Node n1 = v1.asNode();
+				final Node n2 = v2.asNode();
+
+				if ( n1.isBlank() || n2.isBlank() ) {
+					// If at least one of the two elements is a blank node,
+					// throw an error unless both elements are *the same*
+					// blank node.
+					if ( ! n1.equals(n2) ) {
+						throw new ExprEvalException("blank nodes in lists cannot be compared");
+					}
+				}
+
+				if ( ! n1.sameValueAs(n2) ) {
+					return false;
+				}
+			}
+		}
+
+		return true;
+	}
+
+	/**
+	 * Assumes that the datatype of both of the given literals is cdt:List.
+	 * If 'sortOrderingCompare' is true, the two lists are compared as per the
+	 * semantics for ORDER BY. If 'sortOrderingCompare' is false, the comparison
+	 * applies the list-less-than semantics.
+	 */
+	public static int compare( final LiteralLabel value1, final LiteralLabel value2, final boolean sortOrderingCompare ) throws ExprNotComparableException {
+		// Verify first that both literals are well-formed. If at least one of
+		// them is not well-formed, then their relative order is undefined.
+		if ( ! value1.isWellFormed() || ! value2.isWellFormed() )
+			throw new ExprNotComparableException("Can't compare "+value1+" and "+value2);
+
+		final List<CDTValue> list1;
+		final List<CDTValue> list2;
+		try {
+			list1 = getValue(value1);
+			list2 = getValue(value2);
+		}
+		catch ( final Exception e ) {
+			throw new ExprNotComparableException("Can't compare "+value1+" and "+value2);
+		}
+
+		// If at least one of the two lists is empty, then we can decide their
+		// relative order without a pair-wise comparison of the list elements.
+		if ( list1.isEmpty() || list2.isEmpty() ) {
+			// The literal with the non-empty list is greater
+			// than the literal with the empty list.
+			if ( ! list1.isEmpty() ) return Expr.CMP_GREATER;
+			if ( ! list2.isEmpty() ) return Expr.CMP_LESS;
+
+			// If both lists are empty, under the ORDER BY semantics we then
+			// decide the order based on the lexical forms, and under the
+			// list-less-than semantics, both literals are considered equal.
+			if ( sortOrderingCompare )
+				return compareByLexicalForms(value1, value2);
+			else
+				return Expr.CMP_EQUAL;
+		}
+
+		// Now we go into the pair-wise comparison of the list elements.
+		final int n = Math.min( list1.size(), list2.size() );
+		for ( int i = 0; i < n; i++ ) {
+			final CDTValue elmt1 = list1.get(i);
+			final CDTValue elmt2 = list2.get(i);
+
+			if ( ! elmt1.isNull() && ! elmt2.isNull() ) {
+				final NodeValue nv1 = NodeValue.makeNode( elmt1.asNode() );
+				final NodeValue nv2 = NodeValue.makeNode( elmt2.asNode() );
+
+				if ( ! sortOrderingCompare && nv1.isBlank() && nv2.isBlank() ) {
+					throw new ExprNotComparableException("Can't compare "+value1+" and "+value2);
+				}
+
+				// Compare the values represented by the two list elements
+				// in terms of which of them is greater than the other one.
+				try {
+					final int c;
+					if ( sortOrderingCompare )
+						c = NodeValue.compareAlways(nv1, nv2);
+					else
+						c = NodeValue.compare(nv1, nv2);
+
+					// If one of the two values is greater than the other one,
+					// return this result as the result for comparing the two
+					// lists.
+					if ( c < 0 ) return Expr.CMP_LESS;
+					if ( c > 0 ) return Expr.CMP_GREATER;
+				}
+				catch ( final Exception e ) {
+					// If the comparison failed, simply move on to the
+					// next code block (i.e., nothing to do here).
+				}
+
+				// If the comparison of nv1 and nv2 failed with an exception
+				// or none of them is greater than the other, we test whether
+				// they are the same value (i.e., comparing them using =
+				// results in true).
+				boolean sameValueTestResult = false;
+				boolean sameValueTestFailed = false;
+				try {
+					sameValueTestResult = NodeValue.sameValueAs(nv1, nv2);
+				}
+				catch ( final Exception e ) {
+					sameValueTestFailed = true;
+				}
+
+				// If nv1 and nv2 are not the same value or testing whether
+				// they are the same value failed, we can terminate the whole
+				// comparison (as being done in the following if block).
+				// If, however, nv1 and nv2 are indeed the same value (in which
+				// case the following if condition is not true), we continue to
+				// the next pair of list elements.
+				if ( sameValueTestFailed == true || sameValueTestResult == false ) {
+					if ( sortOrderingCompare )
+						return Expr.CMP_INDETERMINATE;
+					else
+						throw new ExprNotComparableException("Can't compare "+value1+" and "+value2);
+				}
+			}
+			else {
+				// This else-branch covers cases in which at least one of the
+				// two list elements is null.
+				if ( ! sortOrderingCompare ) {
+					// When comparing as per the list-less-than semantics,
+					// null cannot be compared to anything other than null.
+					if ( ! elmt1.isNull() || ! elmt2.isNull() )
+						throw new ExprNotComparableException("Can't compare "+value1+" and "+value2);
+				}
+				else {
+					// When comparing as per the ORDER BY semantics, nulls are
+					// ordered the same. Hence, if both elements are null, we
+					// don't do anything here and simply advance to the next
+					// pair of elements.
+					// However, if only one of the two elements is null, then
+					// the literal with this element is ordered lower than the
+					// other literal.
+					if ( elmt1.isNull() && ! elmt2.isNull() )
+						return Expr.CMP_LESS;
+
+					if ( elmt2.isNull() && ! elmt1.isNull() )
+						return Expr.CMP_GREATER;
+				}
+			}
+		}
+
+		// At this point, the pair-wise comparison of list elements has not
+		// led to any decision. Now, we decide based on the size of the lists.
+		final int sizeDiff = list1.size() - list2.size();
+		if ( sizeDiff < 0 ) return Expr.CMP_LESS;
+		if ( sizeDiff > 0 ) return Expr.CMP_GREATER;
+
+		if ( sortOrderingCompare )
+			return compareByLexicalForms(value1, value2);
+		else
+			return Expr.CMP_EQUAL;
+	}
+
+	/**
+	 * Returns true if the given node is a literal with {@link #uri}
+	 * as its datatype URI. Notice that this does not mean that this
+	 * literal is actually valid; for checking validity, use
+	 * {@link #isValidLiteral(LiteralLabel)}.
+	 */
+	public static boolean isListLiteral( final Node n ) {
+		return n.isLiteral() && n.getLiteralDatatypeURI().equals(uri);
+	}
+
+	/**
+	 * Returns true if the datatype URI of the given literal is {@link #uri}.
+	 * Notice that this does not mean that this literal is actually valid;
+	 * for checking validity, use {@link #isValidLiteral(LiteralLabel)}.
+	 */
+	public static boolean isListLiteral( final LiteralLabel lit ) {
+		return lit.getDatatypeURI().equals(uri);
+	}
+
+	/**
+	 * Assumes that the datatype of the given literal is cdt:List.
+	 */
+	public static List<CDTValue> getValue( final LiteralLabel lit ) throws DatatypeFormatException {
+		final Object value = lit.getValue();
+		if ( value == null || ! (value instanceof List<?>) ) {
+			throw new IllegalArgumentException( lit.toString() + " - " + value );
+		}
+
+		@SuppressWarnings("unchecked")
+		final List<CDTValue> list = (List<CDTValue>) value;
+		return list;
+	}
+
+}
diff --git a/jena-arq/src/main/java/org/apache/jena/cdt/CompositeDatatypeMap.java b/jena-arq/src/main/java/org/apache/jena/cdt/CompositeDatatypeMap.java
new file mode 100644
index 0000000000..d3070127f9
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/cdt/CompositeDatatypeMap.java
@@ -0,0 +1,413 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.cdt;
+
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.TreeSet;
+
+import org.apache.jena.datatypes.DatatypeFormatException;
+import org.apache.jena.datatypes.xsd.impl.RDFLangString;
+import org.apache.jena.graph.Node;
+import org.apache.jena.graph.impl.LiteralLabel;
+import org.apache.jena.sparql.expr.Expr;
+import org.apache.jena.sparql.expr.ExprEvalException;
+import org.apache.jena.sparql.expr.ExprNotComparableException;
+import org.apache.jena.sparql.expr.NodeValue;
+
+public class CompositeDatatypeMap extends CompositeDatatypeBase<Map<CDTKey,CDTValue>>
+{
+	public final static String uri = "http://w3id.org/awslabs/neptune/SPARQL-CDTs/Map";
+	public final static CompositeDatatypeMap type = new CompositeDatatypeMap();
+
+	protected CompositeDatatypeMap() {}
+
+	@Override
+	public String getURI() {
+		return uri;
+	}
+
+	@Override
+	public boolean isValidValue( final Object value ) {
+		if ( !(value instanceof Map<?,?>) ) {
+			return false;
+		}
+
+		final Map<?,?> m = (Map<?,?>) value;
+		for ( final Map.Entry<?,?> e : m.entrySet() ) {
+			if ( !(e.getKey() instanceof CDTKey) ) {
+				return false;
+			}
+			if ( !(e.getValue() instanceof CDTValue) ) {
+				return false;
+			}
+		}
+
+		return true; 
+	}
+
+	@Override
+	public boolean isValidLiteral( final LiteralLabel lit ) {
+		final String dtURI = lit.getDatatypeURI();
+		if ( dtURI == null || ! dtURI.equals(uri) ) {
+			return false;
+		}
+
+		final String lang = lit.language();
+		if ( lang != null && ! lang.isEmpty() ) {
+			return false;
+		}
+
+		final String lex = lit.getLexicalForm();
+		return isValid(lex);
+	}
+
+	@Override
+	public boolean isValid( final String lexicalForm ) {
+		try {
+			ParserForCDTLiterals.parseMapLiteral(lexicalForm);
+			return true;
+		}
+		catch ( final Exception ex ) {
+			return false;
+		}
+	}
+
+	@Override
+	public Map<CDTKey,CDTValue> parse( final String lexicalForm ) throws DatatypeFormatException {
+		try {
+			return ParserForCDTLiterals.parseMapLiteral(lexicalForm);
+		}
+		catch ( final Exception ex ) {
+			throw new DatatypeFormatException(lexicalForm, type, ex);
+		}
+	}
+
+	@Override
+	public String unparse( final Object value ) {
+		if ( !(value instanceof Map<?,?>) ) {
+			throw new IllegalArgumentException();
+		}
+
+		@SuppressWarnings("unchecked")
+		final Map<CDTKey,CDTValue> map = (Map<CDTKey,CDTValue>) value;
+
+		return unparseValue(map);
+	}
+
+	@Override
+	public String unparseValue( final Map<CDTKey,CDTValue> map ) {
+		final StringBuilder sb = new StringBuilder();
+		sb.append("{");
+		if ( ! map.isEmpty() ) {
+			final Iterator<Map.Entry<CDTKey,CDTValue>> it = map.entrySet().iterator();
+
+			final Map.Entry<CDTKey,CDTValue> firstEntry = it.next();
+			unparseMapEntry(firstEntry, sb);
+
+			while ( it.hasNext() ) {
+				sb.append(", ");
+
+				final Map.Entry<CDTKey,CDTValue> nextEntry = it.next();
+				unparseMapEntry(nextEntry, sb);
+			}
+		}
+
+		sb.append("}");
+		return sb.toString();
+	}
+
+	protected void unparseMapEntry( final Map.Entry<CDTKey,CDTValue> entry, final StringBuilder sb ) {
+		sb.append( entry.getKey().asLexicalForm() );
+		sb.append(" : ");
+		sb.append( entry.getValue().asLexicalForm() );
+	}
+
+	@Override
+	public int getHashCode( final LiteralLabel lit ) {
+		return lit.hashCode();
+	}
+
+	@Override
+	public boolean isEqual( final LiteralLabel lit1, final LiteralLabel lit2 ) {
+		if ( ! isMapLiteral(lit1) || ! isMapLiteral(lit2) ) {
+			return false;
+		}
+
+		final Map<CDTKey,CDTValue> map1 = getValue(lit1);
+		final Map<CDTKey,CDTValue> map2 = getValue(lit2);
+
+		if ( map1.size() != map2.size() ) return false;
+
+		for ( final Map.Entry<CDTKey,CDTValue> entry1 : map1.entrySet() ) {
+			final CDTValue v1 = entry1.getValue();
+			final CDTValue v2 = map2.get( entry1.getKey() );
+			if ( v2 == null ) return false;
+
+			if ( v1.isNull() || v2.isNull() ) {
+				if ( ! v1.isNull() || ! v2.isNull() ) {
+					return false;
+				}
+			}
+			else {
+				final Node n1 = v1.asNode();
+				final Node n2 = v2.asNode();
+
+				if ( n1.isBlank() || n2.isBlank() ) {
+					// If at least one of the two elements is a blank node,
+					// throw an error unless both elements are *the same*
+					// blank node.
+					if ( ! n1.equals(n2) ) {
+						throw new ExprEvalException("blank nodes in maps cannot be compared");
+					}
+				}
+
+				if ( ! n1.sameValueAs(n2) ) return false;
+			}
+		}
+
+		return true;
+	}
+
+	/**
+	 * Assumes that the datatype of both of the given literals is cdt:Map.
+	 *
+	 * If 'sortOrderingCompare' is true, the two maps are compared as per the
+	 * semantics for ORDER BY. If 'sortOrderingCompare' is false, the comparison
+	 * applies the map-less-than semantics.
+	 */
+	public static int compare( final LiteralLabel value1, final LiteralLabel value2, final boolean sortOrderingCompare ) throws ExprNotComparableException {
+		// Verify first that both literals are well-formed. If at least one of
+		// them is not well-formed, then their relative order is undefined.
+		if ( ! value1.isWellFormed() || ! value2.isWellFormed() )
+			throw new ExprNotComparableException("Can't compare "+value1+" and "+value2);
+
+		final Map<CDTKey,CDTValue> map1;
+		final Map<CDTKey,CDTValue> map2;
+		try {
+			map1 = getValue(value1);
+			map2 = getValue(value2);
+		}
+		catch ( final Exception e ) {
+			throw new ExprNotComparableException("Can't compare "+value1+" and "+value2);
+		}
+
+		// If at least one of the two maps is empty, then we can decide their
+		// relative order without a pair-wise comparison of the map entries.
+		if ( map1.isEmpty() || map2.isEmpty() ) {
+			// The literal with the non-empty map is greater
+			// than the literal with the empty map.
+			if ( ! map1.isEmpty() ) return Expr.CMP_GREATER;
+			if ( ! map2.isEmpty() ) return Expr.CMP_LESS;
+
+			// If both maps are empty, under the ORDER BY semantics we then
+			// decide the order based on the lexical forms, and under the
+			// map-less-than semantics, both literals are considered equal.
+			if ( sortOrderingCompare )
+				return compareByLexicalForms(value1, value2);
+			else
+				return Expr.CMP_EQUAL;
+		}
+
+		// Now we go into the pair-wise comparison of the map entries.
+		// To this end, we first need to sort the map entries.
+		final CDTKeySorter keySorter = new CDTKeySorter();
+		final TreeSet<CDTKey> sortedKeys1 = new TreeSet<>(keySorter);
+		final TreeSet<CDTKey> sortedKeys2 = new TreeSet<>(keySorter);
+		sortedKeys1.addAll( map1.keySet() );
+		sortedKeys2.addAll( map2.keySet() );
+
+		// Now we can perform the pair-wise comparison of the map entries.
+		final Iterator<CDTKey> it1 = sortedKeys1.iterator();
+		final Iterator<CDTKey> it2 = sortedKeys2.iterator();
+		final int n = Math.min( sortedKeys1.size(), sortedKeys2.size() );
+		for ( int i = 0; i < n; i++ ) {
+			final CDTKey k1 = it1.next();
+			final CDTKey k2 = it2.next();
+
+			final int kCmp = keySorter.compare(k1, k2);
+			if ( kCmp < 0 ) return Expr.CMP_LESS;
+			if ( kCmp > 0 ) return Expr.CMP_GREATER;
+
+			final CDTValue v1 = map1.get(k1);
+			final CDTValue v2 = map2.get(k2);
+			if ( ! v1.isNull() && ! v2.isNull() ) {
+				final NodeValue nv1 = NodeValue.makeNode( v1.asNode() );
+				final NodeValue nv2 = NodeValue.makeNode( v2.asNode() );
+
+				// Compare the actual values represented by the two map values
+				// in terms of which of them is greater than the other one.
+				try {
+					final int c;
+					if ( sortOrderingCompare )
+						c = NodeValue.compareAlways(nv1, nv2);
+					else
+						c = NodeValue.compare(nv1, nv2);
+
+					// If one of the two values is greater than the other one,
+					// return this result as the result for comparing the two
+					// maps.
+					if ( c < 0 ) return Expr.CMP_LESS;
+					if ( c > 0 ) return Expr.CMP_GREATER;
+				}
+				catch ( final Exception e ) {
+					// If the comparison failed, simply move on to the
+					// next code block (i.e., nothing to do here).
+				}
+
+				// If the comparison of nv1 and nv2 failed with an exception
+				// or none of them is greater than the other, we test whether
+				// they are the same value (i.e., comparing them using =
+				// results in true).
+				boolean sameValueTestResult = false;
+				boolean sameValueTestFailed = false;
+				try {
+					sameValueTestResult = NodeValue.sameValueAs(nv1, nv2);
+				}
+				catch ( final Exception e ) {
+					sameValueTestFailed = true;
+				}
+
+				// If nv1 and nv2 are not the same value or testing whether
+				// they are the same value failed, we can terminate the whole
+				// comparison (as being done in the following if block).
+				// If, however, nv1 and nv2 are indeed the same value (in which
+				// case the following if condition is not true), we continue to
+				// the next pair of map entries.
+				if ( sameValueTestFailed == true || sameValueTestResult == false ) {
+					if ( sortOrderingCompare )
+						return Expr.CMP_INDETERMINATE;
+					else
+						throw new ExprNotComparableException("Can't compare "+value1+" and "+value2);
+				}
+			}
+			else {
+				// This else-branch covers cases in which at least one of the
+				// two map entries has null as its value.
+				if ( ! sortOrderingCompare ) {
+					// When comparing as per the map-less-than semantics,
+					// null cannot be compared to anything other than null.
+					if ( ! v1.isNull() || ! v2.isNull() )
+						throw new ExprNotComparableException("Can't compare "+value1+" and "+value2);
+				}
+				else {
+					// When comparing as per the ORDER BY semantics, nulls are
+					// ordered the same. Hence, if both map entries have null
+					// as their value, we don't do anything here and simply
+					// advance to the next pair of map entries.
+					// However, if the map value of only one of the two map
+					// entries is null, then the literal with this map entry
+					// is ordered lower than the other literal.
+					if ( v1.isNull() && ! v2.isNull() )
+						return Expr.CMP_LESS;
+
+					if ( v2.isNull() && ! v1.isNull() )
+						return Expr.CMP_GREATER;
+				}
+			}
+		}
+
+		// At this point, the pair-wise comparison of map entries has not led
+		// to any decision. Now, we decide based on the size of the maps.
+		final int sizeDiff = map1.size() - map2.size();
+		if ( sizeDiff < 0 ) return Expr.CMP_LESS;
+		if ( sizeDiff > 0 ) return Expr.CMP_GREATER;
+
+		if ( sortOrderingCompare )
+			return compareByLexicalForms(value1, value2);
+		else
+			return Expr.CMP_EQUAL;
+	}
+
+	/**
+	 * Returns true if the given node is a literal with {@link #uri}
+	 * as its datatype URI. Notice that this does not mean that this
+	 * literal is actually valid; for checking validity, use
+	 * {@link #isValidLiteral(LiteralLabel)}.
+	 */
+	public static boolean isMapLiteral( final Node n ) {
+		return n.isLiteral() && n.getLiteralDatatypeURI().equals(uri);
+	}
+
+	/**
+	 * Returns true if the datatype URI of the given {@link LiteralLabel} is
+	 * {@link #uri}. Notice that this does not mean that this LiteralLabel is
+	 * actually valid; for checking validity, use {@link #isValidLiteral(LiteralLabel)}.
+	 */
+	public static boolean isMapLiteral( final LiteralLabel lit ) {
+		return lit.getDatatypeURI().equals(uri);
+	}
+
+	/**
+	 * Assumes that the datatype of the given literal is cdt:Map.
+	 */
+	public static Map<CDTKey,CDTValue> getValue( final LiteralLabel lit ) throws DatatypeFormatException {
+		final Object value = lit.getValue();
+		if ( value == null || ! (value instanceof Map<?,?>) ) {
+			throw new IllegalArgumentException( lit.toString() + " - " + value );
+		}
+
+		@SuppressWarnings("unchecked")
+		final Map<CDTKey,CDTValue> map = (Map<CDTKey,CDTValue>) value;
+		return map;
+	}
+
+	protected static class CDTKeySorter implements Comparator<CDTKey> {
+		@Override
+		public int compare( final CDTKey k1, final CDTKey k2 ) {
+			final Node n1 = k1.asNode();
+			final Node n2 = k2.asNode();
+
+			if ( n1.isURI() ) {
+				if ( ! n2.isURI() ) {
+					return Expr.CMP_LESS;
+				}
+
+				final String uri1 = n1.getURI();
+				final String uri2 = n2.getURI();
+				return uri1.compareTo(uri2);
+			}
+			else if ( n2.isURI() ) {
+				return Expr.CMP_GREATER;
+			}
+
+			// at this point, both RDF terms (n1 and n2) should be literals
+			final String dt1 = n1.getLiteralDatatypeURI();
+			final String dt2 = n2.getLiteralDatatypeURI();
+			final int dtCmp = dt1.compareTo(dt2);
+			if ( dtCmp != 0 ) {
+				return dtCmp;
+			}
+
+			final String lex1 = n1.getLiteralLexicalForm();
+			final String lex2 = n2.getLiteralLexicalForm();
+			final int lexCmp = lex1.compareTo(lex2);
+			if ( lexCmp != 0 || ! RDFLangString.rdfLangString.getURI().equals(dt1) ) {
+				return lexCmp;
+			}
+
+			// at this point, both literals are rdf:LangString literals with the same value
+			final String lang1 = n1.getLiteralLanguage();
+			final String lang2 = n2.getLiteralLanguage();
+			return lang1.compareTo(lang2);
+		}
+	}
+
+}
diff --git a/jena-arq/src/main/java/org/apache/jena/cdt/ParserForCDTLiterals.java b/jena-arq/src/main/java/org/apache/jena/cdt/ParserForCDTLiterals.java
new file mode 100644
index 0000000000..28b376f7f4
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/cdt/ParserForCDTLiterals.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.cdt;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.jena.cdt.parser.CDTLiteralParser;
+import org.apache.jena.cdt.parser.ParseException;
+import org.apache.jena.cdt.parser.TokenMgrError;
+import org.apache.jena.riot.system.ParserProfile;
+import org.apache.jena.riot.system.RiotLib;
+
+public class ParserForCDTLiterals
+{
+	public static List<CDTValue> parseListLiteral( final String lex ) {
+		return parseListLiteral( RiotLib.dftProfile(), lex );
+	}
+
+	public static List<CDTValue> parseListLiteral( final ParserProfile pp, final String lex ) {
+		final Reader reader = new StringReader(lex);
+		final List<CDTValue> result = parseListLiteral(pp, reader);
+
+		try { reader.close(); } catch ( final IOException e ) {
+			throw new CDTLiteralParseException("Closing the reader caused an exception.", e);
+		}
+
+		return result;
+	}
+
+	public static List<CDTValue> parseListLiteral( final Reader reader ) {
+		return parseListLiteral( RiotLib.dftProfile(), reader );
+	}
+
+	public static List<CDTValue> parseListLiteral( final ParserProfile pp, final Reader reader ) {
+		final CDTLiteralParser parser = new CDTLiteralParser(reader);
+		parser.setProfile(pp);
+
+		final List<CDTValue> list;
+		try {
+			list = parser.List();
+		}
+		catch ( final ParseException | TokenMgrError ex ) {
+			throw new CDTLiteralParseException( ex.getMessage() );
+		}
+		catch ( final CDTLiteralParseException ex ) {
+			throw ex;
+		}
+		catch ( final Throwable th ) {
+			throw new CDTLiteralParseException( th.getMessage(), th );
+		}
+
+		return list;
+	}
+
+	public static Map<CDTKey,CDTValue> parseMapLiteral( final String lex ) {
+		return parseMapLiteral( RiotLib.dftProfile(), lex );
+	}
+
+	public static Map<CDTKey,CDTValue> parseMapLiteral( final ParserProfile pp, final String lex ) {
+		final Reader reader = new StringReader(lex);
+		final Map<CDTKey,CDTValue> result = parseMapLiteral(pp, reader);
+
+		try { reader.close(); } catch ( final IOException e ) {
+			throw new CDTLiteralParseException("Closing the reader caused an exception.", e);
+		}
+
+		return result;
+	}
+
+	public static Map<CDTKey,CDTValue> parseMapLiteral( final Reader reader ) {
+		return parseMapLiteral( RiotLib.dftProfile(), reader );
+	}
+
+	public static Map<CDTKey,CDTValue> parseMapLiteral( final ParserProfile pp, final Reader reader ) {
+		final CDTLiteralParser parser = new CDTLiteralParser(reader);
+		parser.setProfile(pp);
+
+		final Map<CDTKey,CDTValue> map;
+		try {
+			map = parser.Map();
+		}
+		catch ( final ParseException | TokenMgrError ex ) {
+			throw new CDTLiteralParseException( ex.getMessage() );
+		}
+		catch ( final CDTLiteralParseException ex ) {
+			throw ex;
+		}
+		catch ( final Throwable th ) {
+			throw new CDTLiteralParseException( th.getMessage(), th );
+		}
+
+		return map;
+	}
+
+}
diff --git a/jena-arq/src/main/java/org/apache/jena/cdt/parser/CDTLiteralParser.java b/jena-arq/src/main/java/org/apache/jena/cdt/parser/CDTLiteralParser.java
new file mode 100644
index 0000000000..febae9906b
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/cdt/parser/CDTLiteralParser.java
@@ -0,0 +1,560 @@
+/* Generated By:JavaCC: Do not edit this line. CDTLiteralParser.java */
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.cdt.parser;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.jena.cdt.*;
+import org.apache.jena.graph.Node;
+
+import static org.apache.jena.riot.lang.extra.LangParserLib.*;
+
+public class CDTLiteralParser extends CDTLiteralParserBase implements CDTLiteralParserConstants {
+
+// --- Entry point for cdt:List literals
+  final public List<CDTValue> List() throws ParseException {
+                          List<CDTValue> l = new ArrayList<CDTValue>();
+    jj_consume_token(LBRACKET);
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+    case TRUE:
+    case FALSE:
+    case INTEGER:
+    case DECIMAL:
+    case DOUBLE:
+    case STRING_LITERAL1:
+    case STRING_LITERAL2:
+    case STRING_LITERAL_LONG1:
+    case STRING_LITERAL_LONG2:
+    case IRIref:
+    case BLANK_NODE_LABEL:
+    case NULL:
+    case LBRACE:
+    case LBRACKET:
+      NonEmptyListContent(l);
+      break;
+    default:
+      jj_la1[0] = jj_gen;
+      ;
+    }
+    jj_consume_token(RBRACKET);
+    {if (true) return l;}
+    throw new Error("Missing return statement in function");
+  }
+
+  final public void NonEmptyListContent(List<CDTValue> l) throws ParseException {
+    ListElement(l);
+    label_1:
+    while (true) {
+      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+      case COMMA:
+        ;
+        break;
+      default:
+        jj_la1[1] = jj_gen;
+        break label_1;
+      }
+      jj_consume_token(COMMA);
+      ListElement(l);
+    }
+  }
+
+  final public void ListElement(List<CDTValue> l) throws ParseException {
+                                       String iri; Node n; List<CDTValue> subList; Map<CDTKey,CDTValue> m;
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+    case IRIref:
+      iri = IRI_REF();
+                          n = createURI(iri, token.beginLine, token.beginColumn);  l.add( CDTFactory.createValue(n) );
+      break;
+    case BLANK_NODE_LABEL:
+      n = BlankNode();
+                          l.add( CDTFactory.createValue(n) );
+      break;
+    case STRING_LITERAL1:
+    case STRING_LITERAL2:
+    case STRING_LITERAL_LONG1:
+    case STRING_LITERAL_LONG2:
+      n = RDFLiteral();
+                          l.add( CDTFactory.createValue(n) );
+      break;
+    case INTEGER:
+    case DECIMAL:
+    case DOUBLE:
+      n = NumericLiteral();
+                          l.add( CDTFactory.createValue(n) );
+      break;
+    case TRUE:
+    case FALSE:
+      n = BooleanLiteral();
+                          l.add( CDTFactory.createValue(n) );
+      break;
+    case NULL:
+      jj_consume_token(NULL);
+                          l.add( CDTFactory.getNullValue() );
+      break;
+    case LBRACKET:
+      subList = List();
+                          l.add( CDTFactory.createValue(subList) );
+      break;
+    case LBRACE:
+      m = Map();
+                          l.add( CDTFactory.createValue(m) );
+      break;
+    default:
+      jj_la1[2] = jj_gen;
+      jj_consume_token(-1);
+      throw new ParseException();
+    }
+  }
+
+// --- Entry point for cdt:Map literals
+  final public Map<CDTKey,CDTValue> Map() throws ParseException {
+                               Map<CDTKey,CDTValue> m = new HashMap<CDTKey,CDTValue>();
+    jj_consume_token(LBRACE);
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+    case TRUE:
+    case FALSE:
+    case INTEGER:
+    case DECIMAL:
+    case DOUBLE:
+    case STRING_LITERAL1:
+    case STRING_LITERAL2:
+    case STRING_LITERAL_LONG1:
+    case STRING_LITERAL_LONG2:
+    case IRIref:
+      NonEmptyMapContent(m);
+      break;
+    default:
+      jj_la1[3] = jj_gen;
+      ;
+    }
+    jj_consume_token(RBRACE);
+    {if (true) return m;}
+    throw new Error("Missing return statement in function");
+  }
+
+  final public void NonEmptyMapContent(Map<CDTKey,CDTValue> m) throws ParseException {
+    MapEntry(m);
+    label_2:
+    while (true) {
+      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+      case COMMA:
+        ;
+        break;
+      default:
+        jj_la1[4] = jj_gen;
+        break label_2;
+      }
+      jj_consume_token(COMMA);
+      MapEntry(m);
+    }
+  }
+
+  final public void MapEntry(Map<CDTKey,CDTValue> m) throws ParseException {
+                                          CDTKey key; CDTValue value;
+    key = MapKey();
+    jj_consume_token(COLON);
+    value = MapValue();
+    final CDTValue oldValue = m.put(key, value);
+    if ( oldValue != null ) {if (true) throw new ParseException("map with non-unique key (" + key.toString() + ")");}
+  }
+
+  final public CDTKey MapKey() throws ParseException {
+                    String iri; Node n;
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+    case IRIref:
+      iri = IRI_REF();
+                          n = createURI(iri, token.beginLine, token.beginColumn);  {if (true) return CDTFactory.createKey(n);}
+      break;
+    case STRING_LITERAL1:
+    case STRING_LITERAL2:
+    case STRING_LITERAL_LONG1:
+    case STRING_LITERAL_LONG2:
+      n = RDFLiteral();
+                          {if (true) return CDTFactory.createKey(n);}
+      break;
+    case INTEGER:
+    case DECIMAL:
+    case DOUBLE:
+      n = NumericLiteral();
+                          {if (true) return CDTFactory.createKey(n);}
+      break;
+    case TRUE:
+    case FALSE:
+      n = BooleanLiteral();
+                          {if (true) return CDTFactory.createKey(n);}
+      break;
+    default:
+      jj_la1[5] = jj_gen;
+      jj_consume_token(-1);
+      throw new ParseException();
+    }
+    throw new Error("Missing return statement in function");
+  }
+
+  final public CDTValue MapValue() throws ParseException {
+                        String iri; Node n; List<CDTValue> subList; Map<CDTKey,CDTValue> m;
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+    case IRIref:
+      iri = IRI_REF();
+                          n = createURI(iri, token.beginLine, token.beginColumn);  {if (true) return CDTFactory.createValue(n);}
+      break;
+    case BLANK_NODE_LABEL:
+      n = BlankNode();
+                          {if (true) return CDTFactory.createValue(n);}
+      break;
+    case STRING_LITERAL1:
+    case STRING_LITERAL2:
+    case STRING_LITERAL_LONG1:
+    case STRING_LITERAL_LONG2:
+      n = RDFLiteral();
+                          {if (true) return CDTFactory.createValue(n);}
+      break;
+    case INTEGER:
+    case DECIMAL:
+    case DOUBLE:
+      n = NumericLiteral();
+                          {if (true) return CDTFactory.createValue(n);}
+      break;
+    case TRUE:
+    case FALSE:
+      n = BooleanLiteral();
+                          {if (true) return CDTFactory.createValue(n);}
+      break;
+    case NULL:
+      jj_consume_token(NULL);
+                          {if (true) return CDTFactory.getNullValue();}
+      break;
+    case LBRACKET:
+      subList = List();
+                          {if (true) return CDTFactory.createValue(subList);}
+      break;
+    case LBRACE:
+      m = Map();
+                          {if (true) return CDTFactory.createValue(m);}
+      break;
+    default:
+      jj_la1[6] = jj_gen;
+      jj_consume_token(-1);
+      throw new ParseException();
+    }
+    throw new Error("Missing return statement in function");
+  }
+
+// ---- Basic terms
+  final public String IRI_REF() throws ParseException {
+                     Token t ;
+    t = jj_consume_token(IRIref);
+    {if (true) return resolveQuotedIRI(t.image, t.beginLine, t.beginColumn) ;}
+    throw new Error("Missing return statement in function");
+  }
+
+  final public Node BlankNode() throws ParseException {
+                      Token t = null ;
+    t = jj_consume_token(BLANK_NODE_LABEL);
+    {if (true) return createBNode(t.image, t.beginLine, t.beginColumn);}
+    throw new Error("Missing return statement in function");
+  }
+
+  final public Node NumericLiteral() throws ParseException {
+                          Token t ;
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+    case INTEGER:
+      t = jj_consume_token(INTEGER);
+                  {if (true) return createLiteralInteger(t.image, t.beginLine, t.beginColumn);}
+      break;
+    case DECIMAL:
+      t = jj_consume_token(DECIMAL);
+                  {if (true) return createLiteralDecimal(t.image, t.beginLine, t.beginColumn);}
+      break;
+    case DOUBLE:
+      t = jj_consume_token(DOUBLE);
+                  {if (true) return createLiteralDouble(t.image, t.beginLine, t.beginColumn);}
+      break;
+    default:
+      jj_la1[7] = jj_gen;
+      jj_consume_token(-1);
+      throw new ParseException();
+    }
+    throw new Error("Missing return statement in function");
+  }
+
+  final public Node BooleanLiteral() throws ParseException {
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+    case TRUE:
+      jj_consume_token(TRUE);
+            {if (true) return XSD_TRUE;}
+      break;
+    case FALSE:
+      jj_consume_token(FALSE);
+             {if (true) return XSD_FALSE;}
+      break;
+    default:
+      jj_la1[8] = jj_gen;
+      jj_consume_token(-1);
+      throw new ParseException();
+    }
+    throw new Error("Missing return statement in function");
+  }
+
+  final public Node RDFLiteral() throws ParseException {
+                      String lex = null ;
+    lex = String();
+    String lang = null ; String dt = null ;
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+    case LANGTAG:
+    case DATATYPE:
+      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+      case LANGTAG:
+        lang = Langtag();
+        break;
+      case DATATYPE:
+        jj_consume_token(DATATYPE);
+        dt = IRI_REF();
+        break;
+      default:
+        jj_la1[9] = jj_gen;
+        jj_consume_token(-1);
+        throw new ParseException();
+      }
+      break;
+    default:
+      jj_la1[10] = jj_gen;
+      ;
+    }
+    {if (true) return createLiteral(lex, lang, dt, token.beginLine, token.beginColumn);}
+    throw new Error("Missing return statement in function");
+  }
+
+  final public String Langtag() throws ParseException {
+                     Token t ;
+    t = jj_consume_token(LANGTAG);
+    String lang = stripChars(t.image, 1) ; {if (true) return lang ;}
+    throw new Error("Missing return statement in function");
+  }
+
+  final public String String() throws ParseException {
+                    Token t ;  String lex ;
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+    case STRING_LITERAL1:
+      t = jj_consume_token(STRING_LITERAL1);
+                            lex = stripQuotes(t.image) ;
+      break;
+    case STRING_LITERAL2:
+      t = jj_consume_token(STRING_LITERAL2);
+                            lex = stripQuotes(t.image) ;
+      break;
+    case STRING_LITERAL_LONG1:
+      t = jj_consume_token(STRING_LITERAL_LONG1);
+                                 lex = stripQuotes3(t.image) ;
+      break;
+    case STRING_LITERAL_LONG2:
+      t = jj_consume_token(STRING_LITERAL_LONG2);
+                                 lex = stripQuotes3(t.image) ;
+      break;
+    default:
+      jj_la1[11] = jj_gen;
+      jj_consume_token(-1);
+      throw new ParseException();
+    }
+      lex = unescapeStr(lex,  t.beginLine, t.beginColumn) ;
+      {if (true) return lex ;}
+    throw new Error("Missing return statement in function");
+  }
+
+  /** Generated Token Manager. */
+  public CDTLiteralParserTokenManager token_source;
+  JavaCharStream jj_input_stream;
+  /** Current token. */
+  public Token token;
+  /** Next token. */
+  public Token jj_nt;
+  private int jj_ntk;
+  private int jj_gen;
+  final private int[] jj_la1 = new int[12];
+  static private int[] jj_la1_0;
+  static private int[] jj_la1_1;
+  static {
+      jj_la1_init_0();
+      jj_la1_init_1();
+   }
+   private static void jj_la1_init_0() {
+      jj_la1_0 = new int[] {0x2c6f0f80,0x80000000,0x2c6f0f80,0x2f0f80,0x80000000,0x2f0f80,0x2c6f0f80,0xe00,0x180,0x800000,0x800000,0xf0000,};
+   }
+   private static void jj_la1_init_1() {
+      jj_la1_1 = new int[] {0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x2,0x0,};
+   }
+
+  /** Constructor with InputStream. */
+  public CDTLiteralParser(java.io.InputStream stream) {
+     this(stream, null);
+  }
+  /** Constructor with InputStream and supplied encoding */
+  public CDTLiteralParser(java.io.InputStream stream, String encoding) {
+    try { jj_input_stream = new JavaCharStream(stream, encoding, 1, 1); } catch(java.io.UnsupportedEncodingException e) { throw new RuntimeException(e); }
+    token_source = new CDTLiteralParserTokenManager(jj_input_stream);
+    token = new Token();
+    jj_ntk = -1;
+    jj_gen = 0;
+    for (int i = 0; i < 12; i++) jj_la1[i] = -1;
+  }
+
+  /** Reinitialise. */
+  public void ReInit(java.io.InputStream stream) {
+     ReInit(stream, null);
+  }
+  /** Reinitialise. */
+  public void ReInit(java.io.InputStream stream, String encoding) {
+    try { jj_input_stream.ReInit(stream, encoding, 1, 1); } catch(java.io.UnsupportedEncodingException e) { throw new RuntimeException(e); }
+    token_source.ReInit(jj_input_stream);
+    token = new Token();
+    jj_ntk = -1;
+    jj_gen = 0;
+    for (int i = 0; i < 12; i++) jj_la1[i] = -1;
+  }
+
+  /** Constructor. */
+  public CDTLiteralParser(java.io.Reader stream) {
+    jj_input_stream = new JavaCharStream(stream, 1, 1);
+    token_source = new CDTLiteralParserTokenManager(jj_input_stream);
+    token = new Token();
+    jj_ntk = -1;
+    jj_gen = 0;
+    for (int i = 0; i < 12; i++) jj_la1[i] = -1;
+  }
+
+  /** Reinitialise. */
+  public void ReInit(java.io.Reader stream) {
+    jj_input_stream.ReInit(stream, 1, 1);
+    token_source.ReInit(jj_input_stream);
+    token = new Token();
+    jj_ntk = -1;
+    jj_gen = 0;
+    for (int i = 0; i < 12; i++) jj_la1[i] = -1;
+  }
+
+  /** Constructor with generated Token Manager. */
+  public CDTLiteralParser(CDTLiteralParserTokenManager tm) {
+    token_source = tm;
+    token = new Token();
+    jj_ntk = -1;
+    jj_gen = 0;
+    for (int i = 0; i < 12; i++) jj_la1[i] = -1;
+  }
+
+  /** Reinitialise. */
+  public void ReInit(CDTLiteralParserTokenManager tm) {
+    token_source = tm;
+    token = new Token();
+    jj_ntk = -1;
+    jj_gen = 0;
+    for (int i = 0; i < 12; i++) jj_la1[i] = -1;
+  }
+
+  private Token jj_consume_token(int kind) throws ParseException {
+    Token oldToken;
+    if ((oldToken = token).next != null) token = token.next;
+    else token = token.next = token_source.getNextToken();
+    jj_ntk = -1;
+    if (token.kind == kind) {
+      jj_gen++;
+      return token;
+    }
+    token = oldToken;
+    jj_kind = kind;
+    throw generateParseException();
+  }
+
+
+/** Get the next Token. */
+  final public Token getNextToken() {
+    if (token.next != null) token = token.next;
+    else token = token.next = token_source.getNextToken();
+    jj_ntk = -1;
+    jj_gen++;
+    return token;
+  }
+
+/** Get the specific Token. */
+  final public Token getToken(int index) {
+    Token t = token;
+    for (int i = 0; i < index; i++) {
+      if (t.next != null) t = t.next;
+      else t = t.next = token_source.getNextToken();
+    }
+    return t;
+  }
+
+  private int jj_ntk() {
+    if ((jj_nt=token.next) == null)
+      return (jj_ntk = (token.next=token_source.getNextToken()).kind);
+    else
+      return (jj_ntk = jj_nt.kind);
+  }
+
+  private java.util.List<int[]> jj_expentries = new java.util.ArrayList<int[]>();
+  private int[] jj_expentry;
+  private int jj_kind = -1;
+
+  /** Generate ParseException. */
+  public ParseException generateParseException() {
+    jj_expentries.clear();
+    boolean[] la1tokens = new boolean[40];
+    if (jj_kind >= 0) {
+      la1tokens[jj_kind] = true;
+      jj_kind = -1;
+    }
+    for (int i = 0; i < 12; i++) {
+      if (jj_la1[i] == jj_gen) {
+        for (int j = 0; j < 32; j++) {
+          if ((jj_la1_0[i] & (1<<j)) != 0) {
+            la1tokens[j] = true;
+          }
+          if ((jj_la1_1[i] & (1<<j)) != 0) {
+            la1tokens[32+j] = true;
+          }
+        }
+      }
+    }
+    for (int i = 0; i < 40; i++) {
+      if (la1tokens[i]) {
+        jj_expentry = new int[1];
+        jj_expentry[0] = i;
+        jj_expentries.add(jj_expentry);
+      }
+    }
+    int[][] exptokseq = new int[jj_expentries.size()][];
+    for (int i = 0; i < jj_expentries.size(); i++) {
+      exptokseq[i] = jj_expentries.get(i);
+    }
+    return new ParseException(token, exptokseq, tokenImage);
+  }
+
+  /** Enable tracing. */
+  final public void enable_tracing() {
+  }
+
+  /** Disable tracing. */
+  final public void disable_tracing() {
+  }
+
+}
diff --git a/jena-arq/src/main/java/org/apache/jena/cdt/parser/CDTLiteralParserConstants.java b/jena-arq/src/main/java/org/apache/jena/cdt/parser/CDTLiteralParserConstants.java
new file mode 100644
index 0000000000..804c8a99a5
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/cdt/parser/CDTLiteralParserConstants.java
@@ -0,0 +1,147 @@
+/* Generated By:JavaCC: Do not edit this line. CDTLiteralParserConstants.java */
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.cdt.parser;
+
+
+/**
+ * Token literal values and constants.
+ * Generated by org.javacc.parser.OtherFilesGen#start()
+ */
+public interface CDTLiteralParserConstants {
+
+  /** End of File. */
+  int EOF = 0;
+  /** RegularExpression Id. */
+  int WS = 6;
+  /** RegularExpression Id. */
+  int TRUE = 7;
+  /** RegularExpression Id. */
+  int FALSE = 8;
+  /** RegularExpression Id. */
+  int INTEGER = 9;
+  /** RegularExpression Id. */
+  int DECIMAL = 10;
+  /** RegularExpression Id. */
+  int DOUBLE = 11;
+  /** RegularExpression Id. */
+  int EXPONENT = 12;
+  /** RegularExpression Id. */
+  int QUOTE_3D = 13;
+  /** RegularExpression Id. */
+  int QUOTE_3S = 14;
+  /** RegularExpression Id. */
+  int ECHAR = 15;
+  /** RegularExpression Id. */
+  int STRING_LITERAL1 = 16;
+  /** RegularExpression Id. */
+  int STRING_LITERAL2 = 17;
+  /** RegularExpression Id. */
+  int STRING_LITERAL_LONG1 = 18;
+  /** RegularExpression Id. */
+  int STRING_LITERAL_LONG2 = 19;
+  /** RegularExpression Id. */
+  int DIGITS = 20;
+  /** RegularExpression Id. */
+  int IRIref = 21;
+  /** RegularExpression Id. */
+  int BLANK_NODE_LABEL = 22;
+  /** RegularExpression Id. */
+  int LANGTAG = 23;
+  /** RegularExpression Id. */
+  int A2Z = 24;
+  /** RegularExpression Id. */
+  int A2ZN = 25;
+  /** RegularExpression Id. */
+  int NULL = 26;
+  /** RegularExpression Id. */
+  int LBRACE = 27;
+  /** RegularExpression Id. */
+  int RBRACE = 28;
+  /** RegularExpression Id. */
+  int LBRACKET = 29;
+  /** RegularExpression Id. */
+  int RBRACKET = 30;
+  /** RegularExpression Id. */
+  int COMMA = 31;
+  /** RegularExpression Id. */
+  int COLON = 32;
+  /** RegularExpression Id. */
+  int DATATYPE = 33;
+  /** RegularExpression Id. */
+  int AT = 34;
+  /** RegularExpression Id. */
+  int PN_CHARS_BASE = 35;
+  /** RegularExpression Id. */
+  int PN_CHARS_U = 36;
+  /** RegularExpression Id. */
+  int PN_CHARS = 37;
+  /** RegularExpression Id. */
+  int PN_LOCAL = 38;
+  /** RegularExpression Id. */
+  int UNKNOWN = 39;
+
+  /** Lexical state. */
+  int DEFAULT = 0;
+
+  /** Literal token values. */
+  String[] tokenImage = {
+    "<EOF>",
+    "\" \"",
+    "\"\\t\"",
+    "\"\\n\"",
+    "\"\\r\"",
+    "\"\\f\"",
+    "<WS>",
+    "\"true\"",
+    "\"false\"",
+    "<INTEGER>",
+    "<DECIMAL>",
+    "<DOUBLE>",
+    "<EXPONENT>",
+    "\"\\\"\\\"\\\"\"",
+    "\"\\\'\\\'\\\'\"",
+    "<ECHAR>",
+    "<STRING_LITERAL1>",
+    "<STRING_LITERAL2>",
+    "<STRING_LITERAL_LONG1>",
+    "<STRING_LITERAL_LONG2>",
+    "<DIGITS>",
+    "<IRIref>",
+    "<BLANK_NODE_LABEL>",
+    "<LANGTAG>",
+    "<A2Z>",
+    "<A2ZN>",
+    "\"null\"",
+    "\"{\"",
+    "\"}\"",
+    "\"[\"",
+    "\"]\"",
+    "\",\"",
+    "\":\"",
+    "\"^^\"",
+    "\"@\"",
+    "<PN_CHARS_BASE>",
+    "<PN_CHARS_U>",
+    "<PN_CHARS>",
+    "<PN_LOCAL>",
+    "<UNKNOWN>",
+  };
+
+}
diff --git a/jena-arq/src/main/java/org/apache/jena/cdt/parser/CDTLiteralParserTokenManager.java b/jena-arq/src/main/java/org/apache/jena/cdt/parser/CDTLiteralParserTokenManager.java
new file mode 100644
index 0000000000..9f7b775c89
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/cdt/parser/CDTLiteralParserTokenManager.java
@@ -0,0 +1,1045 @@
+/* Generated By:JavaCC: Do not edit this line. CDTLiteralParserTokenManager.java */
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.cdt.parser;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.jena.cdt.*;
+import org.apache.jena.graph.Node;
+import static org.apache.jena.riot.lang.extra.LangParserLib.*;
+
+/** Token Manager. */
+public class CDTLiteralParserTokenManager implements CDTLiteralParserConstants
+{
+
+  /** Debug output. */
+  public  java.io.PrintStream debugStream = System.out;
+  /** Set debug output. */
+  public  void setDebugStream(java.io.PrintStream ds) { debugStream = ds; }
+private int jjStopAtPos(int pos, int kind)
+{
+   jjmatchedKind = kind;
+   jjmatchedPos = pos;
+   return pos + 1;
+}
+private int jjMoveStringLiteralDfa0_0()
+{
+   switch(curChar)
+   {
+      case 9:
+         jjmatchedKind = 2;
+         return jjMoveNfa_0(0, 0);
+      case 10:
+         jjmatchedKind = 3;
+         return jjMoveNfa_0(0, 0);
+      case 12:
+         jjmatchedKind = 5;
+         return jjMoveNfa_0(0, 0);
+      case 13:
+         jjmatchedKind = 4;
+         return jjMoveNfa_0(0, 0);
+      case 32:
+         jjmatchedKind = 1;
+         return jjMoveNfa_0(0, 0);
+      case 44:
+         jjmatchedKind = 31;
+         return jjMoveNfa_0(0, 0);
+      case 58:
+         jjmatchedKind = 32;
+         return jjMoveNfa_0(0, 0);
+      case 64:
+         jjmatchedKind = 34;
+         return jjMoveNfa_0(0, 0);
+      case 70:
+         return jjMoveStringLiteralDfa1_0(0x100L);
+      case 84:
+         return jjMoveStringLiteralDfa1_0(0x80L);
+      case 91:
+         jjmatchedKind = 29;
+         return jjMoveNfa_0(0, 0);
+      case 93:
+         jjmatchedKind = 30;
+         return jjMoveNfa_0(0, 0);
+      case 94:
+         return jjMoveStringLiteralDfa1_0(0x200000000L);
+      case 102:
+         return jjMoveStringLiteralDfa1_0(0x100L);
+      case 110:
+         return jjMoveStringLiteralDfa1_0(0x4000000L);
+      case 116:
+         return jjMoveStringLiteralDfa1_0(0x80L);
+      case 123:
+         jjmatchedKind = 27;
+         return jjMoveNfa_0(0, 0);
+      case 125:
+         jjmatchedKind = 28;
+         return jjMoveNfa_0(0, 0);
+      default :
+         return jjMoveNfa_0(0, 0);
+   }
+}
+private int jjMoveStringLiteralDfa1_0(long active0)
+{
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) {
+   return jjMoveNfa_0(0, 0);
+   }
+   switch(curChar)
+   {
+      case 65:
+         return jjMoveStringLiteralDfa2_0(active0, 0x100L);
+      case 82:
+         return jjMoveStringLiteralDfa2_0(active0, 0x80L);
+      case 94:
+         if ((active0 & 0x200000000L) != 0L)
+         {
+            jjmatchedKind = 33;
+            jjmatchedPos = 1;
+         }
+         break;
+      case 97:
+         return jjMoveStringLiteralDfa2_0(active0, 0x100L);
+      case 114:
+         return jjMoveStringLiteralDfa2_0(active0, 0x80L);
+      case 117:
+         return jjMoveStringLiteralDfa2_0(active0, 0x4000000L);
+      default :
+         break;
+   }
+   return jjMoveNfa_0(0, 1);
+}
+private int jjMoveStringLiteralDfa2_0(long old0, long active0)
+{
+   if (((active0 &= old0)) == 0L)
+      return jjMoveNfa_0(0, 1);
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) {
+   return jjMoveNfa_0(0, 1);
+   }
+   switch(curChar)
+   {
+      case 76:
+         return jjMoveStringLiteralDfa3_0(active0, 0x100L);
+      case 85:
+         return jjMoveStringLiteralDfa3_0(active0, 0x80L);
+      case 108:
+         return jjMoveStringLiteralDfa3_0(active0, 0x4000100L);
+      case 117:
+         return jjMoveStringLiteralDfa3_0(active0, 0x80L);
+      default :
+         break;
+   }
+   return jjMoveNfa_0(0, 2);
+}
+private int jjMoveStringLiteralDfa3_0(long old0, long active0)
+{
+   if (((active0 &= old0)) == 0L)
+      return jjMoveNfa_0(0, 2);
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) {
+   return jjMoveNfa_0(0, 2);
+   }
+   switch(curChar)
+   {
+      case 69:
+         if ((active0 & 0x80L) != 0L)
+         {
+            jjmatchedKind = 7;
+            jjmatchedPos = 3;
+         }
+         break;
+      case 83:
+         return jjMoveStringLiteralDfa4_0(active0, 0x100L);
+      case 101:
+         if ((active0 & 0x80L) != 0L)
+         {
+            jjmatchedKind = 7;
+            jjmatchedPos = 3;
+         }
+         break;
+      case 108:
+         if ((active0 & 0x4000000L) != 0L)
+         {
+            jjmatchedKind = 26;
+            jjmatchedPos = 3;
+         }
+         break;
+      case 115:
+         return jjMoveStringLiteralDfa4_0(active0, 0x100L);
+      default :
+         break;
+   }
+   return jjMoveNfa_0(0, 3);
+}
+private int jjMoveStringLiteralDfa4_0(long old0, long active0)
+{
+   if (((active0 &= old0)) == 0L)
+      return jjMoveNfa_0(0, 3);
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) {
+   return jjMoveNfa_0(0, 3);
+   }
+   switch(curChar)
+   {
+      case 69:
+         if ((active0 & 0x100L) != 0L)
+         {
+            jjmatchedKind = 8;
+            jjmatchedPos = 4;
+         }
+         break;
+      case 101:
+         if ((active0 & 0x100L) != 0L)
+         {
+            jjmatchedKind = 8;
+            jjmatchedPos = 4;
+         }
+         break;
+      default :
+         break;
+   }
+   return jjMoveNfa_0(0, 4);
+}
+static final long[] jjbitVec0 = {
+   0xfffffffffffffffeL, 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffffffffffffffL
+};
+static final long[] jjbitVec2 = {
+   0x0L, 0x0L, 0xffffffffffffffffL, 0xffffffffffffffffL
+};
+static final long[] jjbitVec3 = {
+   0xfffe7000fffffff6L, 0xffffffffffffffffL, 0xffffffffffffffffL, 0x7e00000000ffffffL
+};
+static final long[] jjbitVec4 = {
+   0x0L, 0x0L, 0x0L, 0xff7fffffff7fffffL
+};
+static final long[] jjbitVec5 = {
+   0x0L, 0xbfff000000000000L, 0xffffffffffffffffL, 0xffffffffffffffffL
+};
+static final long[] jjbitVec6 = {
+   0x3000L, 0xffff000000000000L, 0xffffffffffffffffL, 0xffffffffffffffffL
+};
+static final long[] jjbitVec7 = {
+   0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffL, 0x0L
+};
+static final long[] jjbitVec8 = {
+   0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffffffffffL
+};
+static final long[] jjbitVec9 = {
+   0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffffffffffffffL, 0x3fffffffffffffffL
+};
+static final long[] jjbitVec10 = {
+   0x0L, 0x0L, 0x80000000000000L, 0xff7fffffff7fffffL
+};
+static final long[] jjbitVec11 = {
+   0xffffffffffffffffL, 0xbfffffffffffffffL, 0xffffffffffffffffL, 0xffffffffffffffffL
+};
+static final long[] jjbitVec12 = {
+   0x8000000000003000L, 0xffff000000000001L, 0xffffffffffffffffL, 0xffffffffffffffffL
+};
+private int jjMoveNfa_0(int startState, int curPos)
+{
+   int strKind = jjmatchedKind;
+   int strPos = jjmatchedPos;
+   int seenUpto;
+   input_stream.backup(seenUpto = curPos + 1);
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) { throw new Error("Internal Error"); }
+   curPos = 0;
+   int startsAt = 0;
+   jjnewStateCnt = 74;
+   int i = 1;
+   jjstateSet[0] = startState;
+   int kind = 0x7fffffff;
+   for (;;)
+   {
+      if (++jjround == 0x7fffffff)
+         ReInitRounds();
+      if (curChar < 64)
+      {
+         long l = 1L << curChar;
+         do
+         {
+            switch(jjstateSet[--i])
+            {
+               case 0:
+                  if ((0x3ff000000000000L & l) != 0L)
+                  {
+                     if (kind > 9)
+                        kind = 9;
+                     jjCheckNAddStates(0, 7);
+                  }
+                  else if ((0x280000000000L & l) != 0L)
+                     jjCheckNAddStates(8, 12);
+                  else if (curChar == 46)
+                     jjCheckNAddTwoStates(54, 56);
+                  else if (curChar == 60)
+                     jjCheckNAddTwoStates(37, 38);
+                  else if (curChar == 34)
+                     jjstateSet[jjnewStateCnt++] = 34;
+                  else if (curChar == 39)
+                     jjstateSet[jjnewStateCnt++] = 22;
+                  if (curChar == 34)
+                     jjCheckNAddStates(13, 15);
+                  else if (curChar == 39)
+                     jjCheckNAddStates(16, 18);
+                  break;
+               case 1:
+                  if ((0x8400000000L & l) != 0L && kind > 15)
+                     kind = 15;
+                  break;
+               case 2:
+                  if (curChar == 39)
+                     jjCheckNAddStates(16, 18);
+                  break;
+               case 3:
+                  if ((0xffffff7fffffdbffL & l) != 0L)
+                     jjCheckNAddStates(16, 18);
+                  break;
+               case 5:
+                  if ((0x8400000000L & l) != 0L)
+                     jjCheckNAddStates(16, 18);
+                  break;
+               case 6:
+                  if (curChar == 39 && kind > 16)
+                     kind = 16;
+                  break;
+               case 7:
+                  if (curChar == 34)
+                     jjCheckNAddStates(13, 15);
+                  break;
+               case 8:
+                  if ((0xfffffffbffffdbffL & l) != 0L)
+                     jjCheckNAddStates(13, 15);
+                  break;
+               case 10:
+                  if ((0x8400000000L & l) != 0L)
+                     jjCheckNAddStates(13, 15);
+                  break;
+               case 11:
+                  if (curChar == 34 && kind > 17)
+                     kind = 17;
+                  break;
+               case 12:
+                  if (curChar == 39)
+                     jjCheckNAddStates(19, 22);
+                  break;
+               case 13:
+               case 17:
+                  if ((0xffffff7fffffffffL & l) != 0L)
+                     jjCheckNAddStates(19, 22);
+                  break;
+               case 15:
+                  if ((0x8400000000L & l) != 0L)
+                     jjCheckNAddStates(19, 22);
+                  break;
+               case 16:
+               case 19:
+                  if (curChar == 39)
+                     jjCheckNAdd(17);
+                  break;
+               case 18:
+                  if (curChar == 39)
+                     jjAddStates(23, 24);
+                  break;
+               case 20:
+                  if (curChar == 39 && kind > 18)
+                     kind = 18;
+                  break;
+               case 21:
+                  if (curChar == 39)
+                     jjstateSet[jjnewStateCnt++] = 20;
+                  break;
+               case 22:
+                  if (curChar == 39)
+                     jjstateSet[jjnewStateCnt++] = 12;
+                  break;
+               case 23:
+                  if (curChar == 39)
+                     jjstateSet[jjnewStateCnt++] = 22;
+                  break;
+               case 24:
+                  if (curChar == 34)
+                     jjCheckNAddStates(25, 28);
+                  break;
+               case 25:
+               case 29:
+                  if ((0xfffffffbffffffffL & l) != 0L)
+                     jjCheckNAddStates(25, 28);
+                  break;
+               case 27:
+                  if ((0x8400000000L & l) != 0L)
+                     jjCheckNAddStates(25, 28);
+                  break;
+               case 28:
+               case 31:
+                  if (curChar == 34)
+                     jjCheckNAdd(29);
+                  break;
+               case 30:
+                  if (curChar == 34)
+                     jjAddStates(29, 30);
+                  break;
+               case 32:
+                  if (curChar == 34 && kind > 19)
+                     kind = 19;
+                  break;
+               case 33:
+                  if (curChar == 34)
+                     jjstateSet[jjnewStateCnt++] = 32;
+                  break;
+               case 34:
+                  if (curChar == 34)
+                     jjstateSet[jjnewStateCnt++] = 24;
+                  break;
+               case 35:
+                  if (curChar == 34)
+                     jjstateSet[jjnewStateCnt++] = 34;
+                  break;
+               case 36:
+                  if (curChar == 60)
+                     jjCheckNAddTwoStates(37, 38);
+                  break;
+               case 37:
+                  if ((0xaffffffa00000000L & l) != 0L)
+                     jjCheckNAddTwoStates(37, 38);
+                  break;
+               case 38:
+                  if (curChar == 62 && kind > 21)
+                     kind = 21;
+                  break;
+               case 39:
+                  if (curChar == 58)
+                     jjstateSet[jjnewStateCnt++] = 40;
+                  break;
+               case 40:
+                  if ((0x3ff000000000000L & l) == 0L)
+                     break;
+                  if (kind > 22)
+                     kind = 22;
+                  jjCheckNAddTwoStates(41, 42);
+                  break;
+               case 41:
+                  if ((0x3ff600000000000L & l) != 0L)
+                     jjCheckNAddTwoStates(41, 42);
+                  break;
+               case 42:
+                  if ((0x3ff200000000000L & l) != 0L && kind > 22)
+                     kind = 22;
+                  break;
+               case 46:
+                  if (curChar == 45)
+                     jjCheckNAdd(47);
+                  break;
+               case 47:
+                  if ((0x3ff000000000000L & l) == 0L)
+                     break;
+                  if (kind > 23)
+                     kind = 23;
+                  jjCheckNAddTwoStates(46, 47);
+                  break;
+               case 48:
+                  if ((0x280000000000L & l) != 0L)
+                     jjCheckNAddStates(8, 12);
+                  break;
+               case 49:
+                  if ((0x3ff000000000000L & l) == 0L)
+                     break;
+                  if (kind > 9)
+                     kind = 9;
+                  jjCheckNAdd(49);
+                  break;
+               case 50:
+                  if ((0x3ff000000000000L & l) != 0L)
+                     jjCheckNAddTwoStates(50, 51);
+                  break;
+               case 51:
+                  if (curChar != 46)
+                     break;
+                  if (kind > 10)
+                     kind = 10;
+                  jjCheckNAdd(52);
+                  break;
+               case 52:
+                  if ((0x3ff000000000000L & l) == 0L)
+                     break;
+                  if (kind > 10)
+                     kind = 10;
+                  jjCheckNAdd(52);
+                  break;
+               case 53:
+                  if (curChar == 46)
+                     jjCheckNAdd(54);
+                  break;
+               case 54:
+                  if ((0x3ff000000000000L & l) == 0L)
+                     break;
+                  if (kind > 10)
+                     kind = 10;
+                  jjCheckNAdd(54);
+                  break;
+               case 55:
+                  if (curChar == 46)
+                     jjCheckNAdd(56);
+                  break;
+               case 56:
+                  if ((0x3ff000000000000L & l) != 0L)
+                     jjCheckNAddTwoStates(56, 57);
+                  break;
+               case 58:
+                  if ((0x280000000000L & l) != 0L)
+                     jjCheckNAdd(59);
+                  break;
+               case 59:
+                  if ((0x3ff000000000000L & l) == 0L)
+                     break;
+                  if (kind > 11)
+                     kind = 11;
+                  jjCheckNAdd(59);
+                  break;
+               case 60:
+                  if ((0x3ff000000000000L & l) != 0L)
+                     jjCheckNAddStates(31, 34);
+                  break;
+               case 61:
+                  if ((0x3ff000000000000L & l) != 0L)
+                     jjCheckNAddTwoStates(61, 62);
+                  break;
+               case 62:
+                  if (curChar == 46)
+                     jjCheckNAddTwoStates(63, 64);
+                  break;
+               case 63:
+                  if ((0x3ff000000000000L & l) != 0L)
+                     jjCheckNAddTwoStates(63, 64);
+                  break;
+               case 65:
+                  if ((0x280000000000L & l) != 0L)
+                     jjCheckNAdd(66);
+                  break;
+               case 66:
+                  if ((0x3ff000000000000L & l) == 0L)
+                     break;
+                  if (kind > 11)
+                     kind = 11;
+                  jjCheckNAdd(66);
+                  break;
+               case 67:
+                  if ((0x3ff000000000000L & l) != 0L)
+                     jjCheckNAddTwoStates(67, 68);
+                  break;
+               case 69:
+                  if ((0x280000000000L & l) != 0L)
+                     jjCheckNAdd(70);
+                  break;
+               case 70:
+                  if ((0x3ff000000000000L & l) == 0L)
+                     break;
+                  if (kind > 11)
+                     kind = 11;
+                  jjCheckNAdd(70);
+                  break;
+               case 71:
+                  if ((0x3ff000000000000L & l) == 0L)
+                     break;
+                  if (kind > 9)
+                     kind = 9;
+                  jjCheckNAddStates(0, 7);
+                  break;
+               case 72:
+                  if ((0x3ff000000000000L & l) == 0L)
+                     break;
+                  if (kind > 20)
+                     kind = 20;
+                  jjCheckNAdd(72);
+                  break;
+               case 73:
+                  if (curChar == 46)
+                     jjCheckNAddTwoStates(54, 56);
+                  break;
+               default : break;
+            }
+         } while(i != startsAt);
+      }
+      else if (curChar < 128)
+      {
+         long l = 1L << (curChar & 077);
+         do
+         {
+            switch(jjstateSet[--i])
+            {
+               case 0:
+                  if (curChar == 64)
+                     jjCheckNAdd(45);
+                  else if (curChar == 95)
+                     jjstateSet[jjnewStateCnt++] = 39;
+                  else if (curChar == 92)
+                     jjstateSet[jjnewStateCnt++] = 1;
+                  break;
+               case 1:
+                  if ((0x14404410144044L & l) != 0L && kind > 15)
+                     kind = 15;
+                  break;
+               case 3:
+                  if ((0xffffffffefffffffL & l) != 0L)
+                     jjCheckNAddStates(16, 18);
+                  break;
+               case 4:
+                  if (curChar == 92)
+                     jjstateSet[jjnewStateCnt++] = 5;
+                  break;
+               case 5:
+                  if ((0x14404410144044L & l) != 0L)
+                     jjCheckNAddStates(16, 18);
+                  break;
+               case 8:
+                  if ((0xffffffffefffffffL & l) != 0L)
+                     jjCheckNAddStates(13, 15);
+                  break;
+               case 9:
+                  if (curChar == 92)
+                     jjstateSet[jjnewStateCnt++] = 10;
+                  break;
+               case 10:
+                  if ((0x14404410144044L & l) != 0L)
+                     jjCheckNAddStates(13, 15);
+                  break;
+               case 13:
+                  if ((0xffffffffefffffffL & l) != 0L)
+                     jjCheckNAddStates(19, 22);
+                  break;
+               case 14:
+                  if (curChar == 92)
+                     jjstateSet[jjnewStateCnt++] = 15;
+                  break;
+               case 15:
+                  if ((0x14404410144044L & l) != 0L)
+                     jjCheckNAddStates(19, 22);
+                  break;
+               case 17:
+                  jjCheckNAddStates(19, 22);
+                  break;
+               case 25:
+                  if ((0xffffffffefffffffL & l) != 0L)
+                     jjCheckNAddStates(25, 28);
+                  break;
+               case 26:
+                  if (curChar == 92)
+                     jjstateSet[jjnewStateCnt++] = 27;
+                  break;
+               case 27:
+                  if ((0x14404410144044L & l) != 0L)
+                     jjCheckNAddStates(25, 28);
+                  break;
+               case 29:
+                  jjCheckNAddStates(25, 28);
+                  break;
+               case 37:
+                  if ((0xc7fffffeafffffffL & l) != 0L)
+                     jjAddStates(35, 36);
+                  break;
+               case 40:
+                  if ((0x7fffffe87fffffeL & l) == 0L)
+                     break;
+                  if (kind > 22)
+                     kind = 22;
+                  jjCheckNAddTwoStates(41, 42);
+                  break;
+               case 41:
+                  if ((0x7fffffe87fffffeL & l) != 0L)
+                     jjCheckNAddTwoStates(41, 42);
+                  break;
+               case 42:
+                  if ((0x7fffffe87fffffeL & l) != 0L && kind > 22)
+                     kind = 22;
+                  break;
+               case 43:
+                  if (curChar == 95)
+                     jjstateSet[jjnewStateCnt++] = 39;
+                  break;
+               case 44:
+                  if (curChar == 64)
+                     jjCheckNAdd(45);
+                  break;
+               case 45:
+                  if ((0x7fffffe07fffffeL & l) == 0L)
+                     break;
+                  if (kind > 23)
+                     kind = 23;
+                  jjCheckNAddTwoStates(45, 46);
+                  break;
+               case 47:
+                  if ((0x7fffffe07fffffeL & l) == 0L)
+                     break;
+                  if (kind > 23)
+                     kind = 23;
+                  jjCheckNAddTwoStates(46, 47);
+                  break;
+               case 57:
+                  if ((0x2000000020L & l) != 0L)
+                     jjAddStates(37, 38);
+                  break;
+               case 64:
+                  if ((0x2000000020L & l) != 0L)
+                     jjAddStates(39, 40);
+                  break;
+               case 68:
+                  if ((0x2000000020L & l) != 0L)
+                     jjAddStates(41, 42);
+                  break;
+               default : break;
+            }
+         } while(i != startsAt);
+      }
+      else
+      {
+         int hiByte = (int)(curChar >> 8);
+         int i1 = hiByte >> 6;
+         long l1 = 1L << (hiByte & 077);
+         int i2 = (curChar & 0xff) >> 6;
+         long l2 = 1L << (curChar & 077);
+         do
+         {
+            switch(jjstateSet[--i])
+            {
+               case 3:
+                  if (jjCanMove_0(hiByte, i1, i2, l1, l2))
+                     jjAddStates(16, 18);
+                  break;
+               case 8:
+                  if (jjCanMove_0(hiByte, i1, i2, l1, l2))
+                     jjAddStates(13, 15);
+                  break;
+               case 13:
+               case 17:
+                  if (jjCanMove_0(hiByte, i1, i2, l1, l2))
+                     jjCheckNAddStates(19, 22);
+                  break;
+               case 25:
+               case 29:
+                  if (jjCanMove_0(hiByte, i1, i2, l1, l2))
+                     jjCheckNAddStates(25, 28);
+                  break;
+               case 37:
+                  if (jjCanMove_0(hiByte, i1, i2, l1, l2))
+                     jjAddStates(35, 36);
+                  break;
+               case 40:
+                  if (!jjCanMove_1(hiByte, i1, i2, l1, l2))
+                     break;
+                  if (kind > 22)
+                     kind = 22;
+                  jjCheckNAddTwoStates(41, 42);
+                  break;
+               case 41:
+                  if (jjCanMove_2(hiByte, i1, i2, l1, l2))
+                     jjCheckNAddTwoStates(41, 42);
+                  break;
+               case 42:
+                  if (jjCanMove_2(hiByte, i1, i2, l1, l2) && kind > 22)
+                     kind = 22;
+                  break;
+               default : break;
+            }
+         } while(i != startsAt);
+      }
+      if (kind != 0x7fffffff)
+      {
+         jjmatchedKind = kind;
+         jjmatchedPos = curPos;
+         kind = 0x7fffffff;
+      }
+      ++curPos;
+      if ((i = jjnewStateCnt) == (startsAt = 74 - (jjnewStateCnt = startsAt)))
+         break;
+      try { curChar = input_stream.readChar(); }
+      catch(java.io.IOException e) { break; }
+   }
+   if (jjmatchedPos > strPos)
+      return curPos;
+
+   int toRet = Math.max(curPos, seenUpto);
+
+   if (curPos < toRet)
+      for (i = toRet - Math.min(curPos, seenUpto); i-- > 0; )
+         try { curChar = input_stream.readChar(); }
+         catch(java.io.IOException e) { throw new Error("Internal Error : Please send a bug report."); }
+
+   if (jjmatchedPos < strPos)
+   {
+      jjmatchedKind = strKind;
+      jjmatchedPos = strPos;
+   }
+   else if (jjmatchedPos == strPos && jjmatchedKind > strKind)
+      jjmatchedKind = strKind;
+
+   return toRet;
+}
+static final int[] jjnextStates = {
+   49, 50, 51, 61, 62, 67, 68, 72, 49, 50, 53, 55, 60, 8, 9, 11, 
+   3, 4, 6, 13, 14, 16, 18, 19, 21, 25, 26, 28, 30, 31, 33, 61, 
+   62, 67, 68, 37, 38, 58, 59, 65, 66, 69, 70, 
+};
+private static final boolean jjCanMove_0(int hiByte, int i1, int i2, long l1, long l2)
+{
+   switch(hiByte)
+   {
+      case 0:
+         return ((jjbitVec2[i2] & l2) != 0L);
+      default :
+         if ((jjbitVec0[i1] & l1) != 0L)
+            return true;
+         return false;
+   }
+}
+private static final boolean jjCanMove_1(int hiByte, int i1, int i2, long l1, long l2)
+{
+   switch(hiByte)
+   {
+      case 0:
+         return ((jjbitVec4[i2] & l2) != 0L);
+      case 3:
+         return ((jjbitVec5[i2] & l2) != 0L);
+      case 32:
+         return ((jjbitVec6[i2] & l2) != 0L);
+      case 33:
+         return ((jjbitVec7[i2] & l2) != 0L);
+      case 47:
+         return ((jjbitVec8[i2] & l2) != 0L);
+      case 48:
+         return ((jjbitVec0[i2] & l2) != 0L);
+      case 255:
+         return ((jjbitVec9[i2] & l2) != 0L);
+      default :
+         if ((jjbitVec3[i1] & l1) != 0L)
+            return true;
+         return false;
+   }
+}
+private static final boolean jjCanMove_2(int hiByte, int i1, int i2, long l1, long l2)
+{
+   switch(hiByte)
+   {
+      case 0:
+         return ((jjbitVec10[i2] & l2) != 0L);
+      case 3:
+         return ((jjbitVec11[i2] & l2) != 0L);
+      case 32:
+         return ((jjbitVec12[i2] & l2) != 0L);
+      case 33:
+         return ((jjbitVec7[i2] & l2) != 0L);
+      case 47:
+         return ((jjbitVec8[i2] & l2) != 0L);
+      case 48:
+         return ((jjbitVec0[i2] & l2) != 0L);
+      case 255:
+         return ((jjbitVec9[i2] & l2) != 0L);
+      default :
+         if ((jjbitVec3[i1] & l1) != 0L)
+            return true;
+         return false;
+   }
+}
+
+/** Token literal values. */
+public static final String[] jjstrLiteralImages = {
+"", null, null, null, null, null, null, null, null, null, null, null, null, 
+null, null, null, null, null, null, null, null, null, null, null, null, null, 
+"\156\165\154\154", "\173", "\175", "\133", "\135", "\54", "\72", "\136\136", "\100", null, null, 
+null, null, null, };
+
+/** Lexer state names. */
+public static final String[] lexStateNames = {
+   "DEFAULT",
+};
+static final long[] jjtoToken = {
+   0x7fcff8f81L, 
+};
+static final long[] jjtoSkip = {
+   0x3eL, 
+};
+protected JavaCharStream input_stream;
+private final int[] jjrounds = new int[74];
+private final int[] jjstateSet = new int[148];
+protected char curChar;
+/** Constructor. */
+public CDTLiteralParserTokenManager(JavaCharStream stream){
+   if (JavaCharStream.staticFlag)
+      throw new Error("ERROR: Cannot use a static CharStream class with a non-static lexical analyzer.");
+   input_stream = stream;
+}
+
+/** Constructor. */
+public CDTLiteralParserTokenManager(JavaCharStream stream, int lexState){
+   this(stream);
+   SwitchTo(lexState);
+}
+
+/** Reinitialise parser. */
+public void ReInit(JavaCharStream stream)
+{
+   jjmatchedPos = jjnewStateCnt = 0;
+   curLexState = defaultLexState;
+   input_stream = stream;
+   ReInitRounds();
+}
+private void ReInitRounds()
+{
+   int i;
+   jjround = 0x80000001;
+   for (i = 74; i-- > 0;)
+      jjrounds[i] = 0x80000000;
+}
+
+/** Reinitialise parser. */
+public void ReInit(JavaCharStream stream, int lexState)
+{
+   ReInit(stream);
+   SwitchTo(lexState);
+}
+
+/** Switch to specified lex state. */
+public void SwitchTo(int lexState)
+{
+   if (lexState >= 1 || lexState < 0)
+      throw new TokenMgrError("Error: Ignoring invalid lexical state : " + lexState + ". State unchanged.", TokenMgrError.INVALID_LEXICAL_STATE);
+   else
+      curLexState = lexState;
+}
+
+protected Token jjFillToken()
+{
+   final Token t;
+   final String curTokenImage;
+   final int beginLine;
+   final int endLine;
+   final int beginColumn;
+   final int endColumn;
+   String im = jjstrLiteralImages[jjmatchedKind];
+   curTokenImage = (im == null) ? input_stream.GetImage() : im;
+   beginLine = input_stream.getBeginLine();
+   beginColumn = input_stream.getBeginColumn();
+   endLine = input_stream.getEndLine();
+   endColumn = input_stream.getEndColumn();
+   t = Token.newToken(jjmatchedKind, curTokenImage);
+
+   t.beginLine = beginLine;
+   t.endLine = endLine;
+   t.beginColumn = beginColumn;
+   t.endColumn = endColumn;
+
+   return t;
+}
+
+int curLexState = 0;
+int defaultLexState = 0;
+int jjnewStateCnt;
+int jjround;
+int jjmatchedPos;
+int jjmatchedKind;
+
+/** Get the next Token. */
+public Token getNextToken() 
+{
+  Token matchedToken;
+  int curPos = 0;
+
+  EOFLoop :
+  for (;;)
+  {
+   try
+   {
+      curChar = input_stream.BeginToken();
+   }
+   catch(java.io.IOException e)
+   {
+      jjmatchedKind = 0;
+      matchedToken = jjFillToken();
+      return matchedToken;
+   }
+
+   jjmatchedKind = 0x7fffffff;
+   jjmatchedPos = 0;
+   curPos = jjMoveStringLiteralDfa0_0();
+   if (jjmatchedKind != 0x7fffffff)
+   {
+      if (jjmatchedPos + 1 < curPos)
+         input_stream.backup(curPos - jjmatchedPos - 1);
+      if ((jjtoToken[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L)
+      {
+         matchedToken = jjFillToken();
+         return matchedToken;
+      }
+      else
+      {
+         continue EOFLoop;
+      }
+   }
+   int error_line = input_stream.getEndLine();
+   int error_column = input_stream.getEndColumn();
+   String error_after = null;
+   boolean EOFSeen = false;
+   try { input_stream.readChar(); input_stream.backup(1); }
+   catch (java.io.IOException e1) {
+      EOFSeen = true;
+      error_after = curPos <= 1 ? "" : input_stream.GetImage();
+      if (curChar == '\n' || curChar == '\r') {
+         error_line++;
+         error_column = 0;
+      }
+      else
+         error_column++;
+   }
+   if (!EOFSeen) {
+      input_stream.backup(1);
+      error_after = curPos <= 1 ? "" : input_stream.GetImage();
+   }
+   throw new TokenMgrError(EOFSeen, curLexState, error_line, error_column, error_after, curChar, TokenMgrError.LEXICAL_ERROR);
+  }
+}
+
+private void jjCheckNAdd(int state)
+{
+   if (jjrounds[state] != jjround)
+   {
+      jjstateSet[jjnewStateCnt++] = state;
+      jjrounds[state] = jjround;
+   }
+}
+private void jjAddStates(int start, int end)
+{
+   do {
+      jjstateSet[jjnewStateCnt++] = jjnextStates[start];
+   } while (start++ != end);
+}
+private void jjCheckNAddTwoStates(int state1, int state2)
+{
+   jjCheckNAdd(state1);
+   jjCheckNAdd(state2);
+}
+
+private void jjCheckNAddStates(int start, int end)
+{
+   do {
+      jjCheckNAdd(jjnextStates[start]);
+   } while (start++ != end);
+}
+
+}
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/lang/sparql_11/JavaCharStream.java b/jena-arq/src/main/java/org/apache/jena/cdt/parser/JavaCharStream.java
similarity index 75%
copy from jena-arq/src/main/java/org/apache/jena/sparql/lang/sparql_11/JavaCharStream.java
copy to jena-arq/src/main/java/org/apache/jena/cdt/parser/JavaCharStream.java
index 073cf097d1..0db07c051d 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/lang/sparql_11/JavaCharStream.java
+++ b/jena-arq/src/main/java/org/apache/jena/cdt/parser/JavaCharStream.java
@@ -1,6 +1,24 @@
-/* Generated By:JavaCC: Do not edit this line. JavaCharStream.java Version 7.0 */
+/* Generated By:JavaCC: Do not edit this line. JavaCharStream.java Version 5.0 */
 /* JavaCCOptions:STATIC=false,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
-package org.apache.jena.sparql.lang.sparql_11 ;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.cdt.parser;
 
 /**
  * An implementation of interface CharStream, where the stream is assumed to
@@ -13,7 +31,7 @@ public
 class JavaCharStream
 {
   /** Whether parser is static. */
-
+  
 @SuppressWarnings("all")
 public static final boolean staticFlag = false;
 
@@ -64,8 +82,8 @@ public static final boolean staticFlag = false;
     throw new java.io.IOException(); // Should never come here
   }
 
-/* Position in buffer. */
-
+/** Position in buffer. */
+  
 @SuppressWarnings("all")
 public int bufpos = -1;
   int bufsize;
@@ -87,15 +105,10 @@ public int bufpos = -1;
   protected int maxNextCharInd = 0;
   protected int nextCharInd = -1;
   protected int inBuf = 0;
-  protected int tabSize = 1;
-  protected boolean trackLineColumn = true;
+  protected int tabSize = 8;
 
-
-@SuppressWarnings("all")
-public void setTabSize(int i) { tabSize = i; }
-
-@SuppressWarnings("all")
-public int getTabSize() { return tabSize; }
+  protected void setTabSize(int i) { tabSize = i; }
+  protected int getTabSize(int i) { return tabSize; }
 
   protected void ExpandBuff(boolean wrapAround)
   {
@@ -184,8 +197,8 @@ public int getTabSize() { return tabSize; }
     return nextCharBuf[nextCharInd];
   }
 
-/* @return starting character for token. */
-
+/** @return starting character for token. */
+  
 @SuppressWarnings("all")
 public char BeginToken() throws java.io.IOException
   {
@@ -266,8 +279,8 @@ public char BeginToken() throws java.io.IOException
     bufcolumn[bufpos] = column;
   }
 
-/* Read a character. */
-
+/** Read a character. */
+  
 @SuppressWarnings("all")
 public char readChar() throws java.io.IOException
   {
@@ -288,7 +301,7 @@ public char readChar() throws java.io.IOException
 
     if ((buffer[bufpos] = c = ReadByte()) == '\\')
     {
-      if (trackLineColumn) { UpdateLineColumn(c); }
+      UpdateLineColumn(c);
 
       int backSlashCnt = 1;
 
@@ -301,7 +314,7 @@ public char readChar() throws java.io.IOException
         {
           if ((buffer[bufpos] = c = ReadByte()) != '\\')
           {
-            if (trackLineColumn) { UpdateLineColumn(c); }
+            UpdateLineColumn(c);
             // found a non-backslash char.
             if ((c == 'u') && ((backSlashCnt & 1) == 1))
             {
@@ -324,7 +337,7 @@ public char readChar() throws java.io.IOException
           return '\\';
         }
 
-        if (trackLineColumn) { UpdateLineColumn(c); }
+        UpdateLineColumn(c);
         backSlashCnt++;
       }
 
@@ -362,64 +375,58 @@ public char readChar() throws java.io.IOException
     }
   }
 
-  /*
+  @Deprecated
+  /**
    * @deprecated
    * @see #getEndColumn
    */
-  @Deprecated
-
+  
 @SuppressWarnings("all")
 public int getColumn() {
     return bufcolumn[bufpos];
   }
 
-  /*
+  @Deprecated
+  /**
    * @deprecated
    * @see #getEndLine
-   * @return the line number.
    */
-  @Deprecated
-
+  
 @SuppressWarnings("all")
 public int getLine() {
     return bufline[bufpos];
   }
 
-/** Get end column.
- * @return the end column or -1
- */
-
+/** Get end column. */
+  
 @SuppressWarnings("all")
 public int getEndColumn() {
     return bufcolumn[bufpos];
   }
 
-/** Get end line.
- * @return the end line number or -1
- */
-
+/** Get end line. */
+  
 @SuppressWarnings("all")
 public int getEndLine() {
     return bufline[bufpos];
   }
 
-/** Get the beginning column.
- * @return column of token start */
-
+/** @return column of token start */
+  
 @SuppressWarnings("all")
 public int getBeginColumn() {
     return bufcolumn[tokenBegin];
   }
 
 /** @return line number of token start */
-
+  
 @SuppressWarnings("all")
 public int getBeginLine() {
     return bufline[tokenBegin];
   }
 
 /** Retreat. */
-
+  
 @SuppressWarnings("all")
 public void backup(int amount) {
 
@@ -428,13 +435,8 @@ public void backup(int amount) {
       bufpos += bufsize;
   }
 
-/** Constructor.
- * @param dstream the underlying data source.
- * @param startline line number of the first character of the stream, mostly for error messages.
- * @param startcolumn column number of the first character of the stream.
- * @param buffersize size of the buffer
- */
-
+/** Constructor. */
+  
 @SuppressWarnings("all")
 public JavaCharStream(java.io.Reader dstream,
                  int startline, int startcolumn, int buffersize)
@@ -450,12 +452,8 @@ public JavaCharStream(java.io.Reader dstream,
     nextCharBuf = new char[4096];
   }
 
-/** Constructor.
- * @param dstream the underlying data source.
- * @param startline line number of the first character of the stream, mostly for error messages.
- * @param startcolumn column number of the first character of the stream.
- */
-
+/** Constructor. */
+  
 @SuppressWarnings("all")
 public JavaCharStream(java.io.Reader dstream,
                                         int startline, int startcolumn)
@@ -463,17 +461,15 @@ public JavaCharStream(java.io.Reader dstream,
     this(dstream, startline, startcolumn, 4096);
   }
 
-/** Constructor.
- * @param dstream the underlying data source.
- */
-
+/** Constructor. */
+  
 @SuppressWarnings("all")
 public JavaCharStream(java.io.Reader dstream)
   {
     this(dstream, 1, 1, 4096);
   }
-/* Reinitialise. */
-
+/** Reinitialise. */
+  
 @SuppressWarnings("all")
 public void ReInit(java.io.Reader dstream,
                  int startline, int startcolumn, int buffersize)
@@ -495,8 +491,8 @@ public void ReInit(java.io.Reader dstream,
     nextCharInd = bufpos = -1;
   }
 
-/* Reinitialise. */
-
+/** Reinitialise. */
+  
 @SuppressWarnings("all")
 public void ReInit(java.io.Reader dstream,
                                         int startline, int startcolumn)
@@ -504,15 +500,15 @@ public void ReInit(java.io.Reader dstream,
     ReInit(dstream, startline, startcolumn, 4096);
   }
 
-/* Reinitialise. */
-
+/** Reinitialise. */
+  
 @SuppressWarnings("all")
 public void ReInit(java.io.Reader dstream)
   {
     ReInit(dstream, 1, 1, 4096);
   }
 /** Constructor. */
-
+  
 @SuppressWarnings("all")
 public JavaCharStream(java.io.InputStream dstream, String encoding, int startline,
   int startcolumn, int buffersize) throws java.io.UnsupportedEncodingException
@@ -520,28 +516,17 @@ public JavaCharStream(java.io.InputStream dstream, String encoding, int startlin
     this(encoding == null ? new java.io.InputStreamReader(dstream) : new java.io.InputStreamReader(dstream, encoding), startline, startcolumn, buffersize);
   }
 
-/** Constructor.
- * @param dstream the underlying data source.
- * @param startline line number of the first character of the stream, mostly for error messages.
- * @param startcolumn column number of the first character of the stream.
- * @param buffersize size of the buffer
- */
-
+/** Constructor. */
+  
 @SuppressWarnings("all")
 public JavaCharStream(java.io.InputStream dstream, int startline,
   int startcolumn, int buffersize)
   {
-    this(new java.io.InputStreamReader(dstream), startline, startcolumn, buffersize);
+    this(new java.io.InputStreamReader(dstream), startline, startcolumn, 4096);
   }
 
-/** Constructor.
- * @param dstream the underlying data source.
- * @param encoding the character encoding of the data stream.
- * @param startline line number of the first character of the stream, mostly for error messages.
- * @param startcolumn column number of the first character of the stream.
- * @throws java.io.UnsupportedEncodingException encoding is invalid or unsupported.
- */
-
+/** Constructor. */
+  
 @SuppressWarnings("all")
 public JavaCharStream(java.io.InputStream dstream, String encoding, int startline,
                         int startcolumn) throws java.io.UnsupportedEncodingException
@@ -549,12 +534,8 @@ public JavaCharStream(java.io.InputStream dstream, String encoding, int startlin
     this(dstream, encoding, startline, startcolumn, 4096);
   }
 
-/** Constructor.
- * @param dstream the underlying data source.
- * @param startline line number of the first character of the stream, mostly for error messages.
- * @param startcolumn column number of the first character of the stream.
- */
-
+/** Constructor. */
+  
 @SuppressWarnings("all")
 public JavaCharStream(java.io.InputStream dstream, int startline,
                         int startcolumn)
@@ -562,36 +543,24 @@ public JavaCharStream(java.io.InputStream dstream, int startline,
     this(dstream, startline, startcolumn, 4096);
   }
 
-/** Constructor.
- * @param dstream the underlying data source.
- * @param encoding the character encoding of the data stream.
- * @throws java.io.UnsupportedEncodingException encoding is invalid or unsupported.
- */
-
+/** Constructor. */
+  
 @SuppressWarnings("all")
 public JavaCharStream(java.io.InputStream dstream, String encoding) throws java.io.UnsupportedEncodingException
   {
     this(dstream, encoding, 1, 1, 4096);
   }
 
-  /** Constructor.
-   * @param dstream the underlying data source.
-   */
-
+/** Constructor. */
+  
 @SuppressWarnings("all")
 public JavaCharStream(java.io.InputStream dstream)
   {
     this(dstream, 1, 1, 4096);
   }
 
-/** Reinitialise.
- * @param dstream the underlying data source.
- * @param encoding the character encoding of the data stream.
- * @param startline line number of the first character of the stream, mostly for error messages.
- * @param startcolumn column number of the first character of the stream.
- * @param buffersize size of the buffer
- */
-
+/** Reinitialise. */
+  
 @SuppressWarnings("all")
 public void ReInit(java.io.InputStream dstream, String encoding, int startline,
   int startcolumn, int buffersize) throws java.io.UnsupportedEncodingException
@@ -599,70 +568,48 @@ public void ReInit(java.io.InputStream dstream, String encoding, int startline,
     ReInit(encoding == null ? new java.io.InputStreamReader(dstream) : new java.io.InputStreamReader(dstream, encoding), startline, startcolumn, buffersize);
   }
 
-/** Reinitialise.
- * @param dstream the underlying data source.
- * @param startline line number of the first character of the stream, mostly for error messages.
- * @param startcolumn column number of the first character of the stream.
- * @param buffersize size of the buffer
- */
-
+/** Reinitialise. */
+  
 @SuppressWarnings("all")
 public void ReInit(java.io.InputStream dstream, int startline,
   int startcolumn, int buffersize)
   {
     ReInit(new java.io.InputStreamReader(dstream), startline, startcolumn, buffersize);
   }
-/** Reinitialise.
- * @param dstream the underlying data source.
- * @param encoding the character encoding of the data stream.
- * @param startline line number of the first character of the stream, mostly for error messages.
- * @param startcolumn column number of the first character of the stream.
- * @throws java.io.UnsupportedEncodingException encoding is invalid or unsupported.
- */
-
+/** Reinitialise. */
+  
 @SuppressWarnings("all")
 public void ReInit(java.io.InputStream dstream, String encoding, int startline,
                      int startcolumn) throws java.io.UnsupportedEncodingException
   {
     ReInit(dstream, encoding, startline, startcolumn, 4096);
   }
-/** Reinitialise.
- * @param dstream the underlying data source.
- * @param startline line number of the first character of the stream, mostly for error messages.
- * @param startcolumn column number of the first character of the stream.
- */
-
+/** Reinitialise. */
+  
 @SuppressWarnings("all")
 public void ReInit(java.io.InputStream dstream, int startline,
                      int startcolumn)
   {
     ReInit(dstream, startline, startcolumn, 4096);
   }
-/** Reinitialise.
- * @param dstream the underlying data source.
- * @param encoding the character encoding of the data stream.
- * @throws java.io.UnsupportedEncodingException encoding is invalid or unsupported.
- */
-
+/** Reinitialise. */
+  
 @SuppressWarnings("all")
 public void ReInit(java.io.InputStream dstream, String encoding) throws java.io.UnsupportedEncodingException
   {
     ReInit(dstream, encoding, 1, 1, 4096);
   }
 
-/** Reinitialise.
- * @param dstream the underlying data source.
- */
-
+/** Reinitialise. */
+  
 @SuppressWarnings("all")
 public void ReInit(java.io.InputStream dstream)
   {
     ReInit(dstream, 1, 1, 4096);
   }
 
-  /** Get the token timage.
-   * @return token image as String */
-
+  /** @return token image as String */
+  
 @SuppressWarnings("all")
 public String GetImage()
   {
@@ -673,10 +620,8 @@ public String GetImage()
                               new String(buffer, 0, bufpos + 1);
   }
 
-  /** Get the suffix as an array of characters.
-   * @param len the length of the array to return.
-   * @return suffix */
-
+  /** @return suffix */
+  
 @SuppressWarnings("all")
 public char[] GetSuffix(int len)
   {
@@ -695,7 +640,7 @@ public char[] GetSuffix(int len)
   }
 
   /** Set buffers back to null when finished. */
-
+  
 @SuppressWarnings("all")
 public void Done()
   {
@@ -707,11 +652,8 @@ public void Done()
 
   /**
    * Method to adjust line and column numbers for the start of a token.
-   *
-   * @param newLine the new line number.
-   * @param newCol the new column number.
    */
-
+  
 @SuppressWarnings("all")
 public void adjustBeginLineColumn(int newLine, int newCol)
   {
@@ -756,8 +698,6 @@ public void adjustBeginLineColumn(int newLine, int newCol)
     line = bufline[j];
     column = bufcolumn[j];
   }
-  boolean getTrackLineColumn() { return trackLineColumn; }
-  void setTrackLineColumn(boolean tlc) { trackLineColumn = tlc; }
 
 }
-/* JavaCC - OriginalChecksum=9698f3eb5988f4ff314d1b8207763eb1 (do not edit this line) */
+/* JavaCC - OriginalChecksum=2ebc561fdb294979325bb97dfcdf1b61 (do not edit this line) */
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/lang/arq/ParseException.java b/jena-arq/src/main/java/org/apache/jena/cdt/parser/ParseException.java
similarity index 76%
copy from jena-arq/src/main/java/org/apache/jena/sparql/lang/arq/ParseException.java
copy to jena-arq/src/main/java/org/apache/jena/cdt/parser/ParseException.java
index cb1919325b..9fff7e2c63 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/lang/arq/ParseException.java
+++ b/jena-arq/src/main/java/org/apache/jena/cdt/parser/ParseException.java
@@ -1,6 +1,24 @@
-/* Generated By:JavaCC: Do not edit this line. ParseException.java Version 7.0 */
-/* JavaCCOptions:KEEP_LINE_COLUMN=true */
-package org.apache.jena.sparql.lang.arq ;
+/* Generated By:JavaCC: Do not edit this line. ParseException.java Version 5.0 */
+/* JavaCCOptions:KEEP_LINE_COL=null */
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.cdt.parser;
 
 /**
  * This exception is thrown when parse errors are encountered.
@@ -20,11 +38,6 @@ public class ParseException extends Exception {
    */
   private static final long serialVersionUID = 1L;
 
-  /**
-   * The end of line string for this machine.
-   */
-  protected static String EOL = System.getProperty("line.separator", "\n");
-
   /**
    * This constructor is used by the method "generateParseException"
    * in the generated parser.  Calling this constructor generates
@@ -65,7 +78,7 @@ public class ParseException extends Exception {
   /**
    * This is the last token that has been consumed successfully.  If
    * this object has been created due to a parse error, the token
-   * following this token will (therefore) be the first error token.
+   * followng this token will (therefore) be the first error token.
    */
   public Token currentToken;
 
@@ -93,8 +106,8 @@ public class ParseException extends Exception {
   private static String initialise(Token currentToken,
                            int[][] expectedTokenSequences,
                            String[] tokenImage) {
-
-    StringBuilder expected = new StringBuilder();
+    String eol = System.getProperty("line.separator", "\n");
+    StringBuffer expected = new StringBuffer();
     int maxSize = 0;
     for (int i = 0; i < expectedTokenSequences.length; i++) {
       if (maxSize < expectedTokenSequences[i].length) {
@@ -106,7 +119,7 @@ public class ParseException extends Exception {
       if (expectedTokenSequences[i][expectedTokenSequences[i].length - 1] != 0) {
         expected.append("...");
       }
-      expected.append(EOL).append("    ");
+      expected.append(eol).append("    ");
     }
     String retval = "Encountered \"";
     Token tok = currentToken.next;
@@ -122,26 +135,21 @@ public class ParseException extends Exception {
       retval += " \"";
       tok = tok.next;
     }
-    if (currentToken.next != null) {
-      retval += "\" at line " + currentToken.next.beginLine + ", column " + currentToken.next.beginColumn;
-    }
-    retval += "." + EOL;
-    
-    
-    if (expectedTokenSequences.length == 0) {
-        // Nothing to add here
+    retval += "\" at line " + currentToken.next.beginLine + ", column " + currentToken.next.beginColumn;
+    retval += "." + eol;
+    if (expectedTokenSequences.length == 1) {
+      retval += "Was expecting:" + eol + "    ";
     } else {
-	    if (expectedTokenSequences.length == 1) {
-	      retval += "Was expecting:" + EOL + "    ";
-	    } else {
-	      retval += "Was expecting one of:" + EOL + "    ";
-	    }
-	    retval += expected.toString();
+      retval += "Was expecting one of:" + eol + "    ";
     }
-    
+    retval += expected.toString();
     return retval;
   }
 
+  /**
+   * The end of line string for this machine.
+   */
+  protected String eol = System.getProperty("line.separator", "\n");
 
   /**
    * Used to convert raw characters to their escaped version
@@ -149,11 +157,13 @@ public class ParseException extends Exception {
    * string literal.
    */
   static String add_escapes(String str) {
-      StringBuilder retval = new StringBuilder();
+      StringBuffer retval = new StringBuffer();
       char ch;
       for (int i = 0; i < str.length(); i++) {
         switch (str.charAt(i))
         {
+           case 0 :
+              continue;
            case '\b':
               retval.append("\\b");
               continue;
@@ -192,4 +202,4 @@ public class ParseException extends Exception {
    }
 
 }
-/* JavaCC - OriginalChecksum=7f03520468cc43d4f83f09bbb6a97299 (do not edit this line) */
+/* JavaCC - OriginalChecksum=0bd53902852438cd5670da2f50328753 (do not edit this line) */
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/lang/arq/Token.java b/jena-arq/src/main/java/org/apache/jena/cdt/parser/Token.java
similarity index 79%
copy from jena-arq/src/main/java/org/apache/jena/sparql/lang/arq/Token.java
copy to jena-arq/src/main/java/org/apache/jena/cdt/parser/Token.java
index b4da352e69..3e7793689f 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/lang/arq/Token.java
+++ b/jena-arq/src/main/java/org/apache/jena/cdt/parser/Token.java
@@ -1,6 +1,24 @@
-/* Generated By:JavaCC: Do not edit this line. Token.java Version 7.0 */
-/* JavaCCOptions:TOKEN_EXTENDS=,KEEP_LINE_COLUMN=true,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
-package org.apache.jena.sparql.lang.arq ;
+/* Generated By:JavaCC: Do not edit this line. Token.java Version 5.0 */
+/* JavaCCOptions:TOKEN_EXTENDS=,KEEP_LINE_COL=null,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.cdt.parser;
 
 /**
  * Describes the input token stream.
@@ -97,7 +115,6 @@ public class Token implements java.io.Serializable {
   /**
    * Returns the image.
    */
-  @Override
   public String toString()
   {
     return image;
@@ -129,4 +146,4 @@ public class Token implements java.io.Serializable {
   }
 
 }
-/* JavaCC - OriginalChecksum=0ea2e0bf17ab3b36b824cbe5bb2a103c (do not edit this line) */
+/* JavaCC - OriginalChecksum=0bb1e7330fe557a4194caa87be20ae38 (do not edit this line) */
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/lang/arq/TokenMgrError.java b/jena-arq/src/main/java/org/apache/jena/cdt/parser/TokenMgrError.java
similarity index 65%
copy from jena-arq/src/main/java/org/apache/jena/sparql/lang/arq/TokenMgrError.java
copy to jena-arq/src/main/java/org/apache/jena/cdt/parser/TokenMgrError.java
index 7260bb1d48..307f2e7efd 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/lang/arq/TokenMgrError.java
+++ b/jena-arq/src/main/java/org/apache/jena/cdt/parser/TokenMgrError.java
@@ -1,6 +1,24 @@
-/* Generated By:JavaCC: Do not edit this line. TokenMgrError.java Version 7.0 */
+/* Generated By:JavaCC: Do not edit this line. TokenMgrError.java Version 5.0 */
 /* JavaCCOptions: */
-package org.apache.jena.sparql.lang.arq ;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.cdt.parser;
 
 /** Token Manager Error. */
 
@@ -22,22 +40,22 @@ public class TokenMgrError extends Error
   /**
    * Lexical error occurred.
    */
-  public static final int LEXICAL_ERROR = 0;
+  static final int LEXICAL_ERROR = 0;
 
   /**
    * An attempt was made to create a second instance of a static token manager.
    */
-  public static final int STATIC_LEXER_ERROR = 1;
+  static final int STATIC_LEXER_ERROR = 1;
 
   /**
    * Tried to change to an invalid lexical state.
    */
-  public static final int INVALID_LEXICAL_STATE = 2;
+  static final int INVALID_LEXICAL_STATE = 2;
 
   /**
    * Detected (and bailed out of) an infinite loop in the token manager.
    */
-  public static final int LOOP_DETECTED = 3;
+  static final int LOOP_DETECTED = 3;
 
   /**
    * Indicates the reason why the exception is thrown. It will have
@@ -50,11 +68,13 @@ public class TokenMgrError extends Error
    * equivalents in the given string
    */
   protected static final String addEscapes(String str) {
-    StringBuilder retval = new StringBuilder();
+    StringBuffer retval = new StringBuffer();
     char ch;
     for (int i = 0; i < str.length(); i++) {
       switch (str.charAt(i))
       {
+        case 0 :
+          continue;
         case '\b':
           retval.append("\\b");
           continue;
@@ -97,20 +117,19 @@ public class TokenMgrError extends Error
    * token manager to indicate a lexical error.
    * Parameters :
    *    EOFSeen     : indicates if EOF caused the lexical error
-   *    lexState    : lexical state in which this error occurred
+   *    curLexState : lexical state in which this error occurred
    *    errorLine   : line number when the error occurred
    *    errorColumn : column number when the error occurred
    *    errorAfter  : prefix that was seen before this error occurred
    *    curchar     : the offending character
    * Note: You can customize the lexical error message by modifying this method.
    */
-  protected static String LexicalErr(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, int curChar) {
-    return("Lexical error at line " + //
-          errorLine + ", column " + //
-          errorColumn + ".  Encountered: " + //
-          (EOFSeen ? "<EOF>" : ("'" + addEscapes(String.valueOf(curChar)) + "' (" + curChar + "),")) + //
-          (errorAfter == null || errorAfter.length() == 0 ? "" : " after prefix \"" + addEscapes(errorAfter) + "\"")) + //
-          (lexState == 0 ? "" : " (in lexical state " + lexState + ")");
+  protected static String LexicalError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar) {
+    return("Lexical error at line " +
+          errorLine + ", column " +
+          errorColumn + ".  Encountered: " +
+          (EOFSeen ? "<EOF> " : ("\"" + addEscapes(String.valueOf(curChar)) + "\"") + " (" + (int)curChar + "), ") +
+          "after : \"" + addEscapes(errorAfter) + "\"");
   }
 
   /**
@@ -122,7 +141,6 @@ public class TokenMgrError extends Error
    *
    * from this method for such cases in the release version of your parser.
    */
-  @Override
   public String getMessage() {
     return super.getMessage();
   }
@@ -142,8 +160,8 @@ public class TokenMgrError extends Error
   }
 
   /** Full Constructor. */
-  public TokenMgrError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, int curChar, int reason) {
-    this(LexicalErr(EOFSeen, lexState, errorLine, errorColumn, errorAfter, curChar), reason);
+  public TokenMgrError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar, int reason) {
+    this(LexicalError(EOFSeen, lexState, errorLine, errorColumn, errorAfter, curChar), reason);
   }
 }
-/* JavaCC - OriginalChecksum=26900e63a554da1bde0d1d6ebd69f6c3 (do not edit this line) */
+/* JavaCC - OriginalChecksum=8d2f5d5473ff1b03e240576a846cb262 (do not edit this line) */
diff --git a/jena-arq/src/main/java/org/apache/jena/query/ARQ.java b/jena-arq/src/main/java/org/apache/jena/query/ARQ.java
index a104dcf8ca..941f90f6c8 100644
--- a/jena-arq/src/main/java/org/apache/jena/query/ARQ.java
+++ b/jena-arq/src/main/java/org/apache/jena/query/ARQ.java
@@ -22,6 +22,9 @@ import java.net.http.HttpClient;
 import java.util.concurrent.TimeUnit;
 
 import org.apache.jena.atlas.lib.Version;
+import org.apache.jena.cdt.CompositeDatatypeList;
+import org.apache.jena.cdt.CompositeDatatypeMap;
+import org.apache.jena.datatypes.TypeMapper;
 import org.apache.jena.http.sys.HttpRequestModifier;
 import org.apache.jena.http.sys.RegistryRequestModifier;
 import org.apache.jena.riot.RIOT;
@@ -664,6 +667,10 @@ public class ARQ
             AggregateRegistry.init();
             PropertyFunctionRegistry.init();
 
+            // Register the datatypes for the CDT literals
+            TypeMapper.getInstance().registerDatatype(CompositeDatatypeList.type) ;
+            TypeMapper.getInstance().registerDatatype(CompositeDatatypeMap.type) ;
+
             JenaSystem.logLifecycle("ARQ.init - finish");
         }
     }
diff --git a/jena-arq/src/main/java/org/apache/jena/riot/RDFParser.java b/jena-arq/src/main/java/org/apache/jena/riot/RDFParser.java
index 35c451d004..f9e04c747c 100644
--- a/jena-arq/src/main/java/org/apache/jena/riot/RDFParser.java
+++ b/jena-arq/src/main/java/org/apache/jena/riot/RDFParser.java
@@ -553,7 +553,7 @@ public class RDFParser {
                 ? resolver
                 : IRIxResolver.create().base(baseStr).resolve(resolve).allowRelative(allowRelative).build();
         PrefixMap pmap = ( this.prefixMap != null ) ? this.prefixMap : PrefixMapFactory.create();
-        ParserProfileStd parserFactory = new ParserProfileStd(factory, errorHandler,
+        ParserProfileStd parserFactory = new CDTAwareParserProfile(factory, errorHandler,
                                                               parserResolver, pmap,
                                                               context, checking$, strict);
         return parserFactory;
diff --git a/jena-arq/src/main/java/org/apache/jena/riot/system/CDTAwareParserProfile.java b/jena-arq/src/main/java/org/apache/jena/riot/system/CDTAwareParserProfile.java
new file mode 100644
index 0000000000..660b0c2580
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/riot/system/CDTAwareParserProfile.java
@@ -0,0 +1,132 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.riot.system;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.jena.cdt.CDTKey;
+import org.apache.jena.cdt.CDTValue;
+import org.apache.jena.cdt.CompositeDatatypeList;
+import org.apache.jena.cdt.CompositeDatatypeMap;
+import org.apache.jena.cdt.ParserForCDTLiterals;
+import org.apache.jena.datatypes.DatatypeFormatException;
+import org.apache.jena.datatypes.RDFDatatype;
+import org.apache.jena.graph.Node;
+import org.apache.jena.graph.NodeFactory;
+import org.apache.jena.graph.impl.LiteralLabel;
+import org.apache.jena.graph.impl.LiteralLabelFactory;
+import org.apache.jena.irix.IRIxResolver;
+import org.apache.jena.sparql.util.Context;
+
+/**
+ * This is a {@link ParserProfile} that supports parsing of CDT literals
+ * that occur within the parsed file. The main point is to share the
+ * {@link FactoryRDF} object of this parser profile with the parser of
+ * these literals in order to get the same blank nodes for the same
+ * blank node identifiers both within and outside of the literals, as
+ * well as across multiple CDT literals that occur in the parsed file.
+ */
+public class CDTAwareParserProfile extends ParserProfileStd {
+
+	public CDTAwareParserProfile( final FactoryRDF factory,
+	                              final ErrorHandler errorHandler,
+	                              final IRIxResolver resolver,
+	                              final PrefixMap prefixMap,
+	                              final Context context,
+	                              final boolean checking,
+	                              final boolean strictMode ) {
+		super(factory, errorHandler, resolver, prefixMap, context, checking, strictMode);
+	}
+
+	@Override
+	public Node createTypedLiteral( final String lex, final RDFDatatype datatype, final long line, final long col ) {
+		// cdt:List and cdt:Map literals need to be treated in a special way
+		// because we need to construct their value by parsing them using this
+		// same parser profile; this is necessary to make sure that blank node
+		// identifiers inside the lexical forms of multiple of these literals
+		// within the same file are all mapped to the same blank node, and so
+		// are all blank node identifers that occur directly in the file (i.e.,
+		// ourside of CDT literals).
+
+		if ( datatype.equals(CompositeDatatypeList.type) ) {
+			return createListLiteral(lex);
+		}
+
+		if ( datatype.equals(CompositeDatatypeMap.type) ) {
+			return createMapLiteral(lex);
+		}
+
+		return super.createTypedLiteral(lex, datatype, line, col);
+	}
+
+	protected Node createListLiteral( final String lex ) {
+		// Attention: In contrast to the overridden createTypedLiteral function
+		// in the superclass, for literals of the CDT datatypes we do not perform
+		// a checkLiteral check because that would parse the lexical form of the
+		// literal already once before doing the other parse to obtain the value.
+
+		// parse the given lexical form using this same parser profile
+		final List<CDTValue> value;
+		try {
+			value = ParserForCDTLiterals.parseListLiteral(this, lex);
+		}
+		catch ( final Exception ex ) {
+			throw new DatatypeFormatException(lex, CompositeDatatypeList.type, ex);
+		}
+
+		// At this point we have both the lexical form (which, after the
+		// parsing, we now know is well formed) and the corresponding value
+		// of the literal. We create a LiteralLabel with both of them, for
+		// the following reasons.
+		// If we were to create the LiteralLabel with the lexical form only,
+		// then the LiteralLabel implementation would parse that lexical form
+		// again, which gives us an unnecessary performance penalty (in
+		// particular for huge lists).
+		// If we were to create the LiteralLabel with the value only, then the
+		// LiteralLabel implementation would reproduce the lexical form, which
+		// may not be identical to the lexical form with which we have created
+		// the value here. The issue with that is that a SAMETERM comparison
+		// would incorrectly return false.
+		final LiteralLabel ll = LiteralLabelFactory.createIncludingValue(lex, value, CompositeDatatypeList.type);
+		return NodeFactory.createLiteral(ll);
+	}
+
+	protected Node createMapLiteral( final String lex ) {
+		// Attention: In contrast to the overridden createTypedLiteral function
+		// in the superclass, for literals of the CDT datatypes we do not perform
+		// a checkLiteral check because that would parse the lexical form of the
+		// literal already once before doing the other parse to obtain the value.
+
+		// parse the given lexical form using this same parser profile
+		final Map<CDTKey,CDTValue> value;
+		try {
+			value = ParserForCDTLiterals.parseMapLiteral(this, lex);
+		}
+		catch ( final Exception ex ) {
+			throw new DatatypeFormatException(lex, CompositeDatatypeMap.type, ex);
+		}
+
+		// We create a LiteralLabel with both the lexical form and the value,
+		// for the same reasons as described above in createListLiteral().
+		final LiteralLabel ll = LiteralLabelFactory.createIncludingValue(lex, value, CompositeDatatypeMap.type);
+		return NodeFactory.createLiteral(ll);
+	}
+
+}
diff --git a/jena-arq/src/main/java/org/apache/jena/riot/system/RiotLib.java b/jena-arq/src/main/java/org/apache/jena/riot/system/RiotLib.java
index b81691b289..11e3568a3e 100644
--- a/jena-arq/src/main/java/org/apache/jena/riot/system/RiotLib.java
+++ b/jena-arq/src/main/java/org/apache/jena/riot/system/RiotLib.java
@@ -224,7 +224,7 @@ public class RiotLib {
      * Create a {@link ParserProfile} with default settings, and a specific error handler.
      */
     public static ParserProfile createParserProfile(FactoryRDF factory, ErrorHandler errorHandler, boolean checking) {
-        return new ParserProfileStd(factory,
+        return new CDTAwareParserProfile(factory,
                                     errorHandler,
                                     IRIxResolver.create(IRIs.getSystemBase()).build(),
                                     PrefixMapFactory.create(),
@@ -238,7 +238,7 @@ public class RiotLib {
      */
     public static ParserProfile createParserProfile(FactoryRDF factory, ErrorHandler errorHandler,
                                                     IRIxResolver resolver, boolean checking) {
-        return new ParserProfileStd(factory,
+        return new CDTAwareParserProfile(factory,
                 errorHandler,
                 resolver,
                 PrefixMapFactory.create(),
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java b/jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java
index 8f6ac06a24..d18420b3f6 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java
@@ -80,6 +80,9 @@ public class ARQConstants
     /** URI scheme that triggers the loader to load a java class */
     public static final String javaClassURIScheme = "java:" ;
 
+    /** The ARQ function library URI space */
+    public static final String CDTFunctionLibraryURI = "http://w3id.org/awslabs/neptune/SPARQL-CDTs/" ;
+
     /** The ARQ function library URI space */
     public static final String ARQFunctionLibraryURI = "http://jena.apache.org/ARQ/function#" ;
 
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/algebra/AlgebraGenerator.java b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/AlgebraGenerator.java
index 5174505d0e..016a0240c9 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/algebra/AlgebraGenerator.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/AlgebraGenerator.java
@@ -324,6 +324,10 @@ public class AlgebraGenerator
         if ( elt instanceof ElementNotExists elt2 )
             return compileElementNotExists(current, elt2);
 
+        if ( elt instanceof ElementUnfold eltUnfold ) {
+            return new OpUnfold(current, eltUnfold.getExpr(), eltUnfold.getVar1(), eltUnfold.getVar2());
+        }
+
         // Filters were collected together by prepareGroup
         // This only handles filters left in place by some magic.
         if ( elt instanceof ElementFilter ef)
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/algebra/OpAsQuery.java b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/OpAsQuery.java
index 1c74499dbd..4b23ab44aa 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/algebra/OpAsQuery.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/OpAsQuery.java
@@ -828,6 +828,15 @@ public class OpAsQuery {
             }) ;
         }
 
+        @Override
+        public void visit(OpUnfold opUnfold) {
+            Element e = asElement(opUnfold.getSubOp()) ;
+            // If (unfold ... (table unit)), and first in group, don't add the empty group.
+            insertIntoGroup(currentGroup(), e) ;
+            Element elmtUnfold = new ElementUnfold( opUnfold.getExpr(), opUnfold.getVar1(), opUnfold.getVar2() ) ;
+            currentGroup().addElement(elmtUnfold) ;
+        }
+
         @Override
         public void visit(OpList opList) {
             opList.getSubOp().visit(this) ;
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/algebra/OpVars.java b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/OpVars.java
index dcdde83de9..96c4b287bb 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/algebra/OpVars.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/OpVars.java
@@ -311,6 +311,14 @@ public class OpVars
             acc.addAll(opExtend.getVarExprList().getVars());
         }
 
+        @Override
+        public void visit(OpUnfold opUnfold) {
+            acc.add( opUnfold.getVar1() );
+            if ( opUnfold.getVar2() != null ) {
+                acc.add( opUnfold.getVar2() );
+            }
+        }
+
         @Override
         public void visit(OpPropFunc opPropFunc) {
             PropFuncArg.addVars(acc, opPropFunc.getSubjectArgs());
@@ -430,6 +438,14 @@ public class OpVars
             unknownAcc.addAll(opExtend.getVarExprList().getVars());
         }
 
+        @Override
+        public void visit(OpUnfold opUnfold) {
+            unknownAcc.add( opUnfold.getVar1() );
+            if ( opUnfold.getVar2() != null ) {
+                unknownAcc.add( opUnfold.getVar2() );
+            }
+        }
+
         @Override
         public void visit(OpPropFunc opPropFunc) {
             addvars(subjAcc, opPropFunc.getSubjectArgs());
@@ -496,6 +512,11 @@ public class OpVars
             visitExtendAssign(opAssign);
         }
 
+        @Override
+        public void visit(OpUnfold opUnfold) {
+            ExprVars.varsMentioned( acc, opUnfold.getExpr() );
+        }
+
         // The expression side of (extend) and (assign)
         private void visitExtendAssign(OpExtendAssign op) {
             op.getVarExprList().forEachExpr((v, e) -> ExprVars.varsMentioned(acc, e));
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/algebra/OpVisitor.java b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/OpVisitor.java
index 15a13d5fd4..a7a74c7919 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/algebra/OpVisitor.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/OpVisitor.java
@@ -42,6 +42,7 @@ public interface OpVisitor
     public void visit(OpLabel opLabel) ;
     public void visit(OpAssign opAssign) ;
     public void visit(OpExtend opExtend) ;
+    public void visit(OpUnfold opUnfold) ;
 
     // Op2
     public void visit(OpJoin opJoin) ;
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/algebra/OpVisitorBase.java b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/OpVisitorBase.java
index 8eccc40e9d..89cbe7692d 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/algebra/OpVisitorBase.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/OpVisitorBase.java
@@ -80,6 +80,8 @@ public class OpVisitorBase implements OpVisitor
 
     @Override public void visit(OpExtend opExtend)              {}
 
+    @Override public void visit(OpUnfold opUnfold)              {}
+
     @Override public void visit(OpList opList)                  {}
 
     @Override public void visit(OpOrder opOrder)                {}
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/algebra/OpVisitorByType.java b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/OpVisitorByType.java
index b331f817a5..fd8f0b4b8b 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/algebra/OpVisitorByType.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/OpVisitorByType.java
@@ -150,6 +150,10 @@ public abstract class OpVisitorByType implements OpVisitor
     public void visit(OpExtend opExtend)
     { visit1(opExtend) ; }
 
+    @Override
+    public void visit(OpUnfold opUnfold)
+    { visit1(opUnfold) ; }
+
     @Override
     public void visit(OpList opList)
     { visitModifer(opList) ; }
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/algebra/Transform.java b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/Transform.java
index 98fbc1b872..bdebe52b98 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/algebra/Transform.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/Transform.java
@@ -44,6 +44,7 @@ public interface Transform
     public Op transform(OpLabel opLabel, Op subOp) ;
     public Op transform(OpAssign opAssign, Op subOp) ;
     public Op transform(OpExtend opExtend, Op subOp) ;
+    public Op transform(OpUnfold opUnfold, Op subOp) ;
 
     // Op2
     public Op transform(OpJoin opJoin, Op left, Op right) ;
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/algebra/TransformCopy.java b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/TransformCopy.java
index 32ecd34b17..6c21d89be8 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/algebra/TransformCopy.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/TransformCopy.java
@@ -68,6 +68,8 @@ public class TransformCopy implements Transform
     public Op transform(OpAssign opAssign, Op subOp)                { return xform(opAssign, subOp) ; }
     @Override
     public Op transform(OpExtend opExtend, Op subOp)                { return xform(opExtend, subOp) ; }
+    @Override
+    public Op transform(OpUnfold opUnfold, Op subOp)                { return xform(opUnfold, subOp) ; }
 
     @Override
     public Op transform(OpJoin opJoin, Op left, Op right)           { return xform(opJoin, left, right) ; }
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/algebra/TransformSingle.java b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/TransformSingle.java
index 5515cb85e7..69a525bd76 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/algebra/TransformSingle.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/TransformSingle.java
@@ -64,6 +64,8 @@ public class TransformSingle implements Transform
     public Op transform(OpAssign opAssign, Op subOp)        { return opAssign ; }
     @Override
     public Op transform(OpExtend opExtend, Op subOp)        { return opExtend ; }
+    @Override
+    public Op transform(OpUnfold opUnfold, Op subOp)        { return opUnfold ; }
 
     @Override
     public Op transform(OpJoin opJoin, Op left, Op right)           { return opJoin ; }
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/algebra/TransformWrapper.java b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/TransformWrapper.java
index 30bff50c70..0be481886f 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/algebra/TransformWrapper.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/TransformWrapper.java
@@ -66,6 +66,8 @@ public class TransformWrapper implements Transform
     public Op transform(OpAssign opAssign, Op subOp)        { return transform.transform(opAssign, subOp) ; }
     @Override
     public Op transform(OpExtend opExtend, Op subOp)        { return transform.transform(opExtend, subOp) ; }
+    @Override
+    public Op transform(OpUnfold opUnfold, Op subOp)        { return transform.transform(opUnfold, subOp) ; }
 
     @Override
     public Op transform(OpJoin opJoin, Op left, Op right)           { return transform.transform(opJoin, left, right) ; }
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/algebra/op/OpUnfold.java b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/op/OpUnfold.java
new file mode 100644
index 0000000000..55a193e133
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/op/OpUnfold.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.sparql.algebra.op;
+
+import java.util.Objects;
+
+import org.apache.jena.sparql.algebra.Op;
+import org.apache.jena.sparql.algebra.OpVisitor;
+import org.apache.jena.sparql.algebra.Transform;
+import org.apache.jena.sparql.core.Var;
+import org.apache.jena.sparql.expr.Expr;
+import org.apache.jena.sparql.sse.Tags;
+import org.apache.jena.sparql.util.NodeIsomorphismMap;
+
+public class OpUnfold extends Op1
+{
+    protected final Expr expr ;
+    protected final Var var1 ;
+    protected final Var var2 ;
+
+    public OpUnfold(Op subOp, Expr expr, Var var1, Var var2) {
+        super(subOp) ;
+        this.expr = expr ;
+        this.var1 = var1 ;
+        this.var2 = var2 ;
+    }
+
+    public Expr getExpr()
+    {
+        return expr ;
+    }
+
+    public Var getVar1()
+    {
+        return var1 ;
+    }
+
+    public Var getVar2()
+    {
+        return var2 ;
+    }
+
+    @Override
+    public String getName() {
+        return Tags.tagUnfold ;
+    }
+
+    @Override
+    public void visit(OpVisitor opVisitor) {
+        opVisitor.visit(this) ;
+    }
+
+    @Override
+    public Op1 copy(Op subOp) {
+        OpUnfold op = new OpUnfold(subOp, expr, var1, var2) ;
+        return op ;
+    }
+
+    @Override
+    public boolean equalTo(Op other, NodeIsomorphismMap labelMap) {
+        if ( !(other instanceof OpUnfold) )
+            return false ;
+        OpUnfold unfold = (OpUnfold)other ;
+
+        if (    !Objects.equals(var1, unfold.var1) )
+            return false ;
+        if (    !Objects.equals(var2, unfold.var2) )
+            return false ;
+        if (    !Objects.equals(expr, unfold.expr) )
+            return false ;
+        return getSubOp().equalTo(unfold.getSubOp(), labelMap) ;
+    }
+
+    @Override
+    public Op apply(Transform transform, Op subOp) {
+        return transform.transform(this, subOp) ;
+    }
+
+    @Override
+    public int hashCode() {
+        if ( var2 == null )
+            return getName().hashCode() ^ expr.hashCode() ^ var1.hashCode() ^ getSubOp().hashCode() ;
+        else
+            return getName().hashCode() ^ expr.hashCode() ^ var1.hashCode() ^ var2.hashCode() ^ getSubOp().hashCode() ;
+    }
+}
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/algebra/optimize/VariableUsageVisitor.java b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/optimize/VariableUsageVisitor.java
index 9cc3bb1413..747ee97bf5 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/algebra/optimize/VariableUsageVisitor.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/optimize/VariableUsageVisitor.java
@@ -41,6 +41,7 @@ import org.apache.jena.sparql.algebra.op.OpQuadBlock;
 import org.apache.jena.sparql.algebra.op.OpQuadPattern;
 import org.apache.jena.sparql.algebra.op.OpTable;
 import org.apache.jena.sparql.algebra.op.OpTopN;
+import org.apache.jena.sparql.algebra.op.OpUnfold;
 import org.apache.jena.sparql.core.Quad;
 import org.apache.jena.sparql.core.Var;
 import org.apache.jena.sparql.core.Vars;
@@ -169,6 +170,17 @@ public abstract class VariableUsageVisitor extends OpVisitorBase {
         action(vars);
     }
 
+    @Override
+    public void visit(OpUnfold opUnfold) {
+        Collection<Var> vars = new ArrayList<>();
+        ExprVars.varsMentioned(vars, opUnfold.getExpr());
+        vars.add( opUnfold.getVar1() );
+        if ( opUnfold.getVar2() != null ) {
+            vars.add( opUnfold.getVar2() );
+        }
+        action(vars);
+    }
+
     @Override
     public void visit(OpOrder opOrder) {
         Collection<Var> vars = new ArrayList<>();
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/algebra/walker/ApplyTransformVisitor.java b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/walker/ApplyTransformVisitor.java
index 1954333480..0e9ae51129 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/algebra/walker/ApplyTransformVisitor.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/walker/ApplyTransformVisitor.java
@@ -147,6 +147,15 @@ public class ApplyTransformVisitor implements OpVisitorByTypeAndExpr, ExprVisito
         visit1(opExtend2) ;
     }
 
+    @Override
+    public void visit(OpUnfold opUnfold) {
+        final Expr e2 = pop(exprStack);
+        if ( ! opUnfold.getExpr().equals(e2) )
+            throw new IllegalArgumentException("Expression on stack differs from expression of UNFOLD.");
+
+        visit1(opUnfold) ;
+    }
+
     // Special test cases for collectors.
 
     // Careful about order.
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/algebra/walker/OpVisitorByType.java b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/walker/OpVisitorByType.java
index 3f373291f0..2ec2fbb802 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/algebra/walker/OpVisitorByType.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/walker/OpVisitorByType.java
@@ -188,6 +188,11 @@ public interface OpVisitorByType extends OpVisitor
         visit1(opExtend);
     }
 
+    @Override
+    public default void visit(OpUnfold opUnfold) {
+        visit1(opUnfold);
+    }
+
     @Override
     public default void visit(OpList opList) {
         visitModifer(opList);
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/algebra/walker/OpVisitorByTypeAndExpr.java b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/walker/OpVisitorByTypeAndExpr.java
index 5387afab57..bf3d358964 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/algebra/walker/OpVisitorByTypeAndExpr.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/walker/OpVisitorByTypeAndExpr.java
@@ -215,6 +215,12 @@ public interface OpVisitorByTypeAndExpr extends OpVisitor
         visit1(opExtend);
     }
 
+    @Override
+    public default void visit(OpUnfold opUnfold) {
+        visitExpr( new ExprList(opUnfold.getExpr()) ) ;
+        visit1(opUnfold);
+    }
+
     @Override
     public default void visit(OpList opList) {
         visitModifer(opList);
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/algebra/walker/WalkerVisitor.java b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/walker/WalkerVisitor.java
index 6243225afc..d1fa6e7e9e 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/algebra/walker/WalkerVisitor.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/walker/WalkerVisitor.java
@@ -215,6 +215,15 @@ public class WalkerVisitor implements OpVisitorByTypeAndExpr, ExprVisitorFunctio
         after(opExtend) ;
     }
 
+    @Override
+    public void visit(OpUnfold opUnfold) {
+        before(opUnfold) ;
+        ExprList varExpr = new ExprList( opUnfold.getExpr() ) ;
+        visitExpr(varExpr);
+        visit1$(opUnfold) ;
+        after(opUnfold) ;
+    }
+
     // Transforming to quads needs the graph node handled before doing the sub-algebra ops
     // so it has to be done as before/after by the Walker. By the time visit(OpGraph) is called,
     // the sub-tree has already been visited.
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/engine/iterator/QueryIterUnfold.java b/jena-arq/src/main/java/org/apache/jena/sparql/engine/iterator/QueryIterUnfold.java
new file mode 100644
index 0000000000..6de9f31fc8
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/engine/iterator/QueryIterUnfold.java
@@ -0,0 +1,346 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.sparql.engine.iterator;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+import org.apache.jena.atlas.io.IndentedWriter;
+import org.apache.jena.cdt.CDTKey;
+import org.apache.jena.cdt.CDTValue;
+import org.apache.jena.cdt.CompositeDatatypeList;
+import org.apache.jena.cdt.CompositeDatatypeMap;
+import org.apache.jena.datatypes.xsd.XSDDatatype;
+import org.apache.jena.graph.Node;
+import org.apache.jena.graph.NodeFactory;
+import org.apache.jena.graph.impl.LiteralLabel;
+import org.apache.jena.sparql.core.Var;
+import org.apache.jena.sparql.engine.ExecutionContext;
+import org.apache.jena.sparql.engine.QueryIterator;
+import org.apache.jena.sparql.engine.binding.Binding;
+import org.apache.jena.sparql.engine.binding.BindingFactory;
+import org.apache.jena.sparql.expr.Expr;
+import org.apache.jena.sparql.expr.ExprEvalException;
+import org.apache.jena.sparql.expr.NodeValue;
+import org.apache.jena.sparql.serializer.SerializationContext;
+
+public class QueryIterUnfold extends QueryIterRepeatApply
+{
+    protected final Expr expr ;
+    protected final Var var1 ;
+    protected final Var var2 ;
+
+    public QueryIterUnfold(QueryIterator qIter, Expr expr, Var var1, Var var2, ExecutionContext execCxt) {
+        super(qIter, execCxt) ;
+
+        this.expr = expr ;
+        this.var1 = var1 ;
+        this.var2 = var2 ;
+    }
+
+    @Override
+    protected QueryIterator nextStage(Binding inputBinding) {
+        final NodeValue nv;
+        try {
+            nv = expr.eval( inputBinding, getExecContext() );
+        }
+        catch ( final ExprEvalException ex ) {
+            // If the expression failed to evaluate, we create no
+            // no assignment (exactly as in the case of BIND, see
+            // the 'accept' method in 'QueryIterAssign')
+
+            return QueryIterSingleton.create( inputBinding, getExecContext() );
+        }
+
+        Node n = nv.asNode();
+        if ( n.isLiteral() ) {
+            String dtURI = n.getLiteralDatatypeURI();
+            if ( CompositeDatatypeList.uri.equals(dtURI) )
+                return unfoldList( n.getLiteral(), inputBinding );
+            if ( CompositeDatatypeMap.uri.equals(dtURI) )
+                return unfoldMap( n.getLiteral(), inputBinding );
+        }
+
+        return QueryIterSingleton.create( inputBinding, getExecContext() );
+    }
+
+    protected QueryIterator unfoldList( final LiteralLabel lit, final Binding inputBinding ) {
+        final Iterable<CDTValue> itListElmts = CompositeDatatypeList.getValue(lit);
+        return new QueryIterUnfoldWorkerForLists(inputBinding, itListElmts);
+    }
+
+    protected QueryIterator unfoldMap( final LiteralLabel lit, final Binding inputBinding ) {
+        final Iterable<Map.Entry<CDTKey,CDTValue>> itMapEntries = CompositeDatatypeMap.getValue(lit).entrySet();
+        return new QueryIterUnfoldWorkerForMaps(inputBinding, itMapEntries);
+    }
+
+    protected abstract class QueryIterUnfoldWorkerBase<T> extends QueryIteratorBase {
+        protected final Binding inputBinding;
+        protected final Iterator<T> itElmts;
+
+        protected QueryIterUnfoldWorkerBase(Binding inputBinding, Iterator<T> itElmts) {
+            this.inputBinding = inputBinding;
+            this.itElmts = itElmts;
+        }
+
+        protected QueryIterUnfoldWorkerBase(Binding inputBinding, Iterable<T> itElmts) {
+            this( inputBinding, itElmts.iterator() );
+        }
+
+        @Override
+        protected boolean hasNextBinding() { return itElmts.hasNext(); }
+
+        @Override
+        protected void requestCancel() { } // nothing to do really
+
+        @Override
+        protected void closeIterator() { } // nothing to do really
+    }
+
+
+    protected class QueryIterUnfoldWorkerForLists extends QueryIterUnfoldWorkerBase<CDTValue> {
+
+        protected int nextIndex = 0;
+
+        public QueryIterUnfoldWorkerForLists(Binding inputBinding, Iterator<CDTValue> itListElmts) {
+            super(inputBinding, itListElmts);
+        }
+
+        public QueryIterUnfoldWorkerForLists(Binding inputBinding, Iterable<CDTValue> itListElmts) {
+            super(inputBinding, itListElmts);
+        }
+
+        @Override
+        public void output(IndentedWriter out, SerializationContext sCxt) {
+            out.write("QueryIterUnfoldWorkerForLists");
+        }
+
+        @Override
+        protected Binding moveToNextBinding() {
+            final CDTValue nextElmt = itElmts.next();
+            nextIndex++; // index values to be assigned to the second variable start at 1
+
+            final Node indexNode;
+            if ( var2 != null ) {
+                final String indexStr = Integer.toString(nextIndex);
+                indexNode = NodeFactory.createLiteral(indexStr, XSDDatatype.XSDinteger);
+            }
+            else {
+                indexNode = null;
+            }
+
+            if ( nextElmt.isNull() ) {
+                if ( var2 != null )
+                    return BindingFactory.binding(inputBinding, var2, indexNode);
+                else
+                    return inputBinding;
+            }
+
+            if ( nextElmt.isNode() ) {
+                final Node elmtNode = nextElmt.asNode();
+
+                if ( var2 != null )
+                    return BindingFactory.binding(inputBinding, var1, elmtNode, var2, indexNode);
+                else
+                    return BindingFactory.binding(inputBinding, var1, elmtNode);
+            }
+
+            throw new UnsupportedOperationException( "unexpected list element: " + nextElmt.getClass().getName() );
+        }
+    }
+
+
+    protected class QueryIterUnfoldWorkerForMaps extends QueryIterUnfoldWorkerBase<Map.Entry<CDTKey,CDTValue>> {
+
+        public QueryIterUnfoldWorkerForMaps(Binding inputBinding, Iterator<Map.Entry<CDTKey,CDTValue>> itMapElmts) {
+            super(inputBinding, itMapElmts);
+        }
+
+        public QueryIterUnfoldWorkerForMaps(Binding inputBinding, Iterable<Map.Entry<CDTKey,CDTValue>> itMapElmts) {
+            super(inputBinding, itMapElmts);
+        }
+
+        @Override
+        public void output(IndentedWriter out, SerializationContext sCxt) {
+            out.write("QueryIterUnfoldWorkerForMaps");
+        }
+
+        @Override
+        protected Binding moveToNextBinding() {
+            final Map.Entry<CDTKey,CDTValue> elmt = itElmts.next();
+            final CDTKey key     = elmt.getKey();
+            final CDTValue value = elmt.getValue();
+
+            final Node keyNode = key.asNode();
+
+            if ( value.isNull() || var2 == null ) {
+                return BindingFactory.binding( inputBinding, var1, keyNode );
+            }
+
+            if ( value.isNode() ) {
+                final Node valueNode = value.asNode();
+                return BindingFactory.binding(inputBinding, var1, keyNode, var2, valueNode);
+            }
+
+            throw new UnsupportedOperationException( "unexpected map value: " + value.getClass().getName() );
+        }
+    }
+
+
+    protected static abstract class ElementExtractorBase<T> implements Iterator<T> {
+        protected final String str;
+        protected int cursor = 0;
+        private T nextElmt = null;
+
+        protected ElementExtractorBase( final String str ) {
+            this.str = str.strip();
+        }
+
+        @Override
+        public boolean hasNext() {
+            if ( nextElmt != null )
+                return true;
+
+            nextElmt = produceNext();
+            return ( nextElmt != null );
+        }
+
+        @Override
+        public T next() {
+            if ( ! hasNext() )
+                throw new NoSuchElementException();
+
+            final T r = nextElmt;
+            nextElmt = null;
+            return r;
+        }
+
+        protected abstract T produceNext();
+
+        protected void consumeWhiteSpace() {
+            while ( cursor < str.length() && str.charAt(cursor) == ' ' )
+                cursor++;
+        }
+
+        protected void advanceToEndOfSubList() {
+            while ( cursor < str.length() && str.charAt(cursor) != ']' ) {
+                cursor++;
+                if ( str.charAt(cursor) == '[' ) {
+                    advanceToEndOfSubList();
+                    cursor++;
+                }
+                else if ( str.charAt(cursor) == '{' ) {
+                    advanceToEndOfSubMap();
+                    cursor++;
+                }
+            }
+        }
+
+        protected void advanceToEndOfSubMap() {
+            while ( cursor < str.length() && str.charAt(cursor) != '}' ) {
+                cursor++;
+                if ( str.charAt(cursor) == '[' ) {
+                    advanceToEndOfSubList();
+                    cursor++;
+                }
+                else if ( str.charAt(cursor) == '{' ) {
+                    advanceToEndOfSubMap();
+                    cursor++;
+                }
+            }
+        }
+
+        protected void advanceToNextCommaOrEnd() {
+            while ( cursor < str.length() && str.charAt(cursor) != ',' )
+                cursor++;
+        }
+    }
+
+
+    protected static class ListElementExtractor extends ElementExtractorBase<String> {
+        public ListElementExtractor( final String listAsString ) {
+            super(listAsString);
+        }
+
+        @Override
+        public String produceNext() {
+            consumeWhiteSpace();
+            if ( cursor >= str.length() ) {
+                return null;
+            }
+
+            final int nextElmtBegin = cursor;
+            if ( str.charAt(cursor) == '[' ) {
+                advanceToEndOfSubList();
+            }
+            else if ( str.charAt(cursor) == '{' ) {
+                advanceToEndOfSubMap();
+            }
+            advanceToNextCommaOrEnd();
+            final String nextElmt = str.substring(nextElmtBegin, cursor).strip();
+            cursor++; // move cursor to after comma
+            return nextElmt;
+        }
+    }
+
+
+    protected static class MapElementExtractor extends ElementExtractorBase<Map.Entry<String,String>> {
+        public MapElementExtractor( final String mapAsString ) {
+            super(mapAsString);
+        }
+
+        @Override
+        public Map.Entry<String,String> produceNext() {
+            consumeWhiteSpace();
+            if ( cursor >= str.length() ) {
+                return null;
+            }
+
+            final int nextKeyBegin = cursor;
+            advanceToNextColon();
+            final String nextKey = str.substring(nextKeyBegin, cursor).strip();
+
+            cursor++; // move cursor to after colon
+            consumeWhiteSpace();
+            final int nextValueBegin = cursor;
+            if ( str.charAt(cursor) == '[' ) {
+                advanceToEndOfSubList();
+            }
+            else if ( str.charAt(cursor) == '{' ) {
+                advanceToEndOfSubMap();
+            }
+            advanceToNextCommaOrEnd();
+            final String nextValue = str.substring(nextValueBegin, cursor).strip();
+            cursor++; // move cursor to after comma
+
+            return new Map.Entry<>() {
+                @Override public String getKey() { return nextKey; }
+                @Override public String getValue() { return nextValue; }
+                @Override public String setValue(String v) { throw new UnsupportedOperationException(); }
+            };
+        }
+
+        protected void advanceToNextColon() {
+            while ( cursor < str.length() && str.charAt(cursor) != ':' )
+                cursor++;
+        }
+    }
+
+}
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/engine/main/ExecutionDispatch.java b/jena-arq/src/main/java/org/apache/jena/sparql/engine/main/ExecutionDispatch.java
index 923836a7bc..9697424993 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/engine/main/ExecutionDispatch.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/engine/main/ExecutionDispatch.java
@@ -272,6 +272,13 @@ class ExecutionDispatch implements OpVisitor {
         push(qIter);
     }
 
+    @Override
+    public void visit(OpUnfold opUnfold) {
+        QueryIterator input = pop();
+        QueryIterator qIter = opExecutor.execute(opUnfold, input);
+        push(qIter);
+    }
+
     @Override
     public void visit(OpSlice opSlice) {
         QueryIterator input = pop();
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/engine/main/OpExecutor.java b/jena-arq/src/main/java/org/apache/jena/sparql/engine/main/OpExecutor.java
index 93b12bfed6..e7293e21ee 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/engine/main/OpExecutor.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/engine/main/OpExecutor.java
@@ -450,6 +450,12 @@ public class OpExecutor {
         return qIter;
     }
 
+    protected QueryIterator execute(OpUnfold opUnfold, QueryIterator input) {
+        QueryIterator qIter = exec(opUnfold.getSubOp(), input);
+        qIter = new QueryIterUnfold(qIter, opUnfold.getExpr(), opUnfold.getVar1(), opUnfold.getVar2(), execCxt);
+        return qIter;
+    }
+
     public static QueryIterator createRootQueryIterator(ExecutionContext execCxt) {
         return QueryIterRoot.create(execCxt);
     }
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/engine/main/VarFinder.java b/jena-arq/src/main/java/org/apache/jena/sparql/engine/main/VarFinder.java
index 882fa9b93d..46d0f6dbc8 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/engine/main/VarFinder.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/engine/main/VarFinder.java
@@ -122,7 +122,7 @@ public class VarFinder
         Set<Var> filterMentions     = null ;
         // Used in a filter, before defined
         Set<Var> filterMentionsOnly = null ;
-        // Used in assign or extend expression
+        // Used in assign or extend or unfold expression
         Set<Var> assignMentions     = null ;
 
         VarUsageVisitor() {
@@ -376,6 +376,18 @@ public class VarFinder
             return result;
         }
 
+        @Override
+        public void visit(OpUnfold opUnfold) {
+            opUnfold.getSubOp().visit(this);
+
+            defines.add( opUnfold.getVar1() );
+
+            if ( opUnfold.getVar2() != null )
+                defines.add( opUnfold.getVar2() );
+
+            ExprVars.nonOpVarsMentioned( assignMentions, opUnfold.getExpr() );
+        }
+
         @Override
         public void visit(OpProject opProject) {
             List<Var> vars = opProject.getVars();
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/engine/ref/Evaluator.java b/jena-arq/src/main/java/org/apache/jena/sparql/engine/ref/Evaluator.java
index 5299b5c1f7..201ce5a892 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/engine/ref/Evaluator.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/engine/ref/Evaluator.java
@@ -29,6 +29,7 @@ import org.apache.jena.sparql.core.TriplePath;
 import org.apache.jena.sparql.core.Var;
 import org.apache.jena.sparql.core.VarExprList;
 import org.apache.jena.sparql.engine.ExecutionContext;
+import org.apache.jena.sparql.expr.Expr;
 import org.apache.jena.sparql.expr.ExprAggregator;
 import org.apache.jena.sparql.expr.ExprList;
 import org.apache.jena.sparql.pfunction.PropFuncArg;
@@ -46,6 +47,7 @@ public interface Evaluator
 
     public Table assign(Table table, VarExprList exprs);
     public Table extend(Table table, VarExprList exprs);
+    public Table unfold(Table table, Expr expr, Var var1, Var var2);
 
     public Table join(Table tableLeft, Table tableRight);
     public Table leftJoin(Table tableLeft, Table tableRight, ExprList expr);
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/engine/ref/EvaluatorDispatch.java b/jena-arq/src/main/java/org/apache/jena/sparql/engine/ref/EvaluatorDispatch.java
index f1b1de2790..b459ce606b 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/engine/ref/EvaluatorDispatch.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/engine/ref/EvaluatorDispatch.java
@@ -309,6 +309,13 @@ public class EvaluatorDispatch implements OpVisitor {
         push(table);
     }
 
+    @Override
+    public void visit(OpUnfold opUnfold) {
+        Table table = eval(opUnfold.getSubOp());
+        table = evaluator.unfold(table, opUnfold.getExpr(), opUnfold.getVar1(), opUnfold.getVar2());
+        push(table);
+    }
+
     @Override
     public void visit(OpGroup opGroup) {
         Table table = eval(opGroup.getSubOp());
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/engine/ref/EvaluatorSimple.java b/jena-arq/src/main/java/org/apache/jena/sparql/engine/ref/EvaluatorSimple.java
index c37b84a7cf..a6db3df526 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/engine/ref/EvaluatorSimple.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/engine/ref/EvaluatorSimple.java
@@ -36,6 +36,7 @@ import org.apache.jena.sparql.engine.binding.Binding;
 import org.apache.jena.sparql.engine.binding.BindingBuilder;
 import org.apache.jena.sparql.engine.iterator.*;
 import org.apache.jena.sparql.engine.main.QC;
+import org.apache.jena.sparql.expr.Expr;
 import org.apache.jena.sparql.expr.ExprAggregator;
 import org.apache.jena.sparql.expr.ExprList;
 import org.apache.jena.sparql.pfunction.PropFuncArg;
@@ -250,6 +251,13 @@ public class EvaluatorSimple implements Evaluator {
         return new TableN(qIter);
     }
 
+    @Override
+    public Table unfold(Table table, Expr expr, Var var1, Var var2) {
+        QueryIterator qIter = table.iterator(getExecContext());
+        qIter = new QueryIterUnfold(qIter, expr, var1, var2, getExecContext());
+        return new TableN(qIter);
+    }
+
     @Override
     public Table unit() {
         return TableFactory.createUnit();
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/expr/NodeValueCmp.java b/jena-arq/src/main/java/org/apache/jena/sparql/expr/NodeValueCmp.java
index 23fc679298..280f37506d 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/expr/NodeValueCmp.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/expr/NodeValueCmp.java
@@ -28,8 +28,11 @@ import java.util.Objects;
 import javax.xml.datatype.Duration;
 
 import org.apache.jena.atlas.lib.StrUtils;
+import org.apache.jena.cdt.CompositeDatatypeList;
+import org.apache.jena.cdt.CompositeDatatypeMap;
 import org.apache.jena.graph.Node;
 import org.apache.jena.graph.Triple;
+import org.apache.jena.graph.impl.LiteralLabel;
 import org.apache.jena.sparql.ARQInternalErrorException;
 import org.apache.jena.sparql.SystemARQ;
 import org.apache.jena.sparql.expr.nodevalue.NodeFunctions;
@@ -144,6 +147,17 @@ public class NodeValueCmp {
                     raise(new ExprEvalException("Incompatible: " + nv1 + " and " + nv2));
                 // Not same node.
                 return false;
+
+            case VSPACE_CDT_LIST : {
+                final LiteralLabel lit1 = nv1.asNode().getLiteral() ;
+                final LiteralLabel lit2 = nv2.asNode().getLiteral() ;
+                return CompositeDatatypeList.type.isEqual(lit1, lit2) ;
+            }
+            case VSPACE_CDT_MAP : {
+                final LiteralLabel lit1 = nv1.asNode().getLiteral() ;
+                final LiteralLabel lit2 = nv2.asNode().getLiteral() ;
+                return CompositeDatatypeMap.type.isEqual(lit1, lit2) ;
+            }
         }
 
         throw new ARQInternalErrorException("sameValueAs failure " + nv1 + " and " + nv2);
@@ -204,14 +218,17 @@ public class NodeValueCmp {
         if ( nv2 == null )
             return CMP_GREATER;
 
-        if ( nv1.hasNode() && nv2.hasNode() ) {
+        ValueSpace compType = classifyValueOp(nv1, nv2) ;
+
+        if ( nv1.hasNode()
+             && nv2.hasNode()
+             && compType != ValueSpace.VSPACE_CDT_LIST
+             && compType != ValueSpace.VSPACE_CDT_MAP ) {
             // Fast path - same RDF term => CMP_EQUAL
             if ( nv1.getNode().equals(nv2.getNode()) )
                 return CMP_EQUAL;
         }
 
-        ValueSpace compType = classifyValueOp(nv1, nv2) ;
-
         // Special case - date/dateTime comparison is affected by timezones and may be
         // indeterminate based on the value of the dateTime/date.
         // Do this first, so that indeterminate can drop through to a general ordering.
@@ -349,6 +366,28 @@ public class NodeValueCmp {
                 raise(new ExprNotComparableException("Can't compare valiables as values "+nv1+" and "+nv2)) ;
             }
 
+            case VSPACE_CDT_LIST : {
+                final LiteralLabel lit1 = nv1.asNode().getLiteral() ;
+                final LiteralLabel lit2 = nv2.asNode().getLiteral() ;
+                try {
+                    return CompositeDatatypeList.compare(lit1, lit2, sortOrderingCompare) ;
+                }
+                catch( final ExprNotComparableException e ) {
+                    raise(e) ;
+                }
+            }
+
+            case VSPACE_CDT_MAP : {
+                final LiteralLabel lit1 = nv1.asNode().getLiteral() ;
+                final LiteralLabel lit2 = nv2.asNode().getLiteral() ;
+                try {
+                    return CompositeDatatypeMap.compare(lit1, lit2, sortOrderingCompare) ;
+                }
+                catch( final ExprNotComparableException e ) {
+                    raise(e) ;
+                }
+            }
+
             case VSPACE_UNKNOWN : {
                 // One or two unknown value spaces.
                 Node node1 = nv1.asNode() ;
@@ -427,6 +466,18 @@ public class NodeValueCmp {
             return NodeCmp$compareRDFTerms(nv1, nv2);
         }
 
+        if ( vs1 == ValueSpace.VSPACE_CDT_LIST && vs2 == ValueSpace.VSPACE_CDT_LIST ) {
+            LiteralLabel l1 = nv1.asNode().getLiteral();
+            LiteralLabel l2 = nv2.asNode().getLiteral();
+            return CompositeDatatypeList.compare(l1, l2, true);
+        }
+
+        if ( vs1 == ValueSpace.VSPACE_CDT_MAP && vs2 == ValueSpace.VSPACE_CDT_MAP ) {
+            LiteralLabel l1 = nv1.asNode().getLiteral();
+            LiteralLabel l2 = nv2.asNode().getLiteral();
+            return CompositeDatatypeMap.compare(l1, l2, true);
+        }
+
         // XXX G and Date cases.
         int vsOrder = ValueSpace.comparisonOrder(vs1, vs2);
 
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/expr/ValueSpace.java b/jena-arq/src/main/java/org/apache/jena/sparql/expr/ValueSpace.java
index 4ac00d4dab..8440eff14c 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/expr/ValueSpace.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/expr/ValueSpace.java
@@ -18,6 +18,8 @@
 
 package org.apache.jena.sparql.expr;
 
+import org.apache.jena.cdt.CompositeDatatypeList;
+import org.apache.jena.cdt.CompositeDatatypeMap;
 import org.apache.jena.sparql.SystemARQ;
 import org.apache.jena.sparql.util.NodeUtils;
 
@@ -95,6 +97,9 @@ public enum ValueSpace {
 //    VSPACE_G_MONTH(230),
 //    VSPACE_G_DAY(240),
 
+    VSPACE_CDT_LIST(300),
+    VSPACE_CDT_MAP(301),
+
     VSPACE_SORTKEY(900),
     VSPACE_QUOTED_TRIPLE(999),      // RDF-star : Last recognized value space.
 
@@ -188,6 +193,12 @@ public enum ValueSpace {
 
         if ( nv.isSortKey() )       return VSPACE_SORTKEY ;
 
+        if ( nv.isLiteral() ) {
+            final String dtURI = nv.getDatatypeURI() ;
+            if ( CompositeDatatypeList.uri.equals(dtURI) )  return VSPACE_CDT_LIST ;
+            if ( CompositeDatatypeMap.uri.equals(dtURI) )   return VSPACE_CDT_MAP ;
+        }
+
         //if ( nv.isLiteral() )       return VSPACE_UNKNOWN ;
 
         if ( nv.isBlank() )         return VSPACE_BLANKNODE;
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/expr/aggregate/AccumulatorExpr.java b/jena-arq/src/main/java/org/apache/jena/sparql/expr/aggregate/AccumulatorExpr.java
index d8c5de3f35..4d59ad2632 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/expr/aggregate/AccumulatorExpr.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/expr/aggregate/AccumulatorExpr.java
@@ -35,7 +35,7 @@ public abstract class AccumulatorExpr implements Accumulator
     private long accCount = 0 ;
     protected long errorCount = 0 ; 
     private final Expr expr ;
-    private final boolean makeDistinct;
+    protected final boolean makeDistinct;
     
     protected AccumulatorExpr(Expr expr, boolean makeDistinct) {
         this.expr = expr;
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/expr/aggregate/AggFoldList.java b/jena-arq/src/main/java/org/apache/jena/sparql/expr/aggregate/AggFoldList.java
new file mode 100644
index 0000000000..52687ef030
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/expr/aggregate/AggFoldList.java
@@ -0,0 +1,279 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.sparql.expr.aggregate;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Objects;
+
+import org.apache.jena.atlas.data.BagFactory;
+import org.apache.jena.atlas.data.SerializationFactory;
+import org.apache.jena.atlas.data.SortedDataBag;
+import org.apache.jena.atlas.data.ThresholdPolicy;
+import org.apache.jena.atlas.data.ThresholdPolicyFactory;
+import org.apache.jena.atlas.io.IndentedLineBuffer;
+import org.apache.jena.cdt.CDTFactory;
+import org.apache.jena.cdt.CDTValue;
+import org.apache.jena.graph.Node;
+import org.apache.jena.query.ARQ;
+import org.apache.jena.query.SortCondition;
+import org.apache.jena.sparql.engine.binding.Binding;
+import org.apache.jena.sparql.engine.binding.BindingComparator;
+import org.apache.jena.sparql.expr.Expr;
+import org.apache.jena.sparql.expr.ExprList;
+import org.apache.jena.sparql.expr.NodeValue;
+import org.apache.jena.sparql.function.FunctionEnv;
+import org.apache.jena.sparql.function.library.cdt.CDTLiteralFunctionUtils;
+import org.apache.jena.sparql.serializer.SerializationContext;
+import org.apache.jena.sparql.sse.writers.WriterExpr;
+import org.apache.jena.sparql.system.SerializationFactoryFinder;
+import org.apache.jena.sparql.util.ExprUtils;
+
+public class AggFoldList extends AggregatorBase
+{
+	protected final List<SortCondition> orderBy;
+	protected final ThresholdPolicy<Binding> policy;
+	protected final Comparator<Binding> comparator;
+
+	public AggFoldList( final boolean isDistinct, final Expr expr1 ) {
+		this(isDistinct, expr1, null);
+	}
+
+	public AggFoldList( final boolean isDistinct, final Expr expr1, final List<SortCondition> orderBy ) {
+		// We need to extract the expressions from the sort conditions
+		// as well in order for them to be considered by the algebra
+		// transformer that renames variables from subqueries (see
+		// {@link TransformScopeRename#RenameByScope}).
+		super( "FOLD", isDistinct, collectExprs(expr1, orderBy) );
+
+		this.orderBy = orderBy;
+
+		if ( orderBy != null && ! orderBy.isEmpty() ) {
+			policy = ThresholdPolicyFactory.policyFromContext( ARQ.getContext() );
+			comparator = new BindingComparator(orderBy);
+		}
+		else {
+			policy = null;
+			comparator = null;
+		}
+	}
+
+	protected static ExprList collectExprs( final Expr expr1, final List<SortCondition> orderBy ) {
+		final ExprList l = new ExprList(expr1);
+
+		if ( orderBy != null ) {
+			for ( final SortCondition c : orderBy ) {
+				l.add( c.getExpression() );
+			}
+		}
+
+		return l;
+	}
+
+	@Override
+	public Aggregator copy( final ExprList exprs ) {
+		final Expr _expr1;
+		final List<SortCondition> _orderBy;
+		if ( orderBy == null ) {
+			if ( exprs.size() != 1 ) throw new IllegalArgumentException();
+
+			_expr1 = exprs.get(0);
+			_orderBy = null;
+		}
+		else {
+			if ( exprs.size() != orderBy.size()+1 ) throw new IllegalArgumentException();
+
+
+			final Iterator<SortCondition> cit = orderBy.iterator();
+			final Iterator<Expr> eit = exprs.iterator();
+
+			_expr1 = eit.next();
+
+			_orderBy = new ArrayList<>( orderBy.size() );
+			while ( eit.hasNext() ) {
+				final SortCondition c = new SortCondition( eit.next(), cit.next().getDirection() );
+				_orderBy.add(c);
+			}
+		}
+
+		return new AggFoldList(isDistinct, _expr1, _orderBy);
+	}
+
+	@Override
+	public boolean equals( final Aggregator other, final boolean bySyntax ) {
+		if ( other == null ) return false;
+		if ( this == other ) return true;
+		if ( ! ( other instanceof AggFoldList ) )
+			return false;
+		final AggFoldList fold = (AggFoldList) other;
+		return (isDistinct == fold.isDistinct)
+		       && getExprList().get(0).equals(fold.getExprList().get(0), bySyntax)
+		       && Objects.equals(orderBy, fold.orderBy);
+	}
+
+	@Override
+	public Accumulator createAccumulator() {
+		if ( orderBy != null && ! orderBy.isEmpty() )
+			return new SortingListAccumulator();
+		else
+			return new BasicListAccumulator( getExprList().get(0), isDistinct );
+	}
+
+	@Override
+	public Node getValueEmpty() {
+		final List<CDTValue> emptyList = new ArrayList<>();
+		return CDTLiteralFunctionUtils.createNode(emptyList);
+	}
+
+	@Override
+	public int hashCode() {
+		return HC_AggFoldList ^ getExpr().hashCode();
+	}
+
+	@Override
+	public String asSparqlExpr( final SerializationContext sCxt ) {
+		final IndentedLineBuffer out = new IndentedLineBuffer();
+		out.append( getName() );
+		out.append( "(" );
+
+		if ( isDistinct )
+			out.append("DISTINCT ");
+
+		ExprUtils.fmtSPARQL(out, getExprList().get(0), sCxt);
+
+		if ( orderBy != null && ! orderBy.isEmpty() ) {
+			out.append(" ORDER BY ");
+			final Iterator<SortCondition> it = orderBy.iterator();
+			while ( it.hasNext() ) {
+				it.next().output(out, sCxt);
+				if ( it.hasNext() ) out.append(", ");
+			}
+		}
+
+		out.append(")");
+		return out.asString();
+	}
+
+	@Override
+	public String toPrefixString() {
+		final IndentedLineBuffer out = new IndentedLineBuffer();
+		out.append("(");
+		out.append( getName().toLowerCase(Locale.ROOT) );
+		out.incIndent();
+
+		if ( isDistinct )
+			out.append(" distinct");
+
+		WriterExpr.output(out, getExprList().get(0), null);
+
+		if ( orderBy != null && ! orderBy.isEmpty() ) {
+			out.append(" order by ");
+			out.incIndent();
+			for ( final SortCondition c : orderBy ) {
+				c.output(out);
+			}
+			out.decIndent();
+		}
+
+		out.decIndent();
+		out.append(")");
+		return out.asString();
+	}
+
+	protected static class BasicListAccumulator extends AccumulatorExpr {
+		final protected List<CDTValue> list = new ArrayList<>();
+		protected boolean nullValueAdded = false;
+
+		protected BasicListAccumulator( final Expr expr, final boolean makeDistinct ) {
+			super(expr, makeDistinct);
+		}
+
+		@Override
+		protected void accumulate( final NodeValue nv, final Binding binding, final FunctionEnv functionEnv ) {
+			final CDTValue v = CDTFactory.createValue( nv.asNode() );
+			list.add(v);
+		}
+
+		@Override
+		protected void accumulateError( final Binding binding, final FunctionEnv functionEnv ) {
+			// By definition of FOLD, evaluation errors result in null values
+			// for the created list. If FOLD is used with the keyword DISTINCT,
+			// then all errors collapse to a single null value.
+			if ( makeDistinct && nullValueAdded ) {
+				return;
+			}
+
+			nullValueAdded = true;
+
+			final CDTValue v = CDTFactory.getNullValue();
+			list.add(v);
+		}
+
+		@Override
+		protected NodeValue getAccValue() {
+			return CDTLiteralFunctionUtils.createNodeValue(list);
+		}
+
+		// Overriding this function because the base class would otherwise not
+		// call getAccValue() in cases in which there was an error during the
+		// evaluation of the 'expr' for any of the solution mappings. For FOLD,
+		// however, errors do not cause an aggregation error but just produce
+		// null values in the created list.
+		@Override
+		public NodeValue getValue() {
+			return getAccValue();
+		}
+	}
+
+	protected class SortingListAccumulator implements Accumulator {
+		protected final SortedDataBag<Binding> sbag;
+		protected FunctionEnv functionEnv = null;
+
+		public SortingListAccumulator() {
+			final SerializationFactory<Binding> sf = SerializationFactoryFinder.bindingSerializationFactory();
+			sbag = BagFactory.newSortedBag(policy, sf, comparator);
+		}
+
+		@Override
+		public void accumulate( final Binding binding, final FunctionEnv functionEnv ) {
+			sbag.add(binding);
+
+			if ( this.functionEnv == null )
+				this.functionEnv = functionEnv;
+		}
+
+		@Override
+		public NodeValue getValue() {
+			final Iterator<Binding> it = sbag.iterator();
+			final Accumulator acc = new BasicListAccumulator( getExprList().get(0), isDistinct );
+
+			while ( it.hasNext() ) {
+				acc.accumulate( it.next(), functionEnv );
+			}
+
+			sbag.close();
+
+			return acc.getValue();
+		}
+	}
+
+}
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/expr/aggregate/AggFoldMap.java b/jena-arq/src/main/java/org/apache/jena/sparql/expr/aggregate/AggFoldMap.java
new file mode 100644
index 0000000000..923e42934b
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/expr/aggregate/AggFoldMap.java
@@ -0,0 +1,285 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.sparql.expr.aggregate;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+
+import org.apache.jena.atlas.data.BagFactory;
+import org.apache.jena.atlas.data.SerializationFactory;
+import org.apache.jena.atlas.data.SortedDataBag;
+import org.apache.jena.atlas.data.ThresholdPolicy;
+import org.apache.jena.atlas.data.ThresholdPolicyFactory;
+import org.apache.jena.atlas.io.IndentedLineBuffer;
+import org.apache.jena.cdt.CDTFactory;
+import org.apache.jena.cdt.CDTKey;
+import org.apache.jena.cdt.CDTValue;
+import org.apache.jena.graph.Node;
+import org.apache.jena.query.ARQ;
+import org.apache.jena.query.SortCondition;
+import org.apache.jena.sparql.engine.binding.Binding;
+import org.apache.jena.sparql.engine.binding.BindingComparator;
+import org.apache.jena.sparql.expr.Expr;
+import org.apache.jena.sparql.expr.ExprLib;
+import org.apache.jena.sparql.expr.ExprList;
+import org.apache.jena.sparql.expr.NodeValue;
+import org.apache.jena.sparql.function.FunctionEnv;
+import org.apache.jena.sparql.function.library.cdt.CDTLiteralFunctionUtils;
+import org.apache.jena.sparql.serializer.SerializationContext;
+import org.apache.jena.sparql.sse.writers.WriterExpr;
+import org.apache.jena.sparql.system.SerializationFactoryFinder;
+import org.apache.jena.sparql.util.ExprUtils;
+
+public class AggFoldMap extends AggregatorBase
+{
+	protected final Expr expr1;  // While the values of these member variables can also be extracted from the ExprList (see
+	protected final Expr expr2;  // createExprList below), keeping these copies here makes it easier to access these values.
+
+	protected final List<SortCondition> orderBy;
+	protected final ThresholdPolicy<Binding> policy;
+	protected final Comparator<Binding> comparator;
+
+	public AggFoldMap( final Expr expr1, final Expr expr2 ) {
+		this(expr1, expr2, null);
+	}
+
+	public AggFoldMap( final Expr expr1, final Expr expr2, final List<SortCondition> orderBy ) {
+		// We need to extract the expressions from the sort conditions
+		// as well in order for them to be considered by the algebra
+		// transformer that renames variables from subqueries (see
+		// {@link TransformScopeRename#RenameByScope}).
+		super( "FOLD", false, collectExprs(expr1, expr2, orderBy) );
+
+		this.expr1 = expr1;
+		this.expr2 = expr2;
+
+		this.orderBy = orderBy;
+
+		if ( orderBy != null && ! orderBy.isEmpty() ) {
+			policy = ThresholdPolicyFactory.policyFromContext( ARQ.getContext() );
+			comparator = new BindingComparator(orderBy);
+		}
+		else {
+			policy = null;
+			comparator = null;
+		}
+	}
+
+	protected static ExprList collectExprs( final Expr expr1, final Expr expr2, final List<SortCondition> orderBy ) {
+		final ExprList l = new ExprList();
+		l.add(expr1);
+		l.add(expr2);
+
+		if ( orderBy != null ) {
+			for ( final SortCondition c : orderBy ) {
+				l.add( c.getExpression() );
+			}
+		}
+
+		return l;
+	}
+
+	@Override
+	public Aggregator copy( final ExprList exprs ) {
+		final Expr _expr1;
+		final Expr _expr2;
+		final List<SortCondition> _orderBy;
+		if ( orderBy == null ) {
+			if ( exprs.size() != 2 ) throw new IllegalArgumentException();
+
+			_expr1 = exprs.get(0);
+			_expr2 = exprs.get(1);
+			_orderBy = null;
+		}
+		else {
+			if ( exprs.size() != orderBy.size()+2 ) throw new IllegalArgumentException();
+
+
+			final Iterator<SortCondition> cit = orderBy.iterator();
+			final Iterator<Expr> eit = exprs.iterator();
+
+			_expr1 = eit.next();
+			_expr2 = eit.next();
+
+			_orderBy = new ArrayList<>( orderBy.size() );
+			while ( eit.hasNext() ) {
+				final SortCondition c = new SortCondition( eit.next(), cit.next().getDirection() );
+				_orderBy.add(c);
+			}
+		}
+
+		return new AggFoldMap(_expr1, _expr2, _orderBy);
+	}
+
+	@Override
+	public boolean equals( final Aggregator other, final boolean bySyntax ) {
+		if ( other == null ) return false;
+		if ( this == other ) return true;
+		if ( ! ( other instanceof AggFoldMap ) )
+			return false;
+		final AggFoldMap fold = (AggFoldMap) other;
+		return exprList.equals(fold.exprList, bySyntax)
+		       && Objects.equals(orderBy, fold.orderBy);
+	}
+
+	@Override
+	public Accumulator createAccumulator() {
+		if ( orderBy != null && ! orderBy.isEmpty() )
+			return new SortingMapAccumulator();
+		else
+			return new BasicMapAccumulator();
+	}
+
+	@Override
+	public Node getValueEmpty() {
+		final Map<CDTKey,CDTValue> emptyMap = new HashMap<>();
+		return CDTLiteralFunctionUtils.createNode(emptyMap);
+	}
+
+	@Override
+	public int hashCode() {
+		int hc = HC_AggFoldMap;
+		hc ^= getExprList().get(0).hashCode();
+		hc ^= getExprList().get(1).hashCode();
+
+		return hc;
+	}
+
+	@Override
+	public String asSparqlExpr( final SerializationContext sCxt ) {
+		final IndentedLineBuffer out = new IndentedLineBuffer();
+		out.append( getName() );
+		out.append( "(" );
+
+		ExprUtils.fmtSPARQL(out, expr1, sCxt);
+		out.append(", ");
+		ExprUtils.fmtSPARQL(out, expr2, sCxt);
+
+		if ( orderBy != null && ! orderBy.isEmpty() ) {
+			out.append(" ORDER BY ");
+			final Iterator<SortCondition> it = orderBy.iterator();
+			while ( it.hasNext() ) {
+				it.next().output(out, sCxt);
+				if ( it.hasNext() ) out.append(", ");
+			}
+		}
+
+		out.append(")");
+		return out.asString();
+	}
+
+	@Override
+	public String toPrefixString() {
+		final IndentedLineBuffer out = new IndentedLineBuffer();
+		out.append("(");
+		out.append( getName().toLowerCase(Locale.ROOT) );
+		out.incIndent();
+
+		WriterExpr.output(out, expr1, null);
+		out.append(", ");
+		WriterExpr.output(out, expr2, null);
+
+		if ( orderBy != null && ! orderBy.isEmpty() ) {
+			out.append(" order by ");
+			out.incIndent();
+			for ( final SortCondition c : orderBy ) {
+				c.output(out);
+			}
+			out.decIndent();
+		}
+
+		out.decIndent();
+		out.append(")");
+		return out.asString();
+	}
+
+	protected class BasicMapAccumulator implements Accumulator {
+		final protected Map<CDTKey,CDTValue> map = new HashMap<>();
+
+		@Override
+		public void accumulate( final Binding binding, final FunctionEnv functionEnv ) {
+			final NodeValue nvKey = ExprLib.evalOrNull(expr1, binding, functionEnv);
+			if ( nvKey == null ) {
+				return; // ignore if creating the key using the given binding failed
+			}
+			if ( nvKey.isBlank() ) {
+				return; // ignore if the key would be a blank node
+			}
+
+			final CDTKey key = CDTFactory.createKey( nvKey.asNode() );
+
+			// TODO: what do we do if the map already contains an entry with the same key?
+
+			final CDTValue value;
+			final NodeValue nvValue = ExprLib.evalOrNull(expr2, binding, functionEnv);
+			if ( nvValue == null ) {
+				value = CDTFactory.getNullValue();
+			}
+			else {
+				value = CDTFactory.createValue( nvValue.asNode() );
+			}
+
+			map.put(key, value);
+		}
+
+		@Override
+		public NodeValue getValue() {
+			return CDTLiteralFunctionUtils.createNodeValue(map);
+		}
+	}
+
+	protected class SortingMapAccumulator implements Accumulator {
+		protected final SortedDataBag<Binding> sbag;
+		protected FunctionEnv functionEnv = null;
+
+		public SortingMapAccumulator() {
+			final SerializationFactory<Binding> sf = SerializationFactoryFinder.bindingSerializationFactory();
+			sbag = BagFactory.newSortedBag(policy, sf, comparator);
+		}
+
+		@Override
+		public void accumulate( final Binding binding, final FunctionEnv functionEnv ) {
+			sbag.add(binding);
+
+			if ( this.functionEnv == null )
+				this.functionEnv = functionEnv;
+		}
+
+		@Override
+		public NodeValue getValue() {
+			final Iterator<Binding> it = sbag.iterator();
+			final Accumulator acc = new BasicMapAccumulator();
+
+			while ( it.hasNext() ) {
+				acc.accumulate( it.next(), functionEnv );
+			}
+
+			sbag.close();
+
+			return acc.getValue();
+		}
+	}
+
+}
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/expr/aggregate/AggregatorBase.java b/jena-arq/src/main/java/org/apache/jena/sparql/expr/aggregate/AggregatorBase.java
index d60edb1116..4942b842f4 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/expr/aggregate/AggregatorBase.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/expr/aggregate/AggregatorBase.java
@@ -187,4 +187,7 @@ public abstract class AggregatorBase implements Aggregator
     protected static final int HC_AggMode                   =  0x184 ;
     protected static final int HC_AggModeDistinct           =  0x185 ;
 
+    protected static final int HC_AggFoldList               =  0x186 ;
+    protected static final int HC_AggFoldMap                =  0x187 ;
+
 }
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/expr/aggregate/AggregatorFactory.java b/jena-arq/src/main/java/org/apache/jena/sparql/expr/aggregate/AggregatorFactory.java
index ee99bf43e8..4d99bdeb9a 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/expr/aggregate/AggregatorFactory.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/expr/aggregate/AggregatorFactory.java
@@ -18,8 +18,11 @@
 
 package org.apache.jena.sparql.expr.aggregate ;
 
+import java.util.List;
+
 import org.apache.jena.atlas.lib.NotImplemented ;
 import org.apache.jena.atlas.logging.Log ;
+import org.apache.jena.query.SortCondition;
 import org.apache.jena.sparql.expr.Expr ;
 import org.apache.jena.sparql.expr.ExprList ;
 
@@ -71,6 +74,13 @@ public class AggregatorFactory {
         return new AggNull() ;
     }
 
+    public static Aggregator createFold(boolean distinct, Expr expr1, Expr expr2, List<SortCondition> orderBy) {
+        if ( expr2 == null )
+            return new AggFoldList(distinct, expr1, orderBy) ;
+        else
+            return new AggFoldMap(expr1, expr2, orderBy) ;
+    }
+
     public static Aggregator createCustom(String iri, Args a) {
         return createCustom(iri, a.distinct, ExprList.copy(a)) ;
     }
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/ARQFunctions.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/ARQFunctions.java
index a7452a464a..9c76c03723 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/function/ARQFunctions.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/ARQFunctions.java
@@ -19,6 +19,7 @@
 package org.apache.jena.sparql.function;
 
 import org.apache.jena.sparql.ARQConstants;
+import org.apache.jena.sparql.function.library.cdt.CDTLiteralFunctions;
 import org.apache.jena.sparql.function.library.triple.TripleTermFunctions;
 
 /** Load ARQ functions ().
@@ -33,5 +34,6 @@ public class ARQFunctions {
 
     public static void load(FunctionRegistry reg) {
         TripleTermFunctions.register(reg);
+        CDTLiteralFunctions.register(reg);
     }
 }
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/CDTLiteralFunctionUtils.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/CDTLiteralFunctionUtils.java
new file mode 100644
index 0000000000..d7d5f14674
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/CDTLiteralFunctionUtils.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.sparql.function.library.cdt;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.jena.cdt.CDTKey;
+import org.apache.jena.cdt.CDTValue;
+import org.apache.jena.cdt.CompositeDatatypeList;
+import org.apache.jena.cdt.CompositeDatatypeMap;
+import org.apache.jena.datatypes.DatatypeFormatException;
+import org.apache.jena.graph.Node;
+import org.apache.jena.graph.NodeFactory;
+import org.apache.jena.graph.impl.LiteralLabel;
+import org.apache.jena.sparql.expr.ExprEvalException;
+import org.apache.jena.sparql.expr.NodeValue;
+
+public class CDTLiteralFunctionUtils
+{
+	/**
+	 * Uses {@link CompositeDatatypeList#isListLiteral(Node)} to check whether
+	 * the given node is a cdt:List literal and throws an exception if not.
+	 */
+	public static final void ensureListLiteral( final Node n ) throws ExprEvalException {
+		if ( ! CompositeDatatypeList.isListLiteral(n) )
+			throw new ExprEvalException("Not a cdt:List literal: " + n);
+	}
+
+	/**
+	 * Uses {@link CompositeDatatypeMap#isMapLiteral(Node)} to check whether
+	 * the given node is a cdt:Map literal and throws an exception if not.
+	 */
+	public static final void ensureMapLiteral( final Node n ) throws ExprEvalException {
+		if ( ! CompositeDatatypeMap.isMapLiteral(n) )
+			throw new ExprEvalException("Not a cdt:Map literal: " + n);
+	}
+
+	/**
+	 * Assumes that the given node is a cdt:List literal and uses
+	 * {@link CompositeDatatypeList#getValue(LiteralLabel)} to get
+	 * the list.
+	 * 
+	 * Throws an ExprEvalException if a DatatypeFormatException is
+	 * thrown by {@link CompositeDatatypeList#getValue(LiteralLabel)}.
+	 */
+	public static final List<CDTValue> getList( final Node n ) throws ExprEvalException {
+		if ( ! n.getLiteral().isWellFormed() )
+			throw new ExprEvalException("Not a well-formed cdt:Map literal: " + n);
+
+		try {
+			return CompositeDatatypeList.getValue( n.getLiteral() );
+		}
+		catch ( final DatatypeFormatException ex ) {
+			throw new ExprEvalException("Not a well-formed cdt:List literal: " + n, ex);
+		}
+	}
+
+	/**
+	 * Assumes that the given node is a cdt:Map literal and uses
+	 * {@link CompositeDatatypeMap#getValue(LiteralLabel)} to get
+	 * the map.
+	 * 
+	 * Throws an ExprEvalException if a DatatypeFormatException is
+	 * thrown by {@link CompositeDatatypeMap#getValue(LiteralLabel)}.
+	 */
+	public static final Map<CDTKey,CDTValue> getMap( final Node n ) throws ExprEvalException {
+		if ( ! n.getLiteral().isWellFormed() )
+			throw new ExprEvalException("Not a well-formed cdt:Map literal: " + n);
+
+		try {
+			return CompositeDatatypeMap.getValue( n.getLiteral() );
+		}
+		catch ( final DatatypeFormatException ex ) {
+			throw new ExprEvalException("Not a well-formed cdt:Map literal: " + n, ex);
+		}
+	}
+
+	/**
+	 * Calls {@link #ensureListLiteral(Node)} first, and {@link #getList(Node)}
+	 * afterwards.
+	 */
+	public static final List<CDTValue> checkAndGetList( final Node n ) throws ExprEvalException {
+		ensureListLiteral(n);
+		return getList(n);
+	}
+
+	/**
+	 * Calls {@link #ensureMapLiteral(Node)} first, and {@link #getMap(Node)}
+	 * afterwards.
+	 */
+	public static final Map<CDTKey,CDTValue> checkAndGetMap( final Node n ) throws ExprEvalException {
+		ensureMapLiteral(n);
+		return getMap(n);
+	}
+
+	public static final List<CDTValue> checkAndGetList( final NodeValue nv ) throws ExprEvalException {
+		return checkAndGetList( nv.asNode() );
+	}
+
+	public static final Map<CDTKey,CDTValue> checkAndGetMap( final NodeValue nv ) throws ExprEvalException {
+		return checkAndGetMap( nv.asNode() );
+	}
+
+	/**
+	 * Creates a {@link NodeV} with a cdt:List literal that represents the
+	 * given list.
+	 */
+	public static final Node createNode( final List<CDTValue> list ) {
+		return NodeFactory.createLiteralByValue(list, CompositeDatatypeList.type);
+	}
+
+	/**
+	 * Creates a {@link Node} with a cdt:Map literal that represents the
+	 * given map.
+	 */
+	public static final Node createNode( final Map<CDTKey,CDTValue> map ) {
+		return NodeFactory.createLiteralByValue(map, CompositeDatatypeMap.type);
+	}
+
+	/**
+	 * Creates a {@link NodeValue} with a cdt:List literal that represents the
+	 * given list.
+	 */
+	public static final NodeValue createNodeValue( final List<CDTValue> list ) {
+		return NodeValue.makeNode( createNode(list) );
+	}
+
+	/**
+	 * Creates a {@link NodeValue} with a cdt:Map literal that represents the
+	 * given map.
+	 */
+	public static final NodeValue createNodeValue( final Map<CDTKey,CDTValue> map ) {
+		return NodeValue.makeNode( createNode(map) );
+	}
+
+}
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/CDTLiteralFunctions.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/CDTLiteralFunctions.java
new file mode 100644
index 0000000000..35b2e78a78
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/CDTLiteralFunctions.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.sparql.function.library.cdt;
+
+import org.apache.jena.sparql.ARQConstants;
+import org.apache.jena.sparql.function.FunctionRegistry;
+
+public class CDTLiteralFunctions {
+	public static void register( final FunctionRegistry functionRegistry ) {
+		functionRegistry.put( ARQConstants.CDTFunctionLibraryURI + "concat",  ConcatFct.class );
+		functionRegistry.put( ARQConstants.CDTFunctionLibraryURI + "contains",     ContainsFct.class );
+		functionRegistry.put( ARQConstants.CDTFunctionLibraryURI + "containsKey",  ContainsKeyFct.class );
+		functionRegistry.put( ARQConstants.CDTFunctionLibraryURI + "containsTerm", ContainsTermFct.class );
+		functionRegistry.put( ARQConstants.CDTFunctionLibraryURI + "get",     GetFct.class );
+		functionRegistry.put( ARQConstants.CDTFunctionLibraryURI + "head",    HeadFct.class );
+		functionRegistry.put( ARQConstants.CDTFunctionLibraryURI + "keys",    KeysFct.class );
+		functionRegistry.put( ARQConstants.CDTFunctionLibraryURI + "List",    ListFct.class );
+		functionRegistry.put( ARQConstants.CDTFunctionLibraryURI + "Map",     MapFct.class );
+		functionRegistry.put( ARQConstants.CDTFunctionLibraryURI + "merge",   MergeFct.class );
+		functionRegistry.put( ARQConstants.CDTFunctionLibraryURI + "put",     PutFct.class );
+		functionRegistry.put( ARQConstants.CDTFunctionLibraryURI + "remove",  RemoveFct.class );
+		functionRegistry.put( ARQConstants.CDTFunctionLibraryURI + "reverse", ReverseFct.class );
+		functionRegistry.put( ARQConstants.CDTFunctionLibraryURI + "size",    SizeFct.class );
+		functionRegistry.put( ARQConstants.CDTFunctionLibraryURI + "subseq",  SubSeqFct.class );
+		functionRegistry.put( ARQConstants.CDTFunctionLibraryURI + "tail",    TailFct.class );
+	}
+}
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/ConcatFct.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/ConcatFct.java
new file mode 100644
index 0000000000..6a0dba2350
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/ConcatFct.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.sparql.function.library.cdt;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.jena.cdt.CDTValue;
+import org.apache.jena.sparql.expr.ExprList;
+import org.apache.jena.sparql.expr.NodeValue;
+import org.apache.jena.sparql.function.FunctionBase;
+
+public class ConcatFct extends FunctionBase
+{
+	@Override
+	public void checkBuild( final String uri, final ExprList args ) {
+		// nothing to check
+	}
+
+	@Override
+	public NodeValue exec( final List<NodeValue> args ) {
+		if ( args.isEmpty() ) {
+			final List<CDTValue> result = Collections.emptyList();
+			return CDTLiteralFunctionUtils.createNodeValue(result);
+		}
+
+		if ( args.size() == 1 ) {
+			final NodeValue nv =  args.get(0);
+			// make sure that the argument is a well-formed cdt:List literal
+			CDTLiteralFunctionUtils.checkAndGetList(nv);
+
+			return nv;
+		}
+
+		final List<CDTValue> result = new ArrayList<>();
+		for ( final NodeValue nv : args ) {
+			final List<CDTValue> l = CDTLiteralFunctionUtils.checkAndGetList(nv);
+			result.addAll(l);
+		}
+
+		return CDTLiteralFunctionUtils.createNodeValue(result);
+	}
+
+}
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/ContainsFct.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/ContainsFct.java
new file mode 100644
index 0000000000..90aa9217a8
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/ContainsFct.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.sparql.function.library.cdt;
+
+import java.util.List;
+
+import org.apache.jena.cdt.CDTValue;
+import org.apache.jena.sparql.expr.NodeValue;
+import org.apache.jena.sparql.function.FunctionBase2;
+
+public class ContainsFct extends FunctionBase2
+{
+	@Override
+	public NodeValue exec( final NodeValue nv1, final NodeValue nv2 ) {
+		final List<CDTValue> list = CDTLiteralFunctionUtils.checkAndGetList(nv1);
+		final boolean result = containsNode( list, nv2 );
+		return NodeValue.booleanReturn(result);
+	}
+
+	/**
+	 * Returns true if the given list contains the given RDF term.
+	 */
+	protected boolean containsNode( final List<CDTValue> list, final NodeValue nv ) {
+		for ( final CDTValue v : list ) {
+			if ( v.isNode() ) {
+				final NodeValue vv = NodeValue.makeNode( v.asNode() );
+				if ( NodeValue.sameValueAs(vv, nv) ) {
+					return true;
+				}
+			}
+		}
+
+		return false;
+	}
+
+}
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/ContainsKeyFct.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/ContainsKeyFct.java
new file mode 100644
index 0000000000..88a46fd2d0
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/ContainsKeyFct.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.sparql.function.library.cdt;
+
+import java.util.Map;
+
+import org.apache.jena.cdt.CDTFactory;
+import org.apache.jena.cdt.CDTKey;
+import org.apache.jena.cdt.CDTValue;
+import org.apache.jena.graph.Node;
+import org.apache.jena.sparql.expr.NodeValue;
+import org.apache.jena.sparql.function.FunctionBase2;
+
+public class ContainsKeyFct extends FunctionBase2
+{
+	@Override
+	public NodeValue exec( final NodeValue nv1, final NodeValue nv2 ) {
+		final Map<CDTKey,CDTValue> map = CDTLiteralFunctionUtils.checkAndGetMap(nv1);
+
+		final Node n2 = nv2.asNode();
+
+		// If the second argument is not an IRI or a literal, then it cannot be
+		// a map key in the given map and, by definition of cdt:containsKey, we
+		// have to return false.
+		if ( ! n2.isURI() && ! n2.isLiteral() )
+			return NodeValue.booleanReturn(false);
+
+		if ( map.isEmpty() )
+			return NodeValue.booleanReturn(false);
+
+		final CDTKey key = CDTFactory.createKey(n2);
+		final CDTValue value = map.get(key);
+
+		return NodeValue.booleanReturn( value != null );
+	}
+
+}
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/ContainsTermFct.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/ContainsTermFct.java
new file mode 100644
index 0000000000..e5fc0421f4
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/ContainsTermFct.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.sparql.function.library.cdt;
+
+import java.util.List;
+
+import org.apache.jena.cdt.CDTValue;
+import org.apache.jena.sparql.expr.NodeValue;
+import org.apache.jena.sparql.expr.nodevalue.NodeFunctions;
+import org.apache.jena.sparql.function.FunctionBase2;
+
+public class ContainsTermFct extends FunctionBase2
+{
+	@Override
+	public NodeValue exec( final NodeValue nv1, final NodeValue nv2 ) {
+		final List<CDTValue> list = CDTLiteralFunctionUtils.checkAndGetList(nv1);
+		final boolean result = containsTerm( list, nv2 );
+		return NodeValue.booleanReturn(result);
+	}
+
+	/**
+	 * Returns true if the given list contains the given RDF term.
+	 */
+	protected boolean containsTerm( final List<CDTValue> list, final NodeValue nv ) {
+		for ( final CDTValue v : list ) {
+			if ( v.isNode() ) {
+				if ( NodeFunctions.sameTerm(v.asNode(), nv.asNode()) ) {
+					return true;
+				}
+			}
+		}
+
+		return false;
+	}
+
+}
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/ARQFunctions.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/FunctionBase1List.java
similarity index 58%
copy from jena-arq/src/main/java/org/apache/jena/sparql/function/ARQFunctions.java
copy to jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/FunctionBase1List.java
index a7452a464a..0b2af54810 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/function/ARQFunctions.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/FunctionBase1List.java
@@ -16,22 +16,21 @@
  * limitations under the License.
  */
 
-package org.apache.jena.sparql.function;
+package org.apache.jena.sparql.function.library.cdt;
 
-import org.apache.jena.sparql.ARQConstants;
-import org.apache.jena.sparql.function.library.triple.TripleTermFunctions;
+import java.util.List;
 
-/** Load ARQ functions ().
- * The one in package {@link org.apache.jena.sparql.function.library}
- * can be invoked by afn:name without registration because afn:-&gt;java: is built-in
- * in MappedLoader.
- *
- * @see ARQConstants#ARQFunctionLibraryURI
- * @see ARQConstants#ARQFunctionLibrary
- */
-public class ARQFunctions {
+import org.apache.jena.cdt.CDTValue;
+import org.apache.jena.sparql.expr.NodeValue;
+import org.apache.jena.sparql.function.FunctionBase1;
+
+public abstract class FunctionBase1List extends FunctionBase1
+{
+	@Override
+	public NodeValue exec( final NodeValue nv ) {
+		final List<CDTValue> list = CDTLiteralFunctionUtils.checkAndGetList(nv);
+		return _exec(list, nv);
+	}
 
-    public static void load(FunctionRegistry reg) {
-        TripleTermFunctions.register(reg);
-    }
+	protected abstract NodeValue _exec( List<CDTValue> list, NodeValue nvList );
 }
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/GetFct.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/GetFct.java
new file mode 100644
index 0000000000..24bcbfcfb5
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/GetFct.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.sparql.function.library.cdt;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.jena.cdt.CDTFactory;
+import org.apache.jena.cdt.CDTKey;
+import org.apache.jena.cdt.CDTValue;
+import org.apache.jena.cdt.CompositeDatatypeList;
+import org.apache.jena.cdt.CompositeDatatypeMap;
+import org.apache.jena.graph.Node;
+import org.apache.jena.sparql.expr.ExprEvalException;
+import org.apache.jena.sparql.expr.NodeValue;
+import org.apache.jena.sparql.function.FunctionBase2;
+
+public class GetFct extends FunctionBase2
+{
+	@Override
+	public NodeValue exec( final NodeValue nv1, final NodeValue nv2 ) {
+		final Node n1 = nv1.asNode();
+
+		if ( CompositeDatatypeList.isListLiteral(n1) )
+			return getFromList(n1, nv2);
+
+		if ( CompositeDatatypeMap.isMapLiteral(n1) )
+			return getFromMap(n1, nv2);
+
+		throw new ExprEvalException("Neither a list nor a map literal: " + nv1);
+	}
+
+	protected NodeValue getFromList( final Node n1, final NodeValue nv2 ) {
+		if ( ! nv2.isInteger() )
+			throw new ExprEvalException("Not an integer literal: " + nv2);
+
+		final int index = nv2.getInteger().intValue();
+
+		if ( index < 1 )
+			throw new ExprEvalException("Out of bounds index value: " + nv2);
+
+		final List<CDTValue> list = CDTLiteralFunctionUtils.getList(n1);
+
+		if ( index > list.size() )
+			throw new ExprEvalException("Out of bounds index value: " + nv2);
+
+		final CDTValue value = list.get( index-1 );
+		if ( value.isNull() ) {
+			throw new ExprEvalException("accessing null value from list" );
+		}
+		else if ( value.isNode() ) {
+			return NodeValue.makeNode( value.asNode() );
+		}
+		else {
+			throw new ExprEvalException("Unexpected type of CDTValue: " + value.getClass().getName() );
+		}
+	}
+
+	protected NodeValue getFromMap( final Node n1, final NodeValue nv2 ) {
+		final Node n2 = nv2.asNode();
+		if ( ! n2.isURI() && ! n2.isLiteral() )
+			throw new ExprEvalException("Not a valid map key: " + nv2);
+
+		final Map<CDTKey,CDTValue> map = CDTLiteralFunctionUtils.getMap(n1);
+
+		if ( map.isEmpty() )
+			throw new ExprEvalException("empty map");
+
+		final CDTKey key = CDTFactory.createKey(n2);
+		final CDTValue value = map.get(key);
+
+		if ( value == null ) {
+			throw new ExprEvalException("key is not in the map");
+		}
+		else if ( value.isNull() ) {
+			throw new ExprEvalException("value for key is in null");
+		}
+		else if ( value.isNode() ) {
+			return NodeValue.makeNode( value.asNode() );
+		}
+		else {
+			throw new ExprEvalException("Unexpected type of CDTValue: " + value.getClass().getName() );
+		}
+	}
+
+}
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/ARQFunctions.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/HeadFct.java
similarity index 51%
copy from jena-arq/src/main/java/org/apache/jena/sparql/function/ARQFunctions.java
copy to jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/HeadFct.java
index a7452a464a..0eb1348f98 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/function/ARQFunctions.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/HeadFct.java
@@ -16,22 +16,31 @@
  * limitations under the License.
  */
 
-package org.apache.jena.sparql.function;
+package org.apache.jena.sparql.function.library.cdt;
 
-import org.apache.jena.sparql.ARQConstants;
-import org.apache.jena.sparql.function.library.triple.TripleTermFunctions;
+import java.util.List;
 
-/** Load ARQ functions ().
- * The one in package {@link org.apache.jena.sparql.function.library}
- * can be invoked by afn:name without registration because afn:-&gt;java: is built-in
- * in MappedLoader.
- *
- * @see ARQConstants#ARQFunctionLibraryURI
- * @see ARQConstants#ARQFunctionLibrary
- */
-public class ARQFunctions {
+import org.apache.jena.cdt.CDTValue;
+import org.apache.jena.sparql.expr.ExprEvalException;
+import org.apache.jena.sparql.expr.NodeValue;
+
+public class HeadFct extends FunctionBase1List
+{
+	@Override
+	protected NodeValue _exec( final List<CDTValue> list, final NodeValue nvList ) {
+		if ( list.size() == 0 )
+			throw new ExprEvalException("Empty list");
+
+		final CDTValue value = list.get(0);
+		if ( value.isNull() ) {
+			throw new ExprEvalException("accessing null value from list" );
+		}
+		else if ( value.isNode() ) {
+			return NodeValue.makeNode( value.asNode() );
+		}
+		else {
+			throw new ExprEvalException("Unexpected type of CDTValue: " + value.getClass().getName() );
+		}
+	}
 
-    public static void load(FunctionRegistry reg) {
-        TripleTermFunctions.register(reg);
-    }
 }
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/ARQFunctions.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/KeysFct.java
similarity index 51%
copy from jena-arq/src/main/java/org/apache/jena/sparql/function/ARQFunctions.java
copy to jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/KeysFct.java
index a7452a464a..2c919eb934 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/function/ARQFunctions.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/KeysFct.java
@@ -16,22 +16,31 @@
  * limitations under the License.
  */
 
-package org.apache.jena.sparql.function;
+package org.apache.jena.sparql.function.library.cdt;
 
-import org.apache.jena.sparql.ARQConstants;
-import org.apache.jena.sparql.function.library.triple.TripleTermFunctions;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
 
-/** Load ARQ functions ().
- * The one in package {@link org.apache.jena.sparql.function.library}
- * can be invoked by afn:name without registration because afn:-&gt;java: is built-in
- * in MappedLoader.
- *
- * @see ARQConstants#ARQFunctionLibraryURI
- * @see ARQConstants#ARQFunctionLibrary
- */
-public class ARQFunctions {
+import org.apache.jena.cdt.CDTFactory;
+import org.apache.jena.cdt.CDTKey;
+import org.apache.jena.cdt.CDTValue;
+import org.apache.jena.sparql.expr.NodeValue;
+import org.apache.jena.sparql.function.FunctionBase1;
+
+public class KeysFct extends FunctionBase1
+{
+	@Override
+	public NodeValue exec( final NodeValue nv ) {
+		final Map<CDTKey,CDTValue> map = CDTLiteralFunctionUtils.checkAndGetMap(nv);
+
+		final List<CDTValue> list = new ArrayList<>( map.size() );
+		for ( final CDTKey key : map.keySet() ) {
+			final CDTValue value = CDTFactory.createValue( key.asNode() );
+			list.add(value);
+		}
+
+		return CDTLiteralFunctionUtils.createNodeValue(list);
+	}
 
-    public static void load(FunctionRegistry reg) {
-        TripleTermFunctions.register(reg);
-    }
 }
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/ListFct.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/ListFct.java
new file mode 100644
index 0000000000..dc50559d9f
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/ListFct.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.sparql.function.library.cdt;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.jena.cdt.CDTFactory;
+import org.apache.jena.cdt.CDTValue;
+import org.apache.jena.sparql.engine.binding.Binding;
+import org.apache.jena.sparql.expr.Expr;
+import org.apache.jena.sparql.expr.ExprException;
+import org.apache.jena.sparql.expr.ExprList;
+import org.apache.jena.sparql.expr.NodeValue;
+import org.apache.jena.sparql.function.FunctionBase;
+import org.apache.jena.sparql.function.FunctionEnv;
+
+public class ListFct extends FunctionBase
+{
+	@Override
+	public void checkBuild( final String uri, final ExprList args ) {
+		// nothing to do here
+	}
+
+	@Override
+	public NodeValue exec(Binding binding, ExprList args, String uri, FunctionEnv env) {
+		final List<CDTValue> list = new ArrayList<>( args.size() );
+
+		for ( final Expr e : args ) {
+			CDTValue v;
+			try {
+				final NodeValue nv = e.eval(binding, env);
+				v = CDTFactory.createValue( nv.asNode() );
+			} catch ( final ExprException ex ) {
+				v = CDTFactory.getNullValue();
+			}
+
+			list.add(v);
+		}
+
+		return CDTLiteralFunctionUtils.createNodeValue(list);
+	}
+
+	@Override
+	public NodeValue exec( final List<NodeValue> args ) {
+		throw new IllegalStateException("should never end up here");
+	}
+
+}
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/MapFct.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/MapFct.java
new file mode 100644
index 0000000000..fec5c22052
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/MapFct.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.sparql.function.library.cdt;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.jena.atlas.lib.Lib;
+import org.apache.jena.cdt.CDTFactory;
+import org.apache.jena.cdt.CDTKey;
+import org.apache.jena.cdt.CDTValue;
+import org.apache.jena.graph.Node;
+import org.apache.jena.query.QueryBuildException;
+import org.apache.jena.sparql.engine.binding.Binding;
+import org.apache.jena.sparql.expr.Expr;
+import org.apache.jena.sparql.expr.ExprException;
+import org.apache.jena.sparql.expr.ExprList;
+import org.apache.jena.sparql.expr.NodeValue;
+import org.apache.jena.sparql.function.FunctionBase;
+import org.apache.jena.sparql.function.FunctionEnv;
+
+public class MapFct extends FunctionBase
+{
+	@Override
+	public void checkBuild( final String uri, final ExprList args ) {
+		if ( args.size() % 2 == 1 )
+			throw new QueryBuildException("Function '"+Lib.className(this)+"' takes an even number of arguments");
+	}
+
+	@Override
+	public NodeValue exec(Binding binding, ExprList args, String uri, FunctionEnv env) {
+		if ( args.size() % 2 == 1 )
+			throw new ExprException("Function '"+Lib.className(this)+"' takes an even number of arguments");
+
+		final Map<CDTKey,CDTValue> map = new HashMap<>();
+
+		final Iterator<Expr> it = args.iterator();
+		while ( it.hasNext() ) {
+			final Expr exprKey = it.next();
+			final Expr exprValue = it.next();
+
+			final CDTKey key = getKey(exprKey, binding, env);
+			if ( key != null ) {
+				final CDTValue value = getValue(exprValue, binding, env);
+				map.put(key, value);
+			}
+		}
+
+		return CDTLiteralFunctionUtils.createNodeValue(map);
+	}
+
+	protected CDTKey getKey( final Expr e, final Binding binding, final FunctionEnv env ) {
+		final NodeValue nv;
+		try {
+			nv = e.eval(binding, env);
+		} catch ( final ExprException ex ) {
+			return null;
+		}
+
+		final Node n = nv.asNode();
+		if ( ! n.isURI() && ! n.isLiteral() )
+			return null;
+
+		return CDTFactory.createKey(n);
+	}
+
+	protected CDTValue getValue( final Expr e, final Binding binding, final FunctionEnv env ) {
+		final NodeValue nv;
+		try {
+			nv = e.eval(binding, env);
+		} catch ( final ExprException ex ) {
+			return CDTFactory.getNullValue();
+		}
+
+		return CDTFactory.createValue( nv.asNode() );
+	}
+
+
+	@Override
+	public NodeValue exec( final List<NodeValue> args ) {
+		throw new IllegalStateException("should never end up here");
+	}
+
+}
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/ARQFunctions.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/MergeFct.java
similarity index 50%
copy from jena-arq/src/main/java/org/apache/jena/sparql/function/ARQFunctions.java
copy to jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/MergeFct.java
index a7452a464a..344f3117fa 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/function/ARQFunctions.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/MergeFct.java
@@ -16,22 +16,33 @@
  * limitations under the License.
  */
 
-package org.apache.jena.sparql.function;
+package org.apache.jena.sparql.function.library.cdt;
 
-import org.apache.jena.sparql.ARQConstants;
-import org.apache.jena.sparql.function.library.triple.TripleTermFunctions;
+import java.util.HashMap;
+import java.util.Map;
 
-/** Load ARQ functions ().
- * The one in package {@link org.apache.jena.sparql.function.library}
- * can be invoked by afn:name without registration because afn:-&gt;java: is built-in
- * in MappedLoader.
- *
- * @see ARQConstants#ARQFunctionLibraryURI
- * @see ARQConstants#ARQFunctionLibrary
- */
-public class ARQFunctions {
+import org.apache.jena.cdt.CDTKey;
+import org.apache.jena.cdt.CDTValue;
+import org.apache.jena.sparql.expr.NodeValue;
+import org.apache.jena.sparql.function.FunctionBase2;
+
+public class MergeFct extends FunctionBase2
+{
+	@Override
+	public NodeValue exec( final NodeValue nv1, final NodeValue nv2 ) {
+		final Map<CDTKey,CDTValue> map1 = CDTLiteralFunctionUtils.checkAndGetMap(nv1);
+		final Map<CDTKey,CDTValue> map2 = CDTLiteralFunctionUtils.checkAndGetMap(nv2);
+
+		if ( map1.isEmpty() )
+			return nv2;
+
+		if ( map2.isEmpty() )
+			return nv1;
+
+		final Map<CDTKey,CDTValue> newMap = new HashMap<>(map2);
+		newMap.putAll(map1);
+
+		return CDTLiteralFunctionUtils.createNodeValue(newMap);
+	}
 
-    public static void load(FunctionRegistry reg) {
-        TripleTermFunctions.register(reg);
-    }
 }
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/PutFct.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/PutFct.java
new file mode 100644
index 0000000000..428c000d5b
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/PutFct.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.sparql.function.library.cdt;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.jena.atlas.lib.Lib;
+import org.apache.jena.cdt.CDTFactory;
+import org.apache.jena.cdt.CDTKey;
+import org.apache.jena.cdt.CDTValue;
+import org.apache.jena.graph.Node;
+import org.apache.jena.query.QueryBuildException;
+import org.apache.jena.sparql.engine.binding.Binding;
+import org.apache.jena.sparql.expr.ExprEvalException;
+import org.apache.jena.sparql.expr.ExprException;
+import org.apache.jena.sparql.expr.ExprList;
+import org.apache.jena.sparql.expr.NodeValue;
+import org.apache.jena.sparql.function.FunctionBase;
+import org.apache.jena.sparql.function.FunctionEnv;
+
+public class PutFct extends FunctionBase
+{
+	@Override
+	public void checkBuild( final String uri, final ExprList args ) {
+		if ( args.size() < 2 || args.size() > 3 )
+			throw new QueryBuildException("Function '"+Lib.className(this)+"' takes two or three arguments");
+	}
+
+	@Override
+	public NodeValue exec(Binding binding, ExprList args, String uri, FunctionEnv env) {
+		if ( args.size() < 2 || args.size() > 3 )
+			throw new ExprException("wrong number of arguments (" + args.size() + "), must be 2 or 3");
+
+		// check the second argument first because that's less expensive
+		final NodeValue nv2 = args.get(1).eval(binding, env);
+		final Node n2 = nv2.asNode();
+		if ( ! n2.isURI() && ! n2.isLiteral() )
+			throw new ExprEvalException("Not a valid map key: " + nv2);
+
+		// now check the first argument
+		final NodeValue nv1 = args.get(0).eval(binding, env);
+		final Map<CDTKey,CDTValue> map = CDTLiteralFunctionUtils.checkAndGetMap(nv1);
+
+		final CDTKey key = CDTFactory.createKey(n2);
+
+		// produce a map value from the third argument (if any)
+		final CDTValue newValue;
+		if ( args.size() == 2 ) {
+			newValue = CDTFactory.getNullValue();
+		}
+		else {  // in this case, we have that args.size() == 3
+			NodeValue nv3 = null;
+			try {
+				nv3 = args.get(2).eval(binding, env);
+			}
+			catch ( final ExprException ex ) {
+				// nothing to do here
+			}
+
+			if ( nv3 != null ) {
+				newValue = CDTFactory.createValue( nv3.asNode() );
+			}
+			else {
+				newValue = CDTFactory.getNullValue();
+			}
+		}
+
+		// check if the given map already contains the exact same map entry
+		// if so, simply return the given cdt:Map literal
+		final CDTValue oldValue = map.get(key);
+		if ( oldValue != null ) {
+			if ( oldValue.isNull() && newValue.isNull() )
+				return nv1;
+
+			if ( ! oldValue.isNull() && ! newValue.isNull() ) {
+				final Node on = oldValue.asNode();
+				final Node nn = newValue.asNode();
+				if ( on.equals(nn) )
+					return nv1;
+			}
+		}
+
+		final Map<CDTKey,CDTValue> newMap = new HashMap<>(map);
+		newMap.put(key, newValue);
+
+		return CDTLiteralFunctionUtils.createNodeValue(newMap);
+	}
+
+	@Override
+	public NodeValue exec( final List<NodeValue> args ) {
+		throw new IllegalStateException("should never end up here");
+	}
+
+}
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/RemoveFct.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/RemoveFct.java
new file mode 100644
index 0000000000..b5df5a5a29
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/RemoveFct.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.sparql.function.library.cdt;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.jena.cdt.CDTFactory;
+import org.apache.jena.cdt.CDTKey;
+import org.apache.jena.cdt.CDTValue;
+import org.apache.jena.graph.Node;
+import org.apache.jena.sparql.expr.NodeValue;
+import org.apache.jena.sparql.function.FunctionBase2;
+
+public class RemoveFct extends FunctionBase2
+{
+	@Override
+	public NodeValue exec( final NodeValue nv1, final NodeValue nv2 ) {
+		final Map<CDTKey,CDTValue> map = CDTLiteralFunctionUtils.checkAndGetMap(nv1);
+
+		if ( map.isEmpty() )
+			return nv1;
+
+		final Node n2 = nv2.asNode();
+
+		// If the second term is not a map key (and the first term is a well-
+		// formed cdt:Map literal), the first term needs to be returned as is.
+		if ( ! n2.isURI() && ! n2.isLiteral() )
+			return nv1;
+
+		final CDTKey key = CDTFactory.createKey(n2);
+
+		if ( ! map.containsKey(key) )
+			return nv1;
+
+		final Map<CDTKey,CDTValue> newMap = new HashMap<>(map);
+		newMap.remove(key);
+
+		return CDTLiteralFunctionUtils.createNodeValue(newMap);
+	}
+
+}
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/ARQFunctions.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/ReverseFct.java
similarity index 54%
copy from jena-arq/src/main/java/org/apache/jena/sparql/function/ARQFunctions.java
copy to jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/ReverseFct.java
index a7452a464a..9b918036c7 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/function/ARQFunctions.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/ReverseFct.java
@@ -16,22 +16,30 @@
  * limitations under the License.
  */
 
-package org.apache.jena.sparql.function;
+package org.apache.jena.sparql.function.library.cdt;
 
-import org.apache.jena.sparql.ARQConstants;
-import org.apache.jena.sparql.function.library.triple.TripleTermFunctions;
+import java.util.Arrays;
+import java.util.List;
 
-/** Load ARQ functions ().
- * The one in package {@link org.apache.jena.sparql.function.library}
- * can be invoked by afn:name without registration because afn:-&gt;java: is built-in
- * in MappedLoader.
- *
- * @see ARQConstants#ARQFunctionLibraryURI
- * @see ARQConstants#ARQFunctionLibrary
- */
-public class ARQFunctions {
+import org.apache.jena.cdt.CDTValue;
+import org.apache.jena.sparql.expr.NodeValue;
+
+public class ReverseFct extends FunctionBase1List
+{
+	@Override
+	protected NodeValue _exec( final List<CDTValue> list, final NodeValue nvList ) {
+		if ( list.size() < 2 )
+			return nvList;
+
+		final CDTValue[] reverseArray = new CDTValue[ list.size() ];
+		int i = list.size() - 1;
+		for ( final CDTValue v : list ) {
+			reverseArray[i] = v;
+			i--;
+		}
+
+		final List<CDTValue> reverseList = Arrays.asList(reverseArray);
+		return CDTLiteralFunctionUtils.createNodeValue(reverseList);
+	}
 
-    public static void load(FunctionRegistry reg) {
-        TripleTermFunctions.register(reg);
-    }
 }
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/SizeFct.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/SizeFct.java
new file mode 100644
index 0000000000..e0bb791c07
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/SizeFct.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.sparql.function.library.cdt;
+
+import org.apache.jena.cdt.CompositeDatatypeList;
+import org.apache.jena.cdt.CompositeDatatypeMap;
+import org.apache.jena.graph.Node;
+import org.apache.jena.sparql.expr.ExprEvalException;
+import org.apache.jena.sparql.expr.NodeValue;
+import org.apache.jena.sparql.function.FunctionBase1;
+
+public class SizeFct extends FunctionBase1
+{
+	@Override
+	public NodeValue exec( final NodeValue nv ) {
+		final Node n = nv.asNode();
+
+		final int size;
+		if ( CompositeDatatypeList.isListLiteral(n) ) {
+			size = CDTLiteralFunctionUtils.getList(n).size();
+		}
+		else if ( CompositeDatatypeMap.isMapLiteral(n) ) {
+			size = CDTLiteralFunctionUtils.getMap(n).size();
+		}
+		else {
+			throw new ExprEvalException("Neither a list nor a map literal: " + nv);
+		}
+
+		return NodeValue.makeInteger(size);
+	}
+
+}
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/SubSeqFct.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/SubSeqFct.java
new file mode 100644
index 0000000000..e508185c7d
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/SubSeqFct.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.sparql.function.library.cdt;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.jena.atlas.lib.Lib;
+import org.apache.jena.cdt.CDTValue;
+import org.apache.jena.graph.Node;
+import org.apache.jena.query.QueryBuildException;
+import org.apache.jena.sparql.expr.ExprEvalException;
+import org.apache.jena.sparql.expr.ExprList;
+import org.apache.jena.sparql.expr.NodeValue;
+import org.apache.jena.sparql.function.FunctionBase;
+
+public class SubSeqFct extends FunctionBase
+{
+	@Override
+	public void checkBuild( final String uri, final ExprList args ) {
+		if ( args.size() < 2 || args.size() > 3 )
+			throw new QueryBuildException("Function '"+Lib.className(this)+"' takes two or three arguments");
+	}
+
+	@Override
+	public NodeValue exec( final List<NodeValue> args ) {
+		final NodeValue nv1 = args.get(0);
+		final Node n1 = nv1.asNode();
+
+		CDTLiteralFunctionUtils.ensureListLiteral(n1);
+
+		final NodeValue nv2 = args.get(1);
+
+		if ( ! nv2.isInteger() )
+			throw new ExprEvalException("Not an integer literal: " + nv2);
+
+		final int index  = nv2.getInteger().intValue();
+
+		if ( index < 1 )
+			throw new ExprEvalException("Out of bounds index value: " + nv2);
+
+		final int length;
+		final List<CDTValue> list;
+		if ( args.size() == 3 ) {
+			final NodeValue nv3 = args.get(2);
+
+			if ( ! nv3.isInteger() )
+				throw new ExprEvalException("Not an integer literal: " + nv3);
+
+			length = nv3.getInteger().intValue();
+
+			if ( length < 0 )
+				throw new ExprEvalException("Illegal length value: " + nv3);
+
+			list = CDTLiteralFunctionUtils.getList(n1);
+		}
+		else {
+			list = CDTLiteralFunctionUtils.getList(n1);
+			length = list.size() - index + 1;
+		}
+
+		if ( index > list.size() + 1 )
+			throw new ExprEvalException("Out of bounds index value: " + nv2);
+
+		if ( index + length > list.size() + 1 )
+			throw new ExprEvalException("Beyond list length (index: " + index + ", length: " + length + ")");
+
+		final List<CDTValue> sublist;
+		if ( index == list.size() + 1 ) {
+			if ( length != 0 )
+				throw new ExprEvalException("Illegal arguments (index: " + index + ", length: " + length + ")");
+
+			if ( list.isEmpty() )
+				return nv1;
+
+			sublist = Collections.emptyList();
+		}
+		else {
+			sublist = list.subList( index - 1, index - 1 + length );
+		}
+
+		return CDTLiteralFunctionUtils.createNodeValue(sublist);
+	}
+
+}
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/ARQFunctions.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/TailFct.java
similarity index 58%
copy from jena-arq/src/main/java/org/apache/jena/sparql/function/ARQFunctions.java
copy to jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/TailFct.java
index a7452a464a..aee058e658 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/function/ARQFunctions.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/cdt/TailFct.java
@@ -16,22 +16,23 @@
  * limitations under the License.
  */
 
-package org.apache.jena.sparql.function;
+package org.apache.jena.sparql.function.library.cdt;
 
-import org.apache.jena.sparql.ARQConstants;
-import org.apache.jena.sparql.function.library.triple.TripleTermFunctions;
+import java.util.List;
 
-/** Load ARQ functions ().
- * The one in package {@link org.apache.jena.sparql.function.library}
- * can be invoked by afn:name without registration because afn:-&gt;java: is built-in
- * in MappedLoader.
- *
- * @see ARQConstants#ARQFunctionLibraryURI
- * @see ARQConstants#ARQFunctionLibrary
- */
-public class ARQFunctions {
+import org.apache.jena.cdt.CDTValue;
+import org.apache.jena.sparql.expr.ExprEvalException;
+import org.apache.jena.sparql.expr.NodeValue;
+
+public class TailFct extends FunctionBase1List
+{
+	@Override
+	protected NodeValue _exec( final List<CDTValue> list, final NodeValue nvList ) {
+		if ( list.size() == 0 )
+			throw new ExprEvalException("Empty list");
+
+		final List<CDTValue> sublist = list.subList( 1, list.size() );
+		return CDTLiteralFunctionUtils.createNodeValue(sublist);
+	}
 
-    public static void load(FunctionRegistry reg) {
-        TripleTermFunctions.register(reg);
-    }
 }
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/lang/SyntaxVarScope.java b/jena-arq/src/main/java/org/apache/jena/sparql/lang/SyntaxVarScope.java
index 5e0690ad15..dea4f3b302 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/lang/SyntaxVarScope.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/lang/SyntaxVarScope.java
@@ -223,6 +223,7 @@ public class SyntaxVarScope {
         @Override
         public void visit(ElementGroup el) {
             // BIND scope rules
+            // UNFOLD scope rules
             // (and service warning)
 
             for ( int i = 0 ; i < el.size() ; i++ ) {
@@ -232,6 +233,10 @@ public class SyntaxVarScope {
                     Collection<Var> accScope = calcScopeAll(el.getElements(), i);
                     checkBIND(accScope, eltBind);
                 }
+                if ( e instanceof ElementUnfold ) {
+                    Collection<Var> accScope = calcScopeAll(el.getElements(), i);
+                    checkUNFOLD(accScope, (ElementUnfold)e);
+                }
                 if ( e instanceof ElementService eltSvc ) {
                     Collection<Var> accScope = calcScopeAll(el.getElements(), i);
                     checkSERVICE(accScope, eltSvc);
@@ -264,6 +269,18 @@ public class SyntaxVarScope {
             checkExpr(scope, el.getExpr(), var);
         }
 
+        private static void checkUNFOLD(Collection<Var> scope, ElementUnfold el) {
+            Var var1 = el.getVar1();
+            if ( scope.contains(var1) )
+                throw new QueryParseException("UNFOLD: Variable used when already in-scope: " + var1 + " in " + el, -1, -1);
+
+            Var var2 = el.getVar2();
+            if ( var2 != null && scope.contains(var2) )
+                throw new QueryParseException("UNFOLD: Variable used when already in-scope: " + var2 + " in " + el, -1, -1);
+
+            checkExpr(scope, el.getExpr(), var1);
+        }
+
         private static void checkSERVICE(Collection<Var> scope, ElementService el) {
             if ( ARQ.isStrictMode() && el.getServiceNode().isVariable() ) {
                 Var var = Var.alloc(el.getServiceNode());
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/lang/arq/ARQParser.java b/jena-arq/src/main/java/org/apache/jena/sparql/lang/arq/ARQParser.java
index 30e9f835b6..9d1009042c 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/lang/arq/ARQParser.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/lang/arq/ARQParser.java
@@ -1,4 +1,3 @@
-/* ARQParser.java */
 /* Generated By:JavaCC: Do not edit this line. ARQParser.java */
 package org.apache.jena.sparql.lang.arq ;
 import org.apache.jena.graph.* ;
@@ -12,122 +11,116 @@ import org.apache.jena.sparql.expr.aggregate.lib.* ;
 import org.apache.jena.update.* ;
 import org.apache.jena.sparql.modify.request.* ;
 import org.apache.jena.sparql.core.Quad ;
+import java.util.List;
+import java.util.ArrayList;
 
 @SuppressWarnings("all")
 public class ARQParser extends ARQParserBase implements ARQParserConstants {
 
   final public void QueryUnit() throws ParseException {
     ByteOrderMark();
-startQuery() ;
+    startQuery() ;
     Query();
     jj_consume_token(0);
-finishQuery() ;
-}
+    finishQuery() ;
+  }
 
   final public void Query() throws ParseException {
     Prologue();
-    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
-    case SELECT:{
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+    case SELECT:
       SelectQuery();
       break;
-      }
-    case CONSTRUCT:{
+    case CONSTRUCT:
       ConstructQuery();
       break;
-      }
-    case DESCRIBE:{
+    case DESCRIBE:
       DescribeQuery();
       break;
-      }
-    case ASK:{
+    case ASK:
       AskQuery();
       break;
-      }
-    case JSON:{
+    case JSON:
       JsonQuery();
       break;
-      }
     default:
       jj_la1[0] = jj_gen;
       jj_consume_token(-1);
       throw new ParseException();
     }
     ValuesClause();
-}
+  }
 
   final public void UpdateUnit() throws ParseException {
     ByteOrderMark();
-startUpdateRequest() ;
+    startUpdateRequest() ;
     Update();
     jj_consume_token(0);
-finishUpdateRequest() ;
-}
+    finishUpdateRequest() ;
+  }
 
   final public void ByteOrderMark() throws ParseException {
-    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
-    case BOM:{
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+    case BOM:
       jj_consume_token(BOM);
       break;
-      }
     default:
       jj_la1[1] = jj_gen;
       ;
     }
-}
+  }
 
   final public void Prologue() throws ParseException {
     label_1:
     while (true) {
-      switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
+      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
       case BASE:
-      case PREFIX:{
+      case PREFIX:
         ;
         break;
-        }
       default:
         jj_la1[2] = jj_gen;
         break label_1;
       }
-      switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
-      case BASE:{
+      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+      case BASE:
         BaseDecl();
         break;
-        }
-      case PREFIX:{
+      case PREFIX:
         PrefixDecl();
         break;
-        }
       default:
         jj_la1[3] = jj_gen;
         jj_consume_token(-1);
         throw new ParseException();
       }
     }
-}
+  }
 
-  final public void BaseDecl() throws ParseException {Token t ; String iri ;
+  final public void BaseDecl() throws ParseException {
+                    Token t ; String iri ;
     t = jj_consume_token(BASE);
     iri = IRIREF();
-setBase(iri, t.beginLine, t.beginColumn ) ;
-}
+    setBase(iri, t.beginLine, t.beginColumn ) ;
+  }
 
-  final public void PrefixDecl() throws ParseException {Token t ; String iri ;
+  final public void PrefixDecl() throws ParseException {
+                      Token t ; String iri ;
     jj_consume_token(PREFIX);
     t = jj_consume_token(PNAME_NS);
     iri = IRIREF();
-String s = fixupPrefix(t.image, t.beginLine, t.beginColumn);
+      String s = fixupPrefix(t.image, t.beginLine, t.beginColumn);
       setPrefix(s, iri, t.beginLine, t.beginColumn) ;
-}
+  }
 
   final public void SelectQuery() throws ParseException {
     SelectClause();
     label_2:
     while (true) {
-      switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
-      case FROM:{
+      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+      case FROM:
         ;
         break;
-        }
       default:
         jj_la1[4] = jj_gen;
         break label_2;
@@ -136,45 +129,43 @@ String s = fixupPrefix(t.image, t.beginLine, t.beginColumn);
     }
     WhereClause();
     SolutionModifier();
-}
+  }
 
   final public void SubSelect() throws ParseException {
     SelectClause();
     WhereClause();
     SolutionModifier();
     ValuesClause();
-}
+  }
 
-  final public void SelectClause() throws ParseException {Var v ; Expr expr ; Node n ;
+  final public void SelectClause() throws ParseException {
+                        Var v ; Expr expr ; Node n ;
     jj_consume_token(SELECT);
-getQuery().setQuerySelectType() ;
-    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
+      getQuery().setQuerySelectType() ;
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
     case DISTINCT:
-    case REDUCED:{
-      switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
-      case DISTINCT:{
+    case REDUCED:
+      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+      case DISTINCT:
         jj_consume_token(DISTINCT);
-getQuery().setDistinct(true);
+                 getQuery().setDistinct(true);
         break;
-        }
-      case REDUCED:{
+      case REDUCED:
         jj_consume_token(REDUCED);
-getQuery().setReduced(true);
+                getQuery().setReduced(true);
         break;
-        }
       default:
         jj_la1[5] = jj_gen;
         jj_consume_token(-1);
         throw new ParseException();
       }
       break;
-      }
     default:
       jj_la1[6] = jj_gen;
       ;
     }
-setAllowAggregatesInExpressions(true) ;
-    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
+    setAllowAggregatesInExpressions(true) ;
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
     case IRIref:
     case PNAME_NS:
     case PNAME_LN:
@@ -201,6 +192,7 @@ setAllowAggregatesInExpressions(true) ;
     case VARIANCE:
     case VAR_SAMP:
     case VAR_POP:
+    case FOLD:
     case SAMPLE:
     case GROUP_CONCAT:
     case BOUND:
@@ -275,16 +267,15 @@ setAllowAggregatesInExpressions(true) ;
     case STRING_LITERAL2:
     case STRING_LITERAL_LONG1:
     case STRING_LITERAL_LONG2:
-    case LPAREN:{
+    case LPAREN:
       label_3:
       while (true) {
-        switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
+        switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
         case VAR1:
-        case VAR2:{
+        case VAR2:
           v = Var();
-getQuery().addResultVar(v) ;
+                  getQuery().addResultVar(v) ;
           break;
-          }
         case IRIref:
         case PNAME_NS:
         case PNAME_LN:
@@ -309,6 +300,7 @@ getQuery().addResultVar(v) ;
         case VARIANCE:
         case VAR_SAMP:
         case VAR_POP:
+        case FOLD:
         case SAMPLE:
         case GROUP_CONCAT:
         case BOUND:
@@ -382,27 +374,25 @@ getQuery().addResultVar(v) ;
         case STRING_LITERAL1:
         case STRING_LITERAL2:
         case STRING_LITERAL_LONG1:
-        case STRING_LITERAL_LONG2:{
+        case STRING_LITERAL_LONG2:
           if (jj_2_1(2)) {
             expr = BuiltInCall();
-getQuery().addResultVar((Var)null, expr) ;
+                               getQuery().addResultVar((Var)null, expr) ;
           } else {
-            switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
+            switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
             case IRIref:
             case PNAME_NS:
-            case PNAME_LN:{
+            case PNAME_LN:
               expr = FunctionCall();
-getQuery().addResultVar((Var)null, expr) ;
+                                getQuery().addResultVar((Var)null, expr) ;
               break;
-              }
             case STRING_LITERAL1:
             case STRING_LITERAL2:
             case STRING_LITERAL_LONG1:
-            case STRING_LITERAL_LONG2:{
+            case STRING_LITERAL_LONG2:
               n = RDFLiteral();
-getQuery().addResultVar((Var)null, NodeValue.makeNode(n)) ;
+                           getQuery().addResultVar((Var)null, NodeValue.makeNode(n)) ;
               break;
-              }
             case INTEGER:
             case DECIMAL:
             case DOUBLE:
@@ -411,17 +401,15 @@ getQuery().addResultVar((Var)null, NodeValue.makeNode(n)) ;
             case DOUBLE_POSITIVE:
             case INTEGER_NEGATIVE:
             case DECIMAL_NEGATIVE:
-            case DOUBLE_NEGATIVE:{
+            case DOUBLE_NEGATIVE:
               n = NumericLiteral();
-getQuery().addResultVar((Var)null, NodeValue.makeNode(n)) ;
+                               getQuery().addResultVar((Var)null, NodeValue.makeNode(n)) ;
               break;
-              }
             case TRUE:
-            case FALSE:{
+            case FALSE:
               n = BooleanLiteral();
-getQuery().addResultVar((Var)null, NodeValue.makeNode(n)) ;
+                               getQuery().addResultVar((Var)null, NodeValue.makeNode(n)) ;
               break;
-              }
             default:
               jj_la1[7] = jj_gen;
               jj_consume_token(-1);
@@ -429,32 +417,29 @@ getQuery().addResultVar((Var)null, NodeValue.makeNode(n)) ;
             }
           }
           break;
-          }
-        case LPAREN:{
-v = null ;
+        case LPAREN:
+          v = null ;
           jj_consume_token(LPAREN);
           expr = Expression();
-          switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
-          case AS:{
+          switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+          case AS:
             jj_consume_token(AS);
             v = Var();
             break;
-            }
           default:
             jj_la1[8] = jj_gen;
             ;
           }
           jj_consume_token(RPAREN);
-getQuery().addResultVar(v, expr) ;
-getQuery().setQueryResultStar(false) ;
+          getQuery().addResultVar(v, expr) ;
+        getQuery().setQueryResultStar(false) ;
           break;
-          }
         default:
           jj_la1[9] = jj_gen;
           jj_consume_token(-1);
           throw new ParseException();
         }
-        switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
+        switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
         case IRIref:
         case PNAME_NS:
         case PNAME_LN:
@@ -481,6 +466,7 @@ getQuery().setQueryResultStar(false) ;
         case VARIANCE:
         case VAR_SAMP:
         case VAR_POP:
+        case FOLD:
         case SAMPLE:
         case GROUP_CONCAT:
         case BOUND:
@@ -555,45 +541,42 @@ getQuery().setQueryResultStar(false) ;
         case STRING_LITERAL2:
         case STRING_LITERAL_LONG1:
         case STRING_LITERAL_LONG2:
-        case LPAREN:{
+        case LPAREN:
           ;
           break;
-          }
         default:
           jj_la1[10] = jj_gen;
           break label_3;
         }
       }
       break;
-      }
-    case STAR:{
+    case STAR:
       jj_consume_token(STAR);
-getQuery().setQueryResultStar(true) ;
+             getQuery().setQueryResultStar(true) ;
       break;
-      }
     default:
       jj_la1[11] = jj_gen;
       jj_consume_token(-1);
       throw new ParseException();
     }
-setAllowAggregatesInExpressions(false) ;
-}
+    setAllowAggregatesInExpressions(false) ;
+  }
 
-  final public void ConstructQuery() throws ParseException {Template t ;
+  final public void ConstructQuery() throws ParseException {
+                          Template t ;
                           QuadAcc acc = new QuadAcc() ;
     jj_consume_token(CONSTRUCT);
-getQuery().setQueryConstructType() ;
-    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
-    case LBRACE:{
+     getQuery().setQueryConstructType() ;
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+    case LBRACE:
       t = ConstructTemplate();
-getQuery().setConstructTemplate(t) ;
+        getQuery().setConstructTemplate(t) ;
       label_4:
       while (true) {
-        switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
-        case FROM:{
+        switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+        case FROM:
           ;
           break;
-          }
         default:
           jj_la1[12] = jj_gen;
           break label_4;
@@ -603,16 +586,14 @@ getQuery().setConstructTemplate(t) ;
       WhereClause();
       SolutionModifier();
       break;
-      }
     case FROM:
-    case WHERE:{
+    case WHERE:
       label_5:
       while (true) {
-        switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
-        case FROM:{
+        switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+        case FROM:
           ;
           break;
-          }
         default:
           jj_la1[13] = jj_gen;
           break label_5;
@@ -624,54 +605,51 @@ getQuery().setConstructTemplate(t) ;
       ConstructQuads(acc);
       jj_consume_token(RBRACE);
       SolutionModifier();
-t = new Template(acc) ;
+      t = new Template(acc) ;
       getQuery().setConstructTemplate(t) ;
    ElementGroup elg = createQueryPattern(t);
       getQuery().setQueryPattern(elg) ;
       break;
-      }
     default:
       jj_la1[14] = jj_gen;
       jj_consume_token(-1);
       throw new ParseException();
     }
-}
+  }
 
-  final public void DescribeQuery() throws ParseException {Node n ;
+  final public void DescribeQuery() throws ParseException {
+                         Node n ;
     jj_consume_token(DESCRIBE);
-getQuery().setQueryDescribeType() ;
-    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
+      getQuery().setQueryDescribeType() ;
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
     case IRIref:
     case PNAME_NS:
     case PNAME_LN:
     case VAR1:
-    case VAR2:{
+    case VAR2:
       label_6:
       while (true) {
         n = VarOrIri();
-getQuery().addDescribeNode(n) ;
-        switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
+                       getQuery().addDescribeNode(n) ;
+        switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
         case IRIref:
         case PNAME_NS:
         case PNAME_LN:
         case VAR1:
-        case VAR2:{
+        case VAR2:
           ;
           break;
-          }
         default:
           jj_la1[15] = jj_gen;
           break label_6;
         }
       }
-getQuery().setQueryResultStar(false) ;
+      getQuery().setQueryResultStar(false) ;
       break;
-      }
-    case STAR:{
+    case STAR:
       jj_consume_token(STAR);
-getQuery().setQueryResultStar(true) ;
+      getQuery().setQueryResultStar(true) ;
       break;
-      }
     default:
       jj_la1[16] = jj_gen;
       jj_consume_token(-1);
@@ -679,40 +657,37 @@ getQuery().setQueryResultStar(true) ;
     }
     label_7:
     while (true) {
-      switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
-      case FROM:{
+      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+      case FROM:
         ;
         break;
-        }
       default:
         jj_la1[17] = jj_gen;
         break label_7;
       }
       DatasetClause();
     }
-    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
     case WHERE:
-    case LBRACE:{
+    case LBRACE:
       WhereClause();
       break;
-      }
     default:
       jj_la1[18] = jj_gen;
       ;
     }
     SolutionModifier();
-}
+  }
 
   final public void AskQuery() throws ParseException {
     jj_consume_token(ASK);
-getQuery().setQueryAskType() ;
+          getQuery().setQueryAskType() ;
     label_8:
     while (true) {
-      switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
-      case FROM:{
+      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+      case FROM:
         ;
         break;
-        }
       default:
         jj_la1[19] = jj_gen;
         break label_8;
@@ -721,17 +696,16 @@ getQuery().setQueryAskType() ;
     }
     WhereClause();
     SolutionModifier();
-}
+  }
 
   final public void JsonQuery() throws ParseException {
     JsonClause();
     label_9:
     while (true) {
-      switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
-      case FROM:{
+      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+      case FROM:
         ;
         break;
-        }
       default:
         jj_la1[20] = jj_gen;
         break label_9;
@@ -740,20 +714,19 @@ getQuery().setQueryAskType() ;
     }
     WhereClause();
     SolutionModifier();
-}
+  }
 
   final public void JsonClause() throws ParseException {
     jj_consume_token(JSON);
-getQuery().setQueryJsonType() ;
+           getQuery().setQueryJsonType() ;
     jj_consume_token(LBRACE);
     JsonObjectMember();
     label_10:
     while (true) {
-      switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
-      case COMMA:{
+      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+      case COMMA:
         ;
         break;
-        }
       default:
         jj_la1[21] = jj_gen;
         break label_10;
@@ -762,28 +735,27 @@ getQuery().setQueryJsonType() ;
       JsonObjectMember();
     }
     jj_consume_token(RBRACE);
-}
+  }
 
-  final public void JsonObjectMember() throws ParseException {Node o ; String s ; Token t;
+  final public void JsonObjectMember() throws ParseException {
+                            Node o ; String s ; Token t;
     s = String();
     t = jj_consume_token(PNAME_NS);
-if ( ! t.image.equals(":") )
+    if ( ! t.image.equals(":") )
       throwParseException("Prefix name expression not legal at this point : "+t.image, t.beginLine, t.beginColumn) ;
-    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
     case VAR1:
-    case VAR2:{
+    case VAR2:
       o = Var();
-getQuery().addResultVar((Var)o) ; getQuery().addJsonMapping(s, o) ;
+                getQuery().addResultVar((Var)o) ; getQuery().addJsonMapping(s, o) ;
       break;
-      }
     case STRING_LITERAL1:
     case STRING_LITERAL2:
     case STRING_LITERAL_LONG1:
-    case STRING_LITERAL_LONG2:{
+    case STRING_LITERAL_LONG2:
       o = RDFLiteral();
-getQuery().addResultVar(s, NodeValue.makeNode(o)) ; getQuery().addJsonMapping(s, o) ;
+                       getQuery().addResultVar(s, NodeValue.makeNode(o)) ; getQuery().addJsonMapping(s, o) ;
       break;
-      }
     case INTEGER:
     case DECIMAL:
     case DOUBLE:
@@ -792,116 +764,111 @@ getQuery().addResultVar(s, NodeValue.makeNode(o)) ; getQuery().addJsonMapping(s,
     case DOUBLE_POSITIVE:
     case INTEGER_NEGATIVE:
     case DECIMAL_NEGATIVE:
-    case DOUBLE_NEGATIVE:{
+    case DOUBLE_NEGATIVE:
       o = NumericLiteral();
-getQuery().addResultVar(s, NodeValue.makeNode(o)) ; getQuery().addJsonMapping(s, o) ;
+                           getQuery().addResultVar(s, NodeValue.makeNode(o)) ; getQuery().addJsonMapping(s, o) ;
       break;
-      }
     case TRUE:
-    case FALSE:{
+    case FALSE:
       o = BooleanLiteral();
-getQuery().addResultVar(s, NodeValue.makeNode(o)) ; getQuery().addJsonMapping(s, o) ;
+                           getQuery().addResultVar(s, NodeValue.makeNode(o)) ; getQuery().addJsonMapping(s, o) ;
       break;
-      }
     default:
       jj_la1[22] = jj_gen;
       jj_consume_token(-1);
       throw new ParseException();
     }
-}
+  }
 
   final public void DatasetClause() throws ParseException {
     jj_consume_token(FROM);
-    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
     case IRIref:
     case PNAME_NS:
-    case PNAME_LN:{
+    case PNAME_LN:
       DefaultGraphClause();
       break;
-      }
-    case NAMED:{
+    case NAMED:
       NamedGraphClause();
       break;
-      }
     default:
       jj_la1[23] = jj_gen;
       jj_consume_token(-1);
       throw new ParseException();
     }
-}
+  }
 
-  final public void DefaultGraphClause() throws ParseException {String iri ;
+  final public void DefaultGraphClause() throws ParseException {
+                              String iri ;
     iri = SourceSelector();
-getQuery().addGraphURI(iri) ;
-}
+    getQuery().addGraphURI(iri) ;
+  }
 
-  final public void NamedGraphClause() throws ParseException {String iri ;
+  final public void NamedGraphClause() throws ParseException {
+                            String iri ;
     jj_consume_token(NAMED);
     iri = SourceSelector();
-getQuery().addNamedGraphURI(iri) ;
-}
+    getQuery().addNamedGraphURI(iri) ;
+  }
 
-  final public String SourceSelector() throws ParseException {String iri ;
+  final public String SourceSelector() throws ParseException {
+                            String iri ;
     iri = iri();
-{if ("" != null) return iri ;}
+                {if (true) return iri ;}
     throw new Error("Missing return statement in function");
-}
+  }
 
-  final public void WhereClause() throws ParseException {Element el ;
-    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
-    case WHERE:{
+  final public void WhereClause() throws ParseException {
+                       Element el ;
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+    case WHERE:
       jj_consume_token(WHERE);
       break;
-      }
     default:
       jj_la1[24] = jj_gen;
       ;
     }
-startWherePattern() ;
+     startWherePattern() ;
     el = GroupGraphPattern();
-getQuery().setQueryPattern(el) ;
-finishWherePattern() ;
-}
+                              getQuery().setQueryPattern(el) ;
+     finishWherePattern() ;
+  }
 
   final public void SolutionModifier() throws ParseException {
-    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
-    case GROUP:{
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+    case GROUP:
       GroupClause();
       break;
-      }
     default:
       jj_la1[25] = jj_gen;
       ;
     }
-    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
-    case HAVING:{
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+    case HAVING:
       HavingClause();
       break;
-      }
     default:
       jj_la1[26] = jj_gen;
       ;
     }
-    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
-    case ORDER:{
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+    case ORDER:
       OrderClause();
       break;
-      }
     default:
       jj_la1[27] = jj_gen;
       ;
     }
-    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
     case LIMIT:
-    case OFFSET:{
+    case OFFSET:
       LimitOffsetClauses();
       break;
-      }
     default:
       jj_la1[28] = jj_gen;
       ;
     }
-}
+  }
 
   final public void GroupClause() throws ParseException {
     jj_consume_token(GROUP);
@@ -909,7 +876,7 @@ finishWherePattern() ;
     label_11:
     while (true) {
       GroupCondition();
-      switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
+      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
       case IRIref:
       case PNAME_NS:
       case PNAME_LN:
@@ -936,6 +903,7 @@ finishWherePattern() ;
       case VARIANCE:
       case VAR_SAMP:
       case VAR_POP:
+      case FOLD:
       case SAMPLE:
       case GROUP_CONCAT:
       case BOUND:
@@ -995,19 +963,19 @@ finishWherePattern() ;
       case SHA256:
       case SHA384:
       case SHA512:
-      case LPAREN:{
+      case LPAREN:
         ;
         break;
-        }
       default:
         jj_la1[29] = jj_gen;
         break label_11;
       }
     }
-}
+  }
 
-  final public void GroupCondition() throws ParseException {Var v = null ; Expr expr = null ;
-    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
+  final public void GroupCondition() throws ParseException {
+                          Var v = null ; Expr expr = null ;
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
     case TRIPLE:
     case IS_TRIPLE:
     case SUBJECT:
@@ -1029,6 +997,7 @@ finishWherePattern() ;
     case VARIANCE:
     case VAR_SAMP:
     case VAR_POP:
+    case FOLD:
     case SAMPLE:
     case GROUP_CONCAT:
     case BOUND:
@@ -1087,55 +1056,50 @@ finishWherePattern() ;
     case SHA1:
     case SHA256:
     case SHA384:
-    case SHA512:{
+    case SHA512:
       expr = BuiltInCall();
-getQuery().addGroupBy((Var)null, expr) ;
+                           getQuery().addGroupBy((Var)null, expr) ;
       break;
-      }
     case IRIref:
     case PNAME_NS:
-    case PNAME_LN:{
+    case PNAME_LN:
       expr = FunctionCall();
-getQuery().addGroupBy((Var)null, expr) ;
+                            getQuery().addGroupBy((Var)null, expr) ;
       break;
-      }
-    case LPAREN:{
+    case LPAREN:
       jj_consume_token(LPAREN);
       expr = Expression();
-      switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
-      case AS:{
+      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+      case AS:
         jj_consume_token(AS);
         v = Var();
         break;
-        }
       default:
         jj_la1[30] = jj_gen;
         ;
       }
       jj_consume_token(RPAREN);
-getQuery().addGroupBy(v ,expr) ;
+      getQuery().addGroupBy(v ,expr) ;
       break;
-      }
     case VAR1:
-    case VAR2:{
+    case VAR2:
       v = Var();
-getQuery().addGroupBy(v) ;
+      getQuery().addGroupBy(v) ;
       break;
-      }
     default:
       jj_la1[31] = jj_gen;
       jj_consume_token(-1);
       throw new ParseException();
     }
-}
+  }
 
   final public void HavingClause() throws ParseException {
-setAllowAggregatesInExpressions(true) ;
+      setAllowAggregatesInExpressions(true) ;
     jj_consume_token(HAVING);
     label_12:
     while (true) {
       HavingCondition();
-      switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
+      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
       case IRIref:
       case PNAME_NS:
       case PNAME_LN:
@@ -1160,6 +1124,7 @@ setAllowAggregatesInExpressions(true) ;
       case VARIANCE:
       case VAR_SAMP:
       case VAR_POP:
+      case FOLD:
       case SAMPLE:
       case GROUP_CONCAT:
       case BOUND:
@@ -1219,31 +1184,31 @@ setAllowAggregatesInExpressions(true) ;
       case SHA256:
       case SHA384:
       case SHA512:
-      case LPAREN:{
+      case LPAREN:
         ;
         break;
-        }
       default:
         jj_la1[32] = jj_gen;
         break label_12;
       }
     }
-setAllowAggregatesInExpressions(false) ;
-}
+      setAllowAggregatesInExpressions(false) ;
+  }
 
-  final public void HavingCondition() throws ParseException {Expr c ;
+  final public void HavingCondition() throws ParseException {
+                           Expr c ;
     c = Constraint();
-getQuery().addHavingCondition(c) ;
-}
+    getQuery().addHavingCondition(c) ;
+  }
 
   final public void OrderClause() throws ParseException {
-setAllowAggregatesInExpressions(true) ;
+    setAllowAggregatesInExpressions(true) ;
     jj_consume_token(ORDER);
     jj_consume_token(BY);
     label_13:
     while (true) {
       OrderCondition();
-      switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
+      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
       case IRIref:
       case PNAME_NS:
       case PNAME_LN:
@@ -1272,6 +1237,7 @@ setAllowAggregatesInExpressions(true) ;
       case VARIANCE:
       case VAR_SAMP:
       case VAR_POP:
+      case FOLD:
       case SAMPLE:
       case GROUP_CONCAT:
       case BOUND:
@@ -1331,34 +1297,32 @@ setAllowAggregatesInExpressions(true) ;
       case SHA256:
       case SHA384:
       case SHA512:
-      case LPAREN:{
+      case LPAREN:
         ;
         break;
-        }
       default:
         jj_la1[33] = jj_gen;
         break label_13;
       }
     }
-setAllowAggregatesInExpressions(false) ;
-}
+    setAllowAggregatesInExpressions(false) ;
+  }
 
-  final public void OrderCondition() throws ParseException {int direction = 0 ; Expr expr = null ; Node v = null ;
-direction = Query.ORDER_DEFAULT ;
-    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
+  final public void OrderCondition() throws ParseException {
+  int direction = 0 ; Expr expr = null ; Node v = null ;
+    direction = Query.ORDER_DEFAULT ;
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
     case ASC:
-    case DESC:{
-      switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
-      case ASC:{
+    case DESC:
+      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+      case ASC:
         jj_consume_token(ASC);
-direction = Query.ORDER_ASCENDING ;
+                direction = Query.ORDER_ASCENDING ;
         break;
-        }
-      case DESC:{
+      case DESC:
         jj_consume_token(DESC);
-direction = Query.ORDER_DESCENDING ;
+                 direction = Query.ORDER_DESCENDING ;
         break;
-        }
       default:
         jj_la1[34] = jj_gen;
         jj_consume_token(-1);
@@ -1366,7 +1330,6 @@ direction = Query.ORDER_DESCENDING ;
       }
       expr = BrackettedExpression();
       break;
-      }
     case IRIref:
     case PNAME_NS:
     case PNAME_LN:
@@ -1393,6 +1356,7 @@ direction = Query.ORDER_DESCENDING ;
     case VARIANCE:
     case VAR_SAMP:
     case VAR_POP:
+    case FOLD:
     case SAMPLE:
     case GROUP_CONCAT:
     case BOUND:
@@ -1452,8 +1416,8 @@ direction = Query.ORDER_DESCENDING ;
     case SHA256:
     case SHA384:
     case SHA512:
-    case LPAREN:{
-      switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
+    case LPAREN:
+      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
       case IRIref:
       case PNAME_NS:
       case PNAME_LN:
@@ -1478,6 +1442,7 @@ direction = Query.ORDER_DESCENDING ;
       case VARIANCE:
       case VAR_SAMP:
       case VAR_POP:
+      case FOLD:
       case SAMPLE:
       case GROUP_CONCAT:
       case BOUND:
@@ -1537,546 +1502,743 @@ direction = Query.ORDER_DESCENDING ;
       case SHA256:
       case SHA384:
       case SHA512:
-      case LPAREN:{
+      case LPAREN:
         expr = Constraint();
         break;
-        }
       case VAR1:
-      case VAR2:{
+      case VAR2:
         v = Var();
         break;
-        }
       default:
         jj_la1[35] = jj_gen;
         jj_consume_token(-1);
         throw new ParseException();
       }
       break;
-      }
     default:
       jj_la1[36] = jj_gen;
       jj_consume_token(-1);
       throw new ParseException();
     }
-if ( v == null )
+    if ( v == null )
           getQuery().addOrderBy(expr, direction) ;
       else
           getQuery().addOrderBy(v, direction) ;
-}
+  }
 
-  final public void LimitOffsetClauses() throws ParseException {
-    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
-    case LIMIT:{
-      LimitClause();
-      switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
-      case OFFSET:{
-        OffsetClause();
-        break;
-        }
-      default:
-        jj_la1[37] = jj_gen;
-        ;
-      }
-      break;
-      }
-    case OFFSET:{
-      OffsetClause();
-      switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
-      case LIMIT:{
-        LimitClause();
+  final public SortCondition OrderConditionForAggregationFunction() throws ParseException {
+  int direction = 0 ; Expr expr = null ; Node v = null ;
+    direction = Query.ORDER_DEFAULT ;
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+    case ASC:
+    case DESC:
+      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+      case ASC:
+        jj_consume_token(ASC);
+                direction = Query.ORDER_ASCENDING ;
         break;
-        }
-      default:
-        jj_la1[38] = jj_gen;
-        ;
-      }
-      break;
-      }
-    default:
-      jj_la1[39] = jj_gen;
-      jj_consume_token(-1);
-      throw new ParseException();
-    }
-}
-
-  final public void LimitClause() throws ParseException {Token t ;
-    jj_consume_token(LIMIT);
-    t = jj_consume_token(INTEGER);
-getQuery().setLimit(integerValue(t.image)) ;
-}
-
-  final public void OffsetClause() throws ParseException {Token t ;
-    jj_consume_token(OFFSET);
-    t = jj_consume_token(INTEGER);
-getQuery().setOffset(integerValue(t.image)) ;
-}
-
-  final public void ValuesClause() throws ParseException {Token t ;
-    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
-    case VALUES:{
-      t = jj_consume_token(VALUES);
-startValuesClause(t.beginLine, t.beginColumn) ;
-      DataBlock();
-finishValuesClause(t.beginLine, t.beginColumn) ;
-      break;
-      }
-    default:
-      jj_la1[40] = jj_gen;
-      ;
-    }
-}
-
-  final public void Update() throws ParseException {
-    Prologue();
-    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
-    case INSERT:
-    case DELETE:
-    case INSERT_DATA:
-    case DELETE_DATA:
-    case DELETE_WHERE:
-    case LOAD:
-    case CLEAR:
-    case CREATE:
-    case ADD:
-    case MOVE:
-    case COPY:
-    case DROP:
-    case WITH:{
-      Update1();
-      label_14:
-      while (true) {
-        if (jj_2_2(2147483647)) {
-          ;
-        } else {
-          break label_14;
-        }
-        jj_consume_token(SEMICOLON);
-        Prologue();
-        Update1();
-      }
-      switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
-      case SEMICOLON:{
-        jj_consume_token(SEMICOLON);
-        Prologue();
+      case DESC:
+        jj_consume_token(DESC);
+                 direction = Query.ORDER_DESCENDING ;
         break;
-        }
       default:
-        jj_la1[41] = jj_gen;
-        ;
-      }
-      break;
-      }
-    default:
-      jj_la1[42] = jj_gen;
-      ;
-    }
-}
-
... 34002 lines suppressed ...