Add some comments, fix cleanup, improve tests
[tedtools.git] / template.c
index 6561ee1..73d9cc4 100644 (file)
 #include "tlog.h"
 #include "template.h"
 
+/*
+ * Default operations and functions
+ */
+
+static int
+isVariable(VariableValue value) {
+       if ( value == NULL )
+               return 0;
+
+       if ( (value->flags & TND_DEFINED) == 0 ) {
+               return 0;
+       } else {
+               switch (value->type) {
+                       case valueInt:
+                               return value->value.intValue;
+                       case valueString:
+                               if ( value->value.stringValue == NULL || *value->value.stringValue == '\0' )
+                                       return 0;
+                               return 1;
+                       case valueTime:
+                               return ( value->value.timeValue > 0 ) ? 1 : 0;
+                       case valueBool:
+                               return value->value.boolValue;
+                       case valueDouble:
+                               return (value->value.doubleValue == 0) ? 0 : 1;
+                       case valuePointer:
+                       default:
+                               return 0;
+               }
+       }
+
+       return 0;
+}
+
+static VariableValue
+makeBoolValue(Template tmpl, int v) {
+       VariableValue   outvalue = mcalloc(tmpl->templateContext, sizeof(VariableValueData));
+
+       outvalue->type = valueBool;
+       outvalue->flags= TND_DEFINED;
+       outvalue->value.boolValue = (v) ? 1 : 0;
+       return outvalue;
+}
+
+static VariableValue
+copyValue(Template tmpl, VariableValue in) {
+       VariableValue   out= mcalloc(tmpl->templateContext, sizeof(VariableValueData));
+
+       if (in)
+               memcpy(out, in, sizeof(VariableValueData));
+       else {
+               out->type = valueBool; /* something */
+               out->flags = 0;
+       }
+       return out;
+}
+
+static VariableValue
+isDefinedFn(Template tmpl, int n, VariableValue *vals) {
+       return makeBoolValue(tmpl, vals[0]->flags & TND_DEFINED );
+}
+
+static int
+strmblen(char *str) {
+       int len = strlen(str);
+       int     totlen = 0, clen;
+
+       mblen(NULL,0); /* reset internal state */
+       while( len > 0 ) {
+               clen = mblen( str, len );
+               str += clen;
+               len -= clen;
+               totlen++;
+       }
+
+       return totlen;
+}
+
+static VariableValue
+LengthFn(Template tmpl, int n, VariableValue *vals) {
+       VariableValue   outvalue = NULL;
+
+       outvalue = copyValue( tmpl, NULL );
+       outvalue->type = valueInt;
+
+       if ( vals[0]->type == valueString && vals[0]->value.stringValue &&
+                                                               (vals[0]->flags & TND_DEFINED) ) {
+               outvalue->flags |= TND_DEFINED;
+               outvalue->value.intValue = strmblen( vals[0]->value.stringValue );
+       } else 
+               outvalue->flags &= ~TND_DEFINED;
+
+       return outvalue;
+}
+
+static VariableValue
+NotFn(Template tmpl, int n, VariableValue *vals) {
+       return makeBoolValue(tmpl, !isVariable(vals[0]));
+}
+
+static VariableValue
+AndFn(Template tmpl, int n, VariableValue *vals) {
+       return makeBoolValue(tmpl, isVariable(vals[0]) && isVariable(vals[1]) );
+}
+
+static VariableValue
+OrFn(Template tmpl, int n, VariableValue *vals) {
+       return makeBoolValue(tmpl, isVariable(vals[0]) || isVariable(vals[1]) );
+}
+
+static VariableValue
+CondFn(Template tmpl, int n, VariableValue *vals) {
+       return isVariable(vals[0]) ? vals[1] : vals[2]; 
+}
+
+static VariableValue
+ModFn(Template tmpl, int n, VariableValue *vals) {
+       VariableValue   outvalue = copyValue( tmpl, NULL );
+
+       outvalue->type = valueInt;
+
+       if ( (vals[0]->flags & vals[1]->flags & TND_DEFINED) &&
+                                       vals[0]->type == valueInt && vals[1]->type == valueInt) {
+       
+               outvalue->flags |= TND_DEFINED;
+               outvalue->value.intValue = vals[0]->value.intValue % vals[1]->value.intValue;
+       } else 
+               outvalue->flags &= ~TND_DEFINED;
+
+       return outvalue;
+}
+
+static VariableValue
+UnaryMinesFn(Template tmpl, int n, VariableValue *vals) {
+       VariableValue   outvalue = copyValue( tmpl, vals[0] );
+
+       if (outvalue->type == valueInt)
+               outvalue->value.intValue = -outvalue->value.intValue;
+       else if (outvalue->type == valueDouble)
+               outvalue->value.doubleValue = -outvalue->value.doubleValue;
+       else
+               outvalue->flags &= ~TND_DEFINED;
+
+       return outvalue;
+}
+
+#define ISNUM(v)       ( ((v)->type == valueDouble || (v)->type == valueInt) && ((v)->flags & TND_DEFINED)  )
+
+#define ARIPHACT(OP)                                                                                                                                                           \
+       VariableValue outvalue = copyValue( tmpl, NULL );                                                                                               \
+       if ( !(ISNUM(vals[0]) && ISNUM(vals[1])) ) {                                                                                                    \
+               outvalue->flags &= ~TND_DEFINED;                                                                                                                        \
+       } else if ( vals[0]->type == valueDouble || vals[1]->type == valueDouble ) {                                    \
+               outvalue->flags |= TND_DEFINED;                                                                                                                         \
+               outvalue->type = valueDouble;                                                                                                                           \
+               if ( vals[0]->type == valueDouble )                                                                                                                     \
+                       outvalue->value.doubleValue = vals[0]->value.doubleValue;                                                               \
+               else                                                                                                                                                                            \
+                       outvalue->value.doubleValue = vals[0]->value.intValue;                                                                  \
+               if ( vals[1]->type == valueDouble )                                                                                                                     \
+                       outvalue->value.doubleValue OP##= vals[1]->value.doubleValue;                                                   \
+               else                                                                                                                                                                            \
+                       outvalue->value.doubleValue OP##= vals[1]->value.intValue;                                                              \
+       } else {                                                                                                                                                                                \
+               outvalue->flags |= TND_DEFINED;                                                                                                                         \
+               outvalue->type = valueInt;                                                                                                                                      \
+               outvalue->value.intValue = vals[0]->value.intValue OP vals[1]->value.intValue;                          \
+       }
+
+static VariableValue
+PlusFn(Template tmpl, int n, VariableValue *vals) {
+       ARIPHACT(+)
+       return outvalue;
+}
+
+static VariableValue
+MinesFn(Template tmpl, int n, VariableValue *vals) {
+       ARIPHACT(-)
+       return outvalue;
+}
+
+static VariableValue
+MulFn(Template tmpl, int n, VariableValue *vals) {
+       ARIPHACT(*)
+       return outvalue;
+}
+
+static VariableValue
+DivFn(Template tmpl, int n, VariableValue *vals) {
+       ARIPHACT(/)
+       return outvalue;
+}
+
+#define CMPACT(OP)                                                                                                                                                                     \
+       VariableValue outvalue = copyValue( tmpl, NULL );                                                                                               \
+       outvalue->type = valueBool;                                                                                                                                             \
+       if ( !(ISNUM(vals[0]) && ISNUM(vals[1])) ) {                                                                                                    \
+               outvalue->flags &= ~TND_DEFINED;                                                                                                                        \
+       } else if ( vals[0]->type == valueDouble || vals[1]->type == valueDouble ) {                                    \
+               outvalue->flags |= TND_DEFINED;                                                                                                                         \
+               if ( vals[0]->type == valueDouble && vals[1]->type == valueDouble )                                             \
+                       outvalue->value.boolValue = (vals[0]->value.doubleValue OP vals[1]->value.doubleValue)  \
+                                                               ? 1 : 0;                                                                                                                        \
+               else if ( vals[0]->type == valueDouble )                                                                                                        \
+                       outvalue->value.boolValue = (vals[0]->value.doubleValue OP vals[1]->value.intValue)     \
+                                                               ? 1 : 0;                                                                                                                        \
+               else                                                                                                                                                                            \
+                       outvalue->value.boolValue = (vals[0]->value.intValue OP vals[1]->value.doubleValue)     \
+                                                               ? 1 : 0;                                                                                                                        \
+       } else {                                                                                                                                                                                \
+               outvalue->flags |= TND_DEFINED;                                                                                                                         \
+               outvalue->value.boolValue = (vals[0]->value.intValue OP vals[1]->value.intValue) ? 1 : 0;       \
+       }
+
+static VariableValue
+LtFn(Template tmpl, int n, VariableValue *vals) {
+       CMPACT(<)
+       return outvalue;
+}
+
+static VariableValue
+LeFn(Template tmpl, int n, VariableValue *vals) {
+       CMPACT(<=)
+       return outvalue;
+}
+
+static VariableValue
+EqFn(Template tmpl, int n, VariableValue *vals) {
+       CMPACT(==)
+       return outvalue;
+}
+
+static VariableValue
+GeFn(Template tmpl, int n, VariableValue *vals) {
+       CMPACT(>=)
+       return outvalue;
+}
+
+static VariableValue
+GtFn(Template tmpl, int n, VariableValue *vals) {
+       CMPACT(>)
+       return outvalue;
+}
+
+static VariableValue
+NeFn(Template tmpl, int n, VariableValue *vals) {
+       CMPACT(!=)
+       return outvalue;
+}
+
+static executeFunctionDescData Functions[] = {
+       {"defined",     1,      isDefinedFn},
+       {"length",      1,      LengthFn},
+       {"+",           2,      PlusFn},
+       {"-",           2,      MinesFn},
+       {"*",           2,      MulFn},
+       {"/",           2,      DivFn},
+       {"%",           2,      ModFn},
+       {"-",           1,      UnaryMinesFn},
+       {"?",           3,      CondFn},
+       {"||",          2,      OrFn},
+       {"&&",          2,      AndFn},
+       {"!",           1,      NotFn},
+       {"<",           2,      LtFn},
+       {"<=",          2,      LeFn},
+       {"==",          2,      EqFn},
+       {">=",          2,      GeFn},
+       {">",           2,      GtFn},
+       {"!=",          2,      NeFn},
+       {"<>",          2,      NeFn},
+       {NULL,          -1,     NULL}
+};
+
+
+/*
+ * Initialize functions
+ */
+
 #define MAXDEPTH       (128)
 
 typedef struct ParseState {
@@ -106,8 +384,12 @@ findIncludes(ParseState *state, TemplateNode *node) {
                                GLCELL_DATA(cell) = chld;
                        }
                        break;
-               case    TextNode:
+               case    ExpressionNode: /* any expression node can not include files */
+               case    PrintNode:
+               case    ConstNode:
                case    VariableNode:
+               case    TextNode:
+               case    NestNode:
                        break;
                default:
                        tlog(TL_CRIT|TL_EXIT, "unknown node type: %d", (*node)->type);
@@ -207,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 ); 
@@ -240,13 +516,39 @@ 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;
 }
 
+static void 
+findFunction(Template tmpl, TemplateNode node) {
+       executeFunctionDesc     ptr = tmpl->functions;
+
+       tassert(ptr != NULL);
+
+       while(ptr && ptr->name) {
+               if ( node->nodeData.expression.nargs < 0 || node->nodeData.expression.nargs == ptr->nargs ) {
+                       if ( strcmp( node->nodeData.expression.functionName, ptr->name ) == 0 ) {
+                               node->nodeData.expression.function = ptr;
+                               return;
+                       }
+               }
+
+               ptr++;
+       }
+       tlog(TL_CRIT|TL_EXIT, "Can not find function named '%s' with %d arguments", 
+                                                                               node->nodeData.expression.functionName,
+                                                                               node->nodeData.expression.nargs);
+}
+
 static int 
 compileTree( Template tmpl, TemplateNode node, TemplateNode loopParentNode ) {
        GListCell               *cell;
@@ -265,21 +567,19 @@ 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:
-                       node->nodeData.condition.flags = checkSpecialVariable(
-                                                                                               node->nodeData.condition.flags,
-                                                                                               node->nodeData.condition.varName );
-
-                       if ( (node->nodeData.condition.flags & TND_GLOBAL) == 0 )
-                               node->nodeData.condition.varName = qualifyVarname(tmpl, loopParentNode,
-                                                                                                       node->nodeData.condition.varName,
-                                                                                                       &node->nodeData.condition.varNameLength);
-
-                       node->nodeData.condition.value = findVariable( tmpl, loopParentNode,
-                                                                                               node->nodeData.condition.flags,
-                                                                                               node->nodeData.condition.varName, 
-                                                                                               node->nodeData.condition.varNameLength );
-
+                       if ( compileTree(tmpl, node->nodeData.condition.expressionNode, loopParentNode) )
+                                       return 1;
                        if ( compileTree(tmpl, node->nodeData.condition.ifNode, loopParentNode) )
                                        return 1;
                        if ( compileTree(tmpl, node->nodeData.condition.elseNode, loopParentNode) )
@@ -308,9 +608,25 @@ compileTree( Template tmpl, TemplateNode node, TemplateNode loopParentNode ) {
                                                                                                node->nodeData.variable.varName, 
                                                                                                node->nodeData.variable.varNameLength );
                        break;
+               case PrintNode:
+                       if ( compileTree(tmpl, node->nodeData.print.expressionNode, loopParentNode) )
+                               return 1;
+                       break;
+               case ExpressionNode:
+                       node->nodeData.expression.nargs = GLIST_LENGTH(node->nodeData.expression.argsNode);
+                       findFunction(tmpl, node);
+
+                       node->nodeData.expression.argsValue = mcalloc(tmpl->templateContext, sizeof(VariableValue) * 
+                                                                                                                               node->nodeData.expression.nargs );
+
+                       GListForeach(cell, node->nodeData.expression.argsNode) 
+                               if ( compileTree(tmpl, (TemplateNode)GLCELL_DATA(cell), loopParentNode) )
+                                       return 1;
+                       break;
                case IncludeNode:
                        tlog(TL_CRIT|TL_EXIT, "unexpected IncludeNode");
                        break;
+               case ConstNode:
                case TextNode:
                        break;
                default:
@@ -321,7 +637,7 @@ compileTree( Template tmpl, TemplateNode node, TemplateNode loopParentNode ) {
 }
 
 int
-initTemplate( Template tmpl, MemoryContext *mc, char *basedir, char *filename ) {
+initTemplate( Template tmpl, MemoryContext *mc, executeFunctionDesc functions, char *basedir, char *filename ) {
        ParseState      state;
        int err;
 
@@ -333,6 +649,22 @@ initTemplate( Template tmpl, MemoryContext *mc, char *basedir, char *filename )
        tmpl->templateContext = mc;
        SFSInit_dp(&tmpl->variables, sizeof(void*), NULL);
 
+       if ( functions ) {
+               executeFunctionDesc     ptr = functions;
+               int n=0;
+
+               while(ptr->name) 
+                       ptr++;
+
+               n = ptr - functions;
+               tmpl->functions = mcalloc(tmpl->templateContext, 
+                       sizeof(executeFunctionDescData) * n + sizeof(Functions));
+
+               memcpy( tmpl->functions, functions, sizeof(executeFunctionDescData) * n );
+               memcpy( tmpl->functions + n, Functions, sizeof(Functions));
+       } else 
+               tmpl->functions = Functions; 
+
        if ( (err=recursiveReadTemplate(&state, NULL, filename))!=0 )
                return err;
        if ( (err=compileTree( tmpl, tmpl->tree, NULL ))!=0 )
@@ -341,6 +673,10 @@ initTemplate( Template tmpl, MemoryContext *mc, char *basedir, char *filename )
        return 0;
 }
 
+/*
+ * Reset/cleanup
+ */
+
 void
 resetTemplate( Template tmpl ) {
        SFSDataIO       out;
@@ -382,7 +718,7 @@ freeNode( TemplateNode node ) {
 
        switch (node->type) {
                case    LoopNode:
-                       freeNode( node->nodeData.loop.bodyNode );
+                       freeNode( node->nodeData.loop.bodyNode ); /* selfNode is somewhere inside bodyNode */
                        GListFree( node->nodeData.loop.childrenLoop );
                        GListFree( node->nodeData.loop.listVarValues );
 
@@ -399,12 +735,25 @@ freeNode( TemplateNode node ) {
                        GListFree( node->nodeData.children );
                        break;
                case    ConditionNode:
+                       freeNode( node->nodeData.condition.expressionNode );
                        freeNode( node->nodeData.condition.ifNode );
                        freeNode( node->nodeData.condition.elseNode );
                        break;
-               case    IncludeNode:
+               case    PrintNode:
+                       freeNode( node->nodeData.print.expressionNode);
+                       break;
+               case    ExpressionNode:
+                       GListForeach(cell, node->nodeData.expression.argsNode)
+                               freeNode( GLCELL_DATA(cell) );
+                       GListFree( node->nodeData.expression.argsNode );
+                       break;
+               case    NestNode:
+                       GListFree( node->nodeData.nest.childrenLoopAfterSelf ); 
+                       break;  
                case    VariableNode:
+               case    IncludeNode:
                case    TextNode:
+               case    ConstNode:
                default:
                        break;
        }
@@ -416,21 +765,35 @@ freeTemplate( Template tmpl ) {
        freeNode( tmpl->tree );
 }
 
+/*
+ * Set value routines
+ */
+
 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);
@@ -454,23 +817,92 @@ 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;        
 
-       instance->nrow++;
-       instance->rowValues = GListPush( instance->rowValues, varvals );
+       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;
+
+       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;
        
 static int
@@ -495,7 +927,7 @@ setTemplateValue( Template tmpl, char *key) {
                if ( varval == NULL )
                        return TVAR_NOROW;
 
-               tassert( (varval->type & TND_GLOBAL) == 0 );
+               tassert( (varval->flags & TND_GLOBAL) == 0 );
        } 
 
        if ( varval->flags & TND__SPECIALMASK )
@@ -559,6 +991,10 @@ setTemplateValueUndefined( Template tmpl, char * key ) {
        return setTemplateValue( tmpl, key );
 }
 
+/*
+ * output routines
+ */
+
 static char *
 printVal( Template tmpl, VariableValue value, int *len, char *format ) {
        int             printedlen;
@@ -627,40 +1063,57 @@ printVal( Template tmpl, VariableValue value, int *len, char *format ) {
        return res;
 }
 
-static int
-isVariable(VariableValue value, int flags) {
-       if ( value == NULL )
-               return 0;
 
-       if ( flags & TND_DEFINED ) {
-               return (value->flags & TND_DEFINED);
-       } else {
-               switch (value->type) {
-                       case valueInt:
-                               return value->value.intValue;
-                       case valueString:
-                               if ( value->value.stringValue == NULL || *value->value.stringValue == '\0' )
-                                       return 0;
-                               return 1;
-                       case valueTime:
-                               return ( value->value.timeValue > 0 ) ? 1 : 0;
-                       case valueBool:
-                               return value->value.boolValue;
-                       case valueDouble:
-                               return (value->value.doubleValue == 0) ? 0 : 1;
-                       case valuePointer:
-                       default:
-                               return 0;
-               }
+static VariableValue
+executeExpression( Template tmpl, TemplateNode node ) {
+       VariableValue   outvalue = NULL;
+       GListCell               *cell;
+       int                             i = 0;
+
+
+       switch (node->type) {
+               case ConstNode:
+                       outvalue = &node->nodeData.value;
+                       break;
+               case VariableNode:
+                       if ( node->nodeData.variable.value->type == valuePointer )
+                               outvalue = node->nodeData.variable.value->value.ptrValue;
+                       else
+                               outvalue = node->nodeData.variable.value;
+                       break;
+               case ExpressionNode:
+                       GListForeach(cell, node->nodeData.expression.argsNode)
+                               node->nodeData.expression.argsValue[i++] = 
+                                       executeExpression( tmpl, (TemplateNode)GLCELL_DATA(cell) );
+                       
+                       outvalue = node->nodeData.expression.function->execFn(tmpl, 
+                                                                                                       node->nodeData.expression.nargs,
+                                                                                                       node->nodeData.expression.argsValue);
+                       break;
+               case TextNode:
+               case IncludeNode:
+               case LoopNode:
+               case ConditionNode:
+               case CollectionNode:
+               case PrintNode:
+               case NestNode:
+                       tlog(TL_CRIT|TL_EXIT, "Unexpected node type: %d", node->type); 
+                       break;
+               default:
+                       tlog(TL_CRIT|TL_EXIT, "Unknown node type: %d", node->type); 
+                       break;
        }
 
-       return 0;
+       tassert( outvalue!=NULL );
+
+       return outvalue;
 }
 
 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;
@@ -668,97 +1121,152 @@ 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    ConditionNode:
-                       value = node->nodeData.condition.value;
-                       if ( value->type == valuePointer )
-                               value = value->value.ptrValue;
+               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 );
+                                               }
+                                       }
+                               }
 
-                       if ( node->nodeData.condition.flags & TND_NOT ) { 
-                               if ( isVariable(value, node->nodeData.condition.flags) ) 
-                                       printNode( tmpl, node->nodeData.condition.elseNode );
-                               else
-                                       printNode( tmpl, node->nodeData.condition.ifNode );
-                       } else {
-                               if ( isVariable(value, node->nodeData.condition.flags) ) 
-                                       printNode( tmpl, node->nodeData.condition.ifNode );
-                               else
-                                       printNode( tmpl, node->nodeData.condition.elseNode );
+                               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, loopInstance );
+                       else
+                               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    VariableNode:
-                       value = node->nodeData.variable.value;
-                       if ( value->type == valuePointer )
-                               value = value->value.ptrValue;
+               case    PrintNode:
+                       value = executeExpression( tmpl, node->nodeData.condition.expressionNode ); 
 
                        if ( value && (value->flags & TND_DEFINED) != 0 ) {
                                int len;
                                char *res;
 
-                               res = printVal(tmpl, value, &len, node->nodeData.variable.formatValue);
+                               res = printVal(tmpl, value, &len, node->nodeData.print.formatValue);
 
                                if ( (node->nodeData.variable.flags & TND_HTMLESCAPE) && tmpl->htmlEscape )
                                        res = tmpl->htmlEscape(res, &len);
@@ -769,16 +1277,22 @@ printNode( Template tmpl, TemplateNode node ) {
                                        tmpl->printString( res, len );
                                        mcfree(res);
                                }
-                       } else if ( node->nodeData.variable.defaultValue ) {
-                               tmpl->printString( node->nodeData.variable.defaultValue,
-                                                                       strlen( node->nodeData.variable.defaultValue ) );
+                       } else if ( node->nodeData.print.defaultValue ) {
+                               tmpl->printString( node->nodeData.print.defaultValue,
+                                                                       strlen( node->nodeData.print.defaultValue ) );
                        }
                        break;
                case    TextNode:
                        tmpl->printString( node->nodeData.text.value,  node->nodeData.text.valueLength );
                        break;
                case    IncludeNode:
+               case    VariableNode:
+               case    ConstNode:
+               case    ExpressionNode:
+                       tlog(TL_CRIT|TL_EXIT, "Unexpected node type: %d", node->type); 
+                       break;
                default:
+                       tlog(TL_CRIT|TL_EXIT, "Unknown node type: %d", node->type); 
                        break;
        }
 }
@@ -788,43 +1302,65 @@ printTemplate( Template tmpl ) {
        if (!tmpl->printString)
                return 1;
 
-       printNode(tmpl, tmpl->tree);
+       printNode(tmpl, tmpl->tree, NULL);
 
        return 0;
 }
 
+static
+void printLevel(int level) {
+       while(level-- > 0 )
+               fputs("  ", stdout);
+}
 static void
 recursiveDump(Template tmpl,  TemplateNode node, int level) {
        GListCell               *cell;
 
+       printLevel(level);
        if (node == NULL ) {
-               printf("%d void\n", level);
+               printf("VOID\n");
                return;
        }
 
        switch(node->type) {
                case    IncludeNode:
-                       printf("%d IncludeNode\n", level);
+                       printf("IncludeNode\n");
                        break;
                case    LoopNode:
-                       printf("%d LoopNode\n", level);
+                       printf("LoopNode '%s'\n", node->nodeData.loop.varName);
                        recursiveDump(tmpl, node->nodeData.loop.bodyNode, level+1);
                        break;
                case    ConditionNode:
-                       printf("%d ConditionNode\n", level);
+                       printf("ConditionNode\n");
+                       recursiveDump(tmpl, node->nodeData.condition.expressionNode, level+1);
                        recursiveDump(tmpl, node->nodeData.condition.ifNode, level+1);
                        recursiveDump(tmpl, node->nodeData.condition.elseNode, level+1);
                        break;
                case    CollectionNode:
-                       printf("%d CollectionNode\n", level);
+                       printf("CollectionNode\n");
                        GListForeach(cell, node->nodeData.children) 
                                recursiveDump(tmpl, (TemplateNode)GLCELL_DATA(cell), level+1);
                        break;
                case    TextNode:
-                       printf("%d TextNode len:%d\n", level, node->nodeData.text.valueLength);
+                       printf("TextNode len:%d\n", node->nodeData.text.valueLength);
                        break;
                case    VariableNode:
-                       printf("%d VariableNode '%s'\n", level, node->nodeData.variable.varName);
+                       printf("VariableNode '%s'\n", node->nodeData.variable.varName);
+                       break;
+               case    ExpressionNode:
+                       printf("ExpressionNode '%s'\n", node->nodeData.expression.functionName);
+                       GListForeach(cell, node->nodeData.expression.argsNode)
+                               recursiveDump( tmpl, GLCELL_DATA(cell) ,level + 1);
+                       break;
+               case    PrintNode:
+                       printf("PrintNode\n");
+                       recursiveDump( tmpl, node->nodeData.print.expressionNode, level + 1 );
+                       break;
+               case    ConstNode:
+                       printf("ConstNode\n");
+                       break;
+               case    NestNode:
+                       printf("NestNode\n");
                        break;
                default:
                        tlog(TL_CRIT|TL_EXIT, "unknown node type: %d", node->type);