package org.basex.query.func; import static org.basex.query.util.Err.*; import org.basex.query.QueryContext; import org.basex.query.QueryException; import org.basex.query.expr.Expr; import org.basex.query.expr.VarRef; import org.basex.query.item.AtomType; import org.basex.query.item.Empty; import org.basex.query.item.FItem; import org.basex.query.item.FuncType; import org.basex.query.item.Item; import org.basex.query.item.Int; import org.basex.query.item.QNm; import org.basex.query.item.Value; import org.basex.query.iter.Iter; import org.basex.query.util.Err; import org.basex.query.util.Var; import org.basex.util.InputInfo; /** * Functions on functions. * * @author BaseX Team 2005-12, BSD License * @author Leo Woerteler */ public final class FNFunc extends StandardFunc { /** * Constructor. * @param ii input info * @param f function definition * @param e arguments */ public FNFunc(final InputInfo ii, final Function f, final Expr... e) { super(ii, f, e); } @Override public Iter iter(final QueryContext ctx) throws QueryException { switch(sig) { case MAP: return map(ctx); case FILTER: return filter(ctx); case MAP_PAIRS: return zip(ctx); case FOLD_LEFT: return foldLeft(ctx); case FOLD_RIGHT: return foldRight(ctx); default: return super.iter(ctx); } } @Override public Item item(final QueryContext ctx, final InputInfo ii) throws QueryException { switch(sig) { case FUNCTION_ARITY: return Int.get(getFun(0, FuncType.ANY_FUN, ctx).arity()); case FUNCTION_NAME: return getFun(0, FuncType.ANY_FUN, ctx).fName(); case PARTIAL_APPLY: return partApp(ctx, ii); case FUNCTION_LOOKUP: return lookup(ctx, ii); default: return super.item(ctx, ii); } } /** * Looks up the specified function item. * @param ctx query context * @param ii input info * @return function item * @throws QueryException query exception */ private Item lookup(final QueryContext ctx, final InputInfo ii) throws QueryException { final QNm name = (QNm) checkType(expr[0].item(ctx, ii), AtomType.QNM); final long arity = checkItr(expr[1], ctx); try { return UserFuncs.get(name, arity, true, ctx, ii); } catch(final QueryException e) { // function not found return null; } } /** * Partially applies the function to one argument. * @param ctx query context * @param ii input info * @return function item * @throws QueryException query exception */ private Item partApp(final QueryContext ctx, final InputInfo ii) throws QueryException { final FItem f = getFun(0, FuncType.ANY_FUN, ctx); final long pos = expr.length == 2 ? 0 : checkItr(expr[2], ctx) - 1; final int arity = f.arity(); if(pos < 0 || pos >= arity) INVPOS.thrw(ii, f.info(), pos + 1); final FuncType ft = (FuncType) f.type; final Var[] vars = new Var[arity - 1]; final Expr[] vals = new Expr[arity]; vals[(int) pos] = expr[1]; for(int i = 0, j = 0; i < arity - 1; i++, j++) { if(i == pos) j++; vars[i] = ctx.uniqueVar(ii, ft.args[j]); vals[j] = new VarRef(ii, vars[i]); } return new PartFunc(ii, new DynamicFunc(ii, f, vals), vars).comp(ctx).item(ctx, ii); } /** * Maps a function onto a sequence of items. * @param ctx query context * @return sequence of results * @throws QueryException exception */ private Iter map(final QueryContext ctx) throws QueryException { final FItem f = withArity(0, 1, ctx); final Iter xs = expr[1].iter(ctx); return new Iter() { /** Results. */ Iter ys = Empty.ITER; @Override public Item next() throws QueryException { do { final Item it = ys.next(); if(it != null) return it; final Item x = xs.next(); if(x == null) return null; ys = f.invIter(ctx, input, x); } while(true); } }; } /** * Filters the given sequence with the given predicate. * @param ctx query context * @return filtered sequence * @throws QueryException query exception */ private Iter filter(final QueryContext ctx) throws QueryException { final FItem f = withArity(0, 1, ctx); final Iter xs = expr[1].iter(ctx); return new Iter() { @Override public Item next() throws QueryException { do { final Item it = xs.next(); if(it == null) return null; if(checkBln(checkNoEmpty(f.invItem(ctx, input, it)), ctx)) return it; } while(true); } }; } /** * Zips two sequences with the given zipper function. * @param ctx query context * @return sequence of results * @throws QueryException query exception */ private Iter zip(final QueryContext ctx) throws QueryException { final FItem zipper = withArity(0, 2, ctx); final Iter xs = expr[1].iter(ctx); final Iter ys = expr[2].iter(ctx); return new Iter() { /** Results. */ Iter zs = Empty.ITER; @Override public Item next() throws QueryException { do { final Item it = zs.next(); if(it != null) return it; final Item x = xs.next(), y = ys.next(); if(x == null || y == null) return null; zs = zipper.invIter(ctx, input, x, y); } while(true); } }; } /** * Folds a sequence into a return value, starting from the left. * @param ctx query context * @return resulting sequence * @throws QueryException query exception */ private Iter foldLeft(final QueryContext ctx) throws QueryException { final FItem f = withArity(0, 2, ctx); final Iter xs = expr[2].iter(ctx); Item x = xs.next(); // don't convert to a value if not necessary if(x == null) return expr[1].iter(ctx); Value sum = ctx.value(expr[1]); do sum = f.invValue(ctx, input, sum, x); while((x = xs.next()) != null); return sum.iter(); } /** * Folds a sequence into a return value, starting from the left. * @param ctx query context * @return resulting sequence * @throws QueryException query exception */ private Iter foldRight(final QueryContext ctx) throws QueryException { final FItem f = withArity(0, 2, ctx); final Value xs = ctx.value(expr[2]); // evaluate start value lazily if it's passed straight through if(xs.isEmpty()) return expr[1].iter(ctx); Value res = ctx.value(expr[1]); for(long i = xs.size(); --i >= 0;) res = f.invValue(ctx, input, xs.itemAt(i), res); return res.iter(); } /** * Checks the type of the given function item. * @param p position * @param t type * @param ctx query context * @return function item * @throws QueryException query exception */ private FItem getFun(final int p, final FuncType t, final QueryContext ctx) throws QueryException { return (FItem) checkType(checkItem(expr[p], ctx), t); } /** * Casts and checks the function item for its arity. * @param p position of the function * @param a arity * @param ctx query context * @return function item * @throws QueryException query exception */ private FItem withArity(final int p, final int a, final QueryContext ctx) throws QueryException { final Item f = checkItem(expr[p], ctx); if(!f.type.isFunction() || ((FItem) f).arity() != a) Err.type(this, FuncType.arity(a), f); return (FItem) f; } @Override public boolean uses(final Use u) { return (sig == Function.PARTIAL_APPLY || sig == Function.FUNCTION_LOOKUP) && u == Use.CTX || u == Use.X30 || super.uses(u); } }