diff options
-rw-r--r-- | ChangeLog | 7 | ||||
-rw-r--r-- | TODO | 25 | ||||
-rw-r--r-- | libxslt/pattern.c | 352 | ||||
-rw-r--r-- | libxslt/transform.c | 21 |
4 files changed, 386 insertions, 19 deletions
@@ -1,3 +1,10 @@ +Tue Jan 16 17:17:17 CET 2001 Daniel Veillard <Daniel.Veillard@imag.fr> + + * TODO: started filling it :-( + * libxslt/pattern.c: should now at least compile the full + set of patterns authorized. Default priorities added. + * libxslt/transform.c: a bit more work and cleanup. + Mon Jan 15 15:34:17 CET 2001 Daniel Veillard <Daniel.Veillard@imag.fr> * TODO: started adding in there :-( @@ -1,5 +1,30 @@ +Design: + - should transforms for a given stylesheet be thread clean, + or can a stylesheet be enriched with document specific + informations and cleaned up later ? + +Import: + -> parse them + -> provide functions to circulate in the import tree of stylesheets + +Pattern tester: + -> try to optimize for ID scan and tests. + +Pattern scanner: + -> add error checks on all returns + -> handle unions + -> compute priority + +Separate util module: + -> macros, config, verbosity ? + Support for disable-output-escaping="yes": -> looks problematic, libxml has no support for anything like this, and unless adding a new node type :-( or tweaking text node and output routines this is gonna be messy ... must be handled at libxml level. + +Error handling: + -> check the version stuff, design a separate module for error interfacing + and default handling, parsing vs. runtime, fatal / compat / warning, + and lack of optionnal features. diff --git a/libxslt/pattern.c b/libxslt/pattern.c index e1510d14..6d439609 100644 --- a/libxslt/pattern.c +++ b/libxslt/pattern.c @@ -56,6 +56,10 @@ typedef enum { XSLT_OP_KEY, XSLT_OP_NS, XSLT_OP_ALL, + XSLT_OP_PI, + XSLT_OP_COMMENT, + XSLT_OP_TEXT, + XSLT_OP_NODE, XSLT_OP_PREDICATE } xsltOp; @@ -72,10 +76,10 @@ typedef struct _xsltCompMatch xsltCompMatch; typedef xsltCompMatch *xsltCompMatchPtr; struct _xsltCompMatch { struct _xsltCompMatch *next; /* siblings in the name hash */ - int priority; /* the priority */ + float priority; /* the priority */ xsltTemplatePtr template; /* the associated template */ - /* TODO fix the statically allocated size */ + /* TODO fix the statically allocated size steps[] */ int nbStep; int maxStep; xsltStepOp steps[20]; /* ops for computation */ @@ -294,7 +298,7 @@ xsltTestCompMatch(xsltCompMatchPtr comp, xmlNodePtr node) { /* TODO: Handle namespace ... */ continue; case XSLT_OP_CHILD: - TODO /* Hummm !!! */ + TODO /* Handle OP_CHILD */ return(0); case XSLT_OP_ATTR: if (node->type != XML_ATTRIBUTE_NODE) @@ -351,10 +355,22 @@ xsltTestCompMatch(xsltCompMatchPtr comp, xmlNodePtr node) { TODO /* Handle Namespace */ break; case XSLT_OP_ALL: - TODO /* Handle Namespace */ + TODO /* Handle * */ break; case XSLT_OP_PREDICATE: - TODO /* Handle Namespace */ + TODO /* Handle Predicate */ + break; + case XSLT_OP_PI: + TODO /* Handle PI() */ + break; + case XSLT_OP_COMMENT: + TODO /* Handle Comments() */ + break; + case XSLT_OP_TEXT: + TODO /* Handle Text() */ + break; + case XSLT_OP_NODE: + TODO /* Handle Node() */ break; } } @@ -382,6 +398,66 @@ xsltTestCompMatch(xsltCompMatchPtr comp, xmlNodePtr node) { #define PUSH(op, val, val2) \ if (xsltCompMatchAdd(ctxt->comp, (op), (val), (val2))) goto error; +#define XSLT_ERROR(X) \ + { xsltError(ctxt, __FILE__, __LINE__, X); \ + ctxt->error = (X); return; } + +#define XSLT_ERROR0(X) \ + { xsltError(ctxt, __FILE__, __LINE__, X); \ + ctxt->error = (X); return(0); } + +/** + * xsltScanLiteral: + * @ctxt: the XPath Parser context + * + * Parse an XPath Litteral: + * + * [29] Literal ::= '"' [^"]* '"' + * | "'" [^']* "'" + * + * Returns the Literal parsed or NULL + */ + +xmlChar * +xsltScanLiteral(xsltParserContextPtr ctxt) { + const xmlChar *q; + xmlChar *ret = NULL; + + SKIP_BLANKS; + if (CUR == '"') { + NEXT; + q = CUR_PTR; + while ((IS_CHAR(CUR)) && (CUR != '"')) + NEXT; + if (!IS_CHAR(CUR)) { + /* XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR); */ + ctxt->error = 1; + return(NULL); + } else { + ret = xmlStrndup(q, CUR_PTR - q); + NEXT; + } + } else if (CUR == '\'') { + NEXT; + q = CUR_PTR; + while ((IS_CHAR(CUR)) && (CUR != '\'')) + NEXT; + if (!IS_CHAR(CUR)) { + /* XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR); */ + ctxt->error = 1; + return(NULL); + } else { + ret = xmlStrndup(q, CUR_PTR - q); + NEXT; + } + } else { + /* XP_ERROR(XPATH_START_LITERAL_ERROR); */ + ctxt->error = 1; + return(NULL); + } + return(ret); +} + /** * xsltScanName: * @ctxt: the XPath Parser context @@ -433,10 +509,141 @@ xsltScanName(xsltParserContextPtr ctxt) { return(xmlStrndup(buf, len)); } /* - * Compile the XSLT LocationPathPattern + * xsltCompileIdKeyPattern: + * @comp: the compilation context + * @name: a preparsed name + * @aid: whether id/key are allowed there + * + * Compile the XSLT LocationIdKeyPattern * [3] IdKeyPattern ::= 'id' '(' Literal ')' * | 'key' '(' Literal ',' Literal ')' + * + * also handle NodeType and PI from: + * + * [7] NodeTest ::= NameTest + * | NodeType '(' ')' + * | 'processing-instruction' '(' Literal ')' */ +void +xsltCompileIdKeyPattern(xsltParserContextPtr ctxt, xmlChar *name, int aid) { + xmlChar *lit = NULL; + xmlChar *lit2 = NULL; + + if (CUR != '(') { + xsltGenericError(xsltGenericErrorContext, + "xsltCompileIdKeyPattern : ( expected\n"); + ctxt->error = 1; + return; + } + if ((aid) && (xmlStrEqual(name, (const xmlChar *)"id"))) { + NEXT; + SKIP_BLANKS; + lit = xsltScanLiteral(ctxt); + if (ctxt->error) + return; + SKIP_BLANKS; + if (CUR != ')') { + xsltGenericError(xsltGenericErrorContext, + "xsltCompileIdKeyPattern : ) expected\n"); + ctxt->error = 1; + return; + } + NEXT; + PUSH(XSLT_OP_ID, lit, NULL); + } else if ((aid) && (xmlStrEqual(name, (const xmlChar *)"key"))) { + NEXT; + SKIP_BLANKS; + lit = xsltScanLiteral(ctxt); + if (ctxt->error) + return; + SKIP_BLANKS; + if (CUR != ',') { + xsltGenericError(xsltGenericErrorContext, + "xsltCompileIdKeyPattern : , expected\n"); + ctxt->error = 1; + return; + } + NEXT; + SKIP_BLANKS; + lit2 = xsltScanLiteral(ctxt); + if (ctxt->error) + return; + SKIP_BLANKS; + if (CUR != ')') { + xsltGenericError(xsltGenericErrorContext, + "xsltCompileIdKeyPattern : ) expected\n"); + ctxt->error = 1; + return; + } + NEXT; + PUSH(XSLT_OP_KEY, lit, NULL); + } else if (xmlStrEqual(name, (const xmlChar *)"processing-instruction")) { + NEXT; + SKIP_BLANKS; + if (CUR != ')') { + lit = xsltScanLiteral(ctxt); + if (ctxt->error) + return; + SKIP_BLANKS; + if (CUR != ')') { + xsltGenericError(xsltGenericErrorContext, + "xsltCompileIdKeyPattern : ) expected\n"); + ctxt->error = 1; + return; + } + } + NEXT; + PUSH(XSLT_OP_PI, lit, NULL); + } else if (xmlStrEqual(name, (const xmlChar *)"text")) { + NEXT; + SKIP_BLANKS; + if (CUR != ')') { + xsltGenericError(xsltGenericErrorContext, + "xsltCompileIdKeyPattern : ) expected\n"); + ctxt->error = 1; + return; + } + NEXT; + PUSH(XSLT_OP_TEXT, NULL, NULL); + } else if (xmlStrEqual(name, (const xmlChar *)"comment")) { + NEXT; + SKIP_BLANKS; + if (CUR != ')') { + xsltGenericError(xsltGenericErrorContext, + "xsltCompileIdKeyPattern : ) expected\n"); + ctxt->error = 1; + return; + } + NEXT; + PUSH(XSLT_OP_COMMENT, NULL, NULL); + } else if (xmlStrEqual(name, (const xmlChar *)"comment")) { + NEXT; + SKIP_BLANKS; + if (CUR != ')') { + xsltGenericError(xsltGenericErrorContext, + "xsltCompileIdKeyPattern : ) expected\n"); + ctxt->error = 1; + return; + } + NEXT; + PUSH(XSLT_OP_NODE, NULL, NULL); + } else if (aid) { + xsltGenericError(xsltGenericErrorContext, + "xsltCompileIdKeyPattern : expecting 'key' or 'id' or node type\n"); + ctxt->error = 1; + return; + } else { + xsltGenericError(xsltGenericErrorContext, + "xsltCompileIdKeyPattern : node type\n"); + ctxt->error = 1; + return; + } +error: + if (lit != NULL) + xmlFree(lit); + if (lit2 != NULL) + xmlFree(lit2); +} /** * xsltCompileStepPattern: @@ -461,6 +668,8 @@ xsltScanName(xsltParserContextPtr ctxt) { void xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token) { + xmlChar *name = NULL; + SKIP_BLANKS; if ((token == NULL) && (CUR == '@')) { token = xsltScanName(ctxt); @@ -468,8 +677,10 @@ xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token) { xsltGenericError(xsltGenericErrorContext, "xsltCompilePattern : Name expected\n"); ctxt->error = 1; - return; + goto error; } + PUSH(XSLT_OP_ATTR, token, NULL); + return; } if (token == NULL) token = xsltScanName(ctxt); @@ -477,26 +688,82 @@ xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token) { xsltGenericError(xsltGenericErrorContext, "xsltCompilePattern : Name expected\n"); ctxt->error = 1; - return; + goto error; } SKIP_BLANKS; if (CUR == '(') { - TODO; - /* if (xmlStrEqual(token, "processing-instruction")) */ + xsltCompileIdKeyPattern(ctxt, token, 0); + if (ctxt->error) + goto error; } else if (CUR == ':') { - TODO; + NEXT; + if (NXT(1) != ':') { + xsltGenericError(xsltGenericErrorContext, + "xsltCompilePattern : sequence '::' expected\n"); + ctxt->error = 1; + goto error; + } + NEXT; + if (xmlStrEqual(token, (const xmlChar *) "child")) { + /* TODO: handle namespace */ + name = xsltScanName(ctxt); + if (name == NULL) { + xsltGenericError(xsltGenericErrorContext, + "xsltCompilePattern : QName expected\n"); + ctxt->error = 1; + goto error; + } + PUSH(XSLT_OP_CHILD, name, NULL); + } else if (xmlStrEqual(token, (const xmlChar *) "attribute")) { + /* TODO: handle namespace */ + name = xsltScanName(ctxt); + if (name == NULL) { + xsltGenericError(xsltGenericErrorContext, + "xsltCompilePattern : QName expected\n"); + ctxt->error = 1; + goto error; + } + PUSH(XSLT_OP_ATTR, name, NULL); + } else { + xsltGenericError(xsltGenericErrorContext, + "xsltCompilePattern : 'child' or 'attribute' expected\n"); + ctxt->error = 1; + goto error; + } + xmlFree(token); } else if (CUR == '*') { - TODO; + NEXT; + PUSH(XSLT_OP_ALL, token, NULL); } else { + /* TODO: handle namespace */ PUSH(XSLT_OP_ELEM, token, NULL); } SKIP_BLANKS; while (CUR == '[') { - TODO; + const xmlChar *q; + xmlChar *ret = NULL; + + NEXT; + q = CUR_PTR; + /* TODO: avoid breaking in strings ... */ + while ((IS_CHAR(CUR)) && (CUR != ']')) + NEXT; + if (!IS_CHAR(CUR)) { + xsltGenericError(xsltGenericErrorContext, + "xsltCompilePattern : ']' expected\n"); + ctxt->error = 1; + goto error; + } + NEXT; + ret = xmlStrndup(q, CUR_PTR - q); + PUSH(XSLT_OP_PREDICATE, ret, NULL); } return; error: - ctxt->error = 1; + if (token != NULL) + xmlFree(token); + if (name != NULL) + xmlFree(name); } /** @@ -586,7 +853,20 @@ xsltCompileLocationPathPattern(xsltParserContextPtr ctxt) { } SKIP_BLANKS; if (CUR == '(') { - TODO + xsltCompileIdKeyPattern(ctxt, name, 1); + if ((CUR == '/') && (NXT(1) == '/')) { + PUSH(XSLT_OP_ANCESTOR, NULL, NULL); + NEXT; + NEXT; + SKIP_BLANKS; + xsltCompileRelativePathPattern(ctxt, NULL); + } else if (CUR == '/') { + PUSH(XSLT_OP_PARENT, NULL, NULL); + NEXT; + SKIP_BLANKS; + xsltCompileRelativePathPattern(ctxt, NULL); + } + return; } xsltCompileRelativePathPattern(ctxt, name); } @@ -653,6 +933,32 @@ xsltCompilePattern(const xmlChar *pattern) { */ xsltReverseCompMatch(ret); + /* + * Set-up the priority + */ + if (((ret->steps[0].op == XSLT_OP_ELEM) || + (ret->steps[0].op == XSLT_OP_ATTR)) && + (ret->steps[0].value != NULL) && + (ret->steps[1].op == XSLT_OP_END)) { + ret->priority = 0; + } else if ((ret->steps[0].op == XSLT_OP_PI) && + (ret->steps[0].value != NULL) && + (ret->steps[1].op == XSLT_OP_END)) { + ret->priority = 0; + } else if ((ret->steps[0].op == XSLT_OP_NS) && + (ret->steps[0].value != NULL) && + (ret->steps[1].op == XSLT_OP_END)) { + ret->priority = -0.25; + } else if (((ret->steps[0].op == XSLT_OP_PI) || + (ret->steps[0].op == XSLT_OP_TEXT) || + (ret->steps[0].op == XSLT_OP_NODE) || + (ret->steps[0].op == XSLT_OP_COMMENT)) && + (ret->steps[1].op == XSLT_OP_END)) { + ret->priority = -0.5; + } else { + ret->priority = 0.5; + } + xsltFreeParserContext(ctxt); return(ret); @@ -721,6 +1027,22 @@ xsltAddTemplate(xsltStylesheetPtr style, xsltTemplatePtr cur) { "xsltAddTemplate: invalid compiled pattern\n"); xsltFreeCompMatch(pat); return(-1); + /* + * TODO: some flags at the top level about type based patterns + * would be faster than inclusion in the hash table. + */ + case XSLT_OP_PI: + name = (const xmlChar *) "processing-instruction()"; + break; + case XSLT_OP_COMMENT: + name = (const xmlChar *) "comment()"; + break; + case XSLT_OP_TEXT: + name = (const xmlChar *) "text()"; + break; + case XSLT_OP_NODE: + name = (const xmlChar *) "node()"; + break; } if (name == NULL) { xsltGenericError(xsltGenericErrorContext, diff --git a/libxslt/transform.c b/libxslt/transform.c index 8af973e1..8df46c50 100644 --- a/libxslt/transform.c +++ b/libxslt/transform.c @@ -35,6 +35,7 @@ * To cleanup */ xmlChar *xmlSplitQName2(const xmlChar *name, xmlChar **prefix); +void xmlXPathBooleanFunction(xmlXPathParserContextPtr ctxt, int nargs); /* * There is no XSLT specific error reporting module yet @@ -206,16 +207,28 @@ xsltAttribute(xsltTransformContextPtr ctxt, xmlNodePtr node, value = xmlNodeListGetString(inst->doc, inst->children, 1); if (value == NULL) { - if (ns) + if (ns) { +#if LIBXML_VERSION > 202111 attr = xmlSetNsProp(ctxt->insert, ncname, ns->href, (const xmlChar *)""); - else +#else + xsltGenericError(xsltGenericErrorContext, + "xsl:attribute: recompile against newer libxml version\n"); + attr = xmlSetProp(ctxt->insert, ncname, (const xmlChar *)""); +#endif + } else attr = xmlSetProp(ctxt->insert, ncname, (const xmlChar *)""); } else { /* TODO: attribute value template */ - if (ns) + if (ns) { +#if LIBXML_VERSION > 202111 attr = xmlSetNsProp(ctxt->insert, ncname, ns->href, value); - else +#else + xsltGenericError(xsltGenericErrorContext, + "xsl:attribute: recompile against newer libxml version\n"); + attr = xmlSetProp(ctxt->insert, ncname, value); +#endif + } else attr = xmlSetProp(ctxt->insert, ncname, value); } |