]> git.ipfire.org Git - pakfire.git/commitdiff
parser: Parse multi-line definitions
authorMichael Tremer <michael.tremer@ipfire.org>
Tue, 23 Feb 2021 20:30:57 +0000 (20:30 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Tue, 23 Feb 2021 20:30:57 +0000 (20:30 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/libpakfire/parser/grammar.y
src/libpakfire/parser/scanner.l
tests/libpakfire/parser.c

index 74dd2da0f85d841ca6314a479d6ecbae9e338569..cd6439ade4b81cedc2e6488a1d133a7792b1b53f 100644 (file)
@@ -59,17 +59,25 @@ enum operator {
 
 %}
 
+%token                                         T_INDENT
+%token                                         T_OUTDENT
+
 %token <string>                                T_KEY
 %token <string>                                T_STRING
 
 %token                                         T_EOL
 
+%token                                         T_END
+
 %token                                         T_ASSIGN
 %token                                         T_APPEND
 
 %type <string>                         key
 %type <string>                         value
 
+%type <string>                         lines
+%type <string>                         line
+
 %union {
        PakfireParser parser;
        char* string;
@@ -97,6 +105,12 @@ declaration                                 : key T_ASSIGN value T_EOL
                                                                if (r)
                                                                        ABORT;
                                                        }
+                                                       | key T_EOL T_INDENT lines T_OUTDENT T_END T_EOL
+                                                       {
+                                                               int r = pakfire_parser_set(parser, $1, $4);
+                                                               if (r)
+                                                                       ABORT;
+                                                       }                                                       
                                                        ;
 
 key                                                    : T_KEY
@@ -113,6 +127,20 @@ value                                              : T_STRING
                                                        }
                                                        ;
 
+lines                                          : lines line
+                                                       {
+                                                               int r = asprintf(&$$, "%s\n%s", $1, $2);
+                                                               if (r < 0)
+                                                                       ABORT;
+                                                       }
+                                                       | line;
+
+line                                           : T_STRING T_EOL
+                                                       {
+                                                               $$ = $1;
+                                                       }
+                                                       ;
+
 %%
 
 int pakfire_parser_parse_data(PakfireParser parent, const char* data, size_t len) {
index 0177ce495a516e3843b3d3dde6a3c337879936b7..5b3c347f2e80bc5b91ebf2354c5a017061a97f5e 100644 (file)
@@ -43,9 +43,18 @@ int num_lines;
 
 #include "grammar.h"
 
+// Indentation level
+unsigned int indent_level = 0;
+
+// Remember current indentation
+unsigned int current_indent = 0;
+unsigned int readline_indent = 0;
+
 %}
 
+%x INDENT
 %x READLINE
+%x READLINES
 
 letter                                 [A-Za-z]
 digit                                  [0-9]
@@ -66,11 +75,30 @@ key                                         ({letter}|{underscore})(({letter}|{digit}|{underscore})*({letter}|{digi
                                                }
 
 \n                                             {
+                                                       // Jump back into indentation processing after a newline
+                                                       current_indent = 0;
+
+                                                       yy_push_state(INDENT);
+
                                                        return T_EOL;
                                                }
 
 {whitespace}                   { /* consume any whitespace */ }
 
+{key}$                                 {
+                                                       // Hack to detect "end"
+                                                       if (strcmp(yytext, "end") == 0)
+                                                               return T_END;
+
+                                                       // Copy the value
+                                                       yylval.string = strdup(yytext);
+
+                                                       // Enter into READLINES mode
+                                                       yy_push_state(READLINES);
+
+                                                       return T_KEY;
+                                               }
+
 {key}                                  {
                                                        // Copy the value
                                                        yylval.string = strdup(yytext);
@@ -111,4 +139,60 @@ key                                                ({letter}|{underscore})(({letter}|{digit}|{underscore})*({letter}|{digi
                                                        return T_STRING;
                                                }
 
+<READLINES>\t                  {
+                                                       if (readline_indent && current_indent >= readline_indent)
+                                                               REJECT;
+
+                                                       current_indent++;
+                                               }
+<READLINES>\n                  {
+                                                       // Jump back into indentation processing after a newline
+                                                       current_indent = 0;
+
+                                                       return T_EOL;
+                                               }
+<READLINES>.                   {
+                                                       unput(*yytext);
+
+                                                       if (!readline_indent) {
+                                                               readline_indent = current_indent;
+                                                               return T_INDENT;
+                                                       }
+
+                                                       // <-- ?
+                                                       if (current_indent < readline_indent) {
+                                                               readline_indent = 0;
+                                                               yy_pop_state();
+                                                               return T_OUTDENT;
+                                                       }
+
+                                                       yy_push_state(READLINE);
+                                               }
+
+<INDENT>\t                             { current_indent++; }
+<INDENT>\n                             { current_indent = 0; }
+<INDENT>.                              {
+                                                       // Put the read character back for the next round
+                                                       unput(*yytext);
+
+                                                       // --> - More indentation?
+                                                       if (current_indent > indent_level) {
+                                                               indent_level++;
+                                                               yy_pop_state();
+
+                                                               return T_INDENT;
+
+                                                       // <-- - If indentation has become less
+                                                       } else if (current_indent < indent_level) {
+                                                               indent_level--;
+                                                               yy_pop_state();
+
+                                                               return T_OUTDENT;
+
+                                                       // If indentation has stayed the same
+                                                       } else {
+                                                               yy_pop_state();
+                                                       }
+                                               }
+
 %%
index 63516073e6d1499b952ed5e2a2d62a75595949db..f40c28dcfe91022ff475963e399a31a5d98f551b 100644 (file)
@@ -106,7 +106,13 @@ static int test_parser(const struct test* t) {
 static int test_parser_assign(const struct test* t) {
        PakfireParser parser = pakfire_parser_create(t->pakfire, NULL, NULL);
 
-       static const char* INPUT = "a = 1\nb = 2\nc = %{a}%{b}\nd = A\nd += BC\n";
+       static const char* INPUT =
+               "a = 1\n"
+               "b = 2\n"
+               "c = %{a}%{b}\n"
+               "d = A\n"
+               "d += BC\n"
+               "lines\n\tline 1\n\tline 2\nend\n";
 
        int r = pakfire_parser_parse(parser, INPUT, strlen(INPUT));
        ASSERT(r == 0);