001: // Copyright (c) 2003 Per M.A. Bothner.
002: // This is free software; for terms and warranty disclaimer see ./COPYING.
003:
004: package gnu.xquery.util;
005:
006: import gnu.lists.*;
007: import gnu.mapping.*;
008: import gnu.bytecode.*;
009: import gnu.expr.*;
010: import gnu.kawa.xml.*;
011: import gnu.math.IntNum;
012: import gnu.kawa.functions.*;
013: import gnu.kawa.reflect.OccurrenceType;
014:
015: /** Implements XPath path expression.
016: * The XPath expression E1/E2 is compiled into:
017: * (relative-step E1 (lambda (dot position last) E2)).
018: */
019:
020: public class RelativeStep extends MethodProc implements CanInline,
021: Inlineable {
022: public static final RelativeStep relativeStep = new RelativeStep();
023:
024: public int numArgs() {
025: return 0x2002;
026: }
027:
028: public void apply(CallContext ctx) throws Throwable {
029: Object arg = ctx.getNextArg();
030: Object next = ctx.getNextArg();
031: Procedure proc = (Procedure) next;
032: Consumer out = ctx.consumer;
033: IntNum countObj;
034: Nodes values;
035: if (arg instanceof Nodes)
036: values = (Nodes) arg;
037: else {
038: values = new Nodes();
039: Values.writeValues(arg, values);
040: }
041: int count = values.size();
042: int it = 0;
043: countObj = IntNum.make(count);
044: RelativeStepFilter filter = new RelativeStepFilter(out);
045: for (int pos = 1; pos <= count; pos++) {
046: it = values.nextPos(it);
047: Object dot = values.getPosPrevious(it);
048: proc.check3(dot, IntNum.make(pos), countObj, ctx);
049: Values.writeValues(ctx.runUntilValue(), filter);
050: }
051: filter.finish();
052: }
053:
054: public Expression inline(ApplyExp exp, ExpWalker walker) {
055: Expression[] args = exp.getArgs();
056: Expression exp1 = args[0];
057: Expression exp2 = args[1];
058: LambdaExp lexp2;
059: Compilation comp = walker.getCompilation();
060: if (!(exp2 instanceof LambdaExp)
061: // The following optimization breaks when interpreting, because
062: // then CoerceToNodes may not work.
063: || !comp.mustCompile
064: || (lexp2 = (LambdaExp) exp2).min_args != 3
065: || lexp2.max_args != 3)
066: return exp;
067:
068: lexp2.setInlineOnly(true);
069: lexp2.returnContinuation = exp;
070:
071: exp2 = lexp2.body;
072:
073: Declaration dotArg = lexp2.firstDecl();
074: Declaration posArg = dotArg.nextDecl();
075: Declaration lastArg = posArg.nextDecl();
076: // Splice out the "last" argument - we'll move it out.
077: // The remaining two arguments are suitable for a ValuesMap.
078: posArg.setNext(lastArg.nextDecl());
079: lastArg.setNext(null);
080: lexp2.min_args = 2;
081: lexp2.max_args = 2;
082:
083: Type type1 = exp1.getType();
084: if (type1 != null && NodeType.anyNodeTest.compare(type1) == -3) {
085: Language language = walker.getCompilation().getLanguage();
086: String message = "step input is "
087: + language.formatType(type1)
088: + " - not a node sequence";
089: walker.getMessages().error('e', message);
090: return new ErrorExp(message);
091: }
092:
093: Type rtype = exp.getTypeRaw();
094: Type rtypePrime;
095: int nodeCompare;
096: if (rtype == null || rtype == Type.pointer_type) {
097: Type type2 = exp2.getType();
098: rtypePrime = OccurrenceType.itemPrimeType(type2);
099: nodeCompare = NodeType.anyNodeTest.compare(rtypePrime);
100: if (nodeCompare >= 0)
101: rtype = NodeSetType.getInstance(rtypePrime);
102: else
103: rtype = OccurrenceType.getInstance(rtypePrime, 0, -1);
104: exp.setType(rtype);
105: }
106: if (lastArg.getCanRead()) {
107: ClassType typeNodes = CoerceNodes.typeNodes;
108: comp.letStart();
109: Declaration sequence = comp.letVariable(null, typeNodes,
110: new ApplyExp(CoerceNodes.coerceNodes,
111: new Expression[] { exp1 }));
112: comp.letEnter();
113:
114: Method sizeMethod = typeNodes.getDeclaredMethod("size", 0);
115: Expression lastInit = new ApplyExp(sizeMethod,
116: new Expression[] { new ReferenceExp(sequence) });
117: LetExp lastLet = new LetExp(new Expression[] { lastInit });
118: lastLet.addDeclaration(lastArg);
119: lastLet.body = new ApplyExp(exp.getFunction(),
120: new Expression[] { new ReferenceExp(sequence),
121: lexp2 });
122: return comp.letDone(lastLet);
123: }
124:
125: ApplyExp result = exp;
126:
127: // Try to rewrite A/B[P] to (A/B)[P].
128: // This only works if P doesn't depend in position() or last().
129: if (exp2 instanceof ApplyExp) {
130: ApplyExp aexp2 = (ApplyExp) exp2;
131: Object proc2 = aexp2.getFunction().valueIfConstant();
132: Expression vexp2;
133: if (proc2 instanceof ValuesFilter
134: && (vexp2 = aexp2.getArgs()[1]) instanceof LambdaExp) {
135: LambdaExp lvexp2 = (LambdaExp) vexp2;
136: Declaration dot2 = lvexp2.firstDecl();
137: Declaration pos2;
138: if (dot2 != null && (pos2 = dot2.nextDecl()) != null
139: && pos2.nextDecl() == null
140: && !pos2.getCanRead()
141: // If the predicate can evaluate to a number, then the
142: // optimization is unsafe, since we implicitly
143: // compare against position().
144: && ClassType.make("java.lang.Number").compare(
145: lvexp2.body.getType()) == -3) {
146: exp2 = aexp2.getArg(0);
147: lexp2.body = exp2;
148: aexp2.setArg(0, exp);
149: result = aexp2;
150: }
151: }
152: }
153: // Now we can rewrite 'descendant-or-self::node()/B' (which is the
154: // expansion of the abbreviated syntax '//B') to /descdendant::B'.
155: if (exp1 instanceof ApplyExp && exp2 instanceof ApplyExp) {
156: ApplyExp aexp1 = (ApplyExp) exp1;
157: ApplyExp aexp2 = (ApplyExp) exp2;
158: Object p1 = aexp1.getFunction().valueIfConstant();
159: Object p2 = aexp2.getFunction().valueIfConstant();
160: Expression exp12;
161: if (p1 == relativeStep && p2 instanceof ChildAxis
162: && aexp1.getArgCount() == 2
163: && (exp12 = aexp1.getArg(1)) instanceof LambdaExp) {
164: LambdaExp lexp12 = (LambdaExp) exp12;
165: ApplyExp aexp12;
166: if (lexp12.body instanceof ApplyExp
167: && (aexp12 = (ApplyExp) lexp12.body)
168: .getFunction().valueIfConstant() == DescendantOrSelfAxis.anyNode) {
169: exp.setArg(0, aexp1.getArg(0));
170: aexp2
171: .setFunction(new QuoteExp(DescendantAxis
172: .make(((ChildAxis) p2)
173: .getNodePredicate())));
174: }
175: }
176: }
177:
178: return result;
179: }
180:
181: public void compile(ApplyExp exp, Compilation comp, Target target) {
182: Expression[] args = exp.getArgs();
183: Expression exp1 = args[0];
184: Expression exp2 = args[1];
185: if (target instanceof IgnoreTarget) {
186: exp1.compile(comp, target);
187: exp2.compile(comp, target);
188: return;
189: }
190:
191: Type rtype = exp.getTypeRaw();
192: if (rtype == null) // should never happen
193: rtype = Type.pointer_type;
194: Type rtypePrime = OccurrenceType.itemPrimeType(rtype);
195: int nodeCompare = NodeType.anyNodeTest.compare(rtypePrime);
196: // 'A' - atomic; 'N' - nodes; 'S' - pre-sorted nodes; ' ' - unknown.
197: char expectedKind;
198: if (nodeCompare >= 0)
199: expectedKind = 'N';
200: else if (nodeCompare == -3)
201: expectedKind = 'A';
202: else
203: expectedKind = ' ';
204: TreeScanner step = extractStep(exp2);
205: if (step != null) {
206: Type type1 = exp1.getType();
207: if (step instanceof ChildAxis
208: || step instanceof AttributeAxis
209: || step instanceof SelfAxis) {
210: if (type1 instanceof NodeSetType
211: || (expectedKind == 'N' && OccurrenceType
212: .itemCountIsZeroOrOne(exp1.getType())))
213: expectedKind = 'S';
214: /*
215: // It's presumably more efficient to sort the argument
216: // nodes rather than the result nodes. FIXME
217: else
218: {
219: exp1 = SortNodes(exp1);
220: expectedKind = 'S';
221: }
222: */
223: }
224: }
225:
226: if (!(target instanceof ConsumerTarget || (target instanceof SeriesTarget && (expectedKind == 'A' || expectedKind == 'S')))) {
227: ConsumerTarget.compileUsingConsumer(exp, comp, target);
228: return;
229: }
230:
231: CodeAttr code = comp.getCode();
232: Target mtarget;
233: Scope scope = code.pushScope();
234: Variable mconsumer;
235: Variable tconsumer;
236: ClassType mclass;
237:
238: if (expectedKind == 'A' || expectedKind == 'S') {
239: mtarget = target;
240: mclass = null;
241: mconsumer = null;
242: tconsumer = null;
243: } else {
244: // We need a helper consumer.
245: Method initMethod;
246: if (expectedKind == 'N') {
247: mclass = ClassType.make("gnu.kawa.xml.SortedNodes");
248: initMethod = mclass.getDeclaredMethod("<init>", 0);
249: } else {
250: mclass = ClassType
251: .make("gnu.xquery.util.RelativeStepFilter");
252: initMethod = mclass.getDeclaredMethod("<init>", 1);
253: }
254: mconsumer = scope.addVariable(code, mclass, null);
255: mtarget = new ConsumerTarget(mconsumer);
256: code.emitNew(mclass);
257: code.emitDup(mclass);
258: tconsumer = ((ConsumerTarget) target).getConsumerVariable();
259: if (expectedKind != 'N')
260: code.emitLoad(tconsumer);
261: code.emitInvoke(initMethod);
262: code.emitStore(mconsumer);
263: }
264:
265: ValuesMap.compileInlined((LambdaExp) exp2, exp1, 1, null, comp,
266: mtarget);
267:
268: // Now finish up from the helper consumer.
269: if (expectedKind == 'N') {
270: code.emitLoad(mconsumer);
271: code.emitLoad(tconsumer);
272: code.emitInvokeStatic(Compilation.typeValues
273: .getDeclaredMethod("writeValues", 2));
274: } else if (expectedKind == ' ') {
275: code.emitLoad(mconsumer);
276: code.emitInvoke(mclass.getDeclaredMethod("finish", 0));
277: }
278:
279: code.popScope();
280: }
281:
282: public Type getReturnType(Expression[] args) {
283: // Needlessly convervative, but it shouldn't matter, since this
284: // shouldn't be called if the ApplyExp.setType has been done.
285: return Type.pointer_type;
286: }
287:
288: public static TreeScanner extractStep(Expression exp) {
289: for (;;) {
290: if (!(exp instanceof ApplyExp))
291: return null;
292: ApplyExp aexp = (ApplyExp) exp;
293: Expression func = aexp.getFunction();
294: if (func instanceof QuoteExp) {
295: Object value = ((QuoteExp) func).getValue();
296: if (value instanceof TreeScanner)
297: return (TreeScanner) value;
298: // This doesn't work, if we've already inlined ValuesFilter. FIXME
299: if (value instanceof ValuesFilter) {
300: exp = aexp.getArgs()[0];
301: continue;
302: }
303: }
304: return null;
305: }
306: }
307: }
|