2 * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
25 #define MAX_RULE_LENGTH 2048
27 /* define to enable some more debug */
31 gst_asm_node_new (void)
35 node = g_new0 (GstASMNode, 1);
36 node->type = GST_ASM_NODE_UNKNOWN;
42 gst_asm_node_free (GstASMNode * node)
45 gst_asm_node_free (node->left);
47 gst_asm_node_free (node->right);
48 if (node->type == GST_ASM_NODE_VARIABLE && node->data.varname)
49 g_free (node->data.varname);
54 gst_asm_operator_eval (GstASMOp optype, gfloat left, gfloat right)
59 case GST_ASM_OP_GREATER:
60 result = (gfloat) (left > right);
63 result = (gfloat) (left < right);
65 case GST_ASM_OP_GREATEREQUAL:
66 result = (gfloat) (left >= right);
68 case GST_ASM_OP_LESSEQUAL:
69 result = (gfloat) (left <= right);
71 case GST_ASM_OP_EQUAL:
72 result = (gfloat) (left == right);
74 case GST_ASM_OP_NOTEQUAL:
75 result = (gfloat) (left != right);
78 result = (gfloat) (left && right);
81 result = (gfloat) (left || right);
90 gst_asm_node_evaluate (GstASMNode * node, GHashTable * vars)
98 case GST_ASM_NODE_VARIABLE:
102 val = g_hash_table_lookup (vars, node->data.varname);
104 result = (gfloat) atof (val);
107 case GST_ASM_NODE_INTEGER:
108 result = (gfloat) node->data.intval;
110 case GST_ASM_NODE_FLOAT:
111 result = node->data.floatval;
113 case GST_ASM_NODE_OPERATOR:
117 left = gst_asm_node_evaluate (node->left, vars);
118 right = gst_asm_node_evaluate (node->right, vars);
120 result = gst_asm_operator_eval (node->data.optype, left, right);
129 #define IS_SPACE(p) (((p) == ' ') || ((p) == '\n') || \
130 ((p) == '\r') || ((p) == '\t'))
131 #define IS_RULE_DELIM(p) (((p) == ',') || ((p) == ';') || ((p) == ')'))
132 #define IS_OPERATOR(p) (((p) == '>') || ((p) == '<') || \
133 ((p) == '=') || ((p) == '!') || \
134 ((p) == '&') || ((p) == '|'))
135 #define IS_NUMBER(p) ((((p) >= '0') && ((p) <= '9')) || ((p) == '.'))
136 #define IS_CHAR(p) (!IS_OPERATOR(ch) && !IS_RULE_DELIM(ch) && (ch != '\0'))
138 #define IS_OP_TOKEN(t) (((t) == GST_ASM_TOKEN_AND) || ((t) == GST_ASM_TOKEN_OR))
139 #define IS_COND_TOKEN(t) (((t) == GST_ASM_TOKEN_LESS) || ((t) == GST_ASM_TOKEN_LESSEQUAL) || \
140 ((t) == GST_ASM_TOKEN_GREATER) || ((t) == GST_ASM_TOKEN_GREATEREQUAL) || \
141 ((t) == GST_ASM_TOKEN_EQUAL) || ((t) == GST_ASM_TOKEN_NOTEQUAL))
150 gchar val[MAX_RULE_LENGTH];
153 #define NEXT_CHAR(scan) ((scan)->ch = (scan)->buffer[(scan)->pos++])
154 #define THIS_CHAR(scan) ((scan)->ch)
157 gst_asm_scan_new (const gchar * buffer)
161 scan = g_new0 (GstASMScan, 1);
162 scan->buffer = buffer;
169 gst_asm_scan_free (GstASMScan * scan)
175 gst_asm_scan_string (GstASMScan * scan, gchar delim)
180 ch = THIS_CHAR (scan);
181 while ((ch != delim) && (ch != '\0')) {
182 if (i < MAX_RULE_LENGTH - 1)
184 ch = NEXT_CHAR (scan);
186 ch = NEXT_CHAR (scan);
193 scan->token = GST_ASM_TOKEN_STRING;
197 gst_asm_scan_number (GstASMScan * scan)
201 gboolean have_float = FALSE;
203 ch = THIS_CHAR (scan);
204 /* real strips all spaces that are not inside quotes for numbers */
205 while ((IS_NUMBER (ch) || IS_SPACE (ch))) {
206 if (i < (MAX_RULE_LENGTH - 1) && !IS_SPACE (ch))
210 ch = NEXT_CHAR (scan);
215 scan->token = GST_ASM_TOKEN_FLOAT;
217 scan->token = GST_ASM_TOKEN_INT;
221 gst_asm_scan_identifier (GstASMScan * scan)
226 ch = THIS_CHAR (scan);
227 /* real strips all spaces that are not inside quotes for identifiers */
228 while ((IS_CHAR (ch) || IS_SPACE (ch))) {
229 if (i < (MAX_RULE_LENGTH - 1) && !IS_SPACE (ch))
231 ch = NEXT_CHAR (scan);
235 scan->token = GST_ASM_TOKEN_IDENTIFIER;
239 gst_asm_scan_print_token (GstASMScan * scan)
242 switch (scan->token) {
243 case GST_ASM_TOKEN_NONE:
246 case GST_ASM_TOKEN_EOF:
250 case GST_ASM_TOKEN_INT:
251 g_print ("INT %d\n", atoi (scan->val));
253 case GST_ASM_TOKEN_FLOAT:
254 g_print ("FLOAT %f\n", atof (scan->val));
256 case GST_ASM_TOKEN_IDENTIFIER:
257 g_print ("ID %s\n", scan->val);
259 case GST_ASM_TOKEN_STRING:
260 g_print ("STRING %s\n", scan->val);
263 case GST_ASM_TOKEN_HASH:
266 case GST_ASM_TOKEN_SEMICOLON:
267 g_print ("SEMICOLON\n");
269 case GST_ASM_TOKEN_COMMA:
272 case GST_ASM_TOKEN_EQUAL:
275 case GST_ASM_TOKEN_NOTEQUAL:
278 case GST_ASM_TOKEN_AND:
281 case GST_ASM_TOKEN_OR:
284 case GST_ASM_TOKEN_LESS:
287 case GST_ASM_TOKEN_LESSEQUAL:
290 case GST_ASM_TOKEN_GREATER:
293 case GST_ASM_TOKEN_GREATEREQUAL:
296 case GST_ASM_TOKEN_DOLLAR:
299 case GST_ASM_TOKEN_LPAREN:
302 case GST_ASM_TOKEN_RPAREN:
312 gst_asm_scan_next_token (GstASMScan * scan)
316 ch = THIS_CHAR (scan);
319 while (IS_SPACE (ch))
320 ch = NEXT_CHAR (scan);
322 /* remove \ which is common in front of " */
324 ch = NEXT_CHAR (scan);
328 scan->token = GST_ASM_TOKEN_HASH;
332 scan->token = GST_ASM_TOKEN_SEMICOLON;
336 scan->token = GST_ASM_TOKEN_COMMA;
340 scan->token = GST_ASM_TOKEN_EQUAL;
341 if (NEXT_CHAR (scan) == '=')
345 if (NEXT_CHAR (scan) == '=') {
346 scan->token = GST_ASM_TOKEN_NOTEQUAL;
351 scan->token = GST_ASM_TOKEN_AND;
352 if (NEXT_CHAR (scan) == '&')
356 scan->token = GST_ASM_TOKEN_OR;
357 if (NEXT_CHAR (scan) == '|')
361 scan->token = GST_ASM_TOKEN_LESS;
362 if (NEXT_CHAR (scan) == '=') {
363 scan->token = GST_ASM_TOKEN_LESSEQUAL;
368 scan->token = GST_ASM_TOKEN_GREATER;
369 if (NEXT_CHAR (scan) == '=') {
370 scan->token = GST_ASM_TOKEN_GREATEREQUAL;
375 scan->token = GST_ASM_TOKEN_DOLLAR;
379 scan->token = GST_ASM_TOKEN_LPAREN;
383 scan->token = GST_ASM_TOKEN_RPAREN;
388 gst_asm_scan_string (scan, '"');
392 gst_asm_scan_string (scan, '\'');
404 gst_asm_scan_number (scan);
407 scan->token = GST_ASM_TOKEN_EOF;
410 gst_asm_scan_identifier (scan);
413 gst_asm_scan_print_token (scan);
418 gst_asm_rule_new (void)
422 rule = g_new (GstASMRule, 1);
424 rule->props = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
430 gst_asm_rule_free (GstASMRule * rule)
432 g_hash_table_destroy (rule->props);
434 gst_asm_node_free (rule->root);
439 gst_asm_rule_add_property (GstASMRule * rule, gchar * key, gchar * val)
441 g_hash_table_insert (rule->props, key, val);
444 static GstASMNode *gst_asm_scan_parse_condition (GstASMScan * scan);
447 gst_asm_scan_parse_operand (GstASMScan * scan)
451 switch (scan->token) {
452 case GST_ASM_TOKEN_DOLLAR:
453 gst_asm_scan_next_token (scan);
455 if (scan->token != GST_ASM_TOKEN_IDENTIFIER)
456 g_warning ("identifier expected");
458 node = gst_asm_node_new ();
459 node->type = GST_ASM_NODE_VARIABLE;
460 node->data.varname = g_strdup (scan->val);
462 case GST_ASM_TOKEN_INT:
463 node = gst_asm_node_new ();
464 node->type = GST_ASM_NODE_INTEGER;
465 node->data.intval = (gfloat) atof (scan->val);
467 case GST_ASM_TOKEN_FLOAT:
468 node = gst_asm_node_new ();
469 node->type = GST_ASM_NODE_FLOAT;
470 node->data.floatval = atoi (scan->val);
472 case GST_ASM_TOKEN_LPAREN:
473 gst_asm_scan_next_token (scan);
474 node = gst_asm_scan_parse_condition (scan);
475 if (scan->token != GST_ASM_TOKEN_RPAREN)
476 g_warning (") expected");
479 g_warning ("$ <number> or ) expected");
483 gst_asm_scan_next_token (scan);
489 gst_asm_scan_parse_expression (GstASMScan * scan)
491 GstASMNode *node, *left;
493 node = gst_asm_scan_parse_operand (scan);
495 while (IS_COND_TOKEN (scan->token)) {
498 node = gst_asm_node_new ();
499 node->type = GST_ASM_NODE_OPERATOR;
500 node->data.optype = (GstASMOp) scan->token;
502 gst_asm_scan_next_token (scan);
504 node->right = gst_asm_scan_parse_operand (scan);
511 gst_asm_scan_parse_condition (GstASMScan * scan)
513 GstASMNode *node, *left;
515 node = gst_asm_scan_parse_expression (scan);
517 while (IS_OP_TOKEN (scan->token)) {
520 node = gst_asm_node_new ();
521 node->type = GST_ASM_NODE_OPERATOR;
522 node->data.optype = (GstASMOp) scan->token;
524 gst_asm_scan_next_token (scan);
526 node->right = gst_asm_scan_parse_expression (scan);
533 gst_asm_scan_parse_property (GstASMRule * rule, GstASMScan * scan)
537 if (scan->token != GST_ASM_TOKEN_IDENTIFIER) {
538 g_warning ("identifier expected");
541 key = g_strdup (scan->val);
543 gst_asm_scan_next_token (scan);
544 if (scan->token != GST_ASM_TOKEN_EQUAL) {
545 g_warning ("= expected");
549 gst_asm_scan_next_token (scan);
550 val = g_strdup (scan->val);
552 gst_asm_rule_add_property (rule, key, val);
553 gst_asm_scan_next_token (scan);
557 gst_asm_scan_parse_rule (GstASMScan * scan)
561 rule = gst_asm_rule_new ();
563 if (scan->token == GST_ASM_TOKEN_HASH) {
564 gst_asm_scan_next_token (scan);
565 rule->root = gst_asm_scan_parse_condition (scan);
566 if (scan->token == GST_ASM_TOKEN_COMMA)
567 gst_asm_scan_next_token (scan);
570 if (scan->token != GST_ASM_TOKEN_SEMICOLON) {
571 gst_asm_scan_parse_property (rule, scan);
572 while (scan->token == GST_ASM_TOKEN_COMMA) {
573 gst_asm_scan_next_token (scan);
574 gst_asm_scan_parse_property (rule, scan);
576 gst_asm_scan_next_token (scan);
582 gst_asm_rule_evaluate (GstASMRule * rule, GHashTable * vars)
587 res = (gboolean) gst_asm_node_evaluate (rule->root, vars);
595 gst_asm_rule_book_new (const gchar * rulebook)
597 GstASMRuleBook *book;
598 GstASMRule *rule = NULL;
602 book = g_new0 (GstASMRuleBook, 1);
603 book->rulebook = rulebook;
605 scan = gst_asm_scan_new (book->rulebook);
606 gst_asm_scan_next_token (scan);
609 rule = gst_asm_scan_parse_rule (scan);
611 book->rules = g_list_append (book->rules, rule);
615 } while (token != GST_ASM_TOKEN_EOF);
617 gst_asm_scan_free (scan);
623 gst_asm_rule_book_free (GstASMRuleBook * book)
627 for (walk = book->rules; walk; walk = g_list_next (walk)) {
628 GstASMRule *rule = (GstASMRule *) walk->data;
630 gst_asm_rule_free (rule);
632 g_list_free (book->rules);
637 gst_asm_rule_book_match (GstASMRuleBook * book, GHashTable * vars,
643 for (walk = book->rules, i = 0; walk; walk = g_list_next (walk), i++) {
644 GstASMRule *rule = (GstASMRule *) walk->data;
646 if (gst_asm_rule_evaluate (rule, vars)) {
647 rulematches[n++] = i;
655 main (gint argc, gchar * argv[])
657 GstASMRuleBook *book;
658 gint rulematch[MAX_RULEMATCHES];
662 static const gchar rules1[] =
663 "#($Bandwidth < 67959),TimestampDelivery=T,DropByN=T,"
664 "priority=9;#($Bandwidth >= 67959) && ($Bandwidth < 167959),"
665 "AverageBandwidth=67959,Priority=9;#($Bandwidth >= 67959) && ($Bandwidth"
666 " < 167959),AverageBandwidth=0,Priority=5,OnDepend=\\\"1\\\";#($Bandwidth >= 167959)"
667 " && ($Bandwidth < 267959),AverageBandwidth=167959,Priority=9;#($Bandwidth >= 167959)"
668 " && ($Bandwidth < 267959),AverageBandwidth=0,Priority=5,OnDepend=\\\"3\\\";"
669 "#($Bandwidth >= 267959),AverageBandwidth=267959,Priority=9;#($Bandwidth >= 267959)"
670 ",AverageBandwidth=0,Priority=5,OnDepend=\\\"5\\\";";
671 static const gchar rules2[] =
672 "AverageBandwidth=32041,Priority=5;AverageBandwidth=0,"
673 "Priority=5,OnDepend=\\\"0\\\", OffDepend=\\\"0\\\";";
674 static const gchar rules3[] =
675 "#(($Bandwidth >= 27500) && ($OldPNMPlayer)),AverageBandwidth=27500,priority=9,PNMKeyframeRule=T;#(($Bandwidth >= 27500) && ($OldPNMPlayer)),AverageBandwidth=0,priority=5,PNMNonKeyframeRule=T;#(($Bandwidth < 27500) && ($OldPNMPlayer)),TimestampDelivery=T,DropByN=T,priority=9,PNMThinningRule=T;#($Bandwidth < 13899),TimestampDelivery=T,DropByN=T,priority=9;#($Bandwidth >= 13899) && ($Bandwidth < 19000),AverageBandwidth=13899,Priority=9;#($Bandwidth >= 13899) && ($Bandwidth < 19000),AverageBandwidth=0,Priority=5,OnDepend=\\\"4\\\";#($Bandwidth >= 19000) && ($Bandwidth < 27500),AverageBandwidth=19000,Priority=9;#($Bandwidth >= 19000) && ($Bandwidth < 27500),AverageBandwidth=0,Priority=5,OnDepend=\\\"6\\\";#($Bandwidth >= 27500) && ($Bandwidth < 132958),AverageBandwidth=27500,Priority=9;#($Bandwidth >= 27500) && ($Bandwidth < 132958),AverageBandwidth=0,Priority=5,OnDepend=\\\"8\\\";#($Bandwidth >= 132958) && ($Bandwidth < 187958),AverageBandwidth=132958,Priority=9;#($Bandwidth >= 132958) && ($Bandwidth < 187958),AverageBandwidth=0,Priority=5,OnDepend=\\\"10\\\";#($Bandwidth >= 187958),AverageBandwidth=187958,Priority=9;#($Bandwidth >= 187958),AverageBandwidth=0,Priority=5,OnDepend=\\\"12\\\";";
677 vars = g_hash_table_new (g_str_hash, g_str_equal);
678 g_hash_table_insert (vars, (gchar *) "Bandwidth", (gchar *) "300000");
680 book = gst_asm_rule_book_new (rules1);
681 n = gst_asm_rule_book_match (book, vars, rulematch);
682 gst_asm_rule_book_free (book);
684 g_print ("%d rules matched\n", n);
685 for (i = 0; i < n; i++) {
686 g_print ("rule %d matched\n", rulematch[i]);
689 book = gst_asm_rule_book_new (rules2);
690 n = gst_asm_rule_book_match (book, vars, rulematch);
691 gst_asm_rule_book_free (book);
693 g_print ("%d rules matched\n", n);
694 for (i = 0; i < n; i++) {
695 g_print ("rule %d matched\n", rulematch[i]);
698 book = gst_asm_rule_book_new (rules3);
699 n = gst_asm_rule_book_match (book, vars, rulematch);
700 gst_asm_rule_book_free (book);
703 g_print ("%d rules matched\n", n);
704 for (i = 0; i < n; i++) {
705 g_print ("rule %d matched\n", rulematch[i]);
708 g_hash_table_destroy (vars);