*/

    protected Expression parseForExpression() throws XPathException {
        int offset = t.currentTokenStartOffset;
        Expression whereCondition = null;
        int whereOffset = -1;
        //boolean stableOrder = false;
        List<FLWORClause> clauseList = new ArrayList<FLWORClause>(4);
        boolean foundGroupBy = false;
        while (true) {
            if (t.currentToken == Token.FOR) {
                parseForClause(clauseList);
            } else if (t.currentToken == Token.LET) {
                parseLetClause(clauseList);
            } else if (t.currentToken == Token.GROUP_BY) {
                parseGroupByClause(clauseList);
                foundGroupBy = true;
            } else if (t.currentToken == Token.OUTER_FOR) {
//                nextToken();
//                expect(Token.DOLLAR);
                parseOuterForClause(clauseList);
            } else {
                break;
            }
        }
        if (t.currentToken == Token.WHERE || isKeyword("where")) {
            whereOffset = t.currentTokenStartOffset;
            nextToken();
            whereCondition = parseExprSingle();
        }
        int orderByOffset = t.currentTokenStartOffset;
        if (isKeyword("stable")) {
            // we read the "stable" keyword but ignore it; Saxon ordering is always stable
            nextToken();
            if (!isKeyword("order")) {
                grumble("'stable' must be followed by 'order by'");
            }
        }
        List sortSpecList = null;
        if (isKeyword("order")) {
            t.setState(Tokenizer.BARE_NAME_STATE);
            nextToken();
            if (!isKeyword("by")) {
                grumble("'order' must be followed by 'by'");
            }
            t.setState(Tokenizer.DEFAULT_STATE);
            nextToken();
            sortSpecList = parseSortDefinition();
        }
        int returnOffset = t.currentTokenStartOffset;
        expect(Token.RETURN);
        t.setState(Tokenizer.DEFAULT_STATE);
        nextToken();
        Expression action = parseExprSingle();
        action = makeTracer(returnOffset, action, Location.RETURN_EXPRESSION, null);

        // If there is an order by clause, we modify the "return" expression so that it
        // returns a tuple containing the actual return value, plus the value of
        // each of the sort keys. We then wrap the entire FLWR expression inside a
        // TupleSorter that sorts the stream of tuples according to the sort keys,
        // discarding the sort keys and returning only the true result. The tuple
        // is implemented as a Java object wrapped inside an ObjectValue, which is
        // a general-purpose wrapper for objects that don't fit in the XPath type system.

        if (sortSpecList != null) {
            TupleExpression exp = new TupleExpression(1 + sortSpecList.size());
            setLocation(exp);
            exp.setExpression(0, action);
            for (int i = 0; i < sortSpecList.size(); i++) {
                try {
                    RoleLocator role = new RoleLocator(RoleLocator.ORDER_BY, "FLWR", i);
                    //role.setSourceLocator(makeLocator());
                    Expression sk =
                            TypeChecker.staticTypeCheck(((SortSpec)sortSpecList.get(i)).sortKey,
                                    SequenceType.OPTIONAL_ATOMIC,
                                    false,
                                    role, ExpressionVisitor.make(env));
                    exp.setExpression(i + 1, sk);
                } catch (XPathException err) {
                    grumble(err.getMessage());
                }
            }
            action = exp;
        }

        // if there is a "where" condition, we implement this by wrapping an if/then/else
        // around the "return" expression. Optimization is done later.

        if (whereCondition != null) {
            action = Choose.makeConditional(whereCondition, action);
            action = makeTracer(whereOffset, action, Location.WHERE_CLAUSE, null);
            setLocation(action);
        }

        if (foundGroupBy) {
            action = processGroupingExpression(clauseList, action);
        } else {
            for (int i = clauseList.size() - 1; i >= 0; i--) {
                Object clause = clauseList.get(i);
                if (clause instanceof ExpressionParser.ForClause) {
                    ExpressionParser.ForClause fc = (ExpressionParser.ForClause)clause;
                    ForExpression exp = (ForExpression)fc.rangeVariable;
                    exp.setPositionVariable(fc.positionVariable);
                    exp.setLocationId(env.getLocationMap().allocateLocationId(env.getSystemId(), t.getLineNumber(fc.offset)));
                    exp.setSequence(fc.sequence);
                    exp.setAction(action);
                    action = makeTracer(fc.offset, exp, Location.FOR_EXPRESSION, fc.rangeVariable.getVariableQName());
                } else {
                    LetClause lc = (LetClause)clause;
                    LetExpression exp = lc.variable;
                    exp.setLocationId(env.getLocationMap().allocateLocationId(env.getSystemId(), t.getLineNumber(lc.offset)));
                    //exp.setSequence(lc.value);
                    exp.setAction(action);
                    action = makeTracer(lc.offset, exp, Location.LET_EXPRESSION, lc.variable.getVariableQName());
                }
            }
        }

        // Now wrap the whole expression in a TupleSorter if there is a sort specification

        if (sortSpecList != null) {
            SortKeyDefinition[] keys = new SortKeyDefinition[sortSpecList.size()];
            for (int i = 0; i < keys.length; i++) {
                SortSpec spec = (SortSpec)sortSpecList.get(i);
                SortKeyDefinition key = new SortKeyDefinition();
                key.setSortKey(((SortSpec) sortSpecList.get(i)).sortKey);
                key.setOrder(new StringLiteral(spec.ascending ? "ascending" : "descending"));
                key.setEmptyLeast(spec.emptyLeast);

                if (spec.collation != null) {
                    final StringCollator comparator = env.getCollation(spec.collation);
                    if (comparator == null) {
                        grumble("Unknown collation '" + spec.collation + '\'', "XQST0076");
                    }
                    key.setCollation(comparator);
                }
                keys[i] = key;
            }
            TupleSorter sorter = new TupleSorter(action, keys);
            setLocation(sorter);
            action = makeTracer(orderByOffset, sorter, Location.ORDER_BY_CLAUSE, null);
        }

        // undeclare all the range variables
       
        for (int i = clauseList.size()-1; i>=0; i--) {
            FLWORClause clause = clauseList.get(i);
            for (int n = 0; n < clause.numberOfRangeVariables(); n++) {
                undeclareRangeVariable();
            }
        }

        setLocation(action, offset);
        return action;

    }