Add recursive loop to support tree-like structures
[tedtools.git] / template.c
index 45b366b..5651124 100644 (file)
@@ -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; i<instance->nrow;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);
        }