Bladeren bron

ncd: refactor parsing and implement if clauses

ambrop7 13 jaren geleden
bovenliggende
commit
dd25051125

+ 3 - 3
examples/CMakeLists.txt

@@ -29,13 +29,13 @@ endif ()
 
 
 if (BUILD_NCD)
 if (BUILD_NCD)
     add_executable(ncd_tokenizer_test ncd_tokenizer_test.c)
     add_executable(ncd_tokenizer_test ncd_tokenizer_test.c)
-    target_link_libraries(ncd_tokenizer_test ncdconfig)
+    target_link_libraries(ncd_tokenizer_test ncdtokenizer)
 
 
     add_executable(ncd_parser_test ncd_parser_test.c)
     add_executable(ncd_parser_test ncd_parser_test.c)
-    target_link_libraries(ncd_parser_test ncdconfig)
+    target_link_libraries(ncd_parser_test ncdconfigparser ncdvaluegenerator)
 
 
     add_executable(ncd_value_parser_test ncd_value_parser_test.c)
     add_executable(ncd_value_parser_test ncd_value_parser_test.c)
-    target_link_libraries(ncd_value_parser_test ncdvalue)
+    target_link_libraries(ncd_value_parser_test ncdvalueparser ncdvaluegenerator)
 
 
     add_executable(ncdinterfacemonitor_test ncdinterfacemonitor_test.c)
     add_executable(ncdinterfacemonitor_test ncdinterfacemonitor_test.c)
     target_link_libraries(ncdinterfacemonitor_test ncdinterfacemonitor)
     target_link_libraries(ncdinterfacemonitor_test ncdinterfacemonitor)

+ 61 - 53
examples/ncd_parser_test.c

@@ -30,10 +30,12 @@
 #include <stddef.h>
 #include <stddef.h>
 #include <stdio.h>
 #include <stdio.h>
 #include <string.h>
 #include <string.h>
+#include <stdlib.h>
 
 
 #include <misc/debug.h>
 #include <misc/debug.h>
 #include <base/BLog.h>
 #include <base/BLog.h>
 #include <ncd/NCDConfigParser.h>
 #include <ncd/NCDConfigParser.h>
+#include <ncd/NCDValueGenerator.h>
 
 
 int error;
 int error;
 
 
@@ -45,37 +47,64 @@ static void print_indent (unsigned int indent)
     }
     }
 }
 }
 
 
-static void print_list (struct NCDConfig_list *l, unsigned int indent)
+static void print_value (NCDValue *v, unsigned int indent)
 {
 {
-    while (l) {
-        print_indent(indent);
-        switch (l->type) {
-            case NCDCONFIG_ARG_STRING: {
-                printf("string: %s\n", l->string);
+    char *str = NCDValueGenerator_Generate(v);
+    if (!str) {
+        DEBUG("NCDValueGenerator_Generate failed");
+        exit(1);
+    }
+    
+    print_indent(indent);
+    printf("%s\n", str);
+    
+    free(str);
+}
+
+static void print_block (NCDBlock *block, unsigned int indent)
+{
+    for (NCDStatement *st = NCDBlock_FirstStatement(block); st; st = NCDBlock_NextStatement(block, st)) {
+        const char *name = NCDStatement_Name(st) ? NCDStatement_Name(st) : "";
+        
+        switch (NCDStatement_Type(st)) {
+            case NCDSTATEMENT_REG: {
+                const char *objname = NCDStatement_RegObjName(st) ? NCDStatement_RegObjName(st) : "";
+                const char *cmdname = NCDStatement_RegCmdName(st);
+                
+                print_indent(indent);
+                printf("reg name=%s objname=%s cmdname=%s args:\n", name, objname, cmdname);
+                
+                print_value(NCDStatement_RegArgs(st), indent + 2);
             } break;
             } 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;
+            
+            case NCDSTATEMENT_IF: {
+                print_indent(indent);
+                printf("if name=%s\n", name);
+                
+                NCDIfBlock *ifb = NCDStatement_IfBlock(st);
+                
+                for (NCDIf *ifc = NCDIfBlock_FirstIf(ifb); ifc; ifc = NCDIfBlock_NextIf(ifb, ifc)) {
+                    print_indent(indent + 2);
+                    printf("if\n");
+                    
+                    print_value(NCDIf_Cond(ifc), indent + 4);
+                    
+                    print_indent(indent + 2);
+                    printf("then\n");
+                    
+                    print_block(NCDIf_Block(ifc), indent + 4);
+                }
+                
+                if (NCDStatement_IfElse(st)) {
+                    print_indent(indent + 2);
+                    printf("else\n");
+                    
+                    print_block(NCDStatement_IfElse(st), indent + 4);
                 }
                 }
-                printf("\n");
-            } break;
-            case NCDCONFIG_ARG_LIST: {
-                printf("list\n");
-                print_list(l->list, indent + 1);
-            } break;
-            case NCDCONFIG_ARG_MAPLIST: {
-                printf("maplist\n");
-                print_list(l->list, indent + 1);
             } break;
             } break;
-            default:
-                ASSERT(0);
+            
+            default: ASSERT(0);
         }
         }
-        l = l->next;
     }
     }
 }
 }
 
 
@@ -93,40 +122,19 @@ int main (int argc, char **argv)
     BLog_InitStdout();
     BLog_InitStdout();
     
     
     // parse
     // parse
-    struct NCDConfig_processes *ast;
-    if (!NCDConfigParser_Parse(argv[1], strlen(argv[1]), &ast)) {
+    NCDProgram prog;
+    if (!NCDConfigParser_Parse(argv[1], strlen(argv[1]), &prog)) {
         DEBUG("NCDConfigParser_Parse failed");
         DEBUG("NCDConfigParser_Parse failed");
         return 1;
         return 1;
     }
     }
     
     
     // print
     // print
-    struct NCDConfig_processes *iface = ast;
-    while (iface) {
-        printf("process %s\n", iface->name);
-        
-        struct NCDConfig_statements *st = iface->statements;
-        while (st) {
-            struct NCDConfig_strings *name = st->names;
-            ASSERT(name)
-            printf("  %s", name->value);
-            name = name->next;
-            
-            while (name) {
-                printf(".%s", name->value);
-                name = name->next;
-            }
-            
-            printf("\n");
-            
-            print_list(st->args, 2);
-            
-            st = st->next;
-        }
-        
-        iface = iface->next;
+    for (NCDProcess *p = NCDProgram_FirstProcess(&prog); p; p = NCDProgram_NextProcess(&prog, p)) {
+        printf("process name=%s is_template=%d\n", NCDProcess_Name(p), NCDProcess_IsTemplate(p));
+        print_block(NCDProcess_Block(p), 2);
     }
     }
     
     
-    NCDConfig_free_processes(ast);
+    NCDProgram_Free(&prog);
     
     
     return 0;
     return 0;
 }
 }

File diff suppressed because it is too large
+ 745 - 312
generated/NCDConfigParser_parse.c


+ 11 - 8
generated/NCDConfigParser_parse.h

@@ -5,11 +5,14 @@
 #define ROUND_CLOSE                     5
 #define ROUND_CLOSE                     5
 #define SEMICOLON                       6
 #define SEMICOLON                       6
 #define ARROW                           7
 #define ARROW                           7
-#define DOT                             8
-#define COMMA                           9
-#define COLON                          10
-#define BRACKET_OPEN                   11
-#define BRACKET_CLOSE                  12
-#define STRING                         13
-#define PROCESS                        14
-#define TEMPLATE                       15
+#define IF                              8
+#define ELIF                            9
+#define ELSE                           10
+#define DOT                            11
+#define COMMA                          12
+#define COLON                          13
+#define BRACKET_OPEN                   14
+#define BRACKET_CLOSE                  15
+#define STRING                         16
+#define PROCESS                        17
+#define TEMPLATE                       18

+ 470 - 247
generated/NCDConfigParser_parse.out

@@ -5,17 +5,17 @@ State 0:
           process_or_template ::= * PROCESS
           process_or_template ::= * PROCESS
           process_or_template ::= * TEMPLATE
           process_or_template ::= * TEMPLATE
 
 
-                       PROCESS shift  32
-                      TEMPLATE shift  33
-                     processes shift  16
-           process_or_template shift  17
+                       PROCESS shift  52
+                      TEMPLATE shift  53
+                     processes shift  25
+           process_or_template shift  26
                          input accept
                          input accept
 
 
 State 1:
 State 1:
-          statement ::= statement_names ROUND_OPEN * statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON
-          statement_names ::= * NAME
-          statement_names ::= * NAME DOT statement_names
-      (9) statement_args_maybe ::= *
+          statement ::= dotted_name ROUND_OPEN * statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON
+          dotted_name ::= * NAME
+          dotted_name ::= * NAME DOT dotted_name
+     (16) statement_args_maybe ::= *
           statement_args_maybe ::= * list_contents
           statement_args_maybe ::= * list_contents
           list_contents ::= * value
           list_contents ::= * value
           list_contents ::= * value COMMA list_contents
           list_contents ::= * value COMMA list_contents
@@ -24,27 +24,27 @@ State 1:
           map ::= * BRACKET_OPEN BRACKET_CLOSE
           map ::= * BRACKET_OPEN BRACKET_CLOSE
           map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE
           map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE
           value ::= * STRING
           value ::= * STRING
-          value ::= * statement_names
+          value ::= * dotted_name
           value ::= * list
           value ::= * list
           value ::= * map
           value ::= * map
 
 
-                          NAME shift  22
+                          NAME shift  31
                     CURLY_OPEN shift  3
                     CURLY_OPEN shift  3
                   BRACKET_OPEN shift  4
                   BRACKET_OPEN shift  4
-                        STRING shift  42
-               statement_names shift  43
-          statement_args_maybe shift  20
-                 list_contents shift  37
-                          list shift  44
-                           map shift  45
-                         value shift  23
-                     {default} reduce 9
+                        STRING shift  62
+                   dotted_name shift  63
+          statement_args_maybe shift  29
+                 list_contents shift  57
+                          list shift  64
+                           map shift  65
+                         value shift  32
+                     {default} reduce 16
 
 
 State 2:
 State 2:
-          statement ::= statement_names ARROW statement_names ROUND_OPEN * statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON
-          statement_names ::= * NAME
-          statement_names ::= * NAME DOT statement_names
-      (9) statement_args_maybe ::= *
+          statement ::= dotted_name ARROW dotted_name ROUND_OPEN * statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON
+          dotted_name ::= * NAME
+          dotted_name ::= * NAME DOT dotted_name
+     (16) statement_args_maybe ::= *
           statement_args_maybe ::= * list_contents
           statement_args_maybe ::= * list_contents
           list_contents ::= * value
           list_contents ::= * value
           list_contents ::= * value COMMA list_contents
           list_contents ::= * value COMMA list_contents
@@ -53,25 +53,25 @@ State 2:
           map ::= * BRACKET_OPEN BRACKET_CLOSE
           map ::= * BRACKET_OPEN BRACKET_CLOSE
           map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE
           map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE
           value ::= * STRING
           value ::= * STRING
-          value ::= * statement_names
+          value ::= * dotted_name
           value ::= * list
           value ::= * list
           value ::= * map
           value ::= * map
 
 
-                          NAME shift  22
+                          NAME shift  31
                     CURLY_OPEN shift  3
                     CURLY_OPEN shift  3
                   BRACKET_OPEN shift  4
                   BRACKET_OPEN shift  4
-                        STRING shift  42
-               statement_names shift  43
-          statement_args_maybe shift  29
-                 list_contents shift  37
-                          list shift  44
-                           map shift  45
-                         value shift  23
-                     {default} reduce 9
+                        STRING shift  62
+                   dotted_name shift  63
+          statement_args_maybe shift  38
+                 list_contents shift  57
+                          list shift  64
+                           map shift  65
+                         value shift  32
+                     {default} reduce 16
 
 
 State 3:
 State 3:
-          statement_names ::= * NAME
-          statement_names ::= * NAME DOT statement_names
+          dotted_name ::= * NAME
+          dotted_name ::= * NAME DOT dotted_name
           list_contents ::= * value
           list_contents ::= * value
           list_contents ::= * value COMMA list_contents
           list_contents ::= * value COMMA list_contents
           list ::= * CURLY_OPEN CURLY_CLOSE
           list ::= * CURLY_OPEN CURLY_CLOSE
@@ -81,24 +81,24 @@ State 3:
           map ::= * BRACKET_OPEN BRACKET_CLOSE
           map ::= * BRACKET_OPEN BRACKET_CLOSE
           map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE
           map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE
           value ::= * STRING
           value ::= * STRING
-          value ::= * statement_names
+          value ::= * dotted_name
           value ::= * list
           value ::= * list
           value ::= * map
           value ::= * map
 
 
-                          NAME shift  22
+                          NAME shift  31
                     CURLY_OPEN shift  3
                     CURLY_OPEN shift  3
-                   CURLY_CLOSE shift  39
+                   CURLY_CLOSE shift  59
                   BRACKET_OPEN shift  4
                   BRACKET_OPEN shift  4
-                        STRING shift  42
-               statement_names shift  43
-                 list_contents shift  24
-                          list shift  44
-                           map shift  45
-                         value shift  23
+                        STRING shift  62
+                   dotted_name shift  63
+                 list_contents shift  33
+                          list shift  64
+                           map shift  65
+                         value shift  32
 
 
 State 4:
 State 4:
-          statement_names ::= * NAME
-          statement_names ::= * NAME DOT statement_names
+          dotted_name ::= * NAME
+          dotted_name ::= * NAME DOT dotted_name
           list ::= * CURLY_OPEN CURLY_CLOSE
           list ::= * CURLY_OPEN CURLY_CLOSE
           list ::= * CURLY_OPEN list_contents CURLY_CLOSE
           list ::= * CURLY_OPEN list_contents CURLY_CLOSE
           map_contents ::= * value COLON value
           map_contents ::= * value COLON value
@@ -108,24 +108,24 @@ State 4:
           map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE
           map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE
           map ::= BRACKET_OPEN * map_contents BRACKET_CLOSE
           map ::= BRACKET_OPEN * map_contents BRACKET_CLOSE
           value ::= * STRING
           value ::= * STRING
-          value ::= * statement_names
+          value ::= * dotted_name
           value ::= * list
           value ::= * list
           value ::= * map
           value ::= * map
 
 
-                          NAME shift  22
+                          NAME shift  31
                     CURLY_OPEN shift  3
                     CURLY_OPEN shift  3
                   BRACKET_OPEN shift  4
                   BRACKET_OPEN shift  4
-                 BRACKET_CLOSE shift  46
-                        STRING shift  42
-               statement_names shift  43
-                          list shift  44
-                  map_contents shift  27
-                           map shift  45
-                         value shift  25
+                 BRACKET_CLOSE shift  66
+                        STRING shift  62
+                   dotted_name shift  63
+                          list shift  64
+                  map_contents shift  36
+                           map shift  65
+                         value shift  34
 
 
 State 5:
 State 5:
-          statement_names ::= * NAME
-          statement_names ::= * NAME DOT statement_names
+          dotted_name ::= * NAME
+          dotted_name ::= * NAME DOT dotted_name
           list_contents ::= * value
           list_contents ::= * value
           list_contents ::= * value COMMA list_contents
           list_contents ::= * value COMMA list_contents
           list_contents ::= value COMMA * list_contents
           list_contents ::= value COMMA * list_contents
@@ -134,23 +134,23 @@ State 5:
           map ::= * BRACKET_OPEN BRACKET_CLOSE
           map ::= * BRACKET_OPEN BRACKET_CLOSE
           map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE
           map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE
           value ::= * STRING
           value ::= * STRING
-          value ::= * statement_names
+          value ::= * dotted_name
           value ::= * list
           value ::= * list
           value ::= * map
           value ::= * map
 
 
-                          NAME shift  22
+                          NAME shift  31
                     CURLY_OPEN shift  3
                     CURLY_OPEN shift  3
                   BRACKET_OPEN shift  4
                   BRACKET_OPEN shift  4
-                        STRING shift  42
-               statement_names shift  43
-                 list_contents shift  38
-                          list shift  44
-                           map shift  45
-                         value shift  23
+                        STRING shift  62
+                   dotted_name shift  63
+                 list_contents shift  58
+                          list shift  64
+                           map shift  65
+                         value shift  32
 
 
 State 6:
 State 6:
-          statement_names ::= * NAME
-          statement_names ::= * NAME DOT statement_names
+          dotted_name ::= * NAME
+          dotted_name ::= * NAME DOT dotted_name
           list ::= * CURLY_OPEN CURLY_CLOSE
           list ::= * CURLY_OPEN CURLY_CLOSE
           list ::= * CURLY_OPEN list_contents CURLY_CLOSE
           list ::= * CURLY_OPEN list_contents CURLY_CLOSE
           map_contents ::= * value COLON value
           map_contents ::= * value COLON value
@@ -159,23 +159,23 @@ State 6:
           map ::= * BRACKET_OPEN BRACKET_CLOSE
           map ::= * BRACKET_OPEN BRACKET_CLOSE
           map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE
           map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE
           value ::= * STRING
           value ::= * STRING
-          value ::= * statement_names
+          value ::= * dotted_name
           value ::= * list
           value ::= * list
           value ::= * map
           value ::= * map
 
 
-                          NAME shift  22
+                          NAME shift  31
                     CURLY_OPEN shift  3
                     CURLY_OPEN shift  3
                   BRACKET_OPEN shift  4
                   BRACKET_OPEN shift  4
-                        STRING shift  42
-               statement_names shift  43
-                          list shift  44
-                  map_contents shift  41
-                           map shift  45
-                         value shift  25
+                        STRING shift  62
+                   dotted_name shift  63
+                          list shift  64
+                  map_contents shift  61
+                           map shift  65
+                         value shift  34
 
 
 State 7:
 State 7:
-          statement_names ::= * NAME
-          statement_names ::= * NAME DOT statement_names
+          dotted_name ::= * NAME
+          dotted_name ::= * NAME DOT dotted_name
           list ::= * CURLY_OPEN CURLY_CLOSE
           list ::= * CURLY_OPEN CURLY_CLOSE
           list ::= * CURLY_OPEN list_contents CURLY_CLOSE
           list ::= * CURLY_OPEN list_contents CURLY_CLOSE
           map_contents ::= value COLON * value
           map_contents ::= value COLON * value
@@ -183,284 +183,501 @@ State 7:
           map ::= * BRACKET_OPEN BRACKET_CLOSE
           map ::= * BRACKET_OPEN BRACKET_CLOSE
           map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE
           map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE
           value ::= * STRING
           value ::= * STRING
-          value ::= * statement_names
+          value ::= * dotted_name
           value ::= * list
           value ::= * list
           value ::= * map
           value ::= * map
 
 
-                          NAME shift  22
+                          NAME shift  31
                     CURLY_OPEN shift  3
                     CURLY_OPEN shift  3
                   BRACKET_OPEN shift  4
                   BRACKET_OPEN shift  4
-                        STRING shift  42
-               statement_names shift  43
-                          list shift  44
-                           map shift  45
-                         value shift  26
+                        STRING shift  62
+                   dotted_name shift  63
+                          list shift  64
+                           map shift  65
+                         value shift  35
 
 
 State 8:
 State 8:
-          processes ::= process_or_template NAME CURLY_OPEN * statements CURLY_CLOSE
-          processes ::= process_or_template NAME CURLY_OPEN * statements CURLY_CLOSE processes
-          statement ::= * statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON
-          statement ::= * statement_names ARROW statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON
-          statements ::= * statement
-          statements ::= * statement statements
-          statement_names ::= * NAME
-          statement_names ::= * NAME DOT statement_names
+          statement ::= IF ROUND_OPEN * value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif_maybe else_maybe name_maybe SEMICOLON
+          dotted_name ::= * NAME
+          dotted_name ::= * NAME DOT dotted_name
+          list ::= * CURLY_OPEN CURLY_CLOSE
+          list ::= * CURLY_OPEN list_contents CURLY_CLOSE
+          map ::= * BRACKET_OPEN BRACKET_CLOSE
+          map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE
+          value ::= * STRING
+          value ::= * dotted_name
+          value ::= * list
+          value ::= * map
 
 
-                          NAME shift  22
-                     statement shift  9
-                    statements shift  19
-               statement_names shift  15
+                          NAME shift  31
+                    CURLY_OPEN shift  3
+                  BRACKET_OPEN shift  4
+                        STRING shift  62
+                   dotted_name shift  63
+                          list shift  64
+                           map shift  65
+                         value shift  41
 
 
 State 9:
 State 9:
-          statement ::= * statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON
-          statement ::= * statement_names ARROW statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON
-          statements ::= * statement
-      (5) statements ::= statement *
-          statements ::= * statement statements
-          statements ::= statement * statements
-          statement_names ::= * NAME
-          statement_names ::= * NAME DOT statement_names
+          elif ::= ELIF ROUND_OPEN * value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE
+          elif ::= ELIF ROUND_OPEN * value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif
+          dotted_name ::= * NAME
+          dotted_name ::= * NAME DOT dotted_name
+          list ::= * CURLY_OPEN CURLY_CLOSE
+          list ::= * CURLY_OPEN list_contents CURLY_CLOSE
+          map ::= * BRACKET_OPEN BRACKET_CLOSE
+          map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE
+          value ::= * STRING
+          value ::= * dotted_name
+          value ::= * list
+          value ::= * map
 
 
-                          NAME shift  22
-                     statement shift  9
-                    statements shift  49
-               statement_names shift  15
-                     {default} reduce 5
+                          NAME shift  31
+                    CURLY_OPEN shift  3
+                  BRACKET_OPEN shift  4
+                        STRING shift  62
+                   dotted_name shift  63
+                          list shift  64
+                           map shift  65
+                         value shift  48
 
 
 State 10:
 State 10:
-          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
+          processes ::= process_or_template NAME CURLY_OPEN * statements CURLY_CLOSE
+          processes ::= process_or_template NAME CURLY_OPEN * statements CURLY_CLOSE processes
+          statement ::= * dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON
+          statement ::= * dotted_name ARROW dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON
+          statement ::= * IF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif_maybe else_maybe name_maybe SEMICOLON
+          statements ::= * statement
+          statements ::= * statement statements
+          dotted_name ::= * NAME
+          dotted_name ::= * NAME DOT dotted_name
 
 
-                       PROCESS shift  32
-                      TEMPLATE shift  33
-                     processes shift  31
-           process_or_template shift  17
-                     {default} reduce 1
+                          NAME shift  31
+                            IF shift  40
+                     statement shift  13
+                    statements shift  28
+                   dotted_name shift  24
 
 
 State 11:
 State 11:
-          statement ::= statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE * name_maybe SEMICOLON
-     (23) name_maybe ::= *
-          name_maybe ::= * NAME
+          statement ::= * dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON
+          statement ::= * dotted_name ARROW dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON
+          statement ::= * IF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif_maybe else_maybe name_maybe SEMICOLON
+          statement ::= IF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN * statements CURLY_CLOSE elif_maybe else_maybe name_maybe SEMICOLON
+          statements ::= * statement
+          statements ::= * statement statements
+          dotted_name ::= * NAME
+          dotted_name ::= * NAME DOT dotted_name
 
 
-                          NAME shift  35
-                    name_maybe shift  21
-                     {default} reduce 23
+                          NAME shift  31
+                            IF shift  40
+                     statement shift  13
+                    statements shift  43
+                   dotted_name shift  24
 
 
 State 12:
 State 12:
-          statement_names ::= * NAME
-          statement_names ::= * NAME DOT statement_names
-          statement_names ::= NAME DOT * statement_names
+          statement ::= * dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON
+          statement ::= * dotted_name ARROW dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON
+          statement ::= * IF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif_maybe else_maybe name_maybe SEMICOLON
+          else_maybe ::= ELSE CURLY_OPEN * statements CURLY_CLOSE
+          statements ::= * statement
+          statements ::= * statement statements
+          dotted_name ::= * NAME
+          dotted_name ::= * NAME DOT dotted_name
 
 
-                          NAME shift  22
-               statement_names shift  36
+                          NAME shift  31
+                            IF shift  40
+                     statement shift  13
+                    statements shift  46
+                   dotted_name shift  24
 
 
 State 13:
 State 13:
-          statement ::= statement_names ARROW * statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON
-          statement_names ::= * NAME
-          statement_names ::= * NAME DOT statement_names
-
-                          NAME shift  22
-               statement_names shift  28
+          statement ::= * dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON
+          statement ::= * dotted_name ARROW dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON
+          statement ::= * IF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif_maybe else_maybe name_maybe SEMICOLON
+          statements ::= * statement
+     (12) statements ::= statement *
+          statements ::= * statement statements
+          statements ::= statement * statements
+          dotted_name ::= * NAME
+          dotted_name ::= * NAME DOT dotted_name
+
+                          NAME shift  31
+                            IF shift  40
+                     statement shift  13
+                    statements shift  71
+                   dotted_name shift  24
+                     {default} reduce 12
 
 
 State 14:
 State 14:
-          statement ::= statement_names ARROW statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE * name_maybe SEMICOLON
-     (23) name_maybe ::= *
-          name_maybe ::= * NAME
+          statement ::= * dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON
+          statement ::= * dotted_name ARROW dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON
+          statement ::= * IF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif_maybe else_maybe name_maybe SEMICOLON
+          elif ::= ELIF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN * statements CURLY_CLOSE
+          elif ::= ELIF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN * statements CURLY_CLOSE elif
+          statements ::= * statement
+          statements ::= * statement statements
+          dotted_name ::= * NAME
+          dotted_name ::= * NAME DOT dotted_name
 
 
-                          NAME shift  35
-                    name_maybe shift  30
-                     {default} reduce 23
+                          NAME shift  31
+                            IF shift  40
+                     statement shift  13
+                    statements shift  50
+                   dotted_name shift  24
 
 
 State 15:
 State 15:
-          statement ::= statement_names * ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON
-          statement ::= statement_names * ARROW statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON
+          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
 
 
-                    ROUND_OPEN shift  1
-                         ARROW shift  13
+                       PROCESS shift  52
+                      TEMPLATE shift  53
+                     processes shift  51
+           process_or_template shift  26
+                     {default} reduce 1
 
 
 State 16:
 State 16:
-      (0) input ::= processes *
-
-                             $ reduce 0
+          statement ::= IF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE * elif_maybe else_maybe name_maybe SEMICOLON
+      (6) elif_maybe ::= *
+          elif_maybe ::= * elif
+          elif ::= * ELIF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE
+          elif ::= * ELIF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif
+
+                          ELIF shift  47
+                    elif_maybe shift  21
+                          elif shift  72
+                     {default} reduce 6
 
 
 State 17:
 State 17:
-          processes ::= process_or_template * NAME CURLY_OPEN statements CURLY_CLOSE
-          processes ::= process_or_template * NAME CURLY_OPEN statements CURLY_CLOSE processes
+          statement ::= dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE * name_maybe SEMICOLON
+     (30) name_maybe ::= *
+          name_maybe ::= * NAME
 
 
-                          NAME shift  18
+                          NAME shift  55
+                    name_maybe shift  30
+                     {default} reduce 30
 
 
 State 18:
 State 18:
-          processes ::= process_or_template NAME * CURLY_OPEN statements CURLY_CLOSE
-          processes ::= process_or_template NAME * CURLY_OPEN statements CURLY_CLOSE processes
+          dotted_name ::= * NAME
+          dotted_name ::= * NAME DOT dotted_name
+          dotted_name ::= NAME DOT * dotted_name
 
 
-                    CURLY_OPEN shift  8
+                          NAME shift  31
+                   dotted_name shift  56
 
 
 State 19:
 State 19:
-          processes ::= process_or_template NAME CURLY_OPEN statements * CURLY_CLOSE
-          processes ::= process_or_template NAME CURLY_OPEN statements * CURLY_CLOSE processes
+          statement ::= dotted_name ARROW * dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON
+          dotted_name ::= * NAME
+          dotted_name ::= * NAME DOT dotted_name
 
 
-                   CURLY_CLOSE shift  10
+                          NAME shift  31
+                   dotted_name shift  37
 
 
 State 20:
 State 20:
-          statement ::= statement_names ROUND_OPEN statement_args_maybe * ROUND_CLOSE name_maybe SEMICOLON
+          statement ::= dotted_name ARROW dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE * name_maybe SEMICOLON
+     (30) name_maybe ::= *
+          name_maybe ::= * NAME
 
 
-                   ROUND_CLOSE shift  11
+                          NAME shift  55
+                    name_maybe shift  39
+                     {default} reduce 30
 
 
 State 21:
 State 21:
-          statement ::= statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe * SEMICOLON
+          statement ::= IF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif_maybe * else_maybe name_maybe SEMICOLON
+     (10) else_maybe ::= *
+          else_maybe ::= * ELSE CURLY_OPEN statements CURLY_CLOSE
 
 
-                     SEMICOLON shift  34
+                          ELSE shift  45
+                    else_maybe shift  22
+                     {default} reduce 10
 
 
 State 22:
 State 22:
-      (7) statement_names ::= NAME *
-          statement_names ::= NAME * DOT statement_names
+          statement ::= IF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif_maybe else_maybe * name_maybe SEMICOLON
+     (30) name_maybe ::= *
+          name_maybe ::= * NAME
 
 
-                           DOT shift  12
-                     {default} reduce 7
+                          NAME shift  55
+                    name_maybe shift  44
+                     {default} reduce 30
 
 
 State 23:
 State 23:
-     (11) list_contents ::= value *
-          list_contents ::= value * COMMA list_contents
+          elif ::= * ELIF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE
+      (8) elif ::= ELIF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE *
+          elif ::= * ELIF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif
+          elif ::= ELIF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE * elif
 
 
-                         COMMA shift  5
-                     {default} reduce 11
+                          ELIF shift  47
+                          elif shift  73
+                     {default} reduce 8
 
 
 State 24:
 State 24:
-          list ::= CURLY_OPEN list_contents * CURLY_CLOSE
+          statement ::= dotted_name * ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON
+          statement ::= dotted_name * ARROW dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON
 
 
-                   CURLY_CLOSE shift  40
+                    ROUND_OPEN shift  1
+                         ARROW shift  19
 
 
 State 25:
 State 25:
-          map_contents ::= value * COLON value
-          map_contents ::= value * COLON value COMMA map_contents
+      (0) input ::= processes *
 
 
-                         COLON shift  7
+                             $ reduce 0
 
 
 State 26:
 State 26:
-     (15) map_contents ::= value COLON value *
-          map_contents ::= value COLON value * COMMA map_contents
+          processes ::= process_or_template * NAME CURLY_OPEN statements CURLY_CLOSE
+          processes ::= process_or_template * NAME CURLY_OPEN statements CURLY_CLOSE processes
 
 
-                         COMMA shift  6
-                     {default} reduce 15
+                          NAME shift  27
 
 
 State 27:
 State 27:
-          map ::= BRACKET_OPEN map_contents * BRACKET_CLOSE
+          processes ::= process_or_template NAME * CURLY_OPEN statements CURLY_CLOSE
+          processes ::= process_or_template NAME * CURLY_OPEN statements CURLY_CLOSE processes
 
 
-                 BRACKET_CLOSE shift  47
+                    CURLY_OPEN shift  10
 
 
 State 28:
 State 28:
-          statement ::= statement_names ARROW statement_names * ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON
+          processes ::= process_or_template NAME CURLY_OPEN statements * CURLY_CLOSE
+          processes ::= process_or_template NAME CURLY_OPEN statements * CURLY_CLOSE processes
 
 
-                    ROUND_OPEN shift  2
+                   CURLY_CLOSE shift  15
 
 
 State 29:
 State 29:
-          statement ::= statement_names ARROW statement_names ROUND_OPEN statement_args_maybe * ROUND_CLOSE name_maybe SEMICOLON
+          statement ::= dotted_name ROUND_OPEN statement_args_maybe * ROUND_CLOSE name_maybe SEMICOLON
 
 
-                   ROUND_CLOSE shift  14
+                   ROUND_CLOSE shift  17
 
 
 State 30:
 State 30:
-          statement ::= statement_names ARROW statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe * SEMICOLON
+          statement ::= dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe * SEMICOLON
 
 
-                     SEMICOLON shift  48
+                     SEMICOLON shift  54
 
 
 State 31:
 State 31:
-      (2) processes ::= process_or_template NAME CURLY_OPEN statements CURLY_CLOSE processes *
+     (14) dotted_name ::= NAME *
+          dotted_name ::= NAME * DOT dotted_name
 
 
-                     {default} reduce 2
+                           DOT shift  18
+                     {default} reduce 14
 
 
 State 32:
 State 32:
-     (25) process_or_template ::= PROCESS *
+     (18) list_contents ::= value *
+          list_contents ::= value * COMMA list_contents
 
 
-                     {default} reduce 25
+                         COMMA shift  5
+                     {default} reduce 18
 
 
 State 33:
 State 33:
-     (26) process_or_template ::= TEMPLATE *
+          list ::= CURLY_OPEN list_contents * CURLY_CLOSE
 
 
-                     {default} reduce 26
+                   CURLY_CLOSE shift  60
 
 
 State 34:
 State 34:
-      (3) statement ::= statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON *
+          map_contents ::= value * COLON value
+          map_contents ::= value * COLON value COMMA map_contents
 
 
-                     {default} reduce 3
+                         COLON shift  7
 
 
 State 35:
 State 35:
-     (24) name_maybe ::= NAME *
+     (22) map_contents ::= value COLON value *
+          map_contents ::= value COLON value * COMMA map_contents
 
 
-                     {default} reduce 24
+                         COMMA shift  6
+                     {default} reduce 22
 
 
 State 36:
 State 36:
-      (8) statement_names ::= NAME DOT statement_names *
+          map ::= BRACKET_OPEN map_contents * BRACKET_CLOSE
 
 
-                     {default} reduce 8
+                 BRACKET_CLOSE shift  67
 
 
 State 37:
 State 37:
-     (10) statement_args_maybe ::= list_contents *
+          statement ::= dotted_name ARROW dotted_name * ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON
 
 
-                     {default} reduce 10
+                    ROUND_OPEN shift  2
 
 
 State 38:
 State 38:
-     (12) list_contents ::= value COMMA list_contents *
+          statement ::= dotted_name ARROW dotted_name ROUND_OPEN statement_args_maybe * ROUND_CLOSE name_maybe SEMICOLON
 
 
-                     {default} reduce 12
+                   ROUND_CLOSE shift  20
 
 
 State 39:
 State 39:
-     (13) list ::= CURLY_OPEN CURLY_CLOSE *
+          statement ::= dotted_name ARROW dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe * SEMICOLON
 
 
-                     {default} reduce 13
+                     SEMICOLON shift  68
 
 
 State 40:
 State 40:
-     (14) list ::= CURLY_OPEN list_contents CURLY_CLOSE *
+          statement ::= IF * ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif_maybe else_maybe name_maybe SEMICOLON
 
 
-                     {default} reduce 14
+                    ROUND_OPEN shift  8
 
 
 State 41:
 State 41:
-     (16) map_contents ::= value COLON value COMMA map_contents *
+          statement ::= IF ROUND_OPEN value * ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif_maybe else_maybe name_maybe SEMICOLON
 
 
-                     {default} reduce 16
+                   ROUND_CLOSE shift  42
 
 
 State 42:
 State 42:
-     (19) value ::= STRING *
+          statement ::= IF ROUND_OPEN value ROUND_CLOSE * CURLY_OPEN statements CURLY_CLOSE elif_maybe else_maybe name_maybe SEMICOLON
 
 
-                     {default} reduce 19
+                    CURLY_OPEN shift  11
 
 
 State 43:
 State 43:
-     (20) value ::= statement_names *
+          statement ::= IF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements * CURLY_CLOSE elif_maybe else_maybe name_maybe SEMICOLON
 
 
-                     {default} reduce 20
+                   CURLY_CLOSE shift  16
 
 
 State 44:
 State 44:
-     (21) value ::= list *
+          statement ::= IF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif_maybe else_maybe name_maybe * SEMICOLON
 
 
-                     {default} reduce 21
+                     SEMICOLON shift  69
 
 
 State 45:
 State 45:
-     (22) value ::= map *
+          else_maybe ::= ELSE * CURLY_OPEN statements CURLY_CLOSE
 
 
-                     {default} reduce 22
+                    CURLY_OPEN shift  12
 
 
 State 46:
 State 46:
-     (17) map ::= BRACKET_OPEN BRACKET_CLOSE *
+          else_maybe ::= ELSE CURLY_OPEN statements * CURLY_CLOSE
 
 
-                     {default} reduce 17
+                   CURLY_CLOSE shift  70
 
 
 State 47:
 State 47:
-     (18) map ::= BRACKET_OPEN map_contents BRACKET_CLOSE *
+          elif ::= ELIF * ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE
+          elif ::= ELIF * ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif
 
 
-                     {default} reduce 18
+                    ROUND_OPEN shift  9
 
 
 State 48:
 State 48:
-      (4) statement ::= statement_names ARROW statement_names ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON *
+          elif ::= ELIF ROUND_OPEN value * ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE
+          elif ::= ELIF ROUND_OPEN value * ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif
 
 
-                     {default} reduce 4
+                   ROUND_CLOSE shift  49
 
 
 State 49:
 State 49:
-      (6) statements ::= statement statements *
+          elif ::= ELIF ROUND_OPEN value ROUND_CLOSE * CURLY_OPEN statements CURLY_CLOSE
+          elif ::= ELIF ROUND_OPEN value ROUND_CLOSE * CURLY_OPEN statements CURLY_CLOSE elif
 
 
-                     {default} reduce 6
+                    CURLY_OPEN shift  14
+
+State 50:
+          elif ::= ELIF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements * CURLY_CLOSE
+          elif ::= ELIF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements * CURLY_CLOSE elif
+
+                   CURLY_CLOSE shift  23
+
+State 51:
+      (2) processes ::= process_or_template NAME CURLY_OPEN statements CURLY_CLOSE processes *
+
+                     {default} reduce 2
+
+State 52:
+     (32) process_or_template ::= PROCESS *
+
+                     {default} reduce 32
+
+State 53:
+     (33) process_or_template ::= TEMPLATE *
+
+                     {default} reduce 33
+
+State 54:
+      (3) statement ::= dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON *
+
+                     {default} reduce 3
+
+State 55:
+     (31) name_maybe ::= NAME *
+
+                     {default} reduce 31
+
+State 56:
+     (15) dotted_name ::= NAME DOT dotted_name *
+
+                     {default} reduce 15
+
+State 57:
+     (17) statement_args_maybe ::= list_contents *
+
+                     {default} reduce 17
+
+State 58:
+     (19) list_contents ::= value COMMA list_contents *
+
+                     {default} reduce 19
+
+State 59:
+     (20) list ::= CURLY_OPEN CURLY_CLOSE *
+
+                     {default} reduce 20
+
+State 60:
+     (21) list ::= CURLY_OPEN list_contents CURLY_CLOSE *
+
+                     {default} reduce 21
+
+State 61:
+     (23) map_contents ::= value COLON value COMMA map_contents *
+
+                     {default} reduce 23
+
+State 62:
+     (26) value ::= STRING *
+
+                     {default} reduce 26
+
+State 63:
+     (27) value ::= dotted_name *
+
+                     {default} reduce 27
+
+State 64:
+     (28) value ::= list *
+
+                     {default} reduce 28
+
+State 65:
+     (29) value ::= map *
+
+                     {default} reduce 29
+
+State 66:
+     (24) map ::= BRACKET_OPEN BRACKET_CLOSE *
+
+                     {default} reduce 24
+
+State 67:
+     (25) map ::= BRACKET_OPEN map_contents BRACKET_CLOSE *
+
+                     {default} reduce 25
+
+State 68:
+      (4) statement ::= dotted_name ARROW dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON *
+
+                     {default} reduce 4
+
+State 69:
+      (5) statement ::= IF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif_maybe else_maybe name_maybe SEMICOLON *
+
+                     {default} reduce 5
+
+State 70:
+     (11) else_maybe ::= ELSE CURLY_OPEN statements CURLY_CLOSE *
+
+                     {default} reduce 11
+
+State 71:
+     (13) statements ::= statement statements *
+
+                     {default} reduce 13
+
+State 72:
+      (7) elif_maybe ::= elif *
+
+                     {default} reduce 7
+
+State 73:
+      (9) elif ::= ELIF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif *
+
+                     {default} reduce 9
 
 
 ----------------------------------------------------
 ----------------------------------------------------
 Symbols:
 Symbols:
@@ -472,25 +689,31 @@ Symbols:
     5: ROUND_CLOSE
     5: ROUND_CLOSE
     6: SEMICOLON
     6: SEMICOLON
     7: ARROW
     7: ARROW
-    8: DOT
-    9: COMMA
-   10: COLON
-   11: BRACKET_OPEN
-   12: BRACKET_CLOSE
-   13: STRING
-   14: PROCESS
-   15: TEMPLATE
-   16: error:
-   17: processes: PROCESS TEMPLATE
-   18: statement: NAME
-   19: statements: NAME
-   20: statement_names: NAME
-   21: statement_args_maybe: <lambda> NAME CURLY_OPEN BRACKET_OPEN STRING
-   22: list_contents: NAME CURLY_OPEN BRACKET_OPEN STRING
-   23: list: CURLY_OPEN
-   24: map_contents: NAME CURLY_OPEN BRACKET_OPEN STRING
-   25: map: BRACKET_OPEN
-   26: value: NAME CURLY_OPEN BRACKET_OPEN STRING
-   27: name_maybe: <lambda> NAME
-   28: process_or_template: PROCESS TEMPLATE
-   29: input: PROCESS TEMPLATE
+    8: IF
+    9: ELIF
+   10: ELSE
+   11: DOT
+   12: COMMA
+   13: COLON
+   14: BRACKET_OPEN
+   15: BRACKET_CLOSE
+   16: STRING
+   17: PROCESS
+   18: TEMPLATE
+   19: error:
+   20: processes: PROCESS TEMPLATE
+   21: statement: NAME IF
+   22: elif_maybe: <lambda> ELIF
+   23: elif: ELIF
+   24: else_maybe: <lambda> ELSE
+   25: statements: NAME IF
+   26: dotted_name: NAME
+   27: statement_args_maybe: <lambda> NAME CURLY_OPEN BRACKET_OPEN STRING
+   28: list_contents: NAME CURLY_OPEN BRACKET_OPEN STRING
+   29: list: CURLY_OPEN
+   30: map_contents: NAME CURLY_OPEN BRACKET_OPEN STRING
+   31: map: BRACKET_OPEN
+   32: value: NAME CURLY_OPEN BRACKET_OPEN STRING
+   33: name_maybe: <lambda> NAME
+   34: process_or_template: PROCESS TEMPLATE
+   35: input: PROCESS TEMPLATE

+ 485 - 114
generated/NCDConfigParser_parse.y

@@ -33,50 +33,90 @@
 #include <stddef.h>
 #include <stddef.h>
 
 
 #include <misc/debug.h>
 #include <misc/debug.h>
-#include <ncd/NCDConfig.h>
+#include <misc/concat_strings.h>
+#include <ncd/NCDAst.h>
 
 
-struct parser_minor {
+struct parser_out {
+    int out_of_memory;
+    int syntax_error;
+    int have_ast;
+    NCDProgram ast;
+};
+
+struct token {
     char *str;
     char *str;
     size_t len;
     size_t len;
 };
 };
 
 
-struct parser_out {
-    int out_of_memory;
-    int syntax_error;
-    struct NCDConfig_processes *ast;
+struct program {
+    int have;
+    NCDProgram v;
+};
+
+struct block {
+    int have;
+    NCDBlock v;
+};
+
+struct statement {
+    int have;
+    NCDStatement v;
+};
+
+struct ifblock {
+    int have;
+    NCDIfBlock v;
+};
+
+struct value {
+    int have;
+    NCDValue v;
 };
 };
 
 
+static void free_token (struct token o) { free(o.str); }
+static void free_program (struct program o) { if (o.have) NCDProgram_Free(&o.v); }
+static void free_block (struct block o) { if (o.have) NCDBlock_Free(&o.v); }
+static void free_statement (struct statement o) { if (o.have) NCDStatement_Free(&o.v); }
+static void free_ifblock (struct ifblock o) { if (o.have) NCDIfBlock_Free(&o.v); }
+static void free_value (struct value o) { if (o.have) NCDValue_Free(&o.v); }
+
 }
 }
 
 
-%extra_argument {struct parser_out *parser_out}
-
-%token_type {struct parser_minor}
-
-%token_destructor { free($$.str); }
-
-%type processes {struct NCDConfig_processes *}
-%type statement {struct NCDConfig_statements *}
-%type statements {struct NCDConfig_statements *}
-%type statement_names {struct NCDConfig_strings *}
-%type statement_args_maybe {struct NCDConfig_list *}
-%type list_contents {struct NCDConfig_list *}
-%type list {struct NCDConfig_list *}
-%type map_contents {struct NCDConfig_list *}
-%type map {struct NCDConfig_list *}
-%type value {struct NCDConfig_list *}
-%type name_maybe {char *}
-%type process_or_template {int}
-
-%destructor processes { NCDConfig_free_processes($$); }
-%destructor statement { NCDConfig_free_statements($$); }
-%destructor statements { NCDConfig_free_statements($$); }
-%destructor statement_names { NCDConfig_free_strings($$); }
-%destructor statement_args_maybe { NCDConfig_free_list($$); }
-%destructor list_contents { NCDConfig_free_list($$); }
-%destructor list { NCDConfig_free_list($$); }
-%destructor map_contents { NCDConfig_free_list($$); }
-%destructor map { NCDConfig_free_list($$); }
-%destructor value { NCDConfig_free_list($$); }
+%extra_argument { struct parser_out *parser_out }
+
+%token_type { struct token }
+
+%token_destructor { free_token($$); }
+
+%type processes { struct program }
+%type statement { struct statement }
+%type elif_maybe { struct ifblock }
+%type elif { struct ifblock }
+%type else_maybe { struct block }
+%type statements { struct block }
+%type dotted_name { char * }
+%type statement_args_maybe { struct value }
+%type list_contents { struct value }
+%type list { struct value }
+%type map_contents { struct value }
+%type map  { struct value }
+%type value  { struct value }
+%type name_maybe { char * }
+%type process_or_template { int }
+
+%destructor processes { free_program($$); }
+%destructor statement { free_statement($$); }
+%destructor elif_maybe { free_ifblock($$); }
+%destructor elif { free_ifblock($$); }
+%destructor else_maybe { free_block($$); }
+%destructor statements { free_block($$); }
+%destructor dotted_name { free($$); }
+%destructor statement_args_maybe { free_value($$); }
+%destructor list_contents { free_value($$); }
+%destructor list { free_value($$); }
+%destructor map_contents { free_value($$); }
+%destructor map { free_value($$); }
+%destructor value { free_value($$); }
 %destructor name_maybe { free($$); }
 %destructor name_maybe { free($$); }
 
 
 %stack_size 0
 %stack_size 0
@@ -88,76 +128,324 @@ struct parser_out {
 // workaroud Lemon bug: if the stack overflows, the token that caused the overflow will be leaked
 // workaroud Lemon bug: if the stack overflows, the token that caused the overflow will be leaked
 %stack_overflow {
 %stack_overflow {
     if (yypMinor) {
     if (yypMinor) {
-        free(yypMinor->yy0.str);
+        free_token(yypMinor->yy0);
     }
     }
 }
 }
 
 
 input ::= processes(A). {
 input ::= processes(A). {
-    parser_out->ast = A;
+    ASSERT(!parser_out->have_ast)
 
 
-    if (!A) {
-        parser_out->out_of_memory = 1;
+    if (A.have) {
+        parser_out->have_ast = 1;
+        parser_out->ast = A.v;
     }
     }
 }
 }
 
 
 processes(R) ::= process_or_template(T) NAME(A) CURLY_OPEN statements(B) CURLY_CLOSE. {
 processes(R) ::= process_or_template(T) NAME(A) CURLY_OPEN statements(B) CURLY_CLOSE. {
-    R = NCDConfig_make_processes(T, A.str, B, NULL);
-    if (!R) {
-        parser_out->out_of_memory = 1;
+    ASSERT(A.str)
+    if (!B.have) {
+        goto failA0;
+    }
+
+    NCDProcess proc;
+    if (!NCDProcess_Init(&proc, T, A.str, B.v)) {
+        goto failA0;
+    }
+    B.have = 0;
+
+    NCDProgram prog;
+    NCDProgram_Init(&prog);
+
+    if (!NCDProgram_PrependProcess(&prog, proc)) {
+        goto failA1;
     }
     }
+
+    R.have = 1;
+    R.v = prog;
+    goto doneA;
+
+failA1:
+    NCDProgram_Free(&prog);
+    NCDProcess_Free(&proc);
+failA0:
+    R.have = 0;
+    parser_out->out_of_memory = 1;
+doneA:
+    free_token(A);
+    free_block(B);
 }
 }
 
 
 processes(R) ::= process_or_template(T) NAME(A) CURLY_OPEN statements(B) CURLY_CLOSE processes(N). {
 processes(R) ::= process_or_template(T) NAME(A) CURLY_OPEN statements(B) CURLY_CLOSE processes(N). {
-    R = NCDConfig_make_processes(T, A.str, B, N);
-    if (!R) {
-        parser_out->out_of_memory = 1;
+    ASSERT(A.str)
+    if (!B.have || !N.have) {
+        goto failB0;
+    }
+
+    NCDProcess proc;
+    if (!NCDProcess_Init(&proc, T, A.str, B.v)) {
+        goto failB0;
     }
     }
+    B.have = 0;
+
+    if (!NCDProgram_PrependProcess(&N.v, proc)) {
+        goto failB1;
+    }
+
+    R.have = 1;
+    R.v = N.v;
+    N.have = 0;
+    goto doneB;
+
+failB1:
+    NCDProcess_Free(&proc);
+failB0:
+    R.have = 0;
+    parser_out->out_of_memory = 1;
+doneB:
+    free_token(A);
+    free_block(B);
+    free_program(N);
 }
 }
 
 
-statement(R) ::= statement_names(A) ROUND_OPEN statement_args_maybe(B) ROUND_CLOSE name_maybe(C) SEMICOLON. {
-    R = NCDConfig_make_statements(NULL, A, B, C, NULL);
-    if (!R) {
-        parser_out->out_of_memory = 1;
+statement(R) ::= dotted_name(A) ROUND_OPEN statement_args_maybe(B) ROUND_CLOSE name_maybe(C) SEMICOLON. {
+    if (!A || !B.have) {
+        goto failC0;
     }
     }
+
+    if (!NCDStatement_InitReg(&R.v, C, NULL, A, B.v)) {
+        goto failC0;
+    }
+    B.have = 0;
+
+    R.have = 1;
+    goto doneC;
+
+failC0:
+    R.have = 0;
+    parser_out->out_of_memory = 1;
+doneC:
+    free(A);
+    free_value(B);
+    free(C);
 }
 }
 
 
-statement(R) ::= statement_names(M) ARROW statement_names(A) ROUND_OPEN statement_args_maybe(B) ROUND_CLOSE name_maybe(C) SEMICOLON. {
-    R = NCDConfig_make_statements(M, A, B, C, NULL);
-    if (!R) {
-        parser_out->out_of_memory = 1;
+statement(R) ::= dotted_name(M) ARROW dotted_name(A) ROUND_OPEN statement_args_maybe(B) ROUND_CLOSE name_maybe(C) SEMICOLON. {
+    if (!M || !A || !B.have) {
+        goto failD0;
+    }
+
+    if (!NCDStatement_InitReg(&R.v, C, M, A, B.v)) {
+        goto failD0;
     }
     }
+    B.have = 0;
+
+    R.have = 1;
+    goto doneD;
+
+failD0:
+    R.have = 0;
+    parser_out->out_of_memory = 1;
+doneD:
+    free(M);
+    free(A);
+    free_value(B);
+    free(C);
 }
 }
 
 
-statements(R) ::= statement(A). {
+statement(R) ::= IF ROUND_OPEN value(A) ROUND_CLOSE CURLY_OPEN statements(B) CURLY_CLOSE elif_maybe(I) else_maybe(E) name_maybe(C) SEMICOLON. {
+    if (!A.have || !B.have || !I.have) {
+        goto failE0;
+    }
+
+    NCDIf ifc;
+    NCDIf_Init(&ifc, A.v, B.v);
+    A.have = 0;
+    B.have = 0;
+
+    if (!NCDIfBlock_PrependIf(&I.v, ifc)) {
+        NCDIf_Free(&ifc);
+        goto failE0;
+    }
+
+    if (!NCDStatement_InitIf(&R.v, C, I.v)) {
+        goto failE0;
+    }
+    I.have = 0;
+
+    if (E.have) {
+        NCDStatement_IfAddElse(&R.v, E.v);
+        E.have = 0;
+    }
+
+    R.have = 1;
+    goto doneE;
+
+failE0:
+    R.have = 0;
+    parser_out->out_of_memory = 1;
+doneE:
+    free_value(A);
+    free_block(B);
+    free_ifblock(I);
+    free_block(E);
+    free(C);
+}
+
+elif_maybe(R) ::= . {
+    NCDIfBlock_Init(&R.v);
+    R.have = 1;
+}
+
+elif_maybe(R) ::= elif(A). {
     R = A;
     R = A;
 }
 }
 
 
-statements(R) ::= statement(A) statements(N). {
-    if (!A) {
-        NCDConfig_free_statements(N);
-    } else {
-        ASSERT(!A->next)
-        A->next = N;
+elif(R) ::= ELIF ROUND_OPEN value(A) ROUND_CLOSE CURLY_OPEN statements(B) CURLY_CLOSE. {
+    if (!A.have || !B.have) {
+        goto failF0;
     }
     }
-    R = A;
+
+    NCDIfBlock_Init(&R.v);
+
+    NCDIf ifc;
+    NCDIf_Init(&ifc, A.v, B.v);
+    A.have = 0;
+    B.have = 0;
+
+    if (!NCDIfBlock_PrependIf(&R.v, ifc)) {
+        goto failF1;
+    }
+
+    R.have = 1;
+    goto doneF0;
+
+failF1:
+    NCDIf_Free(&ifc);
+    NCDIfBlock_Free(&R.v);
+failF0:
+    R.have = 0;
+    parser_out->out_of_memory = 1;
+doneF0:
+    free_value(A);
+    free_block(B);
 }
 }
 
 
-statement_names(R) ::= NAME(A). {
-    R = NCDConfig_make_strings(A.str, 0, NULL);
-    if (!R) {
-        parser_out->out_of_memory = 1;
+elif(R) ::= ELIF ROUND_OPEN value(A) ROUND_CLOSE CURLY_OPEN statements(B) CURLY_CLOSE elif(N). {
+    if (!A.have || !B.have || !N.have) {
+        goto failG0;
     }
     }
+
+    NCDIf ifc;
+    NCDIf_Init(&ifc, A.v, B.v);
+    A.have = 0;
+    B.have = 0;
+
+    if (!NCDIfBlock_PrependIf(&N.v, ifc)) {
+        goto failG1;
+    }
+
+    R.have = 1;
+    R.v = N.v;
+    N.have = 0;
+    goto doneG0;
+
+failG1:
+    NCDIf_Free(&ifc);
+failG0:
+    R.have = 0;
+    parser_out->out_of_memory = 1;
+doneG0:
+    free_value(A);
+    free_block(B);
+    free_ifblock(N);
 }
 }
 
 
-statement_names(R) ::= NAME(A) DOT statement_names(N). {
-    R = NCDConfig_make_strings(A.str, 1, N);
-    if (!R) {
-        parser_out->out_of_memory = 1;
+else_maybe(R) ::= . {
+    R.have = 0;
+}
+
+else_maybe(R) ::= ELSE CURLY_OPEN statements(B) CURLY_CLOSE. {
+    R = B;
+}
+
+statements(R) ::= statement(A). {
+    if (!A.have) {
+        goto failH0;
+    }
+
+    NCDBlock_Init(&R.v);
+
+    if (!NCDBlock_PrependStatement(&R.v, A.v)) {
+        goto failH1;
     }
     }
+    A.have = 0;
+
+    R.have = 1;
+    goto doneH;
+
+failH1:
+    NCDBlock_Free(&R.v);
+failH0:
+    R.have = 0;
+    parser_out->out_of_memory = 1;
+doneH:
+    free_statement(A);
 }
 }
 
 
-statement_args_maybe(R) ::= . {
+statements(R) ::= statement(A) statements(N). {
+    if (!A.have || !N.have) {
+        goto failI0;
+    }
+
+    if (!NCDBlock_PrependStatement(&N.v, A.v)) {
+        goto failI1;
+    }
+    A.have = 0;
+
+    R.have = 1;
+    R.v = N.v;
+    N.have = 0;
+    goto doneI;
+
+failI1:
+    NCDBlock_Free(&R.v);
+failI0:
+    R.have = 0;
+    parser_out->out_of_memory = 1;
+doneI:
+    free_statement(A);
+    free_block(N);
+}
+
+dotted_name(R) ::= NAME(A). {
+    ASSERT(A.str)
+
+    R = A.str;
+}
+
+dotted_name(R) ::= NAME(A) DOT dotted_name(N). {
+    ASSERT(A.str)
+    if (!N) {
+        goto failJ0;
+    }
+
+    if (!(R = concat_strings(3, A.str, ".", N))) {
+        goto failJ0;
+    }
+
+    goto doneJ;
+
+failJ0:
     R = NULL;
     R = NULL;
+    parser_out->out_of_memory = 1;
+doneJ:
+    free_token(A);
+    free(N);
+}
+
+statement_args_maybe(R) ::= . {
+    R.have = 1;
+    NCDValue_InitList(&R.v);
 }
 }
 
 
 statement_args_maybe(R) ::= list_contents(A). {
 statement_args_maybe(R) ::= list_contents(A). {
@@ -165,21 +453,55 @@ statement_args_maybe(R) ::= list_contents(A). {
 }
 }
 
 
 list_contents(R) ::= value(A). {
 list_contents(R) ::= value(A). {
-    R = A;
+    if (!A.have) {
+        goto failL0;
+    }
+
+    NCDValue_InitList(&R.v);
+
+    if (!NCDValue_ListPrepend(&R.v, A.v)) {
+        goto failL1;
+    }
+    A.have = 0;
+
+    R.have = 1;
+    goto doneL;
+
+failL1:
+    NCDValue_Free(&R.v);
+failL0:
+    R.have = 0;
+    parser_out->out_of_memory = 1;
+doneL:
+    free_value(A);
 }
 }
 
 
 list_contents(R) ::= value(A) COMMA list_contents(N). {
 list_contents(R) ::= value(A) COMMA list_contents(N). {
-    if (!A) {
-        NCDConfig_free_list(N);
-    } else {
-        ASSERT(!A->next)
-        A->next = N;
+    if (!A.have || !N.have) {
+        goto failM0;
     }
     }
-    R = A;
+
+    if (!NCDValue_ListPrepend(&N.v, A.v)) {
+        goto failM0;
+    }
+    A.have = 0;
+
+    R.have = 1;
+    R.v = N.v;
+    N.have = 0;
+    goto doneM;
+
+failM0:
+    R.have = 0;
+    parser_out->out_of_memory = 1;
+doneM:
+    free_value(A);
+    free_value(N);
 }
 }
 
 
 list(R) ::= CURLY_OPEN CURLY_CLOSE. {
 list(R) ::= CURLY_OPEN CURLY_CLOSE. {
-    R = NULL;
+    R.have = 1;
+    NCDValue_InitList(&R.v);
 }
 }
 
 
 list(R) ::= CURLY_OPEN list_contents(A) CURLY_CLOSE. {
 list(R) ::= CURLY_OPEN list_contents(A) CURLY_CLOSE. {
@@ -187,35 +509,66 @@ list(R) ::= CURLY_OPEN list_contents(A) CURLY_CLOSE. {
 }
 }
 
 
 map_contents(R) ::= value(A) COLON value(B). {
 map_contents(R) ::= value(A) COLON value(B). {
-    if (!A || !B) {
-        NCDConfig_free_list(A);
-        NCDConfig_free_list(B);
-        R = NULL;
-    } else {
-        ASSERT(!A->next)
-        ASSERT(!B->next)
-        A->next = B;
-        R = A;
+    if (!A.have || !B.have) {
+        goto failS0;
     }
     }
+
+    NCDValue_InitMap(&R.v);
+
+    if (!NCDValue_MapInsert(&R.v, A.v, B.v)) {
+        goto failS1;
+    }
+    A.have = 0;
+    B.have = 0;
+
+    R.have = 1;
+    goto doneS;
+
+failS1:
+    NCDValue_Free(&R.v);
+failS0:
+    R.have = 0;
+    parser_out->out_of_memory = 1;
+doneS:
+    free_value(A);
+    free_value(B);
 }
 }
 
 
 map_contents(R) ::= value(A) COLON value(B) COMMA map_contents(N). {
 map_contents(R) ::= value(A) COLON value(B) COMMA map_contents(N). {
-    if (!A || !B) {
-        NCDConfig_free_list(A);
-        NCDConfig_free_list(B);
-        NCDConfig_free_list(N);
-        R = NULL;
-    } else {
-        ASSERT(!A->next)
-        ASSERT(!B->next)
-        A->next = B;
-        B->next = N;
-        R = A;
+    if (!A.have || !B.have || !N.have) {
+        goto failT0;
+    }
+
+    if (NCDValue_MapFindKey(&N.v, &A.v)) {
+        BLog(BLOG_ERROR, "duplicate key in map");
+        R.have = 0;
+        parser_out->syntax_error = 1;
+        goto doneT;
+    }
+
+    if (!NCDValue_MapInsert(&N.v, A.v, B.v)) {
+        goto failT0;
     }
     }
+    A.have = 0;
+    B.have = 0;
+
+    R.have = 1;
+    R.v = N.v;
+    N.have = 0;
+    goto doneT;
+
+failT0:
+    R.have = 0;
+    parser_out->out_of_memory = 1;
+doneT:
+    free_value(A);
+    free_value(B);
+    free_value(N);
 }
 }
 
 
 map(R) ::= BRACKET_OPEN BRACKET_CLOSE. {
 map(R) ::= BRACKET_OPEN BRACKET_CLOSE. {
-    R = NULL;
+    R.have = 1;
+    NCDValue_InitMap(&R.v);
 }
 }
 
 
 map(R) ::= BRACKET_OPEN map_contents(A) BRACKET_CLOSE. {
 map(R) ::= BRACKET_OPEN map_contents(A) BRACKET_CLOSE. {
@@ -223,31 +576,47 @@ map(R) ::= BRACKET_OPEN map_contents(A) BRACKET_CLOSE. {
 }
 }
 
 
 value(R) ::= STRING(A). {
 value(R) ::= STRING(A). {
-    R = NCDConfig_make_list_string(A.str, A.len, NULL);
-    if (!R) {
-        parser_out->out_of_memory = 1;
+    ASSERT(A.str)
+
+    if (!NCDValue_InitStringBin(&R.v, A.str, A.len)) {
+        goto failU0;
     }
     }
+
+    R.have = 1;
+    goto doneU;
+
+failU0:
+    R.have = 0;
+    parser_out->out_of_memory = 1;
+doneU:
+    free_token(A);
 }
 }
 
 
-value(R) ::= statement_names(A). {
-    R = NCDConfig_make_list_var(A, NULL);
-    if (!R) {
-        parser_out->out_of_memory = 1;
+value(R) ::= dotted_name(A). {
+    if (!A) {
+        goto failV0;
+    }
+
+    if (!NCDValue_InitVar(&R.v, A)) {
+        goto failV0;
     }
     }
+
+    R.have = 1;
+    goto doneV;
+
+failV0:
+    R.have = 0;
+    parser_out->out_of_memory = 1;
+doneV:
+    free(A);
 }
 }
 
 
 value(R) ::= list(A). {
 value(R) ::= list(A). {
-    R = NCDConfig_make_list_list(A, NULL);
-    if (!R) {
-        parser_out->out_of_memory = 1;
-    }
+    R = A;
 }
 }
 
 
 value(R) ::= map(A). {
 value(R) ::= map(A). {
-    R = NCDConfig_make_list_maplist(A, NULL);
-    if (!R) {
-        parser_out->out_of_memory = 1;
-    }
+    R = A;
 }
 }
 
 
 name_maybe(R) ::= . {
 name_maybe(R) ::= . {
@@ -255,6 +624,8 @@ name_maybe(R) ::= . {
 }
 }
 
 
 name_maybe(R) ::= NAME(A). {
 name_maybe(R) ::= NAME(A). {
+    ASSERT(A.str)
+
     R = A.str;
     R = A.str;
 }
 }
 
 

+ 25 - 8
ncd/CMakeLists.txt

@@ -21,24 +21,40 @@ if (BADVPN_USE_INOTIFY)
     )
     )
 endif ()
 endif ()
 
 
-add_library(ncdconfig
-    NCDConfig.c
+add_library(ncdtokenizer
     NCDConfigTokenizer.c
     NCDConfigTokenizer.c
-    NCDConfigParser.c
 )
 )
-target_link_libraries(ncdconfig base)
+target_link_libraries(ncdtokenizer base)
 
 
 add_library(ncdvalue
 add_library(ncdvalue
     NCDValue.c
     NCDValue.c
-    NCDValueParser.c
+)
+
+add_library(ncdvaluegenerator
     NCDValueGenerator.c
     NCDValueGenerator.c
 )
 )
-target_link_libraries(ncdvalue ncdconfig)
+target_link_libraries(ncdvaluegenerator base ncdvalue)
+
+add_library(ncdvalueparser
+    NCDConfig.c
+    NCDValueParser.c
+)
+target_link_libraries(ncdvalueparser base ncdvalue ncdtokenizer)
+
+add_library(ncdast
+    NCDAst.c
+)
+target_link_libraries(ncdast ncdvalue)
+
+add_library(ncdconfigparser
+    NCDConfigParser.c
+)
+target_link_libraries(ncdconfigparser base ncdtokenizer ncdast)
 
 
 add_library(ncdrequest
 add_library(ncdrequest
     NCDRequestClient.c
     NCDRequestClient.c
 )
 )
-target_link_libraries(ncdrequest base system ncdvalue)
+target_link_libraries(ncdrequest base system ncdvalue ncdvaluegenerator ncdvalueparser)
 
 
 add_library(ncdinterfacemonitor
 add_library(ncdinterfacemonitor
     NCDInterfaceMonitor.c
     NCDInterfaceMonitor.c
@@ -51,6 +67,7 @@ add_executable(badvpn-ncd
     NCDModuleIndex.c
     NCDModuleIndex.c
     NCDIfConfig.c
     NCDIfConfig.c
     NCDObject.c
     NCDObject.c
+    NCDSugar.c
     BEventLock.c
     BEventLock.c
     modules/command_template.c
     modules/command_template.c
     modules/event_template.c
     modules/event_template.c
@@ -114,7 +131,7 @@ add_executable(badvpn-ncd
     modules/call2.c
     modules/call2.c
     ${NCD_ADDITIONAL_SOURCES}
     ${NCD_ADDITIONAL_SOURCES}
 )
 )
-target_link_libraries(badvpn-ncd system flow flowextra dhcpclient arpprobe ncdconfig ncdvalue udevmonitor ncdinterfacemonitor ncdrequest)
+target_link_libraries(badvpn-ncd system flow flowextra dhcpclient arpprobe ncdvalue ncdvaluegenerator ncdvalueparser ncdconfigparser udevmonitor ncdinterfacemonitor ncdrequest)
 
 
 if (BADVPN_USE_LINUX_INPUT)
 if (BADVPN_USE_LINUX_INPUT)
     string(REPLACE " " ";" FLAGS_LIST "${CMAKE_C_FLAGS}")
     string(REPLACE " " ";" FLAGS_LIST "${CMAKE_C_FLAGS}")

+ 432 - 0
ncd/NCDAst.c

@@ -0,0 +1,432 @@
+#include <stdlib.h>
+#include <limits.h>
+
+#include <misc/offset.h>
+
+#include "NCDAst.h"
+
+void NCDProgram_Init (NCDProgram *o)
+{
+    LinkedList1_Init(&o->processes_list);
+}
+
+void NCDProgram_Free (NCDProgram *o)
+{
+    LinkedList1Node *ln;
+    while (ln = LinkedList1_GetFirst(&o->processes_list)) {
+        struct ProgramProcess *e = UPPER_OBJECT(ln, struct ProgramProcess, processes_list_node);
+        NCDProcess_Free(&e->p);
+        LinkedList1_Remove(&o->processes_list, &e->processes_list_node);
+        free(e);
+    }
+}
+
+NCDProcess * NCDProgram_PrependProcess (NCDProgram *o, NCDProcess p)
+{
+    struct ProgramProcess *e = malloc(sizeof(*e));
+    if (!e) {
+        return NULL;
+    }
+    
+    LinkedList1_Prepend(&o->processes_list, &e->processes_list_node);
+    e->p = p;
+    
+    return &e->p;
+}
+
+NCDProcess * NCDProgram_FirstProcess (NCDProgram *o)
+{
+    LinkedList1Node *ln = LinkedList1_GetFirst(&o->processes_list);
+    if (!ln) {
+        return NULL;
+    }
+    
+    struct ProgramProcess *e = UPPER_OBJECT(ln, struct ProgramProcess, processes_list_node);
+    
+    return &e->p;
+}
+
+NCDProcess * NCDProgram_NextProcess (NCDProgram *o, NCDProcess *ep)
+{
+    ASSERT(ep)
+    
+    struct ProgramProcess *cur_e = UPPER_OBJECT(ep, struct ProgramProcess, p);
+    
+    LinkedList1Node *ln = LinkedList1Node_Next(&cur_e->processes_list_node);
+    if (!ln) {
+        return NULL;
+    }
+    
+    struct ProgramProcess *e = UPPER_OBJECT(ln, struct ProgramProcess, processes_list_node);
+    
+    return &e->p;
+}
+
+int NCDProcess_Init (NCDProcess *o, int is_template, const char *name, NCDBlock block)
+{
+    ASSERT(is_template == !!is_template)
+    ASSERT(name)
+    
+    if (!(o->name = strdup(name))) {
+        return 0;
+    }
+    
+    o->is_template = is_template;
+    o->block = block;
+    
+    return 1;
+}
+
+void NCDProcess_Free (NCDProcess *o)
+{
+    NCDBlock_Free(&o->block);
+    free(o->name);
+}
+
+int NCDProcess_IsTemplate (NCDProcess *o)
+{
+    return o->is_template;
+}
+
+const char * NCDProcess_Name (NCDProcess *o)
+{
+    return o->name;
+}
+
+NCDBlock * NCDProcess_Block (NCDProcess *o)
+{
+    return &o->block;
+}
+
+void NCDBlock_Init (NCDBlock *o)
+{
+    LinkedList1_Init(&o->statements_list);
+    o->count = 0;
+}
+
+void NCDBlock_Free (NCDBlock *o)
+{
+    LinkedList1Node *ln;
+    while (ln = LinkedList1_GetFirst(&o->statements_list)) {
+        struct BlockStatement *e = UPPER_OBJECT(ln, struct BlockStatement, statements_list_node);
+        NCDStatement_Free(&e->s);
+        LinkedList1_Remove(&o->statements_list, &e->statements_list_node);
+        free(e);
+    }
+}
+
+int NCDBlock_PrependStatement (NCDBlock *o, NCDStatement s)
+{
+    return NCDBlock_InsertStatementAfter(o, NULL, s);
+}
+
+int NCDBlock_InsertStatementAfter (NCDBlock *o, NCDStatement *after, NCDStatement s)
+{
+    struct BlockStatement *after_e = NULL;
+    if (after) {
+        after_e = UPPER_OBJECT(after, struct BlockStatement, s);
+    }
+    
+    if (o->count == SIZE_MAX) {
+        return 0;
+    }
+    
+    struct BlockStatement *e = malloc(sizeof(*e));
+    if (!e) {
+        return 0;
+    }
+    
+    if (after_e) {
+        LinkedList1_InsertAfter(&o->statements_list, &e->statements_list_node, &after_e->statements_list_node);
+    } else {
+        LinkedList1_Prepend(&o->statements_list, &e->statements_list_node);
+    }
+    e->s = s;
+    
+    o->count++;
+    
+    return 1;
+}
+
+NCDStatement * NCDBlock_ReplaceStatement (NCDBlock *o, NCDStatement *es, NCDStatement s)
+{
+    ASSERT(es)
+    
+    struct BlockStatement *e = UPPER_OBJECT(es, struct BlockStatement, s);
+    
+    NCDStatement_Free(&e->s);
+    e->s = s;
+    
+    return &e->s;
+}
+
+NCDStatement * NCDBlock_FirstStatement (NCDBlock *o)
+{
+    LinkedList1Node *ln = LinkedList1_GetFirst(&o->statements_list);
+    if (!ln) {
+        return NULL;
+    }
+    
+    struct BlockStatement *e = UPPER_OBJECT(ln, struct BlockStatement, statements_list_node);
+    
+    return &e->s;
+}
+
+NCDStatement * NCDBlock_NextStatement (NCDBlock *o, NCDStatement *es)
+{
+    ASSERT(es)
+    
+    struct BlockStatement *cur_e = UPPER_OBJECT(es, struct BlockStatement, s);
+    
+    LinkedList1Node *ln = LinkedList1Node_Next(&cur_e->statements_list_node);
+    if (!ln) {
+        return NULL;
+    }
+    
+    struct BlockStatement *e = UPPER_OBJECT(ln, struct BlockStatement, statements_list_node);
+    
+    return &e->s;
+}
+
+size_t NCDBlock_NumStatements (NCDBlock *o)
+{
+    return o->count;
+}
+
+int NCDStatement_InitReg (NCDStatement *o, const char *name, const char *objname, const char *cmdname, NCDValue args)
+{
+    ASSERT(cmdname)
+    ASSERT(NCDValue_IsList(&args))
+    
+    o->name = NULL;
+    o->reg.objname = NULL;
+    o->reg.cmdname = NULL;
+    
+    if (name && !(o->name = strdup(name))) {
+        goto fail;
+    }
+    
+    if (objname && !(o->reg.objname = strdup(objname))) {
+        goto fail;
+    }
+    
+    if (!(o->reg.cmdname = strdup(cmdname))) {
+        goto fail;
+    }
+    
+    o->type = NCDSTATEMENT_REG;
+    o->reg.args = args;
+    
+    return 1;
+    
+fail:
+    free(o->name);
+    free(o->reg.objname);
+    free(o->reg.cmdname);
+    return 0;
+}
+
+int NCDStatement_InitIf (NCDStatement *o, const char *name, NCDIfBlock ifblock)
+{
+    o->name = NULL;
+    
+    if (name && !(o->name = strdup(name))) {
+        return 0;
+    }
+    
+    o->type = NCDSTATEMENT_IF;
+    o->ifc.ifblock = ifblock;
+    o->ifc.have_else = 0;
+    
+    return 1;
+}
+
+void NCDStatement_Free (NCDStatement *o)
+{
+    switch (o->type) {
+        case NCDSTATEMENT_REG: {
+            NCDValue_Free(&o->reg.args);
+            free(o->reg.cmdname);
+            free(o->reg.objname);
+        } break;
+        
+        case NCDSTATEMENT_IF: {
+            if (o->ifc.have_else) {
+                NCDBlock_Free(&o->ifc.else_block);
+            }
+            
+            NCDIfBlock_Free(&o->ifc.ifblock);
+        } break;
+        
+        default: ASSERT(0);
+    }
+    
+    free(o->name);
+}
+
+int NCDStatement_Type (NCDStatement *o)
+{
+    return o->type;
+}
+
+const char * NCDStatement_Name (NCDStatement *o)
+{
+    return o->name;
+}
+
+const char * NCDStatement_RegObjName (NCDStatement *o)
+{
+    ASSERT(o->type == NCDSTATEMENT_REG)
+    
+    return o->reg.objname;
+}
+
+const char * NCDStatement_RegCmdName (NCDStatement *o)
+{
+    ASSERT(o->type == NCDSTATEMENT_REG)
+    
+    return o->reg.cmdname;
+}
+
+NCDValue * NCDStatement_RegArgs (NCDStatement *o)
+{
+    ASSERT(o->type == NCDSTATEMENT_REG)
+    
+    return &o->reg.args;
+}
+
+NCDIfBlock * NCDStatement_IfBlock (NCDStatement *o)
+{
+    ASSERT(o->type == NCDSTATEMENT_IF)
+    
+    return &o->ifc.ifblock;
+}
+
+void NCDStatement_IfAddElse (NCDStatement *o, NCDBlock else_block)
+{
+    ASSERT(o->type == NCDSTATEMENT_IF)
+    ASSERT(!o->ifc.have_else)
+    
+    o->ifc.have_else = 1;
+    o->ifc.else_block = else_block;
+}
+
+NCDBlock * NCDStatement_IfElse (NCDStatement *o)
+{
+    ASSERT(o->type == NCDSTATEMENT_IF)
+    
+    if (!o->ifc.have_else) {
+        return NULL;
+    }
+    
+    return &o->ifc.else_block;
+}
+
+NCDBlock NCDStatement_IfGrabElse (NCDStatement *o)
+{
+    ASSERT(o->type == NCDSTATEMENT_IF)
+    ASSERT(o->ifc.have_else)
+    
+    o->ifc.have_else = 0;
+    
+    return o->ifc.else_block;
+}
+
+void NCDIfBlock_Init (NCDIfBlock *o)
+{
+    LinkedList1_Init(&o->ifs_list);
+}
+
+void NCDIfBlock_Free (NCDIfBlock *o)
+{
+    LinkedList1Node *ln;
+    while (ln = LinkedList1_GetFirst(&o->ifs_list)) {
+        struct IfBlockIf *e = UPPER_OBJECT(ln, struct IfBlockIf, ifs_list_node);
+        NCDIf_Free(&e->ifc);
+        LinkedList1_Remove(&o->ifs_list, &e->ifs_list_node);
+        free(e);
+    }
+}
+
+int NCDIfBlock_PrependIf (NCDIfBlock *o, NCDIf ifc)
+{
+    struct IfBlockIf *e = malloc(sizeof(*e));
+    if (!e) {
+        return 0;
+    }
+    
+    LinkedList1_Prepend(&o->ifs_list, &e->ifs_list_node);
+    e->ifc = ifc;
+    
+    return 1;
+}
+
+NCDIf * NCDIfBlock_FirstIf (NCDIfBlock *o)
+{
+    LinkedList1Node *ln = LinkedList1_GetFirst(&o->ifs_list);
+    if (!ln) {
+        return NULL;
+    }
+    
+    struct IfBlockIf *e = UPPER_OBJECT(ln, struct IfBlockIf, ifs_list_node);
+    
+    return &e->ifc;
+}
+
+NCDIf * NCDIfBlock_NextIf (NCDIfBlock *o, NCDIf *ei)
+{
+    ASSERT(ei)
+    
+    struct IfBlockIf *cur_e = UPPER_OBJECT(ei, struct IfBlockIf, ifc);
+    
+    LinkedList1Node *ln = LinkedList1Node_Next(&cur_e->ifs_list_node);
+    if (!ln) {
+        return NULL;
+    }
+    
+    struct IfBlockIf *e = UPPER_OBJECT(ln, struct IfBlockIf, ifs_list_node);
+    
+    return &e->ifc;
+}
+
+NCDIf NCDIfBlock_GrabIf (NCDIfBlock *o, NCDIf *ei)
+{
+    ASSERT(ei)
+    
+    struct IfBlockIf *e = UPPER_OBJECT(ei, struct IfBlockIf, ifc);
+    
+    NCDIf old_ifc = e->ifc;
+    
+    LinkedList1_Remove(&o->ifs_list, &e->ifs_list_node);
+    free(e);
+    
+    return old_ifc;
+}
+
+void NCDIf_Init (NCDIf *o, NCDValue cond, NCDBlock block)
+{
+    o->cond = cond;
+    o->block = block;
+}
+
+void NCDIf_Free (NCDIf *o)
+{
+    NCDValue_Free(&o->cond);
+    NCDBlock_Free(&o->block);
+}
+
+void NCDIf_FreeGrab (NCDIf *o, NCDValue *out_cond, NCDBlock *out_block)
+{
+    *out_cond = o->cond;
+    *out_block = o->block;
+}
+
+NCDValue * NCDIf_Cond (NCDIf *o)
+{
+    return &o->cond;
+}
+
+NCDBlock * NCDIf_Block (NCDIf *o)
+{
+    return &o->block;
+}

+ 123 - 0
ncd/NCDAst.h

@@ -0,0 +1,123 @@
+#ifndef BADVPN_NCDAST_H
+#define BADVPN_NCDAST_H
+
+#include <misc/debug.h>
+#include <structure/LinkedList1.h>
+#include <ncd/NCDValue.h>
+
+typedef struct NCDProgram_s NCDProgram;
+typedef struct NCDProcess_s NCDProcess;
+typedef struct NCDBlock_s NCDBlock;
+typedef struct NCDStatement_s NCDStatement;
+typedef struct NCDIfBlock_s NCDIfBlock;
+typedef struct NCDIf_s NCDIf;
+
+struct NCDProgram_s {
+    LinkedList1 processes_list;
+};
+
+struct NCDBlock_s {
+    LinkedList1 statements_list;
+    size_t count;
+};
+
+struct NCDProcess_s {
+    int is_template;
+    char *name;
+    NCDBlock block;
+};
+
+struct NCDIfBlock_s {
+    LinkedList1 ifs_list;
+};
+
+struct NCDStatement_s {
+    int type;
+    char *name;
+    union {
+        struct {
+            char *objname;
+            char *cmdname;
+            NCDValue args;
+        } reg;
+        struct {
+            NCDIfBlock ifblock;
+            int have_else;
+            NCDBlock else_block;
+        } ifc;
+    };
+};
+
+struct NCDIf_s {
+    NCDValue cond;
+    NCDBlock block;
+};
+
+struct ProgramProcess {
+    LinkedList1Node processes_list_node;
+    NCDProcess p;
+};
+
+struct BlockStatement {
+    LinkedList1Node statements_list_node;
+    NCDStatement s;
+};
+
+struct IfBlockIf {
+    LinkedList1Node ifs_list_node;
+    NCDIf ifc;
+};
+
+//
+
+#define NCDSTATEMENT_REG 1
+#define NCDSTATEMENT_IF 2
+
+void NCDProgram_Init (NCDProgram *o);
+void NCDProgram_Free (NCDProgram *o);
+NCDProcess * NCDProgram_PrependProcess (NCDProgram *o, NCDProcess p) WARN_UNUSED;
+NCDProcess * NCDProgram_FirstProcess (NCDProgram *o);
+NCDProcess * NCDProgram_NextProcess (NCDProgram *o, NCDProcess *ep);
+
+int NCDProcess_Init (NCDProcess *o, int is_template, const char *name, NCDBlock block) WARN_UNUSED;
+void NCDProcess_Free (NCDProcess *o);
+int NCDProcess_IsTemplate (NCDProcess *o);
+const char * NCDProcess_Name (NCDProcess *o);
+NCDBlock * NCDProcess_Block (NCDProcess *o);
+
+void NCDBlock_Init (NCDBlock *o);
+void NCDBlock_Free (NCDBlock *o);
+int NCDBlock_PrependStatement (NCDBlock *o, NCDStatement s) WARN_UNUSED;
+int NCDBlock_InsertStatementAfter (NCDBlock *o, NCDStatement *after, NCDStatement s) WARN_UNUSED;
+NCDStatement * NCDBlock_ReplaceStatement (NCDBlock *o, NCDStatement *es, NCDStatement s);
+NCDStatement * NCDBlock_FirstStatement (NCDBlock *o);
+NCDStatement * NCDBlock_NextStatement (NCDBlock *o, NCDStatement *es);
+size_t NCDBlock_NumStatements (NCDBlock *o);
+
+int NCDStatement_InitReg (NCDStatement *o, const char *name, const char *objname, const char *cmdname, NCDValue args) WARN_UNUSED;
+int NCDStatement_InitIf (NCDStatement *o, const char *name, NCDIfBlock ifblock) WARN_UNUSED;
+void NCDStatement_Free (NCDStatement *o);
+int NCDStatement_Type (NCDStatement *o);
+const char * NCDStatement_Name (NCDStatement *o);
+const char * NCDStatement_RegObjName (NCDStatement *o);
+const char * NCDStatement_RegCmdName (NCDStatement *o);
+NCDValue * NCDStatement_RegArgs (NCDStatement *o);
+NCDIfBlock * NCDStatement_IfBlock (NCDStatement *o);
+void NCDStatement_IfAddElse (NCDStatement *o, NCDBlock else_block);
+NCDBlock * NCDStatement_IfElse (NCDStatement *o);
+NCDBlock NCDStatement_IfGrabElse (NCDStatement *o);
+
+void NCDIfBlock_Init (NCDIfBlock *o);
+void NCDIfBlock_Free (NCDIfBlock *o);
+int NCDIfBlock_PrependIf (NCDIfBlock *o, NCDIf ifc) WARN_UNUSED;
+NCDIf * NCDIfBlock_FirstIf (NCDIfBlock *o);
+NCDIf * NCDIfBlock_NextIf (NCDIfBlock *o, NCDIf *ei);
+NCDIf NCDIfBlock_GrabIf (NCDIfBlock *o, NCDIf *ei);
+
+void NCDIf_Init (NCDIf *o, NCDValue cond, NCDBlock block);
+void NCDIf_Free (NCDIf *o);
+void NCDIf_FreeGrab (NCDIf *o, NCDValue *out_cond, NCDBlock *out_block);
+NCDValue * NCDIf_Cond (NCDIf *o);
+NCDBlock * NCDIf_Block (NCDIf *o);
+
+#endif

+ 22 - 7
ncd/NCDConfigParser.c

@@ -35,7 +35,7 @@
 #include <base/BLog.h>
 #include <base/BLog.h>
 #include <ncd/NCDConfigTokenizer.h>
 #include <ncd/NCDConfigTokenizer.h>
 
 
-#include <ncd/NCDConfigParser.h>
+#include "ncd/NCDConfigParser.h"
 
 
 #include <generated/blog_channel_NCDConfigParser.h>
 #include <generated/blog_channel_NCDConfigParser.h>
 
 
@@ -61,7 +61,7 @@ static int tokenizer_output (void *user, int token, char *value, size_t value_le
         return 0;
         return 0;
     }
     }
     
     
-    struct parser_minor minor;
+    struct token minor;
     minor.str = value;
     minor.str = value;
     minor.len = value_len;
     minor.len = value_len;
     
     
@@ -130,6 +130,18 @@ static int tokenizer_output (void *user, int token, char *value, size_t value_le
             Parse(state->parser, BRACKET_CLOSE, minor, &state->out);
             Parse(state->parser, BRACKET_CLOSE, minor, &state->out);
         } break;
         } break;
         
         
+        case NCD_TOKEN_IF: {
+            Parse(state->parser, IF, minor, &state->out);
+        } break;
+        
+        case NCD_TOKEN_ELIF: {
+            Parse(state->parser, ELIF, minor, &state->out);
+        } break;
+        
+        case NCD_TOKEN_ELSE: {
+            Parse(state->parser, ELSE, minor, &state->out);
+        } break;
+        
         default:
         default:
             BLog(BLOG_ERROR, "line %zu, character %zu: invalid token", line, line_char);
             BLog(BLOG_ERROR, "line %zu, character %zu: invalid token", line, line_char);
             free(minor.str);
             free(minor.str);
@@ -153,13 +165,13 @@ static int tokenizer_output (void *user, int token, char *value, size_t value_le
     return 1;
     return 1;
 }
 }
 
 
-int NCDConfigParser_Parse (char *config, size_t config_len, struct NCDConfig_processes **out_ast)
+int NCDConfigParser_Parse (char *config, size_t config_len, NCDProgram *out_ast)
 {
 {
     struct parser_state state;
     struct parser_state state;
     
     
     state.out.out_of_memory = 0;
     state.out.out_of_memory = 0;
     state.out.syntax_error = 0;
     state.out.syntax_error = 0;
-    state.out.ast = NULL;
+    state.out.have_ast = 0;
     state.error = 0;
     state.error = 0;
     
     
     if (!(state.parser = ParseAlloc(malloc))) {
     if (!(state.parser = ParseAlloc(malloc))) {
@@ -170,13 +182,16 @@ int NCDConfigParser_Parse (char *config, size_t config_len, struct NCDConfig_pro
     // tokenize and parse
     // tokenize and parse
     NCDConfigTokenizer_Tokenize(config, config_len, tokenizer_output, &state);
     NCDConfigTokenizer_Tokenize(config, config_len, tokenizer_output, &state);
     
     
+    ParseFree(state.parser, free);
+    
     if (state.error) {
     if (state.error) {
-        ParseFree(state.parser, free);
-        NCDConfig_free_processes(state.out.ast);
+        if (state.out.have_ast) {
+            NCDProgram_Free(&state.out.ast);
+        }
         return 0;
         return 0;
     }
     }
     
     
-    ParseFree(state.parser, free);
+    ASSERT(state.out.have_ast)
     
     
     *out_ast = state.out.ast;
     *out_ast = state.out.ast;
     return 1;
     return 1;

+ 3 - 2
ncd/NCDConfigParser.h

@@ -32,8 +32,9 @@
 
 
 #include <stddef.h>
 #include <stddef.h>
 
 
-#include <ncd/NCDConfig.h>
+#include <misc/debug.h>
+#include <ncd/NCDAst.h>
 
 
-int NCDConfigParser_Parse (char *config, size_t config_len, struct NCDConfig_processes **out_ast);
+int NCDConfigParser_Parse (char *config, size_t config_len, NCDProgram *out_ast) WARN_UNUSED;
 
 
 #endif
 #endif

+ 485 - 114
ncd/NCDConfigParser_parse.y

@@ -33,50 +33,90 @@
 #include <stddef.h>
 #include <stddef.h>
 
 
 #include <misc/debug.h>
 #include <misc/debug.h>
-#include <ncd/NCDConfig.h>
+#include <misc/concat_strings.h>
+#include <ncd/NCDAst.h>
 
 
-struct parser_minor {
+struct parser_out {
+    int out_of_memory;
+    int syntax_error;
+    int have_ast;
+    NCDProgram ast;
+};
+
+struct token {
     char *str;
     char *str;
     size_t len;
     size_t len;
 };
 };
 
 
-struct parser_out {
-    int out_of_memory;
-    int syntax_error;
-    struct NCDConfig_processes *ast;
+struct program {
+    int have;
+    NCDProgram v;
+};
+
+struct block {
+    int have;
+    NCDBlock v;
+};
+
+struct statement {
+    int have;
+    NCDStatement v;
+};
+
+struct ifblock {
+    int have;
+    NCDIfBlock v;
+};
+
+struct value {
+    int have;
+    NCDValue v;
 };
 };
 
 
+static void free_token (struct token o) { free(o.str); }
+static void free_program (struct program o) { if (o.have) NCDProgram_Free(&o.v); }
+static void free_block (struct block o) { if (o.have) NCDBlock_Free(&o.v); }
+static void free_statement (struct statement o) { if (o.have) NCDStatement_Free(&o.v); }
+static void free_ifblock (struct ifblock o) { if (o.have) NCDIfBlock_Free(&o.v); }
+static void free_value (struct value o) { if (o.have) NCDValue_Free(&o.v); }
+
 }
 }
 
 
-%extra_argument {struct parser_out *parser_out}
-
-%token_type {struct parser_minor}
-
-%token_destructor { free($$.str); }
-
-%type processes {struct NCDConfig_processes *}
-%type statement {struct NCDConfig_statements *}
-%type statements {struct NCDConfig_statements *}
-%type statement_names {struct NCDConfig_strings *}
-%type statement_args_maybe {struct NCDConfig_list *}
-%type list_contents {struct NCDConfig_list *}
-%type list {struct NCDConfig_list *}
-%type map_contents {struct NCDConfig_list *}
-%type map {struct NCDConfig_list *}
-%type value {struct NCDConfig_list *}
-%type name_maybe {char *}
-%type process_or_template {int}
-
-%destructor processes { NCDConfig_free_processes($$); }
-%destructor statement { NCDConfig_free_statements($$); }
-%destructor statements { NCDConfig_free_statements($$); }
-%destructor statement_names { NCDConfig_free_strings($$); }
-%destructor statement_args_maybe { NCDConfig_free_list($$); }
-%destructor list_contents { NCDConfig_free_list($$); }
-%destructor list { NCDConfig_free_list($$); }
-%destructor map_contents { NCDConfig_free_list($$); }
-%destructor map { NCDConfig_free_list($$); }
-%destructor value { NCDConfig_free_list($$); }
+%extra_argument { struct parser_out *parser_out }
+
+%token_type { struct token }
+
+%token_destructor { free_token($$); }
+
+%type processes { struct program }
+%type statement { struct statement }
+%type elif_maybe { struct ifblock }
+%type elif { struct ifblock }
+%type else_maybe { struct block }
+%type statements { struct block }
+%type dotted_name { char * }
+%type statement_args_maybe { struct value }
+%type list_contents { struct value }
+%type list { struct value }
+%type map_contents { struct value }
+%type map  { struct value }
+%type value  { struct value }
+%type name_maybe { char * }
+%type process_or_template { int }
+
+%destructor processes { free_program($$); }
+%destructor statement { free_statement($$); }
+%destructor elif_maybe { free_ifblock($$); }
+%destructor elif { free_ifblock($$); }
+%destructor else_maybe { free_block($$); }
+%destructor statements { free_block($$); }
+%destructor dotted_name { free($$); }
+%destructor statement_args_maybe { free_value($$); }
+%destructor list_contents { free_value($$); }
+%destructor list { free_value($$); }
+%destructor map_contents { free_value($$); }
+%destructor map { free_value($$); }
+%destructor value { free_value($$); }
 %destructor name_maybe { free($$); }
 %destructor name_maybe { free($$); }
 
 
 %stack_size 0
 %stack_size 0
@@ -88,76 +128,324 @@ struct parser_out {
 // workaroud Lemon bug: if the stack overflows, the token that caused the overflow will be leaked
 // workaroud Lemon bug: if the stack overflows, the token that caused the overflow will be leaked
 %stack_overflow {
 %stack_overflow {
     if (yypMinor) {
     if (yypMinor) {
-        free(yypMinor->yy0.str);
+        free_token(yypMinor->yy0);
     }
     }
 }
 }
 
 
 input ::= processes(A). {
 input ::= processes(A). {
-    parser_out->ast = A;
+    ASSERT(!parser_out->have_ast)
 
 
-    if (!A) {
-        parser_out->out_of_memory = 1;
+    if (A.have) {
+        parser_out->have_ast = 1;
+        parser_out->ast = A.v;
     }
     }
 }
 }
 
 
 processes(R) ::= process_or_template(T) NAME(A) CURLY_OPEN statements(B) CURLY_CLOSE. {
 processes(R) ::= process_or_template(T) NAME(A) CURLY_OPEN statements(B) CURLY_CLOSE. {
-    R = NCDConfig_make_processes(T, A.str, B, NULL);
-    if (!R) {
-        parser_out->out_of_memory = 1;
+    ASSERT(A.str)
+    if (!B.have) {
+        goto failA0;
+    }
+
+    NCDProcess proc;
+    if (!NCDProcess_Init(&proc, T, A.str, B.v)) {
+        goto failA0;
+    }
+    B.have = 0;
+
+    NCDProgram prog;
+    NCDProgram_Init(&prog);
+
+    if (!NCDProgram_PrependProcess(&prog, proc)) {
+        goto failA1;
     }
     }
+
+    R.have = 1;
+    R.v = prog;
+    goto doneA;
+
+failA1:
+    NCDProgram_Free(&prog);
+    NCDProcess_Free(&proc);
+failA0:
+    R.have = 0;
+    parser_out->out_of_memory = 1;
+doneA:
+    free_token(A);
+    free_block(B);
 }
 }
 
 
 processes(R) ::= process_or_template(T) NAME(A) CURLY_OPEN statements(B) CURLY_CLOSE processes(N). {
 processes(R) ::= process_or_template(T) NAME(A) CURLY_OPEN statements(B) CURLY_CLOSE processes(N). {
-    R = NCDConfig_make_processes(T, A.str, B, N);
-    if (!R) {
-        parser_out->out_of_memory = 1;
+    ASSERT(A.str)
+    if (!B.have || !N.have) {
+        goto failB0;
+    }
+
+    NCDProcess proc;
+    if (!NCDProcess_Init(&proc, T, A.str, B.v)) {
+        goto failB0;
     }
     }
+    B.have = 0;
+
+    if (!NCDProgram_PrependProcess(&N.v, proc)) {
+        goto failB1;
+    }
+
+    R.have = 1;
+    R.v = N.v;
+    N.have = 0;
+    goto doneB;
+
+failB1:
+    NCDProcess_Free(&proc);
+failB0:
+    R.have = 0;
+    parser_out->out_of_memory = 1;
+doneB:
+    free_token(A);
+    free_block(B);
+    free_program(N);
 }
 }
 
 
-statement(R) ::= statement_names(A) ROUND_OPEN statement_args_maybe(B) ROUND_CLOSE name_maybe(C) SEMICOLON. {
-    R = NCDConfig_make_statements(NULL, A, B, C, NULL);
-    if (!R) {
-        parser_out->out_of_memory = 1;
+statement(R) ::= dotted_name(A) ROUND_OPEN statement_args_maybe(B) ROUND_CLOSE name_maybe(C) SEMICOLON. {
+    if (!A || !B.have) {
+        goto failC0;
     }
     }
+
+    if (!NCDStatement_InitReg(&R.v, C, NULL, A, B.v)) {
+        goto failC0;
+    }
+    B.have = 0;
+
+    R.have = 1;
+    goto doneC;
+
+failC0:
+    R.have = 0;
+    parser_out->out_of_memory = 1;
+doneC:
+    free(A);
+    free_value(B);
+    free(C);
 }
 }
 
 
-statement(R) ::= statement_names(M) ARROW statement_names(A) ROUND_OPEN statement_args_maybe(B) ROUND_CLOSE name_maybe(C) SEMICOLON. {
-    R = NCDConfig_make_statements(M, A, B, C, NULL);
-    if (!R) {
-        parser_out->out_of_memory = 1;
+statement(R) ::= dotted_name(M) ARROW dotted_name(A) ROUND_OPEN statement_args_maybe(B) ROUND_CLOSE name_maybe(C) SEMICOLON. {
+    if (!M || !A || !B.have) {
+        goto failD0;
+    }
+
+    if (!NCDStatement_InitReg(&R.v, C, M, A, B.v)) {
+        goto failD0;
     }
     }
+    B.have = 0;
+
+    R.have = 1;
+    goto doneD;
+
+failD0:
+    R.have = 0;
+    parser_out->out_of_memory = 1;
+doneD:
+    free(M);
+    free(A);
+    free_value(B);
+    free(C);
 }
 }
 
 
-statements(R) ::= statement(A). {
+statement(R) ::= IF ROUND_OPEN value(A) ROUND_CLOSE CURLY_OPEN statements(B) CURLY_CLOSE elif_maybe(I) else_maybe(E) name_maybe(C) SEMICOLON. {
+    if (!A.have || !B.have || !I.have) {
+        goto failE0;
+    }
+
+    NCDIf ifc;
+    NCDIf_Init(&ifc, A.v, B.v);
+    A.have = 0;
+    B.have = 0;
+
+    if (!NCDIfBlock_PrependIf(&I.v, ifc)) {
+        NCDIf_Free(&ifc);
+        goto failE0;
+    }
+
+    if (!NCDStatement_InitIf(&R.v, C, I.v)) {
+        goto failE0;
+    }
+    I.have = 0;
+
+    if (E.have) {
+        NCDStatement_IfAddElse(&R.v, E.v);
+        E.have = 0;
+    }
+
+    R.have = 1;
+    goto doneE;
+
+failE0:
+    R.have = 0;
+    parser_out->out_of_memory = 1;
+doneE:
+    free_value(A);
+    free_block(B);
+    free_ifblock(I);
+    free_block(E);
+    free(C);
+}
+
+elif_maybe(R) ::= . {
+    NCDIfBlock_Init(&R.v);
+    R.have = 1;
+}
+
+elif_maybe(R) ::= elif(A). {
     R = A;
     R = A;
 }
 }
 
 
-statements(R) ::= statement(A) statements(N). {
-    if (!A) {
-        NCDConfig_free_statements(N);
-    } else {
-        ASSERT(!A->next)
-        A->next = N;
+elif(R) ::= ELIF ROUND_OPEN value(A) ROUND_CLOSE CURLY_OPEN statements(B) CURLY_CLOSE. {
+    if (!A.have || !B.have) {
+        goto failF0;
     }
     }
-    R = A;
+
+    NCDIfBlock_Init(&R.v);
+
+    NCDIf ifc;
+    NCDIf_Init(&ifc, A.v, B.v);
+    A.have = 0;
+    B.have = 0;
+
+    if (!NCDIfBlock_PrependIf(&R.v, ifc)) {
+        goto failF1;
+    }
+
+    R.have = 1;
+    goto doneF0;
+
+failF1:
+    NCDIf_Free(&ifc);
+    NCDIfBlock_Free(&R.v);
+failF0:
+    R.have = 0;
+    parser_out->out_of_memory = 1;
+doneF0:
+    free_value(A);
+    free_block(B);
 }
 }
 
 
-statement_names(R) ::= NAME(A). {
-    R = NCDConfig_make_strings(A.str, 0, NULL);
-    if (!R) {
-        parser_out->out_of_memory = 1;
+elif(R) ::= ELIF ROUND_OPEN value(A) ROUND_CLOSE CURLY_OPEN statements(B) CURLY_CLOSE elif(N). {
+    if (!A.have || !B.have || !N.have) {
+        goto failG0;
     }
     }
+
+    NCDIf ifc;
+    NCDIf_Init(&ifc, A.v, B.v);
+    A.have = 0;
+    B.have = 0;
+
+    if (!NCDIfBlock_PrependIf(&N.v, ifc)) {
+        goto failG1;
+    }
+
+    R.have = 1;
+    R.v = N.v;
+    N.have = 0;
+    goto doneG0;
+
+failG1:
+    NCDIf_Free(&ifc);
+failG0:
+    R.have = 0;
+    parser_out->out_of_memory = 1;
+doneG0:
+    free_value(A);
+    free_block(B);
+    free_ifblock(N);
 }
 }
 
 
-statement_names(R) ::= NAME(A) DOT statement_names(N). {
-    R = NCDConfig_make_strings(A.str, 1, N);
-    if (!R) {
-        parser_out->out_of_memory = 1;
+else_maybe(R) ::= . {
+    R.have = 0;
+}
+
+else_maybe(R) ::= ELSE CURLY_OPEN statements(B) CURLY_CLOSE. {
+    R = B;
+}
+
+statements(R) ::= statement(A). {
+    if (!A.have) {
+        goto failH0;
+    }
+
+    NCDBlock_Init(&R.v);
+
+    if (!NCDBlock_PrependStatement(&R.v, A.v)) {
+        goto failH1;
     }
     }
+    A.have = 0;
+
+    R.have = 1;
+    goto doneH;
+
+failH1:
+    NCDBlock_Free(&R.v);
+failH0:
+    R.have = 0;
+    parser_out->out_of_memory = 1;
+doneH:
+    free_statement(A);
 }
 }
 
 
-statement_args_maybe(R) ::= . {
+statements(R) ::= statement(A) statements(N). {
+    if (!A.have || !N.have) {
+        goto failI0;
+    }
+
+    if (!NCDBlock_PrependStatement(&N.v, A.v)) {
+        goto failI1;
+    }
+    A.have = 0;
+
+    R.have = 1;
+    R.v = N.v;
+    N.have = 0;
+    goto doneI;
+
+failI1:
+    NCDBlock_Free(&R.v);
+failI0:
+    R.have = 0;
+    parser_out->out_of_memory = 1;
+doneI:
+    free_statement(A);
+    free_block(N);
+}
+
+dotted_name(R) ::= NAME(A). {
+    ASSERT(A.str)
+
+    R = A.str;
+}
+
+dotted_name(R) ::= NAME(A) DOT dotted_name(N). {
+    ASSERT(A.str)
+    if (!N) {
+        goto failJ0;
+    }
+
+    if (!(R = concat_strings(3, A.str, ".", N))) {
+        goto failJ0;
+    }
+
+    goto doneJ;
+
+failJ0:
     R = NULL;
     R = NULL;
+    parser_out->out_of_memory = 1;
+doneJ:
+    free_token(A);
+    free(N);
+}
+
+statement_args_maybe(R) ::= . {
+    R.have = 1;
+    NCDValue_InitList(&R.v);
 }
 }
 
 
 statement_args_maybe(R) ::= list_contents(A). {
 statement_args_maybe(R) ::= list_contents(A). {
@@ -165,21 +453,55 @@ statement_args_maybe(R) ::= list_contents(A). {
 }
 }
 
 
 list_contents(R) ::= value(A). {
 list_contents(R) ::= value(A). {
-    R = A;
+    if (!A.have) {
+        goto failL0;
+    }
+
+    NCDValue_InitList(&R.v);
+
+    if (!NCDValue_ListPrepend(&R.v, A.v)) {
+        goto failL1;
+    }
+    A.have = 0;
+
+    R.have = 1;
+    goto doneL;
+
+failL1:
+    NCDValue_Free(&R.v);
+failL0:
+    R.have = 0;
+    parser_out->out_of_memory = 1;
+doneL:
+    free_value(A);
 }
 }
 
 
 list_contents(R) ::= value(A) COMMA list_contents(N). {
 list_contents(R) ::= value(A) COMMA list_contents(N). {
-    if (!A) {
-        NCDConfig_free_list(N);
-    } else {
-        ASSERT(!A->next)
-        A->next = N;
+    if (!A.have || !N.have) {
+        goto failM0;
     }
     }
-    R = A;
+
+    if (!NCDValue_ListPrepend(&N.v, A.v)) {
+        goto failM0;
+    }
+    A.have = 0;
+
+    R.have = 1;
+    R.v = N.v;
+    N.have = 0;
+    goto doneM;
+
+failM0:
+    R.have = 0;
+    parser_out->out_of_memory = 1;
+doneM:
+    free_value(A);
+    free_value(N);
 }
 }
 
 
 list(R) ::= CURLY_OPEN CURLY_CLOSE. {
 list(R) ::= CURLY_OPEN CURLY_CLOSE. {
-    R = NULL;
+    R.have = 1;
+    NCDValue_InitList(&R.v);
 }
 }
 
 
 list(R) ::= CURLY_OPEN list_contents(A) CURLY_CLOSE. {
 list(R) ::= CURLY_OPEN list_contents(A) CURLY_CLOSE. {
@@ -187,35 +509,66 @@ list(R) ::= CURLY_OPEN list_contents(A) CURLY_CLOSE. {
 }
 }
 
 
 map_contents(R) ::= value(A) COLON value(B). {
 map_contents(R) ::= value(A) COLON value(B). {
-    if (!A || !B) {
-        NCDConfig_free_list(A);
-        NCDConfig_free_list(B);
-        R = NULL;
-    } else {
-        ASSERT(!A->next)
-        ASSERT(!B->next)
-        A->next = B;
-        R = A;
+    if (!A.have || !B.have) {
+        goto failS0;
     }
     }
+
+    NCDValue_InitMap(&R.v);
+
+    if (!NCDValue_MapInsert(&R.v, A.v, B.v)) {
+        goto failS1;
+    }
+    A.have = 0;
+    B.have = 0;
+
+    R.have = 1;
+    goto doneS;
+
+failS1:
+    NCDValue_Free(&R.v);
+failS0:
+    R.have = 0;
+    parser_out->out_of_memory = 1;
+doneS:
+    free_value(A);
+    free_value(B);
 }
 }
 
 
 map_contents(R) ::= value(A) COLON value(B) COMMA map_contents(N). {
 map_contents(R) ::= value(A) COLON value(B) COMMA map_contents(N). {
-    if (!A || !B) {
-        NCDConfig_free_list(A);
-        NCDConfig_free_list(B);
-        NCDConfig_free_list(N);
-        R = NULL;
-    } else {
-        ASSERT(!A->next)
-        ASSERT(!B->next)
-        A->next = B;
-        B->next = N;
-        R = A;
+    if (!A.have || !B.have || !N.have) {
+        goto failT0;
+    }
+
+    if (NCDValue_MapFindKey(&N.v, &A.v)) {
+        BLog(BLOG_ERROR, "duplicate key in map");
+        R.have = 0;
+        parser_out->syntax_error = 1;
+        goto doneT;
+    }
+
+    if (!NCDValue_MapInsert(&N.v, A.v, B.v)) {
+        goto failT0;
     }
     }
+    A.have = 0;
+    B.have = 0;
+
+    R.have = 1;
+    R.v = N.v;
+    N.have = 0;
+    goto doneT;
+
+failT0:
+    R.have = 0;
+    parser_out->out_of_memory = 1;
+doneT:
+    free_value(A);
+    free_value(B);
+    free_value(N);
 }
 }
 
 
 map(R) ::= BRACKET_OPEN BRACKET_CLOSE. {
 map(R) ::= BRACKET_OPEN BRACKET_CLOSE. {
-    R = NULL;
+    R.have = 1;
+    NCDValue_InitMap(&R.v);
 }
 }
 
 
 map(R) ::= BRACKET_OPEN map_contents(A) BRACKET_CLOSE. {
 map(R) ::= BRACKET_OPEN map_contents(A) BRACKET_CLOSE. {
@@ -223,31 +576,47 @@ map(R) ::= BRACKET_OPEN map_contents(A) BRACKET_CLOSE. {
 }
 }
 
 
 value(R) ::= STRING(A). {
 value(R) ::= STRING(A). {
-    R = NCDConfig_make_list_string(A.str, A.len, NULL);
-    if (!R) {
-        parser_out->out_of_memory = 1;
+    ASSERT(A.str)
+
+    if (!NCDValue_InitStringBin(&R.v, A.str, A.len)) {
+        goto failU0;
     }
     }
+
+    R.have = 1;
+    goto doneU;
+
+failU0:
+    R.have = 0;
+    parser_out->out_of_memory = 1;
+doneU:
+    free_token(A);
 }
 }
 
 
-value(R) ::= statement_names(A). {
-    R = NCDConfig_make_list_var(A, NULL);
-    if (!R) {
-        parser_out->out_of_memory = 1;
+value(R) ::= dotted_name(A). {
+    if (!A) {
+        goto failV0;
+    }
+
+    if (!NCDValue_InitVar(&R.v, A)) {
+        goto failV0;
     }
     }
+
+    R.have = 1;
+    goto doneV;
+
+failV0:
+    R.have = 0;
+    parser_out->out_of_memory = 1;
+doneV:
+    free(A);
 }
 }
 
 
 value(R) ::= list(A). {
 value(R) ::= list(A). {
-    R = NCDConfig_make_list_list(A, NULL);
-    if (!R) {
-        parser_out->out_of_memory = 1;
-    }
+    R = A;
 }
 }
 
 
 value(R) ::= map(A). {
 value(R) ::= map(A). {
-    R = NCDConfig_make_list_maplist(A, NULL);
-    if (!R) {
-        parser_out->out_of_memory = 1;
-    }
+    R = A;
 }
 }
 
 
 name_maybe(R) ::= . {
 name_maybe(R) ::= . {
@@ -255,6 +624,8 @@ name_maybe(R) ::= . {
 }
 }
 
 
 name_maybe(R) ::= NAME(A). {
 name_maybe(R) ::= NAME(A). {
+    ASSERT(A.str)
+
     R = A.str;
     R = A.str;
 }
 }
 
 

+ 143 - 0
ncd/NCDSugar.c

@@ -0,0 +1,143 @@
+#include <stdlib.h>
+
+#include <misc/debug.h>
+
+#include "NCDSugar.h"
+
+struct desugar_state {
+    NCDProgram *prog;
+    size_t template_name_ctr;
+};
+
+static int add_template (struct desugar_state *state, NCDBlock block, NCDValue *out_name_val);
+static int desugar_block (struct desugar_state *state, NCDBlock *block);
+static int desugar_if (struct desugar_state *state, NCDBlock *block, NCDStatement *stmt, NCDStatement **out_next);
+
+static int add_template (struct desugar_state *state, NCDBlock block, NCDValue *out_name_val)
+{
+    char name[40];
+    snprintf(name, sizeof(name), "__tmpl%zu", state->template_name_ctr);
+    state->template_name_ctr++;
+    
+    if (!desugar_block(state, &block)) {
+        NCDBlock_Free(&block);
+        return 0;
+    }
+    
+    NCDProcess proc_tmp;
+    if (!NCDProcess_Init(&proc_tmp, 1, name, block)) {
+        NCDBlock_Free(&block);
+        return 0;
+    }
+    
+    if (!NCDProgram_PrependProcess(state->prog, proc_tmp)) {
+        NCDProcess_Free(&proc_tmp);
+        return 0;
+    }
+    
+    if (!NCDValue_InitString(out_name_val, name)) {
+        return 0;
+    }
+    
+    return 1;
+}
+
+static int desugar_block (struct desugar_state *state, NCDBlock *block)
+{
+    NCDStatement *stmt = NCDBlock_FirstStatement(block);
+    
+    while (stmt) {
+        switch (NCDStatement_Type(stmt)) {
+            case NCDSTATEMENT_REG: {
+                stmt = NCDBlock_NextStatement(block, stmt);
+            } break;
+            
+            case NCDSTATEMENT_IF: {
+                if (!desugar_if(state, block, stmt, &stmt)) {
+                    return 0;
+                }
+            } break;
+            
+            default: ASSERT(0);
+        }
+    }
+    
+    return 1;
+}
+
+static int desugar_if (struct desugar_state *state, NCDBlock *block, NCDStatement *stmt, NCDStatement **out_next)
+{
+    ASSERT(NCDStatement_Type(stmt) == NCDSTATEMENT_IF)
+    
+    NCDValue args;
+    NCDValue_InitList(&args);
+    
+    NCDIfBlock *ifblock = NCDStatement_IfBlock(stmt);
+    
+    while (NCDIfBlock_FirstIf(ifblock)) {
+        NCDIf ifc = NCDIfBlock_GrabIf(ifblock, NCDIfBlock_FirstIf(ifblock));
+        
+        NCDValue if_cond;
+        NCDBlock if_block;
+        NCDIf_FreeGrab(&ifc, &if_cond, &if_block);
+        
+        if (!NCDValue_ListAppend(&args, if_cond)) {
+            NCDValue_Free(&if_cond);
+            NCDBlock_Free(&if_block);
+            goto fail;
+        }
+        
+        NCDValue action_arg;
+        if (!add_template(state, if_block, &action_arg)) {
+            goto fail;
+        }
+        
+        if (!NCDValue_ListAppend(&args, action_arg)) {
+            NCDValue_Free(&action_arg);
+            goto fail;
+        }
+    }
+    
+    if (NCDStatement_IfElse(stmt)) {
+        NCDBlock else_block = NCDStatement_IfGrabElse(stmt);
+        
+        NCDValue action_arg;
+        if (!add_template(state, else_block, &action_arg)) {
+            goto fail;
+        }
+        
+        if (!NCDValue_ListAppend(&args, action_arg)) {
+            NCDValue_Free(&action_arg);
+            goto fail;
+        }
+    }
+    
+    NCDStatement new_stmt;
+    if (!NCDStatement_InitReg(&new_stmt, NCDStatement_Name(stmt), NULL, "embcall2_multif", args)) {
+        goto fail;
+    }
+    
+    stmt = NCDBlock_ReplaceStatement(block, stmt, new_stmt);
+    
+    *out_next = NCDBlock_NextStatement(block, stmt);
+    return 1;
+    
+fail:
+    NCDValue_Free(&args);
+    return 0;
+}
+
+int NCDSugar_Desugar (NCDProgram *prog)
+{
+    struct desugar_state state;
+    state.prog = prog;
+    state.template_name_ctr = 0;
+    
+    for (NCDProcess *proc = NCDProgram_FirstProcess(prog); proc; proc = NCDProgram_NextProcess(prog, proc)) {
+        if (!desugar_block(&state, NCDProcess_Block(proc))) {
+            return 0;
+        }
+    }
+    
+    return 1;
+}

+ 9 - 0
ncd/NCDSugar.h

@@ -0,0 +1,9 @@
+#ifndef BADVPN_NCDSUGAR_H
+#define BADVPN_NCDSUGAR_H
+
+#include <misc/debug.h>
+#include <ncd/NCDAst.h>
+
+int NCDSugar_Desugar (NCDProgram *prog) WARN_UNUSED;
+
+#endif

+ 106 - 154
ncd/ncd.c

@@ -42,6 +42,7 @@
 #include <misc/parse_number.h>
 #include <misc/parse_number.h>
 #include <misc/open_standard_streams.h>
 #include <misc/open_standard_streams.h>
 #include <misc/expstring.h>
 #include <misc/expstring.h>
+#include <misc/split_string.h>
 #include <structure/LinkedList1.h>
 #include <structure/LinkedList1.h>
 #include <base/BLog.h>
 #include <base/BLog.h>
 #include <base/BLog_syslog.h>
 #include <base/BLog_syslog.h>
@@ -53,6 +54,7 @@
 #include <ncd/NCDConfigParser.h>
 #include <ncd/NCDConfigParser.h>
 #include <ncd/NCDModule.h>
 #include <ncd/NCDModule.h>
 #include <ncd/NCDModuleIndex.h>
 #include <ncd/NCDModuleIndex.h>
+#include <ncd/NCDSugar.h>
 #include <ncd/modules/modules.h>
 #include <ncd/modules/modules.h>
 
 
 #include <ncd/ncd.h>
 #include <ncd/ncd.h>
@@ -166,8 +168,8 @@ NCDUdevManager umanager;
 // module index
 // module index
 NCDModuleIndex mindex;
 NCDModuleIndex mindex;
 
 
-// config AST
-struct NCDConfig_processes *config_ast;
+// program AST
+NCDProgram program;
 
 
 // common module parameters
 // common module parameters
 struct NCDModuleInst_params module_params;
 struct NCDModuleInst_params module_params;
@@ -182,22 +184,20 @@ static int parse_arguments (int argc, char *argv[]);
 static void signal_handler (void *unused);
 static void signal_handler (void *unused);
 static void start_terminate (int exit_code);
 static void start_terminate (int exit_code);
 static int arg_value_init_string (struct arg_value *o, const char *string, size_t len);
 static int arg_value_init_string (struct arg_value *o, const char *string, size_t len);
-static int arg_value_init_variable (struct arg_value *o, struct NCDConfig_strings *ast_names);
+static int arg_value_init_variable (struct arg_value *o, const char *name);
 static void arg_value_init_list (struct arg_value *o);
 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 int arg_value_list_append (struct arg_value *o, struct arg_value v);
 static void arg_value_init_map (struct arg_value *o);
 static void arg_value_init_map (struct arg_value *o);
 static int arg_value_map_append (struct arg_value *o, struct arg_value key, struct arg_value val);
 static int arg_value_map_append (struct arg_value *o, struct arg_value key, struct arg_value val);
 static void arg_value_free (struct arg_value *o);
 static void arg_value_free (struct arg_value *o);
-static int build_arg_from_ast (struct arg_value *o, struct NCDConfig_list *ast);
-static int build_arg_list_from_ast_list (struct arg_value *o, struct NCDConfig_list *list);
-static int build_arg_map_from_ast_list (struct arg_value *o, struct NCDConfig_list *list);
-static char ** names_new (struct NCDConfig_strings *ast_names);
+static int build_arg_from_ast (struct arg_value *o, NCDValue *val_ast);
+static char ** names_new (const char *name);
 static size_t names_count (char **names);
 static size_t names_count (char **names);
 static char * names_tostring (char **names);
 static char * names_tostring (char **names);
 static void names_free (char **names);
 static void names_free (char **names);
-static int statement_init (struct statement *s, struct NCDConfig_statements *conf);
+static int statement_init (struct statement *s, NCDStatement *stmt_ast);
 static void statement_free (struct statement *s);
 static void statement_free (struct statement *s);
-static int process_new (struct NCDConfig_processes *conf, NCDModuleProcess *module_process);
+static int process_new (NCDProcess *proc_ast, NCDModuleProcess *module_process);
 static void process_free (struct process *p);
 static void process_free (struct process *p);
 static void process_start_terminating (struct process *p);
 static void process_start_terminating (struct process *p);
 static void process_free_statements (struct process *p);
 static void process_free_statements (struct process *p);
@@ -339,7 +339,7 @@ int main (int argc, char **argv)
     }
     }
     
     
     // parse config file
     // parse config file
-    if (!NCDConfigParser_Parse((char *)file, file_len, &config_ast)) {
+    if (!NCDConfigParser_Parse((char *)file, file_len, &program)) {
         BLog(BLOG_ERROR, "NCDConfigParser_Parse failed");
         BLog(BLOG_ERROR, "NCDConfigParser_Parse failed");
         free(file);
         free(file);
         goto fail3;
         goto fail3;
@@ -348,6 +348,12 @@ int main (int argc, char **argv)
     // fee config file memory
     // fee config file memory
     free(file);
     free(file);
     
     
+    // desugar
+    if (!NCDSugar_Desugar(&program)) {
+        BLog(BLOG_ERROR, "NCDSugar_Desugar failed");
+        goto fail4;
+    }
+    
     // init module params
     // init module params
     struct NCDModuleInitParams params;
     struct NCDModuleInitParams params;
     params.reactor = &ss;
     params.reactor = &ss;
@@ -380,11 +386,11 @@ int main (int argc, char **argv)
     LinkedList1_Init(&processes);
     LinkedList1_Init(&processes);
     
     
     // init processes
     // init processes
-    for (struct NCDConfig_processes *conf = config_ast; conf; conf = conf->next) {
-        if (conf->is_template) {
+    for (NCDProcess *p = NCDProgram_FirstProcess(&program); p; p = NCDProgram_NextProcess(&program, p)) {
+        if (NCDProcess_IsTemplate(p)) {
             continue;
             continue;
         }
         }
-        if (!process_new(conf, NULL)) {
+        if (!process_new(p, NULL)) {
             BLog(BLOG_ERROR, "failed to initialize process, exiting");
             BLog(BLOG_ERROR, "failed to initialize process, exiting");
             goto fail6;
             goto fail6;
         }
         }
@@ -411,8 +417,9 @@ fail5:
         }
         }
         num_inited_modules--;
         num_inited_modules--;
     }
     }
-    // free configuration
-    NCDConfig_free_processes(config_ast);
+fail4:
+    // free program AST
+    NCDProgram_Free(&program);
 fail3:
 fail3:
     // remove signal handler
     // remove signal handler
     BSignal_Finish();
     BSignal_Finish();
@@ -655,12 +662,12 @@ int arg_value_init_string (struct arg_value *o, const char *string, size_t len)
     return 1;
     return 1;
 }
 }
 
 
-int arg_value_init_variable (struct arg_value *o, struct NCDConfig_strings *ast_names)
+int arg_value_init_variable (struct arg_value *o, const char *name)
 {
 {
-    ASSERT(ast_names)
+    ASSERT(name)
     
     
     o->type = ARG_VALUE_TYPE_VARIABLE;
     o->type = ARG_VALUE_TYPE_VARIABLE;
-    if (!(o->variable_names = names_new(ast_names))) {
+    if (!(o->variable_names = names_new(name))) {
         return 0;
         return 0;
     }
     }
     
     
@@ -744,31 +751,74 @@ void arg_value_free (struct arg_value *o)
     }
     }
 }
 }
 
 
-int build_arg_from_ast (struct arg_value *o, struct NCDConfig_list *ast)
+int build_arg_from_ast (struct arg_value *o, NCDValue *val_ast)
 {
 {
-    switch (ast->type) {
-        case NCDCONFIG_ARG_STRING: {
-            if (!arg_value_init_string(o, ast->string, ast->string_len)) {
+    switch (NCDValue_Type(val_ast)) {
+        case NCDVALUE_STRING: {
+            if (!arg_value_init_string(o, NCDValue_StringValue(val_ast), NCDValue_StringLength(val_ast))) {
                 return 0;
                 return 0;
             }
             }
         } break;
         } break;
         
         
-        case NCDCONFIG_ARG_VAR: {
-            if (!arg_value_init_variable(o, ast->var)) {
+        case NCDVALUE_VAR: {
+            if (!arg_value_init_variable(o, NCDValue_VarName(val_ast))) {
                 return 0;
                 return 0;
             }
             }
         } break;
         } break;
         
         
-        case NCDCONFIG_ARG_LIST: {
-            if (!build_arg_list_from_ast_list(o, ast->list)) {
-                return 0;
+        case NCDVALUE_LIST: {
+            arg_value_init_list(o);
+            
+            for (NCDValue *ve = NCDValue_ListFirst(val_ast); ve; ve = NCDValue_ListNext(val_ast, ve)) {
+                struct arg_value e;
+                
+                if (!build_arg_from_ast(&e, ve)) {
+                    goto fail_list;
+                }
+                
+                if (!arg_value_list_append(o, e)) {
+                    arg_value_free(&e);
+                    goto fail_list;
+                }
             }
             }
+            
+            break;
+            
+        fail_list:
+            arg_value_free(o);
+            return 0;
         } break;
         } break;
         
         
-        case NCDCONFIG_ARG_MAPLIST: {
-            if (!build_arg_map_from_ast_list(o, ast->list)) {
-                return 0;
+        case NCDVALUE_MAP: {
+            arg_value_init_map(o);
+            
+            for (NCDValue *ekey = NCDValue_MapFirstKey(val_ast); ekey; ekey = NCDValue_MapNextKey(val_ast, ekey)) {
+                NCDValue *eval = NCDValue_MapKeyValue(val_ast, ekey);
+                
+                struct arg_value key;
+                struct arg_value val;
+                
+                if (!build_arg_from_ast(&key, ekey)) {
+                    goto fail_map;
+                }
+                
+                if (!build_arg_from_ast(&val, eval)) {
+                    arg_value_free(&key);
+                    goto fail_map;
+                }
+                
+                if (!arg_value_map_append(o, key, val)) {
+                    arg_value_free(&key);
+                    arg_value_free(&val);
+                    goto fail_map;
+                }
             }
             }
+            
+            break;
+            
+        fail_map:
+            arg_value_free(o);
+            return 0;
         } break;
         } break;
         
         
         default: ASSERT(0);
         default: ASSERT(0);
@@ -777,98 +827,11 @@ int build_arg_from_ast (struct arg_value *o, struct NCDConfig_list *ast)
     return 1;
     return 1;
 }
 }
 
 
-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;
-        
-        if (!build_arg_from_ast(&e, c)) {
-            goto fail;
-        }
-        
-        if (!arg_value_list_append(o, e)) {
-            arg_value_free(&e);
-            goto fail;
-        }
-    }
-    
-    return 1;
-    
-fail:
-    arg_value_free(o);
-    return 0;
-}
-
-int build_arg_map_from_ast_list (struct arg_value *o, struct NCDConfig_list *list)
-{
-    arg_value_init_map(o);
-    
-    for (struct NCDConfig_list *c = list; c; c = c->next->next) {
-        ASSERT(c->next)
-        
-        struct arg_value key;
-        struct arg_value val;
-        
-        if (!build_arg_from_ast(&key, c)) {
-            goto fail;
-        }
-        
-        if (!build_arg_from_ast(&val, c->next)) {
-            arg_value_free(&key);
-            goto fail;
-        }
-        
-        if (!arg_value_map_append(o, key, val)) {
-            arg_value_free(&key);
-            arg_value_free(&val);
-            goto fail;
-        }
-    }
-    
-    return 1;
-    
-fail:
-    arg_value_free(o);
-    return 0;
-}
-
-static char ** names_new (struct NCDConfig_strings *ast_names)
+static char ** names_new (const char *name)
 {
 {
-    ASSERT(ast_names)
-    
-    bsize_t size = bsize_fromsize(1);
-    for (struct NCDConfig_strings *n = ast_names; n; n = n->next) {
-        size = bsize_add(size, bsize_fromsize(1));
-    }
-    
-    char **names;
-    if (size.is_overflow || !(names = BAllocArray(size.value, sizeof(names[0])))) {
-        BLog(BLOG_ERROR, "BAllocArray failed");
-        goto fail0;
-    }
-    
-    size_t i = 0;
-    for (struct NCDConfig_strings *n = ast_names; n; n = n->next) {
-        if (!(names[i] = strdup(n->value))) {
-            BLog(BLOG_ERROR, "strdup failed");
-            goto fail1;
-        }
-        i++;
-    }
-    
-    names[i] = NULL;
-    
-    return names;
+    ASSERT(name)
     
     
-fail1:
-    while (i-- > 0) {
-        free(names[i]);
-    }
-    BFree(names);
-fail0:
-    return NULL;
+    return split_string(name, '.');
 }
 }
 
 
 static size_t names_count (char **names)
 static size_t names_count (char **names)
@@ -920,11 +883,13 @@ static void names_free (char **names)
     BFree(names);
     BFree(names);
 }
 }
 
 
-int statement_init (struct statement *s, struct NCDConfig_statements *conf)
+int statement_init (struct statement *s, NCDStatement *stmt_ast)
 {
 {
+    ASSERT(NCDStatement_Type(stmt_ast) == NCDSTATEMENT_REG)
+    
     // set object names
     // set object names
-    if (conf->objname) {
-        if (!(s->object_names = names_new(conf->objname))) {
+    if (NCDStatement_RegObjName(stmt_ast)) {
+        if (!(s->object_names = names_new(NCDStatement_RegObjName(stmt_ast)))) {
             goto fail0;
             goto fail0;
         }
         }
     } else {
     } else {
@@ -932,14 +897,14 @@ int statement_init (struct statement *s, struct NCDConfig_statements *conf)
     }
     }
     
     
     // set method name
     // set method name
-    if (!(s->method_name = NCDConfig_concat_strings(conf->names))) {
-        BLog(BLOG_ERROR, "NCDConfig_concat_strings failed");
+    if (!(s->method_name = strdup(NCDStatement_RegCmdName(stmt_ast)))) {
+        BLog(BLOG_ERROR, "strdup failed");
         goto fail1;
         goto fail1;
     }
     }
     
     
     // init name
     // init name
-    if (conf->name) {
-        if (!(s->name = strdup(conf->name))) {
+    if (NCDStatement_Name(stmt_ast)) {
+        if (!(s->name = strdup(NCDStatement_Name(stmt_ast)))) {
             BLog(BLOG_ERROR, "strdup failed");
             BLog(BLOG_ERROR, "strdup failed");
             goto fail2;
             goto fail2;
         }
         }
@@ -948,8 +913,8 @@ int statement_init (struct statement *s, struct NCDConfig_statements *conf)
     }
     }
     
     
     // init arguments
     // init arguments
-    if (!build_arg_list_from_ast_list(&s->args, conf->args)) {
-        BLog(BLOG_ERROR, "build_arg_list_from_ast_list failed");
+    if (!build_arg_from_ast(&s->args, NCDStatement_RegArgs(stmt_ast))) {
+        BLog(BLOG_ERROR, "build_arg_from_ast failed");
         goto fail3;
         goto fail3;
     }
     }
     
     
@@ -984,7 +949,7 @@ void statement_free (struct statement *s)
     }
     }
 }
 }
 
 
-int process_new (struct NCDConfig_processes *conf, NCDModuleProcess *module_process)
+int process_new (NCDProcess *proc_ast, NCDModuleProcess *module_process)
 {
 {
     // allocate strucure
     // allocate strucure
     struct process *p = malloc(sizeof(*p));
     struct process *p = malloc(sizeof(*p));
@@ -1004,32 +969,22 @@ int process_new (struct NCDConfig_processes *conf, NCDModuleProcess *module_proc
     }
     }
     
     
     // init name
     // init name
-    if (!(p->name = strdup(conf->name))) {
+    if (!(p->name = strdup(NCDProcess_Name(proc_ast)))) {
         BLog(BLOG_ERROR, "strdup failed");
         BLog(BLOG_ERROR, "strdup failed");
         goto fail1;
         goto fail1;
     }
     }
     
     
-    // count statements
-    size_t num_st = 0;
-    struct NCDConfig_statements *st = conf->statements;
-    while (st) {
-        if (num_st == SIZE_MAX) {
-            BLog(BLOG_ERROR, "too many statements");
-            goto fail2;
-        }
-        num_st++;
-        st = st->next;
-    }
+    // get block
+    NCDBlock *block = NCDProcess_Block(proc_ast);
     
     
     // allocate statements array
     // allocate statements array
-    if (!(p->statements = BAllocArray(num_st, sizeof(p->statements[0])))) {
+    if (!(p->statements = BAllocArray(NCDBlock_NumStatements(block), sizeof(p->statements[0])))) {
         goto fail2;
         goto fail2;
     }
     }
     p->num_statements = 0;
     p->num_statements = 0;
     
     
     // init statements
     // init statements
-    st = conf->statements;
-    while (st) {
+    for (NCDStatement *st = NCDBlock_FirstStatement(block); st; st = NCDBlock_NextStatement(block, st)) {
         struct process_statement *ps = &p->statements[p->num_statements];
         struct process_statement *ps = &p->statements[p->num_statements];
         
         
         ps->p = p;
         ps->p = p;
@@ -1044,8 +999,6 @@ int process_new (struct NCDConfig_processes *conf, NCDModuleProcess *module_proc
         ps->have_error = 0;
         ps->have_error = 0;
         
         
         p->num_statements++;
         p->num_statements++;
-        
-        st = st->next;
     }
     }
     
     
     // set state working
     // set state working
@@ -1081,7 +1034,7 @@ fail2:
 fail1:
 fail1:
     free(p);
     free(p);
 fail0:
 fail0:
-    BLog(BLOG_ERROR, "failed to initialize process %s", conf->name);
+    BLog(BLOG_ERROR, "failed to initialize process %s", NCDProcess_Name(proc_ast));
     return 0;
     return 0;
 }
 }
 
 
@@ -1711,21 +1664,20 @@ int process_statement_instance_func_initprocess (struct process_statement *ps, N
     ASSERT(ps->state != SSTATE_FORGOTTEN)
     ASSERT(ps->state != SSTATE_FORGOTTEN)
     
     
     // find template
     // find template
-    struct NCDConfig_processes *conf = config_ast;
-    while (conf) {
-        if (conf->is_template && !strcmp(conf->name, template_name)) {
+    NCDProcess *p_ast;
+    for (p_ast = NCDProgram_FirstProcess(&program); p_ast; p_ast = NCDProgram_NextProcess(&program, p_ast)) {
+        if (NCDProcess_IsTemplate(p_ast) && !strcmp(NCDProcess_Name(p_ast), template_name)) {
             break;
             break;
         }
         }
-        conf = conf->next;
     }
     }
     
     
-    if (!conf) {
+    if (!p_ast) {
         process_statement_log(ps, BLOG_ERROR, "no template named %s", template_name);
         process_statement_log(ps, BLOG_ERROR, "no template named %s", template_name);
         return 0;
         return 0;
     }
     }
     
     
     // create process
     // create process
-    if (!process_new(conf, mp)) {
+    if (!process_new(p_ast, mp)) {
         process_statement_log(ps, BLOG_ERROR, "failed to create process from template %s", template_name);
         process_statement_log(ps, BLOG_ERROR, "failed to create process from template %s", template_name);
         return 0;
         return 0;
     }
     }

Some files were not shown because too many files changed in this diff