1 %{
2 /* XPathParser.y - An XPath 1.0 parser.
3    Copyright (C) 2004 The Free Software Foundation
4 
5 This file is part of GNU Classpath.
6 
7 GNU Classpath is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
11 
12 GNU Classpath is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with GNU Classpath; see the file COPYING.  If not, write to the
19 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 02110-1301 USA.
21 
22 Linking this library statically or dynamically with other modules is
23 making a combined work based on this library.  Thus, the terms and
24 conditions of the GNU General Public License cover the whole
25 combination.
26 
27 As a special exception, the copyright holders of this library give you
28 permission to link this library with independent modules to produce an
29 executable, regardless of the license terms of these independent
30 modules, and to copy and distribute the resulting executable under
31 terms of your choice, provided that you also meet, for each linked
32 independent module, the terms and conditions of the license of that
33 module.  An independent module is a module which is not derived from
34 or based on this library.  If you modify this library, you may extend
35 this exception to your version of the library, but you are not
36 obligated to do so.  If you do not wish to do so, delete this
37 exception statement from your version. */
38 
39 
40 package gnu.xml.xpath;
41 
42 import java.util.ArrayList;
43 import java.util.Collections;
44 import java.util.List;
45 import java.util.Map;
46 import javax.xml.namespace.NamespaceContext;
47 import javax.xml.namespace.QName;
48 import javax.xml.xpath.XPathFunctionResolver;
49 import javax.xml.xpath.XPathVariableResolver;
50 import org.w3c.dom.Node;
51 
52 /**
53  * An XPath 1.0 parser.
54  *
55  * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
56  */
57 public class XPathParser
58 {
59 
60   NamespaceContext namespaceContext;
61   XPathVariableResolver variableResolver;
62   XPathFunctionResolver functionResolver;
63 
getQName(String name)64   QName getQName(String name)
65   {
66     QName qName = QName.valueOf(name);
67     if (namespaceContext != null)
68       {
69         String prefix = qName.getPrefix();
70         String uri = qName.getNamespaceURI();
71         if (prefix != null && (uri == null || uri.length() == 0))
72           {
73             uri = namespaceContext.getNamespaceURI(prefix);
74             String localName = qName.getLocalPart();
75             qName = new QName(uri, localName, prefix);
76           }
77       }
78     return qName;
79   }
80 
lookupFunction(String name,List<Expr> args)81   Expr lookupFunction(String name, List<Expr> args)
82   {
83     int arity = args.size();
84     if ("position".equals(name) && arity == 0)
85       {
86         return new PositionFunction();
87       }
88     else if ("last".equals(name) && arity == 0)
89       {
90         return new LastFunction();
91       }
92     else if ("string".equals(name) && (arity == 1 || arity == 0))
93       {
94         return new StringFunction(args);
95       }
96     else if ("number".equals(name) && (arity == 1 || arity == 0))
97       {
98         return new NumberFunction(args);
99       }
100     else if ("boolean".equals(name) && arity == 1)
101       {
102         return new BooleanFunction(args);
103       }
104     else if ("count".equals(name) && arity == 1)
105       {
106         return new CountFunction(args);
107       }
108     else if ("not".equals(name) && arity == 1)
109       {
110         return new NotFunction(args);
111       }
112     else if ("id".equals(name) && arity == 1)
113       {
114         return new IdFunction(args);
115       }
116     else if ("concat".equals(name) && arity > 1)
117       {
118         return new ConcatFunction(args);
119       }
120     else if ("true".equals(name) && arity == 0)
121       {
122         return new TrueFunction();
123       }
124     else if ("false".equals(name) && arity == 0)
125       {
126         return new FalseFunction();
127       }
128     else if ("name".equals(name) && (arity == 1 || arity == 0))
129       {
130         return new NameFunction(args);
131       }
132     else if ("local-name".equals(name) && (arity == 1 || arity == 0))
133       {
134         return new LocalNameFunction(args);
135       }
136     else if ("namespace-uri".equals(name) && (arity == 1 || arity == 0))
137       {
138         return new NamespaceUriFunction(args);
139       }
140     else if ("starts-with".equals(name) && arity == 2)
141       {
142         return new StartsWithFunction(args);
143       }
144     else if ("contains".equals(name) && arity == 2)
145       {
146         return new ContainsFunction(args);
147       }
148     else if ("string-length".equals(name) && (arity == 1 || arity == 0))
149       {
150         return new StringLengthFunction(args);
151       }
152     else if ("translate".equals(name) && arity == 3)
153       {
154         return new TranslateFunction(args);
155       }
156     else if ("normalize-space".equals(name) && (arity == 1 || arity == 0))
157       {
158         return new NormalizeSpaceFunction(args);
159       }
160     else if ("substring".equals(name) && (arity == 2 || arity == 3))
161       {
162         return new SubstringFunction(args);
163       }
164     else if ("substring-before".equals(name) && arity == 2)
165       {
166         return new SubstringBeforeFunction(args);
167       }
168     else if ("substring-after".equals(name) && arity == 2)
169       {
170         return new SubstringAfterFunction(args);
171       }
172     else if ("lang".equals(name) && arity == 1)
173       {
174         return new LangFunction(args);
175       }
176     else if ("sum".equals(name) && arity == 1)
177       {
178         return new SumFunction(args);
179       }
180     else if ("floor".equals(name) && arity == 1)
181       {
182         return new FloorFunction(args);
183       }
184     else if ("ceiling".equals(name) && arity == 1)
185       {
186         return new CeilingFunction(args);
187       }
188     else if ("round".equals(name) && arity == 1)
189       {
190         return new RoundFunction(args);
191       }
192     else if (functionResolver != null)
193       {
194         QName qName = QName.valueOf(name);
195         Object function = functionResolver.resolveFunction(qName, arity);
196         if (function != null &&
197             function instanceof Function &&
198             function instanceof Expr)
199           {
200             Function f = (Function) function;
201             f.setArguments(args);
202             return (Expr) function;
203           }
204       }
205     return new FunctionCall(functionResolver, name, args);
206   }
207 
208 %}
209 
210 %token LITERAL
211 %token DIGITS
212 %token NAME
213 
214 %token LP // '('
215 %token RP // ')'
216 %token LB // '['
217 %token RB // ']'
218 %token COMMA // ','
219 %token PIPE // '|'
220 %token SLASH // '/'
221 %token DOUBLE_SLASH // '//'
222 %token EQ // '='
223 %token NE // '!='
224 %token GT // '>'
225 %token LT // '<'
226 %token GTE // '>='
227 %token LTE // '<='
228 %token PLUS // '+'
229 %token MINUS // '-'
230 %token AT // '@'
231 %token STAR // '*'
232 %token DOLLAR // '$'
233 %token COLON // ':'
234 %token DOUBLE_COLON // '::'
235 %token DOT // '.'
236 %token DOUBLE_DOT // '..'
237 
238 %token ANCESTOR
239 %token ANCESTOR_OR_SELF
240 %token ATTRIBUTE
241 %token CHILD
242 %token DESCENDANT
243 %token DESCENDANT_OR_SELF
244 %token FOLLOWING
245 %token FOLLOWING_SIBLING
246 %token NAMESPACE
247 %token PARENT
248 %token PRECEDING
249 %token PRECEDING_SIBLING
250 %token SELF
251 %token DIV
252 %token MOD
253 %token OR
254 %token AND
255 %token COMMENT
256 %token PROCESSING_INSTRUCTION
257 %token TEXT
258 %token NODE
259 
260 %right UNARY
261 
262 %start expr
263 
264 %%
265 
266 expr:
267   or_expr
268   ;
269 
270 location_path:
271   relative_location_path
272   | absolute_location_path
273   ;
274 
275 absolute_location_path:
276   SLASH
277     {
278       $$ = new Root();
279     }
280   | SLASH relative_location_path
281     {
282       Steps steps;
283       if ($2 instanceof Steps)
284         {
285           steps = (Steps) $2;
286         }
287       else
288         {
289           steps = new Steps();
290           steps.path.addFirst((Expr) $2);
291         }
292       steps.path.addFirst(new Root());
293       $$ = steps;
294       //$$ = new Step(new Root(), (Path) $2);
295     }
296   | DOUBLE_SLASH relative_location_path
297     {
298       Test nt = new NodeTypeTest((short) 0);
299       Selector s = new Selector(Selector.DESCENDANT_OR_SELF,
300                                 Collections.singletonList (nt));
301       Steps steps;
302       if ($2 instanceof Steps)
303         {
304           steps = (Steps) $2;
305         }
306       else
307         {
308           steps = new Steps();
309           steps.path.addFirst((Expr) $2);
310         }
311       steps.path.addFirst(s);
312       steps.path.addFirst(new Root());
313       $$ = steps;
314       //Step step = new Step(s, (Path) $2);
315       //$$ = new Step(new Root(), step);
316     }
317   ;
318 
319 relative_location_path:
320   step
321   | relative_location_path SLASH step
322     {
323       Steps steps;
324       if ($1 instanceof Steps)
325         {
326           steps = (Steps) $1;
327         }
328       else
329         {
330           steps = new Steps();
331           steps.path.addFirst((Expr) $1);
332         }
333       steps.path.addLast((Expr) $3);
334       $$ = steps;
335       //$$ = new Step((Expr) $1, (Path) $3);
336     }
337   | relative_location_path DOUBLE_SLASH step
338     {
339       Test nt = new NodeTypeTest((short) 0);
340       Selector s = new Selector(Selector.DESCENDANT_OR_SELF,
341                                 Collections.singletonList (nt));
342       Steps steps;
343       if ($1 instanceof Steps)
344         {
345           steps = (Steps) $1;
346         }
347       else
348         {
349           steps = new Steps();
350           steps.path.addFirst((Expr) $1);
351         }
352       steps.path.addLast(s);
353       steps.path.addLast((Expr) $3);
354       $$ = steps;
355       //Step step = new Step(s, (Path) $3);
356       //$$ = new Step((Expr) $1, step);
357     }
358   ;
359 
360 step:
361   step_node_test
362     {
363       @SuppressWarnings("unchecked") List<Test> tests = (List<Test>) $1;
364       $$ = new Selector (Selector.CHILD, tests);
365     }
366   | AT step_node_test
367     {
368       @SuppressWarnings("unchecked") List<Test> tests = (List<Test>) $2;
369       $$ = new Selector (Selector.ATTRIBUTE, tests);
370     }
371   | axis_name DOUBLE_COLON step_node_test
372     {
373       @SuppressWarnings("unchecked") List<Test> tests = (List<Test>) $3;
374       $$ = new Selector (((Integer) $1).intValue (), tests);
375     }
376   | DOT
377     {
378       List<Test> emptyList = Collections.emptyList();
379       $$ = new Selector (Selector.SELF, emptyList);
380     }
381   | DOUBLE_DOT
382     {
383       List<Test> emptyList = Collections.emptyList();
384       $$ = new Selector (Selector.PARENT, emptyList);
385     }
386   ;
387 
388 step_node_test:
389   node_test
390     {
391       List<Test> list = new ArrayList<Test>();
392       list.add((Test) $1);
393       $$ = list;
394     }
395   | step_node_test predicate
396     {
397       /* This is safe as we create this in one of the other cases */
398       @SuppressWarnings("unchecked") List<Test> tests = (List<Test>)$1;
399       tests.add((Test) $2);
400       $$ = list;
401     }
402   ;
403 
404 /*predicate_list:
405   predicate
406     {
407       List list = new ArrayList ();
408       list.add ($1);
409       $$ = list;
410     }
411   | predicate predicate_list
412     {
413       List list = (List) $3;
414       list.add (0, $1);
415       $$ = list;
416     }
417   ;*/
418 
419 axis_name:
420   ANCESTOR
421     {
422       $$ = new Integer(Selector.ANCESTOR);
423     }
424   | ANCESTOR_OR_SELF
425     {
426       $$ = new Integer(Selector.ANCESTOR_OR_SELF);
427     }
428   | ATTRIBUTE
429     {
430       $$ = new Integer(Selector.ATTRIBUTE);
431     }
432   | CHILD
433     {
434       $$ = new Integer(Selector.CHILD);
435     }
436   | DESCENDANT
437     {
438       $$ = new Integer(Selector.DESCENDANT);
439     }
440   | DESCENDANT_OR_SELF
441     {
442       $$ = new Integer(Selector.DESCENDANT_OR_SELF);
443     }
444   | FOLLOWING
445     {
446       $$ = new Integer(Selector.FOLLOWING);
447     }
448   | FOLLOWING_SIBLING
449     {
450       $$ = new Integer(Selector.FOLLOWING_SIBLING);
451     }
452   | NAMESPACE
453     {
454       $$ = new Integer(Selector.NAMESPACE);
455     }
456   | PARENT
457     {
458       $$ = new Integer(Selector.PARENT);
459     }
460   | PRECEDING
461     {
462       $$ = new Integer(Selector.PRECEDING);
463     }
464   | PRECEDING_SIBLING
465     {
466       $$ = new Integer(Selector.PRECEDING_SIBLING);
467     }
468   | SELF
469     {
470       $$ = new Integer(Selector.SELF);
471     }
472   ;
473 
474 node_test:
475   name_test
476   /*| PROCESSING_INSTRUCTION LP LITERAL RP*/
477   | PROCESSING_INSTRUCTION LITERAL RP
478     {
479       $$ = new NodeTypeTest(Node.PROCESSING_INSTRUCTION_NODE, (String) $2);
480     }
481   /*| node_type LP RP*/
482   | node_type RP
483     {
484       $$ = new NodeTypeTest(((Short) $1).shortValue());
485     }
486   ;
487 
488 predicate:
489   LB expr RB
490     {
491       $$ = new Predicate((Expr) $2);
492     }
493   ;
494 
495 primary_expr:
496   variable_reference
497   | LP expr RP
498     {
499       $$ = new ParenthesizedExpr((Expr) $2);
500     }
501   | LITERAL
502     {
503       $$ = new Constant($1);
504     }
505   | number
506     {
507       $$ = new Constant($1);
508     }
509   | function_call
510   ;
511 
512 function_call:
513   function_name LP RP
514     {
515       List<Expr> emptyList = Collections.emptyList();
516       $$ = lookupFunction((String) $1, emptyList);
517     }
518   | function_name LP argument_list RP
519     {
520       /* This is safe as we create this below  */
521       @SuppressWarnings("unchecked") List<Expr> exprs = (List<Expr>) $3;
522       $$ = lookupFunction((String) $1, (List) exprs);
523     }
524   ;
525 
526 argument_list:
527   expr
528     {
529       List<Expr> list = new ArrayList<Expr>();
530       list.add((Expr) $1);
531       $$ = list;
532     }
533   | expr COMMA argument_list
534     {
535       /* This is safe as we create this above  */
536       @SuppressWarnings("unchecked") List<Expr> list = (List<Expr>) $3;
537       list.add(0, (Expr) $1);
538       $$ = list;
539     }
540   ;
541 
542 union_expr:
543   path_expr
544   | union_expr PIPE path_expr
545     {
546       $$ = new UnionExpr((Expr) $1, (Expr) $3);
547     }
548   ;
549 
550 path_expr:
551   location_path
552   | filter_expr
553   | filter_expr SLASH relative_location_path
554     {
555       Steps steps;
556       if ($3 instanceof Steps)
557         {
558           steps = (Steps) $3;
559         }
560       else
561         {
562           steps = new Steps();
563           steps.path.addFirst((Expr) $3);
564         }
565       steps.path.addFirst((Expr) $1);
566       $$ = steps;
567       //$$ = new Step ((Expr) $1, (Path) $3);
568     }
569   | filter_expr DOUBLE_SLASH relative_location_path
570     {
571       Test nt = new NodeTypeTest((short) 0);
572       Selector s = new Selector(Selector.DESCENDANT_OR_SELF,
573                                 Collections.singletonList(nt));
574       Steps steps;
575       if ($3 instanceof Steps)
576         {
577           steps = (Steps) $3;
578         }
579       else
580         {
581           steps = new Steps();
582           steps.path.addFirst($3);
583         }
584       steps.path.addFirst(s);
585       steps.path.addFirst((Expr) $1);
586       $$ = steps;
587       //Step step = new Step (s, (Path) $3);
588       //$$ = new Step ((Expr) $1, step);
589     }
590   ;
591 
592 filter_expr:
593   primary_expr
594   | filter_expr predicate
595     {
596       Predicate filter = (Predicate) $2;
597       Selector s = new Selector(Selector.SELF,
598                                 Collections.singletonList(filter));
599       Steps steps;
600       if ($1 instanceof Steps)
601         {
602           steps = (Steps) $1;
603         }
604       else
605         {
606           steps = new Steps();
607           steps.path.addFirst((Expr) $1);
608         }
609       steps.path.addLast(s);
610       $$ = steps;
611       //$$ = new Step ((Expr) $1, s);
612     }
613   ;
614 
615 or_expr:
616   and_expr
617   | or_expr OR and_expr
618     {
619       $$ = new OrExpr((Expr) $1, (Expr) $3);
620     }
621   ;
622 
623 and_expr:
624   equality_expr
625   | and_expr AND equality_expr
626     {
627       $$ = new AndExpr((Expr) $1, (Expr) $3);
628     }
629   ;
630 
631 equality_expr:
632   relational_expr
633   | equality_expr EQ relational_expr
634     {
635       $$ = new EqualityExpr((Expr) $1, (Expr) $3, false);
636     }
637   | equality_expr NE relational_expr
638     {
639       $$ = new EqualityExpr((Expr) $1, (Expr) $3, true);
640     }
641   ;
642 
643 relational_expr:
644   additive_expr
645   | relational_expr LT additive_expr
646     {
647       $$ = new RelationalExpr((Expr) $1, (Expr) $3, true, false);
648     }
649   | relational_expr GT additive_expr
650     {
651       $$ = new RelationalExpr((Expr) $1, (Expr) $3, false, false);
652     }
653   | relational_expr LTE additive_expr
654     {
655       $$ = new RelationalExpr((Expr) $1, (Expr) $3, true, true);
656     }
657   | relational_expr GTE additive_expr
658     {
659       $$ = new RelationalExpr((Expr) $1, (Expr) $3, false, true);
660     }
661   ;
662 
663 additive_expr:
664   multiplicative_expr
665   | additive_expr PLUS multiplicative_expr
666     {
667       $$ = new ArithmeticExpr((Expr) $1, (Expr) $3, ArithmeticExpr.ADD);
668     }
669   | additive_expr MINUS multiplicative_expr
670     {
671       $$ = new ArithmeticExpr((Expr) $1, (Expr) $3, ArithmeticExpr.SUBTRACT);
672     }
673   ;
674 
675 multiplicative_expr:
676   unary_expr
677   | multiplicative_expr STAR unary_expr
678     {
679       $$ = new ArithmeticExpr((Expr) $1, (Expr) $3, ArithmeticExpr.MULTIPLY);
680     }
681   | multiplicative_expr DIV unary_expr
682     {
683       $$ = new ArithmeticExpr((Expr) $1, (Expr) $3, ArithmeticExpr.DIVIDE);
684     }
685   | multiplicative_expr MOD unary_expr
686     {
687       $$ = new ArithmeticExpr((Expr) $1, (Expr) $3, ArithmeticExpr.MODULO);
688     }
689   ;
690 
691 unary_expr:
692   union_expr
693   | MINUS unary_expr %prec UNARY
694     {
695       $$ = new NegativeExpr((Expr) $2);
696     }
697   ;
698 
699 number:
700   DIGITS
701     {
702       $$ = new Double((String) $1 + ".0");
703     }
704   | DIGITS DOT
705     {
706       $$ = new Double((String) $1 + ".0");
707     }
708   | DIGITS DOT DIGITS
709     {
710       $$ = new Double((String) $1 + "." + (String) $3);
711     }
712   | DOT DIGITS
713     {
714       $$ = new Double("0." + (String) $2);
715     }
716   ;
717 
718 function_name:
719   qname
720 /*  | node_type
721     {
722       switch (((Short) $1).shortValue ())
723         {
724         case Node.COMMENT_NODE:
725           $$ = "comment";
726           break;
727         case Node.TEXT_NODE:
728           $$ = "text";
729           break;
730         case Node.PROCESSING_INSTRUCTION_NODE:
731           $$ = "processing-instruction";
732           break;
733         default:
734           $$ = "node";
735           break;
736         }
737     }*/
738   ;
739 
740 variable_reference:
741   DOLLAR qname
742     {
743       String name = (String) $2;
744       $$ = new VariableReference(variableResolver, getQName(name));
745     }
746   ;
747 
748 name_test:
749   STAR
750     {
751       $$ = new NameTest(null, true, true);
752     }
753   | NAME COLON STAR
754     {
755       QName qName = getQName((String) $1);
756       $$ = new NameTest(qName, true, false);
757     }
758   | qname
759     {
760       QName qName = getQName((String) $1);
761       $$ = new NameTest(qName, false, false);
762     }
763   ;
764 
765 qname:
766   NAME
767   | NAME COLON NAME
768     {
769       $$ = (String) $1 + ':' + (String) $3;
770     }
771   ;
772 
773 node_type:
774   COMMENT
775     {
776       $$ = new Short(Node.COMMENT_NODE);
777     }
778   | TEXT
779     {
780       $$ = new Short(Node.TEXT_NODE);
781     }
782   | PROCESSING_INSTRUCTION
783     {
784       $$ = new Short(Node.PROCESSING_INSTRUCTION_NODE);
785     }
786   | NODE
787     {
788       $$ = new Short((short) 0);
789     }
790   ;
791 
792 %%
793 
794 }
795