%{ #include #include #include #include #include #include extern char *fileToParse; char *fileToParse; int yylex(void); int yyparse(void); static int yyerror(char *s); 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 flags; int64_t intval; double floatval; TemplateNode node; GList *list; } %type node %type ifnode elsenode elsifnode %type listnodes %type template %type expression %type list_expression %type varname %type elsif %type opt_escape %type opt_format %type opt_default %token STRING %token FILENAME %token TEXT_P %token LEXEME %token VAR_OPEN VAR_CLOSE EXPR_OPEN EXPR_CLOSE INCLUDE_OPEN INCLUDE_CLOSE %token HTMLESCAPE URLESCAPE IF_P ELSIF_P ELSE_P LOOP_P ENDIF_P ENDLOOP_P SELF_P %token CMP_P %token INTEGER %token DOUBLE %left '+' '-' %left '*' '/' '%' %left '?' ':' %left NEG %left OR_P %left AND_P %left NOT_P %left CMP_P %% template: listnodes { curTmpl->tree = $$ = $1; } | { curTmpl->tree = $$ = NULL; } ; listnodes: node { $$ = $1; } | listnodes node { if ( $1->type == CollectionNode ) { GListPush( $1->nodeData.children, $2 ); $$ = $1; } else { $$ = mc0alloc( curTmpl->templateContext, sizeof(TemplateNodeData) ); $$->type = CollectionNode; $$->nodeData.children = GListPush( $$->nodeData.children, $1 ); $$->nodeData.children = GListPush( $$->nodeData.children, $2 ); } } ; varname: LEXEME | HTMLESCAPE | URLESCAPE | IF_P | ELSE_P | LOOP_P | SELF_P | ENDIF_P | ENDLOOP_P ; opt_escape: '|' HTMLESCAPE { $$=TND_HTMLESCAPE; } | '|' URLESCAPE { $$=TND_URLESCAPE; } | { $$=0; } ; opt_format: ',' STRING { $$=$2; } | { $$=NULL; } ; opt_default: '#' STRING { $$=$2; } | { $$=NULL; } ; list_expression: expression ',' expression { $$ = makeList2($1, $3); } | list_expression ',' expression { $$ = GListPush($$, $3); } ; 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; } | 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 '%' 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; } ; elsif: ELSIF_P | ELSE_P IF_P ; elsenode: EXPR_OPEN ELSE_P EXPR_CLOSE listnodes EXPR_OPEN ENDIF_P EXPR_CLOSE { $$ = $4; } ; elsifnode: EXPR_OPEN elsif 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 elsif expression EXPR_CLOSE listnodes elsenode { $$ = mc0alloc( curTmpl->templateContext, sizeof(TemplateNodeData) ); $$->type = ConditionNode; $$->nodeData.condition.expressionNode = $3; $$->nodeData.condition.ifNode = $5; $$->nodeData.condition.elseNode = $6; } | EXPR_OPEN elsif expression EXPR_CLOSE listnodes elsifnode { $$ = mc0alloc( curTmpl->templateContext, sizeof(TemplateNodeData) ); $$->type = ConditionNode; $$->nodeData.condition.expressionNode = $3; $$->nodeData.condition.ifNode = $5; $$->nodeData.condition.elseNode = $6; } ; ifnode: 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 expression EXPR_CLOSE listnodes elsenode { $$ = mc0alloc( curTmpl->templateContext, sizeof(TemplateNodeData) ); $$->type = ConditionNode; $$->nodeData.condition.expressionNode = $3; $$->nodeData.condition.ifNode = $5; $$->nodeData.condition.elseNode = $6; } | EXPR_OPEN IF_P expression EXPR_CLOSE listnodes elsifnode { $$ = mc0alloc( curTmpl->templateContext, sizeof(TemplateNodeData) ); $$->type = ConditionNode; $$->nodeData.condition.expressionNode = $3; $$->nodeData.condition.ifNode = $5; $$->nodeData.condition.elseNode = $6; } ; node: TEXT_P { $$ = mc0alloc( curTmpl->templateContext, sizeof(TemplateNodeData) ); $$->type = TextNode; $$->nodeData.text.value = $1; $$->nodeData.text.valueLength = strlen($1); } | VAR_OPEN expression opt_format opt_default opt_escape VAR_CLOSE { $$ = mc0alloc( curTmpl->templateContext, sizeof(TemplateNodeData) ); $$->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) ); $$->type = IncludeNode; $$->nodeData.includeFile = $2; } | EXPR_OPEN SELF_P EXPR_CLOSE { $$ = mc0alloc( curTmpl->templateContext, sizeof(TemplateNodeData) ); $$->type = NestNode; } | EXPR_OPEN LOOP_P varname EXPR_CLOSE listnodes EXPR_OPEN ENDLOOP_P EXPR_CLOSE { $$ = mc0alloc( curTmpl->templateContext, sizeof(TemplateNodeData) ); $$->type = LoopNode; $$->nodeData.loop.varName = $3; $$->nodeData.loop.varNameLength = strlen($3); $$->nodeData.loop.bodyNode = $5; } | ifnode {$$=$1;} ; %% static int yyerror(char *s) { tlog(TL_CRIT,"template error at line %d in '%s': %s", tmpl_yylineno, fileToParse, s); return 1; } int parseTemplateFile(Template tmpl, char* filename ) { FILE *in = fopen(filename, "r"); int err; if ( in == NULL ) { tlog(TL_CRIT,"Can not open template file '%s': %s", filename, strerror(errno)); return 3; } curTmpl = tmpl; curTmpl->tree = NULL; fileToParse = filename; startTemplateLex(tmpl, in); err = yyparse(); fclose(in); 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 ); }