/*
 * Copyright 2001-2004 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * $Id: xpath.cup 351910 2005-12-03 10:55:53Z zongaro $
 */

/*
 * @author Jacek Ambroziak
 * @author Santiago Pericas-Geertsen
 * @author Morten Jorgensen
 * @author G. Todd Miller
 */

package org.apache.xalan.xsltc.compiler;

import java.util.Stack;
import java.util.Vector;
import java.io.StringReader;
import java_cup.runtime.*;

import org.apache.xml.dtm.DTM;
import org.apache.xalan.xsltc.DOM;
import org.apache.xml.dtm.Axis;
import org.apache.xalan.xsltc.runtime.Operators;
import org.apache.xalan.xsltc.compiler.util.ErrorMsg;

parser code {:
    /**
     * Used by function calls with no args.
     */
    static public final Vector EmptyArgs = new Vector(0);

    /**
     * Reference to non-existing variable.
     */
    static public final VariableRef DummyVarRef = null;

    /**
     * Reference to the Parser class.
     */
    private Parser _parser;
    private XSLTC  _xsltc;

    /**
     * String representation of the expression being parsed.
     */
    private String _expression;

    /**
     * Line number where this expression/pattern was declared.
     */
    private int _lineNumber = 0;

    /**
     * Reference to the symbol table.
     */
    public SymbolTable _symbolTable;

    public XPathParser(Parser parser) {
        _parser = parser;
	_xsltc = parser.getXSLTC();
        _symbolTable = parser.getSymbolTable();
    }

    public int getLineNumber() {
        return _lineNumber;
    }

    public QName getQNameIgnoreDefaultNs(String name) {
          return _parser.getQNameIgnoreDefaultNs(name);
    }    

    public QName getQName(String namespace, String prefix, String localname) {
        return _parser.getQName(namespace, prefix, localname);
    }        

    public void setMultiDocument(boolean flag) {
          _xsltc.setMultiDocument(flag);
    }

    public void setCallsNodeset(boolean flag) {
          _xsltc.setCallsNodeset(flag);
    }

    public void setHasIdCall(boolean flag) {
          _xsltc.setHasIdCall(flag); 
    }
    

    /**
     * This method is similar to findNodeType(int, Object) except that it
     * creates a StepPattern instead of just returning a node type. It also 
     * differs in the way it handles "{uri}:*" and "{uri}:@*". The last two 
     * patterns are expanded as "*[namespace-uri() = 'uri']" and 
     * "@*[namespace-uri() = 'uri']", respectively. This expansion considerably 
     * simplifies the grouping of patterns in the Mode class. For this
     * expansion to be correct, the priority of the pattern/template must be
     * set to -0.25 (when no other predicates are present).
     */
    public StepPattern createStepPattern(int axis, Object test, Vector predicates) {
	int nodeType;

	if (test == null) {  // "*"
	    nodeType = (axis == Axis.ATTRIBUTE) ? NodeTest.ATTRIBUTE : 
		(axis == Axis.NAMESPACE) ? -1 : NodeTest.ELEMENT;

	    return new StepPattern(axis, nodeType, predicates);
        }
        else if (test instanceof Integer) {
	    nodeType = ((Integer) test).intValue();

	    return new StepPattern(axis, nodeType, predicates);
        }
        else {
	    QName name = (QName)test;
	    boolean setPriority = false;

	    if (axis == Axis.NAMESPACE) {
		nodeType = (name.toString().equals("*")) ? -1
				: _xsltc.registerNamespacePrefix(name);;
            }
	    else {
		final String uri = name.getNamespace();
		final String local = name.getLocalPart();
		final QName namespace_uri = 
		    _parser.getQNameIgnoreDefaultNs("namespace-uri");

		// Expand {uri}:* to *[namespace-uri() = 'uri'] - same for @*
		if (uri != null && (local.equals("*") || local.equals("@*"))) {
		    if (predicates == null) {
			predicates = new Vector(2);
		    }

		    // Priority is set by hand if no other predicates exist
		    setPriority = (predicates.size() == 0);

		    predicates.add(
			new Predicate(
			    new EqualityExpr(Operators.EQ, 
				new NamespaceUriCall(namespace_uri), 
				new LiteralExpr(uri))));
		}

		if (local.equals("*")) {
		    nodeType = (axis == Axis.ATTRIBUTE) ? NodeTest.ATTRIBUTE
			: NodeTest.ELEMENT;
		}
		else if (local.equals("@*")) {
		    nodeType = NodeTest.ATTRIBUTE;
		}
		else {
		    nodeType = (axis == Axis.ATTRIBUTE) ? _xsltc.registerAttribute(name)
			: _xsltc.registerElement(name); 
		}
	    }

	    final StepPattern result = new StepPattern(axis, nodeType, predicates); 

	    // Set priority for case prefix:* and prefix:@* (no predicates)
	    if (setPriority) {
		result.setPriority(-0.25);
	    }

	    return result;
	}
    }

    public int findNodeType(int axis, Object test) {
	if (test == null) {  // *
	    return (axis == Axis.ATTRIBUTE) ? 
		NodeTest.ATTRIBUTE :
		(axis == Axis.NAMESPACE) ? -1 : NodeTest.ELEMENT;
        }
        else if (test instanceof Integer) {
            return ((Integer)test).intValue();
        }
        else {
	    QName name = (QName)test;

	    if (axis == Axis.NAMESPACE) {
		return (name.toString().equals("*")) ? -1
		    : _xsltc.registerNamespacePrefix(name);
            }

	    if (name.getNamespace() == null) {
		final String local = name.getLocalPart();

		if (local.equals("*")) {
		    return (axis == Axis.ATTRIBUTE) ? NodeTest.ATTRIBUTE
			: NodeTest.ELEMENT;
		}
		else if (local.equals("@*")) {
		    return NodeTest.ATTRIBUTE;
		}
	    }

	    return (axis == Axis.ATTRIBUTE) ? _xsltc.registerAttribute(name)
		: _xsltc.registerElement(name); 
        }
    }

    /**
     * Parse the expression passed to the current scanner. If this
     * expression contains references to local variables and it will be 
     * compiled in an external module (not in the main class) request 
     * the current template to create a new variable stack frame.
     *
     * @param lineNumber Line where the current expression is defined.
     * @param external   Set to <tt>true</tt> if this expression is
     *                   compiled in a separate module.
     * 
     */
    public Symbol parse(String expression, int lineNumber) throws Exception {
        try {
	    _expression = expression;
	    _lineNumber = lineNumber;
	    return super.parse();
        }
        catch (IllegalCharException e) {
            ErrorMsg err = new ErrorMsg(ErrorMsg.ILLEGAL_CHAR_ERR,
                                        lineNumber, e.getMessage());
            _parser.reportError(Constants.FATAL, err);
        }
        return null;
    }

    /**
     * Lookup a variable or parameter in the symbol table given its name. 
     *
     * @param name Name of the symbol being looked up.
     */
    final SyntaxTreeNode lookupName(QName name) {
        // Is it a local var or param ?
        final SyntaxTreeNode result = _parser.lookupVariable(name);
	if (result != null)
            return(result);
        else
	    return(_symbolTable.lookupName(name));
    }

    public final void addError(ErrorMsg error) {
	_parser.reportError(Constants.ERROR, error);
    } 
           
    public void report_error(String message, Object info) {
	final ErrorMsg err = new ErrorMsg(ErrorMsg.SYNTAX_ERR, _lineNumber, 
	    _expression);
	_parser.reportError(Constants.FATAL, err);
    }

    public void report_fatal_error(String message, Object info) {
        // empty
    }
    
    public RelativeLocationPath insertStep(Step step, RelativeLocationPath rlp) {
	if (rlp instanceof Step) {
	    return new ParentLocationPath(step, (Step) rlp);
	}
	else if (rlp instanceof ParentLocationPath) {
	    final ParentLocationPath plp = (ParentLocationPath) rlp;
	    final RelativeLocationPath newrlp = insertStep(step, plp.getPath());
	    return new ParentLocationPath(newrlp, plp.getStep());
	}
	else {
	    addError(new ErrorMsg(ErrorMsg.INTERNAL_ERR, "XPathParser.insertStep"));
	    return rlp;
	}
    }

    /**
     * Returns true if the axis applies to elements only. The axes
     * child, attribute, namespace, descendant result in non-empty
     * nodesets only if the context node is of type element.
     */
    public boolean isElementAxis(int axis) {
	return (axis == Axis.CHILD || axis == Axis.ATTRIBUTE ||
		axis == Axis.NAMESPACE || axis == Axis.DESCENDANT);
    }
:}

terminal SLASH, DOT, LBRACK, RBRACK, VBAR, LPAREN, RPAREN, STAR, COMMA;
terminal DOLLAR, ATSIGN;
terminal DDOT, DCOLON, DSLASH;
terminal EQ, NE;
terminal LT, GT, LE, GE;
terminal PLUS, MINUS, DIV, MOD, MULT;
terminal String Literal;
terminal String QNAME;
terminal ID, KEY, TEXT, NODE, OR, AND, COMMENT, PI, PIPARAM, PRECEDINGSIBLING;
terminal SELF, PARENT, CHILD, ATTRIBUTE, ANCESTOR, ANCESTORORSELF, DESCENDANT; 
terminal DESCENDANTORSELF, FOLLOWING, FOLLOWINGSIBLING, NAMESPACE, PRECEDING;
terminal Double REAL;
terminal Long INT;
terminal PATTERN, EXPRESSION;

non terminal SyntaxTreeNode TopLevel;
    
non terminal Expression Expr, Argument, LocationPath;
non terminal Expression Predicate, FilterExpr, Step;
non terminal Expression OrExpr, AndExpr, EqualityExpr;
non terminal Expression RelationalExpr, AdditiveExpr;
non terminal Expression MultiplicativeExpr, UnaryExpr;
non terminal Expression VariableReference, FunctionCall;
non terminal Expression PrimaryExpr, UnionExpr, PathExpr, AbbreviatedStep;
non terminal Expression RelativeLocationPath, AbbreviatedRelativeLocationPath;
non terminal Expression AbsoluteLocationPath, AbbreviatedAbsoluteLocationPath;

non terminal Object NodeTest, NameTest;

non terminal IdKeyPattern IdKeyPattern;
non terminal Pattern Pattern;
non terminal Pattern LocationPathPattern;
non terminal StepPattern ProcessingInstructionPattern;
non terminal RelativePathPattern RelativePathPattern;
non terminal StepPattern StepPattern;
non terminal Object NodeTestPattern, NameTestPattern;

non terminal Vector Predicates, NonemptyArgumentList;
non terminal QName QName, FunctionName, VariableName;
non terminal Integer AxisName, AxisSpecifier;
non terminal Integer ChildOrAttributeAxisSpecifier;

precedence left VBAR;
precedence left OR;
precedence left AND;
precedence nonassoc EQ, NE;
precedence left LT, GT, LE, GE;
    
precedence left PLUS, MINUS;
precedence left DIV, MOD, MULT;
precedence left DOLLAR;
precedence left ATSIGN;
precedence right DCOLON;

start with TopLevel;

TopLevel ::= PATTERN Pattern:pattern
            {: RESULT = pattern; :}

            | EXPRESSION Expr:expr
            {: RESULT = expr; :};

/* --------------------------- Patterns ----------------------------------- */

Pattern ::= LocationPathPattern:lpp
            {: RESULT = lpp; :}

            | LocationPathPattern:lpp VBAR Pattern:p
            {: RESULT = new AlternativePattern(lpp, p);  :};

LocationPathPattern ::= SLASH
            {: RESULT = new AbsolutePathPattern(null); :}

            | SLASH RelativePathPattern:rpp
            {: RESULT = new AbsolutePathPattern(rpp); :}

            | IdKeyPattern:ikp
            {: RESULT = ikp; :}

            | IdKeyPattern:ikp SLASH RelativePathPattern:rpp
            {: RESULT = new ParentPattern(ikp, rpp); :}

            | IdKeyPattern:ikp DSLASH RelativePathPattern:rpp
            {: RESULT = new AncestorPattern(ikp, rpp); :}

            | DSLASH RelativePathPattern:rpp
            {: RESULT = new AncestorPattern(rpp); :}

            | RelativePathPattern:rpp
            {: RESULT = rpp; :};

IdKeyPattern ::= ID LPAREN Literal:l RPAREN
            {: RESULT = new IdPattern(l);
               parser.setHasIdCall(true);
            :}

            | KEY LPAREN Literal:l1 COMMA Literal:l2 RPAREN
            {: RESULT = new KeyPattern(l1, l2); :};

ProcessingInstructionPattern ::= PIPARAM LPAREN Literal:l RPAREN
            {: RESULT = new ProcessingInstructionPattern(l); :};

RelativePathPattern ::= StepPattern:sp
            {: RESULT = sp; :}

            | StepPattern:sp SLASH RelativePathPattern:rpp
            {: RESULT = new ParentPattern(sp, rpp); :}

            | StepPattern:sp DSLASH RelativePathPattern:rpp
            {: RESULT = new AncestorPattern(sp, rpp); :};

StepPattern ::= NodeTestPattern:nt
            {: 
		RESULT = parser.createStepPattern(Axis.CHILD, nt, null);
            :}

            | NodeTestPattern:nt Predicates:pp
            {: 
		RESULT = parser.createStepPattern(Axis.CHILD, nt, pp);
            :}

            | ProcessingInstructionPattern:pip
            {: RESULT = pip; :}

	    | ProcessingInstructionPattern:pip Predicates:pp
            {: RESULT = (ProcessingInstructionPattern)pip.setPredicates(pp); :}

            | ChildOrAttributeAxisSpecifier:axis NodeTestPattern:nt
            {: 
	       RESULT = parser.createStepPattern(axis.intValue(), nt, null);
            :}

	    | ChildOrAttributeAxisSpecifier:axis
		  NodeTestPattern:nt Predicates:pp
            {: 
	       RESULT = parser.createStepPattern(axis.intValue(), nt, pp);
            :}

            | ChildOrAttributeAxisSpecifier:axis ProcessingInstructionPattern:pip
            {: 
	       RESULT = pip; 	// TODO: report error if axis is attribute
	    :}

	    | ChildOrAttributeAxisSpecifier:axis ProcessingInstructionPattern:pip 
		Predicates:pp
            {: 
	       // TODO: report error if axis is attribute	
	       RESULT = (ProcessingInstructionPattern)pip.setPredicates(pp); 
	    :};

NodeTestPattern ::= NameTestPattern:nt
            {: RESULT = nt; :}

            | NODE
            {: RESULT = new Integer(NodeTest.ANODE); :} 

            | TEXT
            {: RESULT = new Integer(NodeTest.TEXT); :}

            | COMMENT
            {: RESULT = new Integer(NodeTest.COMMENT); :}

            | PI
            {: RESULT = new Integer(NodeTest.PI); :};

NameTestPattern ::= STAR
            {: RESULT = null; :}

            | QName:qn
            {: RESULT = qn; :};

ChildOrAttributeAxisSpecifier ::= ATSIGN
            {: RESULT = new Integer(Axis.ATTRIBUTE); :}

            | CHILD DCOLON
            {: RESULT = new Integer(Axis.CHILD); :}

            | ATTRIBUTE DCOLON
            {: RESULT = new Integer(Axis.ATTRIBUTE); :};

Predicates ::= Predicate:p
            {: 
		Vector temp = new Vector();
		temp.addElement(p);
		RESULT = temp;     
            :}

            | Predicate:p Predicates:pp
            {: pp.insertElementAt(p, 0); RESULT = pp; :}; 

Predicate ::=  LBRACK Expr:e RBRACK
	    {:
		RESULT = new Predicate(e);
	    :};

/* --------------------------- Expressions --------------------------------- */

Expr ::= OrExpr:ex
        {: RESULT = ex; :};

OrExpr ::= AndExpr:ae
        {: RESULT = ae; :}

        | OrExpr:oe OR AndExpr:ae
        {: RESULT = new LogicalExpr(LogicalExpr.OR, oe, ae); :};

AndExpr ::= EqualityExpr:e
        {: RESULT = e; :}

        | AndExpr:ae AND EqualityExpr:ee
        {: RESULT = new LogicalExpr(LogicalExpr.AND, ae, ee); :};
    
EqualityExpr ::= RelationalExpr:re
        {: RESULT = re; :}

        | EqualityExpr:ee EQ RelationalExpr:re
        {: RESULT = new EqualityExpr(Operators.EQ, ee, re); :}

        | EqualityExpr:ee NE RelationalExpr:re
        {: RESULT = new EqualityExpr(Operators.NE, ee, re); :};

RelationalExpr ::= AdditiveExpr:ae
        {: RESULT = ae; :}

        | RelationalExpr:re LT AdditiveExpr:ae
        {: RESULT = new RelationalExpr(Operators.LT, re, ae); :}

        | RelationalExpr:re GT AdditiveExpr:ae
        {: RESULT = new RelationalExpr(Operators.GT, re, ae); :}

        | RelationalExpr:re LE AdditiveExpr:ae
        {: RESULT = new RelationalExpr(Operators.LE, re, ae); :}

        | RelationalExpr:re GE AdditiveExpr:ae
        {: RESULT = new RelationalExpr(Operators.GE, re, ae); :};

AdditiveExpr ::= MultiplicativeExpr:me
        {: RESULT = me; :}

        | AdditiveExpr:ae PLUS  MultiplicativeExpr:me
        {: RESULT = new BinOpExpr(BinOpExpr.PLUS, ae, me); :}

        | AdditiveExpr:ae MINUS MultiplicativeExpr:me
        {: RESULT = new BinOpExpr(BinOpExpr.MINUS, ae, me); :};

MultiplicativeExpr ::= UnaryExpr:ue
        {: RESULT = ue; :}

        | MultiplicativeExpr:me MULT UnaryExpr:ue
        {: RESULT = new BinOpExpr(BinOpExpr.TIMES, me, ue); :}

        | MultiplicativeExpr:me DIV UnaryExpr:ue
        {: RESULT = new BinOpExpr(BinOpExpr.DIV, me, ue); :}

        | MultiplicativeExpr:me MOD UnaryExpr:ue
        {: RESULT = new BinOpExpr(BinOpExpr.MOD, me, ue); :};

UnaryExpr ::= UnionExpr:ue
        {: RESULT = ue; :}

        | MINUS UnaryExpr:ue
        {: RESULT = new UnaryOpExpr(ue); :};

UnionExpr ::= PathExpr:pe
        {: RESULT = pe; :}
 
        | PathExpr:pe VBAR UnionExpr:rest
        {: RESULT = new UnionPathExpr(pe, rest); :};

PathExpr ::= LocationPath:lp
        {: RESULT = lp; :}

        | FilterExpr:fexp
        {: RESULT = fexp; :}

        | FilterExpr:fexp SLASH RelativeLocationPath:rlp
        {: RESULT = new FilterParentPath(fexp, rlp); :}

        | FilterExpr:fexp DSLASH RelativeLocationPath:rlp
        {:
           // 
	   // Expand '//' into '/descendant-or-self::node()/' or
	   // into /descendant-or-self::*/
	   //
	   int nodeType = DOM.NO_TYPE;
	   if (rlp instanceof Step && 
	       parser.isElementAxis(((Step) rlp).getAxis())) 
	   {
	       nodeType = DTM.ELEMENT_NODE;
	   }
           final Step step = new Step(Axis.DESCENDANTORSELF, nodeType, null);
           FilterParentPath fpp = new FilterParentPath(fexp, step);
           fpp = new FilterParentPath(fpp, rlp);
           if (fexp instanceof KeyCall == false) {
               fpp.setDescendantAxis();
	   }
           RESULT = fpp;
        :};

LocationPath ::= RelativeLocationPath:rlp
        {: RESULT = rlp; :}

        | AbsoluteLocationPath:alp
        {: RESULT = alp; :};

RelativeLocationPath ::= Step:step
        {: RESULT = step; :}

        | RelativeLocationPath:rlp SLASH Step:step
        {:     
        if (rlp instanceof Step && ((Step) rlp).isAbbreviatedDot()) {
              RESULT = step;       // Remove './' from the middle
        }
        else if (((Step) step).isAbbreviatedDot()) {
              RESULT = rlp;        // Remove '/.' from the end
        }
        else {
             RESULT =
                new ParentLocationPath((RelativeLocationPath) rlp, step); 
           }
        :}

        | AbbreviatedRelativeLocationPath:arlp
        {: RESULT = arlp; :};

AbsoluteLocationPath ::= SLASH
        {: RESULT = new AbsoluteLocationPath(); :}

        | SLASH RelativeLocationPath:rlp
        {: RESULT = new AbsoluteLocationPath(rlp); :}

        | AbbreviatedAbsoluteLocationPath:aalp
        {: RESULT = aalp; :};

AbbreviatedRelativeLocationPath ::= RelativeLocationPath:rlp DSLASH Step:step
        {: 
	   final Step right  = (Step)step;
           final int  axis   = right.getAxis();
           final int  type   = right.getNodeType();
           final Vector predicates = right.getPredicates();

           if ((axis == Axis.CHILD) && (type != NodeTest.ATTRIBUTE)) {
               // Compress './/child:E' into 'descendant::E' - if possible
               if (predicates == null) {
                   right.setAxis(Axis.DESCENDANT);
                   if (rlp instanceof Step && ((Step)rlp).isAbbreviatedDot()) {
	               RESULT = right;
                   }
                   else {
                       // Expand 'rlp//child::E' into 'rlp/descendant::E'
                       RelativeLocationPath left = (RelativeLocationPath)rlp;
	               RESULT = new ParentLocationPath(left, right);
                   }
               }
               else {
                   // Expand './/step' -> 'descendant-or-self::*/step'
                   if (rlp instanceof Step && ((Step)rlp).isAbbreviatedDot()) {
                       Step left = new Step(Axis.DESCENDANTORSELF, 
			    DTM.ELEMENT_NODE, null);
                       RESULT = new ParentLocationPath(left, right);
                   }
                   else {
                       // Expand 'rlp//step' -> 'rlp/descendant-or-self::*/step'
                       RelativeLocationPath left = (RelativeLocationPath)rlp;
                       Step mid = new Step(Axis.DESCENDANTORSELF, 
			    DTM.ELEMENT_NODE, null);
                       ParentLocationPath ppl = new ParentLocationPath(mid, right);
                       RESULT = new ParentLocationPath(left, ppl);
                   }
               }
           }
           else if ((axis == Axis.ATTRIBUTE) || (type == NodeTest.ATTRIBUTE)) {
               // Expand 'rlp//step' -> 'rlp/descendant-or-self::*/step'
               RelativeLocationPath left = (RelativeLocationPath)rlp;
               Step middle = new Step(Axis.DESCENDANTORSELF, 
		    DTM.ELEMENT_NODE, null);
               ParentLocationPath ppl = new ParentLocationPath(middle, right);
               RESULT = new ParentLocationPath(left, ppl);
	   }
	   else {
	       // Expand 'rlp//step' -> 'rlp/descendant-or-self::node()/step'
               RelativeLocationPath left = (RelativeLocationPath)rlp;
	       Step middle = new Step(Axis.DESCENDANTORSELF, 
		    DOM.NO_TYPE, null); 
               ParentLocationPath ppl = new ParentLocationPath(middle, right);
	       RESULT = new ParentLocationPath(left, ppl);
	   }
        :};


AbbreviatedAbsoluteLocationPath ::= DSLASH RelativeLocationPath:rlp
        {:
           // 
	   // Expand '//' into '/descendant-or-self::node()/' or
	   // into /descendant-or-self::*/
	   //
	   int nodeType = DOM.NO_TYPE;
	   if (rlp instanceof Step && 
	       parser.isElementAxis(((Step) rlp).getAxis())) 
	   {
	       nodeType = DTM.ELEMENT_NODE;
	   }
	   final Step step = new Step(Axis.DESCENDANTORSELF, nodeType, null);
	   RESULT = new AbsoluteLocationPath(parser.insertStep(step, 
				(RelativeLocationPath) rlp));
	:};

Step ::= NodeTest:ntest
        {:
            if (ntest instanceof Step) {
                RESULT = (Step)ntest;
            }
            else {
		RESULT = new Step(Axis.CHILD,
                             parser.findNodeType(Axis.CHILD, ntest),
                             null);
            }
        :}

        | NodeTest:ntest Predicates:pp
        {: 
            if (ntest instanceof Step) {
                Step step = (Step)ntest;
                step.addPredicates(pp);
                RESULT = (Step)ntest;
            }
            else {
                RESULT = new Step(Axis.CHILD,
                             parser.findNodeType(Axis.CHILD, ntest), pp);
            }
        :}

        | AxisSpecifier:axis NodeTest:ntest Predicates:pp
        {: RESULT = new Step(axis.intValue(),
                             parser.findNodeType(axis.intValue(), ntest),
                             pp);
        :}

        | AxisSpecifier:axis NodeTest:ntest
        {: RESULT = new Step(axis.intValue(),
                             parser.findNodeType(axis.intValue(), ntest),
                             null);
        :}

        | AbbreviatedStep:abbrev
        {: RESULT = abbrev; :};

AxisSpecifier ::= AxisName:an DCOLON
        {: RESULT = an; :}

        | ATSIGN
        {: RESULT = new Integer(Axis.ATTRIBUTE); :};

AxisName ::=    ANCESTOR
        {: RESULT = new Integer(Axis.ANCESTOR); :}

        | ANCESTORORSELF
        {: RESULT = new Integer(Axis.ANCESTORORSELF); :}

        | ATTRIBUTE
        {: RESULT = new Integer(Axis.ATTRIBUTE); :}

        | CHILD
        {: RESULT = new Integer(Axis.CHILD); :}

        | DESCENDANT
        {: RESULT = new Integer(Axis.DESCENDANT); :}

        | DESCENDANTORSELF
        {: RESULT = new Integer(Axis.DESCENDANTORSELF); :}

        | FOLLOWING
        {: RESULT = new Integer(Axis.FOLLOWING); :}

        | FOLLOWINGSIBLING
        {: RESULT = new Integer(Axis.FOLLOWINGSIBLING); :}

        | NAMESPACE
        {: RESULT = new Integer(Axis.NAMESPACE); :}

        | PARENT
        {: RESULT = new Integer(Axis.PARENT); :}

        | PRECEDING
        {: RESULT = new Integer(Axis.PRECEDING); :}

        | PRECEDINGSIBLING
        {: RESULT = new Integer(Axis.PRECEDINGSIBLING); :}

        | SELF
        {: RESULT = new Integer(Axis.SELF); :};

AbbreviatedStep ::= DOT
        {: RESULT = new Step(Axis.SELF, NodeTest.ANODE, null); :}

        | DDOT
        {: RESULT = new Step(Axis.PARENT, NodeTest.ANODE, null); :};

FilterExpr ::=  PrimaryExpr:primary
        {: RESULT = primary; :}

        | PrimaryExpr:primary Predicates:pp
        {: RESULT = new FilterExpr(primary, pp); :};

PrimaryExpr ::= VariableReference:vr
        {: RESULT = vr; :}

        | LPAREN Expr:ex RPAREN
        {: RESULT = ex; :}

	| Literal:string
	{: 
	/*
	* If the string appears to have the syntax of a QName, store 
	* namespace info in the literal expression. This is used for
	* element-available and function-available functions, among
	* others. Also, the default namespace must be ignored.
	*/
	String namespace = null;
	final int index = string.lastIndexOf(':');

	if (index > 0) {
	    final String prefix = string.substring(0, index);
	    namespace = parser._symbolTable.lookupNamespace(prefix);
	}
	RESULT = (namespace == null) ? new LiteralExpr(string)
		     : new LiteralExpr(string, namespace); 
	:}

        | INT:num
        {: 
	   long value = num.longValue();
	   if (value < Integer.MIN_VALUE || value > Integer.MAX_VALUE) {
		RESULT = new RealExpr(value);
	   }
	   else {
               if (num.doubleValue() == -0)
                   RESULT = new RealExpr(num.doubleValue());
               else if (num.intValue() == 0)
                   RESULT = new IntExpr(num.intValue());
               else if (num.doubleValue() == 0.0)
                   RESULT = new RealExpr(num.doubleValue());
               else 
                   RESULT = new IntExpr(num.intValue());
	   }
        :}

        | REAL:num
        {: RESULT = new RealExpr(num.doubleValue()); :}

        | FunctionCall:fc
        {: RESULT = fc; :};

VariableReference ::= DOLLAR VariableName:varName
        {: 
            // An empty qname prefix for a variable or parameter reference
            // should map to the null namespace and not the default URI.
            SyntaxTreeNode node = parser.lookupName(varName);

            if (node != null) {
                if (node instanceof Variable) {
                    RESULT = new VariableRef((Variable)node);
                }
                else if (node instanceof Param) {
                    RESULT = new ParameterRef((Param)node);
                } 
                else {
                    RESULT = new UnresolvedRef(varName);
                }
            }

            if (node == null) {
                RESULT = new UnresolvedRef(varName);
            }
        :};

FunctionCall ::= FunctionName:fname LPAREN RPAREN
        {: 

          if (fname == parser.getQNameIgnoreDefaultNs("current")) {
            RESULT = new CurrentCall(fname);
	  }
          else if (fname == parser.getQNameIgnoreDefaultNs("number")) {
            RESULT = new NumberCall(fname, parser.EmptyArgs);
	  }
          else if (fname == parser.getQNameIgnoreDefaultNs("string")) {
            RESULT = new StringCall(fname, parser.EmptyArgs);
	  }
          else if (fname == parser.getQNameIgnoreDefaultNs("concat")) {
            RESULT = new ConcatCall(fname, parser.EmptyArgs);
	  }
          else if (fname == parser.getQNameIgnoreDefaultNs("true")) {
            RESULT = new BooleanExpr(true);
	  }
          else if (fname == parser.getQNameIgnoreDefaultNs("false")) {
            RESULT = new BooleanExpr(false);
	  }
          else if (fname == parser.getQNameIgnoreDefaultNs("name")) {
            RESULT = new NameCall(fname);
	  }
          else if (fname == parser.getQNameIgnoreDefaultNs("generate-id")) {
            RESULT = new GenerateIdCall(fname, parser.EmptyArgs);
	  }
          else if (fname == parser.getQNameIgnoreDefaultNs("string-length")) {
            RESULT = new StringLengthCall(fname, parser.EmptyArgs);
	  }
          else if (fname == parser.getQNameIgnoreDefaultNs("position")) {
            RESULT = new PositionCall(fname);
	  }
          else if (fname == parser.getQNameIgnoreDefaultNs("last")) {
            RESULT = new LastCall(fname);
	  }
          else if (fname == parser.getQNameIgnoreDefaultNs("local-name")) {
            RESULT = new LocalNameCall(fname);
	  }
          else if (fname == parser.getQNameIgnoreDefaultNs("namespace-uri")) {
            RESULT = new NamespaceUriCall(fname);
	  }
          else {
            RESULT = new FunctionCall(fname, parser.EmptyArgs);
	  }
        :}

        | FunctionName:fname LPAREN NonemptyArgumentList:argl RPAREN
        {:
          if (fname == parser.getQNameIgnoreDefaultNs("concat")) {
            RESULT = new ConcatCall(fname, argl);
	  }
          else if (fname == parser.getQNameIgnoreDefaultNs("number")) {
            RESULT = new NumberCall(fname, argl);
	  }
          else if (fname == parser.getQNameIgnoreDefaultNs("document")) {
	    parser.setMultiDocument(true);
            RESULT = new DocumentCall(fname, argl);
	  }
          else if (fname == parser.getQNameIgnoreDefaultNs("string")) {
            RESULT = new StringCall(fname, argl);
	  }
          else if (fname == parser.getQNameIgnoreDefaultNs("boolean")) {
            RESULT = new BooleanCall(fname, argl);
	  }
          else if (fname == parser.getQNameIgnoreDefaultNs("name")) {
            RESULT = new NameCall(fname, argl);
	  }
          else if (fname == parser.getQNameIgnoreDefaultNs("generate-id")) {
            RESULT = new GenerateIdCall(fname, argl);
	  }
          else if (fname == parser.getQNameIgnoreDefaultNs("not")) {
            RESULT = new NotCall(fname, argl);
	  }
          else if (fname == parser.getQNameIgnoreDefaultNs("format-number")) {
            RESULT = new FormatNumberCall(fname, argl);
	  }
          else if (fname == parser.getQNameIgnoreDefaultNs("unparsed-entity-uri")) {
            RESULT = new UnparsedEntityUriCall(fname, argl);
	  }
          else if (fname == parser.getQNameIgnoreDefaultNs("key")) {
            RESULT = new KeyCall(fname, argl);
	  }
          else if (fname == parser.getQNameIgnoreDefaultNs("id")) {
            RESULT = new KeyCall(fname, argl);
            parser.setHasIdCall(true);
	  }
          else if (fname == parser.getQNameIgnoreDefaultNs("ceiling")) {
            RESULT = new CeilingCall(fname, argl);
	  }
          else if (fname == parser.getQNameIgnoreDefaultNs("round")) {
            RESULT = new RoundCall(fname, argl);
	  }
          else if (fname == parser.getQNameIgnoreDefaultNs("floor")) {
            RESULT = new FloorCall(fname, argl);
	  }
          else if (fname == parser.getQNameIgnoreDefaultNs("contains")) {
            RESULT = new ContainsCall(fname, argl);
	  }
          else if (fname == parser.getQNameIgnoreDefaultNs("string-length")) {
            RESULT = new StringLengthCall(fname, argl);
	  }
          else if (fname == parser.getQNameIgnoreDefaultNs("starts-with")) {
            RESULT = new StartsWithCall(fname, argl);
	  }
          else if (fname == parser.getQNameIgnoreDefaultNs("function-available")) {
            RESULT = new FunctionAvailableCall(fname, argl);
	  }
          else if (fname == parser.getQNameIgnoreDefaultNs("element-available")) {
            RESULT = new ElementAvailableCall(fname, argl);
	  }
          else if (fname == parser.getQNameIgnoreDefaultNs("local-name")) {
            RESULT = new LocalNameCall(fname, argl);
	  }
          else if (fname == parser.getQNameIgnoreDefaultNs("lang")) {
            RESULT = new LangCall(fname, argl);
	  }
          else if (fname == parser.getQNameIgnoreDefaultNs("namespace-uri")) {
            RESULT = new NamespaceUriCall(fname, argl);
	  }
          else if (fname == parser.getQName(Constants.TRANSLET_URI, "xsltc", "cast")) {
            RESULT = new CastCall(fname, argl);
	  }
	  // Special case for extension function nodeset()
          else if (fname.getLocalPart().equals("nodeset") || fname.getLocalPart().equals("node-set")) {
	    parser.setCallsNodeset(true);  // implies MultiDOM
            RESULT = new FunctionCall(fname, argl);
	  }
          else {
            RESULT = new FunctionCall(fname, argl);
	  }
    :};

NonemptyArgumentList ::= Argument:arg
        {: 
            Vector temp = new Vector();
            temp.addElement(arg);
            RESULT = temp;
        :}

        | Argument:arg COMMA NonemptyArgumentList:argl
        {: argl.insertElementAt(arg, 0); RESULT = argl; :};

FunctionName ::= QName:fname
        {: 
	    RESULT = fname; 
	:};

VariableName ::= QName:vname
        {: 
	    RESULT = vname; 
	:};

Argument ::= Expr:ex
        {: RESULT = ex; :};

NodeTest ::= NameTest:nt
        {: RESULT = nt; :}

        | NODE
        {: RESULT = new Integer(NodeTest.ANODE); :}

        | TEXT
        {: RESULT = new Integer(NodeTest.TEXT); :}

        | COMMENT
        {: RESULT = new Integer(NodeTest.COMMENT); :}

        | PIPARAM LPAREN Literal:l RPAREN
        {:
           QName name = parser.getQNameIgnoreDefaultNs("name");
           Expression exp = new EqualityExpr(Operators.EQ,
                                             new NameCall(name),
                                             new LiteralExpr(l));
           Vector predicates = new Vector();
           predicates.addElement(new Predicate(exp));
           RESULT = new Step(Axis.CHILD, NodeTest.PI, predicates);
        :}

        | PI
        {: RESULT = new Integer(NodeTest.PI); :};

NameTest ::= STAR
             {: RESULT = null; :}

             | QName:qn
             {: RESULT = qn; :};

QName ::= QNAME:qname
        {: RESULT = parser.getQNameIgnoreDefaultNs(qname); :}

	| DIV
        {: RESULT = parser.getQNameIgnoreDefaultNs("div"); :}

        | MOD
        {: RESULT = parser.getQNameIgnoreDefaultNs("mod"); :}

        | KEY
        {: RESULT = parser.getQNameIgnoreDefaultNs("key"); :}

        | ANCESTOR
        {: RESULT = parser.getQNameIgnoreDefaultNs("child"); :}

        | ANCESTORORSELF
        {: RESULT = parser.getQNameIgnoreDefaultNs("ancestor-or-self"); :}

        | ATTRIBUTE
        {: RESULT = parser.getQNameIgnoreDefaultNs("attribute"); :}

        | CHILD
        {: RESULT = parser.getQNameIgnoreDefaultNs("child"); :}

        | DESCENDANT
        {: RESULT = parser.getQNameIgnoreDefaultNs("decendant"); :}

        | DESCENDANTORSELF
        {: RESULT = parser.getQNameIgnoreDefaultNs("decendant-or-self"); :}

        | FOLLOWING
        {: RESULT = parser.getQNameIgnoreDefaultNs("following"); :}

        | FOLLOWINGSIBLING
        {: RESULT = parser.getQNameIgnoreDefaultNs("following-sibling"); :}

        | NAMESPACE
        {: RESULT = parser.getQNameIgnoreDefaultNs("namespace"); :}

        | PARENT
        {: RESULT = parser.getQNameIgnoreDefaultNs("parent"); :}

        | PRECEDING
        {: RESULT = parser.getQNameIgnoreDefaultNs("preceding"); :}

        | PRECEDINGSIBLING
        {: RESULT = parser.getQNameIgnoreDefaultNs("preceding-sibling"); :}

        | SELF
        {: RESULT = parser.getQNameIgnoreDefaultNs("self"); :}

        | ID
        {: RESULT = parser.getQNameIgnoreDefaultNs("id"); :};

