]> ocean-lang.org Git - ocean/blobdiff - csrc/parsergen.mdc
pargergen: make use of --tag for calc grammar
[ocean] / csrc / parsergen.mdc
index 2cd4f5c84534903f114d696939cb698d125e1cd3..9def0826d5ea57ac6b333f74c5bbe3767f78de59 100644 (file)
@@ -17,7 +17,10 @@ There are several distinct sections.
 5. `parser_run` is a library function intended to be linked together
    with the generated parser tables to complete the implementation of
    a parser.
-6. `calc.cgm` is a test grammar for a simple calculator.
+6. Finally `calc` is a test grammar for a simple calculator.  The
+   `parsergen` program built from the C code in this file can extract
+   that grammar directly from this file and process it.
+
 
 ###### File: parsergen.c
        #include <unistd.h>
@@ -42,12 +45,10 @@ There are several distinct sections.
        ## parser includes
        ## parser functions
        ## parser_run
-###### File: calc.cgm
-       ## demo grammar
 ###### File: parsergen.mk
        CFLAGS += -Wall -g
        all :: parsergen calc
-       parsergen.c parsergen.mk calc.cgm libparser.c parser.h : parsergen.mdc
+       parsergen.c parsergen.mk libparser.c parser.h : parsergen.mdc
                ./md2c parsergen.mdc
 
 ## Reading the grammar
@@ -58,6 +59,12 @@ sections: `header`, `code`, and `grammar`.  The first two will be
 literally copied into the generated `.c` and `.h`. files.  The last
 contains the grammar.  This is tokenised with "[scanner][]".
 
+If the `--tag` option is given, then any top level header that doesn't
+start with the tag is ignored, and the tag is striped from the rest.  So
+`--tag Foo`
+means that the three needed sections must be `Foo: header`, `Foo: code`,
+and `Foo: grammar`.
+
 [mdcode]: mdcode.html
 [scanner]: scanner.html
 
@@ -103,6 +110,9 @@ comparing we define `text_is` and `prtxt`, which should possibly go in
 `mdcode`.  `scanner` does provide `text_dump` which is useful for strings
 which might contain control characters.
 
+`strip_tag` is a bit like `strncmp`, but adds a test for a colon,
+because that is what we need to detect tags.
+
 ###### functions
        static int text_is(struct text t, char *s)
        {
@@ -114,6 +124,20 @@ which might contain control characters.
                printf("%.*s", t.len, t.txt);
        }
 
+       static int strip_tag(struct text *t, char *tag)
+       {
+               int skip = strlen(tag) + 1;
+               if (skip >= t->len ||
+                   strncmp(t->txt, tag, skip-1) != 0 ||
+                   t->txt[skip-1] != ':')
+                       return 0;
+               while (skip < t->len && t->txt[skip] == ' ')
+                       skip++;
+               t->len -= skip;
+               t->txt += skip;
+               return 1;
+       }
+
 ### Symbols
 
 Productions are comprised primarily of symbols - terminal and
@@ -545,7 +569,7 @@ Now we are ready to read in the grammar.
                };
 
                struct token_state *state = token_open(code, &conf);
-               struct token tk = token_next(state);
+               struct token tk;
                struct symbol *head = NULL;
                struct grammar *g;
                char *err = NULL;
@@ -891,7 +915,7 @@ compute it in a repetitive manner similar to `set_nullable`.
 ### Building the `first` sets
 
 When calculating what can follow a particular non-terminal, we will need to
-know what the "first" terminal in an subsequent non-terminal might be.  So
+know what the "first" terminal in any subsequent non-terminal might be.  So
 we calculate the `first` set for every non-terminal and store them in an
 array.  We don't bother recording the "first" set for terminals as they are
 trivial.
@@ -2069,17 +2093,19 @@ grammar file).
                { "SLR",        0, NULL, 'S' },
                { "LALR",       0, NULL, 'L' },
                { "LR1",        0, NULL, '1' },
+               { "tag",        1, NULL, 't' },
                { "report",     0, NULL, 'R' },
                { "output",     1, NULL, 'o' },
                { NULL,         0, NULL, 0   }
        };
-       const char *options = "05SL1Ro:";
+       const char *options = "05SL1t:Ro:";
 
 ###### process arguments
        int opt;
        char *outfile = NULL;
        char *infile;
        char *name;
+       char *tag = NULL;
        int report = 1;
        enum grammar_type type = LR05;
        while ((opt = getopt_long(argc, argv, options,
@@ -2099,6 +2125,8 @@ grammar file).
                        report = 2; break;
                case 'o':
                        outfile = optarg; break;
+               case 't':
+                       tag = optarg; break;
                default:
                        fprintf(stderr, "Usage: parsergen ...\n");
                        exit(1);
@@ -2164,11 +2192,14 @@ parser with neither. "grammar" must be provided.
        struct code_node *code = NULL;
        struct code_node *gram = NULL;
        for (s = table; s; s = s->next) {
-               if (text_is(s->section, "header"))
+               struct text sec = s->section;
+               if (tag && !strip_tag(&sec, tag))
+                       continue;
+               if (text_is(sec, "header"))
                        hdr = s->code;
-               else if (text_is(s->section, "code"))
+               else if (text_is(sec, "code"))
                        code = s->code;
-               else if (text_is(s->section, "grammar"))
+               else if (text_is(sec, "grammar"))
                        gram = s->code;
                else {
                        fprintf(stderr, "Unknown content section: %.*s\n",
@@ -2347,6 +2378,7 @@ pushing it onto the stack.
                        short sym;
                        short starts_indented;
                        short indents;
+                       short newline_permitted;
                } *stack, next;
                void **asn_stack;
                int stack_size;
@@ -2369,7 +2401,7 @@ that we shift when we can, and reduce when we cannot.  So the `shift`
 function reports if it could.
 
 So `shift` finds the next state.  If that succeed it extends the allocations
-if needed and pushed all the information onto the stacks.
+if needed and pushes all the information onto the stacks.
 
 ###### parser functions
 
@@ -2394,6 +2426,9 @@ if needed and pushed all the information onto the stacks.
                p->next.state = newstate;
                p->next.indents = 0;
                p->next.starts_indented = 0;
+               // if new state doesn't start a line, we inherit newline_permitted status
+               if (states[newstate].starts_line)
+                       p->next.newline_permitted = 1;
                return 1;
        }
 
@@ -2417,6 +2452,9 @@ reduce a production, just before we `shift` the nonterminal in.
                if (num) {
                        p->next.state = p->stack[p->tos].state;
                        p->next.starts_indented = p->stack[p->tos].starts_indented;
+                       p->next.newline_permitted = p->stack[p->tos].newline_permitted;
+                       if (p->next.indents > p->next.starts_indented)
+                               p->next.newline_permitted = 0;
                }
        }
 
@@ -2487,6 +2525,7 @@ at the start of a production.
                int accepted = 0;
                void *ret;
 
+               p.next.newline_permitted = states[0].starts_line;
                while (!accepted) {
                        struct token *err_tk;
                        if (!tk)
@@ -2507,6 +2546,13 @@ at the start of a production.
                                    (p.stack[p.tos-1].indents == 1 &&
                                     states[p.next.state].reduce_size > 1)) {
                                        p.stack[p.tos-1].indents -= 1;
+                                       if (p.stack[p.tos-1].indents == p.stack[p.tos-1].starts_indented) {
+                                               // no internal indent any more, reassess 'newline_permitted'
+                                               if (states[p.stack[p.tos-1].state].starts_line)
+                                                       p.stack[p.tos-1].newline_permitted = 1;
+                                               else if (p.tos > 1)
+                                                       p.stack[p.tos-1].newline_permitted = p.stack[p.tos-2].newline_permitted;
+                                       }
                                        free(tk);
                                        tk = NULL;
                                        continue;
@@ -2514,6 +2560,13 @@ at the start of a production.
                                // fall through and force a REDUCE (as 'shift'
                                // will fail).
                        }
+                       if (p.next.sym == TK_newline) {
+                               if (!p.tos || ! p.stack[p.tos-1].newline_permitted) {
+                                       free(tk);
+                                       tk = NULL;
+                                       continue;
+                               }
+                       }
                        if (shift(&p, tk, states)) {
                                tk = NULL;
                                continue;
@@ -2623,6 +2676,19 @@ end inside square brackets.
                [TK_newline]      = "NEWLINE",
                [TK_eof]          = "$eof",
        };
+       static void parser_trace_state(FILE *trace, struct frame *f, const struct state states[])
+       {
+               fprintf(trace, "(%d", f->state);
+               if (f->indents)
+                       fprintf(trace, "%c%d", f->starts_indented?':':'.',
+                               f->indents);
+               if (states[f->state].starts_line)
+                       fprintf(trace, "s");
+               if (f->newline_permitted)
+                       fprintf(trace, "n");
+               fprintf(trace, ") ");
+       }
+
        void parser_trace(FILE *trace, struct parser *p,
                          struct token *tk, const struct state states[],
                          const char *non_term[], int knowns)
@@ -2630,7 +2696,7 @@ end inside square brackets.
                int i;
                for (i = 0; i < p->tos; i++) {
                        int sym = p->stack[i].sym;
-                       fprintf(trace, "(%d) ", p->stack[i].state);
+                       parser_trace_state(trace, &p->stack[i], states);
                        if (sym < TK_reserved &&
                            reserved_words[sym] != NULL)
                                fputs(reserved_words[sym], trace);
@@ -2642,7 +2708,8 @@ end inside square brackets.
                                      trace);
                        fputs(" ", trace);
                }
-               fprintf(trace, "(%d) [", p->next.state);
+               parser_trace_state(trace, &p->next, states);
+               fprintf(trace, " [");
                if (tk->num < TK_reserved &&
                    reserved_words[tk->num] != NULL)
                        fputs(reserved_words[tk->num], trace);
@@ -2667,15 +2734,12 @@ better approach, but as the grammar file must have 3 components I need
 something like this.
 
 ###### File: parsergen.mk
-       calc.c : parsergen calc.cgm
-               ./parsergen -o calc calc.cgm
+       calc.c calc.h : parsergen parsergen.mdc
+               ./parsergen --tag calc -o calc parsergen.mdc
        calc : calc.o libparser.o libscanner.o libmdcode.o libnumber.o
                $(CC) $(CFLAGS) -o calc calc.o libparser.o libscanner.o libmdcode.o libnumber.o -licuuc -lgmp
 
-###### demo grammar
-
-       # header
-       ~~~~~
+# calc: header
 
        #include "number.h"
        // what do we use for a demo-grammar?  A calculator of course.
@@ -2685,9 +2749,7 @@ something like this.
                int err;
        };
 
-       ~~~~~
-       # code
-       ~~~~~
+# calc: code
 
        #include <stdlib.h>
        #include <unistd.h>
@@ -2728,9 +2790,7 @@ something like this.
                exit(0);
        }
 
-       ~~~~~
-       # grammar
-       ~~~~~
+# calc: grammar
 
        Session -> Session Line
                | Line