Add expressions
authorteodor <teodor>
Thu, 2 Oct 2008 14:05:59 +0000 (14:05 +0000)
committerteodor <teodor>
Thu, 2 Oct 2008 14:05:59 +0000 (14:05 +0000)
data/template.tmpl
data/template_include.tmpl
expected/tmpl
template.c
template.h
tmpl_gram.y
tmpl_scan.l
tmpltest.c

index 774a0b3..95e0060 100644 (file)
@@ -1,43 +1,57 @@
 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)
@@ -46,7 +60,7 @@ zero def: <% zeroID || "zeroID"  %><# COMENT
 
 
 <@ 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 @>
index e7b52cf..a4a6839 100644 (file)
@@ -1 +1 @@
-<@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 %>
index d7dd7a7..57095bd 100644 (file)
@@ -30,6 +30,20 @@ DEFINED zeroID-YES
 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
@@ -96,6 +110,20 @@ DEFINED zeroID-YES
 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
index 6561ee1..d85a610 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,11 @@ 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:
                        break;
                default:
                        tlog(TL_CRIT|TL_EXIT, "unknown node type: %d", (*node)->type);
@@ -247,6 +528,27 @@ addLoop(Template tmpl, TemplateNode node, TemplateNode loopParentNode) {
        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;
@@ -266,20 +568,8 @@ compileTree( Template tmpl, TemplateNode node, TemplateNode loopParentNode ) {
                                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) )
@@ -308,9 +598,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 +627,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 +639,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 +663,10 @@ initTemplate( Template tmpl, MemoryContext *mc, char *basedir, char *filename )
        return 0;
 }
 
+/*
+ * Reset/cleanup
+ */
+
 void
 resetTemplate( Template tmpl ) {
        SFSDataIO       out;
@@ -399,12 +725,22 @@ 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    VariableNode:
+               case    IncludeNode:
                case    TextNode:
+               case    ConstNode:
                default:
                        break;
        }
@@ -416,6 +752,10 @@ freeTemplate( Template tmpl ) {
        freeNode( tmpl->tree );
 }
 
+/*
+ * Set value routines
+ */
+
 static void
 newLoopInstance( Template tmpl, TemplateNode node ) {
        node->nodeData.loop.listInstance = GListPush( 
@@ -559,6 +899,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,34 +971,49 @@ 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:
+                       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
@@ -729,36 +1088,25 @@ printNode( Template tmpl, TemplateNode node ) {
                        }
                        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);
@@ -769,16 +1117,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;
        }
 }
@@ -793,38 +1147,57 @@ printTemplate( Template tmpl ) {
        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);
index 72a57e5..0e12052 100644 (file)
 /******************************************************************************
  *                                  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__
@@ -66,6 +118,9 @@ typedef      enum TemplateNodeType {
        LoopNode,
        ConditionNode,
        CollectionNode,
+       ExpressionNode,
+       PrintNode,
+       ConstNode,
 
        /* value's types of variables */
        valueInt = 200, /* smallest of any values type */
@@ -76,20 +131,23 @@ typedef    enum TemplateNodeType {
        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;
@@ -107,6 +165,14 @@ typedef struct VariableValueData {
        } 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;
@@ -129,10 +195,29 @@ typedef struct  TemplateNodeData {
                        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;
 
@@ -149,10 +234,7 @@ typedef struct  TemplateNodeData {
 
                /* ConditionNode */
                struct {
-                       int                     flags;
-                       char                    *varName;
-                       int                     varNameLength;
-                       VariableValue   value;
+                       TemplateNode    expressionNode;
                        TemplateNode    ifNode;
                        TemplateNode    elseNode;
                } condition;
@@ -170,18 +252,17 @@ typedef char*     (*urlEscapeFn)(char *, int * /* in/out */);
 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 );
index 3730660..3335903 100644 (file)
@@ -16,30 +16,33 @@ void startTemplateLex(Template tmpl, FILE *in);
 
 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
@@ -47,7 +50,22 @@ extern int tmpl_yylineno;
 %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
 
 %%
 
@@ -80,8 +98,6 @@ varname:
        | LOOP_P
        | ENDIF_P
        | ENDLOOP_P
-       | NOT_P
-       | DEFINED_P
        ;
 
 opt_escape:
@@ -90,52 +106,103 @@ 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:  
@@ -145,14 +212,13 @@ 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) );
@@ -166,12 +232,16 @@ node:
                                $$->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;
                }
@@ -208,3 +278,19 @@ parseTemplateFile(Template tmpl, char* filename ) {
        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 );
+}
index 4379919..318736c 100644 (file)
@@ -36,8 +36,10 @@ static MemoryContext *lexContext;
 %x  xQUOTED
 %x  xCOMMENT
 
+LEXEMCHARSTART         [a-zA-Z_]
 LEXEMCHAR      [a-zA-Z0-9_]
 PATH           [a-zA-Z0-9_/\.]
+DIGIT          [0-9]
 
 %%
 
@@ -60,14 +62,7 @@ PATH                 [a-zA-Z0-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;
                                        }
@@ -101,11 +96,47 @@ PATH               [a-zA-Z0-9_/\.]
                                                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;
@@ -172,11 +203,9 @@ static KeyWord keywords[] = {
        { "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       } 
 };
 
index 5c3986f..c5aef19 100644 (file)
@@ -31,6 +31,7 @@
 #include <string.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <locale.h>
 
 
 #include "tmalloc.h"
@@ -42,7 +43,7 @@ static void
 usage() {
        puts(
        "Usage:\n"
-       "memtest [-t TEMPLATENAME]\n"
+       "tmpltest [-t TEMPLATENAME]\n"
        );
        exit(1);
 }
@@ -53,6 +54,19 @@ outfunc(char *str, int len) {
        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;
 
@@ -62,6 +76,12 @@ main(int argn, char *argv[]) {
        char                    *name = NULL;
        TemplateData    template;
        int                     i;
+       executeFunctionDescData funcs[] = {
+               {"callcounter", 0, localCounter},
+               {NULL,0,NULL}
+       };
+
+
 
        opentlog(TL_OPEN_STDERR,TL_DEBUG, NULL);
        opterr=0;
@@ -81,14 +101,16 @@ main(int argn, char *argv[]) {
        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");