Line data Source code
1 : // Snap Websites Servers -- retrieve a list of nodes from a QDomDocument based on an XPath
2 : // Copyright (c) 2013-2019 Made to Order Software Corp. All Rights Reserved
3 : //
4 : // This program is free software; you can redistribute it and/or modify
5 : // it under the terms of the GNU General Public License as published by
6 : // the Free Software Foundation; either version 2 of the License, or
7 : // (at your option) any later version.
8 : //
9 : // This program is distributed in the hope that it will be useful,
10 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : // GNU General Public License for more details.
13 : //
14 : // You should have received a copy of the GNU General Public License
15 : // along with this program; if not, write to the Free Software
16 : // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 :
18 :
19 : // self
20 : //
21 : #include "snapwebsites/qdomxpath.h"
22 :
23 :
24 : // snapwebsites lib
25 : //
26 : #include "snapwebsites/floats.h"
27 : #include "snapwebsites/qstring_stream.h"
28 :
29 :
30 : // snapdev lib
31 : //
32 : #include <snapdev/not_used.h>
33 :
34 :
35 : // C++ lib
36 : //
37 : #include <cmath>
38 : #include <iomanip>
39 : #include <limits>
40 :
41 :
42 : // last include
43 : //
44 : #include <snapdev/poison.h>
45 :
46 :
47 :
48 : const char * QDomXPath::MAGIC = "XPTH";
49 : const QDomXPath::instruction_t QDomXPath::VERSION_MAJOR;
50 : const QDomXPath::instruction_t QDomXPath::VERSION_MINOR;
51 :
52 :
53 :
54 : /** \brief Verification mode.
55 : *
56 : * If the QDOM_XPATH_VERIFICATION macro is set to zero (0) then no
57 : * verifications are used while executing a program. Assuming that we
58 : * ironed out all the bugs, this is as safe as with the verifications,
59 : * only faster since we can skip of all the checks.
60 : *
61 : * This flag is mainly for debug purposes to ensure that our tables
62 : * are properly defined as one make use of positioned pointers and
63 : * such need to be 100% correct for the execution environment to
64 : * work as expected.
65 : */
66 : #define QDOM_XPATH_VERIFICATION 1
67 :
68 :
69 : /** \file
70 : * \brief The DOM XPath Compiler transforms an XPath to bytecode.
71 : *
72 : * The bytecode is a very simple set of commands, each defined as one byte.
73 : * Only the PUSH and BRANCH instructions use additional bytes to allow for
74 : * immediate data to be used to get everything to work as expected.
75 : *
76 : * The idea of using a compiler allows us to avoid having to recompile the
77 : * XPath each time (very often) we use it. Overall, it is a lot of time
78 : * saved (if we were to run the XPath expression only once, it would be
79 : * a big waste, but if you run it 1 million times a day, that's quite a
80 : * saving.
81 : *
82 : * The process requires a lot of DOM calls and thus creating a C++ program,
83 : * instead of a simple bytecode would require hundreds of lines of code and
84 : * the result would probably not be any better than the bytecode because it
85 : * would not use much of the CPU code cache.
86 : *
87 : * The main process when executing an XPath is a loop over a set of nodes
88 : * related to what is currently the context node. Each node becomes the
89 : * context node in turn and gets checked against the following set of
90 : * predicates.
91 : *
92 : * The following represents one entry in an XPath:
93 : *
94 : * \code
95 : * // Note that although everything is optional, you should probably have
96 : * // at least one entry (i.e. "." or maybe "[@color='pink']") although
97 : * // the empty xpath is accepted but it returns an empty set of nodes...
98 : * [ '/' [ '/' ] ] [ <axis> '::' ] [ <prefix> ':' ] [ <tagname> ] ( '[' <predicate> ']' )*
99 : * \endcode
100 : *
101 : * Each entry is separated by a slash when there are several. If the XPath
102 : * starts with one or two slashes then the context node is set to the root.
103 : * Otherwise it is a relative path and the current node is chosen.
104 : *
105 : * By default the \<axis> is 'child::node()/' meaning that children of the
106 : * context node become the elements of the list we're working on. Note that
107 : * attributes of a node are somewhat considered as being children, but
108 : * they can only be accessed with the 'attribute' axis (which can be
109 : * abbreviated with '@'.) The double slash syntax is an equivalent to
110 : * the "descendant-or-self::node()/" axis.
111 : *
112 : * The axis defines the list of nodes and in theory all those nodes are
113 : * put in a list. However, in practice this is not wise since the list
114 : * can be quite big. However, many axis represent a large list of nodes
115 : * (possibly all the nodes in the document being worked on) and in that
116 : * case premature application of the tag name predicate would be incorrect.
117 : * For example, "/descendant::p" can be used to retrieve all the \<p> tag
118 : * of an HTML document. If we were to test the tag name early, then the
119 : * process would stop at the \<html> or \<body> tags and not find any
120 : * \<p> tag at all. The algorithms we use take that in account.
121 : *
122 : * Note that \<tagname> is mandatory in an entry, however, it can be set to
123 : * '*' to match all the nodes that the axis defines.
124 : *
125 : * The reason why we need to get all the nodes (at least in theory) is
126 : * because functions such as position(), last(), and count() require the
127 : * right information and that means a correct list (no possible optimization
128 : * there!) However, those functions cannot get applied before the predicates
129 : * written between square brackets ('[' and ']'). This means the gathering of
130 : * the list of nodes can make use of the axis, prefix, and tag name.
131 : *
132 : * Once we have a list of nodes, we can apply the predicates against that
133 : * list. This is done with a for() loop and an index (this is important to
134 : * get the right value for the position() command.) Since a predicate can
135 : * itself include an XPath, the process is repeated there to compute the
136 : * predicate. The current state is therefore saved and a new state created
137 : * to manage the predicate. Once the predicate returns, the result is a
138 : * Boolean. If true, that node is added to the result. If false, the node
139 : * is ignored.
140 : *
141 : * The axis is the function that determines what is to be read from the DOM.
142 : * The prefix and the tag name are the preliminary predicates that can be
143 : * applied as the DOM is being scanned.
144 : *
145 : * \code
146 : * setup state with input node(s) as the list of nodes in that state
147 : * for each union
148 : * create a child state as a clone of the original input state
149 : * for each entry
150 : * if current state node-set is empty, exit loop
151 : * mark entry as current
152 : * for each node in the current state
153 : * (as nodes are selected they become the current context node)
154 : * create a child state
155 : * run the current selection against the current state and save the nodes in the child state
156 : * for each node in the child state
157 : * for each predicate
158 : * calculate the predicate
159 : * if the predicate returned true
160 : * or the predicate returned a number and position() == number
161 : * or the predicate returned a non-empty list of nodes [TBD]
162 : * save the node in the result
163 : * endif
164 : * loop
165 : * loop
166 : * loop
167 : * add the child state result to the current state result
168 : * loop
169 : * add the child state result to the current state result
170 : * loop
171 : * \endcode
172 : *
173 : * The UnionExpr expression becomes the outter most loop. That loop may
174 : * be removed if no union actually exists (no | was used between two
175 : * paths.)
176 : *
177 : * Note that several of the "for each" loops are actually unfolded in
178 : * the program because each loop iteration has completely different things
179 : * to work on. Here are simplied examples of the UnionExpr and the
180 : * PathExpr.
181 : *
182 : * \code
183 : * // Main Program
184 : * setup original state
185 : * call UnionExpr 1
186 : * return
187 : *
188 : * // UnionExpr
189 : * create clone of original state, make the clone current
190 : * call PathExpr 1
191 : * add current state result to original state
192 : * call PathExpr 2
193 : * add current state result to original state
194 : * call PathExpr 3
195 : * add current state result to original state
196 : * ...
197 : * return
198 : *
199 : * // PathExpr 1
200 : * create an empty child state
201 : * apply Entry 1 computing a node-set
202 : * for each node in child state
203 : * call Predicate 1.1
204 : * loop
205 : * if child state result is empty, return
206 : * replace child state node-set with result node-set
207 : * for each node in child state
208 : * call Predicate 1.2
209 : * loop
210 : * if child state result is empty, return
211 : * replace child state node-set with result node-set
212 : * for each node in child state
213 : * call Predicate 1.3
214 : * loop
215 : * if current state result is empty, return
216 : * replace child state node-set with result node-set
217 : * ...
218 : *
219 : * apply Entry 2 computing a node-set
220 : * for each node in child state
221 : * call Predicate 2.1
222 : * loop
223 : * if child state result is empty, return
224 : * replace child state node-set with result node-set
225 : * for each node in child state
226 : * call Predicate 2.2
227 : * loop
228 : * if child state result is empty, return
229 : * replace child state node-set with result node-set
230 : * for each node in child state
231 : * call Predicate 2.3
232 : * loop
233 : * if current state result is empty, return
234 : * replace child state node-set with result node-set
235 : * ...
236 : *
237 : * apply Entry 3 computing a node-set
238 : * for each node in child state
239 : * call Predicate 3.1
240 : * loop
241 : * if child state result is empty, return
242 : * replace child state node-set with result node-set
243 : * for each node in child state
244 : * call Predicate 3.2
245 : * loop
246 : * if child state result is empty, return
247 : * replace child state node-set with result node-set
248 : * for each node in child state
249 : * call Predicate 3.3
250 : * loop
251 : * if current state result is empty, return
252 : * replace child state node-set with result node-set
253 : * ...
254 : *
255 : * return
256 : *
257 : * \endcode
258 : *
259 : * \code
260 : * ...
261 : * create an empty child state + apply Entry 1 computing a node-set (i.e. axis)
262 : * if child node-set is empty, return
263 : * set current node position to 1
264 : * label repeat:
265 : * call predicate
266 : * increment position
267 : * if position <= size of current node-set, goto repeat
268 : * if child state result is empty, return
269 : * ...
270 : * \endcode
271 : *
272 : * So the selection of the nodes to fill the child state is a "simple"
273 : * instruction that implements the axis, that instruction loops through
274 : * the nodes from the DOM and add them to a list if they match the top
275 : * predicates: the prefix and the tag name.
276 : *
277 : * The calculation of the predicate, however, is the equivalent of a
278 : * recursive call to the whole process with the current node as the
279 : * context node. The return value of the predicate function is used
280 : * to know whether the node considered as the context node is kept
281 : * in the list. (Note that we cannot remove the node from the list
282 : * until all the nodes in that one list were processed of functions
283 : * such as count() and last() would fail misarably. Instead we use a
284 : * result list and replace the current list with that result list
285 : * before processing the next predicate.) Also, in the compiled
286 : * form, a predicate is a function since we need to be able to call the
287 : * same predicate over and over again with each child node.
288 : *
289 : * The resulting XPath program is very similar to what you'd get in
290 : * a forth language program. We use a stack to compute the current
291 : * expression and we have the equivalent of a stack for states (when
292 : * we create a child state, we simply make use of the next state in
293 : * a vector of states, adding a new state if the last one was already
294 : * in use.)
295 : *
296 : * The following are the instructions used to run our programs. Note
297 : * that in memory those instructions are bytes.
298 : *
299 : * The different types supported:
300 : *
301 : * \li boolean
302 : * \li integer
303 : * \li double
304 : * \li string
305 : * \li node-set
306 : *
307 : * \code
308 : * // stack instructions
309 : * push_true
310 : * push_false
311 : * push_zero
312 : * push_byte (1 byte)
313 : * push_short (2 bytes)
314 : * push_long (4 bytes)
315 : * push_longlong (8 bytes)
316 : * push_double_zero
317 : * push_double
318 : * push_empty_string
319 : * push_any (string "*")
320 : * push_string
321 : * push_empty_set
322 : * swap # (# is 0 to 9 meaning: apply to 'top of stack + #')
323 : * pop #
324 : * dup #
325 : *
326 : * // change Program Counter (pop 1 integer for offset)
327 : * call
328 : * return
329 : * return_if_true (pop 1 boolean item)
330 : * return_if_false (pop 1 boolean item)
331 : * jump
332 : * jump_if_true (pop 1 boolean item)
333 : * jump_if_false (pop 1 boolean item)
334 : * jump_if_zero (pop 1 number item)
335 : *
336 : * // state management
337 : * clone_state
338 : * new_state
339 : * get_position
340 : * set_position
341 : * get_node_set
342 : * set_node_set
343 : * get_result
344 : * set_result
345 : *
346 : * // applying entry, full (pop axis, pop node type, pop prefix, pop local part)
347 : * // popped axis: ancestor, ancestor-or-self, attribute, child,
348 : * // descendant, descendant-or-self, following,
349 : * // following, following-sibling, namespace, parent,
350 : * // preceding, preceding-sibling, self
351 : * // popped node type: node, comment, processing-instruction, text
352 : * axis
353 : *
354 : * // variable handling
355 : * get_variable -- pop name, get variable, return value
356 : * set_variable -- pop name, pop value, set variable
357 : *
358 : * // arithmetic, logic, relational, ...
359 : * // pop one or two objects, apply operation, push result on the stack
360 : * add
361 : * subtract
362 : * increment
363 : * decrement
364 : * multiply
365 : * divide
366 : * modulo
367 : * negate
368 : * or
369 : * and
370 : * xor
371 : * not
372 : * equal
373 : * not_equal
374 : * less_than
375 : * less_or_equal
376 : * greater_than
377 : * greater_or_equal
378 : * node_set_size
379 : * append_node_set
380 : * interset_node_set
381 : * except_node_set
382 : * \endcode
383 : *
384 : * Example of the "loop" of the UnionExpr:
385 : *
386 : * \code
387 : * ; we assume that the class initializes an initial state
388 : * label start_program:
389 : * ; repeat union code from here
390 : * {insert path_expr_123 here--this does not need to be a sub-function}
391 : * ; WARNING: the following 3 instructions do not apply if the UnionExpr
392 : * ; does not include a 'union' or '|' operator; this is very
393 : * ; important since append_node_set does not accept but node sets
394 : * get_result
395 : * append_node_set
396 : * set_result
397 : * ; repeat for each entry
398 : * get_result
399 : * return
400 : * \endcode
401 : *
402 : * Example of the loop of a PathExpr:
403 : *
404 : * \code
405 : * label path_expr_123: ; some PathExpr
406 : * new_state
407 : * -- repeat PathExpr code from here
408 : * push "page"
409 : * push_any
410 : * push "node"
411 : * push "child"
412 : * axis
413 : * get_node_set
414 : * node_set_size
415 : * jump_if_zero exit
416 : * push_integer 1 ; these two lines probably represent the default position?
417 : * set_position
418 : * -- repeat Predicate code from here
419 : * label next:
420 : * call predicate ; some predicate function...
421 : * get_position
422 : * increment
423 : * dup
424 : * set_position
425 : * get_size
426 : * less_or_equal
427 : * jump_if_true next
428 : * get_result
429 : * node_set_size
430 : * jump_if_zero exit
431 : * get_result
432 : * set_node_set
433 : * push_empty_set
434 : * set_result
435 : * ; repeat with the each predicate
436 : * ; then repeat with the next path entry
437 : * label exit:
438 : * get_node_set
439 : * delete_state
440 : * return
441 : * \endcode
442 : */
443 :
444 :
445 : /** \typedef QDomXPath::instruction_t
446 : * \brief Instructions composed the program once the XPath is compiled.
447 : *
448 : * An XPath is composed of many elements. In order to make it easy to
449 : * process an XPath against XML data, we compile the XPath to a byte
450 : * code language, which is defined as a vector of bytes representing
451 : * instructions, sizes, or immediate data.
452 : *
453 : * This works along an execution environment structure that keeps track
454 : * of the data (list of nodes and current status in general.) The status
455 : * uses a stack to handle expressions as with the forth language.
456 : *
457 : * We use a current state as well as the stack. The current state is
458 : * always accessible (i.e. it is like having access to a variable.)
459 : */
460 :
461 :
462 : /** \brief Implementation of the DOM based XPath.
463 : *
464 : * This class is used to implement the internals of the XPath based
465 : * on a DOM. The class is an xpath 1.0 parser which makes use of a DOM
466 : * as its source of content. This is different from the standard XPath
467 : * implementation offered by Qt which only works on an XML stream.
468 : *
469 : * This implementation is specifically adapted for Snap! since Snap!
470 : * works with DOMs.
471 : */
472 0 : class QDomXPath::QDomXPathImpl
473 : {
474 : public:
475 :
476 :
477 : /** \brief The character type used by this class.
478 : *
479 : * The character type in case it were to change in the future to a UCS-4
480 : * character.
481 : */
482 : typedef ushort char_t;
483 :
484 : /** \brief End of input indicator.
485 : *
486 : * While reading the input (f_in) characters are returned. Once the last
487 : * character is reached, the END_OF_PATH value is returned instead.
488 : *
489 : * Note that this -1 (or 0xFFFF) is not a valid XML character, it will
490 : * not detect END_OF_PATH at the wrong time.
491 : */
492 : static const char_t END_OF_PATH = static_cast<char_t>(-1);
493 :
494 : /** \brief Structure that holds the token information.
495 : *
496 : * This structure is used when parsing a token. By default it is
497 : * marked as undefined. The token can be tested with the bool
498 : * operator (i.e. if(token) ...) to know whether it is defined
499 : * (true) or undefined (false).
500 : */
501 0 : struct token_t
502 : {
503 : /** \brief List of tokens.
504 : *
505 : * This list of token is very large since The XML Path
506 : * defines a rather large number of function and other
507 : * names to be used to query an XML document node.
508 : */
509 : enum class tok_t
510 : {
511 : TOK_UNDEFINED,
512 : TOK_INVALID,
513 :
514 : TOK_OPEN_PARENTHESIS,
515 : TOK_CLOSE_PARENTHESIS,
516 : TOK_OPEN_SQUARE_BRACKET,
517 : TOK_CLOSE_SQUARE_BRACKET,
518 : TOK_DOT,
519 : TOK_DOUBLE_DOT,
520 : TOK_AT,
521 : TOK_COMMA,
522 : TOK_COLON,
523 : TOK_DOUBLE_COLON,
524 : TOK_SLASH,
525 : TOK_DOUBLE_SLASH,
526 : TOK_PIPE,
527 : TOK_PLUS,
528 : TOK_MINUS,
529 : TOK_EQUAL,
530 : TOK_NOT_EQUAL,
531 : TOK_LESS_THAN,
532 : TOK_LESS_OR_EQUAL,
533 : TOK_GREATER_THAN,
534 : TOK_GREATER_OR_EQUAL,
535 : TOK_ASTERISK,
536 : TOK_DOLLAR,
537 : TOK_STRING,
538 : TOK_INTEGER,
539 : TOK_REAL,
540 : TOK_OPERATOR_AND,
541 : TOK_OPERATOR_OR,
542 : TOK_OPERATOR_MOD,
543 : TOK_OPERATOR_DIV,
544 : TOK_NODE_TYPE_COMMENT,
545 : TOK_NODE_TYPE_TEXT,
546 : TOK_NODE_TYPE_PROCESSING_INSTRUCTION,
547 : TOK_NODE_TYPE_NODE,
548 : TOK_AXIS_NAME_ANCESTOR,
549 : TOK_AXIS_NAME_ANCESTOR_OR_SELF,
550 : TOK_AXIS_NAME_ATTRIBUTE,
551 : TOK_AXIS_NAME_CHILD,
552 : TOK_AXIS_NAME_DESCENDANT,
553 : TOK_AXIS_NAME_DESCENDANT_OR_SELF,
554 : TOK_AXIS_NAME_FOLLOWING,
555 : TOK_AXIS_NAME_FOLLOWING_SIBLING,
556 : TOK_AXIS_NAME_NAMESPACE,
557 : TOK_AXIS_NAME_PARENT,
558 : TOK_AXIS_NAME_PRECEDING,
559 : TOK_AXIS_NAME_PRECEDING_SIBLING,
560 : TOK_AXIS_NAME_SELF,
561 : TOK_PREFIX,
562 : TOK_NCNAME
563 : };
564 :
565 : /** \brief Test whether the token is defined.
566 : *
567 : * This function checks whether the token is defined. If defined,
568 : * it returns true.
569 : *
570 : * \return true if the token is not TOK_UNDEFINED.
571 : */
572 0 : operator bool ()
573 : {
574 0 : return f_token != tok_t::TOK_UNDEFINED;
575 : }
576 :
577 : /** \brief Test whether the token is undefined.
578 : *
579 : * This function checks whether the token is undefined. If not defined,
580 : * it returns true.
581 : *
582 : * \return true if the token is TOK_UNDEFINED.
583 : */
584 : bool operator ! ()
585 : {
586 : return f_token == tok_t::TOK_UNDEFINED;
587 : }
588 :
589 : /** \brief Make the token undefined.
590 : *
591 : * This function marks the token as being undefined.
592 : */
593 0 : void reset()
594 : {
595 0 : f_token = tok_t::TOK_UNDEFINED;
596 0 : }
597 :
598 : tok_t f_token = tok_t::TOK_UNDEFINED;
599 : QString f_string = QString();
600 : int64_t f_integer = 0;
601 : double f_real = 0.0;
602 : };
603 :
604 : /** \brief An array of tokens.
605 : *
606 : * The typedef defines an array of token which is generally used to
607 : * represent the stack.
608 : */
609 : typedef QVector<token_t> token_vector_t;
610 :
611 :
612 : /** \brief A map of label offsets.
613 : *
614 : * This map is used to define offset names and their offset in the program.
615 : */
616 : typedef QMap<QString, uint32_t> label_offsets_t;
617 :
618 :
619 : /** \brief Jump to Label structure.
620 : *
621 : * Whenever a jump to a future label is added, we need to "fix" the push
622 : * integer at a later time. This is done by saving the label in this way
623 : * and later by searching for it to fix it.
624 : */
625 : typedef QVector<int> jump_to_label_vector_t;
626 :
627 :
628 : /** \brief An array of future labels.
629 : *
630 : * The list of future labels is saved in a vector. Whenever you mark
631 : * the label, the future labels are corrected and removed from the
632 : * list. Since the calls are "recursive", this table can grow pretty
633 : * big and maybe we'll use a map at a later time.
634 : */
635 : typedef QMap<QString, jump_to_label_vector_t> future_labels_t;
636 :
637 :
638 : /** \brief An array of labels.
639 : *
640 : * This list of labels is used within the parsing of a location path.
641 : * Labels are created before each axis and "released" when the end of
642 : * the location path is reached.
643 : *
644 : * We do not need more than the offset at the time the label is created
645 : * since it is created first and jumped to later.
646 : */
647 : typedef QVector<int> labels_t;
648 :
649 :
650 : /** \brief Current context while running.
651 : *
652 : * While running the program, the context defines the current status of
653 : * the process. It includes the current set of nodes, the context node
654 : * (i.e. f_nodes[f_position]) and the set of nodes in the result being
655 : * computed right now.
656 : *
657 : * By default the position is set to -1 which means "not set"
658 : * and it cannot be used (if accessed [as in INST_GET_POSITION],
659 : * you get an error.)
660 : */
661 0 : struct context_t
662 : {
663 : int32_t f_position = -1;
664 : QDomXPath::node_vector_t f_nodes = QDomXPath::node_vector_t();
665 : QDomXPath::node_vector_t f_result = QDomXPath::node_vector_t();
666 : };
667 :
668 :
669 : /** \brief An array of contexts.
670 : *
671 : * Whenever a new context is created (i.e. when a new entry in a list of
672 : * entries starts execution) then it is added at the back of this array.
673 : *
674 : * The program as a whole has one stack of states.
675 : */
676 : typedef QVector<context_t> context_vector_t;
677 :
678 :
679 : /** \brief A sub-class of the variant_t definition.
680 : *
681 : * The atomic values are defined in a separate class so that way we can
682 : * create sets of atomic values without being bothered by sub-sets which
683 : * are not supported by XPath 2.0 anyway (i.e. ((1, 2), (3, 4)) becomes
684 : * (1, 2, 3, 4) which is one set.)
685 : */
686 0 : class atomic_value_t
687 : {
688 : public:
689 : /** \brief Atomic types.
690 : *
691 : * The atomic types are used internally to determine the type of
692 : * variant the data is. The type is defined in the atomic_value_t
693 : * since it is necessary here. It could be defined outside too
694 : * although this way it defines it in a namespace like environment.
695 : */
696 : enum class type_t
697 : {
698 : ATOMIC_TYPE_UNDEFINED,
699 :
700 : ATOMIC_TYPE_NULL,
701 : ATOMIC_TYPE_END_OF_ARGUMENTS,
702 : ATOMIC_TYPE_BOOLEAN,
703 : ATOMIC_TYPE_INTEGER,
704 : //ATOMIC_TYPE_DECIMAL,
705 : ATOMIC_TYPE_SINGLE,
706 : ATOMIC_TYPE_DOUBLE,
707 : ATOMIC_TYPE_STRING,
708 :
709 : // we include the non-atomic types too
710 : ATOMIC_TYPE_SET,
711 : ATOMIC_TYPE_NODE_SET
712 : //ATOMIC_TYPE_CONTEXT
713 : };
714 :
715 : /** \brief Initialize the atomic value.
716 : *
717 : * This function sets the type of the atomic value to NULL which means
718 : * that it is undefined. Trying to get a value when an atomic value
719 : * is NULL generates an error by default although if the cast is set
720 : * to true.
721 : */
722 0 : atomic_value_t()
723 0 : : f_type(type_t::ATOMIC_TYPE_NULL)
724 : //, f_boolean(false) -- not necessary -- not actually used for now
725 : //, f_integer(0) -- not necessary
726 : //, f_decimal(0) -- not necessary -- not actually used for now
727 : //, f_single(0.0f) -- not necessary
728 : //, f_doulbe(0.0) -- not necessary
729 : //, f_string("") -- auto-init
730 : {
731 0 : }
732 :
733 : /** \brief Copy a value in another.
734 : *
735 : * Because some parameters may not be defined, the copy operator is
736 : * overloaded to only copy what is necessary and avoid errors.
737 : *
738 : * \param[in] rhs The right hand side to copy in this value.
739 : */
740 0 : atomic_value_t(atomic_value_t const& rhs)
741 0 : : f_type(rhs.f_type)
742 : //, f_boolean(false) -- handled below if necessary -- not actually used for now
743 : //, f_integer(0) -- handled below if necessary
744 : //, f_decimal(0) -- handled below if necessary -- not actually used for now
745 : //, f_single(0.0f) -- handled below if necessary
746 : //, f_doulbe(0.0) -- handled below if necessary
747 : //, f_string("") -- handled below if necessary
748 : {
749 0 : switch(f_type)
750 : {
751 0 : case type_t::ATOMIC_TYPE_NULL:
752 : case type_t::ATOMIC_TYPE_END_OF_ARGUMENTS:
753 : // no data to copy
754 0 : break;
755 :
756 0 : case type_t::ATOMIC_TYPE_BOOLEAN:
757 : //f_boolean = rhs.f_boolean; // using integer instead for now
758 : case type_t::ATOMIC_TYPE_INTEGER:
759 0 : f_integer = rhs.f_integer;
760 0 : break;
761 :
762 0 : case type_t::ATOMIC_TYPE_SINGLE:
763 0 : f_single = rhs.f_single;
764 0 : break;
765 :
766 0 : case type_t::ATOMIC_TYPE_DOUBLE:
767 0 : f_double = rhs.f_double;
768 0 : break;
769 :
770 0 : case type_t::ATOMIC_TYPE_STRING:
771 0 : f_string = rhs.f_string;
772 0 : break;
773 :
774 0 : case type_t::ATOMIC_TYPE_SET:
775 : case type_t::ATOMIC_TYPE_NODE_SET:
776 0 : break;
777 :
778 : //case type_t::ATOMIC_TYPE_UNDEFINED:
779 0 : default:
780 0 : throw QDomXPathException_NotImplemented(QString("copying of type %1 is not implemented").arg(static_cast<int>(static_cast<type_t>(f_type))));
781 :
782 : }
783 0 : }
784 :
785 : /** \brief Copy a value in another.
786 : *
787 : * Because some parameters may not be defined, the copy operator is
788 : * overloaded to only copy what is necessary and avoid errors.
789 : *
790 : * \param[in] rhs The right hand side to copy in this value.
791 : *
792 : * \return A reference to this object.
793 : */
794 0 : atomic_value_t& operator = (atomic_value_t const& rhs)
795 : {
796 0 : if(this != &rhs)
797 : {
798 0 : f_type = rhs.f_type;
799 0 : switch(f_type)
800 : {
801 0 : case type_t::ATOMIC_TYPE_NULL:
802 : case type_t::ATOMIC_TYPE_END_OF_ARGUMENTS:
803 : // no data to copy
804 0 : break;
805 :
806 0 : case type_t::ATOMIC_TYPE_BOOLEAN:
807 : //f_boolean = rhs.f_boolean; // using integer instead for now
808 : case type_t::ATOMIC_TYPE_INTEGER:
809 0 : f_integer = rhs.f_integer;
810 0 : break;
811 :
812 0 : case type_t::ATOMIC_TYPE_SINGLE:
813 0 : f_single = rhs.f_single;
814 0 : break;
815 :
816 0 : case type_t::ATOMIC_TYPE_DOUBLE:
817 0 : f_double = rhs.f_double;
818 0 : break;
819 :
820 0 : case type_t::ATOMIC_TYPE_STRING:
821 0 : f_string = rhs.f_string;
822 0 : break;
823 :
824 0 : case type_t::ATOMIC_TYPE_SET:
825 : case type_t::ATOMIC_TYPE_NODE_SET:
826 0 : break;
827 :
828 : //case type_t::ATOMIC_TYPE_UNDEFINED:
829 0 : default:
830 0 : throw QDomXPathException_NotImplemented(QString("copying of type %1 is not implemented").arg(static_cast<int>(static_cast<type_t>(f_type))));
831 :
832 : }
833 : }
834 0 : return *this;
835 : }
836 :
837 : /** \brief Get the type.
838 : *
839 : * Return the type of this atomic value. This determines the get<name>()
840 : * function that can be called to retrieve the value as is. Other
841 : * get<name>() functions can also be called, but only to cast the value
842 : * and in that case the cast parameter must be set to true or the
843 : * function throws a QDomXPathException_WrongType exception.
844 : *
845 : * To test whether an atomtic_value_t is NULL, use the getType()
846 : * and compare it with type_t::ATOMIC_TYPE_NULL.
847 : */
848 0 : type_t getType() const
849 : {
850 0 : return f_type;
851 : }
852 :
853 : /** \brief Set the value to NULL.
854 : *
855 : * This function sets the value to NULL. NULL is the default value
856 : * of an atomic value object.
857 : *
858 : * All other types are lost after this call.
859 : */
860 : void setValue()
861 : {
862 : f_type = type_t::ATOMIC_TYPE_NULL;
863 : }
864 :
865 : /** \brief Set the value to End of Arguments.
866 : *
867 : * This function sets the value to End of Arguments. This special value
868 : * is used to end the list of arguments to a function. Note that it
869 : * is always expected to be popped by the function being called and
870 : * not by an expression. This is why there is not even one convertion
871 : * function for this special type.
872 : */
873 0 : void setEndOfArguments()
874 : {
875 0 : f_type = type_t::ATOMIC_TYPE_END_OF_ARGUMENTS;
876 0 : }
877 :
878 : /** \brief Retrieve the value as a Boolean.
879 : *
880 : * The function retrieves the atomic value as a Boolean. If the value
881 : * is not a Boolean and the \p cast parameter is not set to true,
882 : * then the function generates an exception. Otherwise the function
883 : * attempts to convert the value to a Boolean.
884 : *
885 : * \exception QDomXPathException_WrongType
886 : * This exception is raised if the atomic type is not
887 : * type_t::ATOMIC_TYPE_BOOLEAN.
888 : *
889 : * \param[in] cast Whether to cast the value if it is not a Boolean.
890 : *
891 : * \return The value as a Boolean.
892 : */
893 0 : bool getBooleanValue(bool cast = false) const
894 : {
895 0 : if(f_type != type_t::ATOMIC_TYPE_BOOLEAN)
896 : {
897 0 : if(!cast)
898 : {
899 0 : throw QDomXPathException_WrongType(QString("atomic type is %1, when a Boolean was requested").arg(static_cast<int>(static_cast<type_t>(f_type))));
900 : }
901 : }
902 0 : switch(f_type)
903 : {
904 0 : case type_t::ATOMIC_TYPE_NULL:
905 0 : return false;
906 :
907 0 : case type_t::ATOMIC_TYPE_BOOLEAN:
908 : //return f_boolean;
909 : case type_t::ATOMIC_TYPE_INTEGER:
910 0 : return f_integer != 0;
911 :
912 : #pragma GCC diagnostic push
913 : #pragma GCC diagnostic ignored "-Wfloat-equal"
914 0 : case type_t::ATOMIC_TYPE_SINGLE:
915 0 : return 0.0f != f_single;
916 :
917 0 : case type_t::ATOMIC_TYPE_DOUBLE:
918 0 : return 0.0 != f_double;
919 : #pragma GCC diagnostic pop
920 :
921 0 : case type_t::ATOMIC_TYPE_STRING:
922 : // TODO -- I think this is totally wrong; if I'm correct it
923 : // should interpret the string as true or false and
924 : // not whether the string is empty (will test later)
925 0 : return !f_string.isEmpty();
926 :
927 0 : default:
928 : // this should be done in the previous level
929 0 : throw QDomXPathException_NotImplemented(QString("type %1 to Boolean conversion is not implemented").arg(static_cast<int>(static_cast<type_t>(f_type))));
930 :
931 : }
932 : }
933 :
934 : /** \brief Set the value to the specified Boolean.
935 : *
936 : * This function sets the atomic value to the specified Boolean
937 : * and sets the type to type_t::ATOMIC_TYPE_BOOLEAN.
938 : *
939 : * All other types are lost after this call.
940 : *
941 : * \param[in] b The new Boolean value for this atomtic type.
942 : */
943 0 : void setValue(const bool b)
944 : {
945 0 : f_type = type_t::ATOMIC_TYPE_BOOLEAN;
946 : //f_boolean = b;
947 0 : f_integer = b ? 1 : 0;
948 0 : }
949 :
950 : /** \brief Retrieve the value as an Integer.
951 : *
952 : * The function retrieves the atomic value as an Integer. If the value
953 : * is not an integer and the \p cast parameter is not set to true,
954 : * then the function generates an exception. Otherwise the function
955 : * attempts to convert the value to an integer.
956 : *
957 : * \exception QDomXPathException_WrongType
958 : * This exception is raised if the atomic type is not
959 : * type_t::ATOMIC_TYPE_INTEGER.
960 : *
961 : * \param[in] cast Whether to cast the value if it is not an Integer.
962 : *
963 : * \return The value as an Integer.
964 : */
965 0 : int64_t getIntegerValue(bool cast = false) const
966 : {
967 0 : if(f_type != type_t::ATOMIC_TYPE_INTEGER)
968 : {
969 0 : if(!cast)
970 : {
971 0 : throw QDomXPathException_WrongType(QString("atomic type is %1, when an Integer was requested").arg(static_cast<int>(static_cast<type_t>(f_type))));
972 : }
973 : }
974 0 : switch(f_type)
975 : {
976 0 : case type_t::ATOMIC_TYPE_NULL:
977 0 : return 0;
978 :
979 0 : case type_t::ATOMIC_TYPE_BOOLEAN:
980 0 : return f_integer != 0 ? 1 : 0;
981 :
982 0 : case type_t::ATOMIC_TYPE_INTEGER:
983 0 : return f_integer;
984 :
985 0 : case type_t::ATOMIC_TYPE_SINGLE:
986 0 : return static_cast<int64_t>(std::floor(f_single));
987 :
988 0 : case type_t::ATOMIC_TYPE_DOUBLE:
989 0 : return static_cast<int64_t>(std::floor(f_double));
990 :
991 0 : case type_t::ATOMIC_TYPE_STRING:
992 : // TODO -- create a "correct" string to integer convertion
993 0 : return static_cast<int64_t>(atol(f_string.toUtf8().data()));
994 :
995 0 : default:
996 : // this should be done in the previous level
997 0 : throw QDomXPathException_NotImplemented(QString("type %1 to integer conversion is not implemented").arg(static_cast<int>(static_cast<type_t>(f_type))));
998 :
999 : }
1000 : }
1001 :
1002 : /** \brief Set the value to the specified integer.
1003 : *
1004 : * This function sets the atomic value to the specified integer
1005 : * and sets the type to type_t::ATOMIC_TYPE_INTEGER.
1006 : *
1007 : * All other types are lost after this call.
1008 : *
1009 : * \param[in] integer The new integer value for this atomtic type.
1010 : */
1011 0 : void setValue(const int64_t integer)
1012 : {
1013 0 : f_type = type_t::ATOMIC_TYPE_INTEGER;
1014 0 : f_integer = integer;
1015 0 : }
1016 :
1017 : //void setValue(const QDecimal& decimal)
1018 : //{
1019 : // f_type = type_t::ATOMIC_TYPE_DECIMAL;
1020 : // f_decimal = decimal;
1021 : //}
1022 :
1023 : /** \brief Retrieve the value as a Single.
1024 : *
1025 : * The function retrieves the atomic value as a Single. If the value
1026 : * is not a Single and the \p cast parameter is not set to true,
1027 : * then the function generates an exception. Otherwise the function
1028 : * attempts to convert the value to a Single.
1029 : *
1030 : * \exception QDomXPathException_WrongType
1031 : * This exception is raised if the atomic type is not
1032 : * type_t::ATOMIC_TYPE_SINGLE.
1033 : *
1034 : * \param[in] cast Whether to cast the value if it is not a Single.
1035 : *
1036 : * \return The value as a Single.
1037 : */
1038 0 : float getSingleValue(bool cast = false) const
1039 : {
1040 0 : if(f_type != type_t::ATOMIC_TYPE_SINGLE)
1041 : {
1042 0 : if(!cast)
1043 : {
1044 0 : throw QDomXPathException_WrongType(QString("atomic type is %1, when a Single was requested").arg(static_cast<int>(static_cast<type_t>(f_type))));
1045 : }
1046 : }
1047 0 : switch(f_type)
1048 : {
1049 0 : case type_t::ATOMIC_TYPE_NULL:
1050 0 : return 0.0f;
1051 :
1052 0 : case type_t::ATOMIC_TYPE_BOOLEAN:
1053 0 : return f_integer == 0 ? 0.0f : 1.0f;
1054 :
1055 0 : case type_t::ATOMIC_TYPE_INTEGER:
1056 0 : return static_cast<float>(f_integer);
1057 :
1058 0 : case type_t::ATOMIC_TYPE_SINGLE:
1059 0 : return f_single;
1060 :
1061 0 : case type_t::ATOMIC_TYPE_DOUBLE:
1062 0 : return static_cast<float>(f_double);
1063 :
1064 0 : case type_t::ATOMIC_TYPE_STRING:
1065 0 : return static_cast<float>(atof(f_string.toUtf8().data()));
1066 :
1067 0 : default:
1068 : // this should be done in the previous level
1069 0 : throw QDomXPathException_NotImplemented(QString("type %1 to single is not implemented").arg(static_cast<int>(static_cast<type_t>(f_type))));
1070 :
1071 : }
1072 : }
1073 :
1074 : /** \brief Set the value to the specified Single.
1075 : *
1076 : * This function sets the atomic value to the specified Single
1077 : * and sets the type to type_t::ATOMIC_TYPE_SINGLE.
1078 : *
1079 : * All other types are lost after this call.
1080 : *
1081 : * \param[in] real The new Single value for this atomtic type.
1082 : */
1083 0 : void setValue(const float real)
1084 : {
1085 0 : f_type = type_t::ATOMIC_TYPE_SINGLE;
1086 0 : f_single = real;
1087 0 : }
1088 :
1089 : /** \brief Retrieve the value as a Double.
1090 : *
1091 : * The function retrieves the atomic value as a Double. If the value
1092 : * is not a Double and the \p cast parameter is not set to true,
1093 : * then the function generates an exception. Otherwise the function
1094 : * attempts to convert the value to a Double.
1095 : *
1096 : * \exception QDomXPathException_WrongType
1097 : * This exception is raised if the atomic type is not
1098 : * type_t::ATOMIC_TYPE_DOUBLE.
1099 : *
1100 : * \param[in] cast Whether to cast the value if it is not a Double.
1101 : *
1102 : * \return The value as a double.
1103 : */
1104 0 : double getDoubleValue(bool cast = false) const
1105 : {
1106 0 : if(f_type != type_t::ATOMIC_TYPE_DOUBLE)
1107 : {
1108 0 : if(!cast)
1109 : {
1110 0 : throw QDomXPathException_WrongType(QString("atomic type is %1, when a Double was requested").arg(static_cast<int>(static_cast<type_t>(f_type))));
1111 : }
1112 : }
1113 0 : switch(f_type)
1114 : {
1115 0 : case type_t::ATOMIC_TYPE_NULL:
1116 0 : return 0.0;
1117 :
1118 0 : case type_t::ATOMIC_TYPE_BOOLEAN:
1119 0 : return f_integer == 0 ? 0.0 : 1.0;
1120 :
1121 0 : case type_t::ATOMIC_TYPE_INTEGER:
1122 0 : return static_cast<double>(f_integer);
1123 :
1124 0 : case type_t::ATOMIC_TYPE_SINGLE:
1125 0 : return static_cast<double>(f_single);
1126 :
1127 0 : case type_t::ATOMIC_TYPE_DOUBLE:
1128 0 : return f_double;
1129 :
1130 0 : case type_t::ATOMIC_TYPE_STRING:
1131 : // TODO -- properly convert the string (as per the XPath
1132 : // documentation) we whouls have an external function
1133 : // for the purpose so any convertion uses the same code
1134 0 : return atof(f_string.toUtf8().data());
1135 :
1136 0 : default:
1137 : // this should be done in the previous level
1138 0 : throw QDomXPathException_NotImplemented(QString("type %1 to double conversion is not implemented").arg(static_cast<int>(static_cast<type_t>(f_type))));
1139 :
1140 : }
1141 : }
1142 :
1143 : /** \brief Set the value to the specified double.
1144 : *
1145 : * This function sets the atomic value to the specified double
1146 : * and sets the type to type_t::ATOMIC_TYPE_DOUBLE.
1147 : *
1148 : * All other types are lost after this call.
1149 : *
1150 : * \param[in] real The new double value for this atomtic type.
1151 : */
1152 0 : void setValue(const double real)
1153 : {
1154 0 : f_type = type_t::ATOMIC_TYPE_DOUBLE;
1155 0 : f_double = real;
1156 0 : }
1157 :
1158 : /** \brief Retrieve the value as a String.
1159 : *
1160 : * The function retrieves the atomic value as a String. If the value
1161 : * is not a String and the \p cast parameter is not set to true,
1162 : * then the function generates an exception. Otherwise the function
1163 : * attempts to convert the value to a String.
1164 : *
1165 : * \exception QDomXPathException_WrongType
1166 : * This exception is raised if the atomic type is not
1167 : * type_t::ATOMIC_TYPE_STRING.
1168 : *
1169 : * \param[in] cast Whether to cast the value if it is not a String.
1170 : *
1171 : * \return The value as a String.
1172 : */
1173 0 : QString getStringValue(bool cast = false) const
1174 : {
1175 0 : if(f_type != type_t::ATOMIC_TYPE_STRING)
1176 : {
1177 0 : if(!cast)
1178 : {
1179 0 : throw QDomXPathException_WrongType(QString("atomic type is %1, when a String was requested").arg(static_cast<int>(static_cast<type_t>(f_type))));
1180 : }
1181 : }
1182 0 : switch(f_type)
1183 : {
1184 0 : case type_t::ATOMIC_TYPE_NULL:
1185 0 : return "null";
1186 :
1187 0 : case type_t::ATOMIC_TYPE_BOOLEAN:
1188 0 : return f_integer != 0 ? "true" : "false";
1189 :
1190 0 : case type_t::ATOMIC_TYPE_INTEGER:
1191 0 : return QString("%1").arg(f_integer);
1192 :
1193 0 : case type_t::ATOMIC_TYPE_SINGLE:
1194 0 : return QString("%1").arg(f_single);
1195 :
1196 0 : case type_t::ATOMIC_TYPE_DOUBLE:
1197 0 : return QString("%1").arg(f_double);
1198 :
1199 0 : case type_t::ATOMIC_TYPE_STRING:
1200 0 : return f_string;
1201 :
1202 0 : default:
1203 : // this should be done in the previous level
1204 0 : throw QDomXPathException_NotImplemented(QString("type %1 to string conversion is not implemented").arg(static_cast<int>(static_cast<type_t>(f_type))));
1205 :
1206 : }
1207 : }
1208 :
1209 : /** \brief Set the value to the specified string.
1210 : *
1211 : * This function sets the atomic value to the specified string
1212 : * and sets the type to type_t::ATOMIC_TYPE_STRING.
1213 : *
1214 : * All other types are lost after this call.
1215 : *
1216 : * \param[in] str The new string value for this atomtic type.
1217 : */
1218 0 : void setValue(const QString& str)
1219 : {
1220 0 : f_type = type_t::ATOMIC_TYPE_STRING;
1221 0 : f_string = str;
1222 0 : }
1223 :
1224 : /** \brief Set the value to the specified string.
1225 : *
1226 : * This function sets the atomic value to the specified string
1227 : * and sets the type to type_t::ATOMIC_TYPE_STRING.
1228 : *
1229 : * All other types are lost after this call.
1230 : *
1231 : * \param[in] str The new string value for this atomtic type.
1232 : */
1233 0 : void setValue(const char *str)
1234 : {
1235 0 : f_type = type_t::ATOMIC_TYPE_STRING;
1236 0 : f_string = QString::fromUtf8(str);
1237 0 : }
1238 :
1239 :
1240 : /** \brief Set the value to the specified string.
1241 : *
1242 : * This function sets the atomic value to the specified string
1243 : * and sets the type to type_t::ATOMIC_TYPE_STRING.
1244 : *
1245 : * All other types are lost after this call.
1246 : *
1247 : * \param[in] str The new string value for this atomtic type.
1248 : */
1249 : void setValue(const wchar_t *str)
1250 : {
1251 : f_type = type_t::ATOMIC_TYPE_STRING;
1252 : f_string = QString::fromWCharArray(str);
1253 : }
1254 :
1255 :
1256 : /** \brief Set the value to the specified string.
1257 : *
1258 : * This function sets the atomic value to the specified string
1259 : * and sets the type to type_t::ATOMIC_TYPE_STRING.
1260 : *
1261 : * All other types are lost after this call.
1262 : *
1263 : * \param[in] str The new string value for this atomtic type.
1264 : */
1265 : void setValue(const std::string& str)
1266 : {
1267 : f_type = type_t::ATOMIC_TYPE_STRING;
1268 : f_string = QString::fromUtf8(str.c_str());
1269 : }
1270 :
1271 :
1272 : protected:
1273 : type_t f_type = type_t::ATOMIC_TYPE_UNDEFINED;
1274 :
1275 : private:
1276 : //bool f_boolean = false; -- save some space by using integer 0 or 1
1277 : int64_t f_integer = 0;
1278 : // definition? http://www.w3.org/2002/ws/databinding/patterns/6/09/PrecisionDecimal/
1279 : //QDecimal f_decimal; // a 32:32 fix number (we need a fixed number class!)
1280 : float f_single = 0.0f;
1281 : double f_double = 0.0;
1282 : QString f_string = QString();
1283 : };
1284 :
1285 :
1286 : /** \brief Merge two types together to accelerate selections.
1287 : *
1288 : * This function is used to merge two types together so we can quickly
1289 : * select two types through the use of a switch statement.
1290 : *
1291 : * \param[in] a The left type.
1292 : * \param[in] b The right type.
1293 : *
1294 : * \return The two types merged in an int32_t.
1295 : */
1296 0 : static constexpr int32_t merge_types(atomic_value_t::type_t a, atomic_value_t::type_t b) // we are inside a class, hence the 'static'
1297 : {
1298 : return static_cast<int32_t>(a)
1299 0 : | (static_cast<int32_t>(b) << 16);
1300 : }
1301 :
1302 :
1303 : /** \brief An array of atomic value.
1304 : *
1305 : * This typedef allows us to create arrays of atomic values.
1306 : */
1307 : typedef QVector<atomic_value_t> atomic_vector_t;
1308 :
1309 : /** \brief The variant structure is used at execution time.
1310 : *
1311 : * The variant_t represents a value generally on the stack. It can also
1312 : * be viewed as the current set of nodes in a state and the current set
1313 : * of nodes in the result although for those we directly use node_vector_t
1314 : * because those are always sets of nodes.
1315 : */
1316 0 : class variant_t : public atomic_value_t
1317 : {
1318 : public:
1319 : // need to have an explicit constructor so we can have a copy
1320 : // constructor as well...
1321 0 : variant_t()
1322 : //: atomic_value_t()
1323 : //, f_atomic() -- auto-init
1324 : //, f_set() -- auto-init
1325 : //, f_node_set() -- auto-init
1326 0 : {
1327 0 : }
1328 :
1329 0 : variant_t(variant_t const& rhs)
1330 0 : : atomic_value_t(rhs)
1331 : {
1332 0 : switch(f_type)
1333 : {
1334 0 : case type_t::ATOMIC_TYPE_NULL:
1335 : case type_t::ATOMIC_TYPE_END_OF_ARGUMENTS:
1336 : case type_t::ATOMIC_TYPE_BOOLEAN:
1337 : case type_t::ATOMIC_TYPE_INTEGER:
1338 : case type_t::ATOMIC_TYPE_SINGLE:
1339 : case type_t::ATOMIC_TYPE_DOUBLE:
1340 : case type_t::ATOMIC_TYPE_STRING:
1341 : // already handled, avoid the default: ...
1342 0 : break;
1343 :
1344 0 : case type_t::ATOMIC_TYPE_SET:
1345 0 : f_set = rhs.f_set;
1346 0 : break;
1347 :
1348 0 : case type_t::ATOMIC_TYPE_NODE_SET:
1349 0 : f_node_set = rhs.f_node_set;
1350 0 : break;
1351 :
1352 0 : default:
1353 : // this should be done in the previous level
1354 : // (i.e. this line should not be reachable)
1355 0 : throw QDomXPathException_NotImplemented(QString("copying of type %1 is not implemented").arg(static_cast<int>(static_cast<type_t>(f_type))));
1356 :
1357 : }
1358 0 : }
1359 :
1360 : /** \brief Copy a value in another.
1361 : *
1362 : * Because some parameters may not be defined, the copy operator is
1363 : * overloaded to only copy what is necessary and avoid errors.
1364 : *
1365 : * \param[in] rhs The right hand side to copy in this value.
1366 : *
1367 : * \return A reference to this object.
1368 : */
1369 0 : variant_t& operator = (variant_t const& rhs)
1370 : {
1371 0 : if(this != &rhs)
1372 : {
1373 0 : f_type = rhs.f_type;
1374 0 : switch(f_type)
1375 : {
1376 0 : case type_t::ATOMIC_TYPE_NULL:
1377 : case type_t::ATOMIC_TYPE_END_OF_ARGUMENTS:
1378 : case type_t::ATOMIC_TYPE_BOOLEAN:
1379 : case type_t::ATOMIC_TYPE_INTEGER:
1380 : case type_t::ATOMIC_TYPE_SINGLE:
1381 : case type_t::ATOMIC_TYPE_DOUBLE:
1382 : case type_t::ATOMIC_TYPE_STRING:
1383 : // ignore the result, we return *this below
1384 0 : snap::NOTUSED(atomic_value_t::operator = (rhs));
1385 0 : break;
1386 :
1387 0 : case type_t::ATOMIC_TYPE_SET:
1388 0 : f_set = rhs.f_set;
1389 0 : break;
1390 :
1391 0 : case type_t::ATOMIC_TYPE_NODE_SET:
1392 0 : f_node_set = rhs.f_node_set;
1393 0 : break;
1394 :
1395 : //case type_t::ATOMIC_TYPE_UNDEFINED:
1396 0 : default:
1397 0 : throw QDomXPathException_NotImplemented(QString("copying of type %1 is not implemented").arg(static_cast<int>(static_cast<type_t>(f_type))));
1398 :
1399 : }
1400 : }
1401 0 : return *this;
1402 : }
1403 :
1404 : /** \brief Retrieve the Boolean value.
1405 : *
1406 : * This function retrieves the Boolean value from the variant. If
1407 : * the \p cast parameter is true, then the sets are also checked
1408 : * and return true if they are not empty.
1409 : *
1410 : * \param[in] cast Whether to cast the value if not a Boolean.
1411 : *
1412 : * \return The Boolean value, or whatever value converted to a Boolean.
1413 : */
1414 0 : bool getBooleanValue(bool cast = false) const
1415 : {
1416 0 : if(cast)
1417 : {
1418 0 : switch(f_type)
1419 : {
1420 0 : case type_t::ATOMIC_TYPE_SET:
1421 : // TODO -- do proper implementation
1422 0 : return !f_set.isEmpty();
1423 :
1424 0 : case type_t::ATOMIC_TYPE_NODE_SET:
1425 : // TODO -- do proper implementation
1426 0 : return !f_node_set.isEmpty();
1427 :
1428 0 : default:
1429 0 : break;
1430 :
1431 : }
1432 : }
1433 0 : return atomic_value_t::getBooleanValue(cast);
1434 : }
1435 :
1436 : /** \brief Retrieve the Integer value.
1437 : *
1438 : * This function retrieves the Integer value from the variant. If
1439 : * the \p cast parameter is true, then the sets are also checked
1440 : * and return the first value if they are not empty.
1441 : *
1442 : * \todo
1443 : * See how it should be handled in XPath 2.x because it is different
1444 : * than in XPath 1.x.
1445 : *
1446 : * \param[in] cast Whether to cast the value if not an Integer.
1447 : *
1448 : * \return The Integer value or whatever value converted to an Integer.
1449 : */
1450 0 : int64_t getIntegerValue(bool cast = false) const
1451 : {
1452 0 : if(cast)
1453 : {
1454 0 : switch(f_type)
1455 : {
1456 0 : case type_t::ATOMIC_TYPE_SET:
1457 : // TODO -- do proper implementation
1458 0 : return static_cast<int64_t>(!f_set.isEmpty());
1459 :
1460 0 : case type_t::ATOMIC_TYPE_NODE_SET:
1461 : // TODO -- do proper implementation
1462 0 : return static_cast<int64_t>(!f_node_set.isEmpty());
1463 :
1464 0 : default:
1465 0 : break;
1466 :
1467 : }
1468 : }
1469 0 : return atomic_value_t::getIntegerValue(cast);
1470 : }
1471 :
1472 : /** \brief Retrieve the single value.
1473 : *
1474 : * This function retrieves the single value from the variant. If
1475 : * the \p cast parameter is true, then the sets are also checked
1476 : * and return the first value if they are not empty.
1477 : *
1478 : * \todo
1479 : * See how it should be handled in XPath 2.x because it is different
1480 : * than in XPath 1.x.
1481 : *
1482 : * \param[in] cast Whether to cast the value if not a Single.
1483 : *
1484 : * \return The Single value, or the converted value of another type.
1485 : */
1486 0 : float getSingleValue(bool cast = false) const
1487 : {
1488 0 : if(cast)
1489 : {
1490 0 : switch(f_type)
1491 : {
1492 0 : case type_t::ATOMIC_TYPE_SET:
1493 : // TODO -- do proper implementation
1494 0 : return static_cast<float>(!f_set.isEmpty());
1495 :
1496 0 : case type_t::ATOMIC_TYPE_NODE_SET:
1497 : // TODO -- do proper implementation
1498 0 : return static_cast<float>(!f_node_set.isEmpty());
1499 :
1500 0 : default:
1501 0 : break;
1502 :
1503 : }
1504 : }
1505 0 : return atomic_value_t::getSingleValue(cast);
1506 : }
1507 :
1508 : /** \brief Retrieve the double value.
1509 : *
1510 : * This function retrieves the double value from the variant. If
1511 : * the \p cast parameter is true, then the sets are also checked
1512 : * and return the first value if they are not empty.
1513 : *
1514 : * \todo
1515 : * See how it should be handled in XPath 2.x because it is different
1516 : * than in XPath 1.x.
1517 : *
1518 : * \param[in] cast Whether to cast the value if not a Double.
1519 : *
1520 : * \return The Double value, or the converted value if not Double.
1521 : */
1522 0 : double getDoubleValue(bool cast = false) const
1523 : {
1524 0 : if(cast)
1525 : {
1526 0 : switch(f_type)
1527 : {
1528 0 : case type_t::ATOMIC_TYPE_SET:
1529 : // TODO -- do proper implementation
1530 0 : return static_cast<double>(!f_set.isEmpty());
1531 :
1532 0 : case type_t::ATOMIC_TYPE_NODE_SET:
1533 : {
1534 0 : QString str(getStringValue(true));
1535 : // TODO -- do proper string to double implementation
1536 0 : return atof(str.toUtf8().data());
1537 : }
1538 :
1539 0 : default:
1540 0 : break;
1541 :
1542 : }
1543 : }
1544 0 : return atomic_value_t::getDoubleValue(cast);
1545 : }
1546 :
1547 : /** \brief Retrieve the string value.
1548 : *
1549 : * This function retrieves the string value from the variant. If
1550 : * the \p cast parameter is true, then the sets are also checked.
1551 : *
1552 : * An atomic set is transformed in a list of atomic values written
1553 : * between parenthesis and separated by commas.
1554 : *
1555 : * In case of a node-set, only the first node (in document order) is
1556 : * transformed to a string with the DOM toString() function (maybe with
1557 : * -1 as the indent parameter to avoid having spaces added by the DOM
1558 : * output functions.) The content returned depends on the type of node:
1559 : *
1560 : * \li Document Node -- all the child text nodes.
1561 : * \li Element -- all the child text nodes.
1562 : * \li Namespace Node -- the URI property.
1563 : * \li Attribute -- the attribute value.
1564 : * \li Comment -- the comment value.
1565 : * \li Processing Instruction -- the text between the \<? and ?\>
1566 : * \li Text Node -- the text of the node
1567 : *
1568 : * \todo
1569 : * See how it should be handled in XPath 2.x because it is different
1570 : * than in XPath 1.x.
1571 : *
1572 : * Some information about casting a node-set to a string can be found in:
1573 : *
1574 : * http://saxon.sourceforge.net/saxon6.5.3/expressions.html
1575 : *
1576 : * \param[in] cast Whether to cast the value if not a Double.
1577 : *
1578 : * \return The Double value, or the converted value if not Double.
1579 : */
1580 0 : QString getStringValue(bool cast = false) const
1581 : {
1582 0 : if(cast)
1583 : {
1584 0 : switch(f_type)
1585 : {
1586 0 : case type_t::ATOMIC_TYPE_SET:
1587 : // TODO -- do proper implementation
1588 0 : throw QDomXPathException_NotImplemented("cast(atomic set) as string is not implemented");
1589 :
1590 0 : case type_t::ATOMIC_TYPE_NODE_SET:
1591 0 : if(f_node_set.isEmpty())
1592 : {
1593 : // no nodes, return an empty string
1594 0 : return "";
1595 : }
1596 0 : return node_to_string(f_node_set[0]);
1597 :
1598 0 : default:
1599 0 : break;
1600 :
1601 : }
1602 : }
1603 0 : return atomic_value_t::getStringValue(cast);
1604 : }
1605 :
1606 0 : static QString node_to_string(const QDomNode& node)
1607 : {
1608 0 : switch(node.nodeType())
1609 : {
1610 0 : case QDomNode::ElementNode:
1611 : // return all the text nodes from all the children
1612 0 : return node.toElement().text();
1613 :
1614 0 : case QDomNode::AttributeNode:
1615 : // return the corresponding value
1616 0 : return node.toAttr().value();
1617 :
1618 0 : case QDomNode::TextNode:
1619 0 : return node.toText().data();
1620 :
1621 0 : case QDomNode::CDATASectionNode:
1622 0 : return node.toCDATASection().data();
1623 :
1624 0 : case QDomNode::ProcessingInstructionNode:
1625 0 : return node.toProcessingInstruction().data();
1626 :
1627 0 : case QDomNode::CommentNode:
1628 0 : return node.toComment().data();
1629 :
1630 0 : case QDomNode::DocumentNode:
1631 : {
1632 0 : QDomDocument document(node.toDocument());
1633 0 : QDomElement element(document.documentElement());
1634 0 : if(element.isNull())
1635 : {
1636 0 : return "";
1637 : }
1638 0 : return element.text();
1639 : }
1640 :
1641 0 : case QDomNode::CharacterDataNode:
1642 0 : return node.toCharacterData().data();
1643 :
1644 : //case QDomNode::BaseNode:
1645 : //case QDomNode::DocumentTypeNode:
1646 : //case QDomNode::DocumentFragmentNode:
1647 : //case QDomNode::EntityReferenceNode:
1648 : //case QDomNode::EntityNode:
1649 : //case QDomNode::NotationNode:
1650 0 : default:
1651 0 : throw QDomXPathException_NotImplemented(QString("cast(node) as string for this node type (%1) is not implemented")
1652 0 : .arg(static_cast<int>(node.nodeType())));
1653 :
1654 : }
1655 : /*NOTREACHED*/
1656 : }
1657 :
1658 : /** \brief Retrieve the set value.
1659 : *
1660 : * This function retrieves the set value from the variant. If
1661 : * the \p cast parameter is true, then any other value is returned
1662 : * as a set composed of that value (in case of NULL, an empty set.)
1663 : *
1664 : * \param[in] cast Whether to cast the value if not a Set.
1665 : *
1666 : * \return A set of atomic values.
1667 : */
1668 0 : atomic_vector_t getSetValue(bool cast = false) const
1669 : {
1670 0 : if(f_type != type_t::ATOMIC_TYPE_SET)
1671 : {
1672 0 : if(!cast)
1673 : {
1674 0 : throw QDomXPathException_WrongType(QString("atomic type is %1, when a Set was requested").arg(static_cast<int>(static_cast<type_t>(f_type))));
1675 : }
1676 : }
1677 0 : atomic_vector_t result;
1678 0 : switch(f_type)
1679 : {
1680 0 : case type_t::ATOMIC_TYPE_NULL:
1681 0 : return result; // empty set
1682 :
1683 0 : case type_t::ATOMIC_TYPE_BOOLEAN:
1684 : case type_t::ATOMIC_TYPE_INTEGER:
1685 : case type_t::ATOMIC_TYPE_SINGLE:
1686 : case type_t::ATOMIC_TYPE_DOUBLE:
1687 : case type_t::ATOMIC_TYPE_STRING:
1688 0 : result.push_back(*this);
1689 0 : return result;
1690 :
1691 0 : case type_t::ATOMIC_TYPE_SET:
1692 0 : return f_set;
1693 :
1694 0 : case type_t::ATOMIC_TYPE_NODE_SET: // TODO -- is that an error or should we return something like the string() of each node
1695 : default:
1696 0 : throw QDomXPathException_NotImplemented(QString("type %1 to set conversion is not implemented").arg(static_cast<int>(static_cast<type_t>(f_type))));
1697 :
1698 : }
1699 : }
1700 :
1701 : /** \brief Set the variant to a set of atomic values.
1702 : *
1703 : * In XPath version 2.0 sets of atomic values were added to the XPath
1704 : * specification. For example, a set of all multiple of 5 between 0
1705 : * and 100 inclusive are defined as:
1706 : *
1707 : * \code
1708 : * (0 to 100)[. mod 5 = 0]
1709 : * \endcode
1710 : *
1711 : * \param[in] set The set to become the variant atomic set.
1712 : */
1713 0 : void setValue(atomic_vector_t const& set)
1714 : {
1715 0 : f_type = type_t::ATOMIC_TYPE_SET;
1716 0 : f_set = set;
1717 0 : }
1718 :
1719 :
1720 : /** \brief Retrieve the node set value.
1721 : *
1722 : * This function retrieves the node set value from the variant.
1723 : *
1724 : * If the variant is not a node set then an error is generated
1725 : * (there is no way really to convert an atomic type to a set
1726 : * of nodes.)
1727 : *
1728 : * \param[in] cast Ignored.
1729 : *
1730 : * \return The node set.
1731 : */
1732 : #pragma GCC diagnostic push
1733 : #pragma GCC diagnostic ignored "-Wunused-parameter"
1734 0 : QDomXPath::node_vector_t& getNodeSetValue(bool cast = false) const
1735 : {
1736 0 : if(f_type != type_t::ATOMIC_TYPE_NODE_SET)
1737 : {
1738 0 : throw QDomXPathException_WrongType(QString("atomic type is %1, when a Node Set was requested").arg(static_cast<int>(static_cast<type_t>(f_type))));
1739 : }
1740 0 : return const_cast<QDomXPath::node_vector_t&>(f_node_set);
1741 : }
1742 : #pragma GCC diagnostic pop
1743 :
1744 :
1745 : /** \brief Set the variant to a node set.
1746 : *
1747 : * This function sets the variant to the specified \p node_set.
1748 : *
1749 : * The input value is copied, although remember that in a DOM all
1750 : * the object we have access to are references and changing the
1751 : * DOM will affect the nodes wherever they are.
1752 : *
1753 : * \param[in] node_set The node set to save in this variant.
1754 : */
1755 0 : void setValue(const QDomXPath::node_vector_t& node_set)
1756 : {
1757 0 : f_type = type_t::ATOMIC_TYPE_NODE_SET;
1758 0 : f_node_set = node_set;
1759 0 : }
1760 :
1761 :
1762 : /** \brief Retrieve the context value.
1763 : *
1764 : * This function retrieves the context value from the variant.
1765 : *
1766 : * If the variant is not a context then an error is generated.
1767 : * Contexts should only be used in very specific places which do
1768 : * not call for casting. If an error occurs then there is an
1769 : * internal error.
1770 : *
1771 : * \param[in] cast Ignored.
1772 : *
1773 : * \return The context.
1774 : */
1775 : //context_t& getContextValue(bool cast = false) const
1776 : //{
1777 : // if(f_type != type_t::ATOMIC_TYPE_CONTEXT)
1778 : // {
1779 : // throw QDomXPathException_WrongType(QString("atomic type is %1, when a Context was requested").arg(static_cast<int>(f_type)));
1780 : // }
1781 : // return const_cast<context_t&>(f_context);
1782 : //}
1783 :
1784 :
1785 : /** \brief Set the variant to a context.
1786 : *
1787 : * This function sets the variant to the specified \p context.
1788 : *
1789 : * The input value is copied, although remember that in a DOM all
1790 : * the object we have access to are references and changing the
1791 : * DOM will affect the nodes wherever they are.
1792 : *
1793 : * \param[in] context The context to save in this variant.
1794 : */
1795 : //void setValue(const context_t& context)
1796 : //{
1797 : // f_type = type_t::ATOMIC_TYPE_CONTEXT;
1798 : // f_context = context;
1799 : //}
1800 :
1801 : private:
1802 : atomic_vector_t f_set = atomic_vector_t(); // set of atomic values
1803 : QDomXPath::node_vector_t f_node_set = QDomXPath::node_vector_t(); // set of nodes -- use the node vector in the context to save space
1804 : //context_t f_context; // the current context
1805 : };
1806 :
1807 :
1808 : /** \brief An array of variants.
1809 : *
1810 : * The array of variants is especially useful as a function stack.
1811 : */
1812 : typedef QVector<variant_t> variant_vector_t;
1813 :
1814 :
1815 : /** \brief Current function being run.
1816 : *
1817 : * While running we may call functions. For that purpose we need to have
1818 : * a function class because the current state of the current function
1819 : * cannot be touched while a child function is running. Similarly, the
1820 : * stack of the child should not affect the stack of the caller. This is
1821 : * the easiest way to protect those stacks and have an easy way to
1822 : * apply the INST_RETURN instruction.
1823 : */
1824 0 : struct function_t
1825 : {
1826 : typedef QMap<QString, variant_t> variables_t;
1827 :
1828 : uint32_t f_pc = 0;
1829 : variant_vector_t f_stack = variant_vector_t();
1830 : context_vector_t f_contexts = context_vector_t();
1831 : variables_t f_variables = variables_t();
1832 : };
1833 :
1834 :
1835 : /** \brief An array of function status.
1836 : *
1837 : * This array holds a set of function_t structures, each representing a
1838 : * function environment. When a function calls another, a new function_t
1839 : * is created at the back of the stack of function environment.
1840 : *
1841 : * The program as a whole has one stack of functions.
1842 : *
1843 : * The first function (i.e. front) represents the main program and it
1844 : * is not removed with a return, instead the program ends on a return.
1845 : */
1846 : typedef QVector<function_t> function_vector_t;
1847 :
1848 :
1849 : /** \brief List of internal functions.
1850 : *
1851 : * This enumeration defines a list of internal functions which are called
1852 : * using the INST_CALL basic instruction.
1853 : *
1854 : * Note that some internal functions are transformed at compile time to
1855 : * work just like a simple push or some other basic instruction.
1856 : */
1857 : enum class internal_func_t
1858 : {
1859 : FUNC_UNKNOWN,
1860 : FUNC_AVG,
1861 : FUNC_MIN,
1862 : FUNC_MAX,
1863 : FUNC_SUM
1864 : };
1865 :
1866 :
1867 : /** \brief All the instructions are parameter less functions.
1868 : *
1869 : * Instructions have full access to the QDomXPathImpl class so it is
1870 : * parameter less and they return void.
1871 : *
1872 : * If an internal error is detected, then the corresponding internal
1873 : * error exception is thrown.
1874 : *
1875 : * Otherwise instructions do not really generate errors at this point.
1876 : * In version 2.0 of XPath we are expected to generate errors if a
1877 : * parameter type is incorrect (i.e. you pass a string to a function
1878 : * that expects an integer). That will be added later.
1879 : */
1880 : typedef void (QDomXPathImpl::*instruction_function_t)();
1881 :
1882 : /** \brief The array of instructions.
1883 : *
1884 : * This array definition is used to declare all the instructions.
1885 : * Because of the way the compiler functions, the array itself is
1886 : * initialized outside of the class (search on g_instructions to
1887 : * find it.)
1888 : *
1889 : * Since instructions are defined on one byte, all the entries (256)
1890 : * are defined. Instructions that are undefined call the default
1891 : * inst_undefined_instruction() function which throws the
1892 : * QDomXPathException_UndefinedInstructionError exception.
1893 : */
1894 : static instruction_function_t const g_instructions[256];
1895 :
1896 : /** \brief Function to disassemble instructions.
1897 : *
1898 : * Each one of these functions print the given instruction that looks like
1899 : * assembly language code. The function returns the size of the instruction.
1900 : * It is passed the PC just after the instruction.
1901 : */
1902 : typedef uint32_t (QDomXPathImpl::*disassembly_function_t)(uint32_t pc);
1903 :
1904 : /** \brief The array of disassembling functions.
1905 : *
1906 : * This array definition is used to print out the different instructions
1907 : * either as they are executed or as they are compiled.
1908 : */
1909 : static disassembly_function_t const g_disassemble_instructions[256];
1910 :
1911 :
1912 : static const QDomXPath::instruction_t INST_END = 0x00;
1913 : static const QDomXPath::instruction_t INST_CALL = 0x01;
1914 : static const QDomXPath::instruction_t INST_SMALL_FUNCTION = 0x02;
1915 : static const QDomXPath::instruction_t INST_LARGE_FUNCTION = 0x03;
1916 : static const QDomXPath::instruction_t INST_JUMP = 0x04;
1917 : static const QDomXPath::instruction_t INST_JUMP_IF_TRUE = 0x05;
1918 : static const QDomXPath::instruction_t INST_JUMP_IF_FALSE = 0x06;
1919 : static const QDomXPath::instruction_t INST_JUMP_IF_ZERO = 0x07;
1920 : static const QDomXPath::instruction_t INST_RETURN = 0x08;
1921 :
1922 : static const QDomXPath::instruction_t INST_GET_VARIABLE = 0x10;
1923 : static const QDomXPath::instruction_t INST_SET_VARIABLE = 0x11;
1924 :
1925 : static const QDomXPath::instruction_t INST_POP1 = 0x20;
1926 : static const QDomXPath::instruction_t INST_POP2 = 0x21;
1927 : static const QDomXPath::instruction_t INST_POP3 = 0x22;
1928 : static const QDomXPath::instruction_t INST_POP4 = 0x23;
1929 : static const QDomXPath::instruction_t INST_POP5 = 0x24;
1930 :
1931 : static const QDomXPath::instruction_t INST_DUPLICATE1 = 0x2A;
1932 : static const QDomXPath::instruction_t INST_DUPLICATE2 = 0x2B;
1933 : static const QDomXPath::instruction_t INST_DUPLICATE3 = 0x2C;
1934 : static const QDomXPath::instruction_t INST_DUPLICATE4 = 0x2D;
1935 : static const QDomXPath::instruction_t INST_DUPLICATE5 = 0x2E;
1936 :
1937 : static const QDomXPath::instruction_t INST_SWAP1 = 0x30;
1938 : static const QDomXPath::instruction_t INST_SWAP2 = 0x31;
1939 : static const QDomXPath::instruction_t INST_SWAP3 = 0x32;
1940 : static const QDomXPath::instruction_t INST_SWAP4 = 0x33;
1941 : static const QDomXPath::instruction_t INST_SWAP5 = 0x34;
1942 : static const QDomXPath::instruction_t INST_SWAP2_3 = 0x35;
1943 :
1944 : static const QDomXPath::instruction_t INST_PUSH_ANY_STRING = 0x40;
1945 : static const QDomXPath::instruction_t INST_PUSH_BYTE = 0x41;
1946 : static const QDomXPath::instruction_t INST_PUSH_DOUBLE = 0x42;
1947 : static const QDomXPath::instruction_t INST_PUSH_DOUBLE_ZERO = 0x43;
1948 : static const QDomXPath::instruction_t INST_PUSH_EMPTY_NODE_SET = 0x44;
1949 : static const QDomXPath::instruction_t INST_PUSH_EMPTY_SET = 0x45;
1950 : static const QDomXPath::instruction_t INST_PUSH_EMPTY_STRING = 0x46;
1951 : static const QDomXPath::instruction_t INST_PUSH_END_OF_ARGUMENTS = 0x47;
1952 : static const QDomXPath::instruction_t INST_PUSH_FALSE = 0x48;
1953 : static const QDomXPath::instruction_t INST_PUSH_LARGE_STRING = 0x49;
1954 : static const QDomXPath::instruction_t INST_PUSH_LONG = 0x4A;
1955 : static const QDomXPath::instruction_t INST_PUSH_LONGLONG = 0x4B;
1956 : static const QDomXPath::instruction_t INST_PUSH_MEDIUM_STRING = 0x4C;
1957 : static const QDomXPath::instruction_t INST_PUSH_NEGATIVE_BYTE = 0x4D;
1958 : static const QDomXPath::instruction_t INST_PUSH_NEGATIVE_SHORT = 0x4E;
1959 : static const QDomXPath::instruction_t INST_PUSH_NEGATIVE_LONG = 0x4F;
1960 : static const QDomXPath::instruction_t INST_PUSH_SHORT = 0x50;
1961 : static const QDomXPath::instruction_t INST_PUSH_SMALL_STRING = 0x51;
1962 : static const QDomXPath::instruction_t INST_PUSH_TRUE = 0x52;
1963 : static const QDomXPath::instruction_t INST_PUSH_ZERO = 0x53;
1964 :
1965 : static const QDomXPath::instruction_t INST_ADD = 0x60;
1966 : static const QDomXPath::instruction_t INST_AND = 0x61;
1967 : static const QDomXPath::instruction_t INST_CEILING = 0x62;
1968 : static const QDomXPath::instruction_t INST_DECREMENT = 0x63;
1969 : static const QDomXPath::instruction_t INST_DIVIDE = 0x64;
1970 : static const QDomXPath::instruction_t INST_EQUAL = 0x65;
1971 : static const QDomXPath::instruction_t INST_FLOOR = 0x66;
1972 : static const QDomXPath::instruction_t INST_GREATER_OR_EQUAL = 0x67;
1973 : static const QDomXPath::instruction_t INST_GREATER_THAN = 0x68;
1974 : static const QDomXPath::instruction_t INST_IDIVIDE = 0x69;
1975 : static const QDomXPath::instruction_t INST_INCREMENT = 0x6A;
1976 : static const QDomXPath::instruction_t INST_LESS_OR_EQUAL = 0x6B;
1977 : static const QDomXPath::instruction_t INST_LESS_THAN = 0x6C;
1978 : static const QDomXPath::instruction_t INST_MODULO = 0x6D;
1979 : static const QDomXPath::instruction_t INST_MULTIPLY = 0x6E;
1980 : static const QDomXPath::instruction_t INST_NEGATE = 0x6F;
1981 : static const QDomXPath::instruction_t INST_NOT = 0x70;
1982 : static const QDomXPath::instruction_t INST_NOT_EQUAL = 0x71;
1983 : static const QDomXPath::instruction_t INST_OR = 0x72;
1984 : static const QDomXPath::instruction_t INST_ROUND = 0x73;
1985 : static const QDomXPath::instruction_t INST_STRING_LENGTH = 0x74;
1986 : static const QDomXPath::instruction_t INST_SUBTRACT = 0x75;
1987 :
1988 : static const QDomXPath::instruction_t INST_AXIS = 0x80;
1989 : static const QDomXPath::instruction_t INST_ROOT = 0x81;
1990 : static const QDomXPath::instruction_t INST_GET_NODE_SET = 0x82;
1991 : static const QDomXPath::instruction_t INST_SET_NODE_SET = 0x83;
1992 : static const QDomXPath::instruction_t INST_GET_RESULT = 0x84;
1993 : static const QDomXPath::instruction_t INST_SET_RESULT = 0x85;
1994 : static const QDomXPath::instruction_t INST_GET_POSITION = 0x86;
1995 : static const QDomXPath::instruction_t INST_SET_POSITION = 0x87;
1996 : static const QDomXPath::instruction_t INST_NODE_SET_SIZE = 0x88;
1997 : static const QDomXPath::instruction_t INST_MERGE_SETS = 0x89;
1998 : static const QDomXPath::instruction_t INST_PREDICATE = 0x8A;
1999 : static const QDomXPath::instruction_t INST_CREATE_NODE_CONTEXT = 0x8B;
2000 : static const QDomXPath::instruction_t INST_GET_CONTEXT_NODE = 0x8C;
2001 : static const QDomXPath::instruction_t INST_NEXT_CONTEXT_NODE = 0x8D;
2002 : static const QDomXPath::instruction_t INST_POP_CONTEXT = 0x8E;
2003 :
2004 :
2005 : enum class axis_t
2006 : {
2007 : AXIS_ANCESTOR,
2008 : AXIS_ANCESTOR_OR_SELF,
2009 : AXIS_ATTRIBUTE,
2010 : AXIS_CHILD,
2011 : AXIS_DESCENDANT,
2012 : AXIS_DESCENDANT_OR_SELF,
2013 : AXIS_FOLLOWING,
2014 : AXIS_FOLLOWING_SIBLING,
2015 : AXIS_NAMESPACE,
2016 : AXIS_PARENT,
2017 : AXIS_PRECEDING,
2018 : AXIS_PRECEDING_SIBLING,
2019 : AXIS_SELF
2020 : };
2021 :
2022 : enum class node_type_t
2023 : {
2024 : // XPath 1.0
2025 : NODE_TYPE_COMMENT,
2026 : NODE_TYPE_NODE,
2027 : NODE_TYPE_PROCESSING_INSTRUCTION,
2028 : NODE_TYPE_TEXT,
2029 :
2030 : // XPath 2.0 (not supported yet)
2031 : NODE_TYPE_DOCUMENT_NODE,
2032 : NODE_TYPE_ELEMENT,
2033 : NODE_TYPE_SCHEMA_ELEMENT,
2034 : NODE_TYPE_ATTRIBUTE,
2035 : NODE_TYPE_SCHEMA_ATTRIBUTE
2036 : };
2037 :
2038 :
2039 : /** \brief Initialize the class.
2040 : *
2041 : * This function initializes the class. Once the constructor returns
2042 : * the object parse() function can be called in order to get the
2043 : * XPath transformed to tokens and ready to be applied against nodes.
2044 : *
2045 : * The function also initializes the program header as defined in the
2046 : * QDomXPath::getProgram().
2047 : *
2048 : * \param[in] owner The QDomXPath, the owner or parent of this QDomXPathImpl
2049 : * \param[in] xpath The XPath to be compiled; may be "" when used only to execute a program
2050 : */
2051 0 : QDomXPathImpl(QDomXPath *owner, const QString& xpath)
2052 0 : : f_owner(owner)
2053 : , f_xpath(xpath)
2054 0 : , f_start(f_xpath.data())
2055 0 : , f_in(f_start)
2056 : {
2057 0 : f_program.push_back(QDomXPath::MAGIC[0]);
2058 0 : f_program.push_back(QDomXPath::MAGIC[1]);
2059 0 : f_program.push_back(QDomXPath::MAGIC[2]);
2060 0 : f_program.push_back(QDomXPath::MAGIC[3]);
2061 0 : f_program.push_back(VERSION_MAJOR);
2062 0 : f_program.push_back(VERSION_MINOR);
2063 0 : std::string str(xpath.toUtf8().data());
2064 0 : size_t size(str.length());
2065 0 : if(size > 65535)
2066 : {
2067 0 : size = 65535;
2068 : }
2069 0 : f_program.push_back(static_cast<instruction_t>(size >> 8));
2070 0 : f_program.push_back(static_cast<instruction_t>(size));
2071 0 : for(size_t i(0); i < size; ++i)
2072 : {
2073 0 : f_program.push_back(str[i]);
2074 : }
2075 0 : f_program_start_offset = f_program.size();
2076 0 : }
2077 :
2078 :
2079 : QDomXPathImpl(QDomXPathImpl const & rhs) = delete;
2080 : QDomXPathImpl & operator = (QDomXPathImpl const & rhs) = delete;
2081 :
2082 :
2083 : /** \brief While executing, read a byte.
2084 : *
2085 : * There are two sets of instructions that get data from the program area:
2086 : *
2087 : * \li The push instructions that converts the data to a variant value
2088 : * and push that on the stack.
2089 : *
2090 : * \li The program execution environment which reads one byte instruction
2091 : * and executes the corresponding function.
2092 : *
2093 : * \return The following 1 byte.
2094 : */
2095 0 : int get_next_program_byte()
2096 : {
2097 0 : if(f_functions.back().f_pc >= static_cast<uint32_t>(f_program.size()))
2098 : {
2099 0 : throw QDomXPathException_InternalError("trying to read more bytes from f_program than available");
2100 : }
2101 0 : int result(f_program[f_functions.back().f_pc]);
2102 0 : ++f_functions.back().f_pc;
2103 0 : return result;
2104 : }
2105 :
2106 :
2107 : /** \brief Verify that the stack is not empty.
2108 : *
2109 : * This function checks the stack, if empty, it throws an internal error
2110 : * as this should never happend.
2111 : *
2112 : * By default the type of the variant at the top of the stack is not checked.
2113 : * By setting the \p type parameter to something else than
2114 : * type_t::ATOMIC_TYPE_UNDEFINED, the function enforces that type for the object
2115 : * at the top of the stack.
2116 : *
2117 : * \param[in] type The type of the variant on the top of the stack.
2118 : * Ignore if set to type_t::ATOMIC_TYPE_UNDEFINED.
2119 : */
2120 0 : void stack_not_empty(atomic_value_t::type_t type = atomic_value_t::type_t::ATOMIC_TYPE_UNDEFINED)
2121 : {
2122 0 : if(f_functions.back().f_stack.empty())
2123 : {
2124 0 : throw QDomXPathException_InternalError("cannot pop anything from an empty stack");
2125 : }
2126 0 : if(type != atomic_value_t::type_t::ATOMIC_TYPE_UNDEFINED
2127 0 : && f_functions.back().f_stack.back().getType() != type)
2128 : {
2129 0 : throw QDomXPathException_WrongType(QString("the current type at the top of the stack is not of the right type (expected %1, it is %2)")
2130 0 : .arg(static_cast<int>(type))
2131 0 : .arg(static_cast<int>(f_functions.back().f_stack.back().getType())));
2132 : }
2133 0 : }
2134 :
2135 :
2136 : /** \brief Check that the stack of contexts is not empty.
2137 : *
2138 : * The stack of contexts is created by the INST_CREATE_NODE_CONTEXT
2139 : * instruction. It should never be used if empty so we have this
2140 : * function to check the validity in one place.
2141 : */
2142 0 : void contexts_not_empty()
2143 : {
2144 0 : if(f_functions.back().f_contexts.empty())
2145 : {
2146 0 : throw QDomXPathException_InternalError("cannot pop anything from an empty stack of contexts");
2147 : }
2148 0 : }
2149 :
2150 :
2151 : /** \brief Pop one entry from the stack.
2152 : *
2153 : * This function checks the stack, if empty, it throws an internal error
2154 : * as this should never happend.
2155 : *
2156 : * \return A variant with the data from the top of the stack.
2157 : */
2158 0 : variant_t pop_variant_data()
2159 : {
2160 0 : stack_not_empty();
2161 :
2162 : #if 0
2163 : printf("Stack Trace:\n");
2164 : for(int i(0); i < f_functions.back().f_stack.size(); ++i)
2165 : {
2166 : variant_t v(f_functions.back().f_stack[i]);
2167 : switch(v.getType())
2168 : {
2169 : case atomic_value_t::type_t::ATOMIC_TYPE_UNDEFINED:
2170 : printf(" + Undefined\n");
2171 : break;
2172 :
2173 : case atomic_value_t::type_t::ATOMIC_TYPE_NULL:
2174 : printf(" + NULL\n");
2175 : break;
2176 :
2177 : case atomic_value_t::type_t::ATOMIC_TYPE_END_OF_ARGUMENTS:
2178 : printf(" + End of Arguments\n");
2179 : break;
2180 :
2181 : case atomic_value_t::type_t::ATOMIC_TYPE_BOOLEAN:
2182 : printf(" + Boolean (%s)\n", (v.getBooleanValue() ? "true" : "false"));
2183 : break;
2184 :
2185 : case atomic_value_t::type_t::ATOMIC_TYPE_INTEGER:
2186 : printf(" + Integer (%ld)\n", v.getIntegerValue());
2187 : break;
2188 :
2189 : //case atomic_value_t::type_t::ATOMIC_TYPE_DECIMAL:
2190 : case atomic_value_t::type_t::ATOMIC_TYPE_SINGLE:
2191 : printf(" + Single (%g)\n", v.getSingleValue());
2192 : break;
2193 :
2194 : case atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE:
2195 : printf(" + Double (%g)\n", v.getDoubleValue());
2196 : break;
2197 :
2198 : case atomic_value_t::type_t::ATOMIC_TYPE_STRING:
2199 : printf(" + String (\"%s\")\n", v.getStringValue().toUtf8().data());
2200 : break;
2201 :
2202 : case atomic_value_t::type_t::ATOMIC_TYPE_SET:
2203 : printf(" + Atomic Set\n");
2204 : break;
2205 :
2206 : case atomic_value_t::type_t::ATOMIC_TYPE_NODE_SET:
2207 : printf(" + Node Set\n");
2208 : break;
2209 :
2210 : }
2211 : }
2212 : #endif
2213 :
2214 0 : variant_t v(f_functions.back().f_stack.back());
2215 0 : f_functions.back().f_stack.pop_back();
2216 :
2217 0 : return v;
2218 : }
2219 :
2220 :
2221 : /** \brief For all undefined instructions.
2222 : *
2223 : * This function is used by all the undefined instructions so we can always
2224 : * run something (i.e. instead of using NULL) and avoid testing the pointers.
2225 : *
2226 : * This function always raises the QDomXPathException_UndefinedInstructionError
2227 : * exception.
2228 : */
2229 0 : void inst_undefined_instruction()
2230 : {
2231 0 : QDomXPath::instruction_t inst(f_program[f_functions.back().f_pc - 1]);
2232 0 : throw QDomXPathException_UndefinedInstructionError(QString("instruction %1 is not defined (pc = %2)")
2233 0 : .arg(static_cast<int>(inst)).arg(f_functions.back().f_pc - 1));
2234 : }
2235 :
2236 :
2237 : /** \brief The End instruction.
2238 : *
2239 : * This function raises an exception because it should never be executed.
2240 : * Instead the function running the execution loop is expected to catch this
2241 : * special case and return. However, just in case it were to be executed,
2242 : * we have a function that throws.
2243 : */
2244 0 : void inst_end()
2245 : {
2246 : #if QDOM_XPATH_VERIFICATION
2247 : // verify instruction location
2248 0 : if(f_program[f_functions.back().f_pc - 1] != INST_END)
2249 : {
2250 0 : throw QDomXPathException_InternalError("INST_END not at the right location in the table of instructions");
2251 : }
2252 : #endif
2253 0 : throw QDomXPathException_InternalError("the End instruction is not expected to be executed");
2254 : }
2255 :
2256 :
2257 : /** \brief The Call instruction.
2258 : *
2259 : * This function creates a new function_t with its PC set to the function
2260 : * being called as found on the stack (the last PUSH is the PC of the
2261 : * function being called.)
2262 : *
2263 : * The arguments are also popped from the input stack, up until the
2264 : * End of Argument special value is found. Then this function returns
2265 : * which means we now are running in this sub-function.
2266 : */
2267 0 : void inst_call()
2268 : {
2269 : #if QDOM_XPATH_VERIFICATION
2270 : // verify instruction location
2271 0 : if(f_program[f_functions.back().f_pc - 1] != INST_CALL)
2272 : {
2273 0 : throw QDomXPathException_InternalError("INST_CALL not at the right location in the table of instructions");
2274 : }
2275 : #endif
2276 : // get the internal function number (FUNC_...)
2277 0 : variant_t function_number(pop_variant_data());
2278 0 : if(function_number.atomic_value_t::getType() != atomic_value_t::type_t::ATOMIC_TYPE_INTEGER)
2279 : {
2280 0 : throw QDomXPathException_InternalError("INST_CALL expects the first element on the stack to be of type INTEGER");
2281 : }
2282 :
2283 : // save arguments in variables named "a1", "a2", "a3"...
2284 : // these can be accessed with "$a1", "$a2", "$a3"...
2285 : // --the number of arguments is saved in $argc (argument count)--
2286 0 : variant_vector_t arguments;
2287 0 : for(int a(1);; ++a)
2288 : {
2289 0 : variant_t arg(pop_variant_data());
2290 0 : if(arg.atomic_value_t::getType() == atomic_value_t::type_t::ATOMIC_TYPE_END_OF_ARGUMENTS)
2291 : {
2292 : // found the end of the argument list
2293 0 : break;
2294 : }
2295 0 : arguments.push_back(arg);
2296 0 : }
2297 :
2298 : // now select the function to run
2299 0 : switch(static_cast<internal_func_t>(function_number.getIntegerValue()))
2300 : {
2301 0 : case internal_func_t::FUNC_AVG:
2302 0 : func_avg(arguments);
2303 0 : break;
2304 :
2305 0 : case internal_func_t::FUNC_MAX:
2306 0 : func_max(arguments);
2307 0 : break;
2308 :
2309 0 : case internal_func_t::FUNC_MIN:
2310 0 : func_min(arguments);
2311 0 : break;
2312 :
2313 0 : case internal_func_t::FUNC_SUM:
2314 0 : func_sum(arguments);
2315 0 : break;
2316 :
2317 0 : default:
2318 0 : throw QDomXPathException_NotImplemented(QString("function %1 is not yet implemented").arg(function_number.getIntegerValue()));
2319 :
2320 : }
2321 0 : }
2322 :
2323 :
2324 0 : void func_default_to_context_node(variant_vector_t& arguments)
2325 : {
2326 0 : if(arguments.size() == 0)
2327 : {
2328 : // retrieve the context node if no parameters were specified
2329 0 : context_vector_t::reference context(f_functions.back().f_contexts.back());
2330 0 : if(context.f_position == -1)
2331 : {
2332 0 : throw QDomXPathException_EmptyContext("the sum() function cannot be used without a context node and no parameters");
2333 : }
2334 0 : QDomXPath::node_vector_t context_node;
2335 0 : context_node.push_back(context.f_nodes[context.f_position]);
2336 0 : variant_t value;
2337 0 : value.setValue(context_node);
2338 0 : arguments.push_back(value);
2339 : }
2340 0 : }
2341 :
2342 0 : void func_calculate_sum_or_average(variant_vector_t& arguments, bool sum_only)
2343 : {
2344 0 : func_default_to_context_node(arguments);
2345 : // TODO: support sums of only decimals and singles
2346 0 : bool integer(true);
2347 0 : int64_t isum(0);
2348 0 : int count(0);
2349 0 : double dsum(0.0);
2350 0 : const int imax(arguments.size());
2351 0 : for(int i(0); i < imax; ++i)
2352 : {
2353 0 : variant_t arg(arguments[i]);
2354 0 : switch(arg.getType())
2355 : {
2356 0 : case atomic_value_t::type_t::ATOMIC_TYPE_INTEGER:
2357 0 : if(integer)
2358 : {
2359 0 : isum += arg.getIntegerValue();
2360 0 : dsum = static_cast<double>(isum);
2361 0 : ++count;
2362 0 : break;
2363 : }
2364 : #if __cplusplus >= 201700
2365 : [[fallthrough]];
2366 : #endif
2367 : //case atomic_type_t::ATOMIC_TYPE_DECIMAL:
2368 : case atomic_value_t::type_t::ATOMIC_TYPE_SINGLE:
2369 : case atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE:
2370 0 : integer = false;
2371 0 : dsum += arg.getDoubleValue(true);
2372 0 : ++count;
2373 0 : break;
2374 :
2375 0 : case atomic_value_t::type_t::ATOMIC_TYPE_NODE_SET:
2376 : {
2377 0 : integer = false;
2378 0 : QDomXPath::node_vector_t node_set(arg.getNodeSetValue());
2379 0 : const int jmax(node_set.size());
2380 0 : for(int j(0); j < jmax; ++j)
2381 : {
2382 0 : QString str(variant_t::node_to_string(node_set[j]));
2383 : // TODO verify that the string is a valid number
2384 : // TODO if the number is decimal, avoid forcing double
2385 : // (in XPath 2.0 we can use the best possible type as
2386 : // defined here: http://www.w3.org/TR/xpath20/#promotion )
2387 0 : dsum += atof(str.toUtf8().data());
2388 0 : ++count;
2389 0 : }
2390 : }
2391 0 : break;
2392 :
2393 0 : default:
2394 0 : throw QDomXPathException_WrongType("the sum/avg() functions cannot be used with types other than numbers and node-set");
2395 :
2396 : }
2397 : }
2398 :
2399 0 : variant_t return_value;
2400 0 : if(integer && sum_only)
2401 : {
2402 0 : return_value.atomic_value_t::setValue(isum);
2403 : }
2404 : else
2405 : {
2406 0 : if(!sum_only && count > 0)
2407 : {
2408 : // compute the average
2409 0 : dsum /= static_cast<double>(count);
2410 : }
2411 0 : return_value.atomic_value_t::setValue(dsum);
2412 : }
2413 0 : f_functions.back().f_stack.push_back(return_value);
2414 0 : }
2415 :
2416 :
2417 0 : void func_sum(variant_vector_t& arguments)
2418 : {
2419 0 : func_calculate_sum_or_average(arguments, true);
2420 0 : }
2421 :
2422 :
2423 0 : void func_avg(variant_vector_t& arguments)
2424 : {
2425 0 : func_calculate_sum_or_average(arguments, false);
2426 0 : }
2427 :
2428 :
2429 0 : void func_calculate_min_or_max(variant_vector_t& arguments, bool min)
2430 : {
2431 0 : func_default_to_context_node(arguments);
2432 : // TODO: support min/max of only decimals and singles
2433 : // TODO: support min/max on strings (XPath 2.0 supports such + collation)
2434 0 : bool integer(true);
2435 0 : bool first(true);
2436 0 : int64_t iresult(0);
2437 0 : double dresult(0.0);
2438 0 : const int imax(arguments.size());
2439 0 : for(int i(0); i < imax; ++i)
2440 : {
2441 0 : variant_t arg(arguments[i]);
2442 0 : switch(arg.getType())
2443 : {
2444 0 : case atomic_value_t::type_t::ATOMIC_TYPE_INTEGER:
2445 0 : if(integer)
2446 : {
2447 0 : int64_t v(arg.getIntegerValue());
2448 0 : if(v > iresult ^ min || first)
2449 : {
2450 0 : iresult = v;
2451 0 : first = false;
2452 : }
2453 0 : dresult = static_cast<double>(iresult);
2454 0 : break;
2455 0 : }
2456 : #if __cplusplus >= 201700
2457 : [[fallthrough]];
2458 : #endif
2459 : //case atomic_type_t::ATOMIC_TYPE_DECIMAL:
2460 : case atomic_value_t::type_t::ATOMIC_TYPE_SINGLE:
2461 : case atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE:
2462 0 : integer = false;
2463 : {
2464 0 : double v(arg.getDoubleValue(true));
2465 0 : if(v > dresult ^ min || first)
2466 : {
2467 0 : dresult = v;
2468 0 : first = false;
2469 0 : }
2470 : }
2471 0 : break;
2472 :
2473 0 : case atomic_value_t::type_t::ATOMIC_TYPE_NODE_SET:
2474 : {
2475 0 : integer = false;
2476 0 : QDomXPath::node_vector_t node_set(arg.getNodeSetValue());
2477 0 : const int jmax(node_set.size());
2478 0 : for(int j(0); j < jmax; ++j)
2479 : {
2480 0 : QString str(variant_t::node_to_string(node_set[j]));
2481 : // TODO verify that the string is a valid number
2482 : // TODO if the number is decimal, avoid forcing double
2483 : // (in XPath 2.0 we can use the best possible type as
2484 : // defined here: http://www.w3.org/TR/xpath20/#promotion )
2485 0 : double v(atof(str.toUtf8().data()));
2486 0 : if(v > dresult ^ min || first)
2487 : {
2488 0 : dresult = v;
2489 0 : first = false;
2490 : }
2491 0 : }
2492 : }
2493 0 : break;
2494 :
2495 0 : default:
2496 0 : throw QDomXPathException_WrongType("the min/max() functions cannot be used with types other than numbers and node-set");
2497 :
2498 : }
2499 : }
2500 :
2501 0 : variant_t return_value;
2502 0 : if(integer)
2503 : {
2504 0 : return_value.atomic_value_t::setValue(iresult);
2505 : }
2506 : else
2507 : {
2508 0 : return_value.atomic_value_t::setValue(dresult);
2509 : }
2510 0 : f_functions.back().f_stack.push_back(return_value);
2511 0 : }
2512 :
2513 :
2514 0 : void func_max(variant_vector_t& arguments)
2515 : {
2516 0 : func_calculate_min_or_max(arguments, false);
2517 0 : }
2518 :
2519 :
2520 0 : void func_min(variant_vector_t& arguments)
2521 : {
2522 0 : func_calculate_min_or_max(arguments, true);
2523 0 : }
2524 :
2525 :
2526 : /** \brief Return from a function.
2527 : *
2528 : * This instruction allows the instruction stream to return to the caller.
2529 : *
2530 : * The last variant that got pushed on the stack is returned to the caller.
2531 : */
2532 0 : void inst_return()
2533 : {
2534 : #if QDOM_XPATH_VERIFICATION
2535 : // verify instruction location
2536 0 : if(f_program[f_functions.back().f_pc - 1] != INST_RETURN)
2537 : {
2538 0 : throw QDomXPathException_InternalError("INST_RETURN not at the right location in the table of instructions");
2539 : }
2540 0 : if(f_functions.size() <= 1)
2541 : {
2542 0 : throw QDomXPathException_InternalError("INST_RETURN cannot be called with an empty stack of functions");
2543 : }
2544 : #endif
2545 : // copy the return value from the function stack
2546 : // to the caller's stack
2547 0 : variant_t return_value(pop_variant_data());
2548 :
2549 : // now return to the old function
2550 0 : f_functions.pop_back();
2551 :
2552 0 : f_functions.back().f_stack.push_back(return_value);
2553 0 : }
2554 :
2555 :
2556 : /** \brief Get the contents of a variable.
2557 : *
2558 : * This instruction retrieves the contents of a variable. First the function
2559 : * checks the current function. If the current function does not define such
2560 : * a variable, then the user bound variables are checked.
2561 : *
2562 : * If no variable with that name is defined, then an error is raised.
2563 : */
2564 0 : void inst_get_variable()
2565 : {
2566 : #if QDOM_XPATH_VERIFICATION
2567 : // verify instruction location
2568 0 : if(f_program[f_functions.back().f_pc - 1] != INST_GET_VARIABLE)
2569 : {
2570 0 : throw QDomXPathException_InternalError("INST_GET_VARIABLE not at the right location in the table of instructions");
2571 : }
2572 : #endif
2573 : // retrieve variable name
2574 0 : variant_t variable(pop_variant_data());
2575 0 : QString variable_name(variable.getStringValue());
2576 :
2577 0 : if(f_functions.back().f_variables.contains(variable_name))
2578 : {
2579 0 : f_functions.back().f_stack.push_back(f_functions.back().f_variables[variable_name]);
2580 : }
2581 : else
2582 : {
2583 0 : variant_t value;
2584 0 : value.atomic_value_t::setValue(f_owner->getVariable(variable_name));
2585 0 : f_functions.back().f_stack.push_back(value);
2586 : }
2587 0 : }
2588 :
2589 :
2590 :
2591 : /** \brief Set the contents of a variable.
2592 : *
2593 : * This instruction sets the contents of a variable. The variable is set in
2594 : * the current function only. There is currently no function to change a
2595 : * global variable.
2596 : */
2597 0 : void inst_set_variable()
2598 : {
2599 : #if QDOM_XPATH_VERIFICATION
2600 : // verify instruction location
2601 0 : if(f_program[f_functions.back().f_pc - 1] != INST_SET_VARIABLE)
2602 : {
2603 0 : throw QDomXPathException_InternalError("INST_SET_VARIABLE not at the right location in the table of instructions");
2604 : }
2605 : #endif
2606 : // retrieve variable name
2607 0 : variant_t variable(pop_variant_data());
2608 0 : QString variable_name(variable.getStringValue());
2609 :
2610 : // retrieve the value for the variable
2611 0 : variant_t value(pop_variant_data());
2612 :
2613 0 : f_functions.back().f_variables[variable_name] = value;
2614 0 : }
2615 :
2616 :
2617 : /** \brief Found a function.
2618 : *
2619 : * Functions are just inline code that gets skipped when bumped into.
2620 : * Functions are not named, they just have an offset so we do not need
2621 : * to do anything special about them. At a later time, we may have to
2622 : * handle user functions, but I have the impression that those would
2623 : * be quite different that this.
2624 : *
2625 : * The size of the small function is 16 bits.
2626 : */
2627 0 : void inst_small_function()
2628 : {
2629 : #if QDOM_XPATH_VERIFICATION
2630 : // verify instruction location
2631 0 : if(f_program[f_functions.back().f_pc - 1] != INST_SMALL_FUNCTION)
2632 : {
2633 0 : throw QDomXPathException_InternalError("INST_PUSH_END_OF_ARGUMENTS not at the right location in the table of instructions");
2634 : }
2635 : #endif
2636 0 : int size((get_next_program_byte() << 8) | get_next_program_byte());
2637 0 : f_functions.back().f_pc += size;
2638 0 : }
2639 :
2640 :
2641 : /** \brief Found a function.
2642 : *
2643 : * Functions are just inline code that gets skipped when bumped into.
2644 : * Functions are not named, they just have an offset so we do not need
2645 : * to do anything special about them. At a later time, we may have to
2646 : * handle user functions, but I have the impression that those would
2647 : * be quite different that this.
2648 : *
2649 : * The size of the large function is 32 bits.
2650 : */
2651 0 : void inst_large_function()
2652 : {
2653 : #if QDOM_XPATH_VERIFICATION
2654 : // verify instruction location
2655 0 : if(f_program[f_functions.back().f_pc - 1] != INST_LARGE_FUNCTION)
2656 : {
2657 0 : throw QDomXPathException_InternalError("INST_LARGE_FUNCTION not at the right location in the table of instructions");
2658 : }
2659 : #endif
2660 0 : int size((get_next_program_byte() << 24)
2661 0 : | (get_next_program_byte() << 16)
2662 0 : | (get_next_program_byte() << 8)
2663 0 : | get_next_program_byte());
2664 0 : f_functions.back().f_pc += size;
2665 0 : }
2666 :
2667 :
2668 : /** \brief Jump to a new location.
2669 : *
2670 : * Jump to a new location as found on the stack.
2671 : */
2672 0 : void inst_jump()
2673 : {
2674 : #if QDOM_XPATH_VERIFICATION
2675 : // verify instruction location
2676 0 : if(f_program[f_functions.back().f_pc - 1] != INST_JUMP)
2677 : {
2678 0 : throw QDomXPathException_InternalError("INST_JUMP not at the right location in the table of instructions");
2679 : }
2680 : #endif
2681 0 : variant_t pc(pop_variant_data());
2682 0 : f_functions.back().f_pc = static_cast<uint32_t>(pc.getIntegerValue());
2683 0 : }
2684 :
2685 :
2686 : /** \brief Jump to a new location if true.
2687 : *
2688 : * This function pops a new location and then a Boolean value. If the
2689 : * Boolean value is false, the new location is ignored. If the Boolean
2690 : * value is true, then PC becomes that new location.
2691 : */
2692 0 : void inst_jump_if_true()
2693 : {
2694 : #if QDOM_XPATH_VERIFICATION
2695 : // verify instruction location
2696 0 : if(f_program[f_functions.back().f_pc - 1] != INST_JUMP_IF_TRUE)
2697 : {
2698 0 : throw QDomXPathException_InternalError("INST_JUMP_IF_TRUE not at the right location in the table of instructions");
2699 : }
2700 : #endif
2701 0 : variant_t pc(pop_variant_data());
2702 0 : variant_t boolean(pop_variant_data());
2703 0 : if(boolean.getBooleanValue())
2704 : {
2705 0 : f_functions.back().f_pc = static_cast<uint32_t>(pc.getIntegerValue());
2706 : }
2707 0 : }
2708 :
2709 :
2710 : /** \brief Jump to a new location if false.
2711 : *
2712 : * This function pops a new location and then a Boolean value. If the
2713 : * Boolean value is true, the new location is ignored. If the Boolean
2714 : * value is false, then PC becomes that new location.
2715 : */
2716 0 : void inst_jump_if_false()
2717 : {
2718 : #if QDOM_XPATH_VERIFICATION
2719 : // verify instruction location
2720 0 : if(f_program[f_functions.back().f_pc - 1] != INST_JUMP_IF_FALSE)
2721 : {
2722 0 : throw QDomXPathException_InternalError("INST_JUMP_IF_FALSE not at the right location in the table of instructions");
2723 : }
2724 : #endif
2725 0 : variant_t pc(pop_variant_data());
2726 0 : variant_t boolean(pop_variant_data());
2727 0 : if(!boolean.getBooleanValue())
2728 : {
2729 0 : f_functions.back().f_pc = static_cast<uint32_t>(pc.getIntegerValue());
2730 : }
2731 0 : }
2732 :
2733 :
2734 : /** \brief Jump to a new location if zero.
2735 : *
2736 : * This function pops a new location and then a value. If the
2737 : * value is not zero, the new location is ignored. If the
2738 : * value is zero, then PC becomes that new location.
2739 : */
2740 0 : void inst_jump_if_zero()
2741 : {
2742 : #if QDOM_XPATH_VERIFICATION
2743 : // verify instruction location
2744 0 : if(f_program[f_functions.back().f_pc - 1] != INST_JUMP_IF_ZERO)
2745 : {
2746 0 : throw QDomXPathException_InternalError("INST_JUMP_IF_ZERO not at the right location in the table of instructions");
2747 : }
2748 : #endif
2749 0 : variant_t pc(pop_variant_data());
2750 0 : variant_t object(pop_variant_data());
2751 0 : if(object.getIntegerValue(true) == 0)
2752 : {
2753 0 : f_functions.back().f_pc = static_cast<uint32_t>(pc.getIntegerValue());
2754 : }
2755 0 : }
2756 :
2757 :
2758 : /** \brief Pop one object from the stack.
2759 : *
2760 : * This instruction pops one object from the top of the stack.
2761 : *
2762 : * \code
2763 : * before after
2764 : * o1 o2
2765 : * o2 o3
2766 : * o3 ...
2767 : * ...
2768 : * \endcode
2769 : */
2770 0 : void inst_pop1()
2771 : {
2772 : #if QDOM_XPATH_VERIFICATION
2773 : // verify instruction location
2774 0 : if(f_program[f_functions.back().f_pc - 1] != INST_POP1)
2775 : {
2776 0 : throw QDomXPathException_InternalError("INST_POP1 not at the right location in the table of instructions");
2777 : }
2778 : #endif
2779 0 : if(f_functions.back().f_stack.size() < 1)
2780 : {
2781 0 : throw QDomXPathException_EmptyStack("cannot pop anything from an empty stack");
2782 : }
2783 0 : f_functions.back().f_stack.pop_back();
2784 0 : }
2785 :
2786 :
2787 : /** \brief Pop two objects from the stack.
2788 : *
2789 : * This instruction pops the second object counting from the top stack of
2790 : * the stack.
2791 : *
2792 : * \code
2793 : * before after
2794 : * o1 o1
2795 : * o2 o3
2796 : * o3 ...
2797 : * ...
2798 : * \endcode
2799 : */
2800 0 : void inst_pop2()
2801 : {
2802 : #if QDOM_XPATH_VERIFICATION
2803 : // verify instruction location
2804 0 : if(f_program[f_functions.back().f_pc - 1] != INST_POP2)
2805 : {
2806 0 : throw QDomXPathException_InternalError("INST_POP2 not at the right location in the table of instructions");
2807 : }
2808 : #endif
2809 0 : if(f_functions.back().f_stack.size() < 2)
2810 : {
2811 0 : throw QDomXPathException_EmptyStack("cannot pop the second object from the stack if the stack is not at least two objects");
2812 : }
2813 0 : f_functions.back().f_stack.remove(f_functions.back().f_stack.size() - 2);
2814 0 : }
2815 :
2816 :
2817 : /** \brief Pop three objects from the stack.
2818 : *
2819 : * This instruction pops the thrid object counting from the top stack of
2820 : * the stack.
2821 : *
2822 : * \code
2823 : * before after
2824 : * o1 o1
2825 : * o2 o2
2826 : * o3 o4
2827 : * o4 ...
2828 : * ...
2829 : * \endcode
2830 : */
2831 0 : void inst_pop3()
2832 : {
2833 : #if QDOM_XPATH_VERIFICATION
2834 : // verify instruction location
2835 0 : if(f_program[f_functions.back().f_pc - 1] != INST_POP3)
2836 : {
2837 0 : throw QDomXPathException_InternalError("INST_POP3 not at the right location in the table of instructions");
2838 : }
2839 : #endif
2840 0 : if(f_functions.back().f_stack.size() < 3)
2841 : {
2842 0 : throw QDomXPathException_EmptyStack("cannot pop the third object from the stack if the stack is not at least three objects");
2843 : }
2844 0 : f_functions.back().f_stack.remove(f_functions.back().f_stack.size() - 3);
2845 0 : }
2846 :
2847 :
2848 : /** \brief Pop four objects from the stack.
2849 : *
2850 : * This instruction pops the fourth object counting from the top stack of
2851 : * the stack.
2852 : *
2853 : * \code
2854 : * before after
2855 : * o1 o1
2856 : * o2 o2
2857 : * o3 o3
2858 : * o4 o5
2859 : * o5 ...
2860 : * ...
2861 : * \endcode
2862 : */
2863 0 : void inst_pop4()
2864 : {
2865 : #if QDOM_XPATH_VERIFICATION
2866 : // verify instruction location
2867 0 : if(f_program[f_functions.back().f_pc - 1] != INST_POP4)
2868 : {
2869 0 : throw QDomXPathException_InternalError("INST_POP4 not at the right location in the table of instructions");
2870 : }
2871 : #endif
2872 0 : if(f_functions.back().f_stack.size() < 4)
2873 : {
2874 0 : throw QDomXPathException_EmptyStack("cannot pop the forth object from the stack if the stack is not at least four objects");
2875 : }
2876 0 : f_functions.back().f_stack.remove(f_functions.back().f_stack.size() - 4);
2877 0 : }
2878 :
2879 :
2880 : /** \brief Pop five objects from the stack.
2881 : *
2882 : * This instruction pops the fifth object counting from the top stack of
2883 : * the stack.
2884 : *
2885 : * \code
2886 : * before after
2887 : * o1 o1
2888 : * o2 o2
2889 : * o3 o3
2890 : * o4 o4
2891 : * o5 o6
2892 : * o6 ...
2893 : * ...
2894 : * \endcode
2895 : */
2896 0 : void inst_pop5()
2897 : {
2898 : #if QDOM_XPATH_VERIFICATION
2899 : // verify instruction location
2900 0 : if(f_program[f_functions.back().f_pc - 1] != INST_POP5)
2901 : {
2902 0 : throw QDomXPathException_InternalError("INST_POP5 not at the right location in the table of instructions");
2903 : }
2904 : #endif
2905 0 : if(f_functions.back().f_stack.size() < 5)
2906 : {
2907 0 : throw QDomXPathException_EmptyStack("cannot pop the fifth object from the stack if the stack is not at least five objects");
2908 : }
2909 0 : f_functions.back().f_stack.remove(f_functions.back().f_stack.size() - 5);
2910 0 : }
2911 :
2912 :
2913 : /** \brief Duplicate the last object on the stack.
2914 : *
2915 : * This instruction pops the last object on the stack and then push
2916 : * it back twice on the stack.
2917 : */
2918 0 : void inst_duplicate1()
2919 : {
2920 : #if QDOM_XPATH_VERIFICATION
2921 : // verify instruction location
2922 0 : if(f_program[f_functions.back().f_pc - 1] != INST_DUPLICATE1)
2923 : {
2924 0 : throw QDomXPathException_InternalError("INST_DUPLICATE1 not at the right location in the table of instructions");
2925 : }
2926 : #endif
2927 0 : if(f_functions.back().f_stack.size() == 0)
2928 : {
2929 0 : throw QDomXPathException_EmptyStack("duplicate cannot be used with an empty stack");
2930 : }
2931 0 : variant_t value(f_functions.back().f_stack.back());
2932 0 : f_functions.back().f_stack.push_back(value);
2933 0 : }
2934 :
2935 :
2936 : /** \brief Duplicate the second to last object on the stack.
2937 : *
2938 : * This instruction gets a copy of the second to last object on the stack
2939 : * and push a copy on the stack.
2940 : */
2941 0 : void inst_duplicate2()
2942 : {
2943 : #if QDOM_XPATH_VERIFICATION
2944 : // verify instruction location
2945 0 : if(f_program[f_functions.back().f_pc - 1] != INST_DUPLICATE2)
2946 : {
2947 0 : throw QDomXPathException_InternalError("INST_DUPLICATE2 not at the right location in the table of instructions");
2948 : }
2949 : #endif
2950 0 : int size(f_functions.back().f_stack.size());
2951 0 : if(size <= 1)
2952 : {
2953 0 : throw QDomXPathException_EmptyStack("duplicate(2) cannot be used with a stack of less than 2 items");
2954 : }
2955 0 : variant_t value(f_functions.back().f_stack[size - 2]);
2956 0 : f_functions.back().f_stack.push_back(value);
2957 0 : }
2958 :
2959 :
2960 : /** \brief Duplicate the third to last object on the stack.
2961 : *
2962 : * This instruction gets a copy of the third to last object on the stack
2963 : * and push a copy on the stack.
2964 : */
2965 0 : void inst_duplicate3()
2966 : {
2967 : #if QDOM_XPATH_VERIFICATION
2968 : // verify instruction location
2969 0 : if(f_program[f_functions.back().f_pc - 1] != INST_DUPLICATE3)
2970 : {
2971 0 : throw QDomXPathException_InternalError("INST_DUPLICATE3 not at the right location in the table of instructions");
2972 : }
2973 : #endif
2974 0 : int size(f_functions.back().f_stack.size());
2975 0 : if(size <= 2)
2976 : {
2977 0 : throw QDomXPathException_EmptyStack("duplicate(3) cannot be used with a stack of less than 3 items");
2978 : }
2979 0 : variant_t value(f_functions.back().f_stack[size - 3]);
2980 0 : f_functions.back().f_stack.push_back(value);
2981 0 : }
2982 :
2983 :
2984 : /** \brief Duplicate the forth to last object on the stack.
2985 : *
2986 : * This instruction gets a copy of the forth to last object on the stack
2987 : * and push a copy on the stack.
2988 : */
2989 0 : void inst_duplicate4()
2990 : {
2991 : #if QDOM_XPATH_VERIFICATION
2992 : // verify instruction location
2993 0 : if(f_program[f_functions.back().f_pc - 1] != INST_DUPLICATE4)
2994 : {
2995 0 : throw QDomXPathException_InternalError("INST_DUPLICATE4 not at the right location in the table of instructions");
2996 : }
2997 : #endif
2998 0 : int size(f_functions.back().f_stack.size());
2999 0 : if(size <= 3)
3000 : {
3001 0 : throw QDomXPathException_EmptyStack("duplicate(4) cannot be used with a stack of less than 4 items");
3002 : }
3003 0 : variant_t value(f_functions.back().f_stack[size - 4]);
3004 0 : f_functions.back().f_stack.push_back(value);
3005 0 : }
3006 :
3007 :
3008 : /** \brief Duplicate the fifth to last object on the stack.
3009 : *
3010 : * This instruction gets a copy of the fifth to last object on the stack
3011 : * and push a copy on the stack.
3012 : */
3013 0 : void inst_duplicate5()
3014 : {
3015 : #if QDOM_XPATH_VERIFICATION
3016 : // verify instruction location
3017 0 : if(f_program[f_functions.back().f_pc - 1] != INST_DUPLICATE5)
3018 : {
3019 0 : throw QDomXPathException_InternalError("INST_DUPLICATE5 not at the right location in the table of instructions");
3020 : }
3021 : #endif
3022 0 : int size(f_functions.back().f_stack.size());
3023 0 : if(size <= 4)
3024 : {
3025 0 : throw QDomXPathException_EmptyStack("duplicate(5) cannot be used with a stack of less than 5 items");
3026 : }
3027 0 : variant_t value(f_functions.back().f_stack[size - 5]);
3028 0 : f_functions.back().f_stack.push_back(value);
3029 0 : }
3030 :
3031 :
3032 : /** \brief Swap the last two objects on the stack.
3033 : *
3034 : * This instruction pops the last two objects and push them back in the other
3035 : * order so the last value on the stack is the one before last and vice versa.
3036 : *
3037 : * \code
3038 : * Source Destination
3039 : * stack a stack b
3040 : * stack b stack a
3041 : * stack c stack c
3042 : * stack d stack d
3043 : * ... ...
3044 : * \endcode
3045 : */
3046 0 : void inst_swap1()
3047 : {
3048 : #if QDOM_XPATH_VERIFICATION
3049 : // verify instruction location
3050 0 : if(f_program[f_functions.back().f_pc - 1] != INST_SWAP1)
3051 : {
3052 0 : throw QDomXPathException_InternalError("INST_SWAP1 not at the right location in the table of instructions");
3053 : }
3054 : #endif
3055 0 : int size(f_functions.back().f_stack.size());
3056 0 : if(size < 2)
3057 : {
3058 0 : throw QDomXPathException_EmptyStack("swap(1) cannot be used with a stack of less than 2 items");
3059 : }
3060 0 : std::swap(f_functions.back().f_stack[size - 2], f_functions.back().f_stack[size - 1]);
3061 : //variant_t a(f_functions.back().f_stack[size - 2]);
3062 : //variant_t b(f_functions.back().f_stack[size - 1]);
3063 : //f_functions.back().f_stack[size - 2] = b;
3064 : //f_functions.back().f_stack[size - 1] = a;
3065 0 : }
3066 :
3067 :
3068 : /** \brief Swap the third to last object with the last object on the stack.
3069 : *
3070 : * This instruction swaps the third to last object with the last object on
3071 : * the stack.
3072 : *
3073 : * \code
3074 : * Source Destination
3075 : * stack a stack c
3076 : * stack b stack b
3077 : * stack c stack a
3078 : * stack d stack d
3079 : * ... ...
3080 : * \endcode
3081 : */
3082 0 : void inst_swap2()
3083 : {
3084 : #if QDOM_XPATH_VERIFICATION
3085 : // verify instruction location
3086 0 : if(f_program[f_functions.back().f_pc - 1] != INST_SWAP2)
3087 : {
3088 0 : throw QDomXPathException_InternalError("INST_SWAP2 not at the right location in the table of instructions");
3089 : }
3090 : #endif
3091 0 : int size(f_functions.back().f_stack.size());
3092 0 : if(size < 3)
3093 : {
3094 0 : throw QDomXPathException_EmptyStack("swap(3) cannot be used with a stack of less than 3 items");
3095 : }
3096 0 : std::swap(f_functions.back().f_stack[size - 3], f_functions.back().f_stack[size - 1]);
3097 0 : }
3098 :
3099 :
3100 : /** \brief Swap the forth to last object with the last object on the stack.
3101 : *
3102 : * This instruction swaps the forth to last object with the last object on
3103 : * the stack.
3104 : *
3105 : * \code
3106 : * Source Destination
3107 : * stack a stack d
3108 : * stack b stack b
3109 : * stack c stack c
3110 : * stack d stack a
3111 : * stack e stack e
3112 : * ... ...
3113 : * \endcode
3114 : */
3115 0 : void inst_swap3()
3116 : {
3117 : #if QDOM_XPATH_VERIFICATION
3118 : // verify instruction location
3119 0 : if(f_program[f_functions.back().f_pc - 1] != INST_SWAP3)
3120 : {
3121 0 : throw QDomXPathException_InternalError("INST_SWAP3 not at the right location in the table of instructions");
3122 : }
3123 : #endif
3124 0 : int size(f_functions.back().f_stack.size());
3125 0 : if(size < 4)
3126 : {
3127 0 : throw QDomXPathException_EmptyStack("swap(4) cannot be used with a stack of less than 4 items");
3128 : }
3129 0 : std::swap(f_functions.back().f_stack[size - 4], f_functions.back().f_stack[size - 1]);
3130 0 : }
3131 :
3132 :
3133 : /** \brief Swap the fifth to last object with the last object on the stack.
3134 : *
3135 : * This instruction swaps the fifth to last object with the last object on
3136 : * the stack.
3137 : *
3138 : * \code
3139 : * Source Destination
3140 : * stack a stack e
3141 : * stack b stack b
3142 : * stack c stack c
3143 : * stack d stack d
3144 : * stack e stack a
3145 : * stack f stack e
3146 : * ... ...
3147 : * \endcode
3148 : */
3149 0 : void inst_swap4()
3150 : {
3151 : #if QDOM_XPATH_VERIFICATION
3152 : // verify instruction location
3153 0 : if(f_program[f_functions.back().f_pc - 1] != INST_SWAP4)
3154 : {
3155 0 : throw QDomXPathException_InternalError("INST_SWAP4 not at the right location in the table of instructions");
3156 : }
3157 : #endif
3158 0 : int size(f_functions.back().f_stack.size());
3159 0 : if(size < 5)
3160 : {
3161 0 : throw QDomXPathException_EmptyStack("swap(5) cannot be used with a stack of less than 5 items");
3162 : }
3163 0 : std::swap(f_functions.back().f_stack[size - 5], f_functions.back().f_stack[size - 1]);
3164 0 : }
3165 :
3166 :
3167 : /** \brief Swap the sixth to last object with the last object on the stack.
3168 : *
3169 : * This instruction swaps the sixth to last object with the last object on
3170 : * the stack.
3171 : *
3172 : * \code
3173 : * Source Destination
3174 : * stack a stack f
3175 : * stack b stack b
3176 : * stack c stack c
3177 : * stack d stack d
3178 : * stack e stack e
3179 : * stack f stack a
3180 : * stack g stack g
3181 : * ... ...
3182 : * \endcode
3183 : */
3184 0 : void inst_swap5()
3185 : {
3186 : #if QDOM_XPATH_VERIFICATION
3187 : // verify instruction location
3188 0 : if(f_program[f_functions.back().f_pc - 1] != INST_SWAP5)
3189 : {
3190 0 : throw QDomXPathException_InternalError("INST_SWAP5 not at the right location in the table of instructions");
3191 : }
3192 : #endif
3193 0 : int size(f_functions.back().f_stack.size());
3194 0 : if(size < 6)
3195 : {
3196 0 : throw QDomXPathException_EmptyStack("swap(6) cannot be used with a stack of less than 6 items");
3197 : }
3198 0 : std::swap(f_functions.back().f_stack[size - 6], f_functions.back().f_stack[size - 1]);
3199 0 : }
3200 :
3201 :
3202 : /** \brief Swap the second object with the thrid object.
3203 : *
3204 : * This instruction swaps the second and third objects.
3205 : *
3206 : * \code
3207 : * Source Destination
3208 : * stack a stack a
3209 : * stack b stack c
3210 : * stack c stack b
3211 : * stack d stack d
3212 : * ... ...
3213 : * \endcode
3214 : */
3215 0 : void inst_swap2_3()
3216 : {
3217 : #if QDOM_XPATH_VERIFICATION
3218 : // verify instruction location
3219 0 : if(f_program[f_functions.back().f_pc - 1] != INST_SWAP2_3)
3220 : {
3221 0 : throw QDomXPathException_InternalError("INST_SWAP2_3 not at the right location in the table of instructions");
3222 : }
3223 : #endif
3224 0 : const int size(f_functions.back().f_stack.size());
3225 0 : if(size < 3)
3226 : {
3227 0 : throw QDomXPathException_EmptyStack("swap(2, 3) cannot be used with a stack of less than 3 items");
3228 : }
3229 0 : std::swap(f_functions.back().f_stack[size - 3], f_functions.back().f_stack[size - 2]);
3230 0 : }
3231 :
3232 :
3233 : /** \brief Push the special value: End of Arguments.
3234 : *
3235 : * This function pushes the End of Arguments special mark on the stack.
3236 : * This special value is used to end the list of arguments. It is as fast
3237 : * as having a counter and know how many arguments are defined to call
3238 : * a function.
3239 : */
3240 0 : void inst_push_end_of_arguments()
3241 : {
3242 : #if QDOM_XPATH_VERIFICATION
3243 : // verify instruction location
3244 0 : if(f_program[f_functions.back().f_pc - 1] != INST_PUSH_END_OF_ARGUMENTS)
3245 : {
3246 0 : throw QDomXPathException_InternalError("INST_PUSH_END_OF_ARGUMENTS not at the right location in the table of instructions");
3247 : }
3248 : #endif
3249 0 : variant_t value;
3250 0 : value.atomic_value_t::setEndOfArguments();
3251 0 : f_functions.back().f_stack.push_back(value);
3252 0 : }
3253 :
3254 : /** \brief Push the empty node set.
3255 : *
3256 : * This function pushes an empty set of nodes on the stack.
3257 : */
3258 0 : void inst_push_empty_node_set()
3259 : {
3260 : #if QDOM_XPATH_VERIFICATION
3261 : // verify instruction location
3262 0 : if(f_program[f_functions.back().f_pc - 1] != INST_PUSH_EMPTY_NODE_SET)
3263 : {
3264 0 : throw QDomXPathException_InternalError("INST_PUSH_EMPTY_NODE_SET not at the right location in the table of instructions");
3265 : }
3266 : #endif
3267 0 : variant_t value;
3268 0 : QDomXPath::node_vector_t empty;
3269 0 : value.setValue(empty);
3270 0 : f_functions.back().f_stack.push_back(value);
3271 0 : }
3272 :
3273 : /** \brief Push the empty atomic set.
3274 : *
3275 : * This function pushes an empty set of atomic values on the stack.
3276 : */
3277 0 : void inst_push_empty_set()
3278 : {
3279 : #if QDOM_XPATH_VERIFICATION
3280 : // verify instruction location
3281 0 : if(f_program[f_functions.back().f_pc - 1] != INST_PUSH_EMPTY_SET)
3282 : {
3283 0 : throw QDomXPathException_InternalError("INST_PUSH_EMPTY_SET not at the right location in the table of instructions");
3284 : }
3285 : #endif
3286 0 : variant_t value;
3287 0 : atomic_vector_t empty;
3288 0 : value.setValue(empty);
3289 0 : f_functions.back().f_stack.push_back(value);
3290 0 : }
3291 :
3292 : /** \brief Push the "" string.
3293 : *
3294 : * This function pushes the "*" string on the stack.
3295 : */
3296 0 : void inst_push_empty_string()
3297 : {
3298 : #if QDOM_XPATH_VERIFICATION
3299 : // verify instruction location
3300 0 : if(f_program[f_functions.back().f_pc - 1] != INST_PUSH_EMPTY_STRING)
3301 : {
3302 0 : throw QDomXPathException_InternalError("INST_PUSH_EMPTY_STRING not at the right location in the table of instructions");
3303 : }
3304 : #endif
3305 0 : variant_t value;
3306 0 : value.atomic_value_t::setValue("");
3307 0 : f_functions.back().f_stack.push_back(value);
3308 0 : }
3309 :
3310 : /** \brief Push the "*" string.
3311 : *
3312 : * This function pushes the "*" string on the stack.
3313 : */
3314 0 : void inst_push_any_string()
3315 : {
3316 : #if QDOM_XPATH_VERIFICATION
3317 : // verify instruction location
3318 0 : if(f_program[f_functions.back().f_pc - 1] != INST_PUSH_ANY_STRING)
3319 : {
3320 0 : throw QDomXPathException_InternalError("INST_PUSH_ANY_STRING not at the right location in the table of instructions");
3321 : }
3322 : #endif
3323 0 : variant_t value;
3324 0 : value.atomic_value_t::setValue("*");
3325 0 : f_functions.back().f_stack.push_back(value);
3326 0 : }
3327 :
3328 : /** \brief Push a small string.
3329 : *
3330 : * This function pushes the string as defined by the push instruction.
3331 : * This string is limited to 0 to 255 characters.
3332 : */
3333 0 : void inst_push_small_string()
3334 : {
3335 : #if QDOM_XPATH_VERIFICATION
3336 : // verify instruction location
3337 0 : if(f_program[f_functions.back().f_pc - 1] != INST_PUSH_SMALL_STRING)
3338 : {
3339 0 : throw QDomXPathException_InternalError("INST_PUSH_SMALL_STRING not at the right location in the table of instructions");
3340 : }
3341 : #endif
3342 0 : int length(static_cast<int>(get_next_program_byte()));
3343 0 : variant_t value;
3344 0 : value.atomic_value_t::setValue(QString::fromUtf8(reinterpret_cast<char *>(f_program.data() + f_functions.back().f_pc), length));
3345 0 : f_functions.back().f_pc += length;
3346 0 : f_functions.back().f_stack.push_back(value);
3347 0 : }
3348 :
3349 : /** \brief Push a medium string.
3350 : *
3351 : * This function pushes the string as defined by the push instruction.
3352 : * This string is limited to 1 to 65535 characters (although strings
3353 : * of 1 to 255 characters are considered Small Strings and this
3354 : * PUSH would not apply to them.)
3355 : */
3356 0 : void inst_push_medium_string()
3357 : {
3358 : #if QDOM_XPATH_VERIFICATION
3359 : // verify instruction location
3360 0 : if(f_program[f_functions.back().f_pc - 1] != INST_PUSH_MEDIUM_STRING)
3361 : {
3362 0 : throw QDomXPathException_InternalError("INST_PUSH_MEDIUM_STRING not at the right location in the table of instructions");
3363 : }
3364 : #endif
3365 0 : int64_t length((static_cast<int>(get_next_program_byte()) << 8)
3366 0 : | static_cast<int>(get_next_program_byte()));
3367 0 : variant_t value;
3368 0 : value.atomic_value_t::setValue(QString::fromUtf8(reinterpret_cast<char *>(f_program.data() + f_functions.back().f_pc), static_cast<int>(length)));
3369 0 : f_functions.back().f_pc += static_cast<uint32_t>(length);
3370 0 : f_functions.back().f_stack.push_back(value);
3371 0 : }
3372 :
3373 : /** \brief Push a large string.
3374 : *
3375 : * This function pushes the string as defined by the push instruction.
3376 : * This string is limited to 1 to 4 billion characters (although strings
3377 : * of 1 to 255 characters are considered Small Strings, also strings of
3378 : * 256 to 65535 characters are considered Medium Strings and this
3379 : * PUSH would not apply to either one of those cases.)
3380 : *
3381 : * Note that if you have a string this big... You may have a problem in
3382 : * that XPath.
3383 : */
3384 0 : void inst_push_large_string()
3385 : {
3386 : #if QDOM_XPATH_VERIFICATION
3387 : // verify instruction location
3388 0 : if(f_program[f_functions.back().f_pc - 1] != INST_PUSH_LARGE_STRING)
3389 : {
3390 0 : throw QDomXPathException_InternalError("INST_PUSH_LARGE_STRING not at the right location in the table of instructions");
3391 : }
3392 : #endif
3393 0 : int64_t length((static_cast<int>(get_next_program_byte()) << 24)
3394 0 : | (static_cast<int>(get_next_program_byte()) << 16)
3395 0 : | (static_cast<int>(get_next_program_byte()) << 8)
3396 0 : | static_cast<int>(get_next_program_byte()));
3397 0 : variant_t value;
3398 0 : value.atomic_value_t::setValue(QString::fromUtf8(reinterpret_cast<char *>(f_program.data() + f_functions.back().f_pc), static_cast<int>(length)));
3399 0 : f_functions.back().f_pc += static_cast<uint32_t>(length);
3400 0 : f_functions.back().f_stack.push_back(value);
3401 0 : }
3402 :
3403 : /** \brief Push the zero integer on the stack.
3404 : *
3405 : * This function pushes the value zero on the stack.
3406 : */
3407 0 : void inst_push_zero()
3408 : {
3409 : #if QDOM_XPATH_VERIFICATION
3410 : // verify instruction location
3411 0 : if(f_program[f_functions.back().f_pc - 1] != INST_PUSH_ZERO)
3412 : {
3413 0 : throw QDomXPathException_InternalError("INST_PUSH_ZERO not at the right location in the table of instructions");
3414 : }
3415 : #endif
3416 0 : variant_t value;
3417 0 : int64_t zero(0);
3418 0 : value.atomic_value_t::setValue(zero);
3419 0 : f_functions.back().f_stack.push_back(value);
3420 0 : }
3421 :
3422 : /** \brief Push the Boolean true.
3423 : *
3424 : * This function pushes the Boolean true on the stack.
3425 : */
3426 0 : void inst_push_true()
3427 : {
3428 : #if QDOM_XPATH_VERIFICATION
3429 : // verify instruction location
3430 0 : if(f_program[f_functions.back().f_pc - 1] != INST_PUSH_TRUE)
3431 : {
3432 0 : throw QDomXPathException_InternalError("INST_PUSH_TRUE not at the right location in the table of instructions");
3433 : }
3434 : #endif
3435 0 : variant_t value;
3436 0 : value.atomic_value_t::setValue(true);
3437 0 : f_functions.back().f_stack.push_back(value);
3438 0 : }
3439 :
3440 : /** \brief Push the Boolean false.
3441 : *
3442 : * This function pushes the Boolean false on the stack.
3443 : */
3444 0 : void inst_push_false()
3445 : {
3446 : #if QDOM_XPATH_VERIFICATION
3447 : // verify instruction location
3448 0 : if(f_program[f_functions.back().f_pc - 1] != INST_PUSH_FALSE)
3449 : {
3450 0 : throw QDomXPathException_InternalError("INST_PUSH_FALSE not at the right location in the table of instructions");
3451 : }
3452 : #endif
3453 0 : variant_t value;
3454 0 : value.atomic_value_t::setValue(false);
3455 0 : f_functions.back().f_stack.push_back(value);
3456 0 : }
3457 :
3458 : /** \brief Push an integer on the stack from one byte.
3459 : *
3460 : * This function reads one byte from the program and pushes it on the stack.
3461 : * The byte is taken as an unsigned integer (0 to 255).
3462 : */
3463 0 : void inst_push_byte()
3464 : {
3465 : #if QDOM_XPATH_VERIFICATION
3466 : // verify instruction location
3467 0 : if(f_program[f_functions.back().f_pc - 1] != INST_PUSH_BYTE)
3468 : {
3469 0 : throw QDomXPathException_InternalError("INST_PUSH_BYTE not at the right location in the table of instructions");
3470 : }
3471 : #endif
3472 0 : variant_t value;
3473 0 : value.atomic_value_t::setValue(static_cast<int64_t>(get_next_program_byte()));
3474 0 : f_functions.back().f_stack.push_back(value);
3475 0 : }
3476 :
3477 : /** \brief Push an integer on the stack from one negative byte.
3478 : *
3479 : * This function reads one byte from the program and pushes it on the stack.
3480 : * The byte is taken as a negative integer (-256 to -1).
3481 : */
3482 0 : void inst_push_negative_byte()
3483 : {
3484 : #if QDOM_XPATH_VERIFICATION
3485 : // verify instruction location
3486 0 : if(f_program[f_functions.back().f_pc - 1] != INST_PUSH_NEGATIVE_BYTE)
3487 : {
3488 0 : throw QDomXPathException_InternalError("INST_PUSH_NEGATIVE_BYTE not at the right location in the table of instructions");
3489 : }
3490 : #endif
3491 0 : variant_t value;
3492 0 : value.atomic_value_t::setValue(static_cast<int64_t>(get_next_program_byte() | 0xFFFFFFFFFFFFFF00LL));
3493 0 : f_functions.back().f_stack.push_back(value);
3494 0 : }
3495 :
3496 : /** \brief Push an integer on the stack from two bytes.
3497 : *
3498 : * This function reads two bytes from the program and pushes them on the stack
3499 : * as a short.
3500 : *
3501 : * The bytes are viewed as an unsigned integer (0 to 65535).
3502 : */
3503 0 : void inst_push_short()
3504 : {
3505 : #if QDOM_XPATH_VERIFICATION
3506 : // verify instruction location
3507 0 : if(f_program[f_functions.back().f_pc - 1] != INST_PUSH_SHORT)
3508 : {
3509 0 : throw QDomXPathException_InternalError("INST_PUSH_SHORT not at the right location in the table of instructions");
3510 : }
3511 : #endif
3512 0 : variant_t value;
3513 0 : value.atomic_value_t::setValue((static_cast<int64_t>(get_next_program_byte()) << 8)
3514 0 : | static_cast<int64_t>(get_next_program_byte()));
3515 0 : f_functions.back().f_stack.push_back(value);
3516 0 : }
3517 :
3518 : /** \brief Push an integer on the stack from two negative bytes.
3519 : *
3520 : * This function reads two bytes from the program and pushes them on the stack
3521 : * after making all the other bits 1's since the bytes are taken as a
3522 : * negative integer (-65536 to -1).
3523 : */
3524 0 : void inst_push_negative_short()
3525 : {
3526 : #if QDOM_XPATH_VERIFICATION
3527 : // verify instruction location
3528 0 : if(f_program[f_functions.back().f_pc - 1] != INST_PUSH_NEGATIVE_SHORT)
3529 : {
3530 0 : throw QDomXPathException_InternalError("INST_PUSH_NEGATIVE_SHORT not at the right location in the table of instructions");
3531 : }
3532 : #endif
3533 0 : variant_t value;
3534 0 : value.atomic_value_t::setValue((static_cast<int64_t>(get_next_program_byte() << 8)
3535 0 : | static_cast<int64_t>(get_next_program_byte())
3536 0 : | static_cast<int64_t>(0xFFFFFFFFFFFF0000LL)));
3537 0 : f_functions.back().f_stack.push_back(value);
3538 0 : }
3539 :
3540 : /** \brief Push an integer on the stack from four bytes.
3541 : *
3542 : * This function reads four bytes from the program and pushes them on the stack
3543 : * as a long.
3544 : *
3545 : * The bytes are viewed as an unsigned integer (0 to 0xFFFFFFFF).
3546 : */
3547 0 : void inst_push_long()
3548 : {
3549 : #if QDOM_XPATH_VERIFICATION
3550 : // verify instruction location
3551 0 : if(f_program[f_functions.back().f_pc - 1] != INST_PUSH_LONG)
3552 : {
3553 0 : throw QDomXPathException_InternalError("INST_PUSH_LONG not at the right location in the table of instructions");
3554 : }
3555 : #endif
3556 0 : variant_t value;
3557 0 : value.atomic_value_t::setValue((static_cast<int64_t>(get_next_program_byte()) << 24)
3558 0 : | (static_cast<int64_t>(get_next_program_byte()) << 16)
3559 0 : | (static_cast<int64_t>(get_next_program_byte()) << 8)
3560 0 : | static_cast<int64_t>(get_next_program_byte()));
3561 0 : f_functions.back().f_stack.push_back(value);
3562 0 : }
3563 :
3564 : /** \brief Push an integer on the stack from four negative bytes.
3565 : *
3566 : * This function reads four bytes from the program and pushes them on the stack
3567 : * after making all the other bits 1's since the bytes are taken as a
3568 : * negative integer (-0x100000000 to -1).
3569 : */
3570 0 : void inst_push_negative_long()
3571 : {
3572 : #if QDOM_XPATH_VERIFICATION
3573 : // verify instruction location
3574 0 : if(f_program[f_functions.back().f_pc - 1] != INST_PUSH_NEGATIVE_LONG)
3575 : {
3576 0 : throw QDomXPathException_InternalError("INST_PUSH_NEGATIVE_LONG not at the right location in the table of instructions");
3577 : }
3578 : #endif
3579 0 : variant_t value;
3580 0 : value.atomic_value_t::setValue((static_cast<int64_t>(get_next_program_byte()) << 24)
3581 0 : | (static_cast<int64_t>(get_next_program_byte()) << 16)
3582 0 : | (static_cast<int64_t>(get_next_program_byte()) << 8)
3583 0 : | static_cast<int64_t>(get_next_program_byte())
3584 : | static_cast<int64_t>(0xFFFFFFFF00000000LL));
3585 0 : f_functions.back().f_stack.push_back(value);
3586 0 : }
3587 :
3588 : /** \brief Push an integer on the stack from eight bytes.
3589 : *
3590 : * This function reads eight bytes from the program and pushes them on the
3591 : * stack as a long long.
3592 : */
3593 0 : void inst_push_longlong()
3594 : {
3595 : #if QDOM_XPATH_VERIFICATION
3596 : // verify instruction location
3597 0 : if(f_program[f_functions.back().f_pc - 1] != INST_PUSH_LONGLONG)
3598 : {
3599 0 : throw QDomXPathException_InternalError("INST_PUSH_LONGLONG not at the right location in the table of instructions");
3600 : }
3601 : #endif
3602 0 : variant_t value;
3603 0 : value.atomic_value_t::setValue((static_cast<int64_t>(get_next_program_byte()) << 56)
3604 0 : | (static_cast<int64_t>(get_next_program_byte()) << 48)
3605 0 : | (static_cast<int64_t>(get_next_program_byte()) << 40)
3606 0 : | (static_cast<int64_t>(get_next_program_byte()) << 32)
3607 0 : | (static_cast<int64_t>(get_next_program_byte()) << 24)
3608 0 : | (static_cast<int64_t>(get_next_program_byte()) << 16)
3609 0 : | (static_cast<int64_t>(get_next_program_byte()) << 8)
3610 0 : | static_cast<int64_t>(get_next_program_byte()));
3611 0 : f_functions.back().f_stack.push_back(value);
3612 0 : }
3613 :
3614 : /** \brief Push a Double on the stack from eight bytes.
3615 : *
3616 : * This function reads eight bytes from the program and pushes them on the
3617 : * stack as a double.
3618 : */
3619 0 : void inst_push_double()
3620 : {
3621 : #if QDOM_XPATH_VERIFICATION
3622 : // verify instruction location
3623 0 : if(f_program[f_functions.back().f_pc - 1] != INST_PUSH_DOUBLE)
3624 : {
3625 0 : throw QDomXPathException_InternalError("INST_PUSH_DOUBLE not at the right location in the table of instructions");
3626 : }
3627 : #endif
3628 : union
3629 : {
3630 : uint64_t f_int;
3631 : double f_dbl;
3632 : } convert;
3633 0 : convert.f_int = (static_cast<int64_t>(get_next_program_byte()) << 56)
3634 0 : | (static_cast<int64_t>(get_next_program_byte()) << 48)
3635 0 : | (static_cast<int64_t>(get_next_program_byte()) << 40)
3636 0 : | (static_cast<int64_t>(get_next_program_byte()) << 32)
3637 0 : | (static_cast<int64_t>(get_next_program_byte()) << 24)
3638 0 : | (static_cast<int64_t>(get_next_program_byte()) << 16)
3639 0 : | (static_cast<int64_t>(get_next_program_byte()) << 8)
3640 0 : | static_cast<int64_t>(get_next_program_byte());
3641 0 : variant_t value;
3642 0 : value.atomic_value_t::setValue(convert.f_dbl);
3643 0 : f_functions.back().f_stack.push_back(value);
3644 0 : }
3645 :
3646 : /** \brief Push a Double zero on the stack.
3647 : *
3648 : * This function pushes a double on the stack. The double is set to 0.0.
3649 : */
3650 0 : void inst_push_double_zero()
3651 : {
3652 : #if QDOM_XPATH_VERIFICATION
3653 : // verify instruction location
3654 0 : if(f_program[f_functions.back().f_pc - 1] != INST_PUSH_DOUBLE_ZERO)
3655 : {
3656 0 : throw QDomXPathException_InternalError("INST_PUSH_DOUBLE_ZERO not at the right location in the table of instructions");
3657 : }
3658 : #endif
3659 0 : variant_t value;
3660 0 : double zero(0.0);
3661 0 : value.atomic_value_t::setValue(zero);
3662 0 : f_functions.back().f_stack.push_back(value);
3663 0 : }
3664 :
3665 :
3666 : /** \brief Add one to the number at the top of the stack.
3667 : *
3668 : * This function pops one integer, adds one to it, and push it back.
3669 : * Note that one is added even to single and double numbers, and not
3670 : * epsilon. This means really large numbers will not change.
3671 : *
3672 : * Only integers, decimal, single, and doubles can be added between
3673 : * each others.
3674 : */
3675 0 : void inst_increment()
3676 : {
3677 : #if QDOM_XPATH_VERIFICATION
3678 : // verify instruction location
3679 0 : if(f_program[f_functions.back().f_pc - 1] != INST_INCREMENT)
3680 : {
3681 0 : throw QDomXPathException_InternalError("INST_INCREMENT not at the right location in the table of instructions");
3682 : }
3683 : #endif
3684 0 : variant_t value(pop_variant_data());
3685 0 : switch(value.getType())
3686 : {
3687 0 : case atomic_value_t::type_t::ATOMIC_TYPE_INTEGER:
3688 0 : value.atomic_value_t::setValue(value.getIntegerValue() + 1);
3689 0 : break;
3690 :
3691 0 : case atomic_value_t::type_t::ATOMIC_TYPE_SINGLE:
3692 0 : value.atomic_value_t::setValue(value.getSingleValue(true) + 1.0f);
3693 0 : break;
3694 :
3695 0 : case atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE:
3696 0 : value.atomic_value_t::setValue(value.getDoubleValue(true) + 1.0);
3697 0 : break;
3698 :
3699 0 : default:
3700 0 : throw QDomXPathException_WrongType("the '++' operator cannot be used with the left and right hand side types");
3701 :
3702 : }
3703 0 : f_functions.back().f_stack.push_back(value);
3704 0 : }
3705 :
3706 :
3707 : /** \brief Subtract one to the number at the top of the stack.
3708 : *
3709 : * This function pops one integer, subtracts one to it, and push it back.
3710 : * Note that one is subtracted even to single and double numbers, and not
3711 : * epsilon. This means really large numbers will not change.
3712 : *
3713 : * Only integers, decimal, single, and doubles can be added between
3714 : * each others.
3715 : */
3716 0 : void inst_decrement()
3717 : {
3718 : #if QDOM_XPATH_VERIFICATION
3719 : // verify instruction location
3720 0 : if(f_program[f_functions.back().f_pc - 1] != INST_DECREMENT)
3721 : {
3722 0 : throw QDomXPathException_InternalError("INST_DECREMENT not at the right location in the table of instructions");
3723 : }
3724 : #endif
3725 0 : variant_t value(pop_variant_data());
3726 0 : switch(value.getType())
3727 : {
3728 0 : case atomic_value_t::type_t::ATOMIC_TYPE_INTEGER:
3729 0 : value.atomic_value_t::setValue(value.getIntegerValue() + 1);
3730 0 : break;
3731 :
3732 0 : case atomic_value_t::type_t::ATOMIC_TYPE_SINGLE:
3733 0 : value.atomic_value_t::setValue(value.getSingleValue(true) + 1);
3734 0 : break;
3735 :
3736 0 : case atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE:
3737 0 : value.atomic_value_t::setValue(value.getDoubleValue(true) + 1);
3738 0 : break;
3739 :
3740 0 : default:
3741 0 : throw QDomXPathException_WrongType("the '--' operator cannot be used with the left and right hand side types");
3742 :
3743 : }
3744 0 : f_functions.back().f_stack.push_back(value);
3745 0 : }
3746 :
3747 :
3748 : /** \brief Return the length of a string in characters.
3749 : *
3750 : * This function pops a string from the stack, computes its length in
3751 : * characters, and pushes that length back on the stack.
3752 : */
3753 0 : void inst_string_length()
3754 : {
3755 : #if QDOM_XPATH_VERIFICATION
3756 : // verify instruction location
3757 0 : if(f_program[f_functions.back().f_pc - 1] != INST_STRING_LENGTH)
3758 : {
3759 0 : throw QDomXPathException_InternalError("INST_STRING_LENGTH not at the right location in the table of instructions");
3760 : }
3761 : #endif
3762 0 : variant_t value(pop_variant_data());
3763 0 : if(value.getType() != atomic_value_t::type_t::ATOMIC_TYPE_STRING)
3764 : {
3765 0 : throw QDomXPathException_WrongType("the string-length() function only accepts strings");
3766 : }
3767 0 : value.atomic_value_t::setValue(static_cast<int64_t>(value.getStringValue().length()));
3768 :
3769 0 : f_functions.back().f_stack.push_back(value);
3770 0 : }
3771 :
3772 :
3773 : /** \brief Compute the ceiling of a number.
3774 : *
3775 : * This function pops one number. If the number is an integer, then it pushes
3776 : * it back as is. If the number if a Decimal, a Single, or a Double, it
3777 : * checks the decimal digits. If all are zeroes, then the same value is
3778 : * pushed back on the stack. Otherwise it resets all the decimal digits
3779 : * adds one and then push the result back on the stack as the same type.
3780 : *
3781 : * \note
3782 : * This function does not yet handle NaN numbers.
3783 : *
3784 : * \note
3785 : * The value is NOT transformed to an integer. The cast needs to be used
3786 : * for that purpose.
3787 : */
3788 0 : void inst_ceiling()
3789 : {
3790 : #if QDOM_XPATH_VERIFICATION
3791 : // verify instruction location
3792 0 : if(f_program[f_functions.back().f_pc - 1] != INST_CEILING)
3793 : {
3794 0 : throw QDomXPathException_InternalError("INST_CEILING not at the right location in the table of instructions");
3795 : }
3796 : #endif
3797 0 : variant_t value(pop_variant_data());
3798 0 : switch(value.getType())
3799 : {
3800 0 : case atomic_value_t::type_t::ATOMIC_TYPE_INTEGER:
3801 : // nothing happens
3802 0 : break;
3803 :
3804 0 : case atomic_value_t::type_t::ATOMIC_TYPE_SINGLE:
3805 0 : value.atomic_value_t::setValue(std::ceil(value.getSingleValue()));
3806 0 : break;
3807 :
3808 0 : case atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE:
3809 0 : value.atomic_value_t::setValue(std::ceil(value.getDoubleValue()));
3810 0 : break;
3811 :
3812 0 : default:
3813 0 : throw QDomXPathException_WrongType("the ceiling() function can only be applied against numbers");
3814 :
3815 : }
3816 :
3817 0 : f_functions.back().f_stack.push_back(value);
3818 0 : }
3819 :
3820 :
3821 : /** \brief Compute the floor of a number.
3822 : *
3823 : * This function pops one number. If the number is an integer, then it pushes
3824 : * it back as is. If the number if a Decimal, a Single, or a Double, it
3825 : * sets all of its decimal digits to zero and pushes that back on the
3826 : * same as the same type.
3827 : *
3828 : * \note
3829 : * This function does not yet handle NaN numbers.
3830 : *
3831 : * \note
3832 : * The value is NOT transformed to an integer. The cast needs to be used
3833 : * for that purpose.
3834 : */
3835 0 : void inst_floor()
3836 : {
3837 : #if QDOM_XPATH_VERIFICATION
3838 : // verify instruction location
3839 0 : if(f_program[f_functions.back().f_pc - 1] != INST_FLOOR)
3840 : {
3841 0 : throw QDomXPathException_InternalError("INST_FLOOR not at the right location in the table of instructions");
3842 : }
3843 : #endif
3844 0 : variant_t value(pop_variant_data());
3845 0 : switch(value.getType())
3846 : {
3847 0 : case atomic_value_t::type_t::ATOMIC_TYPE_INTEGER:
3848 : // nothing happens
3849 0 : break;
3850 :
3851 0 : case atomic_value_t::type_t::ATOMIC_TYPE_SINGLE:
3852 0 : value.atomic_value_t::setValue(std::floor(value.getSingleValue()));
3853 0 : break;
3854 :
3855 0 : case atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE:
3856 0 : value.atomic_value_t::setValue(std::floor(value.getDoubleValue()));
3857 0 : break;
3858 :
3859 0 : default:
3860 0 : throw QDomXPathException_WrongType("the floor() function can only be applied against numbers");
3861 :
3862 : }
3863 :
3864 0 : f_functions.back().f_stack.push_back(value);
3865 0 : }
3866 :
3867 :
3868 : /** \brief Compute the rounded value of a number.
3869 : *
3870 : * This function pops one number. If the number is an integer, then it pushes
3871 : * it back as is. If the number if a Decimal, a Single, or a Double, then
3872 : * the function adds 0.5 and then it computes the floor() (the floor
3873 : * sets all the decimal digits of the result to zeroes.)
3874 : *
3875 : * The result is pushed back on the stack as the same type as the input.
3876 : *
3877 : * \note
3878 : * Numbers such as -0.8 are expected to return -0.0 which this function
3879 : * does not do.
3880 : *
3881 : * \note
3882 : * This function does not yet handle NaN numbers.
3883 : *
3884 : * \note
3885 : * The value is NOT transformed to an integer. The cast needs to be used
3886 : * for that purpose.
3887 : */
3888 0 : void inst_round()
3889 : {
3890 : #if QDOM_XPATH_VERIFICATION
3891 : // verify instruction location
3892 0 : if(f_program[f_functions.back().f_pc - 1] != INST_ROUND)
3893 : {
3894 0 : throw QDomXPathException_InternalError("INST_ROUND not at the right location in the table of instructions");
3895 : }
3896 : #endif
3897 0 : variant_t value(pop_variant_data());
3898 0 : switch(value.getType())
3899 : {
3900 0 : case atomic_value_t::type_t::ATOMIC_TYPE_INTEGER:
3901 : // nothing happens
3902 0 : break;
3903 :
3904 0 : case atomic_value_t::type_t::ATOMIC_TYPE_SINGLE:
3905 0 : value.atomic_value_t::setValue(std::floor(value.getSingleValue() + 0.5f));
3906 0 : break;
3907 :
3908 0 : case atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE:
3909 0 : value.atomic_value_t::setValue(std::floor(value.getDoubleValue() + 0.5));
3910 0 : break;
3911 :
3912 0 : default:
3913 0 : throw QDomXPathException_WrongType("the round() function can only be applied against numbers");
3914 :
3915 : }
3916 :
3917 0 : f_functions.back().f_stack.push_back(value);
3918 0 : }
3919 :
3920 :
3921 : /** \brief Add two numbers together.
3922 : *
3923 : * This function adds two values that it first pops from the stack.
3924 : * The add differs slightly depending on the type of each value.
3925 : *
3926 : * Only integers, decimal, single, and doubles can be added between
3927 : * each others.
3928 : */
3929 0 : void inst_add()
3930 : {
3931 : #if QDOM_XPATH_VERIFICATION
3932 : // verify instruction location
3933 0 : if(f_program[f_functions.back().f_pc - 1] != INST_ADD)
3934 : {
3935 0 : throw QDomXPathException_InternalError("INST_ADD not at the right location in the table of instructions");
3936 : }
3937 : #endif
3938 0 : variant_t rhs(pop_variant_data());
3939 0 : variant_t lhs(pop_variant_data());
3940 0 : variant_t result;
3941 0 : switch(merge_types(lhs.getType(), rhs.getType()))
3942 : {
3943 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_INTEGER, atomic_value_t::type_t::ATOMIC_TYPE_INTEGER):
3944 0 : result.atomic_value_t::setValue(lhs.getIntegerValue() + rhs.getIntegerValue());
3945 0 : break;
3946 :
3947 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_SINGLE, atomic_value_t::type_t::ATOMIC_TYPE_INTEGER):
3948 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_INTEGER, atomic_value_t::type_t::ATOMIC_TYPE_SINGLE):
3949 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_SINGLE, atomic_value_t::type_t::ATOMIC_TYPE_SINGLE):
3950 0 : result.atomic_value_t::setValue(lhs.getSingleValue(true) + rhs.getSingleValue(true));
3951 0 : break;
3952 :
3953 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE, atomic_value_t::type_t::ATOMIC_TYPE_INTEGER):
3954 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_INTEGER, atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE):
3955 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_SINGLE, atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE):
3956 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE, atomic_value_t::type_t::ATOMIC_TYPE_SINGLE):
3957 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE, atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE):
3958 0 : result.atomic_value_t::setValue(lhs.getDoubleValue(true) + rhs.getDoubleValue(true));
3959 0 : break;
3960 :
3961 0 : default:
3962 0 : throw QDomXPathException_WrongType("the '+' operator cannot be used with the left and right hand side types");
3963 :
3964 : }
3965 0 : f_functions.back().f_stack.push_back(result);
3966 0 : }
3967 :
3968 :
3969 : /** \brief Subtract two numbers from each others.
3970 : *
3971 : * This function subtract two values that it first pops from the stack.
3972 : * The subtract differs slightly depending on the type of each value.
3973 : *
3974 : * Only integers, decimal, single, and doubles can be subtracted from
3975 : * each others.
3976 : */
3977 0 : void inst_subtract()
3978 : {
3979 : #if QDOM_XPATH_VERIFICATION
3980 : // verify instruction location
3981 0 : if(f_program[f_functions.back().f_pc - 1] != INST_SUBTRACT)
3982 : {
3983 0 : throw QDomXPathException_InternalError("INST_SUBTRACT not at the right location in the table of instructions");
3984 : }
3985 : #endif
3986 0 : variant_t rhs(pop_variant_data());
3987 0 : variant_t lhs(pop_variant_data());
3988 0 : variant_t result;
3989 0 : switch(merge_types(lhs.getType(), rhs.getType()))
3990 : {
3991 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_INTEGER, atomic_value_t::type_t::ATOMIC_TYPE_INTEGER):
3992 0 : result.atomic_value_t::setValue(lhs.getIntegerValue() - rhs.getIntegerValue());
3993 0 : break;
3994 :
3995 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_SINGLE, atomic_value_t::type_t::ATOMIC_TYPE_INTEGER):
3996 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_INTEGER, atomic_value_t::type_t::ATOMIC_TYPE_SINGLE):
3997 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_SINGLE, atomic_value_t::type_t::ATOMIC_TYPE_SINGLE):
3998 0 : result.atomic_value_t::setValue(lhs.getSingleValue(true) - rhs.getSingleValue(true));
3999 0 : break;
4000 :
4001 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE, atomic_value_t::type_t::ATOMIC_TYPE_INTEGER):
4002 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_INTEGER, atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE):
4003 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_SINGLE, atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE):
4004 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE, atomic_value_t::type_t::ATOMIC_TYPE_SINGLE):
4005 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE, atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE):
4006 0 : result.atomic_value_t::setValue(lhs.getDoubleValue(true) - rhs.getDoubleValue(true));
4007 0 : break;
4008 :
4009 0 : default:
4010 0 : throw QDomXPathException_WrongType("the '-' operator cannot be used with the left and right hand side types");
4011 :
4012 : }
4013 0 : f_functions.back().f_stack.push_back(result);
4014 0 : }
4015 :
4016 :
4017 : /** \brief Negate a number.
4018 : *
4019 : * This function pops a number negates it and push it back on the stack.
4020 : *
4021 : * Only integers, decimal, single, and doubles can be negated from
4022 : * each others.
4023 : */
4024 0 : void inst_negate()
4025 : {
4026 : #if QDOM_XPATH_VERIFICATION
4027 : // verify instruction location
4028 0 : if(f_program[f_functions.back().f_pc - 1] != INST_NEGATE)
4029 : {
4030 0 : throw QDomXPathException_InternalError("INST_NEGATE not at the right location in the table of instructions");
4031 : }
4032 : #endif
4033 0 : variant_t value(pop_variant_data());
4034 0 : variant_t result;
4035 0 : switch(value.getType())
4036 : {
4037 0 : case atomic_value_t::type_t::ATOMIC_TYPE_INTEGER:
4038 0 : result.atomic_value_t::setValue(-value.getIntegerValue());
4039 0 : break;
4040 :
4041 0 : case atomic_value_t::type_t::ATOMIC_TYPE_SINGLE:
4042 0 : result.atomic_value_t::setValue(-value.getSingleValue());
4043 0 : break;
4044 :
4045 0 : case atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE:
4046 0 : result.atomic_value_t::setValue(-value.getDoubleValue());
4047 0 : break;
4048 :
4049 0 : default:
4050 0 : throw QDomXPathException_WrongType("the '-' operator cannot be used with this value type");
4051 :
4052 : }
4053 0 : f_functions.back().f_stack.push_back(result);
4054 0 : }
4055 :
4056 :
4057 : /** \brief Multiply two numbers from each others.
4058 : *
4059 : * This function multiplies two values that it first pops from the stack.
4060 : * The multiply differs slightly depending on the type of each value.
4061 : *
4062 : * Only integers, decimal, single, and doubles can be subtracted from
4063 : * each others.
4064 : */
4065 0 : void inst_multiply()
4066 : {
4067 : #if QDOM_XPATH_VERIFICATION
4068 : // verify instruction location
4069 0 : if(f_program[f_functions.back().f_pc - 1] != INST_MULTIPLY)
4070 : {
4071 0 : throw QDomXPathException_InternalError("INST_MULTIPLY not at the right location in the table of instructions");
4072 : }
4073 : #endif
4074 0 : variant_t rhs(pop_variant_data());
4075 0 : variant_t lhs(pop_variant_data());
4076 0 : variant_t result;
4077 0 : switch(merge_types(lhs.getType(), rhs.getType()))
4078 : {
4079 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_INTEGER, atomic_value_t::type_t::ATOMIC_TYPE_INTEGER):
4080 0 : result.atomic_value_t::setValue(lhs.getIntegerValue() * rhs.getIntegerValue());
4081 0 : break;
4082 :
4083 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_SINGLE, atomic_value_t::type_t::ATOMIC_TYPE_INTEGER):
4084 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_INTEGER, atomic_value_t::type_t::ATOMIC_TYPE_SINGLE):
4085 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_SINGLE, atomic_value_t::type_t::ATOMIC_TYPE_SINGLE):
4086 0 : result.atomic_value_t::setValue(lhs.getSingleValue(true) * rhs.getSingleValue(true));
4087 0 : break;
4088 :
4089 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE, atomic_value_t::type_t::ATOMIC_TYPE_INTEGER):
4090 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_INTEGER, atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE):
4091 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_SINGLE, atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE):
4092 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE, atomic_value_t::type_t::ATOMIC_TYPE_SINGLE):
4093 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE, atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE):
4094 0 : result.atomic_value_t::setValue(lhs.getDoubleValue(true) * rhs.getDoubleValue(true));
4095 0 : break;
4096 :
4097 0 : default:
4098 0 : throw QDomXPathException_WrongType("the '*' operator cannot be used with the left and right hand side types");
4099 :
4100 : }
4101 0 : f_functions.back().f_stack.push_back(result);
4102 0 : }
4103 :
4104 :
4105 : /** \brief Divide two numbers from each others.
4106 : *
4107 : * This function divides two values that it first pops from the stack.
4108 : * The divide differs slightly depending on the type of each value.
4109 : * Note that dividing two integers generates a double as the result.
4110 : *
4111 : * Only integers, decimal, single, and doubles can be subtracted from
4112 : * each others.
4113 : */
4114 0 : void inst_divide()
4115 : {
4116 : #if QDOM_XPATH_VERIFICATION
4117 : // verify instruction location
4118 0 : if(f_program[f_functions.back().f_pc - 1] != INST_DIVIDE)
4119 : {
4120 0 : throw QDomXPathException_InternalError("INST_DIVIDE not at the right location in the table of instructions");
4121 : }
4122 : #endif
4123 0 : variant_t rhs(pop_variant_data());
4124 0 : variant_t lhs(pop_variant_data());
4125 0 : variant_t result;
4126 0 : switch(merge_types(lhs.getType(), rhs.getType()))
4127 : {
4128 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_INTEGER, atomic_value_t::type_t::ATOMIC_TYPE_INTEGER):
4129 0 : result.atomic_value_t::setValue(lhs.getDoubleValue(true) / rhs.getDoubleValue(true));
4130 0 : break;
4131 :
4132 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_SINGLE, atomic_value_t::type_t::ATOMIC_TYPE_INTEGER):
4133 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_INTEGER, atomic_value_t::type_t::ATOMIC_TYPE_SINGLE):
4134 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_SINGLE, atomic_value_t::type_t::ATOMIC_TYPE_SINGLE):
4135 0 : result.atomic_value_t::setValue(lhs.getSingleValue(true) / rhs.getSingleValue(true));
4136 0 : break;
4137 :
4138 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE, atomic_value_t::type_t::ATOMIC_TYPE_INTEGER):
4139 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_INTEGER, atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE):
4140 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_SINGLE, atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE):
4141 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE, atomic_value_t::type_t::ATOMIC_TYPE_SINGLE):
4142 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE, atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE):
4143 0 : result.atomic_value_t::setValue(lhs.getDoubleValue(true) / rhs.getDoubleValue(true));
4144 0 : break;
4145 :
4146 0 : default:
4147 0 : throw QDomXPathException_WrongType("the 'div' operator cannot be used with the left and right hand side types");
4148 :
4149 : }
4150 0 : f_functions.back().f_stack.push_back(result);
4151 0 : }
4152 :
4153 :
4154 : /** \brief Divide two numbers as integers.
4155 : *
4156 : * This function divides two values that it first pops from the stack.
4157 : * The divide transforms the left and right hand side numbers to integers
4158 : * before performing the division and the result is always an integer.
4159 : *
4160 : * Only integers, decimal, single, and doubles can be subtracted from
4161 : * each others.
4162 : */
4163 0 : void inst_idivide()
4164 : {
4165 : #if QDOM_XPATH_VERIFICATION
4166 : // verify instruction location
4167 0 : if(f_program[f_functions.back().f_pc - 1] != INST_IDIVIDE)
4168 : {
4169 0 : throw QDomXPathException_InternalError("INST_IDIVIDE not at the right location in the table of instructions");
4170 : }
4171 : #endif
4172 0 : variant_t rhs(pop_variant_data());
4173 0 : int64_t const right_value(rhs.getIntegerValue(true));
4174 0 : if(right_value == 0)
4175 : {
4176 0 : throw QDomXPathException_DivisionByZero("the 'idiv' operator cannot be used with the left and right hand side types");
4177 : }
4178 0 : variant_t lhs(pop_variant_data());
4179 0 : variant_t result;
4180 0 : switch(merge_types(lhs.getType(), rhs.getType()))
4181 : {
4182 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_INTEGER, atomic_value_t::type_t::ATOMIC_TYPE_INTEGER):
4183 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_SINGLE, atomic_value_t::type_t::ATOMIC_TYPE_INTEGER):
4184 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_INTEGER, atomic_value_t::type_t::ATOMIC_TYPE_SINGLE):
4185 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_SINGLE, atomic_value_t::type_t::ATOMIC_TYPE_SINGLE):
4186 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE, atomic_value_t::type_t::ATOMIC_TYPE_INTEGER):
4187 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_INTEGER, atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE):
4188 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_SINGLE, atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE):
4189 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE, atomic_value_t::type_t::ATOMIC_TYPE_SINGLE):
4190 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE, atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE):
4191 0 : result.atomic_value_t::setValue(lhs.getIntegerValue(true) / right_value);
4192 0 : break;
4193 :
4194 0 : default:
4195 0 : throw QDomXPathException_WrongType("the 'idiv' operator cannot be used with the left and right hand side types");
4196 :
4197 : }
4198 0 : f_functions.back().f_stack.push_back(result);
4199 0 : }
4200 :
4201 :
4202 : /** \brief Compute the modulo of two numbers from each others.
4203 : *
4204 : * This function computes the modulo of two number that it first pops
4205 : * from the stack. The modulo differs slightly depending on the type of
4206 : * each value.
4207 : *
4208 : * Only integers, decimal, single, and doubles can be subtracted from
4209 : * each others.
4210 : */
4211 0 : void inst_modulo()
4212 : {
4213 : #if QDOM_XPATH_VERIFICATION
4214 : // verify instruction location
4215 0 : if(f_program[f_functions.back().f_pc - 1] != INST_MODULO)
4216 : {
4217 0 : throw QDomXPathException_InternalError("INST_MODULO not at the right location in the table of instructions");
4218 : }
4219 : #endif
4220 0 : variant_t rhs(pop_variant_data());
4221 0 : variant_t lhs(pop_variant_data());
4222 0 : variant_t result;
4223 0 : switch(merge_types(lhs.getType(), rhs.getType()))
4224 : {
4225 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_INTEGER, atomic_value_t::type_t::ATOMIC_TYPE_INTEGER):
4226 0 : result.atomic_value_t::setValue(lhs.getIntegerValue() % rhs.getIntegerValue());
4227 0 : break;
4228 :
4229 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_SINGLE, atomic_value_t::type_t::ATOMIC_TYPE_INTEGER):
4230 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_INTEGER, atomic_value_t::type_t::ATOMIC_TYPE_SINGLE):
4231 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_SINGLE, atomic_value_t::type_t::ATOMIC_TYPE_SINGLE):
4232 0 : result.atomic_value_t::setValue(std::fmod(lhs.getSingleValue(true), rhs.getSingleValue(true)));
4233 0 : break;
4234 :
4235 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE, atomic_value_t::type_t::ATOMIC_TYPE_INTEGER):
4236 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_INTEGER, atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE):
4237 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_SINGLE, atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE):
4238 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE, atomic_value_t::type_t::ATOMIC_TYPE_SINGLE):
4239 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE, atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE):
4240 0 : result.atomic_value_t::setValue(std::fmod(lhs.getDoubleValue(true), rhs.getDoubleValue(true)));
4241 0 : break;
4242 :
4243 0 : default:
4244 0 : throw QDomXPathException_WrongType("the 'mod' operator cannot be used with the left and right hand side types");
4245 :
4246 : }
4247 0 : f_functions.back().f_stack.push_back(result);
4248 0 : }
4249 :
4250 :
4251 : /** \brief Inverse a Boolean.
4252 : *
4253 : * This function pops a Boolean value and returns the opposite Boolean
4254 : * (so true becomes false and vice versa.)
4255 : *
4256 : * Only Boolean are accepted by this operator.
4257 : */
4258 0 : void inst_not()
4259 : {
4260 : #if QDOM_XPATH_VERIFICATION
4261 : // verify instruction location
4262 0 : if(f_program[f_functions.back().f_pc - 1] != INST_NOT)
4263 : {
4264 0 : throw QDomXPathException_InternalError("INST_NOT not at the right location in the table of instructions");
4265 : }
4266 : #endif
4267 0 : variant_t boolean(pop_variant_data());
4268 0 : if(boolean.getType() != atomic_value_t::type_t::ATOMIC_TYPE_BOOLEAN)
4269 : {
4270 0 : throw QDomXPathException_WrongType("the Not operator can only be applied against a Boolean value");
4271 : }
4272 0 : boolean.atomic_value_t::setValue(!boolean.atomic_value_t::getBooleanValue());
4273 :
4274 0 : f_functions.back().f_stack.push_back(boolean);
4275 0 : }
4276 :
4277 :
4278 : /** \brief Compute the multiplication of two Booleans.
4279 : *
4280 : * This function pops two Boolean values and returns the AND operation
4281 : * (i.e. Boolean multiplication) so if both sides are true, it pushes
4282 : * true back on the stack. In all other cases it pushes false.
4283 : *
4284 : * Only Boolean are accepted by this operator.
4285 : */
4286 0 : void inst_and()
4287 : {
4288 : #if QDOM_XPATH_VERIFICATION
4289 : // verify instruction location
4290 0 : if(f_program[f_functions.back().f_pc - 1] != INST_AND)
4291 : {
4292 0 : throw QDomXPathException_InternalError("INST_AND not at the right location in the table of instructions");
4293 : }
4294 : #endif
4295 0 : variant_t rhs(pop_variant_data());
4296 0 : variant_t lhs(pop_variant_data());
4297 0 : if(lhs.getType() != atomic_value_t::type_t::ATOMIC_TYPE_BOOLEAN
4298 0 : || rhs.getType() != atomic_value_t::type_t::ATOMIC_TYPE_BOOLEAN)
4299 : {
4300 0 : throw QDomXPathException_WrongType("the And operator can only be applied against Boolean values");
4301 : }
4302 0 : variant_t result;
4303 0 : result.atomic_value_t::setValue(lhs.atomic_value_t::getBooleanValue() && rhs.atomic_value_t::getBooleanValue());
4304 :
4305 0 : f_functions.back().f_stack.push_back(result);
4306 0 : }
4307 :
4308 :
4309 : /** \brief Compute the addition of two Booleans.
4310 : *
4311 : * This function pops two Boolean values and returns the OR operation
4312 : * (i.e. Boolean addition) so if either sides is true, it pushes
4313 : * true back on the stack. In all other cases it pushes false.
4314 : *
4315 : * Only Boolean are accepted by this operator.
4316 : */
4317 0 : void inst_or()
4318 : {
4319 : #if QDOM_XPATH_VERIFICATION
4320 : // verify instruction location
4321 0 : if(f_program[f_functions.back().f_pc - 1] != INST_OR)
4322 : {
4323 0 : throw QDomXPathException_InternalError("INST_OR not at the right location in the table of instructions");
4324 : }
4325 : #endif
4326 0 : variant_t rhs(pop_variant_data());
4327 0 : variant_t lhs(pop_variant_data());
4328 0 : if(lhs.getType() != atomic_value_t::type_t::ATOMIC_TYPE_BOOLEAN
4329 0 : || rhs.getType() != atomic_value_t::type_t::ATOMIC_TYPE_BOOLEAN)
4330 : {
4331 0 : throw QDomXPathException_WrongType("the Or operator can only be applied against Boolean values");
4332 : }
4333 0 : variant_t result;
4334 0 : result.atomic_value_t::setValue(lhs.atomic_value_t::getBooleanValue() || rhs.atomic_value_t::getBooleanValue());
4335 :
4336 0 : f_functions.back().f_stack.push_back(result);
4337 0 : }
4338 :
4339 :
4340 : /** \brief Check whether two values are equal.
4341 : *
4342 : * This function compares the two top-most values from the stack.
4343 : * The result is pushed back on the stack.
4344 : *
4345 : * The comparison process depends on the type of data being compared.
4346 : */
4347 0 : void inst_equal()
4348 : {
4349 : #if QDOM_XPATH_VERIFICATION
4350 : // verify instruction location
4351 0 : if(f_program[f_functions.back().f_pc - 1] != INST_EQUAL)
4352 : {
4353 0 : throw QDomXPathException_InternalError("INST_EQUAL not at the right location in the table of instructions");
4354 : }
4355 : #endif
4356 0 : variant_t rhs(pop_variant_data());
4357 0 : variant_t lhs(pop_variant_data());
4358 0 : variant_t result;
4359 0 : switch(merge_types(lhs.getType(), rhs.getType()))
4360 : {
4361 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_BOOLEAN, atomic_value_t::type_t::ATOMIC_TYPE_BOOLEAN):
4362 0 : result.atomic_value_t::setValue(lhs.atomic_value_t::getBooleanValue() == rhs.atomic_value_t::getBooleanValue());
4363 0 : break;
4364 :
4365 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_INTEGER, atomic_value_t::type_t::ATOMIC_TYPE_INTEGER):
4366 0 : result.atomic_value_t::setValue(lhs.atomic_value_t::getIntegerValue() == rhs.atomic_value_t::getIntegerValue());
4367 0 : break;
4368 :
4369 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_SINGLE, atomic_value_t::type_t::ATOMIC_TYPE_INTEGER):
4370 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_INTEGER, atomic_value_t::type_t::ATOMIC_TYPE_SINGLE):
4371 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_SINGLE, atomic_value_t::type_t::ATOMIC_TYPE_SINGLE):
4372 0 : result.atomic_value_t::setValue(snap::compare_floats(lhs.atomic_value_t::getSingleValue(true), rhs.atomic_value_t::getSingleValue(true)));
4373 0 : break;
4374 :
4375 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE, atomic_value_t::type_t::ATOMIC_TYPE_INTEGER):
4376 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_INTEGER, atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE):
4377 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_SINGLE, atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE):
4378 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE, atomic_value_t::type_t::ATOMIC_TYPE_SINGLE):
4379 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE, atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE):
4380 0 : result.atomic_value_t::setValue(snap::compare_floats(lhs.atomic_value_t::getDoubleValue(true), rhs.atomic_value_t::getDoubleValue(true)));
4381 0 : break;
4382 :
4383 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_STRING, atomic_value_t::type_t::ATOMIC_TYPE_STRING):
4384 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_NODE_SET, atomic_value_t::type_t::ATOMIC_TYPE_STRING):
4385 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_STRING, atomic_value_t::type_t::ATOMIC_TYPE_NODE_SET):
4386 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_NODE_SET, atomic_value_t::type_t::ATOMIC_TYPE_NODE_SET):
4387 : try
4388 : {
4389 0 : result.atomic_value_t::setValue(lhs.getStringValue(true) == rhs.getStringValue(true));
4390 : }
4391 0 : catch(QDomXPathException_NotImplemented const&)
4392 : {
4393 0 : result.atomic_value_t::setValue(false);
4394 : }
4395 0 : break;
4396 :
4397 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_NULL, atomic_value_t::type_t::ATOMIC_TYPE_NULL):
4398 0 : result.atomic_value_t::setValue(true);
4399 0 : break;
4400 :
4401 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_NULL, atomic_value_t::type_t::ATOMIC_TYPE_STRING):
4402 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_NULL, atomic_value_t::type_t::ATOMIC_TYPE_INTEGER):
4403 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_NULL, atomic_value_t::type_t::ATOMIC_TYPE_SINGLE):
4404 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_NULL, atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE):
4405 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_NULL, atomic_value_t::type_t::ATOMIC_TYPE_BOOLEAN):
4406 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_STRING, atomic_value_t::type_t::ATOMIC_TYPE_BOOLEAN):
4407 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_INTEGER, atomic_value_t::type_t::ATOMIC_TYPE_BOOLEAN):
4408 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_SINGLE, atomic_value_t::type_t::ATOMIC_TYPE_BOOLEAN):
4409 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE, atomic_value_t::type_t::ATOMIC_TYPE_BOOLEAN):
4410 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_STRING, atomic_value_t::type_t::ATOMIC_TYPE_NULL):
4411 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_INTEGER, atomic_value_t::type_t::ATOMIC_TYPE_NULL):
4412 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_SINGLE, atomic_value_t::type_t::ATOMIC_TYPE_NULL):
4413 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE, atomic_value_t::type_t::ATOMIC_TYPE_NULL):
4414 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_BOOLEAN, atomic_value_t::type_t::ATOMIC_TYPE_NULL):
4415 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_BOOLEAN, atomic_value_t::type_t::ATOMIC_TYPE_STRING):
4416 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_BOOLEAN, atomic_value_t::type_t::ATOMIC_TYPE_INTEGER):
4417 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_BOOLEAN, atomic_value_t::type_t::ATOMIC_TYPE_SINGLE):
4418 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_BOOLEAN, atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE):
4419 0 : result.atomic_value_t::setValue(false);
4420 0 : break;
4421 :
4422 0 : default:
4423 0 : throw QDomXPathException_WrongType("the '=' operator cannot be used with the left and right hand side types");
4424 :
4425 : }
4426 0 : f_functions.back().f_stack.push_back(result);
4427 0 : }
4428 :
4429 :
4430 : /** \brief Check whether two values are not equal.
4431 : *
4432 : * This function compares the two top-most values from the stack.
4433 : * The result is pushed back on the stack.
4434 : *
4435 : * Note that not-equal is not the same as not(equal). Some data may be at
4436 : * the same time equal and not equal or neither.
4437 : */
4438 0 : void inst_not_equal()
4439 : {
4440 : #if QDOM_XPATH_VERIFICATION
4441 : // verify instruction location
4442 0 : if(f_program[f_functions.back().f_pc - 1] != INST_NOT_EQUAL)
4443 : {
4444 0 : throw QDomXPathException_InternalError("INST_NOT_EQUAL not at the right location in the table of instructions");
4445 : }
4446 : #endif
4447 0 : variant_t rhs(pop_variant_data());
4448 0 : variant_t lhs(pop_variant_data());
4449 0 : variant_t result;
4450 0 : switch(merge_types(lhs.getType(), rhs.getType()))
4451 : {
4452 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_BOOLEAN, atomic_value_t::type_t::ATOMIC_TYPE_BOOLEAN):
4453 0 : result.atomic_value_t::setValue(lhs.getBooleanValue() != rhs.getBooleanValue());
4454 0 : break;
4455 :
4456 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_INTEGER, atomic_value_t::type_t::ATOMIC_TYPE_INTEGER):
4457 0 : result.atomic_value_t::setValue(lhs.getIntegerValue() != rhs.getIntegerValue());
4458 0 : break;
4459 :
4460 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_SINGLE, atomic_value_t::type_t::ATOMIC_TYPE_INTEGER):
4461 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_INTEGER, atomic_value_t::type_t::ATOMIC_TYPE_SINGLE):
4462 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_SINGLE, atomic_value_t::type_t::ATOMIC_TYPE_SINGLE):
4463 0 : result.atomic_value_t::setValue(snap::compare_floats(lhs.getSingleValue(true), rhs.getSingleValue(true)));
4464 0 : break;
4465 :
4466 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE, atomic_value_t::type_t::ATOMIC_TYPE_INTEGER):
4467 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_INTEGER, atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE):
4468 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_SINGLE, atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE):
4469 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE, atomic_value_t::type_t::ATOMIC_TYPE_SINGLE):
4470 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE, atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE):
4471 0 : result.atomic_value_t::setValue(snap::compare_floats(lhs.getDoubleValue(true), rhs.getDoubleValue(true)));
4472 0 : break;
4473 :
4474 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_STRING, atomic_value_t::type_t::ATOMIC_TYPE_STRING):
4475 0 : result.atomic_value_t::setValue(lhs.getStringValue() != rhs.getStringValue());
4476 0 : break;
4477 :
4478 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_NULL, atomic_value_t::type_t::ATOMIC_TYPE_NULL):
4479 0 : result.atomic_value_t::setValue(false);
4480 0 : break;
4481 :
4482 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_NULL, atomic_value_t::type_t::ATOMIC_TYPE_STRING):
4483 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_NULL, atomic_value_t::type_t::ATOMIC_TYPE_INTEGER):
4484 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_NULL, atomic_value_t::type_t::ATOMIC_TYPE_SINGLE):
4485 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_NULL, atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE):
4486 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_NULL, atomic_value_t::type_t::ATOMIC_TYPE_BOOLEAN):
4487 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_STRING , atomic_value_t::type_t::ATOMIC_TYPE_BOOLEAN):
4488 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_INTEGER, atomic_value_t::type_t::ATOMIC_TYPE_BOOLEAN):
4489 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_SINGLE, atomic_value_t::type_t::ATOMIC_TYPE_BOOLEAN):
4490 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE, atomic_value_t::type_t::ATOMIC_TYPE_BOOLEAN):
4491 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_STRING, atomic_value_t::type_t::ATOMIC_TYPE_NULL):
4492 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_INTEGER, atomic_value_t::type_t::ATOMIC_TYPE_NULL):
4493 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_SINGLE, atomic_value_t::type_t::ATOMIC_TYPE_NULL):
4494 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE, atomic_value_t::type_t::ATOMIC_TYPE_NULL):
4495 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_BOOLEAN, atomic_value_t::type_t::ATOMIC_TYPE_NULL):
4496 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_BOOLEAN, atomic_value_t::type_t::ATOMIC_TYPE_STRING):
4497 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_BOOLEAN, atomic_value_t::type_t::ATOMIC_TYPE_INTEGER):
4498 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_BOOLEAN, atomic_value_t::type_t::ATOMIC_TYPE_SINGLE):
4499 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_BOOLEAN, atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE):
4500 0 : result.atomic_value_t::setValue(true);
4501 0 : break;
4502 :
4503 0 : default:
4504 0 : throw QDomXPathException_WrongType("the '!=' operator cannot be used with the left and right hand side types");
4505 :
4506 : }
4507 0 : f_functions.back().f_stack.push_back(result);
4508 0 : }
4509 :
4510 :
4511 : /** \brief Check whether two values are ordered.
4512 : *
4513 : * This function compares the two top-most values on the stack.
4514 : * The result is pushed back on the stack.
4515 : */
4516 0 : void inst_less_than()
4517 : {
4518 : #if QDOM_XPATH_VERIFICATION
4519 : // verify instruction location
4520 0 : if(f_program[f_functions.back().f_pc - 1] != INST_LESS_THAN)
4521 : {
4522 0 : throw QDomXPathException_InternalError("INST_LESS_THAN not at the right location in the table of instructions");
4523 : }
4524 : #endif
4525 0 : variant_t rhs(pop_variant_data());
4526 0 : variant_t lhs(pop_variant_data());
4527 0 : variant_t result;
4528 0 : switch(merge_types(lhs.getType(), rhs.getType()))
4529 : {
4530 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_BOOLEAN, atomic_value_t::type_t::ATOMIC_TYPE_BOOLEAN):
4531 0 : result.atomic_value_t::setValue(lhs.getBooleanValue() < rhs.getBooleanValue());
4532 0 : break;
4533 :
4534 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_INTEGER, atomic_value_t::type_t::ATOMIC_TYPE_INTEGER):
4535 0 : result.atomic_value_t::setValue(lhs.getIntegerValue() < rhs.getIntegerValue());
4536 0 : break;
4537 :
4538 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_SINGLE, atomic_value_t::type_t::ATOMIC_TYPE_INTEGER):
4539 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_INTEGER, atomic_value_t::type_t::ATOMIC_TYPE_SINGLE):
4540 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_SINGLE, atomic_value_t::type_t::ATOMIC_TYPE_SINGLE):
4541 0 : result.atomic_value_t::setValue(lhs.getSingleValue(true) < rhs.getSingleValue(true));
4542 0 : break;
4543 :
4544 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE, atomic_value_t::type_t::ATOMIC_TYPE_INTEGER):
4545 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_INTEGER, atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE):
4546 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_SINGLE, atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE):
4547 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE, atomic_value_t::type_t::ATOMIC_TYPE_SINGLE):
4548 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE, atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE):
4549 0 : result.atomic_value_t::setValue(lhs.getDoubleValue(true) < rhs.getDoubleValue(true));
4550 0 : break;
4551 :
4552 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_STRING, atomic_value_t::type_t::ATOMIC_TYPE_STRING):
4553 0 : result.atomic_value_t::setValue(lhs.getStringValue() < rhs.getStringValue());
4554 0 : break;
4555 :
4556 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_NULL, atomic_value_t::type_t::ATOMIC_TYPE_NULL):
4557 0 : result.atomic_value_t::setValue(false);
4558 0 : break;
4559 :
4560 0 : default:
4561 0 : throw QDomXPathException_WrongType("the '<' operator cannot be used with the left and right hand side types");
4562 :
4563 : }
4564 0 : f_functions.back().f_stack.push_back(result);
4565 0 : }
4566 :
4567 :
4568 : /** \brief Check whether two values are ordered.
4569 : *
4570 : * This function compares the two top-most values on the stack.
4571 : * The result is pushed back on the stack.
4572 : */
4573 0 : void inst_less_or_equal()
4574 : {
4575 : #if QDOM_XPATH_VERIFICATION
4576 : // verify instruction location
4577 0 : if(f_program[f_functions.back().f_pc - 1] != INST_LESS_OR_EQUAL)
4578 : {
4579 0 : throw QDomXPathException_InternalError("INST_LESS_OR_EQUAL not at the right location in the table of instructions");
4580 : }
4581 : #endif
4582 0 : variant_t rhs(pop_variant_data());
4583 0 : variant_t lhs(pop_variant_data());
4584 0 : variant_t result;
4585 0 : switch(merge_types(lhs.getType(), rhs.getType()))
4586 : {
4587 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_BOOLEAN, atomic_value_t::type_t::ATOMIC_TYPE_BOOLEAN):
4588 0 : result.atomic_value_t::setValue(lhs.getBooleanValue() <= rhs.getBooleanValue());
4589 0 : break;
4590 :
4591 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_INTEGER, atomic_value_t::type_t::ATOMIC_TYPE_INTEGER):
4592 0 : result.atomic_value_t::setValue(lhs.getIntegerValue() <= rhs.getIntegerValue());
4593 0 : break;
4594 :
4595 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_SINGLE, atomic_value_t::type_t::ATOMIC_TYPE_INTEGER):
4596 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_INTEGER, atomic_value_t::type_t::ATOMIC_TYPE_SINGLE):
4597 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_SINGLE, atomic_value_t::type_t::ATOMIC_TYPE_SINGLE):
4598 0 : result.atomic_value_t::setValue(lhs.getSingleValue(true) <= rhs.getSingleValue(true));
4599 0 : break;
4600 :
4601 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE, atomic_value_t::type_t::ATOMIC_TYPE_INTEGER):
4602 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_INTEGER, atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE):
4603 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_SINGLE, atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE):
4604 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE, atomic_value_t::type_t::ATOMIC_TYPE_SINGLE):
4605 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE, atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE):
4606 0 : result.atomic_value_t::setValue(lhs.getDoubleValue(true) <= rhs.getDoubleValue(true));
4607 0 : break;
4608 :
4609 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_STRING, atomic_value_t::type_t::ATOMIC_TYPE_STRING):
4610 0 : result.atomic_value_t::setValue(lhs.getStringValue() <= rhs.getStringValue());
4611 0 : break;
4612 :
4613 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_NULL, atomic_value_t::type_t::ATOMIC_TYPE_NULL):
4614 0 : result.atomic_value_t::setValue(true);
4615 0 : break;
4616 :
4617 0 : default:
4618 0 : throw QDomXPathException_WrongType("the '<=' operator cannot be used with the left and right hand side types");
4619 :
4620 : }
4621 0 : f_functions.back().f_stack.push_back(result);
4622 0 : }
4623 :
4624 :
4625 : /** \brief Check whether two values are ordered.
4626 : *
4627 : * This function compares the two top-most values on the stack.
4628 : * The result is pushed back on the stack.
4629 : */
4630 0 : void inst_greater_than()
4631 : {
4632 : #if QDOM_XPATH_VERIFICATION
4633 : // verify instruction location
4634 0 : if(f_program[f_functions.back().f_pc - 1] != INST_GREATER_THAN)
4635 : {
4636 0 : throw QDomXPathException_InternalError("INST_GREATER_THAN not at the right location in the table of instructions");
4637 : }
4638 : #endif
4639 0 : variant_t rhs(pop_variant_data());
4640 0 : variant_t lhs(pop_variant_data());
4641 0 : variant_t result;
4642 0 : switch(merge_types(lhs.getType(), rhs.getType()))
4643 : {
4644 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_BOOLEAN, atomic_value_t::type_t::ATOMIC_TYPE_BOOLEAN):
4645 0 : result.atomic_value_t::setValue(lhs.getBooleanValue() > rhs.getBooleanValue());
4646 0 : break;
4647 :
4648 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_INTEGER, atomic_value_t::type_t::ATOMIC_TYPE_INTEGER):
4649 0 : result.atomic_value_t::setValue(lhs.getIntegerValue() > rhs.getIntegerValue());
4650 0 : break;
4651 :
4652 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_SINGLE, atomic_value_t::type_t::ATOMIC_TYPE_INTEGER):
4653 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_INTEGER, atomic_value_t::type_t::ATOMIC_TYPE_SINGLE):
4654 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_SINGLE, atomic_value_t::type_t::ATOMIC_TYPE_SINGLE):
4655 0 : result.atomic_value_t::setValue(lhs.getSingleValue(true) > rhs.getSingleValue(true));
4656 0 : break;
4657 :
4658 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE, atomic_value_t::type_t::ATOMIC_TYPE_INTEGER):
4659 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_INTEGER, atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE):
4660 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_SINGLE, atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE):
4661 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE, atomic_value_t::type_t::ATOMIC_TYPE_SINGLE):
4662 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE, atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE):
4663 0 : result.atomic_value_t::setValue(lhs.getDoubleValue(true) > rhs.getDoubleValue(true));
4664 0 : break;
4665 :
4666 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_STRING, atomic_value_t::type_t::ATOMIC_TYPE_STRING):
4667 0 : result.atomic_value_t::setValue(lhs.getStringValue() > rhs.getStringValue());
4668 0 : break;
4669 :
4670 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_NULL, atomic_value_t::type_t::ATOMIC_TYPE_NULL):
4671 0 : result.atomic_value_t::setValue(false);
4672 0 : break;
4673 :
4674 0 : default:
4675 0 : throw QDomXPathException_WrongType("the '>' operator cannot be used with the left and right hand side types");
4676 :
4677 : }
4678 0 : f_functions.back().f_stack.push_back(result);
4679 0 : }
4680 :
4681 :
4682 : /** \brief Check whether two values are ordered.
4683 : *
4684 : * This function compares the two top-most values on the stack.
4685 : * The result is pushed back on the stack.
4686 : */
4687 0 : void inst_greater_or_equal()
4688 : {
4689 : #if QDOM_XPATH_VERIFICATION
4690 : // verify instruction location
4691 0 : if(f_program[f_functions.back().f_pc - 1] != INST_GREATER_OR_EQUAL)
4692 : {
4693 0 : throw QDomXPathException_InternalError("INST_GREATER_OR_EQUAL not at the right location in the table of instructions");
4694 : }
4695 : #endif
4696 0 : variant_t rhs(pop_variant_data());
4697 0 : variant_t lhs(pop_variant_data());
4698 0 : variant_t result;
4699 0 : switch(merge_types(lhs.getType(), rhs.getType()))
4700 : {
4701 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_BOOLEAN, atomic_value_t::type_t::ATOMIC_TYPE_BOOLEAN):
4702 0 : result.atomic_value_t::setValue(lhs.getBooleanValue() >= rhs.getBooleanValue());
4703 0 : break;
4704 :
4705 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_INTEGER, atomic_value_t::type_t::ATOMIC_TYPE_INTEGER):
4706 0 : result.atomic_value_t::setValue(lhs.getIntegerValue() >= rhs.getIntegerValue());
4707 0 : break;
4708 :
4709 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_SINGLE, atomic_value_t::type_t::ATOMIC_TYPE_INTEGER):
4710 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_INTEGER, atomic_value_t::type_t::ATOMIC_TYPE_SINGLE):
4711 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_SINGLE, atomic_value_t::type_t::ATOMIC_TYPE_SINGLE):
4712 0 : result.atomic_value_t::setValue(lhs.getSingleValue(true) >= rhs.getSingleValue(true));
4713 0 : break;
4714 :
4715 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE, atomic_value_t::type_t::ATOMIC_TYPE_INTEGER):
4716 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_INTEGER, atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE):
4717 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_SINGLE, atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE):
4718 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE, atomic_value_t::type_t::ATOMIC_TYPE_SINGLE):
4719 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE, atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE):
4720 0 : result.atomic_value_t::setValue(lhs.getDoubleValue(true) >= rhs.getDoubleValue(true));
4721 0 : break;
4722 :
4723 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_STRING, atomic_value_t::type_t::ATOMIC_TYPE_STRING):
4724 0 : result.atomic_value_t::setValue(lhs.getStringValue() >= rhs.getStringValue());
4725 0 : break;
4726 :
4727 0 : case merge_types(atomic_value_t::type_t::ATOMIC_TYPE_NULL, atomic_value_t::type_t::ATOMIC_TYPE_NULL):
4728 0 : result.atomic_value_t::setValue(true);
4729 0 : break;
4730 :
4731 0 : default:
4732 0 : throw QDomXPathException_WrongType("the '>=' operator cannot be used with the left and right hand side types");
4733 :
4734 : }
4735 0 : f_functions.back().f_stack.push_back(result);
4736 0 : }
4737 :
4738 :
4739 : /** \brief Get node-set size.
4740 : *
4741 : * This function pushes the size of the node-set on the stack.
4742 : */
4743 0 : void inst_node_set_size()
4744 : {
4745 : #if QDOM_XPATH_VERIFICATION
4746 : // verify instruction location
4747 0 : if(f_program[f_functions.back().f_pc - 1] != INST_NODE_SET_SIZE)
4748 : {
4749 0 : throw QDomXPathException_InternalError("INST_NODE_SET_SIZE not at the right location in the table of instructions");
4750 : }
4751 : #endif
4752 0 : variant_t value(pop_variant_data());
4753 0 : QDomXPath::node_vector_t node_set(value.getNodeSetValue());
4754 0 : variant_t result;
4755 0 : result.atomic_value_t::setValue(static_cast<int64_t>(node_set.size()));
4756 0 : f_functions.back().f_stack.push_back(result);
4757 0 : }
4758 :
4759 :
4760 : /** \brief Append two node-sets or atomic sets together.
4761 : *
4762 : * This function pops two sets from the stack, append one to the other,
4763 : * and pushes the result back on the stack.
4764 : */
4765 0 : void inst_merge_sets()
4766 : {
4767 : #if QDOM_XPATH_VERIFICATION
4768 : // verify instruction location
4769 0 : if(f_program[f_functions.back().f_pc - 1] != INST_MERGE_SETS)
4770 : {
4771 0 : throw QDomXPathException_InternalError("INST_MERGE_SETS not at the right location in the table of instructions");
4772 : }
4773 : #endif
4774 0 : variant_t rhs(pop_variant_data());
4775 0 : variant_t lhs(pop_variant_data());
4776 0 : if(rhs.getType() == atomic_value_t::type_t::ATOMIC_TYPE_NODE_SET
4777 0 : && lhs.getType() == atomic_value_t::type_t::ATOMIC_TYPE_NODE_SET)
4778 : {
4779 0 : QDomXPath::node_vector_t l(lhs.getNodeSetValue());
4780 0 : QDomXPath::node_vector_t r(rhs.getNodeSetValue());
4781 0 : const int imax(r.size());
4782 0 : for(int i(0); i < imax; ++i)
4783 : {
4784 0 : l.push_back(r[i]);
4785 : }
4786 0 : variant_t result;
4787 0 : result.setValue(l);
4788 0 : f_functions.back().f_stack.push_back(result);
4789 : }
4790 0 : else if(rhs.getType() == atomic_value_t::type_t::ATOMIC_TYPE_SET
4791 0 : && lhs.getType() == atomic_value_t::type_t::ATOMIC_TYPE_SET)
4792 : {
4793 0 : atomic_vector_t l(lhs.getSetValue());
4794 0 : atomic_vector_t r(rhs.getSetValue());
4795 0 : const int imax(r.size());
4796 0 : for(int i(0); i < imax; ++i)
4797 : {
4798 0 : l.push_back(r[i]);
4799 : }
4800 0 : variant_t result;
4801 0 : result.setValue(l);
4802 0 : f_functions.back().f_stack.push_back(result);
4803 : }
4804 : else
4805 : {
4806 0 : throw QDomXPathException_WrongType("the 'union' operator cannot be used with anything else than node sets at this point");
4807 : }
4808 0 : }
4809 :
4810 :
4811 : /** \brief Retrieve the current position.
4812 : *
4813 : * This function pushes the current position of the context node
4814 : * on the stack.
4815 : */
4816 0 : void inst_get_position()
4817 : {
4818 : #if QDOM_XPATH_VERIFICATION
4819 : // verify instruction location
4820 0 : if(f_program[f_functions.back().f_pc - 1] != INST_GET_POSITION)
4821 : {
4822 0 : throw QDomXPathException_InternalError("INST_GET_POSITION not at the right location in the table of instructions");
4823 : }
4824 : #endif
4825 0 : contexts_not_empty();
4826 0 : variant_t result;
4827 0 : int64_t position(static_cast<int64_t>(f_functions.back().f_contexts.back().f_position));
4828 0 : result.atomic_value_t::setValue(position + 1);
4829 0 : f_functions.back().f_stack.push_back(result);
4830 0 : }
4831 :
4832 :
4833 : /** \brief Set the position.
4834 : *
4835 : * This internal function is used to set the current position. Internally
4836 : * we use those to go through the whole list of nodes in a node-set.
4837 : */
4838 0 : void inst_set_position()
4839 : {
4840 : #if QDOM_XPATH_VERIFICATION
4841 : // verify instruction location
4842 0 : if(f_program[f_functions.back().f_pc - 1] != INST_SET_POSITION)
4843 : {
4844 0 : throw QDomXPathException_InternalError("INST_SET_POSITION not at the right location in the table of instructions");
4845 : }
4846 : #endif
4847 0 : variant_t position(pop_variant_data());
4848 0 : if(position.getType() != atomic_value_t::type_t::ATOMIC_TYPE_INTEGER)
4849 : {
4850 0 : throw QDomXPathException_WrongType("the 'set_position' operator cannot be used with anything else than an integer as its first operand");
4851 : }
4852 0 : contexts_not_empty();
4853 0 : int p(static_cast<int>(position.getIntegerValue()));
4854 0 : if(p < 1 || p > f_functions.back().f_contexts.back().f_nodes.size())
4855 : {
4856 0 : throw QDomXPathException_OutOfRange("the new position in 'set_position' is out of range");
4857 : }
4858 : // within XPath position is 1 based
4859 0 : f_functions.back().f_contexts.back().f_position = p - 1;
4860 0 : }
4861 :
4862 :
4863 : /** \brief Get the node-set on the stack.
4864 : *
4865 : * This function retrieves the current node-set from this state.
4866 : */
4867 0 : void inst_get_node_set()
4868 : {
4869 : #if QDOM_XPATH_VERIFICATION
4870 : // verify instruction location
4871 0 : if(f_program[f_functions.back().f_pc - 1] != INST_GET_NODE_SET)
4872 : {
4873 0 : throw QDomXPathException_InternalError("INST_GET_NODE_SET not at the right location in the table of instructions");
4874 : }
4875 : #endif
4876 0 : contexts_not_empty();
4877 0 : variant_t result;
4878 0 : result.setValue(f_functions.back().f_contexts.back().f_nodes);
4879 0 : f_functions.back().f_stack.push_back(result);
4880 0 : }
4881 :
4882 :
4883 : /** \brief Set the node-set from the stack.
4884 : *
4885 : * This function saves the stack node-set as the new node-set of this state.
4886 : */
4887 0 : void inst_set_node_set()
4888 : {
4889 : #if QDOM_XPATH_VERIFICATION
4890 : // verify instruction location
4891 0 : if(f_program[f_functions.back().f_pc - 1] != INST_SET_NODE_SET)
4892 : {
4893 0 : throw QDomXPathException_InternalError("INST_SET_NODE_SET not at the right location in the table of instructions");
4894 : }
4895 : #endif
4896 0 : variant_t node_set(pop_variant_data());
4897 0 : if(node_set.getType() != atomic_value_t::type_t::ATOMIC_TYPE_NODE_SET)
4898 : {
4899 0 : throw QDomXPathException_WrongType("the 'set_node_set' operator cannot be used with anything else than a node-set");
4900 : }
4901 0 : contexts_not_empty();
4902 0 : f_functions.back().f_contexts.back().f_nodes = node_set.getNodeSetValue();
4903 0 : }
4904 :
4905 :
4906 : /** \brief Get the result on the stack.
4907 : *
4908 : * This function retrieves the current result on this state.
4909 : */
4910 0 : void inst_get_result()
4911 : {
4912 : #if QDOM_XPATH_VERIFICATION
4913 : // verify instruction location
4914 0 : if(f_program[f_functions.back().f_pc - 1] != INST_GET_RESULT)
4915 : {
4916 0 : throw QDomXPathException_InternalError("INST_GET_RESULT not at the right location in the table of instructions");
4917 : }
4918 : #endif
4919 0 : contexts_not_empty();
4920 0 : variant_t result;
4921 0 : result.setValue(f_functions.back().f_contexts.back().f_result);
4922 0 : f_functions.back().f_stack.push_back(result);
4923 0 : }
4924 :
4925 :
4926 : /** \brief Set the result on the stack.
4927 : *
4928 : * This function saves the stack node-set as the new result of this state.
4929 : */
4930 0 : void inst_set_result()
4931 : {
4932 : #if QDOM_XPATH_VERIFICATION
4933 : // verify instruction location
4934 0 : if(f_program[f_functions.back().f_pc - 1] != INST_SET_RESULT)
4935 : {
4936 0 : throw QDomXPathException_InternalError("INST_SET_RESULT not at the right location in the table of instructions");
4937 : }
4938 : #endif
4939 0 : variant_t result(pop_variant_data());
4940 0 : if(result.getType() != atomic_value_t::type_t::ATOMIC_TYPE_NODE_SET)
4941 : {
4942 0 : throw QDomXPathException_WrongType("the 'set_result' operator cannot be used with anything else than a node-set");
4943 : }
4944 0 : contexts_not_empty();
4945 0 : f_functions.back().f_contexts.back().f_result = result.getNodeSetValue();
4946 0 : }
4947 :
4948 :
4949 : /** \brief Replace the node-set with the root node.
4950 : *
4951 : * This function clears the existing node-set and replace it with one
4952 : * node: the root node.
4953 : */
4954 0 : void inst_root()
4955 : {
4956 : #if QDOM_XPATH_VERIFICATION
4957 : // verify instruction location
4958 0 : if(f_program[f_functions.back().f_pc - 1] != INST_ROOT)
4959 : {
4960 0 : throw QDomXPathException_InternalError("INST_ROOT not at the right location in the table of instructions");
4961 : }
4962 : #endif
4963 : // if empty then it stays that way
4964 0 : stack_not_empty(atomic_value_t::type_t::ATOMIC_TYPE_NODE_SET);
4965 0 : QDomXPath::node_vector_t& node_set(f_functions.back().f_stack.back().getNodeSetValue());
4966 0 : if(!node_set.isEmpty())
4967 : {
4968 0 : QDomNode root(node_set[0].ownerDocument());
4969 0 : if(root.isElement())
4970 : {
4971 : // this happens when the node we start with is an attribute
4972 0 : root = root.ownerDocument();
4973 : }
4974 0 : node_set.clear();
4975 0 : if(!root.isNull())
4976 : {
4977 : // ignore null nodes and keep our list empty of them
4978 0 : node_set.push_back(root);
4979 : }
4980 : }
4981 0 : }
4982 :
4983 :
4984 : /** \brief Check the current result as the predicate result.
4985 : *
4986 : * This function checks the last result on the stack as the result of
4987 : * a predicate. If true, the current node is added to the result set.
4988 : */
4989 0 : void inst_predicate()
4990 : {
4991 : #if QDOM_XPATH_VERIFICATION
4992 : // verify instruction location
4993 0 : if(f_program[f_functions.back().f_pc - 1] != INST_PREDICATE)
4994 : {
4995 0 : throw QDomXPathException_InternalError("INST_PREDICATE not at the right location in the table of instructions");
4996 : }
4997 : #endif
4998 0 : bool result(false);
4999 0 : variant_t predicate_result(pop_variant_data());
5000 0 : switch(predicate_result.getType())
5001 : {
5002 0 : case atomic_value_t::type_t::ATOMIC_TYPE_BOOLEAN:
5003 : case atomic_value_t::type_t::ATOMIC_TYPE_STRING:
5004 : // true or false
5005 0 : result = predicate_result.getBooleanValue(true);
5006 0 : break;
5007 :
5008 0 : case atomic_value_t::type_t::ATOMIC_TYPE_INTEGER:
5009 : case atomic_value_t::type_t::ATOMIC_TYPE_SINGLE:
5010 : case atomic_value_t::type_t::ATOMIC_TYPE_DOUBLE:
5011 0 : contexts_not_empty();
5012 0 : result = predicate_result.getIntegerValue(true) == f_functions.back().f_contexts.back().f_position + 1;
5013 0 : break;
5014 :
5015 0 : case atomic_value_t::type_t::ATOMIC_TYPE_NODE_SET:
5016 : {
5017 : // for a node set, it is considered true only if not empty
5018 0 : QDomXPath::node_vector_t r(predicate_result.getNodeSetValue());
5019 0 : result = r.size() != 0;
5020 : }
5021 0 : break;
5022 :
5023 0 : default:
5024 : // anything else always returns false
5025 : //throw QDomXPathException_WrongType(QString("the 'predicate' operator cannot be used with anything else than a node-set (got node of type %1)").arg(static_cast<int>(predicate_result.getType())));
5026 0 : break;
5027 :
5028 : }
5029 :
5030 0 : context_vector_t::reference context(f_functions.back().f_contexts.back());
5031 0 : if(context.f_position != -1
5032 0 : && result)
5033 : {
5034 0 : context.f_result.push_back(context.f_nodes[context.f_position]);
5035 : }
5036 :
5037 : // if +1 to position is too large, just return false
5038 0 : const bool has_another_position(context.f_position + 1 < context.f_nodes.size());
5039 0 : if(has_another_position)
5040 : {
5041 0 : ++context.f_position;
5042 : }
5043 :
5044 0 : variant_t value;
5045 0 : value.atomic_value_t::setValue(has_another_position);
5046 0 : f_functions.back().f_stack.push_back(value);
5047 0 : }
5048 :
5049 :
5050 0 : void inst_create_node_context()
5051 : {
5052 : #if QDOM_XPATH_VERIFICATION
5053 : // verify instruction location
5054 0 : if(f_program[f_functions.back().f_pc - 1] != INST_CREATE_NODE_CONTEXT)
5055 : {
5056 0 : throw QDomXPathException_InternalError("INST_CREATE_NODE_CONTEXT not at the right location in the table of instructions");
5057 : }
5058 : #endif
5059 0 : variant_t node_set(pop_variant_data());
5060 0 : if(node_set.getType() != atomic_value_t::type_t::ATOMIC_TYPE_NODE_SET)
5061 : {
5062 0 : throw QDomXPathException_WrongType("a node set is required to create a node context");
5063 : }
5064 0 : context_t context;
5065 0 : context.f_nodes = node_set.getNodeSetValue();
5066 0 : context.f_position = context.f_nodes.empty() ? -1 : 0;
5067 0 : f_functions.back().f_contexts.push_back(context);
5068 0 : }
5069 :
5070 :
5071 0 : void inst_next_context_node()
5072 : {
5073 : #if QDOM_XPATH_VERIFICATION
5074 : // verify instruction location
5075 0 : if(f_program[f_functions.back().f_pc - 1] != INST_NEXT_CONTEXT_NODE)
5076 : {
5077 0 : throw QDomXPathException_InternalError("INST_NEXT_CONTEXT_NODE not at the right location in the table of instructions");
5078 : }
5079 : #endif
5080 0 : contexts_not_empty();
5081 :
5082 0 : variant_t expr_result(pop_variant_data());
5083 0 : if(expr_result.getType() != atomic_value_t::type_t::ATOMIC_TYPE_NODE_SET)
5084 : {
5085 0 : throw QDomXPathException_WrongType("the 'next_context_node' operation expected the input to be a node-set");
5086 : }
5087 :
5088 0 : context_vector_t::reference context(f_functions.back().f_contexts.back());
5089 :
5090 0 : QDomXPath::node_vector_t r(expr_result.getNodeSetValue());
5091 0 : QDomXPath::node_vector_t& context_result(context.f_result);
5092 0 : const int imax(r.size());
5093 0 : for(int i(0); i < imax; ++i)
5094 : {
5095 0 : context_result.push_back(r[i]);
5096 : }
5097 :
5098 : // if +1 to position is too large, just return false
5099 0 : const bool has_another_position(context.f_position + 1 < context.f_nodes.size());
5100 0 : if(has_another_position)
5101 : {
5102 0 : ++context.f_position;
5103 : }
5104 :
5105 0 : variant_t value;
5106 0 : value.atomic_value_t::setValue(has_another_position);
5107 0 : f_functions.back().f_stack.push_back(value);
5108 0 : }
5109 :
5110 :
5111 0 : void inst_pop_context()
5112 : {
5113 : #if QDOM_XPATH_VERIFICATION
5114 : // verify instruction location
5115 0 : if(f_program[f_functions.back().f_pc - 1] != INST_POP_CONTEXT)
5116 : {
5117 0 : throw QDomXPathException_InternalError("INST_POP_CONTEXT not at the right location in the table of instructions");
5118 : }
5119 : #endif
5120 0 : contexts_not_empty();
5121 0 : f_functions.back().f_contexts.pop_back();
5122 0 : }
5123 :
5124 :
5125 0 : void inst_get_context_node()
5126 : {
5127 : #if QDOM_XPATH_VERIFICATION
5128 : // verify instruction location
5129 0 : if(f_program[f_functions.back().f_pc - 1] != INST_GET_CONTEXT_NODE)
5130 : {
5131 0 : throw QDomXPathException_InternalError("INST_GET_CONTEXT_NODE not at the right location in the table of instructions");
5132 : }
5133 : #endif
5134 0 : contexts_not_empty();
5135 0 : const context_t context(f_functions.back().f_contexts.back());
5136 0 : QDomXPath::node_vector_t node_set;
5137 0 : if(context.f_position != -1)
5138 : {
5139 0 : node_set.push_back(context.f_nodes[context.f_position]);
5140 : }
5141 0 : variant_t result;
5142 0 : result.setValue(node_set);
5143 0 : f_functions.back().f_stack.push_back(result);
5144 0 : }
5145 :
5146 :
5147 : /** \brief Compute an axis.
5148 : *
5149 : * This function computes a list of nodes as specified by the axis
5150 : * parameters.
5151 : *
5152 : * In some cases this can result in a very large number of nodes getting
5153 : * pushed around!
5154 : */
5155 0 : void inst_axis()
5156 : {
5157 : #if QDOM_XPATH_VERIFICATION
5158 : // verify instruction location
5159 0 : if(f_program[f_functions.back().f_pc - 1] != INST_AXIS)
5160 : {
5161 0 : throw QDomXPathException_InternalError("INST_AXIS not at the right location in the table of instructions");
5162 : }
5163 : #endif
5164 : // the first parameter is the axis (enum axis_t)
5165 0 : variant_t axis_variant(pop_variant_data());
5166 0 : axis_t axis(static_cast<axis_t>(axis_variant.getIntegerValue()));
5167 :
5168 : // the next parameter is the prefix or the node type parameter
5169 : // (in case of a processing-instruction node)
5170 0 : variant_t prefix_or_processing_language(pop_variant_data());
5171 0 : QString prefix;
5172 0 : QString processing_language;
5173 :
5174 : // the next parameter is the local_part (i.e. path entry)
5175 : // or the node type enumeration (enum node_type_t)
5176 0 : variant_t local_part_or_node_type(pop_variant_data());
5177 0 : QString local_part;
5178 0 : node_type_t node_type(node_type_t::NODE_TYPE_ELEMENT);
5179 :
5180 : // the last parameter is the context node which we cannot pop because
5181 : // it is required to run through all the nodes, we only make use of the
5182 : // context node which is the one we're interested in
5183 0 : variant_t context_node_variant(pop_variant_data());
5184 0 : if(context_node_variant.getType() != atomic_value_t::type_t::ATOMIC_TYPE_NODE_SET)
5185 : {
5186 0 : throw QDomXPathException_WrongType("the 4th axis parameters must be a node-set");
5187 : }
5188 : //stack_not_empty(atomic_value_t::type_t::ATOMIC_TYPE_CONTEXT);
5189 : //context_t& context(f_functions.back().f_stack.back().getContextValue());
5190 : //variant_t context_variant(pop_variant_data());
5191 : //context_t& context(context_variant.getContextValue());
5192 :
5193 0 : if(local_part_or_node_type.getType() == atomic_value_t::type_t::ATOMIC_TYPE_INTEGER)
5194 : {
5195 : // we have a node type, thus if not empty the
5196 : // prefix_or_processing_language variable is the
5197 : // language name to match
5198 0 : if(axis == axis_t::AXIS_ATTRIBUTE || axis == axis_t::AXIS_NAMESPACE)
5199 : {
5200 : // Note: XPath 2.0 has an 'attribute' NodeType (KindTest) which
5201 : // means attribute axis would be fine here
5202 0 : throw QDomXPathException_WrongType("attribute and namespace axis are not compatible with a node type");
5203 : }
5204 0 : node_type = static_cast<node_type_t>(local_part_or_node_type.getIntegerValue());
5205 0 : processing_language = prefix_or_processing_language.getStringValue();
5206 : }
5207 : else
5208 : {
5209 : // we got a prefix + local_name
5210 0 : prefix = prefix_or_processing_language.getStringValue();
5211 0 : local_part = local_part_or_node_type.getStringValue();
5212 0 : if(local_part == "*")
5213 : {
5214 0 : local_part = "";
5215 : }
5216 :
5217 0 : if(axis == axis_t::AXIS_ATTRIBUTE)
5218 : {
5219 : // This is an XPath 2.0 feature
5220 0 : node_type = node_type_t::NODE_TYPE_ATTRIBUTE;
5221 : }
5222 : }
5223 0 : const bool any_prefix(prefix == "*");
5224 :
5225 : // node_type_t::NODE_TYPE_COMMENT
5226 : // node_type_t::NODE_TYPE_NODE
5227 : // node_type_t::NODE_TYPE_PROCESSING_INSTRUCTION
5228 : // node_type_t::NODE_TYPE_TEXT
5229 0 : QDomNode::NodeType dom_node_type( QDomNode::ElementNode );
5230 0 : switch(node_type)
5231 : {
5232 0 : case node_type_t::NODE_TYPE_COMMENT:
5233 0 : dom_node_type = QDomNode::CommentNode;
5234 0 : break;
5235 :
5236 0 : case node_type_t::NODE_TYPE_NODE: // any node? (BaseNode)
5237 : case node_type_t::NODE_TYPE_ELEMENT:
5238 0 : dom_node_type = QDomNode::ElementNode;
5239 0 : break;
5240 :
5241 0 : case node_type_t::NODE_TYPE_PROCESSING_INSTRUCTION:
5242 0 : dom_node_type = QDomNode::ProcessingInstructionNode;
5243 0 : break;
5244 :
5245 0 : case node_type_t::NODE_TYPE_TEXT:
5246 0 : dom_node_type = QDomNode::TextNode;
5247 : // should include QDomNode::CDATASectionNode
5248 0 : break;
5249 :
5250 0 : case node_type_t::NODE_TYPE_DOCUMENT_NODE:
5251 0 : dom_node_type = QDomNode::DocumentNode;
5252 0 : break;
5253 :
5254 0 : case node_type_t::NODE_TYPE_SCHEMA_ELEMENT:
5255 : //dom_node_type = QDomNode::??;
5256 0 : throw QDomXPathException_NotImplemented("the schema_element node type is not yet implemented");
5257 : break;
5258 :
5259 0 : case node_type_t::NODE_TYPE_ATTRIBUTE:
5260 0 : dom_node_type = QDomNode::AttributeNode;
5261 0 : break;
5262 :
5263 0 : case node_type_t::NODE_TYPE_SCHEMA_ATTRIBUTE:
5264 : //dom_node_type = QDomNode::??;
5265 0 : throw QDomXPathException_NotImplemented("the schema_attribute node type is not yet implemented");
5266 : break;
5267 :
5268 : }
5269 :
5270 0 : QDomXPath::node_vector_t result;
5271 0 : QDomNode context_node;
5272 0 : if(context_node_variant.getNodeSetValue().size() == 1) // TBD -- can it even be more than one?
5273 : {
5274 0 : context_node = context_node_variant.getNodeSetValue()[0];
5275 : }
5276 :
5277 : // axis only applies on element nodes, right?
5278 0 : if(context_node.isElement()
5279 0 : || context_node.isDocument())
5280 : {
5281 0 : switch(axis)
5282 : {
5283 0 : case axis_t::AXIS_SELF:
5284 0 : switch(node_type)
5285 : {
5286 0 : case node_type_t::NODE_TYPE_NODE:
5287 : case node_type_t::NODE_TYPE_ELEMENT:
5288 : // as far as I know the type node is considered to be
5289 : // the same as elements (at least in XPath 1.0)
5290 : // the local_part & prefix must match for us to keep the node
5291 0 : if(!context_node.isNull()
5292 0 : && (local_part.isEmpty() || local_part == context_node.toElement().tagName())
5293 0 : && (any_prefix || prefix == context_node.prefix()))
5294 : {
5295 0 : result.push_back(context_node);
5296 : }
5297 0 : break;
5298 :
5299 0 : default:
5300 0 : throw QDomXPathException_NotImplemented(QString("this axis (%1) does not support this node type (%2)").arg(static_cast<int>(axis)).arg(static_cast<int>(node_type)));
5301 :
5302 : }
5303 0 : break;
5304 :
5305 0 : case axis_t::AXIS_PARENT:
5306 0 : switch(node_type)
5307 : {
5308 0 : case node_type_t::NODE_TYPE_NODE:
5309 : case node_type_t::NODE_TYPE_ELEMENT:
5310 : {
5311 : // as far as I know the type node is considered to be
5312 : // the same as elements (at least in XPath 1.0)
5313 0 : QDomNode node(context_node.parentNode());
5314 : // the local_part & prefix must match for us to keep the node
5315 0 : if(!node.isNull()
5316 0 : && (local_part.isEmpty() || local_part == node.toElement().tagName())
5317 0 : && (any_prefix || prefix == node.prefix()))
5318 : {
5319 0 : result.push_back(node);
5320 0 : }
5321 : }
5322 0 : break;
5323 :
5324 0 : default:
5325 0 : throw QDomXPathException_NotImplemented(QString("this axis (%1) does not support this node type (%2)").arg(static_cast<int>(axis)).arg(static_cast<int>(node_type)));
5326 :
5327 : }
5328 0 : break;
5329 :
5330 : case axis_t::AXIS_ATTRIBUTE:
5331 0 : axis_attribute:
5332 0 : if(local_part.isEmpty())
5333 : {
5334 0 : QDomNamedNodeMap attributes(context_node.attributes());
5335 0 : const int imax(attributes.size());
5336 0 : for(int i(0); i < imax; ++i)
5337 : {
5338 0 : QDomNode attr(attributes.item(i));
5339 0 : if(any_prefix || prefix == attr.prefix())
5340 : {
5341 0 : result.push_back(attr);
5342 : }
5343 : }
5344 : }
5345 : else
5346 : {
5347 : // no need to go through the whole list slowly if
5348 : // we can just query the map at once
5349 0 : QDomNode attr(context_node.attributes().namedItem(local_part));
5350 0 : if(!attr.isNull()
5351 0 : && (any_prefix || prefix == attr.prefix()))
5352 : {
5353 0 : result.push_back(attr);
5354 : }
5355 : }
5356 0 : break;
5357 :
5358 0 : case axis_t::AXIS_ANCESTOR:
5359 : case axis_t::AXIS_ANCESTOR_OR_SELF:
5360 0 : switch(node_type)
5361 : {
5362 0 : case node_type_t::NODE_TYPE_NODE:
5363 : case node_type_t::NODE_TYPE_ELEMENT:
5364 : {
5365 : // as far as I know the type node is considered to be
5366 : // the same as elements (at least in XPath 1.0)
5367 0 : QDomNode node(context_node);
5368 0 : if(axis == axis_t::AXIS_ANCESTOR)
5369 : {
5370 0 : node = node.parentNode();
5371 : }
5372 0 : while(!node.isNull())
5373 : {
5374 : // the local_part & prefix must match for us to keep the node
5375 0 : if((local_part.isEmpty() || local_part == node.toElement().tagName())
5376 0 : && (any_prefix || prefix == node.prefix()))
5377 : {
5378 0 : result.push_back(node);
5379 : }
5380 0 : node = node.parentNode();
5381 0 : }
5382 : }
5383 0 : break;
5384 :
5385 0 : default:
5386 0 : throw QDomXPathException_NotImplemented(QString("this axis (%1) does not support this node type (%2)").arg(static_cast<int>(axis)).arg(static_cast<int>(node_type)));
5387 :
5388 : }
5389 0 : break;
5390 :
5391 0 : case axis_t::AXIS_CHILD:
5392 0 : switch(node_type)
5393 : {
5394 0 : case node_type_t::NODE_TYPE_NODE:
5395 : case node_type_t::NODE_TYPE_ELEMENT:
5396 : {
5397 : // as far as I know the type node is considered to be
5398 : // the same as elements (at least in XPath 1.0)
5399 : //
5400 : // TBD: here we may want to add support for XML files
5401 : // that were loaded without setting the
5402 : // namespaceProcessing parameter set to true
5403 : // (i.e. thus tag names are "blah:foo")
5404 0 : QDomNode node(context_node.firstChildElement(local_part));
5405 0 : while(!node.isNull())
5406 : {
5407 : // the prefix must also match
5408 0 : if(any_prefix || prefix == node.prefix())
5409 : {
5410 0 : result.push_back(node);
5411 : }
5412 0 : node = node.nextSiblingElement(local_part);
5413 0 : }
5414 : }
5415 0 : break;
5416 :
5417 0 : case node_type_t::NODE_TYPE_ATTRIBUTE:
5418 0 : goto axis_attribute;
5419 :
5420 0 : case node_type_t::NODE_TYPE_COMMENT:
5421 : case node_type_t::NODE_TYPE_TEXT:
5422 : {
5423 0 : QDomNode node(context_node.firstChildElement(local_part));
5424 0 : while(!node.isNull())
5425 : {
5426 : // the prefix must also match
5427 0 : if(dom_node_type == node.nodeType()
5428 0 : && (any_prefix || prefix == node.prefix()))
5429 : {
5430 0 : result.push_back(node);
5431 : }
5432 0 : node = node.nextSiblingElement(local_part);
5433 0 : }
5434 : }
5435 0 : break;
5436 :
5437 0 : case node_type_t::NODE_TYPE_PROCESSING_INSTRUCTION:
5438 : {
5439 0 : QDomNode node(context_node.firstChildElement(local_part));
5440 0 : while(!node.isNull())
5441 : {
5442 : // the prefix must also match
5443 0 : if(QDomNode::ProcessingInstructionNode == node.nodeType()
5444 0 : && node.isProcessingInstruction()
5445 0 : && prefix == node.prefix())
5446 : {
5447 0 : if(!processing_language.isEmpty())
5448 : {
5449 : // further test the processing language
5450 0 : QDomProcessingInstruction pi(node.toProcessingInstruction());
5451 0 : if(pi.target() == processing_language)
5452 : {
5453 0 : result.push_back(node);
5454 : }
5455 : }
5456 : else
5457 : {
5458 0 : result.push_back(node);
5459 : }
5460 : }
5461 0 : node = node.nextSiblingElement(local_part);
5462 0 : }
5463 : }
5464 0 : break;
5465 :
5466 0 : default:
5467 0 : throw QDomXPathException_NotImplemented(QString("this axis (%1) does not support this node type (%2)").arg(static_cast<int>(axis)).arg(static_cast<int>(node_type)));
5468 :
5469 : }
5470 0 : break;
5471 :
5472 0 : case axis_t::AXIS_DESCENDANT:
5473 : case axis_t::AXIS_DESCENDANT_OR_SELF:
5474 0 : switch(node_type)
5475 : {
5476 0 : case node_type_t::NODE_TYPE_NODE:
5477 : case node_type_t::NODE_TYPE_ELEMENT:
5478 : {
5479 : // as far as I know the type node is considered to be
5480 : // the same as elements (at least in XPath 1.0)
5481 0 : QDomNode node(context_node);
5482 0 : if(axis == axis_t::AXIS_DESCENDANT_OR_SELF
5483 0 : && (local_part.isEmpty() || local_part == context_node.toElement().tagName())
5484 0 : && (any_prefix || prefix == context_node.prefix()))
5485 : {
5486 0 : result.push_back(context_node);
5487 : }
5488 0 : while(!node.isNull())
5489 : {
5490 0 : QDomNode next(node.firstChild());
5491 0 : if(next.isNull())
5492 : {
5493 0 : next = node;
5494 0 : while(!next.isNull()) // this should never happend since we should bump in context_node first
5495 : {
5496 0 : if(next == context_node)
5497 : {
5498 : // break 2;
5499 0 : goto axis_descendant_done;
5500 : }
5501 0 : QDomNode parent(next.parentNode());
5502 0 : next = next.nextSibling();
5503 0 : if(!next.isNull())
5504 : {
5505 0 : break;
5506 : }
5507 0 : next = parent;
5508 : }
5509 : }
5510 : // the local_part & prefix must match for us to keep the node
5511 0 : node = next;
5512 0 : if((local_part.isEmpty() || local_part == node.toElement().tagName())
5513 0 : && (any_prefix || prefix == node.prefix()))
5514 : {
5515 : // push so nodes stay in document order
5516 0 : result.push_back(node);
5517 : }
5518 : }
5519 0 : axis_descendant_done:;
5520 : }
5521 0 : break;
5522 :
5523 0 : default:
5524 0 : throw QDomXPathException_NotImplemented(QString("this axis (%1) does not support this node type (%2)").arg(static_cast<int>(axis)).arg(static_cast<int>(node_type)));
5525 :
5526 : }
5527 0 : break;
5528 :
5529 0 : case axis_t::AXIS_NAMESPACE:
5530 : /*
5531 : * I found the following documentation about namespace nodes on
5532 : * an Oracle website:
5533 : *
5534 : * http://docs.oracle.com/cd/B19306_01/appdev.102/b14259/appcxpa.htm
5535 : *
5536 : * It clearly explains what the so called "Namespace Nodes" are
5537 : * and at this time we simply do not support them at all for
5538 : * several reasons:
5539 : *
5540 : * \li We do not need them.
5541 : * \li XPath 2.0 does not support them (it is clearly marked
5542 : * as deprecated).
5543 : * \li Namespace Nodes are not actual nodes in the DOM. We'd
5544 : * have to simulate those special nodes and that's a lot
5545 : * of special cases for something nearly no one uses.
5546 : *
5547 : * Oracle documentation:
5548 : *
5549 : * Namespace Nodes
5550 : *
5551 : * Each element has an associated set of namespace nodes, one for
5552 : * each distinct namespace prefix that is in scope for the element
5553 : * (including the xml prefix, which is implicitly declared by the
5554 : * XML Namespaces Recommendation) and one for the default namespace
5555 : * if one is in scope for the element. The element is the parent of
5556 : * each of these namespace nodes; however, a namespace node is not
5557 : * a child of its parent element.
5558 : *
5559 : * Elements never share namespace nodes: if one element node is not
5560 : * the same node as another element node, then none of the
5561 : * namespace nodes of the one element node will be the same node as
5562 : * the namespace nodes of another element node. This means that an
5563 : * element will have a namespace node:
5564 : *
5565 : * For every attribute on the element whose name starts with
5566 : * xmlns:;
5567 : *
5568 : * For every attribute on an ancestor element whose name starts
5569 : * xmlns: unless the element itself or a nearer ancestor
5570 : * re-declares the prefix;
5571 : *
5572 : * For an xmlns attribute, if the element or some ancestor has an
5573 : * xmlns attribute, and the value of the xmlns attribute for the
5574 : * nearest such element is nonempty
5575 : *
5576 : * \note
5577 : * An attribute xmlns="" undeclares the default namespace.
5578 : *
5579 : * A namespace node has an expanded-name: the local part is the
5580 : * namespace prefix (this is empty if the namespace node is for the
5581 : * default namespace); the namespace URI is always NULL.
5582 : *
5583 : * The string-value of a namespace node is the namespace URI that
5584 : * is being bound to the namespace prefix; if it is relative, then
5585 : * it must be resolved just like a namespace URI in an
5586 : * expanded-name.
5587 : */
5588 0 : throw QDomXPathException_NotImplemented("the namespace axis is not implemented");
5589 :
5590 0 : case axis_t::AXIS_FOLLOWING:
5591 : case axis_t::AXIS_FOLLOWING_SIBLING:
5592 0 : switch(node_type)
5593 : {
5594 0 : case node_type_t::NODE_TYPE_NODE:
5595 : case node_type_t::NODE_TYPE_ELEMENT:
5596 : {
5597 : // as far as I know the type node is considered to be
5598 : // the same as elements (at least in XPath 1.0)
5599 0 : QDomNode node(context_node.nextSibling());
5600 0 : while(!node.isNull())
5601 : {
5602 : // the local_part & prefix must match for us to keep the node
5603 0 : if((local_part.isEmpty() || local_part == node.toElement().tagName())
5604 0 : && (any_prefix || prefix == node.prefix()))
5605 : {
5606 : // push so nodes stay in document order
5607 0 : result.push_back(node);
5608 : }
5609 0 : node = node.nextSibling();
5610 : }
5611 0 : if(axis == axis_t::AXIS_FOLLOWING)
5612 : {
5613 0 : QDomNode next(context_node.parentNode());
5614 0 : while(!next.isNull())
5615 : {
5616 0 : QDomNode parent(next.parentNode());
5617 0 : next = next.nextSibling();
5618 0 : if(!next.isNull())
5619 : {
5620 0 : break;
5621 : }
5622 0 : next = parent;
5623 : }
5624 0 : while(!next.isNull())
5625 : {
5626 : // the local_part & prefix must match for us to keep the node
5627 0 : node = next;
5628 0 : if((local_part.isEmpty() || local_part == node.toElement().tagName())
5629 0 : && (any_prefix || prefix == node.prefix()))
5630 : {
5631 : // push so nodes stay in document order
5632 0 : result.push_back(node);
5633 : }
5634 0 : next = node.firstChild();
5635 0 : if(next.isNull())
5636 : {
5637 0 : next = node;
5638 0 : while(!next.isNull())
5639 : {
5640 0 : QDomNode parent(next.parentNode());
5641 0 : next = next.nextSibling();
5642 0 : if(!next.isNull())
5643 : {
5644 0 : break;
5645 : }
5646 0 : next = parent;
5647 : }
5648 : }
5649 : }
5650 0 : }
5651 : }
5652 0 : break;
5653 :
5654 0 : default:
5655 0 : throw QDomXPathException_NotImplemented(QString("this axis (%1) does not support this node type (%2)").arg(static_cast<int>(axis)).arg(static_cast<int>(node_type)));
5656 :
5657 : }
5658 0 : break;
5659 :
5660 0 : case axis_t::AXIS_PRECEDING:
5661 : case axis_t::AXIS_PRECEDING_SIBLING:
5662 : // WARNING: 'preceding' never include the ancestors
5663 0 : switch(node_type)
5664 : {
5665 0 : case node_type_t::NODE_TYPE_NODE:
5666 : case node_type_t::NODE_TYPE_ELEMENT:
5667 : {
5668 : // as far as I know the type node is considered to be
5669 : // the same as elements (at least in XPath 1.0)
5670 0 : QDomNode node(context_node.previousSibling());
5671 0 : while(!node.isNull())
5672 : {
5673 : // the local_part & prefix must match for us to keep the node
5674 0 : if((local_part.isEmpty() || local_part == node.toElement().tagName())
5675 0 : && (any_prefix || prefix == node.prefix()))
5676 : {
5677 0 : result.push_back(node);
5678 : }
5679 0 : node = node.previousSibling();
5680 : }
5681 0 : if(axis == axis_t::AXIS_PRECEDING)
5682 : {
5683 0 : QDomNode previous(context_node.parentNode());
5684 0 : while(!previous.isNull())
5685 : {
5686 0 : QDomNode parent(previous.parentNode());
5687 0 : previous = previous.previousSibling();
5688 0 : if(!previous.isNull())
5689 : {
5690 0 : break;
5691 : }
5692 0 : previous = parent;
5693 : }
5694 0 : while(!previous.isNull())
5695 : {
5696 : // the local_part & prefix must match for us to keep the node
5697 0 : node = previous;
5698 0 : if((local_part.isEmpty() || local_part == node.toElement().tagName())
5699 0 : && (any_prefix || prefix == node.prefix()))
5700 : {
5701 0 : result.push_back(node);
5702 : }
5703 0 : previous = node.lastChild();
5704 0 : if(previous.isNull())
5705 : {
5706 0 : do
5707 : {
5708 0 : QDomNode parent(node.parentNode());
5709 0 : previous = node.previousSibling();
5710 0 : if(!previous.isNull())
5711 : {
5712 0 : break;
5713 : }
5714 0 : node = parent;
5715 : }
5716 0 : while(!previous.isNull());
5717 : }
5718 : }
5719 0 : }
5720 : }
5721 0 : break;
5722 :
5723 0 : default:
5724 0 : throw QDomXPathException_NotImplemented(QString("this axis (%1) does not support this node type (%2)").arg(static_cast<int>(axis)).arg(static_cast<int>(node_type)));
5725 :
5726 : }
5727 0 : break;
5728 :
5729 : }
5730 : }
5731 :
5732 0 : variant_t node_set;
5733 0 : node_set.setValue(result);
5734 0 : f_functions.back().f_stack.push_back(node_set);
5735 0 : }
5736 :
5737 :
5738 :
5739 :
5740 :
5741 :
5742 :
5743 : /** \brief Get the next character.
5744 : *
5745 : * This function returns the next character found in the input string.
5746 : * If the character is invalid, the function throws an exception.
5747 : *
5748 : * Note that the function returns characters encoded in UTF-16, even
5749 : * though XML expects UCS-4 characters. The main reason is because the
5750 : * QString implementation returns those characters in this way. This
5751 : * works because none of the characters with code values larger than
5752 : * 0xFFFF are tested within this parser. All of those are viewed as
5753 : * standard 'Char' and thus they can as well be defined as 0xD800 to
5754 : * 0xDFFF byte codes.
5755 : *
5756 : * \exception QDomXPathException_InvalidCharacter
5757 : * This exception is raised in the even an input character is not a valid
5758 : * XML character (i.e. Ctrl-A is not acceptable in the input.)
5759 : *
5760 : * \return The next character in the form of an encoded UTF-16 character.
5761 : */
5762 0 : char_t getc()
5763 : {
5764 0 : char_t c(f_in->unicode());
5765 0 : if(c == '\0')
5766 : {
5767 0 : return END_OF_PATH;
5768 : }
5769 : // Char ::= #x9
5770 : // | #xA
5771 : // | #xD
5772 : // | [#x20-#xD7FF]
5773 : // | [#xE000-#xFFFD]
5774 : // | [#x10000-#x10FFFF]
5775 : // The Qt QChar is a UTF-16 character which means that
5776 : // characters larger then 0xFFFF are defined with codes
5777 : // between 0xD800 and 0xDFFF. These are therefore included
5778 : // although we could check that the characters are correct
5779 : // we do not because we do not have to test for specific
5780 : // characters with codes that large.)
5781 0 : if(c != 0x09
5782 0 : && c != 0x0A
5783 0 : && c != 0x0D
5784 0 : && (c < 0x20 || c > 0xFFFD))
5785 : {
5786 0 : throw QDomXPathException_InvalidCharacter(QString("invalid XML character 0x%1").arg(static_cast<int>(c), 4, 16, QChar('0')));
5787 : }
5788 0 : ++f_in;
5789 0 : return c;
5790 : }
5791 :
5792 : /** \brief Restore the input character pointer position.
5793 : *
5794 : * This function can be called to restore the character pointer position
5795 : * to a previous position. It can be called as many times as the getc()
5796 : * function was called. However, note that you cannot specify which
5797 : * character is being ungotten. It will always be the character that
5798 : * you got at that time with getc().
5799 : *
5800 : * Note that the function takes a character as input, if that character is
5801 : * END_OF_PATH, then ungetc() does nothing. This can be a problem if multiple
5802 : * ungetc() need to be used. In that case, just use a character such as ' '.
5803 : *
5804 : * \exception QDomXPathException_TooManyUnget
5805 : * As a protection, the function checks that the unget() function is called
5806 : * more times than the get() function was called. If so, we have a bug.
5807 : *
5808 : * \param[in] c The character to be ungotten.
5809 : */
5810 0 : void ungetc(char_t c)
5811 : {
5812 0 : if(c == END_OF_PATH)
5813 : {
5814 0 : return;
5815 : }
5816 0 : if(f_in <= f_start)
5817 : {
5818 0 : throw QDomXPathException_TooManyUnget("ungetc() called too many times, the algorithm is spurious");
5819 : }
5820 0 : --f_in;
5821 : }
5822 :
5823 : /** \brief Get the next token.
5824 : *
5825 : * This function reads one XML XPath token from the input. This can be a
5826 : * single character ('(' or '@') or a whole complex token such as a string
5827 : * or a real number.
5828 : *
5829 : * The function returns true if the token was successfully read. The token
5830 : * is found in the f_last_token variable. If necessary, the token can be
5831 : * copied for further processing at a later time.
5832 : *
5833 : * \exception QDomXPathException_InvalidCharacter
5834 : * This exception is raised if an invalid combination of characters is found.
5835 : * For example, the exclamation mark ('!') character must be followed by
5836 : * an equal sign ('=') to be valid.
5837 : *
5838 : * \exception QDomXPathException_InvalidString
5839 : * This exception is raised whenever a string ends without a quote (i.e. we
5840 : * reached the end of the input buffer before the end of the string.)
5841 : *
5842 : * \return true if a token was read, false if no more tokens are available.
5843 : */
5844 0 : bool get_token()
5845 : {
5846 : // ExprToken ::= '(' | ')'
5847 : // | '[' | ']'
5848 : // | '.'
5849 : // | '..'
5850 : // | '@'
5851 : // | ','
5852 : // | '::'
5853 : // | NameTest
5854 : // | NodeType
5855 : // | Operator
5856 : // | FunctionName
5857 : // | AxisName
5858 : // | Literal
5859 : // | Number
5860 : // | VariableReference
5861 : //
5862 : // Number ::= Digits ('.' Digits?)?
5863 : // | '.' Digits
5864 : //
5865 : // Digits ::= [0-9]+
5866 : //
5867 : // Operator ::= OperatorName
5868 : // | MultiplyOperator
5869 : // | '/'
5870 : // | '//'
5871 : // | '|'
5872 : // | '+'
5873 : // | '-'
5874 : // | '='
5875 : // | '!='
5876 : // | '<'
5877 : // | '<='
5878 : // | '>'
5879 : // | '>='
5880 : //
5881 : // MultiplyOperator ::= '*'
5882 : //
5883 : // Literal ::= '"' [^"]* '"'
5884 : // | "'" [^']* "'"
5885 : //
5886 : // NameTest ::= '*'
5887 : // | NCName ':' '*'
5888 : // | QName
5889 : //
5890 : // NCName ::= Name - (Char* ':' Char*)
5891 : //
5892 : // NameStartChar ::= ':'
5893 : // | [A-Z]
5894 : // | '_'
5895 : // | [a-z]
5896 : // | [#xC0-#xD6]
5897 : // | [#xD8-#xF6]
5898 : // | [#xF8-#x2FF]
5899 : // | [#x370-#x37D]
5900 : // | [#x37F-#x1FFF]
5901 : // | [#x200C-#x200D]
5902 : // | [#x2070-#x218F]
5903 : // | [#x2C00-#x2FEF]
5904 : // | [#x3001-#xD7FF]
5905 : // | [#xF900-#xFDCF]
5906 : // | [#xFDF0-#xFFFD]
5907 : // | [#x10000-#xEFFFF]
5908 : //
5909 : // NameChar ::= NameStartChar
5910 : // | '-'
5911 : // | '.'
5912 : // | [0-9]
5913 : // | #xB7
5914 : // | [#x0300-#x036F]
5915 : // | [#x203F-#x2040]
5916 : //
5917 : // Name ::= NameStartChar (NameChar)*
5918 : //
5919 : // OperatorName ::= 'and'
5920 : // | 'or'
5921 : // | 'mod'
5922 : // | 'div'
5923 : //
5924 : // NodeType ::= 'comment'
5925 : // | 'text'
5926 : // | 'processing-instruction'
5927 : // | 'node'
5928 : //
5929 : // FunctionName ::= QName - NodeType
5930 : //
5931 : // AxisName ::= 'ancestor'
5932 : // | 'ancestor-or-self'
5933 : // | 'attribute'
5934 : // | 'child'
5935 : // | 'descendant'
5936 : // | 'descendant-or-self'
5937 : // | 'following'
5938 : // | 'following-sibling'
5939 : // | 'namespace'
5940 : // | 'parent'
5941 : // | 'preceding'
5942 : // | 'preceding-sibling'
5943 : // | 'self'
5944 : //
5945 : // VariableReference ::= '$' QName
5946 : //
5947 : // QName ::= PrefixedName
5948 : // | UnprefixedName
5949 : //
5950 : // PrefixedName ::= Prefix ':' LocalPart
5951 : //
5952 : // UnprefixedName ::= LocalPart
5953 : //
5954 : // Prefix ::= NCName
5955 : //
5956 : // LocalPart ::= NCName
5957 : //
5958 :
5959 :
5960 : // if we got an ungotten token, return it
5961 0 : if(f_unget_token)
5962 : {
5963 0 : f_last_token = f_unget_token;
5964 0 : f_unget_token.reset();
5965 0 : return f_last_token;
5966 : }
5967 : else
5968 : {
5969 0 : f_last_token.f_string = "";
5970 0 : char_t c(getc());
5971 : // ignore spaces between tokens
5972 0 : while(c == 0x20 || c == 0x09 || c == 0x0D || c == 0x0A)
5973 : {
5974 0 : c = getc();
5975 : }
5976 0 : if(c == END_OF_PATH)
5977 : {
5978 0 : f_last_token.reset();
5979 0 : return f_last_token;
5980 : }
5981 0 : f_last_token.f_string += QChar(c);
5982 0 : switch(c)
5983 : {
5984 0 : case '(':
5985 0 : f_last_token.f_token = token_t::tok_t::TOK_OPEN_PARENTHESIS;
5986 0 : break;
5987 :
5988 0 : case ')':
5989 0 : f_last_token.f_token = token_t::tok_t::TOK_CLOSE_PARENTHESIS;
5990 0 : break;
5991 :
5992 0 : case '[':
5993 0 : f_last_token.f_token = token_t::tok_t::TOK_OPEN_SQUARE_BRACKET;
5994 0 : break;
5995 :
5996 0 : case ']':
5997 0 : f_last_token.f_token = token_t::tok_t::TOK_CLOSE_SQUARE_BRACKET;
5998 0 : break;
5999 :
6000 0 : case '@':
6001 0 : f_last_token.f_token = token_t::tok_t::TOK_AT;
6002 0 : break;
6003 :
6004 0 : case ',':
6005 0 : f_last_token.f_token = token_t::tok_t::TOK_COMMA;
6006 0 : break;
6007 :
6008 0 : case ':':
6009 0 : c = getc();
6010 0 : if(c == ':')
6011 : {
6012 0 : f_last_token.f_token = token_t::tok_t::TOK_DOUBLE_COLON;
6013 0 : f_last_token.f_string += QChar(c);
6014 : }
6015 : else
6016 : {
6017 0 : ungetc(c);
6018 0 : f_last_token.f_token = token_t::tok_t::TOK_COLON;
6019 : }
6020 0 : break;
6021 :
6022 0 : case '/':
6023 0 : c = getc();
6024 0 : if(c == '/')
6025 : {
6026 0 : f_last_token.f_token = token_t::tok_t::TOK_DOUBLE_SLASH;
6027 0 : f_last_token.f_string += QChar(c);
6028 : }
6029 : else
6030 : {
6031 0 : ungetc(c);
6032 0 : f_last_token.f_token = token_t::tok_t::TOK_SLASH;
6033 : }
6034 0 : break;
6035 :
6036 0 : case '|':
6037 0 : f_last_token.f_token = token_t::tok_t::TOK_PIPE;
6038 0 : break;
6039 :
6040 0 : case '$':
6041 0 : f_last_token.f_token = token_t::tok_t::TOK_DOLLAR;
6042 0 : break;
6043 :
6044 0 : case '+':
6045 0 : f_last_token.f_token = token_t::tok_t::TOK_PLUS;
6046 0 : break;
6047 :
6048 0 : case '-':
6049 0 : f_last_token.f_token = token_t::tok_t::TOK_MINUS;
6050 0 : break;
6051 :
6052 0 : case '=':
6053 0 : f_last_token.f_token = token_t::tok_t::TOK_EQUAL;
6054 0 : break;
6055 :
6056 0 : case '!':
6057 0 : c = getc();
6058 0 : if(c == '=')
6059 : {
6060 0 : f_last_token.f_token = token_t::tok_t::TOK_NOT_EQUAL;
6061 0 : f_last_token.f_string += QChar(c);
6062 : }
6063 : else
6064 : {
6065 0 : throw QDomXPathException_InvalidCharacter("found a stand alone '!' character which is not supported at that location");
6066 : }
6067 0 : break;
6068 :
6069 0 : case '<':
6070 0 : c = getc();
6071 0 : if(c == '=')
6072 : {
6073 0 : f_last_token.f_token = token_t::tok_t::TOK_LESS_OR_EQUAL;
6074 0 : f_last_token.f_string += QChar(c);
6075 : }
6076 : else
6077 : {
6078 0 : ungetc(c);
6079 0 : f_last_token.f_token = token_t::tok_t::TOK_LESS_THAN;
6080 : }
6081 0 : break;
6082 :
6083 0 : case '>':
6084 0 : c = getc();
6085 0 : if(c == '=')
6086 : {
6087 0 : f_last_token.f_token = token_t::tok_t::TOK_GREATER_OR_EQUAL;
6088 0 : f_last_token.f_string += QChar(c);
6089 : }
6090 : else
6091 : {
6092 0 : ungetc(c);
6093 0 : f_last_token.f_token = token_t::tok_t::TOK_GREATER_THAN;
6094 : }
6095 0 : break;
6096 :
6097 0 : case '*':
6098 : // '*' can represent a NameTest or the Multiply operator
6099 : // (this is context dependent)
6100 0 : f_last_token.f_token = token_t::tok_t::TOK_ASTERISK;
6101 0 : break;
6102 :
6103 0 : case '\'':
6104 : case '"':
6105 0 : f_last_token.f_token = token_t::tok_t::TOK_STRING;
6106 0 : f_last_token.f_string = ""; // remove the quote
6107 : {
6108 0 : char_t quote(c);
6109 : for(;;)
6110 : {
6111 0 : c = getc();
6112 0 : if(c == END_OF_PATH)
6113 : {
6114 0 : throw QDomXPathException_InvalidString("a string that was not properly closed");
6115 : }
6116 0 : if(c == quote)
6117 : {
6118 : // in XPath 2.0 we can double quotes to insert
6119 : // a quote in the string
6120 0 : c = getc();
6121 0 : if(c != quote)
6122 : {
6123 0 : ungetc(c);
6124 0 : break;
6125 : }
6126 : }
6127 0 : f_last_token.f_string += QChar(c);
6128 : }
6129 : }
6130 0 : break;
6131 :
6132 0 : case '0':
6133 : case '1':
6134 : case '2':
6135 : case '3':
6136 : case '4':
6137 : case '5':
6138 : case '6':
6139 : case '7':
6140 : case '8':
6141 : case '9':
6142 0 : f_last_token.f_token = token_t::tok_t::TOK_INTEGER;
6143 0 : f_last_token.f_integer = c - '0';
6144 : for(;;)
6145 : {
6146 0 : c = getc();
6147 0 : if(c < '0' || c > '9')
6148 : {
6149 : break;
6150 : }
6151 0 : f_last_token.f_string += QChar(c);
6152 0 : f_last_token.f_integer = f_last_token.f_integer * 10 + c - '0';
6153 : }
6154 0 : if(c != '.')
6155 : {
6156 0 : ungetc(c);
6157 0 : break;
6158 : }
6159 0 : f_last_token.f_string += QChar(c);
6160 0 : f_last_token.f_real = static_cast<double>(f_last_token.f_integer);
6161 : #if __cplusplus >= 201700
6162 : [[fallthrough]];
6163 : #endif
6164 0 : case '.':
6165 0 : c = getc();
6166 0 : if(f_last_token.f_string == ".")
6167 : {
6168 0 : if(c == '.')
6169 : {
6170 0 : f_last_token.f_token = token_t::tok_t::TOK_DOUBLE_DOT;
6171 0 : f_last_token.f_string += QChar(c);
6172 0 : break;
6173 : }
6174 0 : else if(c < '0' || c > '9')
6175 : {
6176 0 : ungetc(c);
6177 0 : f_last_token.f_token = token_t::tok_t::TOK_DOT;
6178 0 : break;
6179 : }
6180 0 : f_last_token.f_string = "0.";
6181 : }
6182 0 : f_last_token.f_token = token_t::tok_t::TOK_REAL;
6183 : { // "protect" frac
6184 0 : double frac(1.0);
6185 : for(;;)
6186 : {
6187 0 : if(c < '0' || c > '9')
6188 : {
6189 : break;
6190 : }
6191 0 : f_last_token.f_string += QChar(c);
6192 0 : frac /= 10.0;
6193 0 : f_last_token.f_real += (c - '0') * frac;
6194 0 : c = getc();
6195 0 : }
6196 : }
6197 0 : ungetc(c);
6198 0 : if(f_last_token.f_string.right(1) == ".")
6199 : {
6200 0 : f_last_token.f_string += '0';
6201 : }
6202 : // we prepend and append zeroes to the f_string for clarity
6203 : // yet, the function really returns f_last_token.f_real
6204 : // the f_last_token.f_integer is the floor()
6205 0 : break;
6206 :
6207 0 : default:
6208 0 : if((c >= 'a' && c <= 'z')
6209 0 : || (c >= 'A' && c <= 'Z')
6210 0 : || (c >= 0x00C0 && c <= 0x00D6)
6211 0 : || (c >= 0x00D8 && c <= 0x00F6)
6212 0 : || (c >= 0x00F8 && c <= 0x02FF)
6213 0 : || (c >= 0x0370 && c <= 0x037D)
6214 0 : || (c >= 0x037F && c <= 0x1FFF)
6215 0 : || (c >= 0x200C && c <= 0x200D)
6216 0 : || (c >= 0x2070 && c <= 0x218F)
6217 0 : || (c >= 0x2C00 && c <= 0x2FEF)
6218 0 : || (c >= 0x3001 && c <= 0xDFFF) // includes 0x10000 to 0xEFFFF
6219 0 : || (c >= 0xF900 && c <= 0xFDCF)
6220 0 : || (c >= 0xFDF0 && c <= 0xFFFD)
6221 0 : || c == '_')
6222 : {
6223 : for(;;)
6224 : {
6225 0 : c = getc();
6226 0 : if(c == END_OF_PATH)
6227 : {
6228 0 : break;
6229 : }
6230 0 : if((c < 'a' || c > 'z')
6231 0 : && (c < 'A' || c > 'Z')
6232 0 : && (c < '0' || c > '9')
6233 0 : && (c < 0x00C0 || c > 0x00D6)
6234 0 : && (c < 0x00D8 || c > 0x00F6)
6235 0 : && (c < 0x00F8 || c > 0x02FF)
6236 0 : && (c < 0x0300 || c > 0x037D)
6237 0 : && (c < 0x037F || c > 0x1FFF)
6238 0 : && (c < 0x200C || c > 0x200D)
6239 0 : && (c < 0x203F || c > 0x2040)
6240 0 : && (c < 0x2070 || c > 0x218F)
6241 0 : && (c < 0x2C00 || c > 0x2FEF)
6242 0 : && (c < 0x3001 || c > 0xDFFF) // includes 0x10000 to 0xEFFFF
6243 0 : && (c < 0xF900 || c > 0xFDCF)
6244 0 : && (c < 0xFDF0 || c > 0xFFFD)
6245 0 : && c != '_' && c != '.' && c != '-' && c != 0xB7)
6246 : {
6247 0 : ungetc(c);
6248 0 : break;
6249 : }
6250 0 : f_last_token.f_string += QChar(c);
6251 : }
6252 : // at this point we return an NCNAME
6253 : // (NC means No Colon)
6254 : // what the name represents changes depending on context
6255 0 : f_last_token.f_token = token_t::tok_t::TOK_NCNAME;
6256 : }
6257 : else
6258 : {
6259 : // this won't match anything and thus return and error
6260 0 : f_last_token.f_token = token_t::tok_t::TOK_INVALID;
6261 : }
6262 0 : break;
6263 :
6264 : }
6265 : }
6266 :
6267 0 : return f_last_token;
6268 : }
6269 :
6270 : #if 0
6271 : /** \brief Check whether an NCNAME represents an operator.
6272 : *
6273 : * This function transforms a TOK_NCNAME token in one of the named operators:
6274 : *
6275 : * \li TOK_OPERATOR_AND
6276 : * \li TOK_OPERATOR_OR
6277 : * \li TOK_OPERATOR_DIV
6278 : * \li TOK_OPERATOR_MOD
6279 : *
6280 : * \note
6281 : * At this point the function is not being used because it is not really
6282 : * practical/useful.
6283 : *
6284 : * \return true if the f_last_token represents an operator.
6285 : */
6286 : bool token_is_operator()
6287 : {
6288 : switch(f_last_token.f_token)
6289 : {
6290 : case token_t::tok_t::TOK_NCNAME:
6291 : if(f_last_token.f_string == "and")
6292 : {
6293 : f_last_token.f_token = token_t::tok_t::TOK_OPERATOR_AND;
6294 : }
6295 : else if(f_last_token.f_string == "or")
6296 : {
6297 : f_last_token.f_token = token_t::tok_t::TOK_OPERATOR_OR;
6298 : }
6299 : else if(f_last_token.f_string == "div")
6300 : {
6301 : f_last_token.f_token = token_t::tok_t::TOK_OPERATOR_DIV;
6302 : }
6303 : else if(f_last_token.f_string == "mod")
6304 : {
6305 : f_last_token.f_token = token_t::tok_t::TOK_OPERATOR_MOD;
6306 : }
6307 : else
6308 : {
6309 : return false;
6310 : }
6311 : #if __cplusplus >= 201700
6312 : [[fallthrough]];
6313 : #endif
6314 : case token_t::tok_t::TOK_OPERATOR_AND:
6315 : case token_t::tok_t::TOK_OPERATOR_OR:
6316 : case token_t::tok_t::TOK_OPERATOR_MOD:
6317 : case token_t::tok_t::TOK_OPERATOR_DIV:
6318 : return true;
6319 :
6320 : default:
6321 : return false;
6322 :
6323 : }
6324 : }
6325 : #endif
6326 :
6327 :
6328 : /** \brief Check whether an NCNAME represents a node type.
6329 : *
6330 : * This function transforms a TOK_NCNAME token in one of the node types:
6331 : *
6332 : * \li TOK_NODE_TYPE_COMMENT
6333 : * \li TOK_NODE_TYPE_TEXT
6334 : * \li TOK_NODE_TYPE_PROCESSING_INSTRUCTION
6335 : * \li TOK_NODE_TYPE_NODE
6336 : *
6337 : * \return true if the f_last_token represents a node type.
6338 : */
6339 0 : bool token_is_node_type()
6340 : {
6341 0 : switch(f_last_token.f_token)
6342 : {
6343 0 : case token_t::tok_t::TOK_NCNAME:
6344 0 : if(f_last_token.f_string == "comment")
6345 : {
6346 0 : f_last_token.f_token = token_t::tok_t::TOK_NODE_TYPE_COMMENT;
6347 : }
6348 0 : else if(f_last_token.f_string == "text")
6349 : {
6350 0 : f_last_token.f_token = token_t::tok_t::TOK_NODE_TYPE_TEXT;
6351 : }
6352 0 : else if(f_last_token.f_string == "processing-instruction")
6353 : {
6354 0 : f_last_token.f_token = token_t::tok_t::TOK_NODE_TYPE_PROCESSING_INSTRUCTION;
6355 : }
6356 0 : else if(f_last_token.f_string == "node")
6357 : {
6358 0 : f_last_token.f_token = token_t::tok_t::TOK_NODE_TYPE_NODE;
6359 : }
6360 : else
6361 : {
6362 0 : return false;
6363 : }
6364 : #if __cplusplus >= 201700
6365 : [[fallthrough]];
6366 : #endif
6367 : case token_t::tok_t::TOK_NODE_TYPE_COMMENT:
6368 : case token_t::tok_t::TOK_NODE_TYPE_TEXT:
6369 : case token_t::tok_t::TOK_NODE_TYPE_PROCESSING_INSTRUCTION:
6370 : case token_t::tok_t::TOK_NODE_TYPE_NODE:
6371 0 : return true;
6372 :
6373 0 : default:
6374 0 : return false;
6375 :
6376 : }
6377 : }
6378 :
6379 : /** \brief Check whether an NCNAME represents an axis.
6380 : *
6381 : * This function transforms a TOK_NCNAME token in one of the axis types:
6382 : *
6383 : * \li TOK_AXIS_NAME_ANCESTOR
6384 : * \li TOK_AXIS_NAME_ANCESTOR_OR_SELF
6385 : * \li TOK_AXIS_NAME_ATTRIBUTE
6386 : * \li TOK_AXIS_NAME_CHILD
6387 : * \li TOK_AXIS_NAME_DESCENDANT
6388 : * \li TOK_AXIS_NAME_DESCENDANT_OR_SELF
6389 : * \li TOK_AXIS_NAME_FOLLOWING
6390 : * \li TOK_AXIS_NAME_FOLLOWING_SIBLING
6391 : * \li TOK_AXIS_NAME_NAMESPACE
6392 : * \li TOK_AXIS_NAME_PARENT
6393 : * \li TOK_AXIS_NAME_PRECEDING
6394 : * \li TOK_AXIS_NAME_PRECEDING_SIBLING
6395 : * \li TOK_AXIS_NAME_SELF
6396 : *
6397 : */
6398 0 : bool token_is_axis_name()
6399 : {
6400 0 : switch(f_last_token.f_token)
6401 : {
6402 0 : case token_t::tok_t::TOK_NCNAME:
6403 : // TODO: add one more level to test the first letter really fast
6404 0 : if(f_last_token.f_string == "ancestor")
6405 : {
6406 0 : f_last_token.f_token = token_t::tok_t::TOK_AXIS_NAME_ANCESTOR;
6407 : }
6408 0 : else if(f_last_token.f_string == "ancestor-or-self")
6409 : {
6410 0 : f_last_token.f_token = token_t::tok_t::TOK_AXIS_NAME_ANCESTOR_OR_SELF;
6411 : }
6412 0 : else if(f_last_token.f_string == "attribute")
6413 : {
6414 0 : f_last_token.f_token = token_t::tok_t::TOK_AXIS_NAME_ATTRIBUTE;
6415 : }
6416 0 : else if(f_last_token.f_string == "child")
6417 : {
6418 0 : f_last_token.f_token = token_t::tok_t::TOK_AXIS_NAME_CHILD;
6419 : }
6420 0 : else if(f_last_token.f_string == "descendant")
6421 : {
6422 0 : f_last_token.f_token = token_t::tok_t::TOK_AXIS_NAME_DESCENDANT;
6423 : }
6424 0 : else if(f_last_token.f_string == "descendant-or-self")
6425 : {
6426 0 : f_last_token.f_token = token_t::tok_t::TOK_AXIS_NAME_DESCENDANT_OR_SELF;
6427 : }
6428 0 : else if(f_last_token.f_string == "following")
6429 : {
6430 0 : f_last_token.f_token = token_t::tok_t::TOK_AXIS_NAME_FOLLOWING;
6431 : }
6432 0 : else if(f_last_token.f_string == "following-sibling")
6433 : {
6434 0 : f_last_token.f_token = token_t::tok_t::TOK_AXIS_NAME_FOLLOWING_SIBLING;
6435 : }
6436 0 : else if(f_last_token.f_string == "namespace")
6437 : {
6438 0 : f_last_token.f_token = token_t::tok_t::TOK_AXIS_NAME_NAMESPACE;
6439 : }
6440 0 : else if(f_last_token.f_string == "parent")
6441 : {
6442 0 : f_last_token.f_token = token_t::tok_t::TOK_AXIS_NAME_PARENT;
6443 : }
6444 0 : else if(f_last_token.f_string == "preceding")
6445 : {
6446 0 : f_last_token.f_token = token_t::tok_t::TOK_AXIS_NAME_PRECEDING;
6447 : }
6448 0 : else if(f_last_token.f_string == "preceding-sibling")
6449 : {
6450 0 : f_last_token.f_token = token_t::tok_t::TOK_AXIS_NAME_PRECEDING_SIBLING;
6451 : }
6452 0 : else if(f_last_token.f_string == "self")
6453 : {
6454 0 : f_last_token.f_token = token_t::tok_t::TOK_AXIS_NAME_SELF;
6455 : }
6456 : #if __cplusplus >= 201700
6457 : [[fallthrough]];
6458 : #endif
6459 : case token_t::tok_t::TOK_AXIS_NAME_ANCESTOR:
6460 : case token_t::tok_t::TOK_AXIS_NAME_ANCESTOR_OR_SELF:
6461 : case token_t::tok_t::TOK_AXIS_NAME_ATTRIBUTE:
6462 : case token_t::tok_t::TOK_AXIS_NAME_CHILD:
6463 : case token_t::tok_t::TOK_AXIS_NAME_DESCENDANT:
6464 : case token_t::tok_t::TOK_AXIS_NAME_DESCENDANT_OR_SELF:
6465 : case token_t::tok_t::TOK_AXIS_NAME_FOLLOWING:
6466 : case token_t::tok_t::TOK_AXIS_NAME_FOLLOWING_SIBLING:
6467 : case token_t::tok_t::TOK_AXIS_NAME_NAMESPACE:
6468 : case token_t::tok_t::TOK_AXIS_NAME_PARENT:
6469 : case token_t::tok_t::TOK_AXIS_NAME_PRECEDING:
6470 : case token_t::tok_t::TOK_AXIS_NAME_PRECEDING_SIBLING:
6471 : case token_t::tok_t::TOK_AXIS_NAME_SELF:
6472 0 : return true;
6473 :
6474 0 : default:
6475 0 : return false;
6476 :
6477 : }
6478 : }
6479 :
6480 0 : void add_to_program(QDomXPath::instruction_t inst)
6481 : {
6482 0 : f_program.push_back(inst);
6483 0 : }
6484 :
6485 0 : void append_instruction(QDomXPath::instruction_t inst)
6486 : {
6487 0 : add_to_program(inst);
6488 :
6489 0 : if(f_show_commands)
6490 : {
6491 0 : disassemble_instruction(f_program.size() - 1);
6492 : }
6493 0 : }
6494 :
6495 0 : void append_push_string(const QString& string)
6496 : {
6497 0 : int offset(f_program.size());
6498 :
6499 : // TODO: make sure that an NCNAME can be pushed as a string instead
6500 : // of an NCNAME
6501 0 : if(string.isEmpty())
6502 : {
6503 : // push_empty_string
6504 0 : add_to_program(INST_PUSH_EMPTY_STRING);
6505 : }
6506 0 : else if(string == "*")
6507 : {
6508 : // push_any
6509 0 : add_to_program(INST_PUSH_ANY_STRING);
6510 : }
6511 : else
6512 : {
6513 : // push_string
6514 0 : std::string str(string.toUtf8().data());
6515 0 : const size_t imax(str.length());
6516 0 : if(imax < 256)
6517 : {
6518 0 : add_to_program(INST_PUSH_SMALL_STRING);
6519 0 : add_to_program(static_cast<QDomXPath::instruction_t>(imax));
6520 : // TODO: use memcpy
6521 0 : for(size_t i(0); i < imax; ++i)
6522 : {
6523 0 : add_to_program(str[i]);
6524 : }
6525 : }
6526 0 : else if(imax < 65536)
6527 : {
6528 0 : add_to_program(INST_PUSH_MEDIUM_STRING);
6529 0 : add_to_program(static_cast<QDomXPath::instruction_t>(imax >> 8));
6530 0 : add_to_program(static_cast<QDomXPath::instruction_t>(imax));
6531 : // TODO: use memcpy
6532 0 : for(size_t i(0); i < imax; ++i)
6533 : {
6534 0 : add_to_program(str[i]);
6535 : }
6536 : }
6537 : else
6538 : {
6539 0 : add_to_program(INST_PUSH_LARGE_STRING);
6540 0 : add_to_program(static_cast<QDomXPath::instruction_t>(imax >> 24));
6541 0 : add_to_program(static_cast<QDomXPath::instruction_t>(imax >> 16));
6542 0 : add_to_program(static_cast<QDomXPath::instruction_t>(imax >> 8));
6543 0 : add_to_program(static_cast<QDomXPath::instruction_t>(imax));
6544 : // TODO: use memcpy
6545 0 : for(size_t i(0); i < imax; ++i)
6546 : {
6547 0 : add_to_program(str[i]);
6548 : }
6549 : }
6550 : }
6551 :
6552 0 : if(f_show_commands)
6553 : {
6554 0 : disassemble_instruction(offset);
6555 : }
6556 0 : }
6557 :
6558 :
6559 0 : void append_push_boolean(const bool boolean)
6560 : {
6561 : // note: I used a terciary here, but that prevents the compiler from
6562 : // optimizing the INST_PUSH_TRUE/FALSE and the link fails!?
6563 0 : if(boolean)
6564 : {
6565 0 : append_instruction(INST_PUSH_TRUE);
6566 : }
6567 : else
6568 : {
6569 0 : append_instruction(INST_PUSH_FALSE);
6570 : }
6571 0 : }
6572 :
6573 :
6574 0 : void append_push_integer(const int64_t integer)
6575 : {
6576 0 : int offset(f_program.size());
6577 :
6578 0 : if(integer == 0)
6579 : {
6580 0 : add_to_program(INST_PUSH_ZERO);
6581 : }
6582 0 : else if(integer >= 0 && integer < 256)
6583 : {
6584 0 : add_to_program(INST_PUSH_BYTE);
6585 0 : add_to_program(static_cast<QDomXPath::instruction_t>(integer));
6586 : }
6587 0 : else if(integer >= -256 && integer < 0)
6588 : {
6589 0 : add_to_program(INST_PUSH_NEGATIVE_BYTE);
6590 0 : add_to_program(static_cast<QDomXPath::instruction_t>(integer));
6591 : }
6592 0 : else if(integer >= 0 && integer < 65536)
6593 : {
6594 0 : add_to_program(INST_PUSH_SHORT);
6595 0 : add_to_program(static_cast<QDomXPath::instruction_t>(integer >> 8));
6596 0 : add_to_program(static_cast<QDomXPath::instruction_t>(integer));
6597 : }
6598 0 : else if(integer >= -65536 && integer < 0)
6599 : {
6600 0 : add_to_program(INST_PUSH_NEGATIVE_SHORT);
6601 0 : add_to_program(static_cast<QDomXPath::instruction_t>(integer >> 8));
6602 0 : add_to_program(static_cast<QDomXPath::instruction_t>(integer));
6603 : }
6604 0 : else if(integer >= 0 && integer < 0x100000000LL)
6605 : {
6606 0 : add_to_program(INST_PUSH_LONG);
6607 0 : add_to_program(static_cast<QDomXPath::instruction_t>(integer >> 24));
6608 0 : add_to_program(static_cast<QDomXPath::instruction_t>(integer >> 16));
6609 0 : add_to_program(static_cast<QDomXPath::instruction_t>(integer >> 8));
6610 0 : add_to_program(static_cast<QDomXPath::instruction_t>(integer));
6611 : }
6612 0 : else if(integer >= -0x100000000LL && integer < 0)
6613 : {
6614 0 : add_to_program(INST_PUSH_NEGATIVE_LONG);
6615 0 : add_to_program(static_cast<QDomXPath::instruction_t>(integer >> 24));
6616 0 : add_to_program(static_cast<QDomXPath::instruction_t>(integer >> 16));
6617 0 : add_to_program(static_cast<QDomXPath::instruction_t>(integer >> 8));
6618 0 : add_to_program(static_cast<QDomXPath::instruction_t>(integer));
6619 : }
6620 : else
6621 : {
6622 0 : add_to_program(INST_PUSH_LONGLONG);
6623 0 : add_to_program(static_cast<QDomXPath::instruction_t>(integer >> 56));
6624 0 : add_to_program(static_cast<QDomXPath::instruction_t>(integer >> 48));
6625 0 : add_to_program(static_cast<QDomXPath::instruction_t>(integer >> 40));
6626 0 : add_to_program(static_cast<QDomXPath::instruction_t>(integer >> 32));
6627 0 : add_to_program(static_cast<QDomXPath::instruction_t>(integer >> 24));
6628 0 : add_to_program(static_cast<QDomXPath::instruction_t>(integer >> 16));
6629 0 : add_to_program(static_cast<QDomXPath::instruction_t>(integer >> 8));
6630 0 : add_to_program(static_cast<QDomXPath::instruction_t>(integer));
6631 : }
6632 :
6633 0 : if(f_show_commands)
6634 : {
6635 0 : disassemble_instruction(offset);
6636 : }
6637 0 : }
6638 :
6639 0 : void append_push_integer(node_type_t const type)
6640 : {
6641 0 : append_push_integer(static_cast<int64_t>(type));
6642 0 : }
6643 :
6644 0 : void append_push_integer(axis_t const type)
6645 : {
6646 0 : append_push_integer(static_cast<int64_t>(type));
6647 0 : }
6648 :
6649 0 : void append_push_integer(internal_func_t const type)
6650 : {
6651 0 : append_push_integer(static_cast<int64_t>(type));
6652 0 : }
6653 :
6654 0 : void append_push_double(double const real)
6655 : {
6656 0 : int offset(f_program.size());
6657 :
6658 : #pragma GCC diagnostic push
6659 : #pragma GCC diagnostic ignored "-Wfloat-equal"
6660 0 : if(real == 0.0)
6661 : #pragma GCC diagnostic pop
6662 : {
6663 0 : add_to_program(INST_PUSH_DOUBLE_ZERO);
6664 : }
6665 : else
6666 : {
6667 0 : add_to_program(INST_PUSH_DOUBLE);
6668 : union
6669 : {
6670 : uint64_t f_int;
6671 : double f_dbl;
6672 : } convert;
6673 0 : convert.f_dbl = real;
6674 0 : add_to_program(static_cast<QDomXPath::instruction_t>(convert.f_int >> 56));
6675 0 : add_to_program(static_cast<QDomXPath::instruction_t>(convert.f_int >> 48));
6676 0 : add_to_program(static_cast<QDomXPath::instruction_t>(convert.f_int >> 40));
6677 0 : add_to_program(static_cast<QDomXPath::instruction_t>(convert.f_int >> 32));
6678 0 : add_to_program(static_cast<QDomXPath::instruction_t>(convert.f_int >> 24));
6679 0 : add_to_program(static_cast<QDomXPath::instruction_t>(convert.f_int >> 16));
6680 0 : add_to_program(static_cast<QDomXPath::instruction_t>(convert.f_int >> 8));
6681 0 : add_to_program(static_cast<QDomXPath::instruction_t>(convert.f_int));
6682 : }
6683 :
6684 0 : if(f_show_commands)
6685 : {
6686 0 : disassemble_instruction(offset);
6687 : }
6688 0 : }
6689 :
6690 0 : void append_push_token(const token_t& token)
6691 : {
6692 0 : switch(token.f_token)
6693 : {
6694 0 : case token_t::tok_t::TOK_ASTERISK:
6695 0 : append_push_string("*");
6696 0 : break;
6697 :
6698 0 : case token_t::tok_t::TOK_STRING:
6699 : case token_t::tok_t::TOK_PREFIX: // this is like a string
6700 : case token_t::tok_t::TOK_NCNAME: // this can be a lie (in case of variable names, it can include a colon)
6701 0 : append_push_string(token.f_string);
6702 0 : break;
6703 :
6704 0 : case token_t::tok_t::TOK_INTEGER:
6705 0 : append_push_integer(token.f_integer);
6706 0 : break;
6707 :
6708 0 : case token_t::tok_t::TOK_REAL:
6709 0 : append_push_double(token.f_real);
6710 0 : break;
6711 :
6712 0 : default:
6713 0 : throw QDomXPathException_InternalError(QString("unexpected token type (%1/%2) in an append_push_token() call").arg(static_cast<int>(token.f_token)).arg(token.f_string));
6714 :
6715 : }
6716 0 : }
6717 :
6718 : void append_function(const QDomXPath::program_t& function)
6719 : {
6720 : const size_t size(function.size());
6721 : if(size < 65536)
6722 : {
6723 : append_instruction(INST_SMALL_FUNCTION);
6724 : append_instruction(static_cast<QDomXPath::instruction_t>(size >> 8));
6725 : append_instruction(static_cast<QDomXPath::instruction_t>(size));
6726 : }
6727 : else
6728 : {
6729 : append_instruction(INST_LARGE_FUNCTION);
6730 : append_instruction(static_cast<QDomXPath::instruction_t>(size >> 24));
6731 : append_instruction(static_cast<QDomXPath::instruction_t>(size >> 16));
6732 : append_instruction(static_cast<QDomXPath::instruction_t>(size >> 8));
6733 : append_instruction(static_cast<QDomXPath::instruction_t>(size));
6734 : }
6735 : f_program += function;
6736 : }
6737 :
6738 :
6739 0 : void append_axis(const token_t& axis, const token_t& prefix, const token_t& local_part)
6740 : {
6741 : // if the prefix is marked as "undefined" then we have a NodeType
6742 : // instead of a name
6743 0 : if(prefix.f_token == token_t::tok_t::TOK_UNDEFINED)
6744 : {
6745 : // Axis '::' NodeType '(' ')'
6746 0 : switch(local_part.f_token)
6747 : {
6748 0 : case token_t::tok_t::TOK_NODE_TYPE_COMMENT: append_push_integer(node_type_t::NODE_TYPE_COMMENT); break;
6749 0 : case token_t::tok_t::TOK_NODE_TYPE_NODE: append_push_integer(node_type_t::NODE_TYPE_NODE); break;
6750 0 : case token_t::tok_t::TOK_NODE_TYPE_PROCESSING_INSTRUCTION: append_push_integer(node_type_t::NODE_TYPE_PROCESSING_INSTRUCTION); break;
6751 0 : case token_t::tok_t::TOK_NODE_TYPE_TEXT: append_push_integer(node_type_t::NODE_TYPE_TEXT); break;
6752 :
6753 0 : default:
6754 0 : throw QDomXPathException_InvalidError("invalid node type");
6755 :
6756 : }
6757 :
6758 : // if 'processing-instruction' then we could have a string here
6759 : // if empty, then no name was specified to the NodeType '(' ')'
6760 0 : append_push_string(axis.f_string);
6761 : }
6762 : else
6763 : {
6764 : // push the name, it may be "*:*" if the node type is specified
6765 0 : append_push_token(local_part);
6766 0 : append_push_token(prefix);
6767 : }
6768 :
6769 : // TODO: node type
6770 :
6771 0 : switch(axis.f_token)
6772 : {
6773 0 : case token_t::tok_t::TOK_AXIS_NAME_ANCESTOR: append_push_integer(axis_t::AXIS_ANCESTOR); break;
6774 0 : case token_t::tok_t::TOK_AXIS_NAME_ANCESTOR_OR_SELF: append_push_integer(axis_t::AXIS_ANCESTOR_OR_SELF); break;
6775 0 : case token_t::tok_t::TOK_AXIS_NAME_ATTRIBUTE: append_push_integer(axis_t::AXIS_ATTRIBUTE); break;
6776 0 : case token_t::tok_t::TOK_AXIS_NAME_CHILD: append_push_integer(axis_t::AXIS_CHILD); break;
6777 0 : case token_t::tok_t::TOK_AXIS_NAME_DESCENDANT: append_push_integer(axis_t::AXIS_DESCENDANT); break;
6778 0 : case token_t::tok_t::TOK_AXIS_NAME_DESCENDANT_OR_SELF: append_push_integer(axis_t::AXIS_DESCENDANT_OR_SELF); break;
6779 0 : case token_t::tok_t::TOK_AXIS_NAME_FOLLOWING: append_push_integer(axis_t::AXIS_FOLLOWING); break;
6780 0 : case token_t::tok_t::TOK_AXIS_NAME_FOLLOWING_SIBLING: append_push_integer(axis_t::AXIS_FOLLOWING_SIBLING); break;
6781 0 : case token_t::tok_t::TOK_AXIS_NAME_NAMESPACE: append_push_integer(axis_t::AXIS_NAMESPACE); break;
6782 0 : case token_t::tok_t::TOK_AXIS_NAME_PARENT: append_push_integer(axis_t::AXIS_PARENT); break;
6783 0 : case token_t::tok_t::TOK_AXIS_NAME_PRECEDING: append_push_integer(axis_t::AXIS_PRECEDING); break;
6784 0 : case token_t::tok_t::TOK_AXIS_NAME_PRECEDING_SIBLING: append_push_integer(axis_t::AXIS_PRECEDING_SIBLING); break;
6785 0 : case token_t::tok_t::TOK_AXIS_NAME_SELF: append_push_integer(axis_t::AXIS_SELF); break;
6786 :
6787 0 : default:
6788 0 : throw QDomXPathException_InvalidError("invalid axis type");
6789 :
6790 : }
6791 :
6792 0 : append_instruction(INST_AXIS);
6793 0 : }
6794 :
6795 :
6796 : void append_push_for_jump(const QString& label)
6797 : {
6798 : if(label.isEmpty())
6799 : {
6800 : throw QDomXPathException_InternalError("pushing for a future label with an empty string is not supported");
6801 : }
6802 : if(f_show_commands)
6803 : {
6804 : std::cout << "=== push for jump (" << f_program.size() << ")\n";
6805 : }
6806 : f_future_labels[label].push_back(f_program.size());
6807 : append_push_integer(0x1111); // reserve 2 bytes for pc
6808 : }
6809 :
6810 :
6811 : void mark_with_label(const QString& label)
6812 : {
6813 : int offset(f_program.size());
6814 : f_labels[label] = offset;
6815 :
6816 : if(f_future_labels.contains(label))
6817 : {
6818 : future_labels_t::iterator future(f_future_labels.find(label));
6819 : const int imax((*future).size());
6820 : for(int i(0); i < imax; ++i)
6821 : {
6822 : int pc((*future)[i]);
6823 : f_program[pc + 1] = static_cast<instruction_t>(offset >> 8);
6824 : f_program[pc + 2] = static_cast<instruction_t>(offset);
6825 :
6826 : if(f_show_commands)
6827 : {
6828 : std::cout << "# Fix offset -- ";
6829 : disassemble_instruction(pc);
6830 : }
6831 : }
6832 : f_future_labels.erase(future);
6833 : }
6834 : }
6835 :
6836 :
6837 :
6838 0 : void unary_expr()
6839 : {
6840 0 : int negate(0);
6841 : for(;;)
6842 : {
6843 0 : if(f_last_token.f_token == token_t::tok_t::TOK_MINUS)
6844 : {
6845 0 : negate ^= 1;
6846 : }
6847 0 : else if(f_last_token.f_token != token_t::tok_t::TOK_PLUS) // XPath 2.0 allows unary '+'
6848 : {
6849 0 : break;
6850 : }
6851 0 : get_token();
6852 : }
6853 0 : union_expr();
6854 0 : if(negate)
6855 : {
6856 0 : append_instruction(INST_NEGATE);
6857 : }
6858 0 : }
6859 :
6860 0 : void multiplicative_expr()
6861 : {
6862 0 : unary_expr();
6863 : for(;;)
6864 : {
6865 0 : instruction_t inst(INST_END);
6866 0 : switch(f_last_token.f_token)
6867 : {
6868 0 : case token_t::tok_t::TOK_ASTERISK:
6869 0 : inst = INST_MULTIPLY;
6870 0 : break;
6871 :
6872 0 : case token_t::tok_t::TOK_NCNAME:
6873 0 : if(f_last_token.f_string == "div")
6874 : {
6875 0 : inst = INST_DIVIDE;
6876 : }
6877 0 : else if(f_last_token.f_string == "idiv")
6878 : {
6879 0 : inst = INST_IDIVIDE;
6880 : }
6881 0 : else if(f_last_token.f_string == "mod")
6882 : {
6883 0 : inst = INST_MODULO;
6884 : }
6885 : else
6886 : {
6887 0 : return;
6888 : }
6889 0 : break;
6890 :
6891 0 : default:
6892 0 : return;
6893 :
6894 : }
6895 0 : get_token();
6896 0 : unary_expr();
6897 0 : append_instruction(inst);
6898 0 : }
6899 : }
6900 :
6901 0 : void additive_expr()
6902 : {
6903 0 : multiplicative_expr();
6904 : for(;;)
6905 : {
6906 0 : instruction_t inst(INST_END);
6907 0 : switch(f_last_token.f_token)
6908 : {
6909 0 : case token_t::tok_t::TOK_PLUS:
6910 0 : inst = INST_ADD;
6911 0 : break;
6912 :
6913 0 : case token_t::tok_t::TOK_MINUS:
6914 0 : inst = INST_SUBTRACT;
6915 0 : break;
6916 :
6917 0 : default:
6918 0 : return;
6919 :
6920 : }
6921 0 : get_token();
6922 0 : multiplicative_expr();
6923 0 : append_instruction(inst);
6924 0 : }
6925 : }
6926 :
6927 0 : void relational_expr()
6928 : {
6929 0 : additive_expr();
6930 : for(;;)
6931 : {
6932 0 : instruction_t inst(INST_END);
6933 0 : switch(f_last_token.f_token)
6934 : {
6935 0 : case token_t::tok_t::TOK_LESS_THAN:
6936 0 : inst = INST_LESS_THAN;
6937 0 : break;
6938 :
6939 0 : case token_t::tok_t::TOK_LESS_OR_EQUAL:
6940 0 : inst = INST_LESS_OR_EQUAL;
6941 0 : break;
6942 :
6943 0 : case token_t::tok_t::TOK_GREATER_THAN:
6944 0 : inst = INST_GREATER_THAN;
6945 0 : break;
6946 :
6947 0 : case token_t::tok_t::TOK_GREATER_OR_EQUAL:
6948 0 : inst = INST_GREATER_OR_EQUAL;
6949 0 : break;
6950 :
6951 0 : default:
6952 0 : return;
6953 :
6954 : }
6955 0 : get_token();
6956 0 : additive_expr();
6957 0 : append_instruction(inst);
6958 0 : }
6959 : }
6960 :
6961 0 : void equality_expr()
6962 : {
6963 0 : relational_expr();
6964 : for(;;)
6965 : {
6966 0 : instruction_t inst(INST_END);
6967 0 : switch(f_last_token.f_token)
6968 : {
6969 0 : case token_t::tok_t::TOK_EQUAL:
6970 0 : inst = INST_EQUAL;
6971 0 : break;
6972 :
6973 0 : case token_t::tok_t::TOK_NOT_EQUAL:
6974 0 : inst = INST_NOT_EQUAL;
6975 0 : break;
6976 :
6977 0 : default:
6978 0 : return;
6979 :
6980 : }
6981 0 : get_token();
6982 0 : relational_expr();
6983 0 : append_instruction(inst);
6984 0 : }
6985 : }
6986 :
6987 0 : void and_expr()
6988 : {
6989 0 : equality_expr();
6990 0 : while(f_last_token.f_token == token_t::tok_t::TOK_NCNAME && f_last_token.f_string == "and")
6991 : {
6992 0 : get_token();
6993 0 : equality_expr();
6994 0 : append_instruction(INST_AND);
6995 : }
6996 0 : }
6997 :
6998 : /** \brief The OrExpr
6999 : *
7000 : * The OrExpr is a one to one equivalent to the Expr.
7001 : *
7002 : * It appears between parenthesis from within a location path.
7003 : */
7004 0 : void or_expr()
7005 : {
7006 0 : and_expr();
7007 0 : while(f_last_token.f_token == token_t::tok_t::TOK_NCNAME && f_last_token.f_string == "or")
7008 : {
7009 0 : get_token();
7010 0 : and_expr();
7011 0 : append_instruction(INST_OR);
7012 : }
7013 0 : }
7014 :
7015 :
7016 : /** \brief Parse a function call parameters.
7017 : *
7018 : * This function handles internal function calls such as fn:position().
7019 : *
7020 : * If the prefix is not defined, we use 'fn' as the default. I'm not
7021 : * totally sure that this is correct since some internal functions
7022 : * are defined with the xs prefix. (the op:... functions are operators
7023 : * only so it wouldn't apply as a default anyway.)
7024 : *
7025 : * Note that for speed a certain number of internal functions are fully
7026 : * parsed here and transformed into code directly instead of an INST_CALL
7027 : * instruction.
7028 : *
7029 : * \param[in] prefix_token The prefix of the function, if "*" or "", then "fn" is used
7030 : * \param[in] local_part The name of the function (i.e. "position", "last", ...)
7031 : */
7032 0 : void function_call(token_t prefix_token, token_t local_part)
7033 : {
7034 : // the last token read was the '(', now we read the arguments which
7035 : // each are full expressions:
7036 : // Argument ::= Expr
7037 : // Expr ::= OrExpr
7038 :
7039 : // skip the '('
7040 0 : get_token();
7041 :
7042 : // make sure we have the default namespace if the user did not specify it
7043 0 : if(prefix_token.f_string == "*" || prefix_token.f_string.isEmpty())
7044 : {
7045 : // the default namespace is "fn"
7046 0 : prefix_token.f_string = "fn";
7047 : }
7048 :
7049 : // set of internal functions that we can transform in one or a very few
7050 : // basic instructions
7051 0 : if(prefix_token.f_string == "fn")
7052 : {
7053 0 : switch(local_part.f_string[0].unicode())
7054 : {
7055 0 : case 'c':
7056 0 : if(local_part.f_string == "ceiling")
7057 : {
7058 0 : if(f_last_token.f_token == token_t::tok_t::TOK_CLOSE_PARENTHESIS)
7059 : {
7060 0 : throw QDomXPathException_SyntaxError("expected one parameter for the ceiling() function");
7061 : }
7062 0 : or_expr();
7063 0 : if(f_last_token.f_token != token_t::tok_t::TOK_CLOSE_PARENTHESIS)
7064 : {
7065 0 : throw QDomXPathException_SyntaxError("expected exactly one parameter for the ceiling() function");
7066 : }
7067 0 : append_instruction(INST_CEILING);
7068 : // skip the ')'
7069 0 : get_token();
7070 0 : return;
7071 : }
7072 0 : if(local_part.f_string == "count")
7073 : {
7074 0 : if(f_last_token.f_token == token_t::tok_t::TOK_CLOSE_PARENTHESIS)
7075 : {
7076 0 : throw QDomXPathException_SyntaxError("expected one parameter for the count() function");
7077 : }
7078 0 : or_expr();
7079 0 : if(f_last_token.f_token != token_t::tok_t::TOK_CLOSE_PARENTHESIS)
7080 : {
7081 0 : throw QDomXPathException_SyntaxError("expected exactly one parameter for the count() function");
7082 : }
7083 0 : append_instruction(INST_NODE_SET_SIZE);
7084 : // skip the ')'
7085 0 : get_token();
7086 0 : return;
7087 : }
7088 0 : break;
7089 :
7090 0 : case 'e':
7091 0 : if(local_part.f_string == "empty"
7092 0 : || local_part.f_string == "exists")
7093 : {
7094 0 : if(f_last_token.f_token == token_t::tok_t::TOK_CLOSE_PARENTHESIS)
7095 : {
7096 0 : throw QDomXPathException_SyntaxError("expected one parameter for the empty() function");
7097 : }
7098 0 : or_expr();
7099 0 : if(f_last_token.f_token != token_t::tok_t::TOK_CLOSE_PARENTHESIS)
7100 : {
7101 0 : throw QDomXPathException_SyntaxError("expected exactly one parameter for the empty() function");
7102 : }
7103 0 : append_instruction(INST_NODE_SET_SIZE);
7104 0 : append_push_integer(0);
7105 0 : append_instruction(INST_EQUAL);
7106 : // skip the ')'
7107 0 : get_token();
7108 0 : return;
7109 : }
7110 0 : break;
7111 :
7112 0 : case 'f':
7113 0 : if(local_part.f_string == "false")
7114 : {
7115 0 : if(f_last_token.f_token != token_t::tok_t::TOK_CLOSE_PARENTHESIS)
7116 : {
7117 0 : throw QDomXPathException_SyntaxError("expected ')' immediately for the false() function does not accept parameters");
7118 : }
7119 0 : append_push_boolean(false);
7120 : // skip the ')'
7121 0 : get_token();
7122 0 : return;
7123 : }
7124 0 : if(local_part.f_string == "floor")
7125 : {
7126 0 : if(f_last_token.f_token == token_t::tok_t::TOK_CLOSE_PARENTHESIS)
7127 : {
7128 0 : throw QDomXPathException_SyntaxError("expected one parameter for the floor() function");
7129 : }
7130 0 : or_expr();
7131 0 : if(f_last_token.f_token != token_t::tok_t::TOK_CLOSE_PARENTHESIS)
7132 : {
7133 0 : throw QDomXPathException_SyntaxError("expected exactly one parameter for the floor() function");
7134 : }
7135 0 : append_instruction(INST_FLOOR);
7136 : // skip the ')'
7137 0 : get_token();
7138 0 : return;
7139 : }
7140 0 : break;
7141 :
7142 0 : case 'l':
7143 0 : if(local_part.f_string == "last")
7144 : {
7145 0 : if(f_last_token.f_token != token_t::tok_t::TOK_CLOSE_PARENTHESIS)
7146 : {
7147 0 : throw QDomXPathException_SyntaxError("expected ')' immediately for the last() function does not accept parameters");
7148 : }
7149 0 : append_instruction(INST_GET_NODE_SET);
7150 0 : append_instruction(INST_NODE_SET_SIZE);
7151 : // skip the ')'
7152 0 : get_token();
7153 0 : return;
7154 : }
7155 0 : break;
7156 :
7157 0 : case 'n':
7158 0 : if(local_part.f_string == "not")
7159 : {
7160 0 : if(f_last_token.f_token == token_t::tok_t::TOK_CLOSE_PARENTHESIS)
7161 : {
7162 0 : throw QDomXPathException_SyntaxError("expected one parameter for the not() function");
7163 : }
7164 0 : or_expr();
7165 0 : if(f_last_token.f_token != token_t::tok_t::TOK_CLOSE_PARENTHESIS)
7166 : {
7167 0 : throw QDomXPathException_SyntaxError("expected exactly one parameter for the not() function");
7168 : }
7169 0 : append_instruction(INST_NOT);
7170 : // skip the ')'
7171 0 : get_token();
7172 0 : return;
7173 : }
7174 0 : break;
7175 :
7176 0 : case 'p':
7177 0 : if(local_part.f_string == "position")
7178 : {
7179 0 : if(f_last_token.f_token != token_t::tok_t::TOK_CLOSE_PARENTHESIS)
7180 : {
7181 0 : throw QDomXPathException_SyntaxError("expected ')' immediately for the position() function does not accept parameters");
7182 : }
7183 0 : append_instruction(INST_GET_POSITION);
7184 : // skip the ')'
7185 0 : get_token();
7186 0 : return;
7187 : }
7188 0 : break;
7189 :
7190 0 : case 'r':
7191 0 : if(local_part.f_string == "round")
7192 : {
7193 0 : if(f_last_token.f_token == token_t::tok_t::TOK_CLOSE_PARENTHESIS)
7194 : {
7195 0 : throw QDomXPathException_SyntaxError("expected one parameter for the round() function");
7196 : }
7197 0 : or_expr();
7198 0 : if(f_last_token.f_token != token_t::tok_t::TOK_CLOSE_PARENTHESIS)
7199 : {
7200 0 : throw QDomXPathException_SyntaxError("expected exactly one parameter for the round() function");
7201 : }
7202 0 : append_instruction(INST_ROUND);
7203 : // skip the ')'
7204 0 : get_token();
7205 0 : return;
7206 : }
7207 0 : break;
7208 :
7209 0 : case 's':
7210 0 : if(local_part.f_string == "string-length")
7211 : {
7212 : // TODO add support for '.' instead of an argument
7213 0 : if(f_last_token.f_token == token_t::tok_t::TOK_CLOSE_PARENTHESIS)
7214 : {
7215 0 : throw QDomXPathException_SyntaxError("expected one parameter for the string-length() function");
7216 : }
7217 0 : or_expr();
7218 0 : if(f_last_token.f_token != token_t::tok_t::TOK_CLOSE_PARENTHESIS)
7219 : {
7220 0 : throw QDomXPathException_SyntaxError("expected exactly one parameter for the string-length() function");
7221 : }
7222 0 : append_instruction(INST_STRING_LENGTH);
7223 : // skip the ')'
7224 0 : get_token();
7225 0 : return;
7226 : }
7227 0 : break;
7228 :
7229 0 : case 't':
7230 0 : if(local_part.f_string == "true")
7231 : {
7232 0 : if(f_last_token.f_token != token_t::tok_t::TOK_CLOSE_PARENTHESIS)
7233 : {
7234 0 : throw QDomXPathException_SyntaxError("expected ')' immediately for the true() function does not accept parameters");
7235 : }
7236 0 : append_push_boolean(true);
7237 : // skip the ')'
7238 0 : get_token();
7239 0 : return;
7240 : }
7241 0 : break;
7242 :
7243 : }
7244 : }
7245 :
7246 0 : int argc(0);
7247 0 : append_instruction(INST_PUSH_END_OF_ARGUMENTS);
7248 0 : if(f_last_token.f_token != token_t::tok_t::TOK_CLOSE_PARENTHESIS)
7249 : {
7250 0 : ++argc;
7251 0 : or_expr();
7252 0 : while(f_last_token.f_token == token_t::tok_t::TOK_COMMA)
7253 : {
7254 0 : ++argc;
7255 0 : get_token();
7256 0 : or_expr();
7257 : }
7258 0 : if(f_last_token.f_token != token_t::tok_t::TOK_CLOSE_PARENTHESIS)
7259 : {
7260 0 : throw QDomXPathException_SyntaxError("expected ')' or ',' in the list of argument to a function call");
7261 : }
7262 : }
7263 : // skip the ')'
7264 0 : get_token();
7265 :
7266 : // by default we expect no parameters
7267 0 : int min_argc(0);
7268 0 : int max_argc(0);
7269 0 : internal_func_t f(internal_func_t::FUNC_UNKNOWN);
7270 :
7271 0 : switch(prefix_token.f_string[0].unicode())
7272 : {
7273 0 : case 'f':
7274 0 : if(prefix_token.f_string == "fn")
7275 : {
7276 0 : switch(local_part.f_string[0].unicode())
7277 : {
7278 0 : case 'a':
7279 0 : if(local_part.f_string == "avg")
7280 : {
7281 0 : f = internal_func_t::FUNC_AVG;
7282 0 : max_argc = -1; // any number of parameters
7283 : }
7284 0 : break;
7285 :
7286 0 : case 'm':
7287 0 : if(local_part.f_string == "max")
7288 : {
7289 0 : f = internal_func_t::FUNC_MAX;
7290 0 : max_argc = -1; // any number of parameters
7291 : }
7292 0 : else if(local_part.f_string == "min")
7293 : {
7294 0 : f = internal_func_t::FUNC_MIN;
7295 0 : max_argc = -1; // any number of parameters
7296 : }
7297 0 : break;
7298 :
7299 0 : case 's':
7300 0 : if(local_part.f_string == "sum")
7301 : {
7302 0 : f = internal_func_t::FUNC_SUM;
7303 0 : max_argc = -1; // any number of parameters
7304 : }
7305 0 : break;
7306 :
7307 : }
7308 : }
7309 0 : break;
7310 :
7311 0 : default:
7312 0 : break;
7313 :
7314 : }
7315 :
7316 : // we do not yet support user defined functions
7317 0 : if(f == internal_func_t::FUNC_UNKNOWN)
7318 : {
7319 0 : throw QDomXPathException_UnknownFunctionError(QString("'%1' is not a known function (we may not yet support it...)")
7320 0 : .arg(prefix_token.f_string + ":" + local_part.f_string));
7321 : }
7322 0 : if(argc < min_argc)
7323 : {
7324 0 : throw QDomXPathException_UnknownFunctionError(QString("'%1' expects at least %2 arguments, but got %3 instead")
7325 0 : .arg(prefix_token.f_string + ":" + local_part.f_string)
7326 0 : .arg(min_argc).arg(argc));
7327 : }
7328 0 : if(max_argc != -1 && argc > max_argc)
7329 : {
7330 0 : throw QDomXPathException_UnknownFunctionError(QString("'%1' expects at most %2 arguments, it got %3 instead")
7331 0 : .arg(prefix_token.f_string + ":" + local_part.f_string)
7332 0 : .arg(max_argc).arg(argc));
7333 : }
7334 :
7335 0 : append_push_integer(f);
7336 0 : append_instruction(INST_CALL);
7337 : }
7338 :
7339 :
7340 0 : void predicate()
7341 : {
7342 0 : QString save_predicate_variable(f_predicate_variable);
7343 0 : ++f_label_counter;
7344 0 : f_predicate_variable = QString("$%1").arg(f_label_counter);
7345 :
7346 0 : append_instruction(INST_CREATE_NODE_CONTEXT);
7347 :
7348 : // we just had an axis, now we have n predicates following
7349 : // only the problem is that each predicate has to be applied
7350 : // to each node as a context node of the current state
7351 0 : do
7352 : {
7353 : // skip the '['
7354 0 : get_token();
7355 :
7356 : // 'next_node' label
7357 0 : const int next_node(f_program.size());
7358 :
7359 0 : append_instruction(INST_GET_CONTEXT_NODE);
7360 0 : append_push_string(f_predicate_variable);
7361 0 : append_instruction(INST_SET_VARIABLE);
7362 :
7363 0 : or_expr();
7364 0 : if(f_last_token.f_token != token_t::tok_t::TOK_CLOSE_SQUARE_BRACKET)
7365 : {
7366 0 : throw QDomXPathException_SyntaxError("missing ']' to close a Predicate");
7367 : }
7368 :
7369 0 : append_instruction(INST_PREDICATE); // "apply" predicate
7370 : //append_instruction(INST_NEXT_CONTEXT_NODE);
7371 0 : append_push_integer(next_node);
7372 0 : append_instruction(INST_JUMP_IF_TRUE); // next_node
7373 0 : append_instruction(INST_GET_RESULT);
7374 0 : append_instruction(INST_SET_NODE_SET);
7375 0 : append_instruction(INST_PUSH_EMPTY_NODE_SET);
7376 0 : append_instruction(INST_SET_RESULT);
7377 :
7378 0 : get_token();
7379 : }
7380 0 : while(f_last_token.f_token == token_t::tok_t::TOK_OPEN_SQUARE_BRACKET);
7381 :
7382 0 : append_instruction(INST_GET_NODE_SET);
7383 0 : append_instruction(INST_POP_CONTEXT); // get rid of the context
7384 :
7385 0 : f_predicate_variable = save_predicate_variable;
7386 0 : }
7387 :
7388 0 : void location_path()
7389 : {
7390 0 : labels_t labels;
7391 :
7392 0 : QString predicate_variable(f_predicate_variable);
7393 0 : bool function_call_valid(f_last_token.f_token == token_t::tok_t::TOK_NCNAME);
7394 0 : bool first_round(true);
7395 : for(;;)
7396 : {
7397 0 : bool double_slash(false);
7398 0 : switch(f_last_token.f_token)
7399 : {
7400 0 : case token_t::tok_t::TOK_DOUBLE_SLASH:
7401 0 : double_slash = true;
7402 : #if __cplusplus >= 201700
7403 : [[fallthrough]];
7404 : #endif
7405 0 : case token_t::tok_t::TOK_SLASH:
7406 0 : if(first_round)
7407 : {
7408 : // this is absolute, start from the root
7409 :
7410 : // replace the current node-set into a node context
7411 : // that way we can properly loop through the list of
7412 : // nodes against this axis
7413 : //
7414 : // TODO: look into optimizating: return only one root
7415 : // per document...
7416 0 : if(!predicate_variable.isEmpty())
7417 : {
7418 0 : append_push_string(predicate_variable);
7419 0 : append_instruction(INST_GET_VARIABLE);
7420 0 : predicate_variable.clear();
7421 : }
7422 0 : append_instruction(INST_CREATE_NODE_CONTEXT);
7423 0 : labels.push_back(f_program.size());
7424 0 : append_instruction(INST_GET_CONTEXT_NODE);
7425 :
7426 0 : append_instruction(INST_ROOT);
7427 : }
7428 0 : get_token();
7429 0 : break;
7430 :
7431 0 : default:
7432 0 : if(!first_round)
7433 : {
7434 : // anything else is not part of the location path
7435 : // if we are in a relative path (this allows "/")
7436 0 : goto finished;
7437 : }
7438 0 : break;
7439 :
7440 : }
7441 0 : bool accept_predicate(true);
7442 0 : first_round = false;
7443 0 : token_t save_token(f_last_token);
7444 0 : token_t axis_token;
7445 0 : axis_token.f_token = double_slash ? token_t::tok_t::TOK_AXIS_NAME_DESCENDANT : token_t::tok_t::TOK_AXIS_NAME_CHILD;
7446 0 : axis_token.f_string = double_slash ? "descendant" : "child";
7447 0 : token_t prefix_token;
7448 0 : prefix_token.f_token = token_t::tok_t::TOK_PREFIX;
7449 0 : prefix_token.f_string = "*";
7450 0 : switch(f_last_token.f_token)
7451 : {
7452 0 : case token_t::tok_t::TOK_DOT:
7453 : // self
7454 0 : save_token.f_token = token_t::tok_t::TOK_NCNAME;
7455 0 : save_token.f_string = "*";
7456 0 : axis_token.f_token = token_t::tok_t::TOK_AXIS_NAME_SELF;
7457 0 : axis_token.f_string = "self";
7458 0 : accept_predicate = false;
7459 0 : goto axis_apply;
7460 :
7461 0 : case token_t::tok_t::TOK_DOUBLE_DOT:
7462 : // parent
7463 0 : save_token.f_token = token_t::tok_t::TOK_NCNAME;
7464 0 : save_token.f_string = "*";
7465 0 : axis_token.f_token = token_t::tok_t::TOK_AXIS_NAME_PARENT;
7466 0 : axis_token.f_string = "parent";
7467 0 : accept_predicate = false;
7468 0 : goto axis_apply;
7469 :
7470 0 : case token_t::tok_t::TOK_ASTERISK:
7471 : // '*' -- a name test by itself
7472 : // this is actually the default!
7473 0 : get_token();
7474 0 : goto axis_apply;
7475 :
7476 0 : case token_t::tok_t::TOK_AT:
7477 : // The '@' is the AbbreviatedAxisSpecifier
7478 : // '@' NoteTest Predicate*
7479 : // 'attribute' :: NodeTest Predicate*
7480 0 : axis_token.f_token = token_t::tok_t::TOK_AXIS_NAME_ATTRIBUTE;
7481 0 : axis_token.f_string = "attribute";
7482 0 : goto axis_name_attribute;
7483 :
7484 0 : case token_t::tok_t::TOK_NCNAME:
7485 0 : get_token();
7486 0 : if(f_last_token.f_token == token_t::tok_t::TOK_DOUBLE_COLON)
7487 : {
7488 0 : function_call_valid = false;
7489 : // the NCNAME before '::' is an AxisName
7490 : // AxisName '::' NoteTest Predicate*
7491 0 : f_last_token = save_token;
7492 0 : if(!token_is_axis_name())
7493 : {
7494 0 : throw QDomXPathException_SyntaxError(QString("a double colon (::) must be preceded by a valid axis name, \"%1\" was not recognized as such").arg(f_last_token.f_string));
7495 : }
7496 0 : axis_token = f_last_token;
7497 0 : axis_name_attribute:
7498 0 : get_token(); // NodeTest
7499 0 : save_token = f_last_token;
7500 0 : if(f_last_token.f_token == token_t::tok_t::TOK_ASTERISK)
7501 : {
7502 : // no specific prefix or local part
7503 0 : get_token();
7504 0 : goto axis_apply;
7505 : }
7506 0 : if(f_last_token.f_token != token_t::tok_t::TOK_NCNAME)
7507 : {
7508 0 : throw QDomXPathException_SyntaxError("a double colon (::) must be followed by an NCName or '*'");
7509 : }
7510 0 : get_token();
7511 : }
7512 0 : if(f_last_token.f_token == token_t::tok_t::TOK_COLON)
7513 : {
7514 : // namespace ':' NCName
7515 : // namespace ':' '*'
7516 0 : prefix_token = save_token;
7517 0 : get_token();
7518 0 : save_token = f_last_token;
7519 0 : get_token();
7520 0 : switch(save_token.f_token)
7521 : {
7522 0 : case token_t::tok_t::TOK_ASTERISK:
7523 0 : break;
7524 :
7525 0 : case token_t::tok_t::TOK_NCNAME:
7526 0 : if(function_call_valid && f_last_token.f_token == token_t::tok_t::TOK_OPEN_PARENTHESIS)
7527 : {
7528 : // A function name is a QName which means it can include a prefix
7529 : // Prefix ':' LocalPart '(' ... ')'
7530 0 : function_call(prefix_token, save_token);
7531 0 : goto finished;
7532 : }
7533 0 : break;
7534 :
7535 0 : default:
7536 0 : throw QDomXPathException_SyntaxError("expected an NCName or '*' after a prefix");
7537 :
7538 : }
7539 : }
7540 0 : else if(f_last_token.f_token == token_t::tok_t::TOK_OPEN_PARENTHESIS)
7541 : {
7542 : // in this case we have:
7543 : // NodeType '(' [...] ')'
7544 : // FunctionCall '(' ... ')'
7545 0 : f_last_token = save_token;
7546 0 : if(!token_is_node_type())
7547 : {
7548 : // Does a function call apply here?
7549 : // LocalPart '(' ... ')'
7550 0 : if(function_call_valid)
7551 : {
7552 : // the default prefix token is "*" meaning no prefix
7553 0 : function_call(prefix_token, save_token);
7554 0 : goto finished;
7555 : }
7556 : // we got a function call here!
7557 0 : throw QDomXPathException_SyntaxError("a path followed by parenthesis must be a NodeType");
7558 : }
7559 : // save the NodeType token
7560 0 : save_token.f_token = f_last_token.f_token;
7561 0 : get_token();
7562 0 : if(f_last_token.f_token == token_t::tok_t::TOK_STRING)
7563 : {
7564 0 : if(axis_token.f_token != token_t::tok_t::TOK_NODE_TYPE_PROCESSING_INSTRUCTION)
7565 : {
7566 0 : throw QDomXPathException_InvalidError("only a processing-instruction NodeType can be given a Literal");
7567 : }
7568 : // a special case where the TOK_NODE_TYPE_PROCESSING_INSTRUCTION
7569 0 : axis_token.f_string = f_last_token.f_string;
7570 0 : get_token();
7571 : }
7572 : else
7573 : {
7574 0 : axis_token.f_string = "";
7575 : }
7576 0 : if(f_last_token.f_token == token_t::tok_t::TOK_CLOSE_PARENTHESIS)
7577 : {
7578 0 : throw QDomXPathException_SyntaxError("missing ')' after the NodeType definition");
7579 : }
7580 0 : prefix_token.f_token = token_t::tok_t::TOK_UNDEFINED;
7581 : }
7582 :
7583 0 : axis_apply:
7584 : {
7585 : // replace the current node-set into a node context
7586 : // that way we can properly loop through the list of
7587 : // nodes against this axis
7588 0 : if(!predicate_variable.isEmpty())
7589 : {
7590 0 : append_push_string(predicate_variable);
7591 0 : append_instruction(INST_GET_VARIABLE);
7592 0 : predicate_variable.clear();
7593 : }
7594 0 : append_instruction(INST_CREATE_NODE_CONTEXT);
7595 0 : labels.push_back(f_program.size());
7596 0 : append_instruction(INST_GET_CONTEXT_NODE);
7597 :
7598 : // axis_token (axis::)
7599 : // prefix_token (prefix:)
7600 : // save_token (path or '*' or NodeType)
7601 0 : append_axis(axis_token, prefix_token, save_token);
7602 :
7603 0 : if(accept_predicate && f_last_token.f_token == token_t::tok_t::TOK_OPEN_SQUARE_BRACKET)
7604 : {
7605 : // process all predicates following this step
7606 0 : predicate();
7607 : }
7608 : }
7609 0 : break;
7610 :
7611 0 : default:
7612 0 : if(!double_slash)
7613 : {
7614 : // in case we have a double slash, the relative path is optional
7615 : //break 2;
7616 0 : goto finished;
7617 : }
7618 0 : throw QDomXPathException_SyntaxError("expected a relative path, none found");
7619 :
7620 : }
7621 : // if we loop function calls are not possible anymore
7622 0 : function_call_valid = false;
7623 0 : }
7624 :
7625 0 : finished:
7626 0 : for(int i(labels.size() - 1); i >= 0; --i)
7627 : {
7628 : // repeat with the next node of that context
7629 0 : append_instruction(INST_NEXT_CONTEXT_NODE);
7630 0 : append_push_integer(labels[i]);
7631 0 : append_instruction(INST_JUMP_IF_TRUE);
7632 :
7633 : // return the result (node-set)
7634 0 : append_instruction(INST_GET_RESULT);
7635 0 : append_instruction(INST_POP_CONTEXT); // get rid of the node context
7636 : }
7637 0 : }
7638 :
7639 :
7640 0 : void variable_reference()
7641 : {
7642 : // we arrive here with the '$' as the token
7643 0 : get_token();
7644 0 : if(f_last_token.f_token != token_t::tok_t::TOK_NCNAME)
7645 : {
7646 0 : throw QDomXPathException_SyntaxError("expected a variable name after the '$' sign");
7647 : }
7648 0 : token_t prefix(f_last_token);
7649 0 : get_token();
7650 0 : if(f_last_token.f_token == token_t::tok_t::TOK_COLON)
7651 : {
7652 0 : get_token();
7653 0 : if(f_last_token.f_token != token_t::tok_t::TOK_NCNAME)
7654 : {
7655 0 : throw QDomXPathException_SyntaxError(QString("expected a variable name after the prefix '%1:' sign").arg(prefix.f_string));
7656 : }
7657 : // concatenate the prefix within the name; not too sure why we'd
7658 : // have a prefix in the first place and we'd not likely to use
7659 : // variables at this point
7660 0 : prefix.f_string += ":" + f_last_token.f_string;
7661 : }
7662 0 : append_push_token(prefix);
7663 0 : append_instruction(INST_GET_VARIABLE);
7664 0 : }
7665 :
7666 :
7667 0 : void path_expr()
7668 : {
7669 0 : switch(f_last_token.f_token)
7670 : {
7671 0 : case token_t::tok_t::TOK_OPEN_PARENTHESIS:
7672 : // we directly call OrExpr since:
7673 : // Expr ::= OrExpr
7674 0 : or_expr();
7675 0 : if(f_last_token.f_token != token_t::tok_t::TOK_CLOSE_PARENTHESIS)
7676 : {
7677 0 : throw QDomXPathException_SyntaxError("expected a ')'");
7678 : }
7679 0 : break;
7680 :
7681 0 : case token_t::tok_t::TOK_INTEGER: // PathExpr => FilterExpr => PrimaryExpr
7682 : case token_t::tok_t::TOK_REAL: // PathExpr => FilterExpr => PrimaryExpr
7683 : case token_t::tok_t::TOK_STRING: // PathExpr => FilterExpr => PrimaryExpr
7684 0 : append_push_token(f_last_token);
7685 0 : get_token();
7686 0 : break;
7687 :
7688 0 : case token_t::tok_t::TOK_DOLLAR:
7689 0 : variable_reference();
7690 0 : break;
7691 :
7692 0 : case token_t::tok_t::TOK_SLASH: // PathExpr => LocationPath => AbsolutePath
7693 : case token_t::tok_t::TOK_DOUBLE_SLASH: // PathExpr => LocationPath => AbsolutePath
7694 : case token_t::tok_t::TOK_NCNAME: // PathExpr => PrimaryExpr => FunctionCall
7695 : case token_t::tok_t::TOK_AT: // PathExpr => LocationPath => RelativePath => Step => AxisSpecifier => AbbreviatedAxisSpecifier
7696 : case token_t::tok_t::TOK_DOT:
7697 : case token_t::tok_t::TOK_DOUBLE_DOT:
7698 0 : location_path();
7699 0 : break;
7700 :
7701 0 : case token_t::tok_t::TOK_PIPE:
7702 : // the '|' character is for the caller
7703 0 : return;
7704 :
7705 0 : default:
7706 0 : throw QDomXPathException_SyntaxError(QString("unexpected token \"%1\"").arg(f_last_token.f_string));
7707 :
7708 : }
7709 : }
7710 :
7711 :
7712 : /** \brief The UnionExpr is where it starts in XPath version 1.0
7713 : *
7714 : * At this point the UnionExpr is as defined in version 1.0 of XPath. In
7715 : * version 2.0 it changed and is a proper primary expression.
7716 : *
7717 : * The UnionExpr is quite peculiar at this point since if the path_expr
7718 : * does not return with a TOK_PIPE, then the union does not happen and
7719 : * the returned value has to be returned as is.
7720 : */
7721 0 : void union_expr()
7722 : {
7723 : // in case there is a pipe we'll need to have a duplicate of
7724 : // the input...
7725 : //append_instruction(INST_DUPLICATE1);
7726 : //++f_label_counter;
7727 : //QString variable(QString("$%1").arg(f_label_counter));
7728 : //append_push_string(variable);
7729 : //append_instruction(INST_SET_VARIABLE);
7730 :
7731 0 : path_expr();
7732 :
7733 0 : while(f_last_token.f_token == token_t::tok_t::TOK_PIPE)
7734 : {
7735 : // skip the pipe
7736 0 : get_token();
7737 :
7738 : //append_push_string(variable);
7739 : //append_instruction(INST_GET_VARIABLE);
7740 :
7741 0 : path_expr();
7742 0 : append_instruction(INST_MERGE_SETS);
7743 : }
7744 0 : }
7745 :
7746 :
7747 0 : void parse(bool show_commands)
7748 : {
7749 0 : f_show_commands = show_commands;
7750 :
7751 0 : if(!get_token())
7752 : {
7753 0 : throw QDomXPathException_SyntaxError("calling parse() immediately generated an error");
7754 : }
7755 : //f_program.clear();
7756 0 : f_label_counter = 0;
7757 :
7758 : // save the input (we expect a node-set) in a variable
7759 0 : ++f_label_counter;
7760 0 : f_predicate_variable = QString("$%1").arg(f_label_counter);
7761 0 : append_push_string(f_predicate_variable);
7762 0 : append_instruction(INST_SET_VARIABLE);
7763 :
7764 0 : union_expr();
7765 :
7766 : // terminate the program with an INST_END instruction
7767 0 : append_instruction(INST_END);
7768 :
7769 0 : if(!f_future_labels.empty())
7770 : {
7771 0 : throw QDomXPathException_SyntaxError("some future labels never got defined");
7772 : }
7773 0 : }
7774 :
7775 :
7776 0 : QDomXPath::node_vector_t apply(QDomXPath::node_vector_t& nodes)
7777 : {
7778 : // reset the states and functions
7779 0 : f_functions.clear();
7780 :
7781 : // setup the execution environment (program)
7782 0 : function_t function;
7783 0 : function.f_pc = f_program_start_offset;
7784 :
7785 : // set the node set on the stack
7786 0 : variant_t node_set;
7787 0 : node_set.setValue(nodes);
7788 0 : function.f_stack.push_back(node_set);
7789 : // one context to hold the result
7790 0 : context_t context;
7791 0 : function.f_contexts.push_back(context);
7792 :
7793 0 : f_functions.push_back(function);
7794 :
7795 : // execute the program
7796 : for(;;)
7797 : {
7798 0 : uint32_t const pc(f_functions.back().f_pc);
7799 0 : if(f_show_commands)
7800 : {
7801 0 : disassemble_instruction(pc);
7802 : }
7803 :
7804 0 : instruction_t const instruction(f_program[pc]);
7805 0 : if(instruction == INST_END)
7806 : {
7807 : // found the end of the program, return
7808 0 : break;
7809 : }
7810 0 : ++f_functions.back().f_pc;
7811 0 : (this->*g_instructions[instruction])();
7812 0 : }
7813 :
7814 : // we must have just one function on exit
7815 0 : if(f_functions.back().f_contexts.size() != 1)
7816 : {
7817 0 : throw QDomXPathException_InvalidError("function stack does not include just one item when existing program");
7818 : }
7819 :
7820 : // the first context we push must still be here, but only that one
7821 0 : if(f_functions.back().f_contexts.size() != 1)
7822 : {
7823 0 : throw QDomXPathException_InvalidError("context stack does not include just one item when existing program");
7824 : }
7825 :
7826 : // retrieve the result
7827 0 : if(f_functions.back().f_stack.size() != 1)
7828 : {
7829 0 : throw QDomXPathException_InvalidError("stack does not include just one item when existing program");
7830 : }
7831 0 : QDomXPath::node_vector_t result(f_functions.back().f_stack.back().getNodeSetValue());
7832 :
7833 : //context = f_functions.back().f_contexts.back();
7834 : //QDomXPath::node_vector_t result(context.f_result);
7835 :
7836 : // reset the states and functions
7837 : // (this won't happen on errors which is why we also do it on entry
7838 0 : f_functions.clear();
7839 :
7840 0 : return result;
7841 : }
7842 :
7843 :
7844 : #pragma GCC diagnostic push
7845 : #pragma GCC diagnostic ignored "-Wunused-parameter"
7846 :
7847 0 : uint32_t disassemble_undefined_instruction(uint32_t pc)
7848 : {
7849 0 : std::cout << "***undefined instruction*** (" << f_program[pc - 1] << ")\n";
7850 0 : return 1;
7851 : }
7852 :
7853 0 : uint32_t disassemble_end(uint32_t pc)
7854 : {
7855 0 : std::cout << "end\n";
7856 0 : return 1;
7857 : }
7858 :
7859 0 : uint32_t disassemble_call(uint32_t pc)
7860 : {
7861 0 : std::cout << "call\n";
7862 0 : return 1;
7863 : }
7864 :
7865 0 : uint32_t disassemble_small_function(uint32_t pc)
7866 : {
7867 0 : uint32_t size((f_program[pc + 0] << 8)
7868 0 : | f_program[pc + 1]);
7869 0 : std::cout << "function (" << size << " bytes)\n";
7870 : // we do not skip the function here, we only print the header
7871 : // and the disassemble loop will print the contents
7872 : // TODO what's missing is a way to mark the end of the function!
7873 0 : return 3;
7874 : }
7875 :
7876 0 : uint32_t disassemble_large_function(uint32_t pc)
7877 : {
7878 0 : uint32_t size((f_program[pc + 0] << 24)
7879 0 : | (f_program[pc + 1] << 16)
7880 0 : | (f_program[pc + 2] << 8)
7881 0 : | f_program[pc + 3]);
7882 0 : std::cout << "function (" << size << " bytes)\n";
7883 : // we do not skip the function here, we only print the header
7884 : // and the disassemble loop will print the contents
7885 : // TODO what's missing is a way to mark the end of the function!
7886 0 : return 5;
7887 : }
7888 :
7889 0 : uint32_t disassemble_jump(uint32_t pc)
7890 : {
7891 0 : std::cout << "jump\n";
7892 0 : return 1;
7893 : }
7894 :
7895 0 : uint32_t disassemble_jump_if_true(uint32_t pc)
7896 : {
7897 0 : std::cout << "jump_if_true\n";
7898 0 : return 1;
7899 : }
7900 :
7901 0 : uint32_t disassemble_jump_if_false(uint32_t pc)
7902 : {
7903 0 : std::cout << "jump_if_false\n";
7904 0 : return 1;
7905 : }
7906 :
7907 0 : uint32_t disassemble_jump_if_zero(uint32_t pc)
7908 : {
7909 0 : std::cout << "jump_if_zero\n";
7910 0 : return 1;
7911 : }
7912 :
7913 0 : uint32_t disassemble_return(uint32_t pc)
7914 : {
7915 0 : std::cout << "return\n";
7916 0 : return 1;
7917 : }
7918 :
7919 0 : uint32_t disassemble_get_variable(uint32_t pc)
7920 : {
7921 0 : std::cout << "get_variable\n";
7922 0 : return 1;
7923 : }
7924 :
7925 0 : uint32_t disassemble_set_variable(uint32_t pc)
7926 : {
7927 0 : std::cout << "set_variable\n";
7928 0 : return 1;
7929 : }
7930 :
7931 0 : uint32_t disassemble_pop1(uint32_t pc)
7932 : {
7933 0 : std::cout << "pop 1\n";
7934 0 : return 1;
7935 : }
7936 :
7937 0 : uint32_t disassemble_pop2(uint32_t pc)
7938 : {
7939 0 : std::cout << "pop 2\n";
7940 0 : return 1;
7941 : }
7942 :
7943 0 : uint32_t disassemble_pop3(uint32_t pc)
7944 : {
7945 0 : std::cout << "pop 3\n";
7946 0 : return 1;
7947 : }
7948 :
7949 0 : uint32_t disassemble_pop4(uint32_t pc)
7950 : {
7951 0 : std::cout << "pop 4\n";
7952 0 : return 1;
7953 : }
7954 :
7955 0 : uint32_t disassemble_pop5(uint32_t pc)
7956 : {
7957 0 : std::cout << "pop 5\n";
7958 0 : return 1;
7959 : }
7960 :
7961 0 : uint32_t disassemble_duplicate1(uint32_t pc)
7962 : {
7963 0 : std::cout << "duplicate 1\n";
7964 0 : return 1;
7965 : }
7966 :
7967 0 : uint32_t disassemble_duplicate2(uint32_t pc)
7968 : {
7969 0 : std::cout << "duplicate 2\n";
7970 0 : return 1;
7971 : }
7972 :
7973 0 : uint32_t disassemble_duplicate3(uint32_t pc)
7974 : {
7975 0 : std::cout << "duplicate 3\n";
7976 0 : return 1;
7977 : }
7978 :
7979 0 : uint32_t disassemble_duplicate4(uint32_t pc)
7980 : {
7981 0 : std::cout << "duplicate 4\n";
7982 0 : return 1;
7983 : }
7984 :
7985 0 : uint32_t disassemble_duplicate5(uint32_t pc)
7986 : {
7987 0 : std::cout << "duplicate 5\n";
7988 0 : return 1;
7989 : }
7990 :
7991 0 : uint32_t disassemble_swap1(uint32_t pc)
7992 : {
7993 0 : std::cout << "swap 1, 2\n";
7994 0 : return 1;
7995 : }
7996 :
7997 0 : uint32_t disassemble_swap2(uint32_t pc)
7998 : {
7999 0 : std::cout << "swap 1, 3\n";
8000 0 : return 1;
8001 : }
8002 :
8003 0 : uint32_t disassemble_swap3(uint32_t pc)
8004 : {
8005 0 : std::cout << "swap 1, 4\n";
8006 0 : return 1;
8007 : }
8008 :
8009 0 : uint32_t disassemble_swap4(uint32_t pc)
8010 : {
8011 0 : std::cout << "swap 1, 5\n";
8012 0 : return 1;
8013 : }
8014 :
8015 0 : uint32_t disassemble_swap5(uint32_t pc)
8016 : {
8017 0 : std::cout << "swap 1, 6\n";
8018 0 : return 1;
8019 : }
8020 :
8021 0 : uint32_t disassemble_swap2_3(uint32_t pc)
8022 : {
8023 0 : std::cout << "swap 2, 3\n";
8024 0 : return 1;
8025 : }
8026 :
8027 0 : uint32_t disassemble_push_any_string(uint32_t pc)
8028 : {
8029 0 : std::cout << "push_string \"*\"\n";
8030 0 : return 1;
8031 : }
8032 :
8033 0 : uint32_t disassemble_push_byte(uint32_t pc)
8034 : {
8035 0 : int64_t value(static_cast<int64_t>(f_program[pc]));
8036 0 : std::cout << "push_integer " << value << std::endl;
8037 0 : return 2;
8038 : }
8039 :
8040 0 : uint32_t disassemble_push_double(uint32_t pc)
8041 : {
8042 : union
8043 : {
8044 : uint64_t f_int;
8045 : double f_dbl;
8046 : } convert;
8047 0 : convert.f_int = (static_cast<int64_t>(f_program[pc + 0]) << 56)
8048 0 : | (static_cast<int64_t>(f_program[pc + 1]) << 48)
8049 0 : | (static_cast<int64_t>(f_program[pc + 2]) << 40)
8050 0 : | (static_cast<int64_t>(f_program[pc + 3]) << 32)
8051 0 : | (static_cast<int64_t>(f_program[pc + 4]) << 24)
8052 0 : | (static_cast<int64_t>(f_program[pc + 5]) << 16)
8053 0 : | (static_cast<int64_t>(f_program[pc + 6]) << 8)
8054 0 : | static_cast<int64_t>(f_program[pc + 7]);
8055 0 : std::cout << "push_double " << convert.f_dbl << std::endl;
8056 0 : return 9;
8057 : }
8058 :
8059 0 : uint32_t disassemble_push_double_zero(uint32_t pc)
8060 : {
8061 0 : std::cout << "push_double_zero\n";
8062 0 : return 1;
8063 : }
8064 :
8065 0 : uint32_t disassemble_push_empty_node_set(uint32_t pc)
8066 : {
8067 0 : std::cout << "push_empty_node_set\n";
8068 0 : return 1;
8069 : }
8070 :
8071 0 : uint32_t disassemble_push_empty_set(uint32_t pc)
8072 : {
8073 0 : std::cout << "push_empty_set\n";
8074 0 : return 1;
8075 : }
8076 :
8077 0 : uint32_t disassemble_push_empty_string(uint32_t pc)
8078 : {
8079 0 : std::cout << "push_string \"\"\n";
8080 0 : return 1;
8081 : }
8082 :
8083 0 : uint32_t disassemble_push_end_of_arguments(uint32_t pc)
8084 : {
8085 0 : std::cout << "push_end_of_arguments\n";
8086 0 : return 1;
8087 : }
8088 :
8089 0 : uint32_t disassemble_push_false(uint32_t pc)
8090 : {
8091 0 : std::cout << "push_false\n";
8092 0 : return 1;
8093 : }
8094 :
8095 0 : uint32_t disassemble_push_large_string(uint32_t pc)
8096 : {
8097 0 : uint32_t size = (static_cast<uint32_t>(f_program[pc + 0]) << 24)
8098 0 : | (static_cast<uint32_t>(f_program[pc + 1]) << 16)
8099 0 : | (static_cast<uint32_t>(f_program[pc + 2]) << 8)
8100 0 : | static_cast<uint32_t>(f_program[pc + 3]);
8101 0 : std::string str(reinterpret_cast<char *>(&f_program[pc + 4]), size);
8102 0 : std::cout << "push_string \"" << str << "\"" << std::endl;
8103 0 : return 5 + size;
8104 : }
8105 :
8106 0 : uint32_t disassemble_push_long(uint32_t pc)
8107 : {
8108 0 : int64_t value = (static_cast<int64_t>(f_program[pc + 0]) << 24)
8109 0 : | (static_cast<int64_t>(f_program[pc + 1]) << 16)
8110 0 : | (static_cast<int64_t>(f_program[pc + 2]) << 8)
8111 0 : | static_cast<int64_t>(f_program[pc + 3]);
8112 0 : std::cout << "push_integer " << value << std::endl;
8113 0 : return 5;
8114 : }
8115 :
8116 0 : uint32_t disassemble_push_longlong(uint32_t pc)
8117 : {
8118 0 : int64_t value = (static_cast<int64_t>(f_program[pc + 0]) << 56)
8119 0 : | (static_cast<int64_t>(f_program[pc + 1]) << 48)
8120 0 : | (static_cast<int64_t>(f_program[pc + 2]) << 40)
8121 0 : | (static_cast<int64_t>(f_program[pc + 3]) << 32)
8122 0 : | (static_cast<int64_t>(f_program[pc + 4]) << 24)
8123 0 : | (static_cast<int64_t>(f_program[pc + 5]) << 16)
8124 0 : | (static_cast<int64_t>(f_program[pc + 6]) << 8)
8125 0 : | static_cast<int64_t>(f_program[pc + 7]);
8126 0 : std::cout << "push_integer " << value << std::endl;
8127 0 : return 9;
8128 : }
8129 :
8130 0 : uint32_t disassemble_push_medium_string(uint32_t pc)
8131 : {
8132 0 : uint32_t size = (static_cast<uint32_t>(f_program[pc + 0]) << 8)
8133 0 : | static_cast<uint32_t>(f_program[pc + 1]);
8134 0 : std::string str(reinterpret_cast<char *>(&f_program[pc + 2]), size);
8135 0 : std::cout << "push_string \"" << str << "\"\n";
8136 0 : return 3 + size;
8137 : }
8138 :
8139 0 : uint32_t disassemble_push_negative_byte(uint32_t pc)
8140 : {
8141 0 : int64_t value(static_cast<int64_t>(f_program[pc]) | 0xFFFFFFFFFFFFFF00LL);
8142 0 : std::cout << "push_integer " << value << std::endl;
8143 0 : return 2;
8144 : }
8145 :
8146 0 : uint32_t disassemble_push_negative_short(uint32_t pc)
8147 : {
8148 0 : int64_t value((static_cast<int64_t>(f_program[pc]) << 8)
8149 0 : | static_cast<int64_t>(f_program[pc])
8150 0 : | 0xFFFFFFFFFFFF0000LL);
8151 0 : std::cout << "push_integer " << value << std::endl;
8152 0 : return 3;
8153 : }
8154 :
8155 0 : uint32_t disassemble_push_negative_long(uint32_t pc)
8156 : {
8157 0 : int64_t value((static_cast<int64_t>(f_program[pc]) << 24)
8158 0 : | (static_cast<int64_t>(f_program[pc]) << 16)
8159 0 : | (static_cast<int64_t>(f_program[pc]) << 8)
8160 0 : | static_cast<int64_t>(f_program[pc])
8161 0 : | 0xFFFFFFFF00000000LL);
8162 0 : std::cout << "push_integer " << value << std::endl;
8163 0 : return 5;
8164 : }
8165 :
8166 0 : uint32_t disassemble_push_short(uint32_t pc)
8167 : {
8168 0 : int64_t value = (static_cast<int64_t>(f_program[pc + 0]) << 8)
8169 0 : | static_cast<int64_t>(f_program[pc + 1]);
8170 0 : std::cout << "push_integer " << value << std::endl;
8171 0 : return 5;
8172 : }
8173 :
8174 0 : uint32_t disassemble_push_small_string(uint32_t pc)
8175 : {
8176 0 : uint32_t size = static_cast<uint32_t>(f_program[pc + 0]);
8177 0 : std::string str(reinterpret_cast<char *>(&f_program[pc + 1]), size);
8178 0 : std::cout << "push_string \"" << str << "\"\n";
8179 0 : return 2 + size;
8180 : }
8181 :
8182 0 : uint32_t disassemble_push_true(uint32_t pc)
8183 : {
8184 0 : std::cout << "push_true\n";
8185 0 : return 1;
8186 : }
8187 :
8188 0 : uint32_t disassemble_push_zero(uint32_t pc)
8189 : {
8190 0 : std::cout << "push_integer 0\n";
8191 0 : return 1;
8192 : }
8193 :
8194 0 : uint32_t disassemble_add(uint32_t pc)
8195 : {
8196 0 : std::cout << "add\n";
8197 0 : return 1;
8198 : }
8199 :
8200 0 : uint32_t disassemble_and(uint32_t pc)
8201 : {
8202 0 : std::cout << "and\n";
8203 0 : return 1;
8204 : }
8205 :
8206 0 : uint32_t disassemble_ceiling(uint32_t pc)
8207 : {
8208 0 : std::cout << "ceiling\n";
8209 0 : return 1;
8210 : }
8211 :
8212 0 : uint32_t disassemble_decrement(uint32_t pc)
8213 : {
8214 0 : std::cout << "decrement\n";
8215 0 : return 1;
8216 : }
8217 :
8218 0 : uint32_t disassemble_divide(uint32_t pc)
8219 : {
8220 0 : std::cout << "divide\n";
8221 0 : return 1;
8222 : }
8223 :
8224 0 : uint32_t disassemble_equal(uint32_t pc)
8225 : {
8226 0 : std::cout << "equal\n";
8227 0 : return 1;
8228 : }
8229 :
8230 0 : uint32_t disassemble_floor(uint32_t pc)
8231 : {
8232 0 : std::cout << "floor\n";
8233 0 : return 1;
8234 : }
8235 :
8236 0 : uint32_t disassemble_greater_or_equal(uint32_t pc)
8237 : {
8238 0 : std::cout << "greater_or_equal\n";
8239 0 : return 1;
8240 : }
8241 :
8242 0 : uint32_t disassemble_greater_than(uint32_t pc)
8243 : {
8244 0 : std::cout << "greater_than\n";
8245 0 : return 1;
8246 : }
8247 :
8248 0 : uint32_t disassemble_idivide(uint32_t pc)
8249 : {
8250 0 : std::cout << "idivide\n";
8251 0 : return 1;
8252 : }
8253 :
8254 0 : uint32_t disassemble_increment(uint32_t pc)
8255 : {
8256 0 : std::cout << "increment\n";
8257 0 : return 1;
8258 : }
8259 :
8260 0 : uint32_t disassemble_less_or_equal(uint32_t pc)
8261 : {
8262 0 : std::cout << "less_or_equal\n";
8263 0 : return 1;
8264 : }
8265 :
8266 0 : uint32_t disassemble_less_than(uint32_t pc)
8267 : {
8268 0 : std::cout << "less_than\n";
8269 0 : return 1;
8270 : }
8271 :
8272 0 : uint32_t disassemble_modulo(uint32_t pc)
8273 : {
8274 0 : std::cout << "modulo\n";
8275 0 : return 1;
8276 : }
8277 :
8278 0 : uint32_t disassemble_multiply(uint32_t pc)
8279 : {
8280 0 : std::cout << "multiply\n";
8281 0 : return 1;
8282 : }
8283 :
8284 0 : uint32_t disassemble_negate(uint32_t pc)
8285 : {
8286 0 : std::cout << "negate\n";
8287 0 : return 1;
8288 : }
8289 :
8290 0 : uint32_t disassemble_not(uint32_t pc)
8291 : {
8292 0 : std::cout << "not\n";
8293 0 : return 1;
8294 : }
8295 :
8296 0 : uint32_t disassemble_not_equal(uint32_t pc)
8297 : {
8298 0 : std::cout << "not_equal\n";
8299 0 : return 1;
8300 : }
8301 :
8302 0 : uint32_t disassemble_or(uint32_t pc)
8303 : {
8304 0 : std::cout << "or\n";
8305 0 : return 1;
8306 : }
8307 :
8308 0 : uint32_t disassemble_round(uint32_t pc)
8309 : {
8310 0 : std::cout << "round\n";
8311 0 : return 1;
8312 : }
8313 :
8314 0 : uint32_t disassemble_string_length(uint32_t pc)
8315 : {
8316 0 : std::cout << "string_length\n";
8317 0 : return 1;
8318 : }
8319 :
8320 0 : uint32_t disassemble_subtract(uint32_t pc)
8321 : {
8322 0 : std::cout << "subtract\n";
8323 0 : return 1;
8324 : }
8325 :
8326 0 : uint32_t disassemble_axis(uint32_t pc)
8327 : {
8328 0 : std::cout << "axis\n";
8329 0 : return 1;
8330 : }
8331 :
8332 0 : uint32_t disassemble_root(uint32_t pc)
8333 : {
8334 0 : std::cout << "root\n";
8335 0 : return 1;
8336 : }
8337 :
8338 0 : uint32_t disassemble_get_node_set(uint32_t pc)
8339 : {
8340 0 : std::cout << "get_node_set\n";
8341 0 : return 1;
8342 : }
8343 :
8344 0 : uint32_t disassemble_set_node_set(uint32_t pc)
8345 : {
8346 0 : std::cout << "set_node_set\n";
8347 0 : return 1;
8348 : }
8349 :
8350 0 : uint32_t disassemble_get_result(uint32_t pc)
8351 : {
8352 0 : std::cout << "get_result\n";
8353 0 : return 1;
8354 : }
8355 :
8356 0 : uint32_t disassemble_set_result(uint32_t pc)
8357 : {
8358 0 : std::cout << "set_result\n";
8359 0 : return 1;
8360 : }
8361 :
8362 0 : uint32_t disassemble_get_position(uint32_t pc)
8363 : {
8364 0 : std::cout << "get_position\n";
8365 0 : return 1;
8366 : }
8367 :
8368 0 : uint32_t disassemble_set_position(uint32_t pc)
8369 : {
8370 0 : std::cout << "set_position\n";
8371 0 : return 1;
8372 : }
8373 :
8374 0 : uint32_t disassemble_node_set_size(uint32_t pc)
8375 : {
8376 0 : std::cout << "node_set_size\n";
8377 0 : return 1;
8378 : }
8379 :
8380 0 : uint32_t disassemble_merge_sets(uint32_t pc)
8381 : {
8382 0 : std::cout << "merge_sets\n";
8383 0 : return 1;
8384 : }
8385 :
8386 0 : uint32_t disassemble_predicate(uint32_t pc)
8387 : {
8388 0 : std::cout << "predicate\n";
8389 0 : return 1;
8390 : }
8391 :
8392 0 : uint32_t disassemble_create_node_context(uint32_t pc)
8393 : {
8394 0 : std::cout << "create_node_context\n";
8395 0 : return 1;
8396 : }
8397 :
8398 0 : uint32_t disassemble_get_context_node(uint32_t pc)
8399 : {
8400 0 : std::cout << "get_context_node\n";
8401 0 : return 1;
8402 : }
8403 :
8404 0 : uint32_t disassemble_next_context_node(uint32_t pc)
8405 : {
8406 0 : std::cout << "next_context_node\n";
8407 0 : return 1;
8408 : }
8409 :
8410 0 : uint32_t disassemble_pop_context(uint32_t pc)
8411 : {
8412 0 : std::cout << "pop_context\n";
8413 0 : return 1;
8414 : }
8415 : #pragma GCC diagnostic pop
8416 :
8417 :
8418 :
8419 0 : int disassemble_instruction(int const pc)
8420 : {
8421 : // a small indentation
8422 0 : std::ostream out(std::cout.rdbuf()); // copy so we don't mess up the manipulator in std::cout
8423 0 : out << std::setw(6) << pc << "- ";
8424 0 : instruction_t const inst(f_program[pc]);
8425 0 : return (this->*g_disassemble_instructions[inst])(pc + 1);
8426 : }
8427 :
8428 :
8429 0 : void disassemble()
8430 : {
8431 0 : int pc(f_program_start_offset);
8432 0 : while(pc < f_program.size())
8433 : {
8434 0 : pc += disassemble_instruction(pc);
8435 : }
8436 0 : }
8437 :
8438 :
8439 0 : QString setProgram(const QDomXPath::program_t& program, bool show_commands)
8440 : {
8441 0 : if(program[0] != QDomXPath::MAGIC[0]
8442 0 : || program[1] != QDomXPath::MAGIC[1]
8443 0 : || program[2] != QDomXPath::MAGIC[2]
8444 0 : || program[3] != QDomXPath::MAGIC[3])
8445 : {
8446 0 : throw QDomXPathException_InvalidMagic("this program does not start with the correct magic code");
8447 : }
8448 0 : if(program[4] != QDomXPath::VERSION_MAJOR
8449 0 : || program[5] != QDomXPath::VERSION_MINOR)
8450 : {
8451 : // we need a better test as we have newer versions, at this time,
8452 : // we do not need much more than what is here
8453 0 : throw QDomXPathException_InvalidMagic("this program version is not compatible");
8454 : }
8455 :
8456 0 : f_show_commands = show_commands;
8457 :
8458 0 : const int size((program[6] << 8) | program[7]);
8459 0 : f_program_start_offset = size + 8;
8460 0 : f_xpath = QString::fromUtf8(reinterpret_cast<const char *>(&program[8]), size);
8461 :
8462 0 : f_program = program;
8463 :
8464 0 : return f_xpath;
8465 : }
8466 :
8467 :
8468 0 : const QDomXPath::program_t& getProgram() const
8469 : {
8470 0 : return f_program;
8471 : }
8472 :
8473 :
8474 : private:
8475 : QDomXPath * f_owner = nullptr;
8476 : bool f_show_commands = false;
8477 :
8478 : // parser parameters
8479 : QString f_xpath = QString();
8480 : const QChar * f_start = nullptr;
8481 : const QChar * f_in = nullptr;
8482 : token_t f_unget_token = token_t();
8483 : token_t f_last_token = token_t();
8484 : uint32_t f_label_counter = 0;
8485 : label_offsets_t f_labels = label_offsets_t();
8486 : future_labels_t f_future_labels = future_labels_t();
8487 : QString f_end_label = QString();
8488 : QString f_predicate_variable = QString();
8489 :
8490 : // execution environment
8491 : //QDomXPath::node_vector_t f_result;
8492 : int32_t f_program_start_offset = -1;
8493 : QDomXPath::program_t f_program = QDomXPath::program_t();
8494 : function_vector_t f_functions = function_vector_t();
8495 : };
8496 :
8497 :
8498 : QDomXPath::QDomXPathImpl::instruction_function_t const QDomXPath::QDomXPathImpl::g_instructions[256] =
8499 : {
8500 : /* 0x00 */ &QDomXPathImpl::inst_end
8501 : , /* 0x01 */ &QDomXPathImpl::inst_call
8502 : , /* 0x02 */ &QDomXPathImpl::inst_small_function
8503 : , /* 0x03 */ &QDomXPathImpl::inst_large_function
8504 : , /* 0x04 */ &QDomXPathImpl::inst_jump
8505 : , /* 0x05 */ &QDomXPathImpl::inst_jump_if_true
8506 : , /* 0x06 */ &QDomXPathImpl::inst_jump_if_false
8507 : , /* 0x07 */ &QDomXPathImpl::inst_jump_if_zero
8508 : , /* 0x08 */ &QDomXPathImpl::inst_return
8509 : , /* 0x09 */ &QDomXPathImpl::inst_undefined_instruction
8510 : , /* 0x0A */ &QDomXPathImpl::inst_undefined_instruction
8511 : , /* 0x0B */ &QDomXPathImpl::inst_undefined_instruction
8512 : , /* 0x0C */ &QDomXPathImpl::inst_undefined_instruction
8513 : , /* 0x0D */ &QDomXPathImpl::inst_undefined_instruction
8514 : , /* 0x0E */ &QDomXPathImpl::inst_undefined_instruction
8515 : , /* 0x0F */ &QDomXPathImpl::inst_undefined_instruction
8516 :
8517 : , /* 0x10 */ &QDomXPathImpl::inst_get_variable
8518 : , /* 0x11 */ &QDomXPathImpl::inst_set_variable
8519 : , /* 0x12 */ &QDomXPathImpl::inst_undefined_instruction
8520 : , /* 0x13 */ &QDomXPathImpl::inst_undefined_instruction
8521 : , /* 0x14 */ &QDomXPathImpl::inst_undefined_instruction
8522 : , /* 0x15 */ &QDomXPathImpl::inst_undefined_instruction
8523 : , /* 0x16 */ &QDomXPathImpl::inst_undefined_instruction
8524 : , /* 0x17 */ &QDomXPathImpl::inst_undefined_instruction
8525 : , /* 0x18 */ &QDomXPathImpl::inst_undefined_instruction
8526 : , /* 0x19 */ &QDomXPathImpl::inst_undefined_instruction
8527 : , /* 0x1A */ &QDomXPathImpl::inst_undefined_instruction
8528 : , /* 0x1B */ &QDomXPathImpl::inst_undefined_instruction
8529 : , /* 0x1C */ &QDomXPathImpl::inst_undefined_instruction
8530 : , /* 0x1D */ &QDomXPathImpl::inst_undefined_instruction
8531 : , /* 0x1E */ &QDomXPathImpl::inst_undefined_instruction
8532 : , /* 0x1F */ &QDomXPathImpl::inst_undefined_instruction
8533 :
8534 : , /* 0x20 */ &QDomXPathImpl::inst_pop1
8535 : , /* 0x21 */ &QDomXPathImpl::inst_pop2
8536 : , /* 0x22 */ &QDomXPathImpl::inst_pop3
8537 : , /* 0x23 */ &QDomXPathImpl::inst_pop4
8538 : , /* 0x24 */ &QDomXPathImpl::inst_pop5
8539 : , /* 0x25 */ &QDomXPathImpl::inst_undefined_instruction
8540 : , /* 0x26 */ &QDomXPathImpl::inst_undefined_instruction
8541 : , /* 0x27 */ &QDomXPathImpl::inst_undefined_instruction
8542 : , /* 0x28 */ &QDomXPathImpl::inst_undefined_instruction
8543 : , /* 0x29 */ &QDomXPathImpl::inst_undefined_instruction
8544 : , /* 0x2A */ &QDomXPathImpl::inst_duplicate1
8545 : , /* 0x2B */ &QDomXPathImpl::inst_duplicate2
8546 : , /* 0x2C */ &QDomXPathImpl::inst_duplicate3
8547 : , /* 0x2D */ &QDomXPathImpl::inst_duplicate4
8548 : , /* 0x2E */ &QDomXPathImpl::inst_duplicate5
8549 : , /* 0x2F */ &QDomXPathImpl::inst_undefined_instruction
8550 :
8551 : , /* 0x30 */ &QDomXPathImpl::inst_swap1
8552 : , /* 0x31 */ &QDomXPathImpl::inst_swap2
8553 : , /* 0x32 */ &QDomXPathImpl::inst_swap3
8554 : , /* 0x33 */ &QDomXPathImpl::inst_swap4
8555 : , /* 0x34 */ &QDomXPathImpl::inst_swap5
8556 : , /* 0x35 */ &QDomXPathImpl::inst_swap2_3
8557 : , /* 0x36 */ &QDomXPathImpl::inst_undefined_instruction
8558 : , /* 0x37 */ &QDomXPathImpl::inst_undefined_instruction
8559 : , /* 0x38 */ &QDomXPathImpl::inst_undefined_instruction
8560 : , /* 0x39 */ &QDomXPathImpl::inst_undefined_instruction
8561 : , /* 0x3A */ &QDomXPathImpl::inst_undefined_instruction
8562 : , /* 0x3B */ &QDomXPathImpl::inst_undefined_instruction
8563 : , /* 0x3C */ &QDomXPathImpl::inst_undefined_instruction
8564 : , /* 0x3D */ &QDomXPathImpl::inst_undefined_instruction
8565 : , /* 0x3E */ &QDomXPathImpl::inst_undefined_instruction
8566 : , /* 0x3F */ &QDomXPathImpl::inst_undefined_instruction
8567 :
8568 : , /* 0x40 */ &QDomXPathImpl::inst_push_any_string
8569 : , /* 0x41 */ &QDomXPathImpl::inst_push_byte
8570 : , /* 0x42 */ &QDomXPathImpl::inst_push_double
8571 : , /* 0x43 */ &QDomXPathImpl::inst_push_double_zero
8572 : , /* 0x44 */ &QDomXPathImpl::inst_push_empty_node_set
8573 : , /* 0x45 */ &QDomXPathImpl::inst_push_empty_set
8574 : , /* 0x46 */ &QDomXPathImpl::inst_push_empty_string
8575 : , /* 0x47 */ &QDomXPathImpl::inst_push_end_of_arguments
8576 : , /* 0x48 */ &QDomXPathImpl::inst_push_false
8577 : , /* 0x49 */ &QDomXPathImpl::inst_push_large_string
8578 : , /* 0x4A */ &QDomXPathImpl::inst_push_long
8579 : , /* 0x4B */ &QDomXPathImpl::inst_push_longlong
8580 : , /* 0x4C */ &QDomXPathImpl::inst_push_medium_string
8581 : , /* 0x4D */ &QDomXPathImpl::inst_push_negative_byte
8582 : , /* 0x4E */ &QDomXPathImpl::inst_push_negative_short
8583 : , /* 0x4F */ &QDomXPathImpl::inst_push_negative_long
8584 :
8585 : , /* 0x50 */ &QDomXPathImpl::inst_push_short
8586 : , /* 0x51 */ &QDomXPathImpl::inst_push_small_string
8587 : , /* 0x52 */ &QDomXPathImpl::inst_push_true
8588 : , /* 0x53 */ &QDomXPathImpl::inst_push_zero
8589 : , /* 0x54 */ &QDomXPathImpl::inst_undefined_instruction
8590 : , /* 0x55 */ &QDomXPathImpl::inst_undefined_instruction
8591 : , /* 0x56 */ &QDomXPathImpl::inst_undefined_instruction
8592 : , /* 0x57 */ &QDomXPathImpl::inst_undefined_instruction
8593 : , /* 0x58 */ &QDomXPathImpl::inst_undefined_instruction
8594 : , /* 0x59 */ &QDomXPathImpl::inst_undefined_instruction
8595 : , /* 0x5A */ &QDomXPathImpl::inst_undefined_instruction
8596 : , /* 0x5B */ &QDomXPathImpl::inst_undefined_instruction
8597 : , /* 0x5C */ &QDomXPathImpl::inst_undefined_instruction
8598 : , /* 0x5D */ &QDomXPathImpl::inst_undefined_instruction
8599 : , /* 0x5E */ &QDomXPathImpl::inst_undefined_instruction
8600 : , /* 0x5F */ &QDomXPathImpl::inst_undefined_instruction
8601 :
8602 : , /* 0x60 */ &QDomXPathImpl::inst_add
8603 : , /* 0x61 */ &QDomXPathImpl::inst_and
8604 : , /* 0x62 */ &QDomXPathImpl::inst_ceiling
8605 : , /* 0x63 */ &QDomXPathImpl::inst_decrement
8606 : , /* 0x64 */ &QDomXPathImpl::inst_divide
8607 : , /* 0x65 */ &QDomXPathImpl::inst_equal
8608 : , /* 0x66 */ &QDomXPathImpl::inst_floor
8609 : , /* 0x67 */ &QDomXPathImpl::inst_greater_or_equal
8610 : , /* 0x68 */ &QDomXPathImpl::inst_greater_than
8611 : , /* 0x69 */ &QDomXPathImpl::inst_idivide
8612 : , /* 0x6A */ &QDomXPathImpl::inst_increment
8613 : , /* 0x6B */ &QDomXPathImpl::inst_less_or_equal
8614 : , /* 0x6C */ &QDomXPathImpl::inst_less_than
8615 : , /* 0x6D */ &QDomXPathImpl::inst_modulo
8616 : , /* 0x6E */ &QDomXPathImpl::inst_multiply
8617 : , /* 0x6F */ &QDomXPathImpl::inst_negate
8618 :
8619 : , /* 0x70 */ &QDomXPathImpl::inst_not
8620 : , /* 0x71 */ &QDomXPathImpl::inst_not_equal
8621 : , /* 0x72 */ &QDomXPathImpl::inst_or
8622 : , /* 0x73 */ &QDomXPathImpl::inst_round
8623 : , /* 0x74 */ &QDomXPathImpl::inst_string_length
8624 : , /* 0x75 */ &QDomXPathImpl::inst_subtract
8625 : , /* 0x76 */ &QDomXPathImpl::inst_undefined_instruction
8626 : , /* 0x77 */ &QDomXPathImpl::inst_undefined_instruction
8627 : , /* 0x78 */ &QDomXPathImpl::inst_undefined_instruction
8628 : , /* 0x79 */ &QDomXPathImpl::inst_undefined_instruction
8629 : , /* 0x7A */ &QDomXPathImpl::inst_undefined_instruction
8630 : , /* 0x7B */ &QDomXPathImpl::inst_undefined_instruction
8631 : , /* 0x7C */ &QDomXPathImpl::inst_undefined_instruction
8632 : , /* 0x7D */ &QDomXPathImpl::inst_undefined_instruction
8633 : , /* 0x7E */ &QDomXPathImpl::inst_undefined_instruction
8634 : , /* 0x7F */ &QDomXPathImpl::inst_undefined_instruction
8635 :
8636 : , /* 0x80 */ &QDomXPathImpl::inst_axis
8637 : , /* 0x81 */ &QDomXPathImpl::inst_root
8638 : , /* 0x82 */ &QDomXPathImpl::inst_get_node_set
8639 : , /* 0x83 */ &QDomXPathImpl::inst_set_node_set
8640 : , /* 0x84 */ &QDomXPathImpl::inst_get_result
8641 : , /* 0x85 */ &QDomXPathImpl::inst_set_result
8642 : , /* 0x86 */ &QDomXPathImpl::inst_get_position
8643 : , /* 0x87 */ &QDomXPathImpl::inst_set_position
8644 : , /* 0x88 */ &QDomXPathImpl::inst_node_set_size
8645 : , /* 0x89 */ &QDomXPathImpl::inst_merge_sets
8646 : , /* 0x8A */ &QDomXPathImpl::inst_predicate
8647 : , /* 0x8B */ &QDomXPathImpl::inst_create_node_context
8648 : , /* 0x8C */ &QDomXPathImpl::inst_get_context_node
8649 : , /* 0x8D */ &QDomXPathImpl::inst_next_context_node
8650 : , /* 0x8E */ &QDomXPathImpl::inst_pop_context
8651 : , /* 0x8F */ &QDomXPathImpl::inst_undefined_instruction
8652 :
8653 : , /* 0x90 */ &QDomXPathImpl::inst_undefined_instruction
8654 : , /* 0x91 */ &QDomXPathImpl::inst_undefined_instruction
8655 : , /* 0x92 */ &QDomXPathImpl::inst_undefined_instruction
8656 : , /* 0x93 */ &QDomXPathImpl::inst_undefined_instruction
8657 : , /* 0x94 */ &QDomXPathImpl::inst_undefined_instruction
8658 : , /* 0x95 */ &QDomXPathImpl::inst_undefined_instruction
8659 : , /* 0x96 */ &QDomXPathImpl::inst_undefined_instruction
8660 : , /* 0x97 */ &QDomXPathImpl::inst_undefined_instruction
8661 : , /* 0x98 */ &QDomXPathImpl::inst_undefined_instruction
8662 : , /* 0x99 */ &QDomXPathImpl::inst_undefined_instruction
8663 : , /* 0x9A */ &QDomXPathImpl::inst_undefined_instruction
8664 : , /* 0x9B */ &QDomXPathImpl::inst_undefined_instruction
8665 : , /* 0x9C */ &QDomXPathImpl::inst_undefined_instruction
8666 : , /* 0x9D */ &QDomXPathImpl::inst_undefined_instruction
8667 : , /* 0x9E */ &QDomXPathImpl::inst_undefined_instruction
8668 : , /* 0x9F */ &QDomXPathImpl::inst_undefined_instruction
8669 :
8670 : , /* 0xA0 */ &QDomXPathImpl::inst_undefined_instruction
8671 : , /* 0xA1 */ &QDomXPathImpl::inst_undefined_instruction
8672 : , /* 0xA2 */ &QDomXPathImpl::inst_undefined_instruction
8673 : , /* 0xA3 */ &QDomXPathImpl::inst_undefined_instruction
8674 : , /* 0xA4 */ &QDomXPathImpl::inst_undefined_instruction
8675 : , /* 0xA5 */ &QDomXPathImpl::inst_undefined_instruction
8676 : , /* 0xA6 */ &QDomXPathImpl::inst_undefined_instruction
8677 : , /* 0xA7 */ &QDomXPathImpl::inst_undefined_instruction
8678 : , /* 0xA8 */ &QDomXPathImpl::inst_undefined_instruction
8679 : , /* 0xA9 */ &QDomXPathImpl::inst_undefined_instruction
8680 : , /* 0xAA */ &QDomXPathImpl::inst_undefined_instruction
8681 : , /* 0xAB */ &QDomXPathImpl::inst_undefined_instruction
8682 : , /* 0xAC */ &QDomXPathImpl::inst_undefined_instruction
8683 : , /* 0xAD */ &QDomXPathImpl::inst_undefined_instruction
8684 : , /* 0xAE */ &QDomXPathImpl::inst_undefined_instruction
8685 : , /* 0xAF */ &QDomXPathImpl::inst_undefined_instruction
8686 :
8687 : , /* 0xB0 */ &QDomXPathImpl::inst_undefined_instruction
8688 : , /* 0xB1 */ &QDomXPathImpl::inst_undefined_instruction
8689 : , /* 0xB2 */ &QDomXPathImpl::inst_undefined_instruction
8690 : , /* 0xB3 */ &QDomXPathImpl::inst_undefined_instruction
8691 : , /* 0xB4 */ &QDomXPathImpl::inst_undefined_instruction
8692 : , /* 0xB5 */ &QDomXPathImpl::inst_undefined_instruction
8693 : , /* 0xB6 */ &QDomXPathImpl::inst_undefined_instruction
8694 : , /* 0xB7 */ &QDomXPathImpl::inst_undefined_instruction
8695 : , /* 0xB8 */ &QDomXPathImpl::inst_undefined_instruction
8696 : , /* 0xB9 */ &QDomXPathImpl::inst_undefined_instruction
8697 : , /* 0xBA */ &QDomXPathImpl::inst_undefined_instruction
8698 : , /* 0xBB */ &QDomXPathImpl::inst_undefined_instruction
8699 : , /* 0xBC */ &QDomXPathImpl::inst_undefined_instruction
8700 : , /* 0xBD */ &QDomXPathImpl::inst_undefined_instruction
8701 : , /* 0xBE */ &QDomXPathImpl::inst_undefined_instruction
8702 : , /* 0xBF */ &QDomXPathImpl::inst_undefined_instruction
8703 :
8704 : , /* 0xC0 */ &QDomXPathImpl::inst_undefined_instruction
8705 : , /* 0xC1 */ &QDomXPathImpl::inst_undefined_instruction
8706 : , /* 0xC2 */ &QDomXPathImpl::inst_undefined_instruction
8707 : , /* 0xC3 */ &QDomXPathImpl::inst_undefined_instruction
8708 : , /* 0xC4 */ &QDomXPathImpl::inst_undefined_instruction
8709 : , /* 0xC5 */ &QDomXPathImpl::inst_undefined_instruction
8710 : , /* 0xC6 */ &QDomXPathImpl::inst_undefined_instruction
8711 : , /* 0xC7 */ &QDomXPathImpl::inst_undefined_instruction
8712 : , /* 0xC8 */ &QDomXPathImpl::inst_undefined_instruction
8713 : , /* 0xC9 */ &QDomXPathImpl::inst_undefined_instruction
8714 : , /* 0xCA */ &QDomXPathImpl::inst_undefined_instruction
8715 : , /* 0xCB */ &QDomXPathImpl::inst_undefined_instruction
8716 : , /* 0xCC */ &QDomXPathImpl::inst_undefined_instruction
8717 : , /* 0xCD */ &QDomXPathImpl::inst_undefined_instruction
8718 : , /* 0xCE */ &QDomXPathImpl::inst_undefined_instruction
8719 : , /* 0xCF */ &QDomXPathImpl::inst_undefined_instruction
8720 :
8721 : , /* 0xD0 */ &QDomXPathImpl::inst_undefined_instruction
8722 : , /* 0xD1 */ &QDomXPathImpl::inst_undefined_instruction
8723 : , /* 0xD2 */ &QDomXPathImpl::inst_undefined_instruction
8724 : , /* 0xD3 */ &QDomXPathImpl::inst_undefined_instruction
8725 : , /* 0xD4 */ &QDomXPathImpl::inst_undefined_instruction
8726 : , /* 0xD5 */ &QDomXPathImpl::inst_undefined_instruction
8727 : , /* 0xD6 */ &QDomXPathImpl::inst_undefined_instruction
8728 : , /* 0xD7 */ &QDomXPathImpl::inst_undefined_instruction
8729 : , /* 0xD8 */ &QDomXPathImpl::inst_undefined_instruction
8730 : , /* 0xD9 */ &QDomXPathImpl::inst_undefined_instruction
8731 : , /* 0xDA */ &QDomXPathImpl::inst_undefined_instruction
8732 : , /* 0xDB */ &QDomXPathImpl::inst_undefined_instruction
8733 : , /* 0xDC */ &QDomXPathImpl::inst_undefined_instruction
8734 : , /* 0xDD */ &QDomXPathImpl::inst_undefined_instruction
8735 : , /* 0xDE */ &QDomXPathImpl::inst_undefined_instruction
8736 : , /* 0xDF */ &QDomXPathImpl::inst_undefined_instruction
8737 :
8738 : , /* 0xE0 */ &QDomXPathImpl::inst_undefined_instruction
8739 : , /* 0xE1 */ &QDomXPathImpl::inst_undefined_instruction
8740 : , /* 0xE2 */ &QDomXPathImpl::inst_undefined_instruction
8741 : , /* 0xE3 */ &QDomXPathImpl::inst_undefined_instruction
8742 : , /* 0xE4 */ &QDomXPathImpl::inst_undefined_instruction
8743 : , /* 0xE5 */ &QDomXPathImpl::inst_undefined_instruction
8744 : , /* 0xE6 */ &QDomXPathImpl::inst_undefined_instruction
8745 : , /* 0xE7 */ &QDomXPathImpl::inst_undefined_instruction
8746 : , /* 0xE8 */ &QDomXPathImpl::inst_undefined_instruction
8747 : , /* 0xE9 */ &QDomXPathImpl::inst_undefined_instruction
8748 : , /* 0xEA */ &QDomXPathImpl::inst_undefined_instruction
8749 : , /* 0xEB */ &QDomXPathImpl::inst_undefined_instruction
8750 : , /* 0xEC */ &QDomXPathImpl::inst_undefined_instruction
8751 : , /* 0xED */ &QDomXPathImpl::inst_undefined_instruction
8752 : , /* 0xEE */ &QDomXPathImpl::inst_undefined_instruction
8753 : , /* 0xEF */ &QDomXPathImpl::inst_undefined_instruction
8754 :
8755 : , /* 0xF0 */ &QDomXPathImpl::inst_undefined_instruction
8756 : , /* 0xF1 */ &QDomXPathImpl::inst_undefined_instruction
8757 : , /* 0xF2 */ &QDomXPathImpl::inst_undefined_instruction
8758 : , /* 0xF3 */ &QDomXPathImpl::inst_undefined_instruction
8759 : , /* 0xF4 */ &QDomXPathImpl::inst_undefined_instruction
8760 : , /* 0xF5 */ &QDomXPathImpl::inst_undefined_instruction
8761 : , /* 0xF6 */ &QDomXPathImpl::inst_undefined_instruction
8762 : , /* 0xF7 */ &QDomXPathImpl::inst_undefined_instruction
8763 : , /* 0xF8 */ &QDomXPathImpl::inst_undefined_instruction
8764 : , /* 0xF9 */ &QDomXPathImpl::inst_undefined_instruction
8765 : , /* 0xFA */ &QDomXPathImpl::inst_undefined_instruction
8766 : , /* 0xFB */ &QDomXPathImpl::inst_undefined_instruction
8767 : , /* 0xFC */ &QDomXPathImpl::inst_undefined_instruction
8768 : , /* 0xFD */ &QDomXPathImpl::inst_undefined_instruction
8769 : , /* 0xFE */ &QDomXPathImpl::inst_undefined_instruction
8770 : , /* 0xFF */ &QDomXPathImpl::inst_undefined_instruction
8771 : };
8772 :
8773 :
8774 : QDomXPath::QDomXPathImpl::disassembly_function_t const QDomXPath::QDomXPathImpl::g_disassemble_instructions[256] =
8775 : {
8776 : /* 0x00 */ &QDomXPathImpl::disassemble_end
8777 : , /* 0x01 */ &QDomXPathImpl::disassemble_call
8778 : , /* 0x02 */ &QDomXPathImpl::disassemble_small_function
8779 : , /* 0x03 */ &QDomXPathImpl::disassemble_large_function
8780 : , /* 0x04 */ &QDomXPathImpl::disassemble_jump
8781 : , /* 0x05 */ &QDomXPathImpl::disassemble_jump_if_true
8782 : , /* 0x06 */ &QDomXPathImpl::disassemble_jump_if_false
8783 : , /* 0x07 */ &QDomXPathImpl::disassemble_jump_if_zero
8784 : , /* 0x08 */ &QDomXPathImpl::disassemble_return
8785 : , /* 0x09 */ &QDomXPathImpl::disassemble_undefined_instruction
8786 : , /* 0x0A */ &QDomXPathImpl::disassemble_undefined_instruction
8787 : , /* 0x0B */ &QDomXPathImpl::disassemble_undefined_instruction
8788 : , /* 0x0C */ &QDomXPathImpl::disassemble_undefined_instruction
8789 : , /* 0x0D */ &QDomXPathImpl::disassemble_undefined_instruction
8790 : , /* 0x0E */ &QDomXPathImpl::disassemble_undefined_instruction
8791 : , /* 0x0F */ &QDomXPathImpl::disassemble_undefined_instruction
8792 :
8793 : , /* 0x10 */ &QDomXPathImpl::disassemble_get_variable
8794 : , /* 0x11 */ &QDomXPathImpl::disassemble_set_variable
8795 : , /* 0x12 */ &QDomXPathImpl::disassemble_undefined_instruction
8796 : , /* 0x13 */ &QDomXPathImpl::disassemble_undefined_instruction
8797 : , /* 0x14 */ &QDomXPathImpl::disassemble_undefined_instruction
8798 : , /* 0x15 */ &QDomXPathImpl::disassemble_undefined_instruction
8799 : , /* 0x16 */ &QDomXPathImpl::disassemble_undefined_instruction
8800 : , /* 0x17 */ &QDomXPathImpl::disassemble_undefined_instruction
8801 : , /* 0x18 */ &QDomXPathImpl::disassemble_undefined_instruction
8802 : , /* 0x19 */ &QDomXPathImpl::disassemble_undefined_instruction
8803 : , /* 0x1A */ &QDomXPathImpl::disassemble_undefined_instruction
8804 : , /* 0x1B */ &QDomXPathImpl::disassemble_undefined_instruction
8805 : , /* 0x1C */ &QDomXPathImpl::disassemble_undefined_instruction
8806 : , /* 0x1D */ &QDomXPathImpl::disassemble_undefined_instruction
8807 : , /* 0x1E */ &QDomXPathImpl::disassemble_undefined_instruction
8808 : , /* 0x1F */ &QDomXPathImpl::disassemble_undefined_instruction
8809 :
8810 : , /* 0x20 */ &QDomXPathImpl::disassemble_pop1
8811 : , /* 0x21 */ &QDomXPathImpl::disassemble_pop2
8812 : , /* 0x22 */ &QDomXPathImpl::disassemble_pop3
8813 : , /* 0x23 */ &QDomXPathImpl::disassemble_pop4
8814 : , /* 0x24 */ &QDomXPathImpl::disassemble_pop5
8815 : , /* 0x25 */ &QDomXPathImpl::disassemble_undefined_instruction
8816 : , /* 0x26 */ &QDomXPathImpl::disassemble_undefined_instruction
8817 : , /* 0x27 */ &QDomXPathImpl::disassemble_undefined_instruction
8818 : , /* 0x28 */ &QDomXPathImpl::disassemble_undefined_instruction
8819 : , /* 0x29 */ &QDomXPathImpl::disassemble_undefined_instruction
8820 : , /* 0x2A */ &QDomXPathImpl::disassemble_duplicate1
8821 : , /* 0x2B */ &QDomXPathImpl::disassemble_duplicate2
8822 : , /* 0x2C */ &QDomXPathImpl::disassemble_duplicate3
8823 : , /* 0x2D */ &QDomXPathImpl::disassemble_duplicate4
8824 : , /* 0x2E */ &QDomXPathImpl::disassemble_duplicate5
8825 : , /* 0x2F */ &QDomXPathImpl::disassemble_undefined_instruction
8826 :
8827 : , /* 0x30 */ &QDomXPathImpl::disassemble_swap1
8828 : , /* 0x31 */ &QDomXPathImpl::disassemble_swap2
8829 : , /* 0x32 */ &QDomXPathImpl::disassemble_swap3
8830 : , /* 0x33 */ &QDomXPathImpl::disassemble_swap4
8831 : , /* 0x34 */ &QDomXPathImpl::disassemble_swap5
8832 : , /* 0x35 */ &QDomXPathImpl::disassemble_swap2_3
8833 : , /* 0x36 */ &QDomXPathImpl::disassemble_undefined_instruction
8834 : , /* 0x37 */ &QDomXPathImpl::disassemble_undefined_instruction
8835 : , /* 0x38 */ &QDomXPathImpl::disassemble_undefined_instruction
8836 : , /* 0x39 */ &QDomXPathImpl::disassemble_undefined_instruction
8837 : , /* 0x3A */ &QDomXPathImpl::disassemble_undefined_instruction
8838 : , /* 0x3B */ &QDomXPathImpl::disassemble_undefined_instruction
8839 : , /* 0x3C */ &QDomXPathImpl::disassemble_undefined_instruction
8840 : , /* 0x3D */ &QDomXPathImpl::disassemble_undefined_instruction
8841 : , /* 0x3E */ &QDomXPathImpl::disassemble_undefined_instruction
8842 : , /* 0x3F */ &QDomXPathImpl::disassemble_undefined_instruction
8843 :
8844 : , /* 0x40 */ &QDomXPathImpl::disassemble_push_any_string
8845 : , /* 0x41 */ &QDomXPathImpl::disassemble_push_byte
8846 : , /* 0x42 */ &QDomXPathImpl::disassemble_push_double
8847 : , /* 0x43 */ &QDomXPathImpl::disassemble_push_double_zero
8848 : , /* 0x44 */ &QDomXPathImpl::disassemble_push_empty_node_set
8849 : , /* 0x45 */ &QDomXPathImpl::disassemble_push_empty_set
8850 : , /* 0x46 */ &QDomXPathImpl::disassemble_push_empty_string
8851 : , /* 0x47 */ &QDomXPathImpl::disassemble_push_end_of_arguments
8852 : , /* 0x48 */ &QDomXPathImpl::disassemble_push_false
8853 : , /* 0x49 */ &QDomXPathImpl::disassemble_push_large_string
8854 : , /* 0x4A */ &QDomXPathImpl::disassemble_push_long
8855 : , /* 0x4B */ &QDomXPathImpl::disassemble_push_longlong
8856 : , /* 0x4C */ &QDomXPathImpl::disassemble_push_medium_string
8857 : , /* 0x4D */ &QDomXPathImpl::disassemble_push_negative_byte
8858 : , /* 0x4E */ &QDomXPathImpl::disassemble_push_negative_short
8859 : , /* 0x4F */ &QDomXPathImpl::disassemble_push_negative_long
8860 :
8861 : , /* 0x50 */ &QDomXPathImpl::disassemble_push_short
8862 : , /* 0x51 */ &QDomXPathImpl::disassemble_push_small_string
8863 : , /* 0x52 */ &QDomXPathImpl::disassemble_push_true
8864 : , /* 0x53 */ &QDomXPathImpl::disassemble_push_zero
8865 : , /* 0x54 */ &QDomXPathImpl::disassemble_undefined_instruction
8866 : , /* 0x55 */ &QDomXPathImpl::disassemble_undefined_instruction
8867 : , /* 0x56 */ &QDomXPathImpl::disassemble_undefined_instruction
8868 : , /* 0x57 */ &QDomXPathImpl::disassemble_undefined_instruction
8869 : , /* 0x58 */ &QDomXPathImpl::disassemble_undefined_instruction
8870 : , /* 0x59 */ &QDomXPathImpl::disassemble_undefined_instruction
8871 : , /* 0x5A */ &QDomXPathImpl::disassemble_undefined_instruction
8872 : , /* 0x5B */ &QDomXPathImpl::disassemble_undefined_instruction
8873 : , /* 0x5C */ &QDomXPathImpl::disassemble_undefined_instruction
8874 : , /* 0x5D */ &QDomXPathImpl::disassemble_undefined_instruction
8875 : , /* 0x5E */ &QDomXPathImpl::disassemble_undefined_instruction
8876 : , /* 0x5F */ &QDomXPathImpl::disassemble_undefined_instruction
8877 :
8878 : , /* 0x60 */ &QDomXPathImpl::disassemble_add
8879 : , /* 0x61 */ &QDomXPathImpl::disassemble_and
8880 : , /* 0x62 */ &QDomXPathImpl::disassemble_ceiling
8881 : , /* 0x63 */ &QDomXPathImpl::disassemble_decrement
8882 : , /* 0x64 */ &QDomXPathImpl::disassemble_divide
8883 : , /* 0x65 */ &QDomXPathImpl::disassemble_equal
8884 : , /* 0x66 */ &QDomXPathImpl::disassemble_floor
8885 : , /* 0x67 */ &QDomXPathImpl::disassemble_greater_or_equal
8886 : , /* 0x68 */ &QDomXPathImpl::disassemble_greater_than
8887 : , /* 0x69 */ &QDomXPathImpl::disassemble_idivide
8888 : , /* 0x6A */ &QDomXPathImpl::disassemble_increment
8889 : , /* 0x6B */ &QDomXPathImpl::disassemble_less_or_equal
8890 : , /* 0x6C */ &QDomXPathImpl::disassemble_less_than
8891 : , /* 0x6D */ &QDomXPathImpl::disassemble_modulo
8892 : , /* 0x6E */ &QDomXPathImpl::disassemble_multiply
8893 : , /* 0x6F */ &QDomXPathImpl::disassemble_negate
8894 :
8895 : , /* 0x70 */ &QDomXPathImpl::disassemble_not
8896 : , /* 0x71 */ &QDomXPathImpl::disassemble_not_equal
8897 : , /* 0x72 */ &QDomXPathImpl::disassemble_or
8898 : , /* 0x73 */ &QDomXPathImpl::disassemble_round
8899 : , /* 0x74 */ &QDomXPathImpl::disassemble_string_length
8900 : , /* 0x75 */ &QDomXPathImpl::disassemble_subtract
8901 : , /* 0x76 */ &QDomXPathImpl::disassemble_undefined_instruction
8902 : , /* 0x77 */ &QDomXPathImpl::disassemble_undefined_instruction
8903 : , /* 0x78 */ &QDomXPathImpl::disassemble_undefined_instruction
8904 : , /* 0x79 */ &QDomXPathImpl::disassemble_undefined_instruction
8905 : , /* 0x7A */ &QDomXPathImpl::disassemble_undefined_instruction
8906 : , /* 0x7B */ &QDomXPathImpl::disassemble_undefined_instruction
8907 : , /* 0x7C */ &QDomXPathImpl::disassemble_undefined_instruction
8908 : , /* 0x7D */ &QDomXPathImpl::disassemble_undefined_instruction
8909 : , /* 0x7E */ &QDomXPathImpl::disassemble_undefined_instruction
8910 : , /* 0x7F */ &QDomXPathImpl::disassemble_undefined_instruction
8911 :
8912 : , /* 0x80 */ &QDomXPathImpl::disassemble_axis
8913 : , /* 0x81 */ &QDomXPathImpl::disassemble_root
8914 : , /* 0x82 */ &QDomXPathImpl::disassemble_get_node_set
8915 : , /* 0x83 */ &QDomXPathImpl::disassemble_set_node_set
8916 : , /* 0x84 */ &QDomXPathImpl::disassemble_get_result
8917 : , /* 0x85 */ &QDomXPathImpl::disassemble_set_result
8918 : , /* 0x86 */ &QDomXPathImpl::disassemble_get_position
8919 : , /* 0x87 */ &QDomXPathImpl::disassemble_set_position
8920 : , /* 0x88 */ &QDomXPathImpl::disassemble_node_set_size
8921 : , /* 0x89 */ &QDomXPathImpl::disassemble_merge_sets
8922 : , /* 0x8A */ &QDomXPathImpl::disassemble_predicate
8923 : , /* 0x8B */ &QDomXPathImpl::disassemble_create_node_context
8924 : , /* 0x8C */ &QDomXPathImpl::disassemble_get_context_node
8925 : , /* 0x8D */ &QDomXPathImpl::disassemble_next_context_node
8926 : , /* 0x8E */ &QDomXPathImpl::disassemble_pop_context
8927 : , /* 0x8F */ &QDomXPathImpl::disassemble_undefined_instruction
8928 :
8929 : , /* 0x90 */ &QDomXPathImpl::disassemble_undefined_instruction
8930 : , /* 0x91 */ &QDomXPathImpl::disassemble_undefined_instruction
8931 : , /* 0x92 */ &QDomXPathImpl::disassemble_undefined_instruction
8932 : , /* 0x93 */ &QDomXPathImpl::disassemble_undefined_instruction
8933 : , /* 0x94 */ &QDomXPathImpl::disassemble_undefined_instruction
8934 : , /* 0x95 */ &QDomXPathImpl::disassemble_undefined_instruction
8935 : , /* 0x96 */ &QDomXPathImpl::disassemble_undefined_instruction
8936 : , /* 0x97 */ &QDomXPathImpl::disassemble_undefined_instruction
8937 : , /* 0x98 */ &QDomXPathImpl::disassemble_undefined_instruction
8938 : , /* 0x99 */ &QDomXPathImpl::disassemble_undefined_instruction
8939 : , /* 0x9A */ &QDomXPathImpl::disassemble_undefined_instruction
8940 : , /* 0x9B */ &QDomXPathImpl::disassemble_undefined_instruction
8941 : , /* 0x9C */ &QDomXPathImpl::disassemble_undefined_instruction
8942 : , /* 0x9D */ &QDomXPathImpl::disassemble_undefined_instruction
8943 : , /* 0x9E */ &QDomXPathImpl::disassemble_undefined_instruction
8944 : , /* 0x9F */ &QDomXPathImpl::disassemble_undefined_instruction
8945 :
8946 : , /* 0xA0 */ &QDomXPathImpl::disassemble_undefined_instruction
8947 : , /* 0xA1 */ &QDomXPathImpl::disassemble_undefined_instruction
8948 : , /* 0xA2 */ &QDomXPathImpl::disassemble_undefined_instruction
8949 : , /* 0xA3 */ &QDomXPathImpl::disassemble_undefined_instruction
8950 : , /* 0xA4 */ &QDomXPathImpl::disassemble_undefined_instruction
8951 : , /* 0xA5 */ &QDomXPathImpl::disassemble_undefined_instruction
8952 : , /* 0xA6 */ &QDomXPathImpl::disassemble_undefined_instruction
8953 : , /* 0xA7 */ &QDomXPathImpl::disassemble_undefined_instruction
8954 : , /* 0xA8 */ &QDomXPathImpl::disassemble_undefined_instruction
8955 : , /* 0xA9 */ &QDomXPathImpl::disassemble_undefined_instruction
8956 : , /* 0xAA */ &QDomXPathImpl::disassemble_undefined_instruction
8957 : , /* 0xAB */ &QDomXPathImpl::disassemble_undefined_instruction
8958 : , /* 0xAC */ &QDomXPathImpl::disassemble_undefined_instruction
8959 : , /* 0xAD */ &QDomXPathImpl::disassemble_undefined_instruction
8960 : , /* 0xAE */ &QDomXPathImpl::disassemble_undefined_instruction
8961 : , /* 0xAF */ &QDomXPathImpl::disassemble_undefined_instruction
8962 :
8963 : , /* 0xB0 */ &QDomXPathImpl::disassemble_undefined_instruction
8964 : , /* 0xB1 */ &QDomXPathImpl::disassemble_undefined_instruction
8965 : , /* 0xB2 */ &QDomXPathImpl::disassemble_undefined_instruction
8966 : , /* 0xB3 */ &QDomXPathImpl::disassemble_undefined_instruction
8967 : , /* 0xB4 */ &QDomXPathImpl::disassemble_undefined_instruction
8968 : , /* 0xB5 */ &QDomXPathImpl::disassemble_undefined_instruction
8969 : , /* 0xB6 */ &QDomXPathImpl::disassemble_undefined_instruction
8970 : , /* 0xB7 */ &QDomXPathImpl::disassemble_undefined_instruction
8971 : , /* 0xB8 */ &QDomXPathImpl::disassemble_undefined_instruction
8972 : , /* 0xB9 */ &QDomXPathImpl::disassemble_undefined_instruction
8973 : , /* 0xBA */ &QDomXPathImpl::disassemble_undefined_instruction
8974 : , /* 0xBB */ &QDomXPathImpl::disassemble_undefined_instruction
8975 : , /* 0xBC */ &QDomXPathImpl::disassemble_undefined_instruction
8976 : , /* 0xBD */ &QDomXPathImpl::disassemble_undefined_instruction
8977 : , /* 0xBE */ &QDomXPathImpl::disassemble_undefined_instruction
8978 : , /* 0xBF */ &QDomXPathImpl::disassemble_undefined_instruction
8979 :
8980 : , /* 0xC0 */ &QDomXPathImpl::disassemble_undefined_instruction
8981 : , /* 0xC1 */ &QDomXPathImpl::disassemble_undefined_instruction
8982 : , /* 0xC2 */ &QDomXPathImpl::disassemble_undefined_instruction
8983 : , /* 0xC3 */ &QDomXPathImpl::disassemble_undefined_instruction
8984 : , /* 0xC4 */ &QDomXPathImpl::disassemble_undefined_instruction
8985 : , /* 0xC5 */ &QDomXPathImpl::disassemble_undefined_instruction
8986 : , /* 0xC6 */ &QDomXPathImpl::disassemble_undefined_instruction
8987 : , /* 0xC7 */ &QDomXPathImpl::disassemble_undefined_instruction
8988 : , /* 0xC8 */ &QDomXPathImpl::disassemble_undefined_instruction
8989 : , /* 0xC9 */ &QDomXPathImpl::disassemble_undefined_instruction
8990 : , /* 0xCA */ &QDomXPathImpl::disassemble_undefined_instruction
8991 : , /* 0xCB */ &QDomXPathImpl::disassemble_undefined_instruction
8992 : , /* 0xCC */ &QDomXPathImpl::disassemble_undefined_instruction
8993 : , /* 0xCD */ &QDomXPathImpl::disassemble_undefined_instruction
8994 : , /* 0xCE */ &QDomXPathImpl::disassemble_undefined_instruction
8995 : , /* 0xCF */ &QDomXPathImpl::disassemble_undefined_instruction
8996 :
8997 : , /* 0xD0 */ &QDomXPathImpl::disassemble_undefined_instruction
8998 : , /* 0xD1 */ &QDomXPathImpl::disassemble_undefined_instruction
8999 : , /* 0xD2 */ &QDomXPathImpl::disassemble_undefined_instruction
9000 : , /* 0xD3 */ &QDomXPathImpl::disassemble_undefined_instruction
9001 : , /* 0xD4 */ &QDomXPathImpl::disassemble_undefined_instruction
9002 : , /* 0xD5 */ &QDomXPathImpl::disassemble_undefined_instruction
9003 : , /* 0xD6 */ &QDomXPathImpl::disassemble_undefined_instruction
9004 : , /* 0xD7 */ &QDomXPathImpl::disassemble_undefined_instruction
9005 : , /* 0xD8 */ &QDomXPathImpl::disassemble_undefined_instruction
9006 : , /* 0xD9 */ &QDomXPathImpl::disassemble_undefined_instruction
9007 : , /* 0xDA */ &QDomXPathImpl::disassemble_undefined_instruction
9008 : , /* 0xDB */ &QDomXPathImpl::disassemble_undefined_instruction
9009 : , /* 0xDC */ &QDomXPathImpl::disassemble_undefined_instruction
9010 : , /* 0xDD */ &QDomXPathImpl::disassemble_undefined_instruction
9011 : , /* 0xDE */ &QDomXPathImpl::disassemble_undefined_instruction
9012 : , /* 0xDF */ &QDomXPathImpl::disassemble_undefined_instruction
9013 :
9014 : , /* 0xE0 */ &QDomXPathImpl::disassemble_undefined_instruction
9015 : , /* 0xE1 */ &QDomXPathImpl::disassemble_undefined_instruction
9016 : , /* 0xE2 */ &QDomXPathImpl::disassemble_undefined_instruction
9017 : , /* 0xE3 */ &QDomXPathImpl::disassemble_undefined_instruction
9018 : , /* 0xE4 */ &QDomXPathImpl::disassemble_undefined_instruction
9019 : , /* 0xE5 */ &QDomXPathImpl::disassemble_undefined_instruction
9020 : , /* 0xE6 */ &QDomXPathImpl::disassemble_undefined_instruction
9021 : , /* 0xE7 */ &QDomXPathImpl::disassemble_undefined_instruction
9022 : , /* 0xE8 */ &QDomXPathImpl::disassemble_undefined_instruction
9023 : , /* 0xE9 */ &QDomXPathImpl::disassemble_undefined_instruction
9024 : , /* 0xEA */ &QDomXPathImpl::disassemble_undefined_instruction
9025 : , /* 0xEB */ &QDomXPathImpl::disassemble_undefined_instruction
9026 : , /* 0xEC */ &QDomXPathImpl::disassemble_undefined_instruction
9027 : , /* 0xED */ &QDomXPathImpl::disassemble_undefined_instruction
9028 : , /* 0xEE */ &QDomXPathImpl::disassemble_undefined_instruction
9029 : , /* 0xEF */ &QDomXPathImpl::disassemble_undefined_instruction
9030 :
9031 : , /* 0xF0 */ &QDomXPathImpl::disassemble_undefined_instruction
9032 : , /* 0xF1 */ &QDomXPathImpl::disassemble_undefined_instruction
9033 : , /* 0xF2 */ &QDomXPathImpl::disassemble_undefined_instruction
9034 : , /* 0xF3 */ &QDomXPathImpl::disassemble_undefined_instruction
9035 : , /* 0xF4 */ &QDomXPathImpl::disassemble_undefined_instruction
9036 : , /* 0xF5 */ &QDomXPathImpl::disassemble_undefined_instruction
9037 : , /* 0xF6 */ &QDomXPathImpl::disassemble_undefined_instruction
9038 : , /* 0xF7 */ &QDomXPathImpl::disassemble_undefined_instruction
9039 : , /* 0xF8 */ &QDomXPathImpl::disassemble_undefined_instruction
9040 : , /* 0xF9 */ &QDomXPathImpl::disassemble_undefined_instruction
9041 : , /* 0xFA */ &QDomXPathImpl::disassemble_undefined_instruction
9042 : , /* 0xFB */ &QDomXPathImpl::disassemble_undefined_instruction
9043 : , /* 0xFC */ &QDomXPathImpl::disassemble_undefined_instruction
9044 : , /* 0xFD */ &QDomXPathImpl::disassemble_undefined_instruction
9045 : , /* 0xFE */ &QDomXPathImpl::disassemble_undefined_instruction
9046 : , /* 0xFF */ &QDomXPathImpl::disassemble_undefined_instruction
9047 : };
9048 :
9049 :
9050 : /** \class QDomXPath
9051 : * \brief A private class used to handle XPath expressions.
9052 : *
9053 : * This class parses the XPath expression and is capable of executing it
9054 : * against a QDomNode.
9055 : *
9056 : * The class is based on the XPath syntax as defined by the W3C consortium:
9057 : *
9058 : * http://www.w3.org/TR/xpath/#node-sets
9059 : *
9060 : * In a way, this is a rewrite of the QXmlQuery, except that this
9061 : * implementation can be used against a QDomNode and thus I can avoid all
9062 : * the problems with the QXmlQuery (i.e. having to convert the XML back to
9063 : * text so it can be used with QXmlQuery without crashing.)
9064 : *
9065 : * As per point "2 Basics" XQuery is a case-sensitive language so we read
9066 : * everything as is and test instructions in lowercase only as expected
9067 : * by the language. There are a few exceptions when testing certain values
9068 : * such as the contains of the lang attribute of the name of a processing
9069 : * instruction language (i.e "php" matches the language name in
9070 : * "<?PHP ... ?>").
9071 : *
9072 : * http://www.w3.org/TR/xquery/#id-basics
9073 : *
9074 : * \note
9075 : * All the expressions are not supported.
9076 : *
9077 : * The following is 'UnionExpr' and corresponding children rules as
9078 : * defined on the w3c website:
9079 : *
9080 : * \code
9081 : * Expr ::= OrExpr
9082 : *
9083 : * PrimaryExpr ::= VariableReference
9084 : * | '(' Expr ')'
9085 : * | Literal
9086 : * | Number
9087 : * | FunctionCall
9088 : *
9089 : * FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument )* )? ')'
9090 : *
9091 : * Argument ::= Expr
9092 : *
9093 : * OrExpr ::= AndExpr
9094 : * | OrExpr 'or' AndExpr
9095 : *
9096 : * AndExpr ::= EqualityExpr
9097 : * | AndExpr 'and' EqualityExpr
9098 : *
9099 : * EqualityExpr ::= RelationalExpr
9100 : * | EqualityExpr '=' RelationalExpr
9101 : * | EqualityExpr '!=' RelationalExpr
9102 : *
9103 : * RelationalExpr ::= AdditiveExpr
9104 : * | RelationalExpr '<' AdditiveExpr
9105 : * | RelationalExpr '>' AdditiveExpr
9106 : * | RelationalExpr '<=' AdditiveExpr
9107 : * | RelationalExpr '>=' AdditiveExpr
9108 : *
9109 : * AdditiveExpr ::= MultiplicativeExpr
9110 : * | AdditiveExpr '+' MultiplicativeExpr
9111 : * | AdditiveExpr '-' MultiplicativeExpr
9112 : *
9113 : * MultiplicativeExpr ::= UnaryExpr
9114 : * | MultiplicativeExpr MultiplicativeOperator UnaryExpr
9115 : * | MultiplicativeExpr 'div' UnaryExpr
9116 : * | MultiplicativeExpr 'mod' UnaryExpr
9117 : *
9118 : * UnaryExpr ::= UnionExpr
9119 : * | '-' UnaryExpr
9120 : *
9121 : * UnionExpr ::= PathExpr
9122 : * | UnionExpr '|' PathExpr
9123 : *
9124 : * PathExpr ::= LocationPath
9125 : * | FilterExpr
9126 : * | FilterExpr '/' RelativeLocationPath
9127 : * | FilterExpr '//' RelativeLocationPath
9128 : *
9129 : * FilterExpr ::= PrimaryExpr
9130 : * | FilterExpr Predicate
9131 : *
9132 : * LocationPath ::= RelativeLocationPath
9133 : * | AbsoluteLocationPath
9134 : *
9135 : * AbsoluteLocationPath ::= '/' RelativeLocationPath?
9136 : * | AbbreviatedAbsoluteLocationPath
9137 : *
9138 : * AbbreviatedAbsoluteLocationPath ::= '//' RelativeLocationPath
9139 : *
9140 : * RelativeLocationPath ::= Step
9141 : * | RelativeLocationPath '/' Step
9142 : * | AbbreviatedRelativeLocationPath
9143 : *
9144 : * AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step
9145 : *
9146 : * Step ::= AxisSpecifier NodeTest Predicate*
9147 : * | AbbreviatedStep
9148 : *
9149 : * AxisSpecifier ::= AxisName '::'
9150 : * | AbbreviatedAxisSpecifier
9151 : *
9152 : * AxisName ::= 'ancestor'
9153 : * | 'ancestor-or-self'
9154 : * | 'attribute'
9155 : * | 'child'
9156 : * | 'descendant'
9157 : * | 'descendant-or-self'
9158 : * | 'following'
9159 : * | 'following-sibling'
9160 : * | 'namespace'
9161 : * | 'parent'
9162 : * | 'preceding'
9163 : * | 'preceding-sibling'
9164 : * | 'self'
9165 : *
9166 : * NodeTest ::= NameTest
9167 : * | NodeType '(' ')'
9168 : * | 'processing-instruction' '(' Literal ')'
9169 : *
9170 : * Predicate ::= '[' PredicateExpr ']'
9171 : *
9172 : * PredicateExpr ::= Expr
9173 : *
9174 : * AbbreviatedStep ::= '.'
9175 : * | '..'
9176 : *
9177 : * AbbreviatedAxisSpecifier ::= '@'?
9178 : *
9179 : * ExprToken ::= '(' | ')'
9180 : * | '[' | ']'
9181 : * | '.'
9182 : * | '..'
9183 : * | '@'
9184 : * | ','
9185 : * | '::'
9186 : * | NameTest
9187 : * | NodeType
9188 : * | Operator
9189 : * | FunctionName
9190 : * | AxisName
9191 : * | Literal
9192 : * | Number
9193 : * | VariableReference
9194 : *
9195 : * Literal ::= '"' [^"]* '"'
9196 : * | "'" [^']* "'"
9197 : *
9198 : * Number ::= Digits ('.' Digits?)?
9199 : * | '.' Digits
9200 : *
9201 : * Digits ::= [0-9]+
9202 : *
9203 : * Operator ::= OperatorName
9204 : * | MultiplyOperator
9205 : * | '/'
9206 : * | '//'
9207 : * | '|'
9208 : * | '+'
9209 : * | '-'
9210 : * | '='
9211 : * | '!='
9212 : * | '<'
9213 : * | '<='
9214 : * | '>'
9215 : * | '>='
9216 : *
9217 : * OperatorName ::= 'and'
9218 : * | 'or'
9219 : * | 'mod'
9220 : * | 'div'
9221 : *
9222 : * MultiplyOperator ::= '*'
9223 : *
9224 : * FunctionName ::= QName - NodeType
9225 : *
9226 : * VariableReference ::= '$' QName
9227 : *
9228 : * NameTest ::= '*'
9229 : * | NCName ':' '*'
9230 : * | QName
9231 : *
9232 : * NodeType ::= 'comment'
9233 : * | 'text'
9234 : * | 'processing-instruction'
9235 : * | 'node'
9236 : *
9237 : * ExprWhitespace ::= S
9238 : *
9239 : * NCName ::= Name - (Char* ':' Char*)
9240 : *
9241 : * S ::= (#x20 | #x9 | #xD | #xA)+
9242 : *
9243 : * Char ::= #x9
9244 : * | #xA
9245 : * | #xD
9246 : * | [#x20-#xD7FF]
9247 : * | [#xE000-#xFFFD]
9248 : * | [#x10000-#x10FFFF]
9249 : *
9250 : * NameStartChar ::= ':'
9251 : * | [A-Z]
9252 : * | '_'
9253 : * | [a-z]
9254 : * | [#xC0-#xD6]
9255 : * | [#xD8-#xF6]
9256 : * | [#xF8-#x2FF]
9257 : * | [#x370-#x37D]
9258 : * | [#x37F-#x1FFF]
9259 : * | [#x200C-#x200D]
9260 : * | [#x2070-#x218F]
9261 : * | [#x2C00-#x2FEF]
9262 : * | [#x3001-#xD7FF]
9263 : * | [#xF900-#xFDCF]
9264 : * | [#xFDF0-#xFFFD]
9265 : * | [#x10000-#xEFFFF]
9266 : *
9267 : * NameChar ::= NameStartChar
9268 : * | '-'
9269 : * | '.'
9270 : * | [0-9]
9271 : * | #xB7
9272 : * | [#x0300-#x036F]
9273 : * | [#x203F-#x2040]
9274 : *
9275 : * Name ::= NameStartChar (NameChar)*
9276 : *
9277 : * Names ::= Name (#x20 Name)*
9278 : *
9279 : * Nmtoken ::= (NameChar)+
9280 : *
9281 : * Nmtokens ::= Nmtoken (#x20 Nmtoken)*
9282 : *
9283 : * QName ::= PrefixedName
9284 : * | UnprefixedName
9285 : *
9286 : * PrefixedName ::= Prefix ':' LocalPart
9287 : *
9288 : * UnprefixedName ::= LocalPart
9289 : *
9290 : * Prefix ::= NCName
9291 : *
9292 : * LocalPart ::= NCName
9293 : * \endcode
9294 : */
9295 :
9296 :
9297 :
9298 : /** \brief Initialize the QDomXPath object.
9299 : *
9300 : * The constructor initializes the QDomXPath object. By default, the XPath
9301 : * is viewed as "." and the internal parameter (f_impl) is set to NULL
9302 : * until you call setXPath() or setProgram(). Once a program was defined,
9303 : * it is possible to apply an XML file against the XPath by calling the
9304 : * apply() functions.
9305 : *
9306 : * The array of variable is initialized, but it remains empty until you
9307 : * bind some variables to it with bindVariable().
9308 : *
9309 : * \sa bindVariable()
9310 : * \sa setXPath()
9311 : * \sa setProgram()
9312 : * \sa apply()
9313 : */
9314 0 : QDomXPath::QDomXPath()
9315 : //: f_xpath("") -- auto-init
9316 0 : : f_impl(NULL)
9317 : //, f_variables() -- auto-init
9318 : {
9319 0 : }
9320 :
9321 :
9322 : /** \brief Clean up the QDomXPath object.
9323 : *
9324 : * Since the f_impl variable member is defined in the qdomxpath.cpp, I
9325 : * cannot have it as a smart pointer in the qdomxpath.h file. For this
9326 : * reason, I manage it here.
9327 : */
9328 0 : QDomXPath::~QDomXPath()
9329 : {
9330 0 : delete f_impl;
9331 0 : }
9332 :
9333 :
9334 : /** \brief Set the XPath.
9335 : *
9336 : * This function sets the XPath of the QDomXPath object. By default, the
9337 : * XPath is set to "." (i.e. return the current node.)
9338 : *
9339 : * If the XPath is considered invalid, then this function returns false
9340 : * and the internal state is not changed. If considered valid, then the
9341 : * new XPath takes effect and the function returns true.
9342 : *
9343 : * Note that if xpath is set to the empty string or ".", it is always
9344 : * accepted and in both cases it represents the current node.
9345 : *
9346 : * \param[in] xpath The new XPath to use in this QDomXPath.
9347 : * \param[in] show_commands Show the assembly while compiling.
9348 : *
9349 : * \return true if the \p xpath is valid, false otherwise.
9350 : */
9351 0 : bool QDomXPath::setXPath(const QString& xpath, bool show_commands)
9352 : {
9353 0 : if(xpath.isEmpty() || xpath == ".")
9354 : {
9355 0 : f_xpath = "";
9356 0 : delete f_impl;
9357 0 : f_impl = NULL;
9358 0 : return true;
9359 : }
9360 :
9361 0 : QDomXPath::QDomXPathImpl *impl(new QDomXPath::QDomXPathImpl(this, xpath));
9362 : try
9363 : {
9364 0 : impl->parse(show_commands);
9365 : }
9366 0 : catch(...)
9367 : {
9368 0 : delete impl;
9369 0 : throw;
9370 : }
9371 :
9372 : // by contract this changes only at the very end of the function
9373 0 : f_xpath = xpath;
9374 0 : delete f_impl;
9375 0 : f_impl = impl;
9376 :
9377 0 : return true;
9378 : }
9379 :
9380 :
9381 : /** \brief Get the current xpath.
9382 : *
9383 : * This function returns the current XPath. If it was never set, then the
9384 : * function returns ".". Note that if the setXPath() function returns false,
9385 : * then the XPath doesn't get changed and thus this function returns the
9386 : * previous XPath.
9387 : *
9388 : * \return The XPath string, or "." if not yet defined.
9389 : */
9390 0 : QString QDomXPath::getXPath() const
9391 : {
9392 0 : if(f_xpath.isEmpty())
9393 : {
9394 0 : return ".";
9395 : }
9396 0 : return f_xpath;
9397 : }
9398 :
9399 :
9400 : /** \brief Apply the XPath against the specified node.
9401 : *
9402 : * This function applies (queries) the XPath that was previously set with
9403 : * the setXPath() function agains the input \p node parameter.
9404 : *
9405 : * The function returns a vector of nodes because it is not possible to
9406 : * add parameters to a QDomNodeList without being within the implementation
9407 : * (i.e. there is no function to add any node to the list.) This may be
9408 : * because a list of nodes is dynamic, it includes a way to remove the
9409 : * node from the list in the event the node gets deleted (just an assumption
9410 : * of course.)
9411 : *
9412 : * \todo
9413 : * We want to implement an apply() function that can return processed
9414 : * data so we could get a string or an integer instead of a list of
9415 : * nodes.
9416 : *
9417 : * \note
9418 : * If no program was loaded, this function returns its input as is.
9419 : *
9420 : * \note
9421 : * XPath does not modifies its input document.
9422 : *
9423 : * \param[in] node The node to query.
9424 : *
9425 : * \return A list of nodes (maybe empty.)
9426 : */
9427 0 : QDomXPath::node_vector_t QDomXPath::apply(QDomNode node) const
9428 : {
9429 0 : node_vector_t nodes;
9430 0 : nodes.push_back(node);
9431 0 : if(f_impl)
9432 : {
9433 0 : return f_impl->apply(nodes);
9434 : }
9435 : // no f_impl means "." which is just this node
9436 0 : return nodes;
9437 : }
9438 :
9439 :
9440 : /** \brief Apply the XPath against the specified list of nodes.
9441 : *
9442 : * This function applies (queries) the XPath that was previously set with
9443 : * one of the setXPath() or setProgram() functions against the set of
9444 : * input nodes.
9445 : *
9446 : * \todo
9447 : * We want to implement an apply() function that can return processed
9448 : * data so we could get a string or an integer instead of a list of
9449 : * nodes.
9450 : *
9451 : * \note
9452 : * The different nodes in the node vector do not all need to be from the
9453 : * same document.
9454 : *
9455 : * \note
9456 : * If no program was loaded, this function returns its input as is.
9457 : *
9458 : * \note
9459 : * XPath does not modifies its input document.
9460 : *
9461 : * \param[in] nodes The list of nodes to query.
9462 : *
9463 : * \return A list of nodes (maybe empty.)
9464 : */
9465 0 : QDomXPath::node_vector_t QDomXPath::apply(node_vector_t nodes) const
9466 : {
9467 0 : if(f_impl)
9468 : {
9469 0 : return f_impl->apply(nodes);
9470 : }
9471 : // no f_impl means "." which is just these nodes
9472 0 : return nodes;
9473 : }
9474 :
9475 :
9476 : /** \brief Disassemble the program.
9477 : *
9478 : * This function prints out the disassembled program in your stdout.
9479 : *
9480 : * The disassembled program shows the pointer counter (position inside the
9481 : * program) the instruction, and for PUSH instruction, the data getting
9482 : * pushed (i.e. a number or a string.)
9483 : *
9484 : * This function is used by the -d option of the cxpath compiler.
9485 : */
9486 0 : void QDomXPath::disassemble() const
9487 : {
9488 0 : if(f_impl)
9489 : {
9490 0 : f_impl->disassemble();
9491 : }
9492 : else
9493 : {
9494 0 : throw QDomXPathException_InternalError("error: no program to disassemble");
9495 : }
9496 0 : }
9497 :
9498 :
9499 : /** \brief Bind a variable to this DOM XPath.
9500 : *
9501 : * This function binds the variable named \p name to this XPath.
9502 : *
9503 : * Within the script, variable can be accessed using the $\<name> syntax.
9504 : *
9505 : * \note
9506 : * The bind function does not (yet) verify that the variable name is
9507 : * valid. It has to be a valid QName to work with the XPath. A QName
9508 : * is defined as an optional prefix, colon, and local-part:
9509 : *
9510 : * \code
9511 : * [ <prefix> ':' ] <local-part>
9512 : * \endcode
9513 : *
9514 : * \param[in] name The name of the variable.
9515 : * \param[in] value The value of the variable.
9516 : */
9517 0 : void QDomXPath::bindVariable(const QString& name, const QString& value)
9518 : {
9519 0 : f_variables[name] = value;
9520 0 : }
9521 :
9522 :
9523 : /** \brief Check whether a variable is defined.
9524 : *
9525 : * This function checks whether a variable is set. If so, the function
9526 : * returns true.
9527 : *
9528 : * Note that it is important for you to call this function if you'd like
9529 : * to get a variable contents and not throw if the variable does not
9530 : * exist:
9531 : *
9532 : * \code
9533 : * if(xpath->hasVariable(name))
9534 : * {
9535 : * value = xpath->getVariable(name);
9536 : * }
9537 : * else
9538 : * {
9539 : * value = "*"; // some default value
9540 : * }
9541 : * \endcode
9542 : *
9543 : * \param[in] name The name of the variable to check.
9544 : *
9545 : * \return true if the variable is defined, false otherwise.
9546 : */
9547 0 : bool QDomXPath::hasVariable(const QString& name)
9548 : {
9549 0 : return f_variables.contains(name);
9550 : }
9551 :
9552 :
9553 : /** \brief Retrieve the variable.
9554 : *
9555 : * This function is used to retrieve the value of a variable bound with
9556 : * the XPath.
9557 : *
9558 : * It is used internally with the $\<QName\> syntax. Note that if a function
9559 : * is called, then that function's variables are checked first and will
9560 : * shadow the main variables.
9561 : *
9562 : * \exception QDomXPathException_UndefinedVariable
9563 : * If the variable does not exist then the function raises this exception.
9564 : * To avoid the exception, use the hasVariable() predicate first.
9565 : *
9566 : * \param[in] name The name of the variable to retrieve.
9567 : *
9568 : * \return A copy of the variable contents as a string.
9569 : */
9570 0 : QString QDomXPath::getVariable(const QString& name)
9571 : {
9572 0 : if(!f_variables.contains(name))
9573 : {
9574 0 : throw QDomXPathException_UndefinedVariable(QString("variable \"%1\" is not defined").arg(name));
9575 : }
9576 0 : return f_variables[name];
9577 : }
9578 :
9579 :
9580 : /** \brief Set the program.
9581 : *
9582 : * When you store a previously compiled program somewhere (you can retrieve
9583 : * a compiled program with the getProgram() function), you can later reload
9584 : * it in your QDomXPath object with the setProgram() function.
9585 : *
9586 : * This is quite useful to compile many XPaths, save them in a file or
9587 : * your Qt resources, and later load them and pass them to this function
9588 : * for instant processing.
9589 : *
9590 : * Note that small XPaths may get compiled faster than the load from a file.
9591 : * It will be up to you to test what seems to be the fastest. Very large
9592 : * and complex XPaths are likely to benefit from a pre-compilation.
9593 : *
9594 : * \param[in] program The program to save in this QDomXPath object.
9595 : * \param[in] show_commands Whether the commands are shown in stdout while executing the program.
9596 : */
9597 0 : void QDomXPath::setProgram(const QDomXPath::program_t& program, bool show_commands)
9598 : {
9599 0 : if(f_impl == NULL)
9600 : {
9601 : // the original XPath is not known (although it could be saved
9602 : // in the program and restored?)
9603 0 : f_impl = new QDomXPath::QDomXPathImpl(this, "");
9604 : }
9605 0 : f_xpath = f_impl->setProgram(program, show_commands);
9606 0 : }
9607 :
9608 :
9609 :
9610 : /** \brief Retrieve the compiled program.
9611 : *
9612 : * The program can be retrieved after calling the setXPath() function.
9613 : * The program must be considered to be an array of bytes once outside
9614 : * of the QDomXPath environment.
9615 : *
9616 : * For the Unix 'file' tool trying to determine the type of a file, it
9617 : * can checks the first few bytes as:
9618 : *
9619 : * \li Byte 0 -- 'X'
9620 : * \li Byte 1 -- 'P'
9621 : * \li Byte 2 -- 'T'
9622 : * \li Byte 3 -- 'H'
9623 : * \li Byte 4 -- The major version, 0x01 or more (unsigned)
9624 : * \li Byte 5 -- The minor version, 0x00 or more (unsigned)
9625 : * \li Byte 6 -- higher size
9626 : * \li Byte 7 -- lower size
9627 : *
9628 : * The first 8 bytes are then followed by the original XPath so one can
9629 : * retrieve it, just in case. Bytes 6 and 7 represent the size of that
9630 : * XPath. If the XPath is more than 64Kb then only the first 65535 bytes
9631 : * are saved in the file.
9632 : *
9633 : * Note that it is possible to set byte 6 and 7 to zero and not save
9634 : * the XPath (this can be useful to save space.)
9635 : *
9636 : * \return A constant reference to the program.
9637 : */
9638 0 : const QDomXPath::program_t& QDomXPath::getProgram() const
9639 : {
9640 0 : if(f_impl)
9641 : {
9642 0 : return f_impl->getProgram();
9643 : }
9644 0 : throw QDomXPathException_InternalError("error: no program to retrieve");
9645 6 : }
9646 :
9647 :
9648 :
9649 : // vim: ts=4 sw=4 et
|