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;