Forráskód Böngészése

Implement Do-Interrupt.

Ambroz Bizjak 11 éve
szülő
commit
2ed5eaaab7

+ 3 - 0
examples/ncd_tokenizer_test.c

@@ -129,6 +129,9 @@ static int tokenizer_output (void *user, int token, char *value, size_t value_le
         case NCD_TOKEN_DO:
             printf("do\n");
             break;
+        case NCD_TOKEN_INTERRUPT:
+            printf("interrupt\n");
+            break;
         default:
             printf("UNKNOWN_TOKEN\n");
             break;

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 428 - 398
generated/NCDConfigParser_parse.c


+ 10 - 9
generated/NCDConfigParser_parse.h

@@ -15,12 +15,13 @@
 #define ELIF                           15
 #define ELSE                           16
 #define BLOCK                          17
-#define TOKEN_DO                       18
-#define DOT                            19
-#define COMMA                          20
-#define BRACKET_OPEN                   21
-#define BRACKET_CLOSE                  22
-#define AT_SIGN                        23
-#define CARET                          24
-#define PROCESS                        25
-#define TEMPLATE                       26
+#define TOKEN_INTERRUPT                18
+#define TOKEN_DO                       19
+#define DOT                            20
+#define COMMA                          21
+#define BRACKET_OPEN                   22
+#define BRACKET_CLOSE                  23
+#define AT_SIGN                        24
+#define CARET                          25
+#define PROCESS                        26
+#define TEMPLATE                       27

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 511 - 464
generated/NCDConfigParser_parse.out


+ 30 - 11
generated/NCDConfigParser_parse.y

@@ -105,6 +105,7 @@ static void free_value (struct value o) { if (o.have) NCDValue_Free(&o.v); }
 %type name_maybe { char * }
 %type process_or_template { int }
 %type name_list { struct value }
+%type interrupt_maybe { struct block }
 
 // mention parser_out in some destructor to avoid an unused-variable warning
 %destructor processes { (void)parser_out; free_program($$); }
@@ -123,6 +124,7 @@ static void free_value (struct value o) { if (o.have) NCDValue_Free(&o.v); }
 %destructor value { free_value($$); }
 %destructor name_maybe { free($$); }
 %destructor name_list { free_value($$); }
+%destructor interrupt_maybe { free_block($$); }
 
 %stack_size 0
 
@@ -480,40 +482,57 @@ doneGA0:
     free(N);
 }
 
-statement(R) ::= TOKEN_DO CURLY_OPEN statements(S) CURLY_CLOSE name_maybe(N) SEMICOLON. {
+interrupt_maybe(R) ::= . {
+    R.have = 0;
+}
+
+interrupt_maybe(R) ::= TOKEN_INTERRUPT CURLY_OPEN statements(S) CURLY_CLOSE. {
+    R = S;
+}
+
+statement(R) ::= TOKEN_DO CURLY_OPEN statements(S) CURLY_CLOSE interrupt_maybe(I) name_maybe(N) SEMICOLON. {
     if (!S.have) {
         goto failGB0;
     }
     
-    NCDValue dummy_val;
-    NCDValue_InitList(&dummy_val);
+    NCDIfBlock if_block;
+    NCDIfBlock_Init(&if_block);
+    
+    if (I.have) {
+        NCDIf int_if;
+        NCDIf_InitBlock(&int_if, I.v);
+        I.have = 0;
+        
+        if (!NCDIfBlock_PrependIf(&if_block, int_if)) {
+            NCDIf_Free(&int_if);
+            goto failGB1;
+        }
+    }
     
     NCDIf the_if;
-    NCDIf_Init(&the_if, dummy_val, S.v);
+    NCDIf_InitBlock(&the_if, S.v);
     S.have = 0;
     
-    NCDIfBlock if_block;
-    NCDIfBlock_Init(&if_block);
-    
     if (!NCDIfBlock_PrependIf(&if_block, the_if)) {
-        NCDIfBlock_Free(&if_block);
         NCDIf_Free(&the_if);
-        goto failGB0;
+        goto failGB1;
     }
     
     if (!NCDStatement_InitIf(&R.v, N, if_block, NCDIFTYPE_DO)) {
-        NCDIfBlock_Free(&if_block);
-        goto failGB0;
+        goto failGB1;
     }
     
     R.have = 1;
     goto doneGB0;
     
+failGB1:
+    NCDIfBlock_Free(&if_block);
 failGB0:
     R.have = 0;
     parser_out->out_of_memory = 1;
 doneGB0:
     free_block(S);
+    free_block(I);
     free(N);
 }
 

+ 12 - 0
ncd/NCDAst.c

@@ -1094,6 +1094,12 @@ void NCDIf_Init (NCDIf *o, NCDValue cond, NCDBlock block)
     o->block = block;
 }
 
+void NCDIf_InitBlock (NCDIf *o, NCDBlock block)
+{
+    NCDValue_InitList(&o->cond);
+    o->block = block;
+}
+
 void NCDIf_Free (NCDIf *o)
 {
     NCDValue_Free(&o->cond);
@@ -1106,6 +1112,12 @@ void NCDIf_FreeGrab (NCDIf *o, NCDValue *out_cond, NCDBlock *out_block)
     *out_block = o->block;
 }
 
+NCDBlock NCDIf_FreeGrabBlock (NCDIf *o)
+{
+    NCDValue_Free(&o->cond);
+    return o->block;
+}
+
 NCDValue * NCDIf_Cond (NCDIf *o)
 {
     return &o->cond;

+ 2 - 0
ncd/NCDAst.h

@@ -250,8 +250,10 @@ 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_InitBlock (NCDIf *o, NCDBlock block);
 void NCDIf_Free (NCDIf *o);
 void NCDIf_FreeGrab (NCDIf *o, NCDValue *out_cond, NCDBlock *out_block);
+NCDBlock NCDIf_FreeGrabBlock (NCDIf *o);
 NCDValue * NCDIf_Cond (NCDIf *o);
 NCDBlock * NCDIf_Block (NCDIf *o);
 

+ 4 - 0
ncd/NCDConfigParser.c

@@ -174,6 +174,10 @@ static int tokenizer_output (void *user, int token, char *value, size_t value_le
             Parse(state->parser, TOKEN_DO, minor, &state->out);
         } break;
         
+        case NCD_TOKEN_INTERRUPT: {
+            Parse(state->parser, TOKEN_INTERRUPT, minor, &state->out);
+        } break;
+        
         default:
             BLog(BLOG_ERROR, "line %zu, character %zu: invalid token", line, line_char);
             free(minor.str);

+ 30 - 11
ncd/NCDConfigParser_parse.y

@@ -105,6 +105,7 @@ static void free_value (struct value o) { if (o.have) NCDValue_Free(&o.v); }
 %type name_maybe { char * }
 %type process_or_template { int }
 %type name_list { struct value }
+%type interrupt_maybe { struct block }
 
 // mention parser_out in some destructor to avoid an unused-variable warning
 %destructor processes { (void)parser_out; free_program($$); }
@@ -123,6 +124,7 @@ static void free_value (struct value o) { if (o.have) NCDValue_Free(&o.v); }
 %destructor value { free_value($$); }
 %destructor name_maybe { free($$); }
 %destructor name_list { free_value($$); }
+%destructor interrupt_maybe { free_block($$); }
 
 %stack_size 0
 
@@ -480,40 +482,57 @@ doneGA0:
     free(N);
 }
 
-statement(R) ::= TOKEN_DO CURLY_OPEN statements(S) CURLY_CLOSE name_maybe(N) SEMICOLON. {
+interrupt_maybe(R) ::= . {
+    R.have = 0;
+}
+
+interrupt_maybe(R) ::= TOKEN_INTERRUPT CURLY_OPEN statements(S) CURLY_CLOSE. {
+    R = S;
+}
+
+statement(R) ::= TOKEN_DO CURLY_OPEN statements(S) CURLY_CLOSE interrupt_maybe(I) name_maybe(N) SEMICOLON. {
     if (!S.have) {
         goto failGB0;
     }
     
-    NCDValue dummy_val;
-    NCDValue_InitList(&dummy_val);
+    NCDIfBlock if_block;
+    NCDIfBlock_Init(&if_block);
+    
+    if (I.have) {
+        NCDIf int_if;
+        NCDIf_InitBlock(&int_if, I.v);
+        I.have = 0;
+        
+        if (!NCDIfBlock_PrependIf(&if_block, int_if)) {
+            NCDIf_Free(&int_if);
+            goto failGB1;
+        }
+    }
     
     NCDIf the_if;
-    NCDIf_Init(&the_if, dummy_val, S.v);
+    NCDIf_InitBlock(&the_if, S.v);
     S.have = 0;
     
-    NCDIfBlock if_block;
-    NCDIfBlock_Init(&if_block);
-    
     if (!NCDIfBlock_PrependIf(&if_block, the_if)) {
-        NCDIfBlock_Free(&if_block);
         NCDIf_Free(&the_if);
-        goto failGB0;
+        goto failGB1;
     }
     
     if (!NCDStatement_InitIf(&R.v, N, if_block, NCDIFTYPE_DO)) {
-        NCDIfBlock_Free(&if_block);
-        goto failGB0;
+        goto failGB1;
     }
     
     R.have = 1;
     goto doneGB0;
     
+failGB1:
+    NCDIfBlock_Free(&if_block);
 failGB0:
     R.have = 0;
     parser_out->out_of_memory = 1;
 doneGB0:
     free_block(S);
+    free_block(I);
     free(N);
 }
 

+ 3 - 0
ncd/NCDConfigTokenizer.c

@@ -150,6 +150,9 @@ void NCDConfigTokenizer_Tokenize (MemRef the_str, NCDConfigTokenizer_output outp
         else if (l = data_begins_with(str, left, "Do")) {
             token = NCD_TOKEN_DO;
         }
+        else if (l = data_begins_with(str, left, "Interrupt")) {
+            token = NCD_TOKEN_INTERRUPT;
+        }
         else if (l = data_begins_with(str, left, "include_guard")) {
             token = NCD_TOKEN_INCLUDE_GUARD;
         }

+ 1 - 0
ncd/NCDConfigTokenizer.h

@@ -62,6 +62,7 @@
 #define NCD_TOKEN_BLOCK 24
 #define NCD_TOKEN_CARET 25
 #define NCD_TOKEN_DO 26
+#define NCD_TOKEN_INTERRUPT 27
 
 typedef int (*NCDConfigTokenizer_output) (void *user, int token, char *value, size_t value_len, size_t line, size_t line_char);
 

+ 16 - 21
ncd/NCDSugar.c

@@ -220,34 +220,28 @@ static int desugar_do (struct desugar_state *state, NCDBlock *block, NCDStatemen
     ASSERT(NCDStatement_IfType(stmt) == NCDIFTYPE_DO)
     
     NCDIfBlock *ifblock = NCDStatement_IfBlock(stmt);
-    ASSERT(NCDIfBlock_FirstIf(ifblock))
-    
-    NCDIf the_if = NCDIfBlock_GrabIf(ifblock, NCDIfBlock_FirstIf(ifblock));
-    
-    NCDValue if_cond;
-    NCDBlock if_block;
-    NCDIf_FreeGrab(&the_if, &if_cond, &if_block);
-    
-    NCDValue_Free(&if_cond);
-    
-    NCDValue action_arg;
-    if (!add_template(state, if_block, &action_arg)) {
-        goto fail;
-    }
     
     NCDValue stmt_args;
     NCDValue_InitList(&stmt_args);
     
-    if (!NCDValue_ListAppend(&stmt_args, action_arg)) {
-        NCDValue_Free(&stmt_args);
-        NCDValue_Free(&action_arg);
-        goto fail;
+    while (NCDIfBlock_FirstIf(ifblock)) {
+        NCDIf the_if = NCDIfBlock_GrabIf(ifblock, NCDIfBlock_FirstIf(ifblock));
+        NCDBlock if_block = NCDIf_FreeGrabBlock(&the_if);
+        
+        NCDValue action_arg;
+        if (!add_template(state, if_block, &action_arg)) {
+            goto fail1;
+        }
+        
+        if (!NCDValue_ListAppend(&stmt_args, action_arg)) {
+            NCDValue_Free(&action_arg);
+            goto fail1;
+        }
     }
     
     NCDStatement new_stmt;
     if (!NCDStatement_InitReg(&new_stmt, NCDStatement_Name(stmt), NULL, "do", stmt_args)) {
-        NCDValue_Free(&stmt_args);
-        goto fail;
+        goto fail1;
     }
     
     stmt = NCDBlock_ReplaceStatement(block, stmt, new_stmt);
@@ -255,7 +249,8 @@ static int desugar_do (struct desugar_state *state, NCDBlock *block, NCDStatemen
     *out_next = NCDBlock_NextStatement(block, stmt);
     return 1;
     
-fail:
+fail1:
+    NCDValue_Free(&stmt_args);
     return 0;
 }
 

+ 118 - 28
ncd/modules/try.c

@@ -31,10 +31,10 @@
  * 
  * Synopsis:
  *   try(string template_name, list args)
- *   do(string template_name)
+ *   do(string template_name[, string interrupt_template_name])
  * 
  * Description:
- *   Does the following:
+ *   The basic functionality is:
  *   1. Starts a template process from the specified template and arguments.
  *   2. Waits for the process to initialize completely, or for a _try->assert()
  *      assertion to fail or a _do->break() call.
@@ -43,9 +43,21 @@
  *      managed to initialize, or an assertion failed.
  *   If at any point during these steps termination of the try statement is
  *   requested, requests the process to terminate (if not already), and dies
- *   when it terminates. The differences between try() and do() are that do()
- *   directly exposes the caller scope (try() does via _caller), and the
- *   availability of assert/break.
+ *   when it terminates.
+ * 
+ *   The differences between try() and do() are that do() directly exposes
+ *   the caller scope (try() does via _caller), and the availability of
+ *   assert/break.
+ * 
+ *   The two-argument version of do() is an extension, where in case of a
+ *   statement termination request while the template process is not yet
+ *   initialized completely, the interrupt_template_name template process is
+ *   started, instead of sending a termination request to the template_name
+ *   process. The interrupt_template_name process has access to the same
+ *   scope as template_name. This means that it can itself call _do->break().
+ *   Termination of the interrupt_template_name process is started as soon
+ *   as it initializes completely, or when termination of the template_name
+ *   process starts.
  * 
  * Variables:
  *   string succeeded - "true" if the template process finished, "false" if assert
@@ -81,20 +93,21 @@
 struct instance {
     NCDModuleInst *i;
     int is_do;
+    NCDValRef interrupt_template_name;
     NCDModuleProcess process;
+    NCDModuleProcess interrupt_process;
     int state;
+    int intstate;
     int dying;
     int succeeded;
 };
 
-enum {STATE_INIT, STATE_DEINIT, STATE_FINISHED};
+enum {STATE_INIT, STATE_DEINIT, STATE_FINISHED, STATE_WAITINT};
+enum {INTSTATE_NONE, INTSTATE_INIT, INTSTATE_DEINIT};
 
-static void process_handler_event (NCDModuleProcess *process, int event);
-static int process_func_getspecialobj (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object);
 static int process_caller_object_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object);
 static void start_terminating (struct instance *o);
 static void instance_free (struct instance *o);
-static void instance_break (struct instance *o);
 
 enum {STRING_TRY, STRING_TRY_TRY, STRING_DO, STRING_DO_DO};
 
@@ -123,9 +136,13 @@ static void process_handler_event (NCDModuleProcess *process, int event)
             // free process
             NCDModuleProcess_Free(&o->process);
             
-            // die finally if requested
             if (o->dying) {
-                instance_free(o);
+                // We want to die but not without interrupt_process still running.
+                if (o->intstate == INTSTATE_NONE) {
+                    instance_free(o);
+                } else {
+                    o->state = STATE_WAITINT;
+                }
                 return;
             }
             
@@ -138,11 +155,44 @@ static void process_handler_event (NCDModuleProcess *process, int event)
     }
 }
 
-static int process_func_getspecialobj (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object)
+static void interrupt_process_handler_event (NCDModuleProcess *process, int event)
 {
-    struct instance *o = UPPER_OBJECT(process, struct instance, process);
-    ASSERT(o->state == STATE_INIT || o->state == STATE_DEINIT)
+    struct instance *o = UPPER_OBJECT(process, struct instance, interrupt_process);
+    ASSERT(o->dying)
+    ASSERT(o->intstate != INTSTATE_NONE)
     
+    switch (event) {
+        case NCDMODULEPROCESS_EVENT_UP: {
+            ASSERT(o->intstate == INTSTATE_INIT)
+            
+            // Start terminating the interrupt_process.
+            NCDModuleProcess_Terminate(&o->interrupt_process);
+            o->intstate = INTSTATE_DEINIT;
+        } break;
+        
+        case NCDMODULEPROCESS_EVENT_DOWN: {
+            // Can't happen since we start terminating with it comes up.
+            ASSERT(0)
+        } break;
+        
+        case NCDMODULEPROCESS_EVENT_TERMINATED: {
+            ASSERT(o->intstate == INTSTATE_DEINIT)
+            
+            // free process
+            NCDModuleProcess_Free(&o->interrupt_process);
+            o->intstate = INTSTATE_NONE;
+            
+            // If the main process has terminated, free the instance now.
+            if (o->state == STATE_WAITINT) {
+                instance_free(o);
+                return;
+            }
+        } break;
+    }
+}
+
+static int common_getspecialobj (struct instance *o, NCD_string_id_t name, NCDObject *out_object)
+{
     if (o->is_do) {
         if (name == ModuleString(o->i, STRING_DO)) {
             *out_object = NCDObject_Build(ModuleString(o->i, STRING_DO_DO), o, NCDObject_no_getvar, NCDObject_no_getobj);
@@ -165,10 +215,21 @@ static int process_func_getspecialobj (NCDModuleProcess *process, NCD_string_id_
     }
 }
 
+static int process_func_getspecialobj (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object)
+{
+    struct instance *o = UPPER_OBJECT(process, struct instance, process);
+    return common_getspecialobj(o, name, out_object);
+}
+
+static int interrupt_process_func_getspecialobj (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object)
+{
+    struct instance *o = UPPER_OBJECT(process, struct instance, interrupt_process);
+    return common_getspecialobj(o, name, out_object);
+}
+
 static int process_caller_object_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object)
 {
     struct instance *o = NCDObject_DataPtr(obj);
-    ASSERT(o->state == STATE_INIT || o->state == STATE_DEINIT)
     
     return NCDModuleInst_Backend_GetObj(o->i, name, out_object);
 }
@@ -177,18 +238,23 @@ static void start_terminating (struct instance *o)
 {
     ASSERT(o->state == STATE_INIT)
     
+    // request termination of the interrupt_process if possible
+    if (o->intstate == INTSTATE_INIT) {
+        NCDModuleProcess_Terminate(&o->interrupt_process);
+        o->intstate = INTSTATE_DEINIT;
+    }
+    
     // request process termination
     NCDModuleProcess_Terminate(&o->process);
-    
-    // set state deinit
     o->state = STATE_DEINIT;
 }
 
-static void func_new_common (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params, int is_do, NCDValRef template_name, NCDValRef args)
+static void func_new_common (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params, int is_do, NCDValRef template_name, NCDValRef args, NCDValRef interrupt_template_name)
 {
     struct instance *o = vo;
     o->i = i;
     o->is_do = is_do;
+    o->interrupt_template_name = interrupt_template_name;
     
     // start process
     if (!NCDModuleProcess_InitValue(&o->process, i, template_name, args, process_handler_event)) {
@@ -201,6 +267,7 @@ static void func_new_common (void *vo, NCDModuleInst *i, const struct NCDModuleI
     
     // set state init, not dying, assume succeeded
     o->state = STATE_INIT;
+    o->intstate = INTSTATE_NONE;
     o->dying = 0;
     o->succeeded = 1;
     return;
@@ -223,7 +290,7 @@ static void func_new_try (void *vo, NCDModuleInst *i, const struct NCDModuleInst
         goto fail;
     }
     
-    return func_new_common(vo, i, params, 0, template_name_arg, args_arg);
+    return func_new_common(vo, i, params, 0, template_name_arg, args_arg, NCDVal_NewInvalid());
     
 fail:
     NCDModuleInst_Backend_DeadError(i);
@@ -233,30 +300,35 @@ static void func_new_do (void *vo, NCDModuleInst *i, const struct NCDModuleInst_
 {
     // check arguments
     NCDValRef template_name_arg;
-    if (!NCDVal_ListRead(params->args, 1, &template_name_arg)) {
+    NCDValRef interrupt_template_name_arg = NCDVal_NewInvalid();
+    if (!NCDVal_ListRead(params->args, 1, &template_name_arg) &&
+        !NCDVal_ListRead(params->args, 2, &template_name_arg, &interrupt_template_name_arg)
+    ) {
         ModuleLog(i, BLOG_ERROR, "wrong arity");
         goto fail;
     }
-    if (!NCDVal_IsString(template_name_arg)) {
+    if (!NCDVal_IsString(template_name_arg) ||
+        (!NCDVal_IsInvalid(interrupt_template_name_arg) && !NCDVal_IsString(interrupt_template_name_arg))
+    ) {
         ModuleLog(i, BLOG_ERROR, "wrong type");
         goto fail;
     }
     
-    return func_new_common(vo, i, params, 1, template_name_arg, NCDVal_NewInvalid());
+    return func_new_common(vo, i, params, 1, template_name_arg, NCDVal_NewInvalid(), interrupt_template_name_arg);
     
 fail:
     NCDModuleInst_Backend_DeadError(i);
 }
 
 static void instance_free (struct instance *o)
-{   
+{
+    ASSERT(o->intstate == INTSTATE_NONE)
+    
     NCDModuleInst_Backend_Dead(o->i);
 }
 
 static void instance_break (struct instance *o)
 {
-    ASSERT(o->state == STATE_INIT || o->state == STATE_DEINIT)
-    
     // mark not succeeded
     o->succeeded = 0;
     
@@ -270,6 +342,7 @@ static void func_die (void *vo)
 {
     struct instance *o = vo;
     ASSERT(!o->dying)
+    ASSERT(o->intstate == INTSTATE_NONE)
     
     // if we're finished, die immediately
     if (o->state == STATE_FINISHED) {
@@ -280,10 +353,27 @@ static void func_die (void *vo)
     // set dying
     o->dying = 1;
     
-    // start terminating if not already
-    if (o->state == STATE_INIT) {
-        start_terminating(o);
+    // if already terminating, nothing to do
+    if (o->state != STATE_INIT) {
+        return;
+    }
+    
+    // if we don't have the interrupt-template, start terminating
+    if (NCDVal_IsInvalid(o->interrupt_template_name)) {
+        goto terminate;
     }
+    
+    // start process
+    if (!NCDModuleProcess_InitValue(&o->interrupt_process, o->i, o->interrupt_template_name, NCDVal_NewInvalid(), interrupt_process_handler_event)) {
+        ModuleLog(o->i, BLOG_ERROR, "NCDModuleProcess_Init failed");
+        goto terminate;
+    }
+    NCDModuleProcess_SetSpecialFuncs(&o->interrupt_process, interrupt_process_func_getspecialobj);
+    o->intstate = INTSTATE_INIT;
+    return;
+    
+terminate:
+    start_terminating(o);
 }
 
 static int func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out)

Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott