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

net.sf.saxon.functions.hof.PartialApply Maven / Gradle / Ivy

There is a newer version: 12.5
Show newest version
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2020 Saxonica Limited
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

package net.sf.saxon.functions.hof;

import net.sf.saxon.expr.*;
import net.sf.saxon.expr.elab.*;
import net.sf.saxon.expr.parser.*;
import net.sf.saxon.om.FunctionItem;
import net.sf.saxon.om.Sequence;
import net.sf.saxon.trace.ExpressionPresenter;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.transpile.CSharpSuppressWarnings;
import net.sf.saxon.type.AnyFunctionType;
import net.sf.saxon.type.ItemType;
import net.sf.saxon.type.SpecificFunctionType;
import net.sf.saxon.value.SequenceType;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Supplier;

/**
 * This expression class implements the operation of currying (or performing partial application) of a function.
 * That is, it takes a function and a set of argument bindings as input, and produces a new function as output.
 */

public class PartialApply extends Expression {

    private final Operand baseOp;
    private final Operand[] boundArgumentsOp; // contains null where the question marks appear

    /**
     * Create a partial function application expression
     *
     * @param base           the expression that returns the function to be called
     * @param boundArguments an array in which each element is either an expression to be bound to the corresponding
     *                       argument, or null to represent a placeholder ("?" in the source syntax)
     */

    public PartialApply(Expression base, Expression[] boundArguments) {
        baseOp = new Operand(this, base, OperandRole.INSPECT);
        adoptChildExpression(base);
        boundArgumentsOp = new Operand[boundArguments.length];
        for (int i=0; i argRole =
                            () -> new RoleDiagnostic(RoleDiagnostic.FUNCTION, "saxon:call", pos);
                    SequenceType requiredArgType = ((SpecificFunctionType) baseType).getArgumentTypes()[i];
                    argTypes[i] = requiredArgType;
                    Expression a3 = tc.staticTypeCheck(
                            arg, requiredArgType, argRole, visitor);
                    if (a3 != arg) {
                        op.setChildExpression(a3);
                    }
                }
            }
        }


        requiredFunctionType = SequenceType.makeSequenceType(
                new SpecificFunctionType(argTypes,
                        (baseType instanceof AnyFunctionType) ? ((AnyFunctionType) baseType).getResultType() : SequenceType.ANY_SEQUENCE),
                        StaticProperty.EXACTLY_ONE);

        Supplier role =
                () -> new RoleDiagnostic(RoleDiagnostic.FUNCTION, "saxon:call", 0);
        setBaseExpression(tc.staticTypeCheck(getBaseExpression(), requiredFunctionType, role, visitor));
        return this;
    }

    /*@NotNull*/
    @Override
    public ItemType getItemType() {
        ItemType baseItemType = getBaseExpression().getItemType();
        SequenceType resultType = SequenceType.ANY_SEQUENCE;
        if (baseItemType instanceof SpecificFunctionType) {
            resultType = ((SpecificFunctionType) baseItemType).getResultType();
        }
        int placeholders = getNumberOfPlaceHolders();
        SequenceType[] argTypes = new SequenceType[placeholders];
        if (baseItemType instanceof SpecificFunctionType) {
            for (int i = 0, j = 0; i < boundArgumentsOp.length; i++) {
                if (boundArgumentsOp[i] == null) {
                    argTypes[j++] = ((SpecificFunctionType) baseItemType).getArgumentTypes()[i];
                }
            }
        } else {
            Arrays.fill(argTypes, SequenceType.ANY_SEQUENCE);
        }
        return new SpecificFunctionType(argTypes, resultType);
    }

    @Override
    public Iterable operands() {
        List operanda = new ArrayList(boundArgumentsOp.length + 1);
        operanda.add(baseOp);
        for (Operand o : boundArgumentsOp) {
            if (o != null) {
                operanda.add(o);
            }
        }
        return operanda;
    }

    public int getNumberOfArguments() {
        return boundArgumentsOp.length;
    }

    public Expression getArgument(int n) {
        Operand o = boundArgumentsOp[n];
        return o==null ? null : o.getChildExpression();
    }

    @Override
    public int getImplementationMethod() {
        return EVALUATE_METHOD;
    }

    /**
     * Is this expression the same as another expression?
     *
     * @param other the expression to be compared with this one
     * @return true if the two expressions are statically equivalent
     */
    @Override
    public boolean equals(Object other) {
        if (!(other instanceof PartialApply)) {
            return false;
        } else {
            PartialApply pa2 = (PartialApply)other;
            if (!getBaseExpression().isEqual(pa2.getBaseExpression())) {
                return false;
            }
            if (boundArgumentsOp.length != pa2.boundArgumentsOp.length) {
                return false;
            }
            for (int i=0; iThe toString() method for an expression attempts to give a representation of the expression
     * in an XPath-like form.

*

For subclasses of Expression that represent XPath expressions, the result should always be a string that * parses as an XPath 3.0 expression. The expression produced should be equivalent to the original making certain * assumptions about the static context. In general the expansion will make no assumptions about namespace bindings, * except that (a) the prefix "xs" is used to refer to the XML Schema namespace, and (b) the default funcion namespace * is assumed to be the "fn" namespace.

*

In the case of XSLT instructions and XQuery expressions, the toString() method gives an abstracted view of the syntax * that is not designed in general to be parseable.

* * @return a representation of the expression as a string */ @Override @CSharpSuppressWarnings("UnsafeIteratorConversion") public String toString() { StringBuilder buff = new StringBuilder(64); boolean par = getBaseExpression().operands().iterator().hasNext(); if (par) { buff.append("(" + getBaseExpression().toString() + ")"); } else { buff.append(getBaseExpression().toString()); } buff.append("("); for (int i = 0; i < boundArgumentsOp.length; i++) { if (boundArgumentsOp[i] == null) { buff.append("?"); } else { buff.append(boundArgumentsOp[i].getChildExpression().toString()); } if (i != boundArgumentsOp.length - 1) { buff.append(", "); } } buff.append(")"); return buff.toString(); } /** * Evaluate this expression at run-time * * @param context The XPath dynamic evaluation context * @return the result of the function, or null to represent an empty sequence * @throws net.sf.saxon.trans.XPathException * if a dynamic error occurs during evaluation of the function. */ @Override public FunctionItem evaluateItem(XPathContext context) throws XPathException { return (FunctionItem)makeElaborator().elaborateForItem().eval(context); } /** * Get a name identifying the kind of expression, in terms meaningful to a user. * * @return a name identifying the kind of expression, in terms meaningful to a user. * The name will always be in the form of a lexical XML QName, and should match the name used * in export() output displaying the expression. */ @Override public String getExpressionName() { return "partialApply"; } /** * Make an elaborator for this expression * * @return an appropriate {@link Elaborator} */ @Override public Elaborator getElaborator() { return new PartialApplyElaborator(); } private static class PartialApplyElaborator extends ItemElaborator { @Override public ItemEvaluator elaborateForItem() { PartialApply expr = (PartialApply)getExpression(); ItemEvaluator functionEval = expr.getBaseExpression().makeElaborator().elaborateForItem(); final int len = expr.boundArgumentsOp.length; SequenceEvaluator[] boundArgumentsEvaluators = new SequenceEvaluator[len]; for (int i = 0; i < len; i++) { if (expr.boundArgumentsOp[i] == null) { boundArgumentsEvaluators[i] = null; } else { boundArgumentsEvaluators[i] = expr.boundArgumentsOp[i].getChildExpression().makeElaborator().eagerly(); } } return context -> { FunctionItem f = (FunctionItem) functionEval.eval(context); assert f != null; Sequence[] values = new Sequence[len]; for (int i = 0; i < boundArgumentsEvaluators.length; i++) { if (boundArgumentsEvaluators[i] != null) { values[i] = boundArgumentsEvaluators[i].evaluate(context); } } return new CurriedFunction(f, values); }; } } } // Copyright (c) 2011-2023 Saxonica Limited




© 2015 - 2024 Weber Informatics LLC | Privacy Policy