Flat: Adjust processing of command-line options

Change the way we process command line options to the flat command to
prepare for more flexibility. The usage remains the same.
diff --git a/experiments/flat/src/flat.c b/experiments/flat/src/flat.c
index ee3c2bb..af69d83 100644
--- a/experiments/flat/src/flat.c
+++ b/experiments/flat/src/flat.c
@@ -44,106 +44,131 @@
     return data;
 }
 
-static Grammar *grammarFromFile(const char *filename)
+static Grammar *grammarFromStr(Grammar *flatGrammar, const char *filename, const char *input)
 {
-    char *input = readStringFromFile(filename);
-    if (input == NULL) {
-        perror(filename);
-        exit(1);
-    }
-
-    Grammar *flatGrammar = GrammarNewBuiltin();
     Term *term = parse(flatGrammar,"Grammar",input,0,strlen(input));
     if (term == NULL) {
         fprintf(stderr,"%s: Parse failed\n",filename);
         exit(1);
     }
 
-    Grammar *builtGrammar = grammarFromTerm(term,input);
+    return grammarFromTerm(term,input);
+}
 
-    free(input);
-    GrammarFree(flatGrammar);
+void usage(void)
+{
+    printf("Usage:\n"
+           "\n"
+           "flat -g\n"
+           "\n"
+           "    Print the built-in PEG grammar\n"
+           "\n"
+           "flat -p FILENAME\n"
+           "\n"
+           "    Parse FILENAME using the built-in PEG grammar, and print out the resulting\n"
+           "    parse tree\n"
+           "\n"
+           "flat -b FILENAME\n"
+           "\n"
+           "    Parse FILENAME using the built-in PEG grammar, then use the resulting parse\n"
+           "    tree to build a Grammar object, and print out the constructed grammar.\n"
+           "\n"
+           "flat GRAMMAR INPUT\n"
+           "\n"
+           "    Use the grammar defined in file GRAMMAR to parse the file INPUT, and print\n"
+           "    out the resulting parse tree\n"
+           "\n");
+    exit(1);
+}
 
-    return builtGrammar;
+char *maybeReadFile(const char *filename)
+{
+    if (filename == NULL)
+        return NULL;
+    char *str = readStringFromFile(filename);
+    if (str == NULL) {
+        perror(filename);
+        exit(1);
+    }
+    return str;
 }
 
 int main(int argc, const char **argv)
 {
+    const char *grammarFilename = NULL;
+    const char *inputFilename = NULL;
+    char *grammarStr = NULL;
+    char *inputStr = NULL;
+    int useBuiltinGrammar = 0;
+    int showGrammar = 0;
+    Grammar *builtGrammar = NULL;
+    Term *inputTerm = NULL;
 
     if ((argc == 2) && !strcmp(argv[1],"-g")) {
-        // Build and print out the built-in PEG grammar
-        Grammar *gram = GrammarNewBuiltin();
-        GrammarPrint(gram);
-        GrammarFree(gram);
+        useBuiltinGrammar = 1;
+        showGrammar = 1;
     }
     else if ((argc == 3) && !strcmp(argv[1],"-p")) {
-        const char *filename = argv[2];
-        char *input = readStringFromFile(filename);
-        if (input == NULL) {
-            perror(filename);
-            exit(1);
-        }
-        Grammar *gram = GrammarNewBuiltin();
-        Term *term = parse(gram,"Grammar",input,0,strlen(input));
-        if (term == NULL) {
-            fprintf(stderr,"%s: Parse failed\n",filename);
-            exit(1);
-        }
-        TermPrint(term,input,"");
-        free(input);
-        GrammarFree(gram);
+        useBuiltinGrammar = 1;
+        inputFilename = argv[2];
     }
     else if ((argc == 3) && !strcmp(argv[1],"-b")) {
-        Grammar *built = grammarFromFile(argv[2]);
-        GrammarPrint(built);
-        GrammarFree(built);
+        grammarFilename = argv[2];
+        showGrammar = 1;
     }
     else if (argc == 3) {
-        const char *grammarFilename = argv[1];
-        const char *inputFilename = argv[2];
+        grammarFilename = argv[1];
+        inputFilename = argv[2];
+    }
+    else {
+        usage();
+    }
 
-        char *inputStr = readStringFromFile(inputFilename);
-        if (inputStr == NULL) {
-            perror(inputFilename);
+    Grammar *flatGrammar = GrammarNewBuiltin();
+
+    inputStr = maybeReadFile(inputFilename);
+    grammarStr = maybeReadFile(grammarFilename);
+
+    if (grammarStr != NULL) {
+        builtGrammar = grammarFromStr(flatGrammar,grammarFilename,grammarStr);
+        if (builtGrammar == NULL) {
+            fprintf(stderr,"Cannot build grammar\n");
             exit(1);
         }
+    }
 
-        Grammar *builtGrammar = grammarFromFile(grammarFilename);
-        const char *firstRuleName = GrammarFirstRuleName(builtGrammar);
-        Term *inputTerm = parse(builtGrammar,firstRuleName,inputStr,0,strlen(inputStr));
+    Grammar *useGrammar = NULL;
+
+    if (useBuiltinGrammar)
+        useGrammar = flatGrammar;
+    else if (builtGrammar != NULL)
+        useGrammar = builtGrammar;
+    else
+        usage();
+
+    if (inputStr != NULL) {
+        const char *firstRuleName = GrammarFirstRuleName(useGrammar);
+        inputTerm = parse(useGrammar,firstRuleName,inputStr,0,strlen(inputStr));
         if (inputTerm == NULL) {
             fprintf(stderr,"%s: Parse failed\n",inputFilename);
             exit(1);
         }
-        TermPrint(inputTerm,inputStr,"");
+    }
 
-        free(inputStr);
-        GrammarFree(builtGrammar);
+    if (showGrammar) {
+        GrammarPrint(useGrammar);
+    }
+    else if (inputTerm != NULL) {
+        TermPrint(inputTerm,inputStr,"");
     }
     else {
-        printf("Usage:\n"
-               "\n"
-               "flat -g\n"
-               "\n"
-               "    Print the built-in PEG grammar\n"
-               "\n"
-               "flat -p FILENAME\n"
-               "\n"
-               "    Parse FILENAME using the built-in PEG grammar, and print out the resulting\n"
-               "    parse tree\n"
-               "\n"
-               "flat -b FILENAME\n"
-               "\n"
-               "    Parse FILENAME using the built-in PEG grammar, then use the resulting parse\n"
-               "    tree to build a Grammar object, and print out the constructed grammar.\n"
-               "\n"
-               "flat GRAMMAR INPUT\n"
-               "\n"
-               "    Use the grammar defined in file GRAMMAR to parse the file INPUT, and print\n"
-               "    out the resulting parse tree\n"
-               "\n");
-
-        return 1;
+        usage();
     }
+
+    free(grammarStr);
+    free(inputStr);
+    GrammarFree(flatGrammar);
+    if (builtGrammar != NULL)
+        GrammarFree(builtGrammar);
     return 0;
 }