From a066d38b3fe615fc99dbe9eb3a0d0ad7f41135b6 Mon Sep 17 00:00:00 2001 From: teodor Date: Fri, 3 Oct 2008 22:57:17 +0000 Subject: [PATCH] Add recursive loop to support tree-like structures --- data/template.tmpl | 10 +- expected/tmpl | 79 +++++++++++++- template.c | 266 ++++++++++++++++++++++++++++++++++++--------- template.h | 35 +++++- tmpl_gram.y | 6 +- tmpl_scan.l | 1 + tmpltest.c | 51 ++++++++- 7 files changed, 384 insertions(+), 64 deletions(-) diff --git a/data/template.tmpl b/data/template.tmpl index 95e0060..7a2db76 100644 --- a/data/template.tmpl +++ b/data/template.tmpl @@ -58,9 +58,8 @@ str ? "yes" : -1 = <% str ? "yes" : -1 %> <@ENDIF@> <@ENDIF@> - <@ LOOP outerLoop @> - <% __COUNTER %>/<% __SIZE %>. odd:<% __ODD %> even:<% __EVEN %> <# <@if __FIRST @>FIRST<@endif@> <@if __LAST @>LAST<@endif@> id: <% ^ID %> <% DATA1 %>:<% DATA2 # "Data is absent" %>/<% DATA1 %> #> + <% __COUNTER %>/<% __SIZE %>. odd:<% __ODD %> even:<% __EVEN %> <& data/template_include.tmpl &> <@ LOOP innerLoop @> @@ -69,3 +68,10 @@ str ? "yes" : -1 = <% str ? "yes" : -1 %> <@ IF __LAST @><@ ENDIF @> <@ ENDLOOP @> <@ ENDLOOP @> + +<@ LOOP selfLoop@> + <@if __FIRST @><@endif@> +<@ ENDLOOP @> diff --git a/expected/tmpl b/expected/tmpl index 57095bd..21b8c27 100644 --- a/expected/tmpl +++ b/expected/tmpl @@ -51,14 +51,13 @@ str ? "yes" : -1 = yes - - 1/3. odd:true even:false + 1/3. odd:true even:false FIRST id: 17 ha1:Data is absent/ha1 - 2/3. odd:false even:true + 2/3. odd:false even:true id: 17 10:WOW/10 @@ -72,12 +71,14 @@ str ? "yes" : -1 = yes - 3/3. odd:true even:false + 3/3. odd:true even:false LAST id: 17 ha3:Data is absent/ha3 + + ================================================ id: 23 - simple idhex: 0x00000017 - HEX @@ -131,8 +132,7 @@ str ? "yes" : -1 = -1 - - 1/1. odd:true even:false + 1/1. odd:true even:false FIRST LAST id: 23 1234:FOO/1234 @@ -142,3 +142,70 @@ str ? "yes" : -1 = -1 + + + + diff --git a/template.c b/template.c index 45b366b..5651124 100644 --- a/template.c +++ b/template.c @@ -389,6 +389,7 @@ findIncludes(ParseState *state, TemplateNode *node) { case ConstNode: case VariableNode: case TextNode: + case NestNode: break; default: tlog(TL_CRIT|TL_EXIT, "unknown node type: %d", (*node)->type); @@ -488,13 +489,7 @@ findVariable( Template tmpl, TemplateNode loopParentNode, int flags, char *varNa * copy special flags to support new inform loopParentNode */ pdata->flags |= flags & TND__SPECIALMASK; - - if ( flags & ( TND___FIRST | TND___LAST | TND___ODD | TND___EVEN ) ) - pdata->type = valueBool; - else if ( flags & (TND___COUNTER | TND___SIZE) ) - pdata->type = valueInt; - else - pdata->type = valuePointer; + pdata->type = valuePointer; loopParentNode->nodeData.loop.listVarValues = GListPush( loopParentNode->nodeData.loop.listVarValues, pdata ); @@ -521,10 +516,15 @@ addLoop(Template tmpl, TemplateNode node, TemplateNode loopParentNode) { SFSAdd(&tmpl->variables, &in); - if ( loopParentNode ) + if ( loopParentNode ) { loopParentNode->nodeData.loop.childrenLoop = GListPush( loopParentNode->nodeData.loop.childrenLoop, node ); + if ( loopParentNode->nodeData.loop.selfNode ) + loopParentNode->nodeData.loop.selfNode->nodeData.nest.childrenLoopAfterSelf = + GListPush( loopParentNode->nodeData.loop.selfNode->nodeData.nest.childrenLoopAfterSelf, node ); + } + return 0; } @@ -567,6 +567,16 @@ compileTree( Template tmpl, TemplateNode node, TemplateNode loopParentNode ) { if ( addLoop( tmpl, node, loopParentNode ) ) return 1; break; + case NestNode: + node->nodeData.nest.loop = loopParentNode; + if ( loopParentNode ) { + if ( loopParentNode->nodeData.loop.selfNode ) + tlog(TL_WARN,"Loop '%s' has duplicated nested call", node->nodeData.loop.varName); + else + loopParentNode->nodeData.loop.selfNode = node; + } else + tlog(TL_WARN,"SELF tag without loop"); + break; case ConditionNode: if ( compileTree(tmpl, node->nodeData.condition.expressionNode, loopParentNode) ) return 1; @@ -709,6 +719,7 @@ freeNode( TemplateNode node ) { switch (node->type) { case LoopNode: freeNode( node->nodeData.loop.bodyNode ); + freeNode( node->nodeData.loop.selfNode ); GListFree( node->nodeData.loop.childrenLoop ); GListFree( node->nodeData.loop.listVarValues ); @@ -737,6 +748,9 @@ freeNode( TemplateNode node ) { freeNode( GLCELL_DATA(cell) ); GListFree( node->nodeData.expression.argsNode ); break; + case NestNode: + GListFree( node->nodeData.nest.childrenLoopAfterSelf ); + break; case VariableNode: case IncludeNode: case TextNode: @@ -758,19 +772,29 @@ freeTemplate( Template tmpl ) { static void newLoopInstance( Template tmpl, TemplateNode node ) { + LoopInstance upper = NULL; + + if ( node->nodeData.loop.currentInstance ) + upper = node->nodeData.loop.currentInstance->upperInstance; + + node->nodeData.loop.currentInstance = + mc0alloc(tmpl->templateContext, sizeof(LoopInstanceData) ); + node->nodeData.loop.currentInstance->upperInstance = upper; + node->nodeData.loop.listInstance = GListPush( node->nodeData.loop.listInstance, - mc0alloc(tmpl->templateContext, sizeof(LoopInstanceData) ) ); + node->nodeData.loop.currentInstance ); + + node->nodeData.loop.lastRow = NULL; } int -addTemplateRow( Template tmpl, char * key ) { +addTemplateRow( Template tmpl, char * key) { TemplateNode *pnode, node; char *lkey = strlower(mcstrdup(tmpl->templateContext, key)); GListCell *cell; - VariableValue varvals; int i=0, nvar; - LoopInstance instance; + LoopRow rowData; pnode = SFSFindData(&tmpl->variables, lkey, 0); mcfree(lkey); @@ -794,21 +818,90 @@ addTemplateRow( Template tmpl, char * key ) { GListForeach( cell, node->nodeData.loop.childrenLoop ) newLoopInstance( tmpl, GLCELL_DATA(cell) ); - varvals = mcalloc( tmpl->templateContext, sizeof(VariableValueData) * nvar ); + node->nodeData.loop.lastRow = rowData = mcalloc( tmpl->templateContext, LRDHDRSZ + sizeof(VariableValueData) * nvar ); + rowData->loop = node; + rowData->nestedInstance = NULL; + GListForeach( cell, node->nodeData.loop.listVarValues ) { VariableValue vv = GLCELL_DATA(cell); - vv->value.ptrValue = varvals + i; - varvals[i].flags = 0; + vv->value.ptrValue = rowData->varvals + i; + rowData->varvals[i].flags = 0; i++; } - instance = GLCELL_DATA(GLIST_TAIL(node->nodeData.loop.listInstance)); + node->nodeData.loop.currentInstance->nrow++; + node->nodeData.loop.currentInstance->rowValues = + GListPush( node->nodeData.loop.currentInstance->rowValues, rowData ); + + return TVAR_OK; +} + +int +addTemplateNestedLoop( Template tmpl, char * key) { + TemplateNode *pnode, node; + char *lkey = strlower(mcstrdup(tmpl->templateContext, key)); + GListCell *cell; + + pnode = SFSFindData(&tmpl->variables, lkey, 0); + mcfree(lkey); + + if ( pnode == NULL ) + return TVAR_NOTFOUND; + + node = *pnode; + + if ( node->type != LoopNode || node->nodeData.loop.selfNode == 0 ) + return TVAR_FORBIDDEN; + + if ( GLIST_LENGTH(node->nodeData.loop.listInstance) == 0 || node->nodeData.loop.lastRow == NULL ) + return TVAR_NOROW; + + GListForeach( cell, node->nodeData.loop.childrenLoop ) + ((TemplateNode)GLCELL_DATA(cell))->nodeData.loop.lastRow = NULL; + + node->nodeData.loop.lastRow->nestedInstance = + mc0alloc(tmpl->templateContext, sizeof(LoopInstanceData) ); + node->nodeData.loop.lastRow->nestedInstance->upperInstance = + node->nodeData.loop.currentInstance; + node->nodeData.loop.currentInstance = + node->nodeData.loop.lastRow->nestedInstance; + node->nodeData.loop.lastRow = NULL; + + return TVAR_OK; +} + +int +returnTemplateNestedLoop( Template tmpl, char * key) { + TemplateNode *pnode, node; + char *lkey = strlower(mcstrdup(tmpl->templateContext, key)); + GListCell *cell; + + pnode = SFSFindData(&tmpl->variables, lkey, 0); + mcfree(lkey); + + if ( pnode == NULL ) + return TVAR_NOTFOUND; - instance->nrow++; - instance->rowValues = GListPush( instance->rowValues, varvals ); + node = *pnode; + + if ( node->type != LoopNode || node->nodeData.loop.selfNode == 0 ) + return TVAR_FORBIDDEN; + + if ( GLIST_LENGTH(node->nodeData.loop.listInstance) == 0 ) + return TVAR_NOROW; + + if ( node->nodeData.loop.currentInstance == NULL ) + return TVAR_NOROW; + + GListForeach( cell, node->nodeData.loop.childrenLoop ) + ((TemplateNode)GLCELL_DATA(cell))->nodeData.loop.lastRow = NULL; + + node->nodeData.loop.currentInstance = node->nodeData.loop.currentInstance->upperInstance; + node->nodeData.loop.lastRow = NULL; return TVAR_OK; + } static VariableValueData storage; @@ -1004,6 +1097,7 @@ executeExpression( Template tmpl, TemplateNode node ) { case ConditionNode: case CollectionNode: case PrintNode: + case NestNode: tlog(TL_CRIT|TL_EXIT, "Unexpected node type: %d", node->type); break; default: @@ -1017,9 +1111,10 @@ executeExpression( Template tmpl, TemplateNode node ) { } static void -printNode( Template tmpl, TemplateNode node ) { - GListCell *cell; +printNode( Template tmpl, TemplateNode node, LoopInstance loopInstance ) { + GListCell *cell; VariableValue value; + int i; if (!node) return; @@ -1027,79 +1122,143 @@ printNode( Template tmpl, TemplateNode node ) { switch (node->type) { case LoopNode: { - GListCell *cell = GListShift( node->nodeData.loop.listInstance ); + GListCell *cell; LoopInstance instance; - int i; - if ( cell == NULL ) - return; + if ( loopInstance ) { + instance = loopInstance; + } else { + cell = GListShift( node->nodeData.loop.listInstance ); + if ( cell == NULL ) + return; - instance = GLCELL_DATA(cell); - GListFreeCell( node->nodeData.loop.listInstance, cell ); + instance = GLCELL_DATA(cell); + GListFreeCell( node->nodeData.loop.listInstance, cell ); + } for(i=0; inrow;i++) { - VariableValue varvals; + LoopRow rowData; int j = 0; + VariableValue realValue; cell = GListShift( instance->rowValues ); - varvals = GLCELL_DATA(cell); + rowData = GLCELL_DATA(cell); GListFreeCell( instance->rowValues, cell ); GListForeach( cell, node->nodeData.loop.listVarValues ) { value = (VariableValue)GLCELL_DATA(cell); + tassert( value->type == valuePointer ); + realValue = value->value.ptrValue = rowData->varvals+j; + if ( value->flags & TND___FIRST ) { + realValue->type = valueInt; if ( i==0 ) { - value->flags |= TND_DEFINED; - value->value.intValue = 1; + realValue->flags |= TND_DEFINED; + realValue->value.intValue = 1; } else { - value->flags &= ~TND_DEFINED; - value->value.intValue = 0; + realValue->flags &= ~TND_DEFINED; + realValue->value.intValue = 0; } } else if ( value->flags & TND___LAST ) { + realValue->type = valueInt; if ( i==instance->nrow - 1 ) { - value->flags |= TND_DEFINED; - value->value.intValue = 1; + realValue->flags |= TND_DEFINED; + realValue->value.intValue = 1; } else { - value->flags &= ~TND_DEFINED; - value->value.intValue = 0; + realValue->flags &= ~TND_DEFINED; + realValue->value.intValue = 0; } } else if ( value->flags & TND___COUNTER ) { - value->flags |= TND_DEFINED; - value->value.intValue = i+1; + realValue->type = valueInt; + realValue->flags |= TND_DEFINED; + realValue->value.intValue = i+1; } else if ( value->flags & TND___SIZE ) { - value->flags |= TND_DEFINED; - value->value.intValue = instance->nrow; + realValue->type = valueInt; + realValue->flags |= TND_DEFINED; + realValue->value.intValue = instance->nrow; } else if ( value->flags & TND___ODD ) { - value->flags |= TND_DEFINED; - value->value.boolValue = (i%2) ? 0 : 1 ; + realValue->type = valueBool; + realValue->flags |= TND_DEFINED; + realValue->value.boolValue = (i%2) ? 0 : 1 ; } else if ( value->flags & TND___EVEN ) { - value->flags |= TND_DEFINED; - value->value.boolValue = (i%2) ? 1 : 0 ; - } else { - tassert( value->type == valuePointer ); - value->value.ptrValue = varvals+j; + realValue->type = valueBool; + realValue->flags |= TND_DEFINED; + realValue->value.boolValue = (i%2) ? 1 : 0 ; } j++; } - printNode( tmpl, node->nodeData.loop.bodyNode ); + + if ( node->nodeData.loop.selfNode ) + node->nodeData.loop.selfNode->nodeData.nest.savedRowData = rowData; + + printNode( tmpl, node->nodeData.loop.bodyNode, NULL ); } GListFree( instance->rowValues ); } break; + case NestNode: + if ( node->nodeData.nest.loop && node->nodeData.nest.savedRowData ) { + LoopRow savedRowData = node->nodeData.nest.savedRowData; /* save current row */ + LoopInstance *savedChildrenInstance = NULL; + + /* + * Save child's instances for current loop + */ + if ( GLIST_LENGTH(node->nodeData.nest.childrenLoopAfterSelf) ) { + savedChildrenInstance = mcalloc(tmpl->templateContext, sizeof(LoopInstance) * + GLIST_LENGTH(node->nodeData.nest.childrenLoopAfterSelf)); + + i=0; + GListForeach( cell, node->nodeData.nest.childrenLoopAfterSelf) { + TemplateNode chld = GLCELL_DATA(cell); + GListCell *cellInstance = GListShift( chld->nodeData.loop.listInstance ); + + if ( cellInstance == NULL ) + savedChildrenInstance[i++] = NULL; + else { + savedChildrenInstance[i++] = GLCELL_DATA(cellInstance); + GListFreeCell( chld->nodeData.loop.listInstance, cellInstance ); + } + } + } + + printNode( tmpl, node->nodeData.nest.loop, savedRowData->nestedInstance ); + + /* + * Restore saved datas + */ + i=0; + GListForeach( cell, node->nodeData.nest.loop->nodeData.loop.listVarValues ) { + ((VariableValue)GLCELL_DATA(cell))->value.ptrValue = savedRowData->varvals + i; + i++; + } + + if ( GLIST_LENGTH(node->nodeData.nest.childrenLoopAfterSelf) ) { + i=0; + GListForeach( cell, node->nodeData.nest.childrenLoopAfterSelf) { + TemplateNode chld = GLCELL_DATA(cell); + + if ( savedChildrenInstance[i] ) + GListUnshift( chld->nodeData.loop.listInstance, savedChildrenInstance[i]); + i++; + } + } + } + break; case ConditionNode: value = executeExpression( tmpl, node->nodeData.condition.expressionNode ); if ( isVariable(value) ) - printNode( tmpl, node->nodeData.condition.ifNode ); + printNode( tmpl, node->nodeData.condition.ifNode, loopInstance ); else - printNode( tmpl, node->nodeData.condition.elseNode ); + printNode( tmpl, node->nodeData.condition.elseNode, loopInstance ); break; case CollectionNode: GListForeach( cell, node->nodeData.children ) - printNode( tmpl, (TemplateNode)GLCELL_DATA(cell) ); + printNode( tmpl, (TemplateNode)GLCELL_DATA(cell), loopInstance ); break; case PrintNode: value = executeExpression( tmpl, node->nodeData.condition.expressionNode ); @@ -1144,7 +1303,7 @@ printTemplate( Template tmpl ) { if (!tmpl->printString) return 1; - printNode(tmpl, tmpl->tree); + printNode(tmpl, tmpl->tree, NULL); return 0; } @@ -1201,6 +1360,9 @@ recursiveDump(Template tmpl, TemplateNode node, int level) { case ConstNode: printf("ConstNode\n"); break; + case NestNode: + printf("NestNode\n"); + break; default: tlog(TL_CRIT|TL_EXIT, "unknown node type: %d", node->type); } diff --git a/template.h b/template.h index 9061e30..6f88989 100644 --- a/template.h +++ b/template.h @@ -63,6 +63,7 @@ * C-level. * * <@LOOP MARKNAME@> + * <@ SELF @> * <@ENDLOOP@> * Loop has predefined variables: * __FIRST - true for first iteration @@ -135,6 +136,7 @@ typedef enum TemplateNodeType { ExpressionNode, PrintNode, ConstNode, + NestNode, /* value's types of variables */ valueInt = 200, /* smallest of any values type */ @@ -188,9 +190,20 @@ typedef struct executeFunctionDescData { } executeFunctionDescData; typedef struct LoopInstanceData * LoopInstance; + +typedef struct LoopRowData *LoopRow; + +typedef struct LoopRowData { + TemplateNode loop; + LoopInstance nestedInstance; + VariableValueData varvals[1]; +} LoopRowData; +#define LRDHDRSZ (offsetof(LoopRowData, varvals)) + typedef struct LoopInstanceData { int nrow; - GList *rowValues; + LoopInstance upperInstance; + GList *rowValues; /*list of LoopRow */ } LoopInstanceData; typedef struct TemplateNodeData { @@ -234,14 +247,31 @@ typedef struct TemplateNodeData { /* IncludeNode */ char *includeFile; + /* NestNode */ + struct { + TemplateNode loop; + LoopRow savedRowData; + GList *childrenLoopAfterSelf; + } nest; + /* LoopNode */ struct { char *varName; int varNameLength; TemplateNode bodyNode; + TemplateNode selfNode; GList *childrenLoop; /* to reset loop's instance */ GList *listVarValues; /* list of loop variables */ GList *listInstance; + /* listInstace -+ + +->instance-+ + +->row + | + | + +->row->nestedinstance + */ + LoopRow lastRow; + LoopInstance currentInstance; } loop; /* ConditionNode */ @@ -292,6 +322,7 @@ int setTemplateValueBool( Template tmpl, char * key, int val ); int setTemplateValueUndefined( Template tmpl, char * key ); int setTemplateValueDouble( Template tmpl, char * key, double val ); int addTemplateRow( Template tmpl, char * key ); - +int addTemplateNestedLoop( Template tmpl, char * key); +int returnTemplateNestedLoop( Template tmpl, char * key); void dumpTemplate( Template tmpl ); #endif diff --git a/tmpl_gram.y b/tmpl_gram.y index 3335903..9bc2f71 100644 --- a/tmpl_gram.y +++ b/tmpl_gram.y @@ -49,7 +49,7 @@ static GList *makeList2(void *a, void *b); %token LEXEME %token VAR_OPEN VAR_CLOSE EXPR_OPEN EXPR_CLOSE INCLUDE_OPEN INCLUDE_CLOSE -%token HTMLESCAPE URLESCAPE IF_P ELSE_P LOOP_P ENDIF_P ENDLOOP_P +%token HTMLESCAPE URLESCAPE IF_P ELSE_P LOOP_P ENDIF_P ENDLOOP_P SELF_P %token CMP_P %token INTEGER @@ -225,6 +225,10 @@ node: $$->type = IncludeNode; $$->nodeData.includeFile = $2; } + | EXPR_OPEN SELF_P EXPR_CLOSE { + $$ = mc0alloc( curTmpl->templateContext, sizeof(TemplateNodeData) ); + $$->type = NestNode; + } | EXPR_OPEN LOOP_P varname EXPR_CLOSE listnodes EXPR_OPEN ENDLOOP_P EXPR_CLOSE { $$ = mc0alloc( curTmpl->templateContext, sizeof(TemplateNodeData) ); $$->type = LoopNode; diff --git a/tmpl_scan.l b/tmpl_scan.l index 318736c..5024589 100644 --- a/tmpl_scan.l +++ b/tmpl_scan.l @@ -205,6 +205,7 @@ static KeyWord keywords[] = { { "IF", 0, IF_P }, { "ELSE", 0, ELSE_P }, { "LOOP", 0, LOOP_P }, + { "SELF", 0, SELF_P }, { "ENDIF", 0, ENDIF_P }, { "ENDLOOP", 0, ENDLOOP_P } }; diff --git a/tmpltest.c b/tmpltest.c index c5aef19..a605f0b 100644 --- a/tmpltest.c +++ b/tmpltest.c @@ -75,7 +75,8 @@ main(int argn, char *argv[]) { MemoryContext *base; char *name = NULL; TemplateData template; - int i; + int i,j,k; + int cnt=0; executeFunctionDescData funcs[] = { {"callcounter", 0, localCounter}, {NULL,0,NULL} @@ -142,9 +143,57 @@ main(int argn, char *argv[]) { addTemplateRow(&template, "outerLoop.innerLoop"); setTemplateValueString(&template, "outerLoop.innerLoop.camenty", "Again 1"); + for(i=0;i<3;i++) { + addTemplateRow(&template,"selfLoop"); + setTemplateValueInt(&template, "selfLoop.CNT", ++cnt); + setTemplateValueString(&template, "selfLoop.NODE", "outer"); + + if (i<1) { + addTemplateRow(&template,"selfLoop.oneloop"); + setTemplateValueInt(&template, "selfLoop.oneloop.CNT", ++cnt); + setTemplateValueString(&template, "selfLoop.oneloop.NODE", "subloop1"); + } + if (i<2) { + addTemplateRow(&template,"selfLoop.oneloop"); + setTemplateValueInt(&template, "selfLoop.oneloop.CNT", ++cnt); + setTemplateValueString(&template, "selfLoop.oneloop.NODE", "subloop2"); + } + + addTemplateNestedLoop(&template, "selfLoop"); + + for(j=0;j<2;j++) { + addTemplateRow(&template,"selfLoop"); + setTemplateValueInt(&template, "selfLoop.CNT", ++cnt); + setTemplateValueString(&template, "selfLoop.NODE", "inner"); + if ( i==1 && j==0 ) { + addTemplateRow(&template,"selfLoop.oneloop"); + setTemplateValueInt(&template, "selfLoop.oneloop.CNT", ++cnt); + setTemplateValueString(&template, "selfLoop.oneloop.NODE", "subloop3"); + } + if (i==2 && j==1) { + addTemplateNestedLoop(&template, "selfLoop"); + for(k=0;k<4;k++) { + addTemplateRow(&template,"selfLoop"); + setTemplateValueInt(&template, "selfLoop.CNT", ++cnt); + setTemplateValueString(&template, "selfLoop.NODE", "innerst"); + if ( k==2 ) { + addTemplateRow(&template,"selfLoop.oneloop"); + setTemplateValueInt(&template, "selfLoop.oneloop.CNT", ++cnt); + setTemplateValueString(&template, "selfLoop.oneloop.NODE", "subloop4"); + } + } + returnTemplateNestedLoop(&template, "selfLoop"); + } + } + + + returnTemplateNestedLoop(&template, "selfLoop"); + } + fputs("================================================\n", stdout); printTemplate( &template ); + resetTemplate(&template); freeTemplate(&template); -- 2.37.3