Просмотр исходного кода

ncd: NCDValueParser: use NCDValCons to construct values

ambrop7 13 лет назад
Родитель
Сommit
4e0dbb3096

+ 65 - 92
generated/NCDValueParser_parse.c

@@ -371,26 +371,16 @@ static void yy_destructor(
     case 6: /* BRACKET_CLOSE */
     case 7: /* STRING */
 {
-#line 34 "NCDValueParser_parse.y"
+#line 37 "NCDValueParser_parse.y"
  free_token((yypminor->yy0)); 
 #line 377 "NCDValueParser_parse.c"
 }
       break;
     case 9: /* list_contents */
 {
-#line 43 "NCDValueParser_parse.y"
- (void)parser_out; free_value((yypminor->yy1)); 
+#line 47 "NCDValueParser_parse.y"
+ (void)parser_out; 
 #line 384 "NCDValueParser_parse.c"
-}
-      break;
-    case 10: /* list */
-    case 11: /* map_contents */
-    case 12: /* map */
-    case 13: /* value */
-{
-#line 44 "NCDValueParser_parse.y"
- free_value((yypminor->yy1)); 
-#line 394 "NCDValueParser_parse.c"
 }
       break;
     default:  break;   /* If no destructor action specified: do nothing */
@@ -563,12 +553,12 @@ static void yyStackOverflow(yyParser *yypParser, YYMINORTYPE *yypMinor){
    while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser);
    /* Here code is inserted which will execute if the parser
    ** stack every overflows */
-#line 56 "NCDValueParser_parse.y"
+#line 58 "NCDValueParser_parse.y"
 
     if (yypMinor) {
         free_token(yypMinor->yy0);
     }
-#line 572 "NCDValueParser_parse.c"
+#line 562 "NCDValueParser_parse.c"
    ParseARG_STORE; /* Suppress warning about unused %extra_argument var */
 }
 
@@ -692,182 +682,166 @@ static void yy_reduce(
   **     break;
   */
       case 0: /* input ::= value */
-#line 62 "NCDValueParser_parse.y"
+#line 64 "NCDValueParser_parse.y"
 {
-    if (!yymsp[0].minor.yy1.have || parser_out->have_value) {
-        free_value(yymsp[0].minor.yy1);
-    } else {
-        parser_out->have_value = 1;
-        parser_out->value = yymsp[0].minor.yy1.v;
+    if (!yymsp[0].minor.yy1.have) {
+        goto failZ0;
+    }
+    
+    if (!NCDVal_IsInvalid(parser_out->value)) {
+        // should never happen
+        parser_out->error_flags |= ERROR_FLAG_SYNTAX;
+        goto failZ0;
+    }
+    
+    if (!NCDValCons_Complete(&parser_out->cons, yymsp[0].minor.yy1.v, &parser_out->value, &parser_out->cons_error)) {
+        handle_cons_error(parser_out);
+        goto failZ0;
     }
+    
+failZ0:;
 }
 #line 705 "NCDValueParser_parse.c"
         break;
       case 1: /* list_contents ::= value */
-#line 71 "NCDValueParser_parse.y"
+#line 83 "NCDValueParser_parse.y"
 {
     if (!yymsp[0].minor.yy1.have) {
         goto failL0;
     }
 
-    NCDValue_InitList(&yygotominor.yy1.v);
+    NCDValCons_NewList(&parser_out->cons, &yygotominor.yy1.v);
 
-    if (!NCDValue_ListPrepend(&yygotominor.yy1.v, yymsp[0].minor.yy1.v)) {
-        goto failL1;
+    if (!NCDValCons_ListPrepend(&parser_out->cons, &yygotominor.yy1.v, yymsp[0].minor.yy1.v, &parser_out->cons_error)) {
+        handle_cons_error(parser_out);
+        goto failL0;
     }
-    yymsp[0].minor.yy1.have = 0;
 
     yygotominor.yy1.have = 1;
     goto doneL;
 
-failL1:
-    NCDValue_Free(&yygotominor.yy1.v);
 failL0:
     yygotominor.yy1.have = 0;
-    parser_out->out_of_memory = 1;
-doneL:
-    free_value(yymsp[0].minor.yy1);
+doneL:;
 }
-#line 732 "NCDValueParser_parse.c"
+#line 728 "NCDValueParser_parse.c"
         break;
       case 2: /* list_contents ::= value COMMA list_contents */
-#line 95 "NCDValueParser_parse.y"
+#line 103 "NCDValueParser_parse.y"
 {
     if (!yymsp[-2].minor.yy1.have || !yymsp[0].minor.yy1.have) {
         goto failM0;
     }
 
-    if (!NCDValue_ListPrepend(&yymsp[0].minor.yy1.v, yymsp[-2].minor.yy1.v)) {
+    if (!NCDValCons_ListPrepend(&parser_out->cons, &yymsp[0].minor.yy1.v, yymsp[-2].minor.yy1.v, &parser_out->cons_error)) {
+        handle_cons_error(parser_out);
         goto failM0;
     }
-    yymsp[-2].minor.yy1.have = 0;
 
     yygotominor.yy1.have = 1;
     yygotominor.yy1.v = yymsp[0].minor.yy1.v;
-    yymsp[0].minor.yy1.have = 0;
     goto doneM;
 
 failM0:
     yygotominor.yy1.have = 0;
-    parser_out->out_of_memory = 1;
-doneM:
-    free_value(yymsp[-2].minor.yy1);
-    free_value(yymsp[0].minor.yy1);
+doneM:;
   yy_destructor(yypParser,1,&yymsp[-1].minor);
 }
-#line 759 "NCDValueParser_parse.c"
+#line 751 "NCDValueParser_parse.c"
         break;
       case 3: /* list ::= CURLY_OPEN CURLY_CLOSE */
-#line 118 "NCDValueParser_parse.y"
+#line 122 "NCDValueParser_parse.y"
 {
+    NCDValCons_NewList(&parser_out->cons, &yygotominor.yy1.v);
     yygotominor.yy1.have = 1;
-    NCDValue_InitList(&yygotominor.yy1.v);
   yy_destructor(yypParser,2,&yymsp[-1].minor);
   yy_destructor(yypParser,3,&yymsp[0].minor);
 }
-#line 769 "NCDValueParser_parse.c"
+#line 761 "NCDValueParser_parse.c"
         break;
       case 4: /* list ::= CURLY_OPEN list_contents CURLY_CLOSE */
-#line 123 "NCDValueParser_parse.y"
+#line 127 "NCDValueParser_parse.y"
 {
     yygotominor.yy1 = yymsp[-1].minor.yy1;
   yy_destructor(yypParser,2,&yymsp[-2].minor);
   yy_destructor(yypParser,3,&yymsp[0].minor);
 }
-#line 778 "NCDValueParser_parse.c"
+#line 770 "NCDValueParser_parse.c"
         break;
       case 5: /* map_contents ::= value COLON value */
-#line 127 "NCDValueParser_parse.y"
+#line 131 "NCDValueParser_parse.y"
 {
     if (!yymsp[-2].minor.yy1.have || !yymsp[0].minor.yy1.have) {
         goto failS0;
     }
 
-    NCDValue_InitMap(&yygotominor.yy1.v);
+    NCDValCons_NewMap(&parser_out->cons, &yygotominor.yy1.v);
 
-    if (!NCDValue_MapInsert(&yygotominor.yy1.v, yymsp[-2].minor.yy1.v, yymsp[0].minor.yy1.v)) {
-        goto failS1;
+    if (!NCDValCons_MapInsert(&parser_out->cons, &yygotominor.yy1.v, yymsp[-2].minor.yy1.v, yymsp[0].minor.yy1.v, &parser_out->cons_error)) {
+        handle_cons_error(parser_out);
+        goto failS0;
     }
-    yymsp[-2].minor.yy1.have = 0;
-    yymsp[0].minor.yy1.have = 0;
 
     yygotominor.yy1.have = 1;
     goto doneS;
 
-failS1:
-    NCDValue_Free(&yygotominor.yy1.v);
 failS0:
     yygotominor.yy1.have = 0;
-    parser_out->out_of_memory = 1;
-doneS:
-    free_value(yymsp[-2].minor.yy1);
-    free_value(yymsp[0].minor.yy1);
+doneS:;
   yy_destructor(yypParser,4,&yymsp[-1].minor);
 }
-#line 808 "NCDValueParser_parse.c"
+#line 794 "NCDValueParser_parse.c"
         break;
       case 6: /* map_contents ::= value COLON value COMMA map_contents */
-#line 153 "NCDValueParser_parse.y"
+#line 151 "NCDValueParser_parse.y"
 {
     if (!yymsp[-4].minor.yy1.have || !yymsp[-2].minor.yy1.have || !yymsp[0].minor.yy1.have) {
         goto failT0;
     }
 
-    if (NCDValue_MapFindKey(&yymsp[0].minor.yy1.v, &yymsp[-4].minor.yy1.v)) {
-        BLog(BLOG_ERROR, "duplicate key in map");
-        yygotominor.yy1.have = 0;
-        parser_out->syntax_error = 1;
-        goto doneT;
-    }
-
-    if (!NCDValue_MapInsert(&yymsp[0].minor.yy1.v, yymsp[-4].minor.yy1.v, yymsp[-2].minor.yy1.v)) {
+    if (!NCDValCons_MapInsert(&parser_out->cons, &yymsp[0].minor.yy1.v, yymsp[-4].minor.yy1.v, yymsp[-2].minor.yy1.v, &parser_out->cons_error)) {
+        handle_cons_error(parser_out);
         goto failT0;
     }
-    yymsp[-4].minor.yy1.have = 0;
-    yymsp[-2].minor.yy1.have = 0;
 
     yygotominor.yy1.have = 1;
     yygotominor.yy1.v = yymsp[0].minor.yy1.v;
-    yymsp[0].minor.yy1.have = 0;
     goto doneT;
 
 failT0:
     yygotominor.yy1.have = 0;
-    parser_out->out_of_memory = 1;
-doneT:
-    free_value(yymsp[-4].minor.yy1);
-    free_value(yymsp[-2].minor.yy1);
-    free_value(yymsp[0].minor.yy1);
+doneT:;
   yy_destructor(yypParser,4,&yymsp[-3].minor);
   yy_destructor(yypParser,1,&yymsp[-1].minor);
 }
-#line 845 "NCDValueParser_parse.c"
+#line 818 "NCDValueParser_parse.c"
         break;
       case 7: /* map ::= BRACKET_OPEN BRACKET_CLOSE */
-#line 185 "NCDValueParser_parse.y"
+#line 170 "NCDValueParser_parse.y"
 {
+    NCDValCons_NewList(&parser_out->cons, &yygotominor.yy1.v);
     yygotominor.yy1.have = 1;
-    NCDValue_InitMap(&yygotominor.yy1.v);
   yy_destructor(yypParser,5,&yymsp[-1].minor);
   yy_destructor(yypParser,6,&yymsp[0].minor);
 }
-#line 855 "NCDValueParser_parse.c"
+#line 828 "NCDValueParser_parse.c"
         break;
       case 8: /* map ::= BRACKET_OPEN map_contents BRACKET_CLOSE */
-#line 190 "NCDValueParser_parse.y"
+#line 175 "NCDValueParser_parse.y"
 {
     yygotominor.yy1 = yymsp[-1].minor.yy1;
   yy_destructor(yypParser,5,&yymsp[-2].minor);
   yy_destructor(yypParser,6,&yymsp[0].minor);
 }
-#line 864 "NCDValueParser_parse.c"
+#line 837 "NCDValueParser_parse.c"
         break;
       case 9: /* value ::= STRING */
-#line 194 "NCDValueParser_parse.y"
+#line 179 "NCDValueParser_parse.y"
 {
     ASSERT(yymsp[0].minor.yy0.str)
 
-    if (!NCDValue_InitStringBin(&yygotominor.yy1.v, (uint8_t *)yymsp[0].minor.yy0.str, yymsp[0].minor.yy0.len)) {
+    if (!NCDValCons_NewString(&parser_out->cons, (const uint8_t *)yymsp[0].minor.yy0.str, yymsp[0].minor.yy0.len, &yygotominor.yy1.v, &parser_out->cons_error)) {
+        handle_cons_error(parser_out);
         goto failU0;
     }
 
@@ -876,19 +850,18 @@ doneT:
 
 failU0:
     yygotominor.yy1.have = 0;
-    parser_out->out_of_memory = 1;
-doneU:
+doneU:;
     free_token(yymsp[0].minor.yy0);
 }
-#line 884 "NCDValueParser_parse.c"
+#line 857 "NCDValueParser_parse.c"
         break;
       case 10: /* value ::= list */
       case 11: /* value ::= map */ yytestcase(yyruleno==11);
-#line 211 "NCDValueParser_parse.y"
+#line 196 "NCDValueParser_parse.y"
 {
     yygotominor.yy1 = yymsp[0].minor.yy1;
 }
-#line 892 "NCDValueParser_parse.c"
+#line 865 "NCDValueParser_parse.c"
         break;
       default:
         break;
@@ -950,10 +923,10 @@ static void yy_syntax_error(
 ){
   ParseARG_FETCH;
 #define TOKEN (yyminor.yy0)
-#line 51 "NCDValueParser_parse.y"
+#line 53 "NCDValueParser_parse.y"
 
-    parser_out->syntax_error = 1;
-#line 957 "NCDValueParser_parse.c"
+    parser_out->error_flags |= ERROR_FLAG_SYNTAX;
+#line 930 "NCDValueParser_parse.c"
   ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */
 }
 

+ 45 - 60
generated/NCDValueParser_parse.y

@@ -27,29 +27,31 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+// argument for passing state to parser hooks
 %extra_argument { struct parser_state *parser_out }
 
+// type of structure representing tokens
 %token_type { struct token }
 
+// token destructor frees extra memory allocated for tokens
 %token_destructor { free_token($$); }
 
+// types of nonterminals
 %type list_contents { struct value }
 %type list { struct value }
 %type map_contents { struct value }
 %type map  { struct value }
 %type value  { struct value }
 
-// mention parser_out in some destructor to a void unused variable warning
-%destructor list_contents { (void)parser_out; free_value($$); }
-%destructor list { free_value($$); }
-%destructor map_contents { free_value($$); }
-%destructor map { free_value($$); }
-%destructor value { free_value($$); }
+// mention parser_out in some destructor to and avoid unused variable warning
+%destructor list_contents { (void)parser_out; }
 
+// try to dynamically grow the parse stack
 %stack_size 0
 
+// on syntax error, set the corresponding error flag
 %syntax_error {
-    parser_out->syntax_error = 1;
+    parser_out->error_flags |= ERROR_FLAG_SYNTAX;
 }
 
 // workaroud Lemon bug: if the stack overflows, the token that caused the overflow will be leaked
@@ -60,12 +62,22 @@
 }
 
 input ::= value(A). {
-    if (!A.have || parser_out->have_value) {
-        free_value(A);
-    } else {
-        parser_out->have_value = 1;
-        parser_out->value = A.v;
+    if (!A.have) {
+        goto failZ0;
+    }
+    
+    if (!NCDVal_IsInvalid(parser_out->value)) {
+        // should never happen
+        parser_out->error_flags |= ERROR_FLAG_SYNTAX;
+        goto failZ0;
     }
+    
+    if (!NCDValCons_Complete(&parser_out->cons, A.v, &parser_out->value, &parser_out->cons_error)) {
+        handle_cons_error(parser_out);
+        goto failZ0;
+    }
+    
+failZ0:;
 }
 
 list_contents(R) ::= value(A). {
@@ -73,23 +85,19 @@ list_contents(R) ::= value(A). {
         goto failL0;
     }
 
-    NCDValue_InitList(&R.v);
+    NCDValCons_NewList(&parser_out->cons, &R.v);
 
-    if (!NCDValue_ListPrepend(&R.v, A.v)) {
-        goto failL1;
+    if (!NCDValCons_ListPrepend(&parser_out->cons, &R.v, A.v, &parser_out->cons_error)) {
+        handle_cons_error(parser_out);
+        goto failL0;
     }
-    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);
+doneL:;
 }
 
 list_contents(R) ::= value(A) COMMA list_contents(N). {
@@ -97,27 +105,23 @@ list_contents(R) ::= value(A) COMMA list_contents(N). {
         goto failM0;
     }
 
-    if (!NCDValue_ListPrepend(&N.v, A.v)) {
+    if (!NCDValCons_ListPrepend(&parser_out->cons, &N.v, A.v, &parser_out->cons_error)) {
+        handle_cons_error(parser_out);
         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);
+doneM:;
 }
 
 list(R) ::= CURLY_OPEN CURLY_CLOSE. {
+    NCDValCons_NewList(&parser_out->cons, &R.v);
     R.have = 1;
-    NCDValue_InitList(&R.v);
 }
 
 list(R) ::= CURLY_OPEN list_contents(A) CURLY_CLOSE. {
@@ -129,25 +133,19 @@ map_contents(R) ::= value(A) COLON value(B). {
         goto failS0;
     }
 
-    NCDValue_InitMap(&R.v);
+    NCDValCons_NewMap(&parser_out->cons, &R.v);
 
-    if (!NCDValue_MapInsert(&R.v, A.v, B.v)) {
-        goto failS1;
+    if (!NCDValCons_MapInsert(&parser_out->cons, &R.v, A.v, B.v, &parser_out->cons_error)) {
+        handle_cons_error(parser_out);
+        goto failS0;
     }
-    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);
+doneS:;
 }
 
 map_contents(R) ::= value(A) COLON value(B) COMMA map_contents(N). {
@@ -155,36 +153,23 @@ map_contents(R) ::= value(A) COLON value(B) COMMA map_contents(N). {
         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)) {
+    if (!NCDValCons_MapInsert(&parser_out->cons, &N.v, A.v, B.v, &parser_out->cons_error)) {
+        handle_cons_error(parser_out);
         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);
+doneT:;
 }
 
 map(R) ::= BRACKET_OPEN BRACKET_CLOSE. {
+    NCDValCons_NewList(&parser_out->cons, &R.v);
     R.have = 1;
-    NCDValue_InitMap(&R.v);
 }
 
 map(R) ::= BRACKET_OPEN map_contents(A) BRACKET_CLOSE. {
@@ -194,7 +179,8 @@ map(R) ::= BRACKET_OPEN map_contents(A) BRACKET_CLOSE. {
 value(R) ::= STRING(A). {
     ASSERT(A.str)
 
-    if (!NCDValue_InitStringBin(&R.v, (uint8_t *)A.str, A.len)) {
+    if (!NCDValCons_NewString(&parser_out->cons, (const uint8_t *)A.str, A.len, &R.v, &parser_out->cons_error)) {
+        handle_cons_error(parser_out);
         goto failU0;
     }
 
@@ -203,8 +189,7 @@ value(R) ::= STRING(A). {
 
 failU0:
     R.have = 0;
-    parser_out->out_of_memory = 1;
-doneU:
+doneU:;
     free_token(A);
 }
 

+ 1 - 1
ncd/CMakeLists.txt

@@ -48,7 +48,7 @@ target_link_libraries(ncdvaluegenerator base ncdvalue ncdval)
 add_library(ncdvalueparser
     NCDValueParser.c
 )
-target_link_libraries(ncdvalueparser base ncdvalue ncdval ncdtokenizer ncdvalcompat)
+target_link_libraries(ncdvalueparser base ncdvalue ncdval ncdtokenizer ncdvalcompat ncdvalcons)
 
 add_library(ncdast
     NCDAst.c

+ 84 - 47
ncd/NCDValueParser.c

@@ -34,6 +34,7 @@
 #include <base/BLog.h>
 #include <ncd/NCDConfigTokenizer.h>
 #include <ncd/NCDValCompat.h>
+#include <ncd/NCDValCons.h>
 
 #include "NCDValueParser.h"
 
@@ -46,27 +47,40 @@ struct token {
 
 struct value {
     int have;
-    NCDValue v;
+    NCDValConsVal v;
 };
 
+#define ERROR_FLAG_MEMORY        (1 << 0)
+#define ERROR_FLAG_TOKENIZATION  (1 << 1)
+#define ERROR_FLAG_SYNTAX        (1 << 2)
+#define ERROR_FLAG_DUPLICATE_KEY (1 << 3)
+
 struct parser_state {
-    int have_value;
-    NCDValue value;
-    int out_of_memory;
-    int syntax_error;
-    int error;
+    NCDValCons cons;
+    NCDValRef value;
+    int cons_error;
+    int error_flags;
     void *parser;
 };
 
 static void free_token (struct token o)
 {
-    free(o.str);
+    if (o.str) {
+        free(o.str);
+    }
 };
 
-static void free_value (struct value o)
+static void handle_cons_error (struct parser_state *state)
 {
-    if (o.have) {
-        NCDValue_Free(&o.v);
+    switch (state->cons_error) {
+        case NCDVALCONS_ERROR_MEMORY:
+            state->error_flags |= ERROR_FLAG_MEMORY;
+            break;
+        case NCDVALCONS_ERROR_DUPLICATE_KEY:
+            state->error_flags |= ERROR_FLAG_DUPLICATE_KEY;
+            break;
+        default:
+            ASSERT(0);
     }
 }
 
@@ -77,18 +91,17 @@ static void free_value (struct value o)
 #define ParseFree ParseFree_NCDValueParser
 #define Parse Parse_NCDValueParser
 
+// include the generated Lemon parser
 #include "../generated/NCDValueParser_parse.c"
 #include "../generated/NCDValueParser_parse.h"
 
 static int tokenizer_output (void *user, int token, char *value, size_t value_len, size_t line, size_t line_char)
 {
     struct parser_state *state = user;
-    ASSERT(!state->out_of_memory)
-    ASSERT(!state->syntax_error)
-    ASSERT(!state->error)
+    ASSERT(!state->error_flags)
     
     if (token == NCD_ERROR) {
-        BLog(BLOG_ERROR, "line %zu, character %zu: tokenizer error", line, line_char);
+        state->error_flags |= ERROR_FLAG_TOKENIZATION;
         goto fail;
     }
     
@@ -130,78 +143,102 @@ static int tokenizer_output (void *user, int token, char *value, size_t value_le
         } break;
         
         default:
-            BLog(BLOG_ERROR, "line %zu, character %zu: invalid token", line, line_char);
+            state->error_flags |= ERROR_FLAG_TOKENIZATION;
             free_token(minor);
             goto fail;
     }
     
-    if (state->syntax_error) {
-        BLog(BLOG_ERROR, "line %zu, character %zu: syntax error", line, line_char);
-        goto fail;
-    }
-    
-    if (state->out_of_memory) {
-        BLog(BLOG_ERROR, "line %zu, character %zu: out of memory", line, line_char);
+    if (state->error_flags) {
         goto fail;
     }
     
     return 1;
     
 fail:
-    state->error = 1;
+    ASSERT(state->error_flags)
+    
+    if ((state->error_flags & ERROR_FLAG_MEMORY)) {
+        BLog(BLOG_ERROR, "line %zu, character %zu: memory allocation error", line, line_char);
+    }
+    
+    if ((state->error_flags & ERROR_FLAG_TOKENIZATION)) {
+        BLog(BLOG_ERROR, "line %zu, character %zu: tokenization error", line, line_char);
+    }
+    
+    if ((state->error_flags & ERROR_FLAG_SYNTAX)) {
+        BLog(BLOG_ERROR, "line %zu, character %zu: syntax error", line, line_char);
+    }
+    
+    if ((state->error_flags & ERROR_FLAG_DUPLICATE_KEY)) {
+        BLog(BLOG_ERROR, "line %zu, character %zu: duplicate key in map error", line, line_char);
+    }
+    
     return 0;
 }
 
-int NCDValueParser_Parse (const char *str, size_t str_len, NCDValue *out_value)
+int NCDValParser_Parse (const char *str, size_t str_len, NCDValMem *mem, NCDValRef *out_value)
 {
     ASSERT(str_len == 0 || str)
+    ASSERT(mem)
     ASSERT(out_value)
     
+    int ret = 0;
+    
     struct parser_state state;
-    state.have_value = 0;
-    state.out_of_memory = 0;
-    state.syntax_error = 0;
-    state.error = 0;
+    state.value = NCDVal_NewInvalid();
+    state.error_flags = 0;
+    
+    if (!NCDValCons_Init(&state.cons, mem)) {
+        BLog(BLOG_ERROR, "NCDValCons_Init failed");
+        goto fail0;
+    }
     
     if (!(state.parser = ParseAlloc(malloc))) {
         BLog(BLOG_ERROR, "ParseAlloc failed");
-        return 0;
+        goto fail1;
     }
     
     NCDConfigTokenizer_Tokenize((char *)str, str_len, tokenizer_output, &state);
     
     ParseFree(state.parser, free);
     
-    if (state.error) {
-        if (state.have_value) {
-            NCDValue_Free(&state.value);
-        }
-        return 0;
+    if (state.error_flags) {
+        goto fail1;
     }
     
-    ASSERT(state.have_value)
+    ASSERT(!NCDVal_IsInvalid(state.value))
     
     *out_value = state.value;
-    return 1;
+    ret = 1;
+    
+fail1:
+    NCDValCons_Free(&state.cons);
+fail0:
+    return ret;
 }
 
-int NCDValParser_Parse (const char *str, size_t str_len, NCDValMem *mem, NCDValRef *out_value)
+int NCDValueParser_Parse (const char *str, size_t str_len, NCDValue *out_value)
 {
     ASSERT(str_len == 0 || str)
-    ASSERT(mem)
     ASSERT(out_value)
     
-    NCDValue value;
-    if (!NCDValueParser_Parse(str, str_len, &value)) {
-        return 0;
+    int res = 0;
+    
+    NCDValMem mem;
+    NCDValMem_Init(&mem);
+    
+    NCDValRef val;
+    if (!NCDValParser_Parse(str, str_len, &mem, &val)) {
+        goto fail;
     }
     
-    if (!NCDValCompat_ValueToVal(&value, mem, out_value)) {
-        BLog(BLOG_ERROR, "NCDValCompat_ValueToVal failed");
-        NCDValue_Free(&value);
-        return 0;
+    if (!NCDValCompat_ValToValue(val, out_value)) {
+        goto fail;
     }
     
-    NCDValue_Free(&value);
-    return 1;
+    res = 1;
+    
+fail:
+    NCDValMem_Free(&mem);
+    return res;
 }

+ 17 - 2
ncd/NCDValueParser.h

@@ -36,8 +36,23 @@
 #include <ncd/NCDValue.h>
 #include <ncd/NCDVal.h>
 
-int NCDValueParser_Parse (const char *str, size_t str_len, NCDValue *out_value) WARN_UNUSED;
-
+/**
+ * Parses an NCD value string into {@link NCDVal} compact representation.
+ * 
+ * @param str pointer to the string to be parsed
+ * @param str_len length of the string in bytes
+ * @param mem value memory object which the result will be stored in
+ * @param out_value on success, the value reference of the result will be
+ *                  written here
+ * @return 1 on success, 0 on failure
+ */
 int NCDValParser_Parse (const char *str, size_t str_len, NCDValMem *mem, NCDValRef *out_value) WARN_UNUSED;
 
+/**
+ * Parses an NCD value string into the old {@link NCDValue} representation.
+ * This is just a wrapper around {@link NCDValParser_Parse} and
+ * {@link NCDValCompat_ValToValue}.
+ */
+int NCDValueParser_Parse (const char *str, size_t str_len, NCDValue *out_value) WARN_UNUSED;
+
 #endif

+ 45 - 60
ncd/NCDValueParser_parse.y

@@ -27,29 +27,31 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+// argument for passing state to parser hooks
 %extra_argument { struct parser_state *parser_out }
 
+// type of structure representing tokens
 %token_type { struct token }
 
+// token destructor frees extra memory allocated for tokens
 %token_destructor { free_token($$); }
 
+// types of nonterminals
 %type list_contents { struct value }
 %type list { struct value }
 %type map_contents { struct value }
 %type map  { struct value }
 %type value  { struct value }
 
-// mention parser_out in some destructor to a void unused variable warning
-%destructor list_contents { (void)parser_out; free_value($$); }
-%destructor list { free_value($$); }
-%destructor map_contents { free_value($$); }
-%destructor map { free_value($$); }
-%destructor value { free_value($$); }
+// mention parser_out in some destructor to and avoid unused variable warning
+%destructor list_contents { (void)parser_out; }
 
+// try to dynamically grow the parse stack
 %stack_size 0
 
+// on syntax error, set the corresponding error flag
 %syntax_error {
-    parser_out->syntax_error = 1;
+    parser_out->error_flags |= ERROR_FLAG_SYNTAX;
 }
 
 // workaroud Lemon bug: if the stack overflows, the token that caused the overflow will be leaked
@@ -60,12 +62,22 @@
 }
 
 input ::= value(A). {
-    if (!A.have || parser_out->have_value) {
-        free_value(A);
-    } else {
-        parser_out->have_value = 1;
-        parser_out->value = A.v;
+    if (!A.have) {
+        goto failZ0;
+    }
+    
+    if (!NCDVal_IsInvalid(parser_out->value)) {
+        // should never happen
+        parser_out->error_flags |= ERROR_FLAG_SYNTAX;
+        goto failZ0;
     }
+    
+    if (!NCDValCons_Complete(&parser_out->cons, A.v, &parser_out->value, &parser_out->cons_error)) {
+        handle_cons_error(parser_out);
+        goto failZ0;
+    }
+    
+failZ0:;
 }
 
 list_contents(R) ::= value(A). {
@@ -73,23 +85,19 @@ list_contents(R) ::= value(A). {
         goto failL0;
     }
 
-    NCDValue_InitList(&R.v);
+    NCDValCons_NewList(&parser_out->cons, &R.v);
 
-    if (!NCDValue_ListPrepend(&R.v, A.v)) {
-        goto failL1;
+    if (!NCDValCons_ListPrepend(&parser_out->cons, &R.v, A.v, &parser_out->cons_error)) {
+        handle_cons_error(parser_out);
+        goto failL0;
     }
-    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);
+doneL:;
 }
 
 list_contents(R) ::= value(A) COMMA list_contents(N). {
@@ -97,27 +105,23 @@ list_contents(R) ::= value(A) COMMA list_contents(N). {
         goto failM0;
     }
 
-    if (!NCDValue_ListPrepend(&N.v, A.v)) {
+    if (!NCDValCons_ListPrepend(&parser_out->cons, &N.v, A.v, &parser_out->cons_error)) {
+        handle_cons_error(parser_out);
         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);
+doneM:;
 }
 
 list(R) ::= CURLY_OPEN CURLY_CLOSE. {
+    NCDValCons_NewList(&parser_out->cons, &R.v);
     R.have = 1;
-    NCDValue_InitList(&R.v);
 }
 
 list(R) ::= CURLY_OPEN list_contents(A) CURLY_CLOSE. {
@@ -129,25 +133,19 @@ map_contents(R) ::= value(A) COLON value(B). {
         goto failS0;
     }
 
-    NCDValue_InitMap(&R.v);
+    NCDValCons_NewMap(&parser_out->cons, &R.v);
 
-    if (!NCDValue_MapInsert(&R.v, A.v, B.v)) {
-        goto failS1;
+    if (!NCDValCons_MapInsert(&parser_out->cons, &R.v, A.v, B.v, &parser_out->cons_error)) {
+        handle_cons_error(parser_out);
+        goto failS0;
     }
-    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);
+doneS:;
 }
 
 map_contents(R) ::= value(A) COLON value(B) COMMA map_contents(N). {
@@ -155,36 +153,23 @@ map_contents(R) ::= value(A) COLON value(B) COMMA map_contents(N). {
         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)) {
+    if (!NCDValCons_MapInsert(&parser_out->cons, &N.v, A.v, B.v, &parser_out->cons_error)) {
+        handle_cons_error(parser_out);
         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);
+doneT:;
 }
 
 map(R) ::= BRACKET_OPEN BRACKET_CLOSE. {
+    NCDValCons_NewList(&parser_out->cons, &R.v);
     R.have = 1;
-    NCDValue_InitMap(&R.v);
 }
 
 map(R) ::= BRACKET_OPEN map_contents(A) BRACKET_CLOSE. {
@@ -194,7 +179,8 @@ map(R) ::= BRACKET_OPEN map_contents(A) BRACKET_CLOSE. {
 value(R) ::= STRING(A). {
     ASSERT(A.str)
 
-    if (!NCDValue_InitStringBin(&R.v, (uint8_t *)A.str, A.len)) {
+    if (!NCDValCons_NewString(&parser_out->cons, (const uint8_t *)A.str, A.len, &R.v, &parser_out->cons_error)) {
+        handle_cons_error(parser_out);
         goto failU0;
     }
 
@@ -203,8 +189,7 @@ value(R) ::= STRING(A). {
 
 failU0:
     R.have = 0;
-    parser_out->out_of_memory = 1;
-doneU:
+doneU:;
     free_token(A);
 }