id: <% ID %> - <i>simple</i>
idhex: <% ID, "0x%08x" %> - <b>HEX
-idhexdef: <% ID, "HEX(0x%08x)" || "-1" %> -<
+idhexdef: <% ID, "HEX(0x%08x)" # "-1" %> -<
ndef: <% ndefID %>
-ndef def: <% ndefID || "Wow" %>
+ndef def: <% ndefID # "Wow" %>
empty <% EmptyId %>
-empty def: <% EmptyId || "\"EmptyId\" - default" %>
+empty def: <% EmptyId # "\"EmptyId\" - default" %>
zero <% zeroID %>
-zero def: <% zeroID || "zeroID" %><# COMENT
+zero def: <% zeroID # "zeroID" %><# COMENT
#>
<@ IF ID @>ID-YES<@ ELSE @>ID-NO<@ ENDIF @>
-<@ IF DEFINED ID @>DEFINED ID-YES<@ ELSE @>DEFINED ID-NO<@ ENDIF @>
-<@ IF NOT ID @>DEFINED ID-YES<@ ELSE @>NOT ID-NO<@ ENDIF @>
-<@ IF NOT DEFINED ID @>DEFINED ID-YES<@ ELSE @>NOT DEFINED ID-NO<@ ENDIF @>
+<@ IF DEFINED(ID) @>DEFINED ID-YES<@ ELSE @>DEFINED ID-NO<@ ENDIF @>
+<@ IF !ID @>DEFINED ID-YES<@ ELSE @>NOT ID-NO<@ ENDIF @>
+<@ IF ! DEFINED (ID) @>DEFINED ID-YES<@ ELSE @>NOT DEFINED ID-NO<@ ENDIF @>
<@ IF ndefID @>ndefID-YES<@ ELSE @>ndefID-NO<@ ENDIF @>
-<@ IF DEFINED ndefID @>DEFINED ndefID-YES<@ ELSE @>DEFINED ndefID-NO<@ ENDIF @>
-<@ IF NOT ndefID @>DEFINED ndefID-YES<@ ELSE @>NOT ndefID-NO<@ ENDIF @>
-<@ IF NOT DEFINED ndefID @>DEFINED ndefID-YES<@ ELSE @>NOT DEFINED ndefID-NO<@ ENDIF @>
+<@ IF DEFINED (ndefID) @>DEFINED ndefID-YES<@ ELSE @>DEFINED ndefID-NO<@ ENDIF @>
+<@ IF ! (ndefID) @>DEFINED ndefID-YES<@ ELSE @>NOT ndefID-NO<@ ENDIF @>
+<@ IF ! DEFINED (ndefID) @>DEFINED ndefID-YES<@ ELSE @>NOT DEFINED ndefID-NO<@ ENDIF @>
<@ IF EmptyId @>EmptyId-YES<@ ELSE @>EmptyId-NO<@ ENDIF @>
-<@ IF DEFINED EmptyId @>DEFINED EmptyId-YES<@ ELSE @>DEFINED EmptyId-NO<@ ENDIF @>
-<@ IF NOT EmptyId @>DEFINED EmptyId-YES<@ ELSE @>NOT EmptyId-NO<@ ENDIF @>
-<@ IF NOT DEFINED EmptyId @>DEFINED EmptyId-YES<@ ELSE @>NOT DEFINED EmptyId-NO<@ ENDIF @>
+<@ IF DEFINED (EmptyId) @>DEFINED EmptyId-YES<@ ELSE @>DEFINED EmptyId-NO<@ ENDIF @>
+<@ IF ! EmptyId @>DEFINED EmptyId-YES<@ ELSE @>NOT EmptyId-NO<@ ENDIF @>
+<@ IF ! DEFINED (EmptyId) @>DEFINED EmptyId-YES<@ ELSE @>NOT DEFINED EmptyId-NO<@ ENDIF @>
<@ IF zeroID @>zeroID-YES<@ ELSE @>zeroID-NO<@ ENDIF @>
-<@ IF DEFINED zeroID @>DEFINED zeroID-YES<@ ELSE @>DEFINED zeroID-NO<@ ENDIF @>
-<@ IF NOT zeroID @>DEFINED zeroID-YES<@ ELSE @>NOT zeroID-NO<@ ENDIF @>
-<@ IF NOT DEFINED zeroID @>DEFINED zeroID-YES<@ ELSE @>NOT DEFINED zeroID-NO<@ ENDIF @>
+<@ IF DEFINED (zeroID) @>DEFINED zeroID-YES<@ ELSE @>DEFINED zeroID-NO<@ ENDIF @>
+<@ IF ! zeroID @>DEFINED zeroID-YES<@ ELSE @>NOT zeroID-NO<@ ENDIF @>
+<@ IF ! DEFINED(zeroID) @>DEFINED zeroID-YES<@ ELSE @>NOT DEFINED zeroID-NO<@ ENDIF @>
+
+id*2 <% ID * 2 %>
+id+2 <% ID + 2 %>
+(id+2)*2 <% (ID + 2)*2 %>
+id+2*2 <% ID + 2*2 %>
+id*2+2 <% ID * 2+2 %>
+id*3+zeroID <% ID * 2+zeroID %>
+length(str) <% length (str) %>
+length(str) > 3: <% length(str) > 3 %>
+
+<@ if length(str) < 10 && id > 16 @>HEH-1<@ endif @>
+CallCounter: <% CallCounter() %>
+
+str ? "yes" : -1 = <% str ? "yes" : -1 %>
<@ IF ID @>
- <@ IF DEFINED zeroID @>
+ <@ IF DEFINED(zeroID) @>
ID!=0 && defined(zeroID) - right
<@ ELSE @>
ID!=0 && !defined(zeroID)
<@endif@>
<@ELSE@>
- <@ IF DEFINED zeroID @>
+ <@ IF DEFINED (zeroID) @>
ID==0 && defined(zeroID)
<@ ELSE @>
ID==0 && && !defined(zeroID)
<@ 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 %> <# <@if __FIRST @>FIRST<@endif@> <@if __LAST @>LAST<@endif@> id: <% ^ID %> <% DATA1 %>:<% DATA2 # "Data is absent" %>/<% DATA1 %> #>
<& data/template_include.tmpl &>
<@ LOOP innerLoop @>
-<@if __FIRST @>FIRST<@endif@> <@if __LAST @>LAST<@endif@> id: <% ^ID %> <% DATA1 %>:<% DATA2 || "Data is absent" %>/<% DATA1 %>
+<@if __FIRST @>FIRST<@endif@> <@if __LAST @>LAST<@endif@> id: <% ^ID %> <% DATA1 %>:<% DATA2 # "Data is absent" %>/<% DATA1 %>
DEFINED zeroID-YES
NOT DEFINED zeroID-NO
+id*2 34
+id+2 19
+(id+2)*2 38
+id+2*2 21
+id*2+2 36
+id*3+zeroID 34
+length(str) 6
+length(str) > 3: true
+
+HEH-1
+CallCounter: 1
+
+str ? "yes" : -1 = yes
+
ID!=0 && defined(zeroID) - right
DEFINED zeroID-YES
NOT DEFINED zeroID-NO
+id*2 46
+id+2 25
+(id+2)*2 50
+id+2*2 27
+id*2+2 48
+id*3+zeroID 46
+length(str)
+length(str) > 3:
+
+
+CallCounter: 2
+
+str ? "yes" : -1 = -1
+
ID!=0 && defined(zeroID) - right
#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 {
GLCELL_DATA(cell) = chld;
}
break;
- case TextNode:
+ case ExpressionNode: /* any expression node can not include files */
+ case PrintNode:
+ case ConstNode:
case VariableNode:
+ case TextNode:
break;
default:
tlog(TL_CRIT|TL_EXIT, "unknown node type: %d", (*node)->type);
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;
return 1;
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) )
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:
}
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;
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 )
return 0;
}
+/*
+ * Reset/cleanup
+ */
+
void
resetTemplate( Template tmpl ) {
SFSDataIO out;
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 VariableNode:
+ case IncludeNode:
case TextNode:
+ case ConstNode:
default:
break;
}
freeNode( tmpl->tree );
}
+/*
+ * Set value routines
+ */
+
static void
newLoopInstance( Template tmpl, TemplateNode node ) {
node->nodeData.loop.listInstance = GListPush(
return setTemplateValue( tmpl, key );
}
+/*
+ * output routines
+ */
+
static char *
printVal( Template tmpl, VariableValue value, int *len, char *format ) {
int printedlen;
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:
+ 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
}
break;
case ConditionNode:
- value = node->nodeData.condition.value;
- if ( value->type == valuePointer )
- value = value->value.ptrValue;
-
- 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 );
- }
+ value = executeExpression( tmpl, node->nodeData.condition.expressionNode );
+
+ if ( isVariable(value) )
+ printNode( tmpl, node->nodeData.condition.ifNode );
+ else
+ printNode( tmpl, node->nodeData.condition.elseNode );
break;
case CollectionNode:
GListForeach( cell, node->nodeData.children )
printNode( tmpl, (TemplateNode)GLCELL_DATA(cell) );
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);
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;
}
}
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;
default:
tlog(TL_CRIT|TL_EXIT, "unknown node type: %d", node->type);
/******************************************************************************
* SYNTAX *
******************************************************************************
- * <% [^]VARNAME[, "FORMAT"] [||"DEFAULTVALUE"] [|(h|u)]%>
- * - '^' mark means global variable.
+ * <% EXPRESSION , "FORMAT"] [# "DEFAULTVALUE"] [|(h|u)]%>
* - format value should be as in strftime for time value and printf
* for all other. Currently, bool values have only "true"/"false"
* string values
- * <@IF [NOT] [DEFINED] [^]VARNAME@>
+ * <@IF EXPRESSION @>
* <@ELSE@>
* <@ENDIF@>
*
- * <@LOOP MARKNAME@>
+ * Expression is classical with support following:
+ * - ['^'] VARNAME
+ * variable defined from C-code. Mark '^' means global
+ * variable, not local in loop.
+ * - expression (+|-|*|/|%) expression
+ * ariphmetic operations
+ * - expression ( || | && ) expression
+ * ! expression
+ * logical OR, AND and NOT
+ * - expression ( < | <= | == | >= | > | != | <> ) expression
+ * compare expression
+ * - LENGTH(expression)
+ * computes length of string
+ * - DEFINED(expression)
+ * returns true if expression is defined
+ * - expression ? expression : expression
+ * - ( expression )
+ * - USERDEFINEDFUNCTION( [expression[,expression[...]]] )
+ * User defined function call. Function should be defined at
+ * C-level.
*
+ * <@LOOP MARKNAME@>
* <@ENDLOOP@>
+ * Loop has predefined variables:
+ * __FIRST - true for first iteration
+ * __LAST - true for last iteration
+ * __COUNTER - iteration's number
+ * __SIZE - number of iterations
+ * __ODD - true for odd iteraion
+ * __EVEN - true for even iteraion
*
* <& FILENAME &>
*
* <# comment #>
*
+ ******************************************************************************
+ * C-Interface
+ ******************************************************************************
+ * - setTemplateValueInt
+ * setTemplateValueString
+ * setTemplateValueTime
+ * setTemplateValueBool
+ * setTemplateValueDouble
+ * setTemplateValueUndefined
+ * Sets varibale's value
+ * - addTemplateRow
+ * Add one iteration to the pointed loop. Local variable in loop should
+ * pointed with predecence loop's mark(s) separated by dot. Example:
+ * HTML:
+ * <@Loop outerLoop@>
+ * <% var1 %>
+ * <@Loop innerLoop@>
+ * <% var2 %>
+ * <@endloop@>
+ * <@endloop@>
+ * C:
+ * addTemplateRow("outerLoop");
+ * setTemplateValueBool("outerLoop.var1");
+ * addTemplateRow("innerLoop");
+ * setTemplateValueBool("outerLoop.innerLoop.var2");
+ *
******************************************************************************/
#ifndef __TEMPLATE_H__
LoopNode,
ConditionNode,
CollectionNode,
+ ExpressionNode,
+ PrintNode,
+ ConstNode,
/* value's types of variables */
valueInt = 200, /* smallest of any values type */
valuePointer
} TemplateNodeType;
-#define TND_DEFINED (0x0001)
-#define TND_NOT (0x0002)
-#define TND_HTMLESCAPE (0x0004)
-#define TND_URLESCAPE (0x0008)
-#define TND_GLOBAL (0x0010)
-#define TND___FIRST (0x0020)
-#define TND___LAST (0x0040)
-#define TND___COUNTER (0x0080)
-#define TND___SIZE (0x0100)
-#define TND___ODD (0x0200)
-#define TND___EVEN (0x0400)
+#define TND_HTMLESCAPE (0x0001)
+#define TND_URLESCAPE (0x0002)
+#define TND_GLOBAL (0x0004)
+
+#define TND___FIRST (0x0008)
+#define TND___LAST (0x0010)
+#define TND___COUNTER (0x0020)
+#define TND___SIZE (0x0040)
+#define TND___ODD (0x0080)
+#define TND___EVEN (0x0100)
+
+#define TND_DEFINED (0x0200)
#define TND__SPECIALMASK (TND___FIRST | TND___LAST | TND___COUNTER | TND___SIZE | TND___ODD | TND___EVEN)
-struct TemplateNodeData;
+
+typedef struct TemplateData *Template;
+
typedef struct TemplateNodeData *TemplateNode;
typedef struct VariableValueData * VariableValue;
} value;
} VariableValueData;
+
+typedef struct executeFunctionDescData *executeFunctionDesc;
+typedef struct executeFunctionDescData {
+ char *name;
+ int nargs; /* -1 - variable number */
+ VariableValue (*execFn)(Template, int, VariableValue*);
+} executeFunctionDescData;
+
typedef struct LoopInstanceData * LoopInstance;
typedef struct LoopInstanceData {
int nrow;
int varNameLength;
VariableValue value;
int flags;
- char *formatValue;
- char *defaultValue;
} variable;
+ /* ExpressionNode */
+ struct {
+ VariableValue *argsValue;
+ int nargs;
+ char *functionName;
+ executeFunctionDesc function;
+ GList *argsNode; /* list of nodes after parsing
+ but before compile */
+ } expression;
+
+ /* ConstNode */
+ VariableValueData value;
+
+ /* PrintNode */
+ struct {
+ TemplateNode expressionNode;
+ char *formatValue;
+ char *defaultValue;
+ int flags;
+ } print;
+
/* IncludeNode */
char *includeFile;
/* ConditionNode */
struct {
- int flags;
- char *varName;
- int varNameLength;
- VariableValue value;
+ TemplateNode expressionNode;
TemplateNode ifNode;
TemplateNode elseNode;
} condition;
typedef char* (*htmlEscapeFn)(char *, int * /* in/out */);
typedef struct TemplateData {
- TemplateNode tree;
- MemoryContext *templateContext;
- SFSTree variables;
- outFn printString;
- urlEscapeFn urlEscape;
- htmlEscapeFn htmlEscape;
+ TemplateNode tree;
+ MemoryContext *templateContext;
+ SFSTree variables;
+ outFn printString;
+ urlEscapeFn urlEscape;
+ htmlEscapeFn htmlEscape;
+ executeFunctionDesc functions;
} TemplateData;
-typedef struct TemplateData *Template;
-
int parseTemplateFile(Template tmpl, char* filename ); /* return non-zero if error */
-int initTemplate( Template tmpl, MemoryContext *mc, char *basedir, char *filename );
+int initTemplate( Template tmpl, MemoryContext *mc, executeFunctionDesc functions, char *basedir, char *filename );
void freeTemplate( Template tmpl );
void resetTemplate( Template tmpl );
int printTemplate( Template tmpl );
static Template curTmpl;
extern int tmpl_yylineno;
+static TemplateNode newExpressionNode(char *op, GList *args);
+static GList *makeList2(void *a, void *b);
+
%}
%union {
- char *str;
- char punct;
- int varname;
- int flags;
- TemplateNode node;
+ char *str;
+ char punct;
+ int flags;
+ int intval;
+ double floatval;
+ TemplateNode node;
+ GList *list;
}
%type <node> node
%type <node> listnodes
%type <node> template
-%type <node> condition
-%type <node> condition_varname
+%type <node> expression
+%type <list> list_expression
%type <str> varname
%type <flags> opt_escape
-%type <flags> opt_global
-%type <str> opt_default
%type <str> opt_format
+%type <str> opt_default
-%token <str> OR_P
%token <str> STRING
%token <str> FILENAME
%token <str> TEXT_P
%token <str> VAR_OPEN VAR_CLOSE EXPR_OPEN EXPR_CLOSE
INCLUDE_OPEN INCLUDE_CLOSE
%token <str> HTMLESCAPE URLESCAPE IF_P ELSE_P LOOP_P ENDIF_P ENDLOOP_P
- NOT_P DEFINED_P
+%token <str> CMP_P
+
+%token <intval> INTEGER
+%token <floatval> DOUBLE
+
+
+%left '+' '-'
+%left '*' '/' '%'
+%left '?' ':'
+%left NEG
+
+%left OR_P
+%left AND_P
+%left NOT_P
+
+%left CMP_P
%%
| LOOP_P
| ENDIF_P
| ENDLOOP_P
- | NOT_P
- | DEFINED_P
;
opt_escape:
| { $$=0; }
;
-opt_global:
- '^' { $$=TND_GLOBAL; }
- | { $$=0; }
- ;
-
opt_format:
',' STRING { $$=$2; }
| { $$=NULL; }
;
opt_default:
- OR_P STRING { $$=$2; }
+ '#' STRING { $$=$2; }
| { $$=NULL; }
;
-condition_varname:
- varname {
- $$ = mc0alloc( curTmpl->templateContext, sizeof(TemplateNodeData) );
- $$->type = ConditionNode;
- $$->nodeData.condition.varName = $1;
- $$->nodeData.condition.varNameLength = strlen($1);
- }
- | '^' varname {
- $$ = mc0alloc( curTmpl->templateContext, sizeof(TemplateNodeData) );
- $$->type = ConditionNode;
- $$->nodeData.condition.flags = TND_GLOBAL;
- $$->nodeData.condition.varName = $2;
- $$->nodeData.condition.varNameLength = strlen($2);
+list_expression:
+ expression ',' expression {
+ $$ = makeList2($1, $3);
}
+ | list_expression ',' expression {
+ $$ = GListPush($$, $3);
+ }
;
-condition:
- condition_varname {
- $$ = $1;
+expression:
+ varname {
+ $$ = mc0alloc( curTmpl->templateContext, sizeof(TemplateNodeData) );
+ $$->type = VariableNode;
+ $$->nodeData.variable.varName = $1;
+ $$->nodeData.variable.varNameLength = strlen($1);
+ }
+ | '^' varname {
+ $$ = mc0alloc( curTmpl->templateContext, sizeof(TemplateNodeData) );
+ $$->type = VariableNode;
+ $$->nodeData.variable.flags = TND_GLOBAL;
+ $$->nodeData.variable.varName = $2;
+ $$->nodeData.variable.varNameLength = strlen($2);
+ }
+ | STRING {
+ $$ = mc0alloc( curTmpl->templateContext, sizeof(TemplateNodeData) );
+ $$->type = ConstNode;
+ $$->nodeData.value.type = valueString;
+ $$->nodeData.value.flags = TND_DEFINED;
+ $$->nodeData.value.value.stringValue = $1;
+ }
+ | INTEGER {
+ $$ = mc0alloc( curTmpl->templateContext, sizeof(TemplateNodeData) );
+ $$->type = ConstNode;
+ $$->nodeData.value.type = valueInt;
+ $$->nodeData.value.flags = TND_DEFINED;
+ $$->nodeData.value.value.intValue = $1;
+ }
+ | DOUBLE {
+ $$ = mc0alloc( curTmpl->templateContext, sizeof(TemplateNodeData) );
+ $$->type = ConstNode;
+ $$->nodeData.value.type = valueDouble;
+ $$->nodeData.value.flags = TND_DEFINED;
+ $$->nodeData.value.value.doubleValue = $1;
}
- | DEFINED_P condition_varname {
- $$ = $2;
- $$->nodeData.condition.flags |= TND_DEFINED;
+ | expression '+' expression {
+ $$ = newExpressionNode( "+", makeList2( $1, $3 ) );
}
- | NOT_P condition {
- $$ = $2;
- if ( $$->nodeData.condition.flags & TND_NOT )
- $$->nodeData.condition.flags &= ~TND_NOT;
- else
- $$->nodeData.condition.flags |= TND_NOT;
+ | expression '-' expression {
+ $$ = newExpressionNode( "-", makeList2( $1, $3 ) );
}
+ | expression '*' expression {
+ $$ = newExpressionNode( "*", makeList2( $1, $3 ) );
+ }
+ | expression '/' expression {
+ $$ = newExpressionNode( "/", makeList2( $1, $3 ) );
+ }
+ | expression '%' expression {
+ $$ = newExpressionNode( "%", makeList2( $1, $3 ) );
+ }
+ | '-' expression %prec NEG {
+ $$ = newExpressionNode( "-", GListPush( NULL, $2 ) );
+ }
+ | expression AND_P expression {
+ $$ = newExpressionNode( "&&", makeList2( $1, $3 ) );
+ }
+ | expression OR_P expression {
+ $$ = newExpressionNode( "||", makeList2( $1, $3 ) );
+ }
+ | expression '?' expression ':' expression {
+ $$ = newExpressionNode( "?", GListPush( makeList2( $1, $3 ), $5 ) );
+ }
+ | NOT_P expression {
+ $$ = newExpressionNode( "!", GListPush( NULL, $2 ) );
+ }
+ | expression CMP_P expression {
+ $$ = newExpressionNode( $2, makeList2( $1, $3 ) );
+ }
+ | varname '(' ')' {
+ $$ = newExpressionNode( $1, NULL );
+ }
+ | varname '(' expression ')' {
+ $$ = newExpressionNode( $1, GListPush( NULL, $3 ) );
+ }
+ | varname '(' list_expression ')' {
+ $$ = newExpressionNode( $1, $3 );
+ }
+ | '(' expression ')' { $$=$2; }
;
node:
$$->nodeData.text.value = $1;
$$->nodeData.text.valueLength = strlen($1);
}
- | VAR_OPEN opt_global varname opt_format opt_default opt_escape VAR_CLOSE {
+ | VAR_OPEN expression opt_format opt_default opt_escape VAR_CLOSE {
$$ = mc0alloc( curTmpl->templateContext, sizeof(TemplateNodeData) );
- $$->type = VariableNode;
- $$->nodeData.variable.varName = $3;
- $$->nodeData.variable.varNameLength = strlen($3);
- $$->nodeData.variable.formatValue = $4;
- $$->nodeData.variable.defaultValue = $5;
- $$->nodeData.variable.flags = $2 | $6;
+ $$->type = PrintNode;
+ $$->nodeData.print.expressionNode = $2;
+ $$->nodeData.print.formatValue = $3;
+ $$->nodeData.print.defaultValue = $4;
+ $$->nodeData.print.flags = $5;
}
| INCLUDE_OPEN FILENAME INCLUDE_CLOSE {
$$ = mc0alloc( curTmpl->templateContext, sizeof(TemplateNodeData) );
$$->nodeData.loop.varNameLength = strlen($3);
$$->nodeData.loop.bodyNode = $5;
}
- | EXPR_OPEN IF_P condition EXPR_CLOSE listnodes EXPR_OPEN ENDIF_P EXPR_CLOSE {
- $$ = $3;
+ | EXPR_OPEN IF_P expression EXPR_CLOSE listnodes EXPR_OPEN ENDIF_P EXPR_CLOSE {
+ $$ = mc0alloc( curTmpl->templateContext, sizeof(TemplateNodeData) );
+ $$->type = ConditionNode;
+ $$->nodeData.condition.expressionNode = $3;
$$->nodeData.condition.ifNode = $5;
}
- | EXPR_OPEN IF_P condition EXPR_CLOSE listnodes EXPR_OPEN ELSE_P EXPR_CLOSE listnodes EXPR_OPEN ENDIF_P EXPR_CLOSE {
- $$ = $3;
+ | EXPR_OPEN IF_P expression EXPR_CLOSE listnodes EXPR_OPEN ELSE_P EXPR_CLOSE listnodes EXPR_OPEN ENDIF_P EXPR_CLOSE {
+ $$ = mc0alloc( curTmpl->templateContext, sizeof(TemplateNodeData) );
+ $$->type = ConditionNode;
+ $$->nodeData.condition.expressionNode = $3;
$$->nodeData.condition.ifNode = $5;
$$->nodeData.condition.elseNode = $9;
}
return err;
}
+static TemplateNode
+newExpressionNode(char *op, GList *args) {
+ TemplateNode res;
+
+ res = mc0alloc( curTmpl->templateContext, sizeof(TemplateNodeData) );
+ res->type = ExpressionNode;
+ res->nodeData.expression.functionName = op;
+ res->nodeData.expression.argsNode = args;
+
+ return res;
+}
+
+static GList *
+makeList2(void *a, void *b) {
+ return GListPush( GListPush(NULL, a), b );
+}
%x xQUOTED
%x xCOMMENT
+LEXEMCHARSTART [a-zA-Z_]
LEXEMCHAR [a-zA-Z0-9_]
PATH [a-zA-Z0-9_/\.]
+DIGIT [0-9]
%%
BEGIN xCOMMENT;
}
-<xVAR>{LEXEMCHAR}+ {
- yylval.str = strlower(mcnstrdup(lexContext, yytext, yyleng));
- return getIdent(yylval.str);
- }
-
-<xVAR>\|\| { return OR_P; }
-
-<xVAR>[,\|\^] {
+<xVAR>[,\|#] {
yylval.punct = *yytext;
return yylval.punct;
}
addchar(*yytext);
}
-<xEXPR>{LEXEMCHAR}+ {
+<xEXPR,xVAR>{DIGIT}+ {
+ yylval.intval = atoi(yytext);
+ return INTEGER;
+ }
+
+<xEXPR,xVAR>{LEXEMCHARSTART}{LEXEMCHAR}* {
yylval.str = strlower(mcnstrdup(lexContext, yytext, yyleng));
return getIdent(yylval.str);
}
+<xEXPR,xVAR>\|\| { return OR_P; }
+
+<xEXPR,xVAR>\&\& { return AND_P; }
+
+<xEXPR,xVAR>\! { return NOT_P; }
+
+<xEXPR,xVAR>[\^\%\+\*\/\-\(\)\?\:] {
+ yylval.punct = *yytext;
+ return yylval.punct;
+ }
+
+<xEXPR,xVAR>(\<|\<=|\>=|\>|==|\!=|\<\>) {
+ yylval.str = mcnstrdup(lexContext, yytext, yyleng);
+ return CMP_P;
+ }
+
+<xEXPR,xVAR>{DIGIT}+"."{DIGIT}+ {
+ yylval.floatval = atof(yytext);
+ return DOUBLE;
+ }
+
+<xEXPR,xVAR>{DIGIT}+[eE]["+""-"]?{DIGIT}+ {
+ yylval.floatval = atof(yytext);
+ return DOUBLE;
+ }
+
+<xEXPR,xVAR>{DIGIT}+"."{DIGIT}+[eE]["+""-"]?{DIGIT}+ {
+ yylval.floatval = atof(yytext);
+ return DOUBLE;
+ }
+
<xEXPR>\@\> {
BEGIN INITIAL;
return EXPR_CLOSE;
{ "H", 0, HTMLESCAPE },
{ "U", 0, URLESCAPE },
{ "IF", 0, IF_P },
- { "NOT", 0, NOT_P },
{ "ELSE", 0, ELSE_P },
{ "LOOP", 0, LOOP_P },
{ "ENDIF", 0, ENDIF_P },
- { "DEFINED", 0, DEFINED_P },
{ "ENDLOOP", 0, ENDLOOP_P }
};
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
+#include <locale.h>
#include "tmalloc.h"
usage() {
puts(
"Usage:\n"
- "memtest [-t TEMPLATENAME]\n"
+ "tmpltest [-t TEMPLATENAME]\n"
);
exit(1);
}
fputs( str, stdout );
}
+static int counter = 0;
+
+static VariableValue
+localCounter(Template tmpl, int nargs, VariableValue *args) {
+ VariableValue out = tmalloc(sizeof(VariableValue));
+
+ out->type = valueInt;
+ out->flags = TND_DEFINED;
+ out->value.intValue = ++counter;
+
+ return out;
+}
+
extern char *optarg;
extern int opterr;
char *name = NULL;
TemplateData template;
int i;
+ executeFunctionDescData funcs[] = {
+ {"callcounter", 0, localCounter},
+ {NULL,0,NULL}
+ };
+
+
opentlog(TL_OPEN_STDERR,TL_DEBUG, NULL);
opterr=0;
if (!name)
usage();
+ setlocale(LC_ALL,"ru_RU.UTF-8");
base = allocMemoryContext(NULL, MC_DEBUG);
- printf("initTemplate: %d\n", initTemplate(&template, base, ".", name) );
- /* dumpTemplate(&template); */
+ printf("initTemplate: %d\n", initTemplate(&template, base, funcs, ".", name) );
+ /* dumpTemplate(&template); */
setTemplateValueInt(&template, "ID", 17);
setTemplateValueUndefined(&template, "emptyID");
setTemplateValueInt(&template, "zeroid", 0);
+ setTemplateValueString(&template, "str", "QWERTY");
addTemplateRow(&template, "outerLoop");
setTemplateValueString(&template, "outerLoop.data1", "ha1");