Flat: Support parsing using custom grammars

Add the ability to use the flat command-line tool to specify both a
grammar file and an input which should be parsed using that grammar.
After a successful parse, the parse tree is printed to standard output.

This is what makes Flat a parser *interpreter*, rather than a parser
*generator*. There is no need for the an intermediate step of compiling
generated files as with many other parsing tools like Bison/Flex, ANTLR,
JavaCC, or Stratego/XT. We can (and probably will) choose to add code
generation as an optional feature later on, but it's not a fundamental
requirement.
diff --git a/experiments/flat/grammars/arithmetic.flat b/experiments/flat/grammars/arithmetic.flat
new file mode 100644
index 0000000..46e2e5d
--- /dev/null
+++ b/experiments/flat/grammars/arithmetic.flat
@@ -0,0 +1,22 @@
+# Note: The Term and Factor rules are right recursive. They are actually
+# supposed to be left recursive, but we don't support that yet.
+Start   : Expr !.;
+Expr    : Term;
+Term    : Factor PLUS Term
+        | Factor MINUS Term
+        | Factor;
+Factor  : Primary TIMES Factor
+        | Primary SLASH Factor
+        | Primary;
+Primary : ID
+        | INT
+        | OPEN Expr CLOSE;
+PLUS    : "+" Spacing;
+MINUS   : "-" Spacing;
+TIMES   : "*" Spacing;
+SLASH   : "/" Spacing;
+OPEN    : "(" Spacing;
+CLOSE   : ")" Spacing;
+ID      : $([a-zA-Z_] [a-zA-Z_0-9]*) Spacing;
+INT     : $([1-9] [0-9]*) Spacing;
+Spacing : $((" " | "\t" | "\r" | "\n")*);
diff --git a/experiments/flat/src/BuildGrammar.c b/experiments/flat/src/BuildGrammar.c
index 7425878..ea546e3 100644
--- a/experiments/flat/src/BuildGrammar.c
+++ b/experiments/flat/src/BuildGrammar.c
@@ -474,6 +474,8 @@
         GrammarDefine(builder->gram,ruleName,ruleExpr);
         free(ruleName);
     }
+
+    GrammarResolve(builder->gram);
 }
 
 // This function creates a new Grammar object from the result of parsing a file usin the built-in
diff --git a/experiments/flat/src/Grammar.c b/experiments/flat/src/Grammar.c
index f015dfb..6743953 100644
--- a/experiments/flat/src/Grammar.c
+++ b/experiments/flat/src/Grammar.c
@@ -131,3 +131,11 @@
 
     free(prefix);
 }
+
+const char *GrammarFirstRuleName(Grammar *gram)
+{
+    if (gram->defList == NULL)
+        return NULL;
+    else
+        return gram->defList->name;
+}
diff --git a/experiments/flat/src/Grammar.h b/experiments/flat/src/Grammar.h
index e94e51b..f51bd18 100644
--- a/experiments/flat/src/Grammar.h
+++ b/experiments/flat/src/Grammar.h
@@ -28,3 +28,4 @@
 Expression *GrammarLookup(Grammar *gram, const char *name);
 void GrammarResolve(Grammar *gram);
 void GrammarPrint(Grammar *gram);
+const char *GrammarFirstRuleName(Grammar *gram);
diff --git a/experiments/flat/src/flat.c b/experiments/flat/src/flat.c
index 8aa4f74..ee3c2bb 100644
--- a/experiments/flat/src/flat.c
+++ b/experiments/flat/src/flat.c
@@ -44,6 +44,29 @@
     return data;
 }
 
+static Grammar *grammarFromFile(const char *filename)
+{
+    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);
+
+    free(input);
+    GrammarFree(flatGrammar);
+
+    return builtGrammar;
+}
+
 int main(int argc, const char **argv)
 {
 
@@ -71,28 +94,32 @@
         GrammarFree(gram);
     }
     else if ((argc == 3) && !strcmp(argv[1],"-b")) {
-        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);
-        }
-
-
-        Grammar *built = grammarFromTerm(term,input);
+        Grammar *built = grammarFromFile(argv[2]);
         GrammarPrint(built);
-
-        free(input);
-        GrammarFree(gram);
         GrammarFree(built);
     }
+    else if (argc == 3) {
+        const char *grammarFilename = argv[1];
+        const char *inputFilename = argv[2];
+
+        char *inputStr = readStringFromFile(inputFilename);
+        if (inputStr == NULL) {
+            perror(inputFilename);
+            exit(1);
+        }
+
+        Grammar *builtGrammar = grammarFromFile(grammarFilename);
+        const char *firstRuleName = GrammarFirstRuleName(builtGrammar);
+        Term *inputTerm = parse(builtGrammar,firstRuleName,inputStr,0,strlen(inputStr));
+        if (inputTerm == NULL) {
+            fprintf(stderr,"%s: Parse failed\n",inputFilename);
+            exit(1);
+        }
+        TermPrint(inputTerm,inputStr,"");
+
+        free(inputStr);
+        GrammarFree(builtGrammar);
+    }
     else {
         printf("Usage:\n"
                "\n"
@@ -109,6 +136,11 @@
                "\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;