
io.brackit.query.compiler.translator.Compiler Maven / Gradle / Ivy
/*
* [New BSD License]
* Copyright (c) 2011-2012, Brackit Project Team
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Brackit Project Team nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package io.brackit.query.compiler.translator;
import io.brackit.query.ErrorCode;
import io.brackit.query.atomic.*;
import io.brackit.query.expr.*;
import io.brackit.query.function.DynamicFunctionExpr;
import io.brackit.query.function.FunctionExpr;
import io.brackit.query.function.InlineFunctionExpr;
import io.brackit.query.function.UDF;
import io.brackit.query.function.bit.BitFun;
import io.brackit.query.function.json.JSONFun;
import io.brackit.query.jdm.*;
import io.brackit.query.jdm.type.*;
import io.brackit.query.module.Module;
import io.brackit.query.module.StaticContext;
import io.brackit.query.operator.*;
import io.brackit.query.update.*;
import io.brackit.query.update.json.*;
import io.brackit.query.util.Cmp;
import io.brackit.query.util.Whitespace;
import io.brackit.query.util.aggregator.Aggregate;
import io.brackit.query.util.sort.Ordering;
import io.brackit.query.QueryException;
import io.brackit.query.compiler.AST;
import io.brackit.query.compiler.Bits;
import io.brackit.query.compiler.XQ;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* @author Sebastian Baechle
*/
public class Compiler implements Translator {
protected static class ClauseBinding {
final ClauseBinding in;
final Operator operator;
final Binding[] bindings;
ClauseBinding(ClauseBinding in, Operator operator, Binding... bindings) {
this.in = in;
this.operator = operator;
this.bindings = bindings;
}
void unbind() {
if (in != null) {
in.unbind();
}
}
}
protected static class AggregateBinding {
final QNm srcVar;
final QNm aggVar;
final SequenceType aggVarType;
final Aggregate agg;
public AggregateBinding(QNm srcVar, QNm aggVar, SequenceType aggVarType, Aggregate agg) {
this.srcVar = srcVar;
this.aggVar = aggVar;
this.aggVarType = aggVarType;
this.agg = agg;
}
}
protected VariableTable table;
protected StaticContext ctx;
protected final Map options;
public Compiler(Map options) {
this.options = options;
}
@Override
public Expr expression(Module module, StaticContext ctx, AST expr, boolean allowUpdate) throws QueryException {
this.table = new VariableTable(module);
this.ctx = ctx;
Expr e = expr(expr, !allowUpdate);
table.resolvePositions();
return e;
}
public Expr inlineFunction(Module module, StaticContext ctx, UDF udf, QNm[] params, AST expr, boolean allowUpdate)
throws QueryException {
final var bound = this.table.bound();
table = new VariableTable(module);
for (final Binding binding : bound) {
table.bind(binding.getName(), binding.getType());
}
for (final Binding binding : bound) {
table.resolve(binding.getName());
}
this.ctx = ctx;
// bind parameter
SequenceType[] types = udf.getSignature().getParams();
for (int i = 0; i < params.length; i++) {
table.bind(params[i], types[i]);
}
// ensure fixed parameter positions
for (final QNm param : params) {
table.resolve(param);
}
// compile body
Expr body = expr(expr, !allowUpdate);
// unbind parameters
for (int i = 0; i < params.length; i++) {
table.unbind();
}
table.resolvePositions();
return body;
}
@Override
public Expr function(Module module, StaticContext ctx, UDF udf, QNm[] params, AST expr, boolean allowUpdate)
throws QueryException {
this.table = new VariableTable(module);
this.ctx = ctx;
// bind parameter
SequenceType[] types = udf.getSignature().getParams();
for (int i = 0; i < params.length; i++) {
table.bind(params[i], types[i]);
}
// ensure fixed parameter positions
for (final QNm param : params) {
table.resolve(param);
}
// compile body
Expr body = expr(expr, !allowUpdate);
// unbind parameters
for (int i = 0; i < params.length; i++) {
table.unbind();
}
table.resolvePositions();
return body;
}
protected Expr expr(AST node, boolean disallowUpdatingExpr) throws QueryException {
Expr expr = anyExpr(node);
if (disallowUpdatingExpr && expr.isUpdating()) {
throw new QueryException(ErrorCode.ERR_UPDATE_ILLEGAL_NESTED_UPDATE, "Illegal nested update expression");
}
return expr;
}
protected Expr anyExpr(AST node) throws QueryException {
return switch (node.getType()) {
case XQ.FlowrExpr -> flowrExpr(node);
case XQ.QuantifiedExpr -> quantifiedExpr(node);
case XQ.EnclosedExpr, XQ.ParenthesizedExpr, XQ.SequenceExpr -> sequenceExpr(node);
case XQ.Str -> new Str(Whitespace.normalizeXML11(node.getStringValue()));
case XQ.Null -> new Null();
case XQ.Int, XQ.Dbl, XQ.Dec, XQ.QNm, XQ.AnyURI, XQ.Bool -> (Atomic) node.getValue();
case XQ.VariableRef -> variableRefExpr(node);
case XQ.ArithmeticExpr -> arithmeticExpr(node);
case XQ.ComparisonExpr -> comparisonExpr(node);
case XQ.RangeExpr -> rangeExpr(node);
case XQ.AndExpr -> andExpr(node);
case XQ.OrExpr -> orExpr(node);
case XQ.CastExpr -> castExpr(node);
case XQ.CastableExpr -> castableExpr(node);
case XQ.TreatExpr -> treatExpr(node);
case XQ.InstanceofExpr -> instanceOfExpr(node);
case XQ.TypeSwitch -> typeswitchExpr(node);
case XQ.IfExpr -> ifExpr(node);
case XQ.SwitchExpr -> switchExpr(node);
case XQ.FilterExpr -> filterExpr(node);
case XQ.DirElementConstructor, XQ.CompElementConstructor -> elementExpr(node);
case XQ.DirAttributeConstructor, XQ.CompAttributeConstructor -> attributeExpr(node);
case XQ.DirCommentConstructor, XQ.CompCommentConstructor -> commentExpr(node);
case XQ.CompTextConstructor -> textExpr(node);
case XQ.CompDocumentConstructor -> documentExpr(node);
case XQ.DirPIConstructor, XQ.CompPIConstructor -> piExpr(node);
case XQ.FunctionCall -> functionCall(node);
case XQ.DynamicFunctionCallExpr -> dynamicFunctionCall(node);
case XQ.PathExpr -> pathExpr(node);
case XQ.StepExpr -> stepExpr(node);
case XQ.ContextItemExpr -> table.resolve(Bits.FS_DOT);
case XQ.InsertExpr -> insertExpr(node);
case XQ.DeleteExpr -> deleteExpr(node);
case XQ.ReplaceNodeExpr, XQ.ReplaceValueExpr -> replaceExpr(node);
case XQ.RenameExpr -> renameExpr(node);
case XQ.TransformExpr -> transformExpr(node);
case XQ.OrderedExpr, XQ.UnorderedExpr, XQ.ObjectField -> anyExpr(node.getChild(0));
case XQ.UnionExpr -> unionExpr(node);
case XQ.ExceptExpr -> exceptExpr(node);
case XQ.IntersectExpr -> intersectExpr(node);
case XQ.TryCatchExpr -> tryCatchExpr(node);
case XQ.ExtensionExpr -> extensionExpr(node);
case XQ.ValidateExpr -> throw new QueryException(ErrorCode.ERR_SCHEMA_VALIDATION_FEATURE_NOT_SUPPORTED,
"Schema validation feature is not supported.");
case XQ.ArrayConstructor -> arrayExpr(node);
case XQ.ArrayAccess -> arrayAccessExpr(node);
case XQ.ArrayIndexSlice -> arrayIndexSliceExpr(node);
case XQ.ObjectConstructor -> objectExpr(node);
case XQ.DerefExpr -> derefExpr(node);
case XQ.DerefDescendantExpr -> derefDescendantExpr(node);
case XQ.ObjectProjection -> projectionExpr(node);
case XQ.InsertJsonExpr -> insertJsonExpr(node);
case XQ.DeleteJsonExpr -> deleteJsonExpr(node);
case XQ.ReplaceJsonExpr -> replaceJsonExpr(node);
case XQ.RenameJsonExpr -> renameJsonExpr(node);
case XQ.AppendJsonExpr -> appendJsonExpr(node);
case XQ.EmptySequenceType -> new EmptyExpr();
case XQ.StringConcatExpr -> stringConcatExpr(node);
case XQ.InlineFuncItem -> inlineFuncExpr(node);
default -> throw new QueryException(ErrorCode.BIT_DYN_RT_ILLEGAL_STATE_ERROR,
"Unexpected AST expr node '%s' of type: %s",
node,
node.getType());
};
}
protected Expr inlineFuncExpr(AST node) throws QueryException {
final var udf = (UDF) node.getProperty("udf");
final var body = node.getChild(node.getChildCount() - 1);
final var pNames = (QNm[]) node.getProperty("paramNames");
final var functionBody = inlineFunction(table.module, node.getStaticContext(), udf, pNames, body, udf.isUpdating());
udf.setExpr(functionBody);
return new InlineFunctionExpr(udf);
}
private Expr stringConcatExpr(AST node) {
final Expr[] stringsToConcatExprs = new Expr[node.getChildCount()];
for (int i = 0; i < node.getChildCount(); i++) {
stringsToConcatExprs[i] = expr(node.getChild(i), true);
}
return new StringConcatExpr(stringsToConcatExprs);
}
private Expr dynamicFunctionCall(AST node) {
final StaticContext sctx = node.getStaticContext();
final Expr functionExpr = expr(node.getChild(0), true);
final Expr[] argumentsExpr = new Expr[node.getChildCount() - 1];
for (int i = 1; i < node.getChildCount(); i++) {
argumentsExpr[i - 1] = expr(node.getChild(i), true);
}
return new DynamicFunctionExpr(sctx, functionExpr, argumentsExpr);
}
private Expr deleteJsonExpr(AST node) {
AST derefOrArrayIndexNode = node.getChild(0);
Expr targetExpr = expr(derefOrArrayIndexNode.getChild(0), true);
Expr fieldOrIndex = expr(derefOrArrayIndexNode.getChild(1), true);
return new DeleteJson(targetExpr, fieldOrIndex);
}
private Expr insertJsonExpr(AST node) {
Expr sourceExpr = expr(node.getChild(0), true);
Expr targetExpr = expr(node.getChild(1), true);
final int position;
if (node.getChildCount() == 3) {
position = ((Int32) node.getChild(2).getValue()).intValue();
} else {
position = -1;
}
return new InsertJson(sourceExpr, targetExpr, position);
}
private Expr appendJsonExpr(AST node) {
Expr sourceExpr = expr(node.getChild(0), true);
Expr targetExpr = expr(node.getChild(1), true);
return new AppendJsonArrayValue(sourceExpr, targetExpr);
}
private Expr replaceJsonExpr(AST node) {
AST derefOrArrayIndexNode = node.getChild(0);
Expr targetExpr = expr(derefOrArrayIndexNode.getChild(0), true);
Expr fieldOrIndex = expr(derefOrArrayIndexNode.getChild(1), true);
Expr sourceExpr = expr(node.getChild(1), true);
return new ReplaceJsonValue(sourceExpr, targetExpr, fieldOrIndex);
}
private Expr renameJsonExpr(AST node) {
AST derefNode = node.getChild(0);
Expr targetExpr = expr(derefNode.getChild(0), true);
Expr oldFieldExpr = expr(derefNode.getChild(1), true);
Expr newFieldExpr = expr(node.getChild(1), true);
return new RenameJsonField(targetExpr, oldFieldExpr, newFieldExpr);
}
protected Expr tryCatchExpr(AST node) throws QueryException {
Expr expr = expr(node.getChild(0), true);
Binding code = table.bind((QNm) node.getChild(1).getChild(0).getValue(),
new SequenceType(AtomicType.QNM, Cardinality.One));
Binding desc = table.bind((QNm) node.getChild(2).getChild(0).getValue(),
new SequenceType(AtomicType.STR, Cardinality.ZeroOrOne));
Binding value = table.bind((QNm) node.getChild(3).getChild(0).getValue(),
new SequenceType(AnyItemType.ANY, Cardinality.ZeroOrMany));
Binding module = table.bind((QNm) node.getChild(4).getChild(0).getValue(),
new SequenceType(AtomicType.STR, Cardinality.ZeroOrOne));
Binding lineNo = table.bind((QNm) node.getChild(5).getChild(0).getValue(),
new SequenceType(AtomicType.INR, Cardinality.ZeroOrOne));
Binding colNo = table.bind((QNm) node.getChild(6).getChild(0).getValue(),
new SequenceType(AtomicType.INR, Cardinality.ZeroOrOne));
int catchCount = node.getChildCount() - 7;
TryCatchExpr.ErrorCatch[][] catches = new TryCatchExpr.ErrorCatch[catchCount][];
Expr[] handler = new Expr[catchCount];
for (int i = 0; i < catchCount; i++) {
AST clause = node.getChild(i + 7);
AST catchErrorList = clause.getChild(0);
int errorCount = catchErrorList.getChildCount();
catches[i] = new TryCatchExpr.ErrorCatch[errorCount];
for (int j = 0; j < errorCount; j++) {
AST nametest = catchErrorList.getChild(j);
catches[i][j] = tryCatchNameTest(nametest);
}
handler[i] = expr(clause.getChild(1), true);
}
for (int i = 0; i < 6; i++) {
table.unbind();
}
return new TryCatchExpr(expr,
catches,
handler,
code.isReferenced(),
desc.isReferenced(),
value.isReferenced(),
module.isReferenced(),
lineNo.isReferenced(),
colNo.isReferenced());
}
protected TryCatchExpr.ErrorCatch tryCatchNameTest(AST child) throws QueryException {
AST name = child.getChild(0);
switch (name.getType()) {
case XQ.Wildcard:
return new TryCatchExpr.Wildcard();
case XQ.NSWildcardNameTest:
return new TryCatchExpr.NSWildcard(name.getStringValue());
case XQ.NSNameWildcardTest:
return new TryCatchExpr.NameWildcard(name.getStringValue());
default:
QNm qnm = (QNm) name.getValue();
return new TryCatchExpr.Name(qnm.getLocalName(), qnm.getNamespaceURI());
}
}
protected Expr extensionExpr(AST node) throws QueryException {
return node.getChildCount() == 2 ? anyExpr(node.getChild(1)) : new EmptyExpr();
}
protected Expr unionExpr(AST node) throws QueryException {
Expr firstExpr = expr(node.getChild(0), true);
Expr secondExpr = expr(node.getChild(1), true);
return new UnionExpr(firstExpr, secondExpr);
}
protected Expr exceptExpr(AST node) throws QueryException {
Expr firstExpr = expr(node.getChild(0), true);
Expr secondExpr = expr(node.getChild(1), true);
return new ExceptExpr(firstExpr, secondExpr);
}
protected Expr intersectExpr(AST node) throws QueryException {
Expr firstExpr = expr(node.getChild(0), true);
Expr secondExpr = expr(node.getChild(1), true);
return new IntersectExpr(firstExpr, secondExpr);
}
protected Expr castExpr(AST node) throws QueryException {
Expr expr = expr(node.getChild(0), true);
AST type = node.getChild(1);
AST aouType = type.getChild(0);
Type targetType = resolveType((QNm) aouType.getChild(0).getValue(), true);
boolean allowEmptySequence = type.getChildCount() == 2 && type.getChild(1).getType() == XQ.CardinalityZeroOrOne;
StaticContext sctx = node.getStaticContext();
return new Cast(sctx, expr, targetType, allowEmptySequence);
}
protected Expr castableExpr(AST node) throws QueryException {
Expr expr = expr(node.getChild(0), true);
AST type = node.getChild(1);
AST aouType = type.getChild(0);
Type targetType = resolveType((QNm) aouType.getChild(0).getValue(), true);
boolean allowEmptySequence = type.getChildCount() == 2 && type.getChild(1).getType() == XQ.CardinalityZeroOrOne;
StaticContext sctx = node.getStaticContext();
return new Castable(sctx, expr, targetType, allowEmptySequence);
}
protected Expr treatExpr(AST node) throws QueryException {
Expr expr = expr(node.getChild(0), true);
SequenceType sequenceType = sequenceType(node.getChild(1));
return new Treat(expr, sequenceType);
}
protected Expr instanceOfExpr(AST node) throws QueryException {
Expr expr = expr(node.getChild(0), true);
SequenceType sequenceType = sequenceType(node.getChild(1));
return new InstanceOf(expr, sequenceType);
}
protected Expr typeswitchExpr(AST node) throws QueryException {
Expr operandExpr = expr(node.getChild(0), false);
if (operandExpr.isUpdating()) {
throw new QueryException(ErrorCode.ERR_UPDATE_ILLEGAL_NESTED_UPDATE,
"Operand expression of typeswitch expression must not be updating.");
}
boolean updating = false;
int vacOrUpdate = 0;
int cases = node.getChildCount() - 2;
Expr[] caseExprs = cases > 0 ? new Expr[cases] : null;
SequenceType[] caseTypes = cases > 0 ? new SequenceType[cases] : null;
boolean[] varRefs = new boolean[cases + 1];
for (int i = 0; i < cases; i++) {
AST caseNode = node.getChild(i + 1);
AST firstChild = caseNode.getChild(0);
int c = 0;
QNm varName = null;
Binding binding = null;
if (firstChild.getType() == XQ.Variable) {
c++;
varName = (QNm) firstChild.getValue();
}
caseTypes[i] = sequenceType(caseNode.getChild(c++));
if (varName != null) {
binding = table.bind(varName, caseTypes[i]);
}
caseExprs[i] = expr(caseNode.getChild(c), false);
if (varName != null) {
if (binding.isReferenced()) {
varRefs[i] = true;
}
table.unbind();
}
if (caseExprs[i].isVacuous()) {
vacOrUpdate++;
}
if (caseExprs[i].isUpdating()) {
updating = true;
vacOrUpdate++;
}
}
AST defaultNode = node.getChild(node.getChildCount() - 1);
AST firstChild = defaultNode.getChild(0);
int c = 0;
QNm varName = null;
Binding binding = null;
if (firstChild.getType() == XQ.Variable) {
c++;
varName = (QNm) firstChild.getValue();
binding = table.bind(varName, SequenceType.ITEM_SEQUENCE);
}
Expr defaultExpr = expr(defaultNode.getChild(c), false);
if (varName != null) {
if (binding.isReferenced()) {
varRefs[varRefs.length - 1] = true;
}
table.unbind();
}
if (defaultExpr.isVacuous()) {
vacOrUpdate++;
}
if (defaultExpr.isUpdating()) {
updating = true;
vacOrUpdate++;
}
if (updating && vacOrUpdate < cases + 1) {
throw new QueryException(ErrorCode.ERR_UPDATE_ILLEGAL_NESTED_UPDATE,
"One updating expression in a typeswitch case requires all branches to be updating or vacuous expressions.");
}
return new TypeswitchExpr(operandExpr,
caseExprs,
caseTypes,
varRefs,
defaultExpr,
updating,
vacOrUpdate == cases + 1);
}
protected Expr filterExpr(AST node) throws QueryException {
Expr expr = expr(node.getChild(0), true);
int noOfPredicates = node.getChildCount() - 1;
Expr[] predicates = new Expr[noOfPredicates];
boolean[] bindItem = new boolean[noOfPredicates];
boolean[] bindPos = new boolean[noOfPredicates];
boolean[] bindSize = new boolean[noOfPredicates];
for (int i = 0; i < noOfPredicates; i++) {
Binding itemBinding = table.bind(Bits.FS_DOT, SequenceType.ITEM);
Binding posBinding = table.bind(Bits.FS_POSITION, SequenceType.INTEGER);
Binding sizeBinding = table.bind(Bits.FS_LAST, SequenceType.INTEGER);
predicates[i] = expr(node.getChild(1 + i).getChild(0), true);
table.unbind();
table.unbind();
table.unbind();
bindItem[i] = itemBinding.isReferenced();
bindPos[i] = posBinding.isReferenced();
bindSize[i] = sizeBinding.isReferenced();
}
return new FilterExpr(expr, predicates, bindItem, bindPos, bindSize);
}
protected Expr insertExpr(AST node) throws QueryException {
final AST typeNode = node.getChild(0);
final Insert.InsertType insertType = switch (typeNode.getType()) {
case XQ.InsertInto -> Insert.InsertType.INTO;
case XQ.InsertBefore -> Insert.InsertType.BEFORE;
case XQ.InsertAfter -> Insert.InsertType.AFTER;
case XQ.InsertFirst -> Insert.InsertType.FIRST;
case XQ.InsertLast -> Insert.InsertType.LAST;
default -> throw new QueryException(ErrorCode.BIT_DYN_RT_ILLEGAL_STATE_ERROR,
"Unexpected AST expr node '%s' of type: %s",
typeNode,
typeNode.getType());
};
final Expr sourceExpr = expr(node.getChild(1), true);
final Expr targetExpr = expr(node.getChild(2), true);
return new Insert(sourceExpr, targetExpr, insertType);
}
protected Expr deleteExpr(AST node) throws QueryException {
Expr targetExpr = expr(node.getChild(0), true);
return new Delete(targetExpr);
}
protected Expr replaceExpr(AST node) throws QueryException {
boolean replaceNode = node.getType() == XQ.ReplaceNodeExpr;
Expr targetExpr = expr(node.getChild(0), true);
Expr sourceExpr = expr(node.getChild(1), true);
return replaceNode ? new ReplaceNode(sourceExpr, targetExpr) : new ReplaceValue(sourceExpr, targetExpr);
}
protected Expr renameExpr(AST node) throws QueryException {
Expr targetExpr = expr(node.getChild(0), true);
Expr sourceExpr = expr(node.getChild(1), true);
return new Rename(node.getStaticContext(), sourceExpr, targetExpr);
}
protected Expr transformExpr(AST node) throws QueryException {
AST current;
QNm varName;
int childCount = node.getChildCount();
Expr[] sourceExprs = new Expr[childCount - 2];
Binding[] bindings = new Binding[childCount - 2];
boolean[] referenced = new boolean[childCount - 2];
int c = 0;
while ((current = node.getChild(c)).getType() == XQ.CopyVariableBinding) {
varName = (QNm) current.getChild(0).getValue();
sourceExprs[c] = expr(current.getChild(1), true);
bindings[c++] = table.bind(varName, SequenceType.ITEM);
}
Expr modifyExpr = expr(current, false);
if (!modifyExpr.isUpdating() && !modifyExpr.isVacuous()) {
throw new QueryException(ErrorCode.ERR_UPDATING_OR_VACUOUS_EXPR_REQUIRED,
"Modify clause must not contain an expression that is non-updating and non-vacuous.");
}
Expr returnExpr = expr(node.getChild(++c), true);
for (int i = 0; i < childCount - 2; i++) {
if (bindings[i].isReferenced()) {
referenced[i] = true;
}
table.unbind();
}
return new Transform(sourceExprs, referenced, modifyExpr, returnExpr);
}
protected Expr quantifiedExpr(AST node) throws QueryException {
int pos = 0;
AST child = node.getChild(pos++);
boolean someQuantified = child.getType() == XQ.SomeQuantifier;
Expr bindingSequenceExpr = quantifiedBindings(new Start(), node, pos);
Function function;
if (someQuantified) {
function = BitFun.SOME_FUNC;
} else {
function = BitFun.EVERY_FUNC;
}
return new IfExpr(new FunctionExpr(node.getStaticContext(), function, bindingSequenceExpr), Bool.TRUE, Bool.FALSE);
}
protected Expr quantifiedBindings(Operator in, AST node, int pos) throws QueryException {
AST child = node.getChild(pos++);
if (child.getType() == XQ.QuantifiedBinding) {
AST varBinding = child.getChild(0);
QNm runVarName = (QNm) varBinding.getChild(0).getValue();
SequenceType type = SequenceType.ITEM_SEQUENCE;
if (varBinding.getChildCount() == 2) {
type = sequenceType(varBinding.getChild(1));
}
Expr sourceExpr = expr(child.getChild(1), true);
ForBind forBind = new ForBind(in, sourceExpr, false);
Binding runVarBinding = table.bind(runVarName, type);
Expr returnExpr = quantifiedBindings(forBind, node, pos);
table.unbind();
forBind.bindVariable(runVarBinding.isReferenced());
forBind.bindPosition(false);
return returnExpr;
} else {
return new PipeExpr(in, expr(child, true));
}
}
protected Expr functionCall(AST node) throws QueryException {
int childCount = node.getChildCount();
QNm name = (QNm) node.getValue();
if (JSONFun.JSON_PREFIX.equals(name.getPrefix()) && "null".equals(name.getLocalName())) {
return new Null();
}
Function function = ctx.getFunctions().resolve(name, childCount);
final var signature = function.getSignature();
final List newParamTypes = new ArrayList<>();
final var params = signature.getParams();
Expr[] args;
final List argumentPlaceHolderExprs = new ArrayList<>();
if (childCount > 0) {
args = new Expr[childCount];
for (int i = 0; i < childCount; i++) {
AST arg = node.getChild(i);
if (arg.getType() == XQ.ArgumentPlaceHolder) {
argumentPlaceHolderExprs.add(new PartialArgumentExpr());
newParamTypes.add(params[i]);
} else {
args[i] = expr(arg, true);
}
}
} else if (signature.defaultCtxItemType() != null) {
Expr contextItemRef = table.resolve(Bits.FS_DOT);
args = new Expr[] { contextItemRef };
} else {
args = new Expr[0];
}
if (argumentPlaceHolderExprs.isEmpty()) {
return new FunctionExpr(node.getStaticContext(), function, args);
} else {
final UDF udf = new UDF(name,
new Signature(signature.getResultType(), newParamTypes.toArray(new SequenceType[0])),
function.isUpdating());
udf.setExpr(function);
return new FunctionExpr(node.getStaticContext(), udf, args);
}
}
protected Expr documentExpr(AST node) throws QueryException {
final Binding binding = table.bind(Bits.FS_PARENT, SequenceType.ITEM);
final Expr contentExpr;
if (node.getChildCount() > 0) {
contentExpr = expr(node.getChild(0), false);
} else {
contentExpr = new EmptyExpr();
}
table.unbind();
final boolean bind = binding.isReferenced();
return new DocumentExpr(contentExpr, bind);
}
protected Expr elementExpr(AST node) throws QueryException {
int pos = 0;
int nsCnt = 0;
while (node.getChild(nsCnt).getType() == XQ.NamespaceDeclaration) {
nsCnt++;
}
ElementExpr.NS[] ns = new ElementExpr.NS[nsCnt];
for (int i = 0; i < nsCnt; i++) {
AST nsDecl = node.getChild(pos++);
if (nsDecl.getChildCount() == 2) {
String prefix = nsDecl.getChild(0).getStringValue();
String uri = nsDecl.getChild(1).getStringValue();
ns[i] = new ElementExpr.NS(prefix, uri);
} else {
String uri = nsDecl.getChild(0).getStringValue();
ns[i] = new ElementExpr.NS(null, uri);
}
}
Expr nameExpr = expr(node.getChild(pos++), true);
boolean appendOnly = appendOnly(node);
boolean bind = false;
Expr[] contentExpr;
if (node.getChildCount() > 0) {
Binding binding = table.bind(Bits.FS_PARENT, SequenceType.ITEM);
contentExpr = contentSequence(node.getChild(pos));
table.unbind();
bind = binding.isReferenced();
} else {
contentExpr = new Expr[0];
}
StaticContext sctx = node.getStaticContext();
return new ElementExpr(sctx, nameExpr, ns, contentExpr, bind, appendOnly);
}
protected Expr[] contentSequence(AST node) throws QueryException {
int childCount = node.getChildCount();
if (childCount == 0) {
return new Expr[0];
}
int size = 0;
Expr[] subExprs = new Expr[childCount];
String merged = null;
for (int i = 0; i < node.getChildCount(); i++) {
AST child = node.getChild(i);
if (child.getType() == XQ.Str) {
merged = merged == null ? child.getStringValue() : merged + child.getStringValue();
} else {
if (merged != null && !merged.isEmpty()) {
subExprs[size++] = new Str(merged);
}
merged = null;
subExprs[size++] = expr(child, true);
}
}
if (merged != null && !merged.isEmpty()) {
subExprs[size++] = new Str(merged);
}
return Arrays.copyOf(subExprs, size);
}
protected Expr attributeExpr(AST node) throws QueryException {
Expr nameExpr = expr(node.getChild(0), true);
Expr[] contentExpr = node.getChildCount() > 1 ? contentSequence(node.getChild(1)) : new Expr[0];
StaticContext sctx = node.getStaticContext();
return new AttributeExpr(sctx, nameExpr, contentExpr, appendOnly(node));
}
protected Expr commentExpr(AST node) throws QueryException {
Expr contentExpr = expr(node.getChild(0), true);
return new CommentExpr(contentExpr, appendOnly(node));
}
protected Expr textExpr(AST node) throws QueryException {
Expr contentExpr = expr(node.getChild(0), true);
return new TextExpr(contentExpr, appendOnly(node));
}
protected Expr piExpr(AST node) throws QueryException {
Expr nameExpr = expr(node.getChild(0), true);
Expr contentExpr = node.getChildCount() > 1 ? expr(node.getChild(1), true) : new EmptyExpr();
return new PIExpr(nameExpr, contentExpr, appendOnly(node));
}
private boolean appendOnly(AST node) throws QueryException {
AST parent = node.getParent();
if (parent == null) {
return false;
}
if (parent.getType() == XQ.ContentSequence) {
parent = parent.getParent();
}
boolean parentIsConstructor = parent.getType() == XQ.CompElementConstructor || parent.getType()
== XQ.CompDocumentConstructor;
if (parentIsConstructor) {
table.resolve(Bits.FS_PARENT);
}
return parentIsConstructor;
}
protected Expr andExpr(AST node) throws QueryException {
Expr firstExpr = expr(node.getChild(0), true);
Expr secondExpr = expr(node.getChild(1), true);
return new AndExpr(firstExpr, secondExpr);
}
protected Expr orExpr(AST node) throws QueryException {
Expr firstExpr = expr(node.getChild(0), true);
Expr secondExpr = expr(node.getChild(1), true);
return new OrExpr(firstExpr, secondExpr);
}
protected Expr ifExpr(AST node) throws QueryException {
Expr condExpr = expr(node.getChild(0), true);
Expr ifExpr = expr(node.getChild(1), false);
Expr elseExpr = expr(node.getChild(2), false);
if (ifExpr.isUpdating()) {
if (!elseExpr.isUpdating() && !elseExpr.isVacuous()) {
throw new QueryException(ErrorCode.ERR_UPDATE_ILLEGAL_NESTED_UPDATE,
"Single updating if branch is not allowed");
}
} else if (elseExpr.isUpdating()) {
if (!ifExpr.isUpdating() && !ifExpr.isVacuous()) {
throw new QueryException(ErrorCode.ERR_UPDATE_ILLEGAL_NESTED_UPDATE,
"Single updating else branch is not allowed");
}
}
return new IfExpr(condExpr, ifExpr, elseExpr);
}
protected Expr switchExpr(AST node) throws QueryException {
Expr opExpr = expr(node.getChild(0), true);
Expr[][] cases = new Expr[node.getChildCount() - 2][];
for (int i = 1; i < node.getChildCount() - 1; i++) {
AST caseClause = node.getChild(i);
Expr[] caseOps = new Expr[caseClause.getChildCount()];
for (int j = 0; j < caseClause.getChildCount(); j++) {
caseOps[j] = expr(caseClause.getChild(j), false);
}
cases[i - 1] = caseOps;
}
Expr dftExpr = expr(node.getChild(node.getChildCount() - 1), false);
return new SwitchExpr(opExpr, cases, dftExpr);
}
protected Expr variableRefExpr(AST node) throws QueryException {
return table.resolve((QNm) node.getValue());
}
protected Expr rangeExpr(AST node) throws QueryException {
Expr firstArg = expr(node.getChild(0), true);
Expr secondArg = expr(node.getChild(1), true);
return new RangeExpr(firstArg, secondArg);
}
protected Expr comparisonExpr(AST node) throws QueryException {
Expr firstArg = expr(node.getChild(1), true);
Expr secondArg = expr(node.getChild(2), true);
AST cmpNode = node.getChild(0);
return switch (cmpNode.getType()) {
case XQ.ValueCompEQ -> new VCmpExpr(Cmp.eq, firstArg, secondArg);
case XQ.ValueCompGE -> new VCmpExpr(Cmp.ge, firstArg, secondArg);
case XQ.ValueCompLE -> new VCmpExpr(Cmp.le, firstArg, secondArg);
case XQ.ValueCompLT -> new VCmpExpr(Cmp.lt, firstArg, secondArg);
case XQ.ValueCompGT -> new VCmpExpr(Cmp.gt, firstArg, secondArg);
case XQ.ValueCompNE -> new VCmpExpr(Cmp.ne, firstArg, secondArg);
case XQ.GeneralCompEQ -> new GCmpExpr(Cmp.eq, firstArg, secondArg);
case XQ.GeneralCompGE -> new GCmpExpr(Cmp.ge, firstArg, secondArg);
case XQ.GeneralCompLE -> new GCmpExpr(Cmp.le, firstArg, secondArg);
case XQ.GeneralCompLT -> new GCmpExpr(Cmp.lt, firstArg, secondArg);
case XQ.GeneralCompGT -> new GCmpExpr(Cmp.gt, firstArg, secondArg);
case XQ.GeneralCompNE -> new GCmpExpr(Cmp.ne, firstArg, secondArg);
case XQ.NodeCompIs -> new NodeCmpExpr(NodeCmpExpr.NodeCmp.Is, firstArg, secondArg);
case XQ.NodeCompFollows -> new NodeCmpExpr(NodeCmpExpr.NodeCmp.Following, firstArg, secondArg);
case XQ.NodeCompPrecedes -> new NodeCmpExpr(NodeCmpExpr.NodeCmp.Preceding, firstArg, secondArg);
default -> throw new QueryException(ErrorCode.BIT_DYN_RT_ILLEGAL_STATE_ERROR,
"Unexpected comparison: '%s'",
cmpNode);
};
}
protected Expr arithmeticExpr(AST node) throws QueryException {
final ArithmeticExpr.ArithmeticOp op = switch (node.getChild(0).getType()) {
case XQ.AddOp -> ArithmeticExpr.ArithmeticOp.PLUS;
case XQ.SubtractOp -> ArithmeticExpr.ArithmeticOp.MINUS;
case XQ.MultiplyOp -> ArithmeticExpr.ArithmeticOp.MULT;
case XQ.DivideOp -> ArithmeticExpr.ArithmeticOp.DIV;
case XQ.IDivideOp -> ArithmeticExpr.ArithmeticOp.IDIV;
case XQ.ModulusOp -> ArithmeticExpr.ArithmeticOp.MOD;
default -> null;
};
final Expr firstArg = expr(node.getChild(1), true);
final Expr secondArg = expr(node.getChild(2), true);
return new ArithmeticExpr(op, firstArg, secondArg);
}
/*
* The compilation of path expressions is a bit tricky. A path of the form
* E1/E2/../EN must be evaluated with "left-deep semantics", i.e.,
* ((E1/E2)/..)/EN and each step EI needs to have the current context item
* (focus, $fs:dot) bound, from the preceding step EI-1.
*/
protected Expr pathExpr(AST node) throws QueryException {
Expr e1 = expr(node.getChild(0), true);
for (int i = 1; i < node.getChildCount(); i++) {
Binding itemBinding = table.bind(Bits.FS_DOT, SequenceType.NODE);
Binding posBinding = table.bind(Bits.FS_POSITION, SequenceType.INTEGER);
Binding sizeBinding = table.bind(Bits.FS_LAST, SequenceType.INTEGER);
AST step = node.getChild(i);
Expr e2 = expr(step, true);
table.unbind();
table.unbind();
table.unbind();
boolean bindItem = itemBinding.isReferenced();
boolean bindPos = posBinding.isReferenced();
boolean bindSize = sizeBinding.isReferenced();
boolean lastStep = i + 1 == node.getChildCount();
boolean skipDDO = step.checkProperty("skipDDO");
boolean checkInput = step.checkProperty("checkInput");
e1 = new PathStepExpr(e1, e2, bindItem, bindPos, bindSize, lastStep, skipDDO, checkInput);
}
return e1;
}
protected Expr stepExpr(AST node) throws QueryException {
AST child = node.getChild(0);
Accessor axis;
if (child.getType() == XQ.AxisSpec) {
axis = axis(child.getChild(0));
child = node.getChild(1);
} else {
axis = Accessor.CHILD;
}
Expr in = table.resolve(Bits.FS_DOT);
NodeType test = nodeTest(child, axis.getAxis());
int noOfPredicates = Math.max(node.getChildCount() - 2, 0);
Expr[] filter = new Expr[noOfPredicates];
boolean[] bindItem = new boolean[noOfPredicates];
boolean[] bindPos = new boolean[noOfPredicates];
boolean[] bindSize = new boolean[noOfPredicates];
for (int i = 0; i < noOfPredicates; i++) {
Binding itemBinding = table.bind(Bits.FS_DOT, SequenceType.ITEM);
Binding posBinding = table.bind(Bits.FS_POSITION, SequenceType.INTEGER);
Binding sizeBinding = table.bind(Bits.FS_LAST, SequenceType.INTEGER);
filter[i] = expr(node.getChild(2 + i).getChild(0), true);
table.unbind();
table.unbind();
table.unbind();
bindItem[i] = itemBinding.isReferenced();
bindPos[i] = posBinding.isReferenced();
bindSize[i] = sizeBinding.isReferenced();
}
return new StepExpr(axis, test, in, filter, bindItem, bindPos, bindSize);
}
protected Accessor axis(AST node) throws QueryException {
return switch (node.getType()) {
case XQ.CHILD -> Accessor.CHILD;
case XQ.DESCENDANT -> Accessor.DESCENDANT;
case XQ.DESCENDANT_OR_SELF -> Accessor.DESCENDANT_OR_SELF;
case XQ.ATTRIBUTE -> Accessor.ATTRIBUTE;
case XQ.PARENT -> Accessor.PARENT;
case XQ.ANCESTOR -> Accessor.ANCESTOR;
case XQ.ANCESTOR_OR_SELF -> Accessor.ANCESTOR_OR_SELF;
case XQ.FOLLOWING_SIBLING -> Accessor.FOLLOWING_SIBLING;
case XQ.FOLLOWING -> Accessor.FOLLOWING;
case XQ.PRECEDING -> Accessor.PRECEDING;
case XQ.PRECEDING_SIBLING -> Accessor.PRECEDING_SIBLING;
case XQ.SELF -> Accessor.SELF;
case XQ.NEXT -> Accessor.NEXT;
case XQ.PREVIOUS -> Accessor.PREVIOUS;
case XQ.FUTURE -> Accessor.FUTURE;
case XQ.FUTURE_OR_SELF -> Accessor.FUTURE_OR_SELF;
case XQ.PAST -> Accessor.PAST;
case XQ.PAST_OR_SELF -> Accessor.PAST_OR_SELF;
case XQ.FIRST -> Accessor.FIRST;
case XQ.LAST -> Accessor.LAST;
case XQ.ALL_TIMES -> Accessor.ALL_TIME;
default -> throw new QueryException(ErrorCode.BIT_DYN_RT_NOT_IMPLEMENTED_YET_ERROR,
"Suport for document axis '%s' not implemented yet",
node);
};
}
protected SequenceType sequenceType(AST node) throws QueryException {
AST child = node.getChild(0);
if (child.getType() == XQ.EmptySequenceType) {
return SequenceType.EMPTY_SEQUENCE;
}
ItemType itemType = itemType(child);
Cardinality cardinality = Cardinality.One;
if (node.getChildCount() == 2) {
cardinality = switch (node.getChild(1).getType()) {
case XQ.CardinalityOneOrMany -> Cardinality.OneOrMany;
case XQ.CardinalityZeroOrMany -> Cardinality.ZeroOrMany;
case XQ.CardinalityZeroOrOne -> Cardinality.ZeroOrOne;
default -> Cardinality.One;
};
}
return new SequenceType(itemType, cardinality);
}
protected ItemType itemType(AST node) throws QueryException {
return switch (node.getType()) {
case XQ.ItemType -> AnyItemType.ANY;
case XQ.AtomicOrUnionType -> atomicOrUnionType(node);
case XQ.StructuredItemTest -> new AnyStructuredItemType();
case XQ.KindTestObject -> new ObjectType();
case XQ.KindTestArray -> new ArrayType();
case XQ.KindTestNull -> new NullType();
case XQ.JsonItemTest -> new AnyJsonItemType();
default -> kindTest(node);
};
}
protected ItemType atomicOrUnionType(AST node) throws QueryException {
Type type = resolveType((QNm) node.getChild(0).getValue(), false);
return new AtomicType(type);
}
protected NodeType nodeTest(AST node, Axis axis) throws QueryException {
if (node.getType() == XQ.NameTest) {
return nameTest(node, axis);
} else {
return kindTest(node);
}
}
protected NodeType kindTest(AST node) throws QueryException {
switch (node.getType()) {
case XQ.KindTestAnyKind:
return AnyNodeType.ANY_NODE;
case XQ.KindTestText:
return new TextType();
case XQ.KindTestElement:
return elementTest(node);
case XQ.KindTestAttribute:
return attributeTest(node);
case XQ.KindTestComment:
return new CommentType();
case XQ.KindTestDocument:
return documentTest(node);
case XQ.KindTestPi:
if (node.getChildCount() == 0) {
return new PIType();
} else {
return new PIType(node.getChild(0).getStringValue());
}
case XQ.KindTestSchemaElement:
return schemaElementTest(node);
case XQ.KindTestSchemaAttribute:
return schemaAttributeTest(node);
default:
throw new QueryException(ErrorCode.BIT_DYN_RT_ILLEGAL_STATE_ERROR, "KindTest translation not implemented yet.");
}
}
protected AttributeType schemaAttributeTest(AST child) throws QueryException {
QNm qname = (QNm) child.getChild(0).getValue();
return new AttributeType(qname, ctx.getTypes().resolveSchemaType(qname));
}
protected ElementType schemaElementTest(AST child) throws QueryException {
QNm qname = (QNm) child.getChild(0).getValue();
return new ElementType(qname, ctx.getTypes().resolveSchemaType(qname));
}
protected DocumentType documentTest(AST child) throws QueryException {
if (child.getChildCount() == 0)
return new DocumentType();
else if (child.getChild(0).getType() == XQ.KindTestElement)
return new DocumentType(elementTest(child.getChild(0)));
else
return new DocumentType(schemaElementTest(child.getChild(0)));
}
protected AttributeType attributeTest(AST child) throws QueryException {
if (child.getChildCount() == 0)
return new AttributeType();
else if (child.getChildCount() == 1)
return new AttributeType(qNameOrWildcard(child.getChild(0)));
else
return new AttributeType(qNameOrWildcard(child.getChild(0)),
resolveType((QNm) child.getChild(1).getValue(), false));
}
protected ElementType elementTest(AST child) throws QueryException {
if (child.getChildCount() == 0)
return new ElementType();
else if (child.getChildCount() == 1)
return new ElementType(qNameOrWildcard(child.getChild(0)));
else
return new ElementType(qNameOrWildcard(child.getChild(0)),
resolveType((QNm) child.getChild(1).getValue(), false));
}
protected QNm qNameOrWildcard(AST name) throws QueryException {
return name.getType() == XQ.Wildcard ? null : (QNm) name.getValue();
}
protected NodeType nameTest(AST child, Axis axis) throws QueryException {
AST name = child.getChild(0);
switch (name.getType()) {
case XQ.Wildcard:
if (axis != Axis.ATTRIBUTE) {
return new ElementType(null);
} else {
return new AttributeType(null);
}
case XQ.NSWildcardNameTest:
return new NSWildcardNameTest(axis == Axis.ATTRIBUTE ? Kind.ATTRIBUTE : Kind.ELEMENT, name.getStringValue());
case XQ.NSNameWildcardTest:
return new NSNameWildcardTest(axis == Axis.ATTRIBUTE ? Kind.ATTRIBUTE : Kind.ELEMENT,
ctx.getNamespaces().resolve(name.getStringValue()));
default:
if (axis != Axis.ATTRIBUTE) {
return new ElementType((QNm) name.getValue());
} else {
return new AttributeType((QNm) name.getValue());
}
}
}
protected Type resolveType(QNm qname, boolean atomic) throws QueryException {
if (atomic) {
return ctx.getTypes().resolveAtomicType(qname);
} else {
return ctx.getTypes().resolveType(qname);
}
}
protected Expr sequenceExpr(AST node) throws QueryException {
boolean allVacouousOrUpdating = false;
Expr[] subExpr = new Expr[node.getChildCount()];
for (int i = 0; i < node.getChildCount(); i++) {
subExpr[i] = expr(node.getChild(i), false);
if (subExpr[i].isUpdating()) {
if (!allVacouousOrUpdating && i > 0) {
// check if all preceding expressions are vacuous
for (int j = 0; j < i; j++) {
if (!subExpr[j].isVacuous()) {
throw new QueryException(ErrorCode.ERR_UPDATE_ILLEGAL_NESTED_UPDATE,
"Illegal nested updating expression.");
}
}
}
allVacouousOrUpdating = true;
} else if (allVacouousOrUpdating) {
if (!subExpr[i].isVacuous()) {
throw new QueryException(ErrorCode.ERR_UPDATE_ILLEGAL_NESTED_UPDATE, "Illegal nested updating expression.");
}
}
}
return new SequenceExpr(subExpr);
}
protected Expr flowrExpr(AST node) throws QueryException {
final int childCount = node.getChildCount();
final ClauseBinding cb = flowrClause(new ClauseBinding(null, new Start()), node, 0, childCount - 2);
final Expr returnExpr = expr(node.getChild(childCount - 1).getChild(0), false);
cb.unbind();
final Expr pipeExpr = new PipeExpr(cb.operator, returnExpr);
return pipeExpr;
}
private ClauseBinding flowrClause(ClauseBinding in, AST node, int pos, int maxPos) throws QueryException {
ClauseBinding cb = in;
while (pos <= maxPos) {
final AST clause = node.getChild(pos++);
cb = switch (clause.getType()) {
case XQ.ForClause -> forClause(clause, cb);
case XQ.LetClause -> letClause(clause, cb);
case XQ.WhereClause -> whereClause(clause, cb);
case XQ.OrderByClause -> orderByClause(clause, cb);
case XQ.CountClause -> countClause(clause, cb);
case XQ.GroupByClause -> groupByClause(clause, cb);
default -> throw new QueryException(ErrorCode.BIT_DYN_RT_ILLEGAL_STATE_ERROR,
"Unknown flowr clause type: %s",
clause);
};
}
return cb;
}
protected ClauseBinding countClause(AST node, ClauseBinding in) throws QueryException {
AST countVarDecl = node.getChild(0);
QNm posVarName = (QNm) countVarDecl.getChild(0).getValue();
SequenceType posVarType = SequenceType.ITEM_SEQUENCE;
if (countVarDecl.getChildCount() == 2) {
posVarType = sequenceType(countVarDecl.getChild(1));
}
final Binding binding = table.bind(posVarName, posVarType);
final Count count = new Count(in.operator);
return new ClauseBinding(in, count, binding) {
@Override
public void unbind() {
super.unbind();
count.bind(binding.isReferenced());
table.unbind();
}
};
}
protected ClauseBinding orderByClause(AST node, ClauseBinding in) throws QueryException {
int orderBySpecCount = node.getChildCount();
Expr[] orderByExprs = new Expr[orderBySpecCount];
Ordering.OrderModifier[] orderBySpec = new Ordering.OrderModifier[orderBySpecCount];
for (int i = 0; i < orderBySpecCount; i++) {
AST orderBy = node.getChild(i);
orderByExprs[i] = expr(orderBy.getChild(0), true);
orderBySpec[i] = orderModifier(orderBy);
}
OrderBy orderBy = new OrderBy(in.operator, orderByExprs, orderBySpec);
return new ClauseBinding(in, orderBy);
}
protected Ordering.OrderModifier orderModifier(AST orderBy) {
boolean asc = true;
boolean emptyLeast = true;
String collation = null;
for (int i = 1; i < orderBy.getChildCount(); i++) {
AST modifier = orderBy.getChild(i);
if (modifier.getType() == XQ.OrderByKind) {
AST direction = modifier.getChild(0);
asc = direction.getType() == XQ.ASCENDING;
} else if (modifier.getType() == XQ.OrderByEmptyMode) {
AST empty = modifier.getChild(0);
emptyLeast = empty.getType() == XQ.LEAST;
} else if (modifier.getType() == XQ.Collation) {
collation = modifier.getChild(0).getStringValue();
}
}
return new Ordering.OrderModifier(asc, emptyLeast, collation);
}
protected ClauseBinding groupByClause(AST node, ClauseBinding in) throws QueryException {
int pos = 0;
while (node.getChild(pos).getType() == XQ.GroupBySpec) {
pos++;
}
int grpSpecCnt = pos;
// collect additional aggregate bindings
List bnds = new ArrayList<>();
while (node.getChild(pos).getType() == XQ.AggregateSpec) {
AST aggSpec = node.getChild(pos);
QNm var = (QNm) aggSpec.getChild(0).getValue();
for (int j = 1; j < aggSpec.getChildCount(); j++) {
AST aggBinding = aggSpec.getChild(j);
AST typedVarBnd = aggBinding.getChild(0);
Aggregate agg = aggregate(aggBinding.getChild(1));
QNm aggVar = (QNm) typedVarBnd.getChild(0).getValue();
SequenceType aggType = SequenceType.ITEM_SEQUENCE;
if (typedVarBnd.getChildCount() == 2) {
aggType = sequenceType(typedVarBnd.getChild(1));
}
bnds.add(new AggregateBinding(var, aggVar, aggType, agg));
}
pos++;
}
Aggregate dftAgg = aggregate(node.getChild(pos).getChild(0));
Aggregate[] addAggs = new Aggregate[bnds.size()];
for (int i = 0; i < bnds.size(); i++) {
AggregateBinding bnd = bnds.get(i);
addAggs[i] = bnd.agg;
}
boolean sequential = node.checkProperty("sequential");
GroupBy groupBy = new GroupBy(in.operator, dftAgg, addAggs, grpSpecCnt, sequential);
// resolve positions grouping variables
for (int i = 0; i < grpSpecCnt; i++) {
QNm grpVarName = (QNm) node.getChild(i).getChild(0).getValue();
table.resolve(grpVarName, groupBy.group(i));
}
// resolve positions for additional aggregates
for (int i = 0; i < bnds.size(); i++) {
AggregateBinding bnd = bnds.get(i);
table.resolve(bnd.srcVar, groupBy.aggregate(i));
}
// bind additional aggregates
for (final AggregateBinding bnd : bnds) {
table.bind(bnd.aggVar, bnd.aggVarType);
// fake binding
table.resolve(bnd.aggVar);
}
return new ClauseBinding(in, groupBy);
}
protected Aggregate aggregate(AST node) throws QueryException {
return switch (node.getType()) {
case XQ.SequenceAgg -> Aggregate.SEQUENCE;
case XQ.CountAgg -> Aggregate.COUNT;
case XQ.SumAgg -> Aggregate.SUM;
case XQ.AvgAgg -> Aggregate.AVG;
case XQ.MinAgg -> Aggregate.MIN;
case XQ.MaxAgg -> Aggregate.MAX;
case XQ.SingleAgg -> Aggregate.SINGLE;
default -> throw new QueryException(ErrorCode.BIT_DYN_RT_ILLEGAL_STATE_ERROR, "Unknown aggregate type: %s", node);
};
}
protected ClauseBinding whereClause(AST node, ClauseBinding in) throws QueryException {
Expr expr = anyExpr(node.getChild(0));
Select select = new Select(in.operator, expr);
return new ClauseBinding(in, select);
}
protected ClauseBinding letClause(AST node, ClauseBinding in) throws QueryException {
int letClausePos = 0;
AST letClause = node;
AST letVarDecl = letClause.getChild(letClausePos++);
QNm letVarName = (QNm) letVarDecl.getChild(0).getValue();
SequenceType letVarType = SequenceType.ITEM_SEQUENCE;
if (letVarDecl.getChildCount() == 2) {
letVarType = sequenceType(letVarDecl.getChild(1));
}
Expr sourceExpr = expr(letClause.getChild(letClausePos++), true);
final Binding binding = table.bind(letVarName, letVarType);
final LetBind letBind = new LetBind(in.operator, sourceExpr);
return new ClauseBinding(in, letBind, binding) {
@Override
public void unbind() {
super.unbind();
letBind.bind(binding.isReferenced());
table.unbind();
}
};
}
protected ClauseBinding forClause(AST node, ClauseBinding in) throws QueryException {
QNm posVarName = null;
int forClausePos = 0;
final AST forClause = node;
final AST runVarDecl = forClause.getChild(forClausePos++);
final QNm runVarName = (QNm) runVarDecl.getChild(0).getValue();
SequenceType runVarType = SequenceType.ITEM_SEQUENCE;
if (runVarDecl.getChildCount() == 2) {
runVarType = sequenceType(runVarDecl.getChild(1));
}
AST posBindingOrSourceExpr = forClause.getChild(forClausePos++);
if (posBindingOrSourceExpr.getType() == XQ.TypedVariableBinding) {
posVarName = (QNm) posBindingOrSourceExpr.getChild(0).getValue();
posBindingOrSourceExpr = forClause.getChild(forClausePos);
}
final Expr sourceExpr = expr(posBindingOrSourceExpr, true);
final Binding runVarBinding = table.bind(runVarName, runVarType);
final Binding posBinding = posVarName != null ? table.bind(posVarName, SequenceType.INTEGER) : null;
final ForBind forBind = new ForBind(in.operator, sourceExpr, false);
return new ClauseBinding(in, forBind, runVarBinding, posBinding) {
@Override
public void unbind() {
super.unbind();
if (posBinding != null) {
forBind.bindPosition(posBinding.isReferenced());
table.unbind();
}
forBind.bindVariable(runVarBinding.isReferenced());
table.unbind();
}
};
}
// BEGIN Custom array syntax extension
protected Expr arrayAccessExpr(AST node) throws QueryException {
Expr expr = expr(node.getChild(0), true);
Expr index = expr(node.getChild(1), true);
return new ArrayAccessExpr(expr, index);
}
protected Expr arrayIndexSliceExpr(AST node) throws QueryException {
Expr expr = expr(node.getChild(0), true);
Expr firstIndex = expr(node.getChild(1), true);
Expr secondIndex = expr(node.getChild(2), true);
Expr increment = expr(node.getChild(3), true);
return new ArrayIndexSliceExpr(expr, firstIndex, secondIndex, increment);
}
protected Expr arrayExpr(AST node) throws QueryException {
int cnt = node.getChildCount();
boolean[] flatten = new boolean[cnt];
Expr[] expr = new Expr[cnt];
for (int i = 0; i < cnt; i++) {
AST field = node.getChild(i);
flatten[i] = field.getType() == XQ.FlattenedField;
expr[i] = expr(field.getChild(0), true);
}
return new ArrayExpr(expr, flatten);
}
// END Custom array syntax extension
// BEGIN Custom object syntax extension
protected Expr objectExpr(AST node) throws QueryException {
int cnt = node.getChildCount();
ObjectExpr.Field[] fields = new ObjectExpr.Field[cnt];
for (int i = 0; i < cnt; i++) {
AST field = node.getChild(i);
if (field.getType() == XQ.KeyValueField) {
fields[i] = new ObjectExpr.KeyValueField(expr(field.getChild(0), true), expr(field.getChild(1), true));
} else {
fields[i] = new ObjectExpr.ObjectField(expr(field, true));
}
}
return new ObjectExpr(fields);
}
protected Expr derefExpr(AST node) throws QueryException {
Expr object = expr(node.getChild(0), true);
Expr field = expr(node.getChild(1), true);
return new DerefExpr(object, field);
}
protected Expr derefDescendantExpr(AST node) throws QueryException {
Expr object = expr(node.getChild(0), true);
Expr[] fields = new Expr[node.getChildCount() - 1];
for (int i = 1; i < node.getChildCount(); i++) {
fields[i - 1] = expr(node.getChild(i), true);
}
return new DerefDescendantExpr(object, fields);
}
protected Expr projectionExpr(AST node) throws QueryException {
final Expr record = expr(node.getChild(0), true);
final Expr[] fields = new Expr[node.getChildCount() - 1];
for (int i = 1; i < node.getChildCount(); i++) {
fields[i - 1] = expr(node.getChild(i), true);
}
return new ProjectionExpr(record, fields);
}
// END Custom object syntax extension
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy