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>
22 #include "xsltInternals.h"
29 xmlChar *xmlSplitQName2(const xmlChar *name, xmlChar **prefix);
32 * There is no XSLT specific error reporting module yet
34 #define xsltGenericError xmlGenericError
35 #define xsltGenericErrorContext xmlGenericErrorContext
56 typedef union _xsltStepOp xsltStepOp;
57 typedef xsltStepOp *xsltStepOpPtr;
63 typedef struct _xsltCompMatch xsltCompMatch;
64 typedef xsltCompMatch *xsltCompMatchPtr;
65 struct _xsltCompMatch {
66 struct _xsltCompMatch *next; /* siblings in the name hash */
67 int priority; /* the priority */
69 /* TODO fix the statically allocated size */
72 xsltStepOp steps[20]; /* ops for computation */
76 /************************************************************************
80 ************************************************************************/
85 * Create a new XSLT CompMatch
87 * Returns the newly allocated xsltCompMatchPtr or NULL in case of error
90 xsltNewCompMatch(void) {
93 cur = (xsltCompMatchPtr) xmlMalloc(sizeof(xsltCompMatch));
95 xsltGenericError(xsltGenericErrorContext,
96 "xsltNewCompMatch : malloc failed\n");
99 memset(cur, 0, sizeof(xsltCompMatch));
106 * @comp: an XSLT comp
108 * Free up the memory allocated by @comp
111 xsltFreeCompMatch(xsltCompMatchPtr comp) {
114 memset(comp, -1, sizeof(xsltCompMatch));
119 * xsltFreeCompMatchList:
120 * @comp: an XSLT comp list
122 * Free up the memory allocated by all the elements of @comp
125 xsltFreeCompMatchList(xsltCompMatchPtr comp) {
126 xsltCompMatchPtr cur;
128 while (comp != NULL) {
131 xsltFreeCompMatch(cur);
136 * xsltCompMatchAddOp:
137 * @comp: the compiled match expression
140 * Add an step to an XSLT Compiled Match
142 * Returns -1 in case of failure, 0 otherwise.
145 xsltCompMatchAddOp(xsltCompMatchPtr comp, xsltOp op) {
146 if (comp->nbStep >= 20) {
147 xsltGenericError(xsltGenericErrorContext,
148 "xsltCompMatchAddOp: overflow\n");
151 comp->steps[comp->nbStep++].op = op;
156 * xsltCompMatchAddValue:
157 * @comp: the compiled match expression
160 * Add an step to an XSLT Compiled Match
162 * Returns -1 in case of failure, 0 otherwise.
165 xsltCompMatchAddValue(xsltCompMatchPtr comp, xmlChar *val) {
166 if (comp->nbStep >= 20) {
167 xsltGenericError(xsltGenericErrorContext,
168 "xsltCompMatchAddOp: overflow\n");
171 comp->steps[comp->nbStep++].value = val;
176 * xsltReverseCompMatch:
177 * @comp: the compiled match expression
179 * reverse all the stack of expressions
182 xsltReverseCompMatch(xsltCompMatchPtr comp) {
184 int j = comp->nbStep - 1;
187 register xmlChar *tmp;
188 tmp = comp->steps[i].value;
189 comp->steps[i].value = comp->steps[j].value;
190 comp->steps[j].value = tmp;
194 comp->steps[comp->nbStep].op = XSLT_OP_END;
197 /************************************************************************
199 * Dedicated parser for templates *
201 ************************************************************************/
203 #define IS_BLANK(c) (((c) == 0x20) || ((c) == 0x09) || ((c) == 0xA) || \
206 #define SKIP_BLANKS while (IS_BLANK(*cur)) cur++;
209 #define NXT (*(cur + 1))
212 #define PUSH(comp, step) \
213 if (xsltCompMatchAddOp((comp), (xsltOp) step)) goto error;
215 #define PUSHSTR(comp, step) \
216 if (xsltCompMatchAddValue((comp), (xmlChar *) step)) goto error;
219 * Compile the XSLT LocationPathPattern
220 * [2] LocationPathPattern ::= '/' RelativePathPattern?
221 * | IdKeyPattern (('/' | '//') RelativePathPattern)?
222 * | '//'? RelativePathPattern
223 * [3] IdKeyPattern ::= 'id' '(' Literal ')'
224 * | 'key' '(' Literal ',' Literal ')'
225 * [4] RelativePathPattern ::= StepPattern
226 * | RelativePathPattern '/' StepPattern
227 * | RelativePathPattern '//' StepPattern
228 * [5] StepPattern ::= ChildOrAttributeAxisSpecifier NodeTest Predicate*
229 * [6] ChildOrAttributeAxisSpecifier ::= AbbreviatedAxisSpecifier
230 * | ('child' | 'attribute') '::'
234 * xsltCompilePattern:
235 * @pattern an XSLT pattern
237 * Compile the XSLT pattern and generates a precompiled form suitable
240 * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern
241 * Returns the generated xsltCompMatchPtr or NULL in case of failure
245 xsltCompilePattern(const xmlChar *pattern) {
246 xsltCompMatchPtr ret;
249 if (pattern == NULL) {
250 xsltGenericError(xsltGenericErrorContext,
251 "xsltCompilePattern : NULL pattern\n");
256 xsltGenericError(xsltGenericErrorContext,
257 "xsltCompilePattern : parsing '%s'\n", pattern);
263 xsltGenericError(xsltGenericErrorContext,
264 "xsltCompilePattern : NULL pattern\n");
267 ret = xsltNewCompMatch();
271 if ((CUR == '/') && (NXT == '/')) {
272 } else if (CUR == '/') {
273 PUSH(ret, XSLT_OP_ROOT);
277 * Reverse for faster interpretation.
279 xsltReverseCompMatch(ret);
284 xsltFreeCompMatch(ret);
290 /************************************************************************
292 * Module interfaces *
294 ************************************************************************/
298 * @style: an XSLT stylesheet
299 * @cur: an XSLT template
301 * Register the XSLT pattern associated to @cur
303 * Returns -1 in case of error, 0 otherwise
306 xsltAddTemplate(xsltStylesheetPtr style, xsltTemplatePtr cur) {
307 xsltCompMatchPtr pat, list;
311 * get a compiled form of the pattern
313 /* TODO : handle | in patterns as multple pat !!! */
314 pat = xsltCompilePattern(cur->match);
317 if (cur->priority != XSLT_PAT_NO_PRIORITY)
318 pat->priority = cur->priority;
321 * insert it in the hash table list corresponding to its lookup name
323 switch (pat->steps[0].op) {
328 case XSLT_OP_ANCESTOR:
332 name = pat->steps[1].value;
335 name = (const xmlChar *) "/";
338 name = (const xmlChar *) "*";
341 case XSLT_OP_PREDICATE:
342 xsltGenericError(xsltGenericErrorContext,
343 "xsltAddTemplate: invalid compiled pattern\n");
344 xsltFreeCompMatch(pat);
347 if (style->templatesHash == NULL) {
348 style->templatesHash = xmlHashCreate(0);
349 if (style->templatesHash == NULL) {
350 xsltFreeCompMatch(pat);
354 xsltGenericError(xsltGenericErrorContext,
355 "xsltAddTemplate: created template hash\n");
357 xmlHashAddEntry(style->templatesHash, name, pat);
359 xsltGenericError(xsltGenericErrorContext,
360 "xsltAddTemplate: added new hash %s\n", name);
363 list = (xsltCompMatchPtr) xmlHashLookup(style->templatesHash, name);
365 xmlHashAddEntry(style->templatesHash, name, pat);
367 xsltGenericError(xsltGenericErrorContext,
368 "xsltAddTemplate: added new hash %s\n", name);
372 * Note '<=' since one must choose among the matching template
373 * rules that are left, the one that occurs last in the stylesheet
375 if (list->priority <= pat->priority) {
377 xmlHashAddEntry(style->templatesHash, name, pat);
379 xsltGenericError(xsltGenericErrorContext,
380 "xsltAddTemplate: added head hash for %s\n", name);
383 while (list->next != NULL) {
384 if (list->next->priority < pat->priority)
387 pat->next = list->next;
397 * @style: an XSLT stylesheet
400 * Finds the template applying to this node
402 * Returns the xsltTemplatePtr or NULL if not found
405 xsltGetTemplate(xsltStylesheetPtr style, xmlNodePtr node) {