001: // Copyright (c) 2001, 2003, 2004 Per M.A. Bothner and Brainfood Inc.
002: // This is free software; for terms and warranty disclaimer see ./COPYING.
003:
004: package gnu.kawa.functions;
005:
006: import gnu.lists.*;
007: import gnu.mapping.*;
008: import gnu.bytecode.*;
009: import gnu.expr.*;
010: import gnu.math.IntNum;
011:
012: /** Map a function over a value sequence, yielding a new sequence.
013: * Normally, the function takes one argument, the item in the sequence.
014: * If startCounter is non-negative, a position index is also passed.
015: * Used to implement XQuery's 'for' form.
016: */
017:
018: public class ValuesMap extends MethodProc implements CanInline,
019: Inlineable {
020: public static final ValuesMap valuesMap = new ValuesMap(-1);
021: public static final ValuesMap valuesMapWithPos = new ValuesMap(1);
022:
023: private ValuesMap(int startCounter) {
024: this .startCounter = startCounter;
025: }
026:
027: /** If non-negative also define a counter variable.
028: * Used for XQuery's 'at' clause in a FLWOR expression. */
029: private final int startCounter;
030:
031: public int numArgs() {
032: return 0x2002;
033: }
034:
035: public void apply(CallContext ctx) throws Throwable {
036: Procedure proc = (Procedure) ctx.getNextArg();
037: Consumer out = ctx.consumer;
038: Object val = ctx.getNextArg();
039: Procedure.checkArgCount(proc, 1);
040: if (val instanceof Values) {
041: int ipos = 0;
042: int count = startCounter;
043: Values values = (Values) val;
044: while ((ipos = values.nextPos(ipos)) != 0) {
045: Object v = values.getPosPrevious(ipos);
046: if (startCounter >= 0)
047: proc.check2(v, IntNum.make(count++), ctx);
048: else
049: proc.check1(v, ctx);
050: ctx.runUntilDone();
051: }
052: } else {
053: if (startCounter >= 0)
054: proc.check2(val, IntNum.make(startCounter), ctx);
055: else
056: proc.check1(val, ctx);
057: ctx.runUntilDone();
058: }
059: }
060:
061: /** If we can inline, return LambdaExp for first arg; otherwise null. */
062: private LambdaExp canInline(ApplyExp exp) {
063: Expression[] args = exp.getArgs();
064: Expression arg0;
065: // FIXME Could if needed wrap expr in LambdaExp:
066: if (args.length == 2 && (arg0 = args[0]) instanceof LambdaExp) {
067: LambdaExp lexp = (LambdaExp) arg0;
068: if (lexp.min_args == lexp.max_args
069: && lexp.min_args == (startCounter >= 0 ? 2 : 1))
070: return lexp;
071: }
072: return null;
073: }
074:
075: public Expression inline(ApplyExp exp, ExpWalker walker) {
076: LambdaExp lexp = canInline(exp);
077: if (lexp != null) {
078: lexp.setInlineOnly(true);
079: lexp.returnContinuation = exp;
080: }
081: return exp;
082: }
083:
084: public void compile(ApplyExp exp, Compilation comp, Target target) {
085: LambdaExp lambda = canInline(exp);
086: if (lambda == null) {
087: ApplyExp.compile(exp, comp, target);
088: return;
089: }
090: Expression[] args = exp.getArgs();
091: if (!(target instanceof IgnoreTarget
092: || target instanceof ConsumerTarget || target instanceof SeriesTarget)) {
093: ConsumerTarget.compileUsingConsumer(exp, comp, target);
094: return;
095: }
096: Expression vals = args[1];
097: compileInlined(lambda, vals, startCounter, null, comp, target);
098: }
099:
100: public static void compileInlined(LambdaExp lambda,
101: Expression vals, int startCounter, Method matchesMethod,
102: Compilation comp, Target target) {
103: Declaration param = lambda.firstDecl();
104: CodeAttr code = comp.getCode();
105: SeriesTarget starget = new SeriesTarget();
106: starget.scope = code.pushScope();
107: Variable counter;
108: Declaration counterDecl;
109: if (startCounter >= 0) {
110: counter = starget.scope.addVariable(code, Type.int_type,
111: "position");
112: code.emitPushInt(startCounter);
113: code.emitStore(counter);
114: counterDecl = new Declaration(counter);
115: } else {
116: counter = null;
117: counterDecl = null;
118: }
119: starget.function = new Label(code);
120: if (target instanceof SeriesTarget)
121: starget.done = ((SeriesTarget) target).done;
122: else
123: starget.done = new Label(code);
124: // If the param Declaration is captured, then it gets messy initializing
125: // it. So just cheat and create a helper variable.
126: if (param.isSimple())
127: param.allocateVariable(code);
128: else
129: param = new Declaration(code.addLocal(param.getType(),
130: param.getName()));
131: starget.param = param;
132: Type retAddrType = Type.pointer_type;
133: Variable retAddr = code.addLocal(retAddrType);
134: vals.compileWithPosition(comp, starget);
135:
136: starget.function.define(code);
137: code.pushType(retAddrType);
138: code.emitStore(retAddr);
139: Expression[] args;
140: if (startCounter >= 0) {
141: args = new Expression[] { new ReferenceExp(param),
142: new ReferenceExp(counterDecl) };
143: } else
144: args = new Expression[] { new ReferenceExp(param) };
145: Expression app = new ApplyExp(lambda, args);
146: if (matchesMethod != null) {
147: // Major kludge - used by ValuesFilter.
148: if (app.getType().getImplementationType() != Type.boolean_type)
149: app = new ApplyExp(matchesMethod, new Expression[] {
150: app, new ReferenceExp(counterDecl) });
151: app = new IfExp(app, new ReferenceExp(param),
152: QuoteExp.voidExp);
153: }
154: if (target instanceof SeriesTarget) {
155: SeriesTarget atarget = (SeriesTarget) target;
156: Label done = atarget.done;
157: atarget.done = null;
158: app.compile(comp, target);
159: atarget.done = done;
160: } else
161: app.compile(comp, target);
162: if (startCounter >= 0) {
163: code.emitInc(counter, (short) 1);
164: }
165: code.emitRet(retAddr);
166: code.popScope();
167: if (!(target instanceof SeriesTarget))
168: starget.done.define(code);
169: }
170:
171: public Type getReturnType(Expression[] args) {
172: return Type.pointer_type;
173: }
174: }
|