Преглед изворни кода

ncd: allow literal list arguments with curly brackets

ambrop7 пре 14 година
родитељ
комит
ad09c747fc

+ 39 - 22
examples/ncd_parser_test.c

@@ -30,6 +30,44 @@
 
 int error;
 
+static void print_indent (unsigned int indent)
+{
+    while (indent > 0) {
+        printf("  ");
+        indent--;
+    }
+}
+
+static void print_list (struct NCDConfig_list *l, unsigned int indent)
+{
+    while (l) {
+        print_indent(indent);
+        switch (l->type) {
+            case NCDCONFIG_ARG_STRING: {
+                printf("string: %s\n", l->string);
+            } break;
+            case NCDCONFIG_ARG_VAR: {
+                printf("var: ");
+                struct NCDConfig_strings *n = l->var;
+                printf("%s", n->value);
+                n = n->next;
+                while (n) {
+                    printf(".%s", n->value);
+                    n = n->next;
+                }
+                printf("\n");
+            } break;
+            case NCDCONFIG_ARG_LIST: {
+                printf("list\n");
+                print_list(l->list, indent + 1);
+            } break;
+            default:
+                ASSERT(0);
+        }
+        l = l->next;
+    }
+}
+
 int main (int argc, char **argv)
 {
     if (argc < 1) {
@@ -69,28 +107,7 @@ int main (int argc, char **argv)
             
             printf("\n");
             
-            struct NCDConfig_list *arg = st->args;
-            while (arg) {
-                switch (arg->type) {
-                    case NCDCONFIG_ARG_STRING:
-                        printf("    string: %s\n", arg->string);
-                        break;
-                    case NCDCONFIG_ARG_VAR:
-                        printf("    var: ");
-                        struct NCDConfig_strings *n = arg->var;
-                        printf("%s", n->value);
-                        n = n->next;
-                        while (n) {
-                            printf(".%s", n->value);
-                            n = n->next;
-                        }
-                        printf("\n");
-                        break;
-                    default:
-                        ASSERT(0);
-                }
-                arg = arg->next;
-            }
+            print_list(st->args, 2);
             
             st = st->next;
         }

+ 195 - 142
generated/NCDConfigParser_parse.c

@@ -70,18 +70,18 @@ struct parser_out {
 **                       defined, then do no error processing.
 */
 #define YYCODETYPE unsigned char
-#define YYNOCODE 23
+#define YYNOCODE 24
 #define YYACTIONTYPE unsigned char
 #define ParseTOKENTYPE void *
 typedef union {
   int yyinit;
   ParseTOKENTYPE yy0;
-  char * yy9;
-  int yy20;
-  struct NCDConfig_list * yy30;
-  struct NCDConfig_statements * yy38;
-  struct NCDConfig_strings * yy40;
+  int yy6;
+  struct NCDConfig_strings * yy10;
+  struct NCDConfig_list * yy24;
+  struct NCDConfig_statements * yy34;
   struct NCDConfig_processes * yy41;
+  char * yy45;
 } YYMINORTYPE;
 #ifndef YYSTACKDEPTH
 #define YYSTACKDEPTH 0
@@ -90,8 +90,8 @@ typedef union {
 #define ParseARG_PDECL ,struct parser_out *parser_out
 #define ParseARG_FETCH struct parser_out *parser_out = yypParser->parser_out
 #define ParseARG_STORE yypParser->parser_out = parser_out
-#define YYNSTATE 36
-#define YYNRULE 19
+#define YYNSTATE 43
+#define YYNRULE 23
 #define YY_NO_ACTION      (YYNSTATE+YYNRULE+2)
 #define YY_ACCEPT_ACTION  (YYNSTATE+YYNRULE+1)
 #define YY_ERROR_ACTION   (YYNSTATE+YYNRULE)
@@ -161,37 +161,42 @@ static const YYMINORTYPE yyzerominor = { 0 };
 **  yy_default[]       Default action for each state.
 */
 static const YYACTIONTYPE yy_action[] = {
- /*     0 */    14,   22,   18,   32,   27,   28,   15,   56,   22,   24,
- /*    10 */    32,   20,   26,   22,   20,   34,   17,   13,   15,   21,
- /*    20 */    22,   31,   33,   29,   13,    1,   35,   13,   11,   36,
- /*    30 */    30,   19,   23,   16,   25,    6,    9,    3,   10,   57,
- /*    40 */    57,    7,    2,   57,   12,   57,    4,    5,    8,
+ /*     0 */    24,   20,   36,   25,   24,   28,   36,   25,   22,    3,
+ /*    10 */    38,   16,   22,    3,   31,   32,   23,   34,   17,   67,
+ /*    20 */    23,   24,   22,   26,   25,   24,   35,   41,   25,   24,
+ /*    30 */    30,   40,   25,   24,   21,   37,   25,   17,   27,   19,
+ /*    40 */    15,   33,   15,   42,   15,    1,   43,   29,   13,   18,
+ /*    50 */     7,    8,   12,    4,   68,   11,   68,    9,    2,   39,
+ /*    60 */    68,   68,    5,    6,   14,   68,   10,
 };
 static const YYCODETYPE yy_lookahead[] = {
- /*     0 */    14,   16,   17,   18,   11,   12,   20,   21,   16,   17,
- /*    10 */    18,    1,   14,   16,    1,   18,   15,   16,   20,    9,
- /*    20 */    16,    1,   18,   15,   16,    4,   15,   16,    7,    0,
- /*    30 */    16,   19,   16,    1,   19,    2,    5,    3,    8,   22,
- /*    40 */    22,    6,    4,   22,    5,   22,   10,   10,    6,
+ /*     0 */    16,   17,   18,   19,   16,   17,   18,   19,    1,    2,
+ /*    10 */     3,   14,    1,    2,   11,   12,    9,   16,   21,   22,
+ /*    20 */     9,   16,    1,   18,   19,   16,    1,   18,   19,   16,
+ /*    30 */    14,   18,   19,   16,   20,   18,   19,   21,   16,   15,
+ /*    40 */    16,   15,   16,   15,   16,    4,    0,   20,    7,    1,
+ /*    50 */     3,    2,    8,   10,   23,    5,   23,    6,    4,    3,
+ /*    60 */    23,   23,   10,   10,    5,   23,    6,
 };
-#define YY_SHIFT_USE_DFLT (-8)
-#define YY_SHIFT_MAX 25
+#define YY_SHIFT_USE_DFLT (-1)
+#define YY_SHIFT_MAX 29
 static const signed char yy_shift_ofst[] = {
- /*     0 */    -7,   10,   10,   -7,   10,   10,   13,   13,   13,   20,
- /*    10 */    13,   13,   20,   21,   29,   32,   33,   34,   31,   35,
- /*    20 */    30,   36,   37,   38,   39,   42,
+ /*     0 */     3,   11,   11,    7,   11,   11,   11,    3,   21,   21,
+ /*    10 */    21,   25,   21,   21,   25,   41,   46,   48,   49,   47,
+ /*    20 */    50,   51,   44,   43,   52,   53,   56,   54,   59,   60,
 };
-#define YY_REDUCE_USE_DFLT (-16)
-#define YY_REDUCE_MAX 12
+#define YY_REDUCE_USE_DFLT (-17)
+#define YY_REDUCE_MAX 14
 static const signed char yy_reduce_ofst[] = {
- /*     0 */   -14,  -15,   -8,   -2,   -3,    4,    1,    8,   11,   12,
- /*    10 */    14,   16,   15,
+ /*     0 */    -3,  -16,  -12,    5,    9,   13,   17,   16,   24,   26,
+ /*    10 */    28,   14,    1,   22,   27,
 };
 static const YYACTIONTYPE yy_default[] = {
- /*     0 */    55,   45,   45,   37,   55,   55,   55,   39,   41,   51,
- /*    10 */    55,   55,   51,   55,   55,   55,   55,   55,   55,   55,
- /*    20 */    43,   47,   48,   55,   55,   55,   38,   53,   54,   40,
- /*    30 */    44,   52,   46,   50,   49,   42,
+ /*     0 */    66,   52,   52,   66,   66,   66,   66,   44,   66,   46,
+ /*    10 */    48,   62,   66,   66,   62,   66,   66,   66,   66,   66,
+ /*    20 */    66,   66,   50,   54,   55,   56,   66,   66,   66,   66,
+ /*    30 */    45,   64,   65,   47,   51,   63,   53,   59,   60,   61,
+ /*    40 */    58,   57,   49,
 };
 #define YY_SZ_ACTTAB (int)(sizeof(yy_action)/sizeof(yy_action[0]))
 
@@ -289,8 +294,8 @@ static const char *const yyTokenName[] = {
   "ROUND_OPEN",    "ROUND_CLOSE",   "SEMICOLON",     "ARROW",       
   "DOT",           "STRING",        "COMMA",         "PROCESS",     
   "TEMPLATE",      "error",         "processes",     "statements",  
-  "statement_names",  "statement_args_maybe",  "statement_args",  "name_maybe",  
-  "process_or_template",  "input",       
+  "statement_names",  "statement_args_maybe",  "list_contents",  "list",        
+  "name_maybe",    "process_or_template",  "input",       
 };
 #endif /* NDEBUG */
 
@@ -308,15 +313,19 @@ static const char *const yyRuleName[] = {
  /*   7 */ "statement_names ::= NAME",
  /*   8 */ "statement_names ::= NAME DOT statement_names",
  /*   9 */ "statement_args_maybe ::=",
- /*  10 */ "statement_args_maybe ::= statement_args",
- /*  11 */ "statement_args ::= STRING",
- /*  12 */ "statement_args ::= statement_names",
- /*  13 */ "statement_args ::= STRING COMMA statement_args",
- /*  14 */ "statement_args ::= statement_names COMMA statement_args",
- /*  15 */ "name_maybe ::=",
- /*  16 */ "name_maybe ::= NAME",
- /*  17 */ "process_or_template ::= PROCESS",
- /*  18 */ "process_or_template ::= TEMPLATE",
+ /*  10 */ "statement_args_maybe ::= list_contents",
+ /*  11 */ "list_contents ::= STRING",
+ /*  12 */ "list_contents ::= statement_names",
+ /*  13 */ "list_contents ::= list",
+ /*  14 */ "list_contents ::= STRING COMMA list_contents",
+ /*  15 */ "list_contents ::= statement_names COMMA list_contents",
+ /*  16 */ "list_contents ::= list COMMA list_contents",
+ /*  17 */ "list ::= CURLY_OPEN CURLY_CLOSE",
+ /*  18 */ "list ::= CURLY_OPEN list_contents CURLY_CLOSE",
+ /*  19 */ "name_maybe ::=",
+ /*  20 */ "name_maybe ::= NAME",
+ /*  21 */ "process_or_template ::= PROCESS",
+ /*  22 */ "process_or_template ::= TEMPLATE",
 };
 #endif /* NDEBUG */
 
@@ -411,43 +420,44 @@ static void yy_destructor(
 {
 #line 43 "NCDConfigParser_parse.y"
  free((yypminor->yy0)); 
-#line 415 "NCDConfigParser_parse.c"
+#line 424 "NCDConfigParser_parse.c"
 }
       break;
     case 14: /* processes */
 {
-#line 53 "NCDConfigParser_parse.y"
+#line 54 "NCDConfigParser_parse.y"
  NCDConfig_free_processes((yypminor->yy41)); 
-#line 422 "NCDConfigParser_parse.c"
+#line 431 "NCDConfigParser_parse.c"
 }
       break;
     case 15: /* statements */
 {
-#line 54 "NCDConfigParser_parse.y"
- NCDConfig_free_statements((yypminor->yy38)); 
-#line 429 "NCDConfigParser_parse.c"
+#line 55 "NCDConfigParser_parse.y"
+ NCDConfig_free_statements((yypminor->yy34)); 
+#line 438 "NCDConfigParser_parse.c"
 }
       break;
     case 16: /* statement_names */
 {
-#line 55 "NCDConfigParser_parse.y"
- NCDConfig_free_strings((yypminor->yy40)); 
-#line 436 "NCDConfigParser_parse.c"
+#line 56 "NCDConfigParser_parse.y"
+ NCDConfig_free_strings((yypminor->yy10)); 
+#line 445 "NCDConfigParser_parse.c"
 }
       break;
     case 17: /* statement_args_maybe */
-    case 18: /* statement_args */
+    case 18: /* list_contents */
+    case 19: /* list */
 {
-#line 56 "NCDConfigParser_parse.y"
- NCDConfig_free_list((yypminor->yy30)); 
-#line 444 "NCDConfigParser_parse.c"
+#line 57 "NCDConfigParser_parse.y"
+ NCDConfig_free_list((yypminor->yy24)); 
+#line 454 "NCDConfigParser_parse.c"
 }
       break;
-    case 19: /* name_maybe */
+    case 20: /* name_maybe */
 {
-#line 58 "NCDConfigParser_parse.y"
- free((yypminor->yy9)); 
-#line 451 "NCDConfigParser_parse.c"
+#line 60 "NCDConfigParser_parse.y"
+ free((yypminor->yy45)); 
+#line 461 "NCDConfigParser_parse.c"
 }
       break;
     default:  break;   /* If no destructor action specified: do nothing */
@@ -620,12 +630,12 @@ static void yyStackOverflow(yyParser *yypParser, YYMINORTYPE *yypMinor){
    while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser);
    /* Here code is inserted which will execute if the parser
    ** stack every overflows */
-#line 67 "NCDConfigParser_parse.y"
+#line 69 "NCDConfigParser_parse.y"
 
     if (yypMinor) {
         free(yypMinor->yy0);
     }
-#line 629 "NCDConfigParser_parse.c"
+#line 639 "NCDConfigParser_parse.c"
    ParseARG_STORE; /* Suppress warning about unused %extra_argument var */
 }
 
@@ -682,7 +692,7 @@ static const struct {
   YYCODETYPE lhs;         /* Symbol on the left-hand side of the rule */
   unsigned char nrhs;     /* Number of right-hand side symbols in the rule */
 } yyRuleInfo[] = {
-  { 21, 1 },
+  { 22, 1 },
   { 14, 5 },
   { 14, 6 },
   { 15, 6 },
@@ -695,12 +705,16 @@ static const struct {
   { 17, 1 },
   { 18, 1 },
   { 18, 1 },
+  { 18, 1 },
   { 18, 3 },
   { 18, 3 },
-  { 19, 0 },
-  { 19, 1 },
-  { 20, 1 },
+  { 18, 3 },
+  { 19, 2 },
+  { 19, 3 },
+  { 20, 0 },
   { 20, 1 },
+  { 21, 1 },
+  { 21, 1 },
 };
 
 static void yy_accept(yyParser*);  /* Forward Declaration */
@@ -756,7 +770,7 @@ static void yy_reduce(
   **     break;
   */
       case 0: /* input ::= processes */
-#line 73 "NCDConfigParser_parse.y"
+#line 75 "NCDConfigParser_parse.y"
 {
     parser_out->ast = yymsp[0].minor.yy41;
 
@@ -764,63 +778,63 @@ static void yy_reduce(
         parser_out->out_of_memory = 1;
     }
 }
-#line 768 "NCDConfigParser_parse.c"
+#line 782 "NCDConfigParser_parse.c"
         break;
       case 1: /* processes ::= process_or_template NAME CURLY_OPEN statements CURLY_CLOSE */
-#line 81 "NCDConfigParser_parse.y"
+#line 83 "NCDConfigParser_parse.y"
 {
-    yygotominor.yy41 = NCDConfig_make_processes(yymsp[-4].minor.yy20, yymsp[-3].minor.yy0, yymsp[-1].minor.yy38, 0, NULL);
+    yygotominor.yy41 = NCDConfig_make_processes(yymsp[-4].minor.yy6, yymsp[-3].minor.yy0, yymsp[-1].minor.yy34, 0, NULL);
     if (!yygotominor.yy41) {
         parser_out->out_of_memory = 1;
     }
   yy_destructor(yypParser,2,&yymsp[-2].minor);
   yy_destructor(yypParser,3,&yymsp[0].minor);
 }
-#line 780 "NCDConfigParser_parse.c"
+#line 794 "NCDConfigParser_parse.c"
         break;
       case 2: /* processes ::= process_or_template NAME CURLY_OPEN statements CURLY_CLOSE processes */
-#line 88 "NCDConfigParser_parse.y"
+#line 90 "NCDConfigParser_parse.y"
 {
-    yygotominor.yy41 = NCDConfig_make_processes(yymsp[-5].minor.yy20, yymsp[-4].minor.yy0, yymsp[-2].minor.yy38, 1, yymsp[0].minor.yy41);
+    yygotominor.yy41 = NCDConfig_make_processes(yymsp[-5].minor.yy6, yymsp[-4].minor.yy0, yymsp[-2].minor.yy34, 1, yymsp[0].minor.yy41);
     if (!yygotominor.yy41) {
         parser_out->out_of_memory = 1;
     }
   yy_destructor(yypParser,2,&yymsp[-3].minor);
   yy_destructor(yypParser,3,&yymsp[-1].minor);
 }
-#line 792 "NCDConfigParser_parse.c"
+#line 806 "NCDConfigParser_parse.c"
         break;
       case 3: /* statements ::= statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON */
-#line 95 "NCDConfigParser_parse.y"
+#line 97 "NCDConfigParser_parse.y"
 {
-    yygotominor.yy38 = NCDConfig_make_statements(NULL, yymsp[-5].minor.yy40, yymsp[-3].minor.yy30, yymsp[-1].minor.yy9, NULL);
-    if (!yygotominor.yy38) {
+    yygotominor.yy34 = NCDConfig_make_statements(NULL, yymsp[-5].minor.yy10, yymsp[-3].minor.yy24, yymsp[-1].minor.yy45, NULL);
+    if (!yygotominor.yy34) {
         parser_out->out_of_memory = 1;
     }
   yy_destructor(yypParser,4,&yymsp[-4].minor);
   yy_destructor(yypParser,5,&yymsp[-2].minor);
   yy_destructor(yypParser,6,&yymsp[0].minor);
 }
-#line 805 "NCDConfigParser_parse.c"
+#line 819 "NCDConfigParser_parse.c"
         break;
       case 4: /* statements ::= statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON statements */
-#line 102 "NCDConfigParser_parse.y"
+#line 104 "NCDConfigParser_parse.y"
 {
-    yygotominor.yy38 = NCDConfig_make_statements(NULL, yymsp[-6].minor.yy40, yymsp[-4].minor.yy30, yymsp[-2].minor.yy9, yymsp[0].minor.yy38);
-    if (!yygotominor.yy38) {
+    yygotominor.yy34 = NCDConfig_make_statements(NULL, yymsp[-6].minor.yy10, yymsp[-4].minor.yy24, yymsp[-2].minor.yy45, yymsp[0].minor.yy34);
+    if (!yygotominor.yy34) {
         parser_out->out_of_memory = 1;
     }
   yy_destructor(yypParser,4,&yymsp[-5].minor);
   yy_destructor(yypParser,5,&yymsp[-3].minor);
   yy_destructor(yypParser,6,&yymsp[-1].minor);
 }
-#line 818 "NCDConfigParser_parse.c"
+#line 832 "NCDConfigParser_parse.c"
         break;
       case 5: /* statements ::= statement_names ARROW statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON */
-#line 109 "NCDConfigParser_parse.y"
+#line 111 "NCDConfigParser_parse.y"
 {
-    yygotominor.yy38 = NCDConfig_make_statements(yymsp[-7].minor.yy40, yymsp[-5].minor.yy40, yymsp[-3].minor.yy30, yymsp[-1].minor.yy9, NULL);
-    if (!yygotominor.yy38) {
+    yygotominor.yy34 = NCDConfig_make_statements(yymsp[-7].minor.yy10, yymsp[-5].minor.yy10, yymsp[-3].minor.yy24, yymsp[-1].minor.yy45, NULL);
+    if (!yygotominor.yy34) {
         parser_out->out_of_memory = 1;
     }
   yy_destructor(yypParser,7,&yymsp[-6].minor);
@@ -828,13 +842,13 @@ static void yy_reduce(
   yy_destructor(yypParser,5,&yymsp[-2].minor);
   yy_destructor(yypParser,6,&yymsp[0].minor);
 }
-#line 832 "NCDConfigParser_parse.c"
+#line 846 "NCDConfigParser_parse.c"
         break;
       case 6: /* statements ::= statement_names ARROW statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON statements */
-#line 116 "NCDConfigParser_parse.y"
+#line 118 "NCDConfigParser_parse.y"
 {
-    yygotominor.yy38 = NCDConfig_make_statements(yymsp[-8].minor.yy40, yymsp[-6].minor.yy40, yymsp[-4].minor.yy30, yymsp[-2].minor.yy9, yymsp[0].minor.yy38);
-    if (!yygotominor.yy38) {
+    yygotominor.yy34 = NCDConfig_make_statements(yymsp[-8].minor.yy10, yymsp[-6].minor.yy10, yymsp[-4].minor.yy24, yymsp[-2].minor.yy45, yymsp[0].minor.yy34);
+    if (!yygotominor.yy34) {
         parser_out->out_of_memory = 1;
     }
   yy_destructor(yypParser,7,&yymsp[-7].minor);
@@ -842,114 +856,153 @@ static void yy_reduce(
   yy_destructor(yypParser,5,&yymsp[-3].minor);
   yy_destructor(yypParser,6,&yymsp[-1].minor);
 }
-#line 846 "NCDConfigParser_parse.c"
+#line 860 "NCDConfigParser_parse.c"
         break;
       case 7: /* statement_names ::= NAME */
-#line 123 "NCDConfigParser_parse.y"
+#line 125 "NCDConfigParser_parse.y"
 {
-    yygotominor.yy40 = NCDConfig_make_strings(yymsp[0].minor.yy0, 0, NULL);
-    if (!yygotominor.yy40) {
+    yygotominor.yy10 = NCDConfig_make_strings(yymsp[0].minor.yy0, 0, NULL);
+    if (!yygotominor.yy10) {
         parser_out->out_of_memory = 1;
     }
 }
-#line 856 "NCDConfigParser_parse.c"
+#line 870 "NCDConfigParser_parse.c"
         break;
       case 8: /* statement_names ::= NAME DOT statement_names */
-#line 130 "NCDConfigParser_parse.y"
+#line 132 "NCDConfigParser_parse.y"
 {
-    yygotominor.yy40 = NCDConfig_make_strings(yymsp[-2].minor.yy0, 1, yymsp[0].minor.yy40);
-    if (!yygotominor.yy40) {
+    yygotominor.yy10 = NCDConfig_make_strings(yymsp[-2].minor.yy0, 1, yymsp[0].minor.yy10);
+    if (!yygotominor.yy10) {
         parser_out->out_of_memory = 1;
     }
   yy_destructor(yypParser,8,&yymsp[-1].minor);
 }
-#line 867 "NCDConfigParser_parse.c"
+#line 881 "NCDConfigParser_parse.c"
         break;
       case 9: /* statement_args_maybe ::= */
-#line 137 "NCDConfigParser_parse.y"
+#line 139 "NCDConfigParser_parse.y"
 {
-    yygotominor.yy30 = NULL;
+    yygotominor.yy24 = NULL;
 }
-#line 874 "NCDConfigParser_parse.c"
+#line 888 "NCDConfigParser_parse.c"
         break;
-      case 10: /* statement_args_maybe ::= statement_args */
-#line 141 "NCDConfigParser_parse.y"
+      case 10: /* statement_args_maybe ::= list_contents */
+#line 143 "NCDConfigParser_parse.y"
 {
-    yygotominor.yy30 = yymsp[0].minor.yy30;
+    yygotominor.yy24 = yymsp[0].minor.yy24;
 }
-#line 881 "NCDConfigParser_parse.c"
+#line 895 "NCDConfigParser_parse.c"
+        break;
+      case 11: /* list_contents ::= STRING */
+#line 147 "NCDConfigParser_parse.y"
+{
+    yygotominor.yy24 = NCDConfig_make_list_string(yymsp[0].minor.yy0, NULL);
+    if (!yygotominor.yy24) {
+        parser_out->out_of_memory = 1;
+    }
+}
+#line 905 "NCDConfigParser_parse.c"
+        break;
+      case 12: /* list_contents ::= statement_names */
+#line 154 "NCDConfigParser_parse.y"
+{
+    yygotominor.yy24 = NCDConfig_make_list_var(yymsp[0].minor.yy10, NULL);
+    if (!yygotominor.yy24) {
+        parser_out->out_of_memory = 1;
+    }
+}
+#line 915 "NCDConfigParser_parse.c"
         break;
-      case 11: /* statement_args ::= STRING */
-#line 145 "NCDConfigParser_parse.y"
+      case 13: /* list_contents ::= list */
+#line 161 "NCDConfigParser_parse.y"
 {
-    yygotominor.yy30 = NCDConfig_make_list_string(yymsp[0].minor.yy0, NULL);
-    if (!yygotominor.yy30) {
+    yygotominor.yy24 = NCDConfig_make_list_list(yymsp[0].minor.yy24, NULL);
+    if (!yygotominor.yy24) {
         parser_out->out_of_memory = 1;
     }
 }
-#line 891 "NCDConfigParser_parse.c"
+#line 925 "NCDConfigParser_parse.c"
         break;
-      case 12: /* statement_args ::= statement_names */
-#line 152 "NCDConfigParser_parse.y"
+      case 14: /* list_contents ::= STRING COMMA list_contents */
+#line 168 "NCDConfigParser_parse.y"
 {
-    yygotominor.yy30 = NCDConfig_make_list_var(yymsp[0].minor.yy40, NULL);
-    if (!yygotominor.yy30) {
+    yygotominor.yy24 = NCDConfig_make_list_string(yymsp[-2].minor.yy0, yymsp[0].minor.yy24);
+    if (!yygotominor.yy24) {
         parser_out->out_of_memory = 1;
     }
+  yy_destructor(yypParser,10,&yymsp[-1].minor);
 }
-#line 901 "NCDConfigParser_parse.c"
+#line 936 "NCDConfigParser_parse.c"
         break;
-      case 13: /* statement_args ::= STRING COMMA statement_args */
-#line 159 "NCDConfigParser_parse.y"
+      case 15: /* list_contents ::= statement_names COMMA list_contents */
+#line 175 "NCDConfigParser_parse.y"
 {
-    yygotominor.yy30 = NCDConfig_make_list_string(yymsp[-2].minor.yy0, yymsp[0].minor.yy30);
-    if (!yygotominor.yy30) {
+    yygotominor.yy24 = NCDConfig_make_list_var(yymsp[-2].minor.yy10, yymsp[0].minor.yy24);
+    if (!yygotominor.yy24) {
         parser_out->out_of_memory = 1;
     }
   yy_destructor(yypParser,10,&yymsp[-1].minor);
 }
-#line 912 "NCDConfigParser_parse.c"
+#line 947 "NCDConfigParser_parse.c"
         break;
-      case 14: /* statement_args ::= statement_names COMMA statement_args */
-#line 166 "NCDConfigParser_parse.y"
+      case 16: /* list_contents ::= list COMMA list_contents */
+#line 182 "NCDConfigParser_parse.y"
 {
-    yygotominor.yy30 = NCDConfig_make_list_var(yymsp[-2].minor.yy40, yymsp[0].minor.yy30);
-    if (!yygotominor.yy30) {
+    yygotominor.yy24 = NCDConfig_make_list_list(yymsp[-2].minor.yy24, yymsp[0].minor.yy24);
+    if (!yygotominor.yy24) {
         parser_out->out_of_memory = 1;
     }
   yy_destructor(yypParser,10,&yymsp[-1].minor);
 }
-#line 923 "NCDConfigParser_parse.c"
+#line 958 "NCDConfigParser_parse.c"
+        break;
+      case 17: /* list ::= CURLY_OPEN CURLY_CLOSE */
+#line 189 "NCDConfigParser_parse.y"
+{
+    yygotominor.yy24 = NULL;
+  yy_destructor(yypParser,2,&yymsp[-1].minor);
+  yy_destructor(yypParser,3,&yymsp[0].minor);
+}
+#line 967 "NCDConfigParser_parse.c"
+        break;
+      case 18: /* list ::= CURLY_OPEN list_contents CURLY_CLOSE */
+#line 193 "NCDConfigParser_parse.y"
+{
+    yygotominor.yy24 = yymsp[-1].minor.yy24;
+  yy_destructor(yypParser,2,&yymsp[-2].minor);
+  yy_destructor(yypParser,3,&yymsp[0].minor);
+}
+#line 976 "NCDConfigParser_parse.c"
         break;
-      case 15: /* name_maybe ::= */
-#line 173 "NCDConfigParser_parse.y"
+      case 19: /* name_maybe ::= */
+#line 197 "NCDConfigParser_parse.y"
 {
-    yygotominor.yy9 = NULL;
+    yygotominor.yy45 = NULL;
 }
-#line 930 "NCDConfigParser_parse.c"
+#line 983 "NCDConfigParser_parse.c"
         break;
-      case 16: /* name_maybe ::= NAME */
-#line 177 "NCDConfigParser_parse.y"
+      case 20: /* name_maybe ::= NAME */
+#line 201 "NCDConfigParser_parse.y"
 {
-    yygotominor.yy9 = yymsp[0].minor.yy0;
+    yygotominor.yy45 = yymsp[0].minor.yy0;
 }
-#line 937 "NCDConfigParser_parse.c"
+#line 990 "NCDConfigParser_parse.c"
         break;
-      case 17: /* process_or_template ::= PROCESS */
-#line 181 "NCDConfigParser_parse.y"
+      case 21: /* process_or_template ::= PROCESS */
+#line 205 "NCDConfigParser_parse.y"
 {
-    yygotominor.yy20 = 0;
+    yygotominor.yy6 = 0;
   yy_destructor(yypParser,11,&yymsp[0].minor);
 }
-#line 945 "NCDConfigParser_parse.c"
+#line 998 "NCDConfigParser_parse.c"
         break;
-      case 18: /* process_or_template ::= TEMPLATE */
-#line 185 "NCDConfigParser_parse.y"
+      case 22: /* process_or_template ::= TEMPLATE */
+#line 209 "NCDConfigParser_parse.y"
 {
-    yygotominor.yy20 = 1;
+    yygotominor.yy6 = 1;
   yy_destructor(yypParser,12,&yymsp[0].minor);
 }
-#line 953 "NCDConfigParser_parse.c"
+#line 1006 "NCDConfigParser_parse.c"
         break;
       default:
         break;
@@ -1011,10 +1064,10 @@ static void yy_syntax_error(
 ){
   ParseARG_FETCH;
 #define TOKEN (yyminor.yy0)
-#line 62 "NCDConfigParser_parse.y"
+#line 64 "NCDConfigParser_parse.y"
 
     parser_out->syntax_error = 1;
-#line 1018 "NCDConfigParser_parse.c"
+#line 1071 "NCDConfigParser_parse.c"
   ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */
 }
 

+ 231 - 137
generated/NCDConfigParser_parse.out

@@ -5,10 +5,10 @@ State 0:
           process_or_template ::= * PROCESS
           process_or_template ::= * TEMPLATE
 
-                       PROCESS shift  27
-                      TEMPLATE shift  28
-                     processes shift  14
-           process_or_template shift  15
+                       PROCESS shift  31
+                      TEMPLATE shift  32
+                     processes shift  16
+           process_or_template shift  17
                          input accept
 
 State 1:
@@ -17,17 +17,23 @@ State 1:
           statement_names ::= * NAME
           statement_names ::= * NAME DOT statement_names
       (9) statement_args_maybe ::= *
-          statement_args_maybe ::= * statement_args
-          statement_args ::= * STRING
-          statement_args ::= * statement_names
-          statement_args ::= * STRING COMMA statement_args
-          statement_args ::= * statement_names COMMA statement_args
-
-                          NAME shift  20
-                        STRING shift  21
-               statement_names shift  22
-          statement_args_maybe shift  18
-                statement_args shift  32
+          statement_args_maybe ::= * list_contents
+          list_contents ::= * STRING
+          list_contents ::= * statement_names
+          list_contents ::= * list
+          list_contents ::= * STRING COMMA list_contents
+          list_contents ::= * statement_names COMMA list_contents
+          list_contents ::= * list COMMA list_contents
+          list ::= * CURLY_OPEN CURLY_CLOSE
+          list ::= * CURLY_OPEN list_contents CURLY_CLOSE
+
+                          NAME shift  22
+                    CURLY_OPEN shift  3
+                        STRING shift  23
+               statement_names shift  24
+          statement_args_maybe shift  20
+                 list_contents shift  36
+                          list shift  25
                      {default} reduce 9
 
 State 2:
@@ -36,62 +42,122 @@ State 2:
           statement_names ::= * NAME
           statement_names ::= * NAME DOT statement_names
       (9) statement_args_maybe ::= *
-          statement_args_maybe ::= * statement_args
-          statement_args ::= * STRING
-          statement_args ::= * statement_names
-          statement_args ::= * STRING COMMA statement_args
-          statement_args ::= * statement_names COMMA statement_args
-
-                          NAME shift  20
-                        STRING shift  21
-               statement_names shift  22
-          statement_args_maybe shift  24
-                statement_args shift  32
+          statement_args_maybe ::= * list_contents
+          list_contents ::= * STRING
+          list_contents ::= * statement_names
+          list_contents ::= * list
+          list_contents ::= * STRING COMMA list_contents
+          list_contents ::= * statement_names COMMA list_contents
+          list_contents ::= * list COMMA list_contents
+          list ::= * CURLY_OPEN CURLY_CLOSE
+          list ::= * CURLY_OPEN list_contents CURLY_CLOSE
+
+                          NAME shift  22
+                    CURLY_OPEN shift  3
+                        STRING shift  23
+               statement_names shift  24
+          statement_args_maybe shift  28
+                 list_contents shift  36
+                          list shift  25
                      {default} reduce 9
 
 State 3:
-          processes ::= * process_or_template NAME CURLY_OPEN statements CURLY_CLOSE
-      (1) processes ::= process_or_template NAME CURLY_OPEN statements CURLY_CLOSE *
-          processes ::= * process_or_template NAME CURLY_OPEN statements CURLY_CLOSE processes
-          processes ::= process_or_template NAME CURLY_OPEN statements CURLY_CLOSE * processes
-          process_or_template ::= * PROCESS
-          process_or_template ::= * TEMPLATE
-
-                       PROCESS shift  27
-                      TEMPLATE shift  28
-                     processes shift  26
-           process_or_template shift  15
-                     {default} reduce 1
+          statement_names ::= * NAME
+          statement_names ::= * NAME DOT statement_names
+          list_contents ::= * STRING
+          list_contents ::= * statement_names
+          list_contents ::= * list
+          list_contents ::= * STRING COMMA list_contents
+          list_contents ::= * statement_names COMMA list_contents
+          list_contents ::= * list COMMA list_contents
+          list ::= * CURLY_OPEN CURLY_CLOSE
+          list ::= CURLY_OPEN * CURLY_CLOSE
+          list ::= * CURLY_OPEN list_contents CURLY_CLOSE
+          list ::= CURLY_OPEN * list_contents CURLY_CLOSE
+
+                          NAME shift  22
+                    CURLY_OPEN shift  3
+                   CURLY_CLOSE shift  38
+                        STRING shift  23
+               statement_names shift  24
+                 list_contents shift  26
+                          list shift  25
 
 State 4:
           statement_names ::= * NAME
           statement_names ::= * NAME DOT statement_names
-          statement_args ::= * STRING
-          statement_args ::= * statement_names
-          statement_args ::= * STRING COMMA statement_args
-          statement_args ::= STRING COMMA * statement_args
-          statement_args ::= * statement_names COMMA statement_args
-
-                          NAME shift  20
-                        STRING shift  21
-               statement_names shift  22
-                statement_args shift  34
+          list_contents ::= * STRING
+          list_contents ::= * statement_names
+          list_contents ::= * list
+          list_contents ::= * STRING COMMA list_contents
+          list_contents ::= STRING COMMA * list_contents
+          list_contents ::= * statement_names COMMA list_contents
+          list_contents ::= * list COMMA list_contents
+          list ::= * CURLY_OPEN CURLY_CLOSE
+          list ::= * CURLY_OPEN list_contents CURLY_CLOSE
+
+                          NAME shift  22
+                    CURLY_OPEN shift  3
+                        STRING shift  23
+               statement_names shift  24
+                 list_contents shift  41
+                          list shift  25
 
 State 5:
           statement_names ::= * NAME
           statement_names ::= * NAME DOT statement_names
-          statement_args ::= * STRING
-          statement_args ::= * statement_names
-          statement_args ::= * STRING COMMA statement_args
-          statement_args ::= * statement_names COMMA statement_args
-          statement_args ::= statement_names COMMA * statement_args
-
-                          NAME shift  20
-                        STRING shift  21
-               statement_names shift  22
-                statement_args shift  33
+          list_contents ::= * STRING
+          list_contents ::= * statement_names
+          list_contents ::= * list
+          list_contents ::= * STRING COMMA list_contents
+          list_contents ::= * statement_names COMMA list_contents
+          list_contents ::= statement_names COMMA * list_contents
+          list_contents ::= * list COMMA list_contents
+          list ::= * CURLY_OPEN CURLY_CLOSE
+          list ::= * CURLY_OPEN list_contents CURLY_CLOSE
+
+                          NAME shift  22
+                    CURLY_OPEN shift  3
+                        STRING shift  23
+               statement_names shift  24
+                 list_contents shift  40
+                          list shift  25
 
 State 6:
+          statement_names ::= * NAME
+          statement_names ::= * NAME DOT statement_names
+          list_contents ::= * STRING
+          list_contents ::= * statement_names
+          list_contents ::= * list
+          list_contents ::= * STRING COMMA list_contents
+          list_contents ::= * statement_names COMMA list_contents
+          list_contents ::= * list COMMA list_contents
+          list_contents ::= list COMMA * list_contents
+          list ::= * CURLY_OPEN CURLY_CLOSE
+          list ::= * CURLY_OPEN list_contents CURLY_CLOSE
+
+                          NAME shift  22
+                    CURLY_OPEN shift  3
+                        STRING shift  23
+               statement_names shift  24
+                 list_contents shift  37
+                          list shift  25
+
+State 7:
+          processes ::= * process_or_template NAME CURLY_OPEN statements CURLY_CLOSE
+      (1) processes ::= process_or_template NAME CURLY_OPEN statements CURLY_CLOSE *
+          processes ::= * process_or_template NAME CURLY_OPEN statements CURLY_CLOSE processes
+          processes ::= process_or_template NAME CURLY_OPEN statements CURLY_CLOSE * processes
+          process_or_template ::= * PROCESS
+          process_or_template ::= * TEMPLATE
+
+                       PROCESS shift  31
+                      TEMPLATE shift  32
+                     processes shift  30
+           process_or_template shift  17
+                     {default} reduce 1
+
+State 8:
           processes ::= process_or_template NAME CURLY_OPEN * statements CURLY_CLOSE
           processes ::= process_or_template NAME CURLY_OPEN * statements CURLY_CLOSE processes
           statements ::= * statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON
@@ -101,11 +167,11 @@ State 6:
           statement_names ::= * NAME
           statement_names ::= * NAME DOT statement_names
 
-                          NAME shift  20
-                    statements shift  17
-               statement_names shift  13
+                          NAME shift  22
+                    statements shift  19
+               statement_names shift  15
 
-State 7:
+State 9:
           statements ::= * statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON
       (3) statements ::= statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON *
           statements ::= * statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON statements
@@ -115,12 +181,12 @@ State 7:
           statement_names ::= * NAME
           statement_names ::= * NAME DOT statement_names
 
-                          NAME shift  20
-                    statements shift  29
-               statement_names shift  13
+                          NAME shift  22
+                    statements shift  33
+               statement_names shift  15
                      {default} reduce 3
 
-State 8:
+State 10:
           statements ::= * statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON
           statements ::= * statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON statements
           statements ::= * statement_names ARROW statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON
@@ -130,177 +196,204 @@ State 8:
           statement_names ::= * NAME
           statement_names ::= * NAME DOT statement_names
 
-                          NAME shift  20
-                    statements shift  35
-               statement_names shift  13
+                          NAME shift  22
+                    statements shift  42
+               statement_names shift  15
                      {default} reduce 5
 
-State 9:
+State 11:
           statements ::= statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE * name_maybe SEMICOLON
           statements ::= statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE * name_maybe SEMICOLON statements
-     (15) name_maybe ::= *
+     (19) name_maybe ::= *
           name_maybe ::= * NAME
 
-                          NAME shift  31
-                    name_maybe shift  19
-                     {default} reduce 15
+                          NAME shift  35
+                    name_maybe shift  21
+                     {default} reduce 19
 
-State 10:
+State 12:
           statement_names ::= * NAME
           statement_names ::= * NAME DOT statement_names
           statement_names ::= NAME DOT * statement_names
 
-                          NAME shift  20
-               statement_names shift  30
+                          NAME shift  22
+               statement_names shift  34
 
-State 11:
+State 13:
           statements ::= statement_names ARROW * statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON
           statements ::= statement_names ARROW * statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON statements
           statement_names ::= * NAME
           statement_names ::= * NAME DOT statement_names
 
-                          NAME shift  20
-               statement_names shift  23
+                          NAME shift  22
+               statement_names shift  27
 
-State 12:
+State 14:
           statements ::= statement_names ARROW statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE * name_maybe SEMICOLON
           statements ::= statement_names ARROW statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE * name_maybe SEMICOLON statements
-     (15) name_maybe ::= *
+     (19) name_maybe ::= *
           name_maybe ::= * NAME
 
-                          NAME shift  31
-                    name_maybe shift  25
-                     {default} reduce 15
+                          NAME shift  35
+                    name_maybe shift  29
+                     {default} reduce 19
 
-State 13:
+State 15:
           statements ::= statement_names * ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON
           statements ::= statement_names * ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON statements
           statements ::= statement_names * ARROW statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON
           statements ::= statement_names * ARROW statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON statements
 
                     ROUND_OPEN shift  1
-                         ARROW shift  11
+                         ARROW shift  13
 
-State 14:
+State 16:
       (0) input ::= processes *
 
                              $ reduce 0
 
-State 15:
+State 17:
           processes ::= process_or_template * NAME CURLY_OPEN statements CURLY_CLOSE
           processes ::= process_or_template * NAME CURLY_OPEN statements CURLY_CLOSE processes
 
-                          NAME shift  16
+                          NAME shift  18
 
-State 16:
+State 18:
           processes ::= process_or_template NAME * CURLY_OPEN statements CURLY_CLOSE
           processes ::= process_or_template NAME * CURLY_OPEN statements CURLY_CLOSE processes
 
-                    CURLY_OPEN shift  6
+                    CURLY_OPEN shift  8
 
-State 17:
+State 19:
           processes ::= process_or_template NAME CURLY_OPEN statements * CURLY_CLOSE
           processes ::= process_or_template NAME CURLY_OPEN statements * CURLY_CLOSE processes
 
-                   CURLY_CLOSE shift  3
+                   CURLY_CLOSE shift  7
 
-State 18:
+State 20:
           statements ::= statement_names ROUND_OPEN statement_args_maybe * ROUND_CLOSE name_maybe SEMICOLON
           statements ::= statement_names ROUND_OPEN statement_args_maybe * ROUND_CLOSE name_maybe SEMICOLON statements
 
-                   ROUND_CLOSE shift  9
+                   ROUND_CLOSE shift  11
 
-State 19:
+State 21:
           statements ::= statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe * SEMICOLON
           statements ::= statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe * SEMICOLON statements
 
-                     SEMICOLON shift  7
+                     SEMICOLON shift  9
 
-State 20:
+State 22:
       (7) statement_names ::= NAME *
           statement_names ::= NAME * DOT statement_names
 
-                           DOT shift  10
+                           DOT shift  12
                      {default} reduce 7
 
-State 21:
-     (11) statement_args ::= STRING *
-          statement_args ::= STRING * COMMA statement_args
+State 23:
+     (11) list_contents ::= STRING *
+          list_contents ::= STRING * COMMA list_contents
 
                          COMMA shift  4
                      {default} reduce 11
 
-State 22:
-     (12) statement_args ::= statement_names *
-          statement_args ::= statement_names * COMMA statement_args
+State 24:
+     (12) list_contents ::= statement_names *
+          list_contents ::= statement_names * COMMA list_contents
 
                          COMMA shift  5
                      {default} reduce 12
 
-State 23:
+State 25:
+     (13) list_contents ::= list *
+          list_contents ::= list * COMMA list_contents
+
+                         COMMA shift  6
+                     {default} reduce 13
+
+State 26:
+          list ::= CURLY_OPEN list_contents * CURLY_CLOSE
+
+                   CURLY_CLOSE shift  39
+
+State 27:
           statements ::= statement_names ARROW statement_names * ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON
           statements ::= statement_names ARROW statement_names * ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON statements
 
                     ROUND_OPEN shift  2
 
-State 24:
+State 28:
           statements ::= statement_names ARROW statement_names ROUND_OPEN statement_args_maybe * ROUND_CLOSE name_maybe SEMICOLON
           statements ::= statement_names ARROW statement_names ROUND_OPEN statement_args_maybe * ROUND_CLOSE name_maybe SEMICOLON statements
 
-                   ROUND_CLOSE shift  12
+                   ROUND_CLOSE shift  14
 
-State 25:
+State 29:
           statements ::= statement_names ARROW statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe * SEMICOLON
           statements ::= statement_names ARROW statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe * SEMICOLON statements
 
-                     SEMICOLON shift  8
+                     SEMICOLON shift  10
 
-State 26:
+State 30:
       (2) processes ::= process_or_template NAME CURLY_OPEN statements CURLY_CLOSE processes *
 
                      {default} reduce 2
 
-State 27:
-     (17) process_or_template ::= PROCESS *
+State 31:
+     (21) process_or_template ::= PROCESS *
 
-                     {default} reduce 17
+                     {default} reduce 21
 
-State 28:
-     (18) process_or_template ::= TEMPLATE *
+State 32:
+     (22) process_or_template ::= TEMPLATE *
 
-                     {default} reduce 18
+                     {default} reduce 22
 
-State 29:
+State 33:
       (4) statements ::= statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON statements *
 
                      {default} reduce 4
 
-State 30:
+State 34:
       (8) statement_names ::= NAME DOT statement_names *
 
                      {default} reduce 8
 
-State 31:
-     (16) name_maybe ::= NAME *
+State 35:
+     (20) name_maybe ::= NAME *
 
-                     {default} reduce 16
+                     {default} reduce 20
 
-State 32:
-     (10) statement_args_maybe ::= statement_args *
+State 36:
+     (10) statement_args_maybe ::= list_contents *
 
                      {default} reduce 10
 
-State 33:
-     (14) statement_args ::= statement_names COMMA statement_args *
+State 37:
+     (16) list_contents ::= list COMMA list_contents *
 
-                     {default} reduce 14
+                     {default} reduce 16
 
-State 34:
-     (13) statement_args ::= STRING COMMA statement_args *
+State 38:
+     (17) list ::= CURLY_OPEN CURLY_CLOSE *
 
-                     {default} reduce 13
+                     {default} reduce 17
 
-State 35:
+State 39:
+     (18) list ::= CURLY_OPEN list_contents CURLY_CLOSE *
+
+                     {default} reduce 18
+
+State 40:
+     (15) list_contents ::= statement_names COMMA list_contents *
+
+                     {default} reduce 15
+
+State 41:
+     (14) list_contents ::= STRING COMMA list_contents *
+
+                     {default} reduce 14
+
+State 42:
       (6) statements ::= statement_names ARROW statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON statements *
 
                      {default} reduce 6
@@ -324,8 +417,9 @@ Symbols:
    14: processes: PROCESS TEMPLATE
    15: statements: NAME
    16: statement_names: NAME
-   17: statement_args_maybe: <lambda> NAME STRING
-   18: statement_args: NAME STRING
-   19: name_maybe: <lambda> NAME
-   20: process_or_template: PROCESS TEMPLATE
-   21: input: PROCESS TEMPLATE
+   17: statement_args_maybe: <lambda> NAME CURLY_OPEN STRING
+   18: list_contents: NAME CURLY_OPEN STRING
+   19: list: CURLY_OPEN
+   20: name_maybe: <lambda> NAME
+   21: process_or_template: PROCESS TEMPLATE
+   22: input: PROCESS TEMPLATE

+ 31 - 7
generated/NCDConfigParser_parse.y

@@ -46,7 +46,8 @@ struct parser_out {
 %type statements {struct NCDConfig_statements *}
 %type statement_names {struct NCDConfig_strings *}
 %type statement_args_maybe {struct NCDConfig_list *}
-%type statement_args {struct NCDConfig_list *}
+%type list_contents {struct NCDConfig_list *}
+%type list {struct NCDConfig_list *}
 %type name_maybe {char *}
 %type process_or_template {int}
 
@@ -54,7 +55,8 @@ struct parser_out {
 %destructor statements { NCDConfig_free_statements($$); }
 %destructor statement_names { NCDConfig_free_strings($$); }
 %destructor statement_args_maybe { NCDConfig_free_list($$); }
-%destructor statement_args { NCDConfig_free_list($$); }
+%destructor list_contents { NCDConfig_free_list($$); }
+%destructor list { NCDConfig_free_list($$); }
 %destructor name_maybe { free($$); }
 
 %stack_size 0
@@ -138,38 +140,60 @@ statement_args_maybe(R) ::= . {
     R = NULL;
 }
 
-statement_args_maybe(R) ::= statement_args(A). {
+statement_args_maybe(R) ::= list_contents(A). {
     R = A;
 }
 
-statement_args(R) ::= STRING(A). {
+list_contents(R) ::= STRING(A). {
     R = NCDConfig_make_list_string(A, NULL);
     if (!R) {
         parser_out->out_of_memory = 1;
     }
 }
 
-statement_args(R) ::= statement_names(A). {
+list_contents(R) ::= statement_names(A). {
     R = NCDConfig_make_list_var(A, NULL);
     if (!R) {
         parser_out->out_of_memory = 1;
     }
 }
 
-statement_args(R) ::= STRING(A) COMMA statement_args(N). {
+list_contents(R) ::= list(A). {
+    R = NCDConfig_make_list_list(A, NULL);
+    if (!R) {
+        parser_out->out_of_memory = 1;
+    }
+}
+
+list_contents(R) ::= STRING(A) COMMA list_contents(N). {
     R = NCDConfig_make_list_string(A, N);
     if (!R) {
         parser_out->out_of_memory = 1;
     }
 }
 
-statement_args(R) ::= statement_names(A) COMMA statement_args(N). {
+list_contents(R) ::= statement_names(A) COMMA list_contents(N). {
     R = NCDConfig_make_list_var(A, N);
     if (!R) {
         parser_out->out_of_memory = 1;
     }
 }
 
+list_contents(R) ::= list(A) COMMA list_contents(N). {
+    R = NCDConfig_make_list_list(A, N);
+    if (!R) {
+        parser_out->out_of_memory = 1;
+    }
+}
+
+list(R) ::= CURLY_OPEN CURLY_CLOSE. {
+    R = NULL;
+}
+
+list(R) ::= CURLY_OPEN list_contents(A) CURLY_CLOSE. {
+    R = A;
+}
+
 name_maybe(R) ::= . {
     R = NULL;
 }

+ 22 - 0
ncd/NCDConfig.c

@@ -69,6 +69,9 @@ void NCDConfig_free_list (struct NCDConfig_list *v)
         case NCDCONFIG_ARG_VAR:
             NCDConfig_free_strings(v->var);
             break;
+        case NCDCONFIG_ARG_LIST:
+            NCDConfig_free_list(v->list);
+            break;
         default:
             ASSERT(0);
     }
@@ -176,6 +179,25 @@ fail:
     return NULL;
 }
 
+struct NCDConfig_list * NCDConfig_make_list_list (struct NCDConfig_list *list, struct NCDConfig_list *next)
+{
+    struct NCDConfig_list *v = malloc(sizeof(*v));
+    if (!v) {
+        goto fail;
+    }
+    
+    v->type = NCDCONFIG_ARG_LIST;
+    v->list = list;
+    v->next = next;
+    
+    return v;
+    
+fail:
+    NCDConfig_free_list(list);
+    NCDConfig_free_list(next);
+    return NULL;
+}
+
 struct NCDConfig_strings * NCDConfig_make_strings (char *value, int need_next, struct NCDConfig_strings *next)
 {
     if (!value || (need_next && !next)) {

+ 3 - 0
ncd/NCDConfig.h

@@ -45,12 +45,14 @@ struct NCDConfig_statements {
 
 #define NCDCONFIG_ARG_STRING 1
 #define NCDCONFIG_ARG_VAR 2
+#define NCDCONFIG_ARG_LIST 3
 
 struct NCDConfig_list {
     int type;
     union {
         char *string;
         struct NCDConfig_strings *var;
+        struct NCDConfig_list *list;
     };
     struct NCDConfig_list *next;
 };
@@ -68,6 +70,7 @@ struct NCDConfig_processes * NCDConfig_make_processes (int is_template, char *na
 struct NCDConfig_statements * NCDConfig_make_statements (struct NCDConfig_strings *objname, struct NCDConfig_strings *names, struct NCDConfig_list *args, char *name, struct NCDConfig_statements *next);
 struct NCDConfig_list * NCDConfig_make_list_string (char *str, struct NCDConfig_list *next);
 struct NCDConfig_list * NCDConfig_make_list_var (struct NCDConfig_strings *var, struct NCDConfig_list *next);
+struct NCDConfig_list * NCDConfig_make_list_list (struct NCDConfig_list *list, struct NCDConfig_list *next);
 struct NCDConfig_strings * NCDConfig_make_strings (char *value, int have_next, struct NCDConfig_strings *next);
 
 int NCDConfig_statement_name_is (struct NCDConfig_statements *st, const char *needle);

+ 31 - 7
ncd/NCDConfigParser_parse.y

@@ -46,7 +46,8 @@ struct parser_out {
 %type statements {struct NCDConfig_statements *}
 %type statement_names {struct NCDConfig_strings *}
 %type statement_args_maybe {struct NCDConfig_list *}
-%type statement_args {struct NCDConfig_list *}
+%type list_contents {struct NCDConfig_list *}
+%type list {struct NCDConfig_list *}
 %type name_maybe {char *}
 %type process_or_template {int}
 
@@ -54,7 +55,8 @@ struct parser_out {
 %destructor statements { NCDConfig_free_statements($$); }
 %destructor statement_names { NCDConfig_free_strings($$); }
 %destructor statement_args_maybe { NCDConfig_free_list($$); }
-%destructor statement_args { NCDConfig_free_list($$); }
+%destructor list_contents { NCDConfig_free_list($$); }
+%destructor list { NCDConfig_free_list($$); }
 %destructor name_maybe { free($$); }
 
 %stack_size 0
@@ -138,38 +140,60 @@ statement_args_maybe(R) ::= . {
     R = NULL;
 }
 
-statement_args_maybe(R) ::= statement_args(A). {
+statement_args_maybe(R) ::= list_contents(A). {
     R = A;
 }
 
-statement_args(R) ::= STRING(A). {
+list_contents(R) ::= STRING(A). {
     R = NCDConfig_make_list_string(A, NULL);
     if (!R) {
         parser_out->out_of_memory = 1;
     }
 }
 
-statement_args(R) ::= statement_names(A). {
+list_contents(R) ::= statement_names(A). {
     R = NCDConfig_make_list_var(A, NULL);
     if (!R) {
         parser_out->out_of_memory = 1;
     }
 }
 
-statement_args(R) ::= STRING(A) COMMA statement_args(N). {
+list_contents(R) ::= list(A). {
+    R = NCDConfig_make_list_list(A, NULL);
+    if (!R) {
+        parser_out->out_of_memory = 1;
+    }
+}
+
+list_contents(R) ::= STRING(A) COMMA list_contents(N). {
     R = NCDConfig_make_list_string(A, N);
     if (!R) {
         parser_out->out_of_memory = 1;
     }
 }
 
-statement_args(R) ::= statement_names(A) COMMA statement_args(N). {
+list_contents(R) ::= statement_names(A) COMMA list_contents(N). {
     R = NCDConfig_make_list_var(A, N);
     if (!R) {
         parser_out->out_of_memory = 1;
     }
 }
 
+list_contents(R) ::= list(A) COMMA list_contents(N). {
+    R = NCDConfig_make_list_list(A, N);
+    if (!R) {
+        parser_out->out_of_memory = 1;
+    }
+}
+
+list(R) ::= CURLY_OPEN CURLY_CLOSE. {
+    R = NULL;
+}
+
+list(R) ::= CURLY_OPEN list_contents(A) CURLY_CLOSE. {
+    R = A;
+}
+
 name_maybe(R) ::= . {
     R = NULL;
 }

+ 207 - 106
ncd/ncd.c

@@ -35,6 +35,7 @@
 #include <misc/string_begins_with.h>
 #include <misc/parse_number.h>
 #include <misc/open_standard_streams.h>
+#include <structure/LinkedList1.h>
 #include <structure/LinkedList2.h>
 #include <base/BLog.h>
 #include <system/BReactor.h>
@@ -57,6 +58,10 @@
 #define LOGGER_STDOUT 1
 #define LOGGER_SYSLOG 2
 
+#define ARG_VALUE_TYPE_STRING 1
+#define ARG_VALUE_TYPE_VARIABLE 2
+#define ARG_VALUE_TYPE_LIST 3
+
 #define SSTATE_CHILD 1
 #define SSTATE_ADULT 2
 #define SSTATE_DYING 3
@@ -67,22 +72,27 @@
 #define PSTATE_WAITING 3
 #define PSTATE_TERMINATING 4
 
+struct arg_value {
+    int type;
+    union {
+        char *string;
+        char *variable;
+        LinkedList1 list;
+    };
+};
+
+struct arg_list_elem {
+    LinkedList1Node list_node;
+    struct arg_value value;
+};
+
 struct statement {
     char *object_name;
     char *method_name;
-    struct argument_elem *first_arg;
+    struct arg_value args;
     char *name;
 };
 
-struct argument_elem {
-    int is_var;
-    union {
-        char *var;
-        NCDValue val;
-    };
-    struct argument_elem *next_arg;
-};
-
 struct process {
     NCDModuleProcess *module_process;
     NCDValue args;
@@ -148,9 +158,14 @@ static void print_version (void);
 static int parse_arguments (int argc, char *argv[]);
 static void signal_handler (void *unused);
 static const struct NCDModule * find_module (const char *name);
+static int arg_value_init_string (struct arg_value *o, const char *string);
+static int arg_value_init_variable (struct arg_value *o, const char *variable);
+static void arg_value_init_list (struct arg_value *o);
+static int arg_value_list_append (struct arg_value *o, struct arg_value v);
+static void arg_value_free (struct arg_value *o);
+static int build_arg_list_from_ast_list (struct arg_value *o, struct NCDConfig_list *list);
 static int statement_init (struct statement *s, struct NCDConfig_statements *conf);
 static void statement_free (struct statement *s);
-static void statement_free_args (struct statement *s);
 static int process_new (struct NCDConfig_processes *conf, NCDModuleProcess *module_process, NCDValue args);
 static void process_free (struct process *p);
 static void process_start_terminating (struct process *p);
@@ -170,6 +185,7 @@ static struct process_statement * process_resolve_object (struct process *p, siz
 static void process_statement_logfunc (struct process_statement *ps);
 static void process_statement_log (struct process_statement *ps, int level, const char *fmt, ...);
 static void process_statement_set_error (struct process_statement *ps);
+static int process_statement_resolve_argument (struct process_statement *ps, struct arg_value *arg, NCDValue *out);
 static void process_statement_instance_func_event (struct process_statement *ps, int event);
 static int process_statement_instance_func_getvar (struct process_statement *ps, const char *varname, NCDValue *out);
 static NCDModuleInst * process_statement_instance_func_getobj (struct process_statement *ps, const char *objname);
@@ -560,85 +576,161 @@ const struct NCDModule * find_module (const char *name)
     return NULL;
 }
 
+int arg_value_init_string (struct arg_value *o, const char *string)
+{
+    o->type = ARG_VALUE_TYPE_STRING;
+    if (!(o->string = strdup(string))) {
+        BLog(BLOG_ERROR, "strdup failed");
+        return 0;
+    }
+    
+    return 1;
+}
+
+int arg_value_init_variable (struct arg_value *o, const char *variable)
+{
+    o->type = ARG_VALUE_TYPE_VARIABLE;
+    if (!(o->variable = strdup(variable))) {
+        BLog(BLOG_ERROR, "strdup failed");
+        return 0;
+    }
+    
+    return 1;
+}
+
+void arg_value_init_list (struct arg_value *o)
+{
+    o->type = ARG_VALUE_TYPE_LIST;
+    LinkedList1_Init(&o->list);
+}
+
+int arg_value_list_append (struct arg_value *o, struct arg_value v)
+{
+    ASSERT(o->type == ARG_VALUE_TYPE_LIST)
+    
+    struct arg_list_elem *elem = malloc(sizeof(*elem));
+    if (!elem) {
+        BLog(BLOG_ERROR, "malloc failed");
+        return 0;
+    }
+    LinkedList1_Append(&o->list, &elem->list_node);
+    elem->value = v;
+    
+    return 1;
+}
+
+void arg_value_free (struct arg_value *o)
+{
+    switch (o->type) {
+        case ARG_VALUE_TYPE_STRING: {
+            free(o->string);
+        } break;
+        
+        case ARG_VALUE_TYPE_VARIABLE: {
+            free(o->variable);
+        } break;
+        
+        case ARG_VALUE_TYPE_LIST: {
+            while (!LinkedList1_IsEmpty(&o->list)) {
+                struct arg_list_elem *elem = UPPER_OBJECT(LinkedList1_GetFirst(&o->list), struct arg_list_elem, list_node);
+                arg_value_free(&elem->value);
+                LinkedList1_Remove(&o->list, &elem->list_node);
+                free(elem);
+            }
+        } break;
+        
+        default: ASSERT(0);
+    }
+}
+
+int build_arg_list_from_ast_list (struct arg_value *o, struct NCDConfig_list *list)
+{
+    arg_value_init_list(o);
+    
+    for (struct NCDConfig_list *c = list; c; c = c->next) {
+        struct arg_value e;
+        
+        switch (c->type) {
+            case NCDCONFIG_ARG_STRING: {
+                if (!arg_value_init_string(&e, c->string)) {
+                    goto fail;
+                }
+            } break;
+            
+            case NCDCONFIG_ARG_VAR: {
+                char *variable = NCDConfig_concat_strings(c->var);
+                if (!variable) {
+                    BLog(BLOG_ERROR, "NCDConfig_concat_strings failed");
+                    goto fail;
+                }
+                
+                if (!arg_value_init_variable(&e, variable)) {
+                    free(variable);
+                    goto fail;
+                }
+                
+                free(variable);
+            } break;
+            
+            case NCDCONFIG_ARG_LIST: {
+                if (!build_arg_list_from_ast_list(&e, c->list)) {
+                    goto fail;
+                }
+            } break;
+            
+            default: ASSERT(0);
+        }
+        
+        if (!arg_value_list_append(o, e)) {
+            arg_value_free(&e);
+            goto fail;
+        }
+    }
+    
+    return 1;
+    
+fail:
+    arg_value_free(o);
+    return 0;
+}
+
 int statement_init (struct statement *s, struct NCDConfig_statements *conf)
 {
     s->object_name = NULL;
     s->method_name = NULL;
     s->name = NULL;
-    s->first_arg = NULL;
     
     // set object name
     if (conf->objname) {
         if (!(s->object_name = NCDConfig_concat_strings(conf->objname))) {
             BLog(BLOG_ERROR, "NCDConfig_concat_strings failed");
-            goto fail;
+            goto fail1;
         }
     }
     
     // set method name
     if (!(s->method_name = NCDConfig_concat_strings(conf->names))) {
         BLog(BLOG_ERROR, "NCDConfig_concat_strings failed");
-        goto fail;
+        goto fail1;
     }
     
     // init name
     if (conf->name) {
         if (!(s->name = strdup(conf->name))) {
             BLog(BLOG_ERROR, "strdup failed");
-            goto fail;
+            goto fail1;
         }
     }
     
     // init arguments
-    struct argument_elem **prevptr = &s->first_arg;
-    struct NCDConfig_list *arg = conf->args;
-    while (arg) {
-        struct argument_elem *e = malloc(sizeof(*e));
-        if (!e) {
-            BLog(BLOG_ERROR, "malloc failed");
-            goto loop_fail0;
-        }
-        
-        switch (arg->type) {
-            case NCDCONFIG_ARG_STRING: {
-                if (!NCDValue_InitString(&e->val, arg->string)) {
-                    BLog(BLOG_ERROR, "NCDValue_InitString failed");
-                    goto loop_fail1;
-                }
-                
-                e->is_var = 0;
-            } break;
-            
-            case NCDCONFIG_ARG_VAR: {
-                if (!(e->var = NCDConfig_concat_strings(arg->var))) {
-                    BLog(BLOG_ERROR, "NCDConfig_concat_strings failed");
-                    goto loop_fail1;
-                }
-                
-                e->is_var = 1;
-            } break;
-            
-            default:
-                ASSERT(0);
-        }
-        
-        *prevptr = e;
-        e->next_arg = NULL;
-        prevptr = &e->next_arg;
-        
-        arg = arg->next;
-        continue;
-        
-    loop_fail1:
-        free(e);
-    loop_fail0:
-        goto fail;
+    if (!build_arg_list_from_ast_list(&s->args, conf->args)) {
+        BLog(BLOG_ERROR, "build_arg_list_from_ast_list failed");
+        goto fail1;
     }
     
     return 1;
     
-fail:
-    statement_free_args(s);
+fail1:
     free(s->name);
     free(s->method_name);
     free(s->object_name);
@@ -648,7 +740,7 @@ fail:
 void statement_free (struct statement *s)
 {
     // free arguments
-    statement_free_args(s);
+    arg_value_free(&s->args);
     
     // free names
     free(s->name);
@@ -656,21 +748,6 @@ void statement_free (struct statement *s)
     free(s->object_name);
 }
 
-void statement_free_args (struct statement *s)
-{
-    struct argument_elem *e = s->first_arg;
-    while (e) {
-        if (e->is_var) {
-            free(e->var);
-        } else {
-            NCDValue_Free(&e->val);
-        }
-        struct argument_elem *n = e->next_arg;
-        free(e);
-        e = n;
-    }
-}
-
 int process_new (struct NCDConfig_processes *conf, NCDModuleProcess *module_process, NCDValue args)
 {
     ASSERT(NCDValue_Type(&args) == NCDVALUE_LIST)
@@ -1077,34 +1154,10 @@ void process_advance_job_handler (struct process *p)
         goto fail1;
     }
     
-    // init arguments list
-    NCDValue_InitList(&ps->inst_args);
-    
-    // build arguments
-    struct argument_elem *arg = ps->s.first_arg;
-    while (arg) {
-        // resolve argument
-        NCDValue v;
-        if (arg->is_var) {
-            if (!process_resolve_variable(p, p->ap, arg->var, &v)) {
-                process_statement_log(ps, BLOG_ERROR, "failed to resolve variable");
-                goto fail2;
-            }
-        } else {
-            if (!NCDValue_InitCopy(&v, &arg->val)) {
-                process_statement_log(ps, BLOG_ERROR, "NCDValue_InitCopy failed");
-                goto fail2;
-            }
-        }
-        
-        // move to list
-        if (!NCDValue_ListAppend(&ps->inst_args, v)) {
-            process_statement_log(ps, BLOG_ERROR, "NCDValue_ListAppend failed");
-            NCDValue_Free(&v);
-            goto fail2;
-        }
-        
-        arg = arg->next_arg;
+    // resolve arguments
+    if (!process_statement_resolve_argument(ps, &ps->s.args, &ps->inst_args)) {
+        process_statement_log(ps, BLOG_ERROR, "failed to resolve arguments");
+        goto fail1;
     }
     
     // initialize module instance
@@ -1131,8 +1184,6 @@ void process_advance_job_handler (struct process *p)
     process_assert_pointers(p);
     return;
     
-fail2:
-    NCDValue_Free(&ps->inst_args);
 fail1:
     free(type);
 fail0:
@@ -1340,6 +1391,56 @@ void process_statement_set_error (struct process_statement *ps)
     ps->error_until = btime_add(btime_gettime(), options.retry_time);
 }
 
+int process_statement_resolve_argument (struct process_statement *ps, struct arg_value *arg, NCDValue *out)
+{
+    ASSERT(ps->i <= process_rap(ps->p))
+    
+    switch (arg->type) {
+        case ARG_VALUE_TYPE_STRING: {
+            if (!NCDValue_InitString(out, arg->string)) {
+                process_statement_log(ps, BLOG_ERROR, "NCDValue_InitString failed");
+                return 0;
+            }
+        } break;
+        
+        case ARG_VALUE_TYPE_VARIABLE: {
+            if (!process_resolve_variable(ps->p, ps->i, arg->variable, out)) {
+                process_statement_log(ps, BLOG_ERROR, "failed to resolve variable");
+                return 0;
+            }
+        } break;
+        
+        case ARG_VALUE_TYPE_LIST: do {
+            NCDValue_InitList(out);
+            
+            for (LinkedList1Node *n = LinkedList1_GetFirst(&arg->list); n; n = LinkedList1Node_Next(n)) {
+                struct arg_list_elem *elem = UPPER_OBJECT(n, struct arg_list_elem, list_node);
+                
+                NCDValue v;
+                if (!process_statement_resolve_argument(ps, &elem->value, &v)) {
+                    goto list_fail1;
+                }
+                
+                if (!NCDValue_ListAppend(out, v)) {
+                    process_statement_log(ps, BLOG_ERROR, "NCDValue_ListAppend failed");
+                    NCDValue_Free(&v);
+                    goto list_fail1;
+                }
+            }
+            
+            break;
+            
+        list_fail1:
+            NCDValue_Free(out);
+            return 0;
+        } while (0); break;
+        
+        default: ASSERT(0);
+    }
+    
+    return 1;
+}
+
 void process_statement_instance_func_event (struct process_statement *ps, int event)
 {
     ASSERT(ps->state == SSTATE_CHILD || ps->state == SSTATE_ADULT || ps->state == SSTATE_DYING)