2 * pattern.c: Implemetation of the template match compilation and lookup
5 * http://www.w3.org/TR/1999/REC-xslt-19991116
7 * See Copyright for the status of this software.
9 * Daniel.Veillard@imag.fr
12 #include "xsltconfig.h"
16 #include <libxml/xmlmemory.h>
17 #include <libxml/tree.h>
18 #include <libxml/valid.h>
19 #include <libxml/hash.h>
20 #include <libxml/xmlerror.h>
21 #include <libxml/parserInternals.h>
23 #include "xsltInternals.h"
28 xsltGenericError(xsltGenericErrorContext, \
29 "Unimplemented block at %s:%d\n", \
35 xmlChar *xmlSplitQName2(const xmlChar *name, xmlChar **prefix);
38 * There is no XSLT specific error reporting module yet
40 #define xsltGenericError xmlGenericError
41 #define xsltGenericErrorContext xmlGenericErrorContext
62 typedef union _xsltStepOp xsltStepOp;
63 typedef xsltStepOp *xsltStepOpPtr;
69 typedef struct _xsltCompMatch xsltCompMatch;
70 typedef xsltCompMatch *xsltCompMatchPtr;
71 struct _xsltCompMatch {
72 struct _xsltCompMatch *next; /* siblings in the name hash */
73 int priority; /* the priority */
75 /* TODO fix the statically allocated size */
78 xsltStepOp steps[20]; /* ops for computation */
81 typedef struct _xsltParserContext xsltParserContext;
82 typedef xsltParserContext *xsltParserContextPtr;
83 struct _xsltParserContext {
84 const xmlChar *cur; /* the current char being parsed */
85 const xmlChar *base; /* the full expression */
86 int error; /* error code */
87 xsltCompMatchPtr comp; /* the result */
90 /************************************************************************
94 ************************************************************************/
99 * Create a new XSLT CompMatch
101 * Returns the newly allocated xsltCompMatchPtr or NULL in case of error
104 xsltNewCompMatch(void) {
105 xsltCompMatchPtr cur;
107 cur = (xsltCompMatchPtr) xmlMalloc(sizeof(xsltCompMatch));
109 xsltGenericError(xsltGenericErrorContext,
110 "xsltNewCompMatch : malloc failed\n");
113 memset(cur, 0, sizeof(xsltCompMatch));
120 * @comp: an XSLT comp
122 * Free up the memory allocated by @comp
125 xsltFreeCompMatch(xsltCompMatchPtr comp) {
128 memset(comp, -1, sizeof(xsltCompMatch));
133 * xsltFreeCompMatchList:
134 * @comp: an XSLT comp list
136 * Free up the memory allocated by all the elements of @comp
139 xsltFreeCompMatchList(xsltCompMatchPtr comp) {
140 xsltCompMatchPtr cur;
142 while (comp != NULL) {
145 xsltFreeCompMatch(cur);
150 * xsltNewParserContext:
152 * Create a new XSLT ParserContext
154 * Returns the newly allocated xsltParserContextPtr or NULL in case of error
157 xsltNewParserContext(void) {
158 xsltParserContextPtr cur;
160 cur = (xsltParserContextPtr) xmlMalloc(sizeof(xsltParserContext));
162 xsltGenericError(xsltGenericErrorContext,
163 "xsltNewParserContext : malloc failed\n");
166 memset(cur, 0, sizeof(xsltParserContext));
171 * xsltFreeParserContext:
172 * @comp: an XSLT comp
174 * Free up the memory allocated by @comp
177 xsltFreeParserContext(xsltParserContextPtr comp) {
180 memset(comp, -1, sizeof(xsltParserContext));
185 * xsltCompMatchAddOp:
186 * @comp: the compiled match expression
189 * Add an step to an XSLT Compiled Match
191 * Returns -1 in case of failure, 0 otherwise.
194 xsltCompMatchAddOp(xsltCompMatchPtr comp, xsltOp op) {
195 if (comp->nbStep >= 20) {
196 xsltGenericError(xsltGenericErrorContext,
197 "xsltCompMatchAddOp: overflow\n");
200 comp->steps[comp->nbStep++].op = op;
205 * xsltCompMatchAddValue:
206 * @comp: the compiled match expression
209 * Add an step to an XSLT Compiled Match
211 * Returns -1 in case of failure, 0 otherwise.
214 xsltCompMatchAddValue(xsltCompMatchPtr comp, xmlChar *val) {
215 if (comp->nbStep >= 20) {
216 xsltGenericError(xsltGenericErrorContext,
217 "xsltCompMatchAddOp: overflow\n");
220 comp->steps[comp->nbStep++].value = val;
225 * xsltReverseCompMatch:
226 * @comp: the compiled match expression
228 * reverse all the stack of expressions
231 xsltReverseCompMatch(xsltCompMatchPtr comp) {
233 int j = comp->nbStep - 1;
236 register xmlChar *tmp;
237 tmp = comp->steps[i].value;
238 comp->steps[i].value = comp->steps[j].value;
239 comp->steps[j].value = tmp;
243 comp->steps[comp->nbStep].op = XSLT_OP_END;
246 /************************************************************************
248 * Dedicated parser for templates *
250 ************************************************************************/
252 #define CUR (*ctxt->cur)
253 #define SKIP(val) ctxt->cur += (val)
254 #define NXT(val) ctxt->cur[(val)]
255 #define CUR_PTR ctxt->cur
257 #define SKIP_BLANKS \
258 while (IS_BLANK(CUR)) NEXT
260 #define CURRENT (*ctxt->cur)
261 #define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
265 if (xsltCompMatchAddOp(ctxt->comp, (xsltOp) step)) goto error;
267 #define PUSHSTR(step) \
268 if (xsltCompMatchAddValue(ctxt->comp, (xmlChar *) step)) goto error;
272 * @ctxt: the XPath Parser context
274 * Trickery: parse an XML name but without consuming the input flow
275 * Needed to avoid insanity in the parser state.
277 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
278 * CombiningChar | Extender
280 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
282 * [6] Names ::= Name (S Name)*
284 * Returns the Name parsed or NULL
288 xsltScanName(xsltParserContextPtr ctxt) {
289 xmlChar buf[XML_MAX_NAMELEN];
293 if (!IS_LETTER(CUR) && (CUR != '_') &&
298 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
299 (NXT(len) == '.') || (NXT(len) == '-') ||
300 (NXT(len) == '_') || (NXT(len) == ':') ||
301 (IS_COMBINING(NXT(len))) ||
302 (IS_EXTENDER(NXT(len)))) {
305 if (len >= XML_MAX_NAMELEN) {
306 xmlGenericError(xmlGenericErrorContext,
307 "xmlScanName: reached XML_MAX_NAMELEN limit\n");
308 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
309 (NXT(len) == '.') || (NXT(len) == '-') ||
310 (NXT(len) == '_') || (NXT(len) == ':') ||
311 (IS_COMBINING(NXT(len))) ||
312 (IS_EXTENDER(NXT(len))))
318 return(xmlStrndup(buf, len));
321 * Compile the XSLT LocationPathPattern
322 * [3] IdKeyPattern ::= 'id' '(' Literal ')'
323 * | 'key' '(' Literal ',' Literal ')'
327 * xsltCompileStepPattern:
328 * @comp: the compilation context
329 * @token: a posible precompiled name
331 * Compile the XSLT StepPattern and generates a precompiled
332 * form suitable for fast matching.
334 * [5] StepPattern ::= ChildOrAttributeAxisSpecifier NodeTest Predicate*
335 * [6] ChildOrAttributeAxisSpecifier ::= AbbreviatedAxisSpecifier
336 * | ('child' | 'attribute') '::'
338 * [7] NodeTest ::= NameTest
340 * | 'processing-instruction' '(' Literal ')'
341 * [8] Predicate ::= '[' PredicateExpr ']'
342 * [9] PredicateExpr ::= Expr
343 * [13] AbbreviatedAxisSpecifier ::= '@'?
344 * [37] NameTest ::= '*' | NCName ':' '*' | QName
348 xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token) {
350 if ((token == NULL) && (CUR == '@')) {
351 token = xsltScanName(ctxt);
353 xsltGenericError(xsltGenericErrorContext,
354 "xsltCompilePattern : Name expected\n");
360 token = xsltScanName(ctxt);
362 xsltGenericError(xsltGenericErrorContext,
363 "xsltCompilePattern : Name expected\n");
370 /* if (xmlStrEqual(token, "processing-instruction")) */
371 } else if (CUR == ':') {
373 } else if (CUR == '*') {
389 * xsltCompileRelativePathPattern:
390 * @comp: the compilation context
391 * @token: a posible precompiled name
393 * Compile the XSLT RelativePathPattern and generates a precompiled
394 * form suitable for fast matching.
396 * [4] RelativePathPattern ::= StepPattern
397 * | RelativePathPattern '/' StepPattern
398 * | RelativePathPattern '//' StepPattern
401 xsltCompileRelativePathPattern(xsltParserContextPtr ctxt, xmlChar *token) {
402 xsltCompileStepPattern(ctxt, token);
406 while ((CUR != 0) && (CUR != '|')) {
407 if ((CUR == '/') && (NXT(1) == '/')) {
408 PUSH(XSLT_OP_ANCESTOR);
412 xsltCompileStepPattern(ctxt, NULL);
413 } else if (CUR == '/') {
414 PUSH(XSLT_OP_PARENT);
417 if ((CUR != 0) || (CUR == '|')) {
418 xsltCompileRelativePathPattern(ctxt, NULL);
432 * xsltCompileLocationPathPattern:
433 * @comp: the compilation context
435 * Compile the XSLT LocationPathPattern and generates a precompiled
436 * form suitable for fast matching.
438 * [2] LocationPathPattern ::= '/' RelativePathPattern?
439 * | IdKeyPattern (('/' | '//') RelativePathPattern)?
440 * | '//'? RelativePathPattern
443 xsltCompileLocationPathPattern(xsltParserContextPtr ctxt) {
445 if ((CUR == '/') && (NXT(1) == '/')) {
447 * since we reverse the query
448 * a leading // can be safely ignored
452 xsltCompileRelativePathPattern(ctxt, NULL);
453 } else if (CUR == '/') {
455 * We need to find root as the parent
460 PUSH(XSLT_OP_PARENT);
461 if ((CUR != 0) || (CUR == '|')) {
462 xsltCompileRelativePathPattern(ctxt, NULL);
466 name = xsltScanName(ctxt);
468 xsltGenericError(xsltGenericErrorContext,
469 "xsltCompilePattern : Name expected\n");
477 xsltCompileRelativePathPattern(ctxt, name);
484 * xsltCompilePattern:
485 * @pattern an XSLT pattern
487 * Compile the XSLT pattern and generates a precompiled form suitable
489 * Note that the splitting as union of patterns is expected to be handled
492 * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern
494 * Returns the generated xsltCompMatchPtr or NULL in case of failure
498 xsltCompilePattern(const xmlChar *pattern) {
499 xsltParserContextPtr ctxt;
500 xsltCompMatchPtr ret;
503 if (pattern == NULL) {
504 xsltGenericError(xsltGenericErrorContext,
505 "xsltCompilePattern : NULL pattern\n");
510 xsltGenericError(xsltGenericErrorContext,
511 "xsltCompilePattern : parsing '%s'\n", pattern);
515 while (IS_BLANK(*cur)) cur++;
517 xsltGenericError(xsltGenericErrorContext,
518 "xsltCompilePattern : NULL pattern\n");
521 ctxt = xsltNewParserContext();
524 ret = xsltNewCompMatch();
526 xsltFreeParserContext(ctxt);
531 ctxt->base = pattern;
533 xsltCompileLocationPathPattern(ctxt);
538 * Reverse for faster interpretation.
540 xsltReverseCompMatch(ret);
542 xsltFreeParserContext(ctxt);
546 xsltFreeParserContext(ctxt);
547 xsltFreeCompMatch(ret);
553 /************************************************************************
555 * Module interfaces *
557 ************************************************************************/
561 * @style: an XSLT stylesheet
562 * @cur: an XSLT template
564 * Register the XSLT pattern associated to @cur
566 * Returns -1 in case of error, 0 otherwise
569 xsltAddTemplate(xsltStylesheetPtr style, xsltTemplatePtr cur) {
570 xsltCompMatchPtr pat, list;
574 * get a compiled form of the pattern
576 /* TODO : handle | in patterns as multple pat !!! */
577 pat = xsltCompilePattern(cur->match);
580 if (cur->priority != XSLT_PAT_NO_PRIORITY)
581 pat->priority = cur->priority;
584 * insert it in the hash table list corresponding to its lookup name
586 switch (pat->steps[0].op) {
591 case XSLT_OP_ANCESTOR:
595 name = pat->steps[1].value;
598 name = (const xmlChar *) "/";
601 name = (const xmlChar *) "*";
604 case XSLT_OP_PREDICATE:
605 xsltGenericError(xsltGenericErrorContext,
606 "xsltAddTemplate: invalid compiled pattern\n");
607 xsltFreeCompMatch(pat);
610 if (style->templatesHash == NULL) {
611 style->templatesHash = xmlHashCreate(0);
612 if (style->templatesHash == NULL) {
613 xsltFreeCompMatch(pat);
617 xsltGenericError(xsltGenericErrorContext,
618 "xsltAddTemplate: created template hash\n");
620 xmlHashAddEntry(style->templatesHash, name, pat);
622 xsltGenericError(xsltGenericErrorContext,
623 "xsltAddTemplate: added new hash %s\n", name);
626 list = (xsltCompMatchPtr) xmlHashLookup(style->templatesHash, name);
628 xmlHashAddEntry(style->templatesHash, name, pat);
630 xsltGenericError(xsltGenericErrorContext,
631 "xsltAddTemplate: added new hash %s\n", name);
635 * Note '<=' since one must choose among the matching template
636 * rules that are left, the one that occurs last in the stylesheet
638 if (list->priority <= pat->priority) {
640 xmlHashAddEntry(style->templatesHash, name, pat);
642 xsltGenericError(xsltGenericErrorContext,
643 "xsltAddTemplate: added head hash for %s\n", name);
646 while (list->next != NULL) {
647 if (list->next->priority < pat->priority)
650 pat->next = list->next;
660 * @style: an XSLT stylesheet
663 * Finds the template applying to this node
665 * Returns the xsltTemplatePtr or NULL if not found
668 xsltGetTemplate(xsltStylesheetPtr style, xmlNodePtr node) {