From d8e43ed3a172f628656d136fe8986ef5e119100c Mon Sep 17 00:00:00 2001 From: mingodad Date: Tue, 14 Dec 2021 15:10:11 +0100 Subject: [PATCH] Add an initial naked grammar dump to use at https://www.bottlecaps.de/convert/ and https://www.bottlecaps.de/rr/ui to have a railroad diagram with a command like: ../cli parse -n -G ../tests/peppa.peg -e grammar abnf.peg --- peppa.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ peppa.h | 7 ++-- shell.c | 10 ++++- 3 files changed, 127 insertions(+), 4 deletions(-) diff --git a/peppa.c b/peppa.c index 353c66e..4859aac 100644 --- a/peppa.c +++ b/peppa.c @@ -2728,6 +2728,120 @@ P4_TxtSourceAst(FILE* stream, P4_Node* node, int depth) { } } +P4_PUBLIC void +P4_NakedSourceAst(FILE* stream, P4_Node* node, int depth, const char *sep) { + P4_size_t child_count, sep_count = 0; + P4_ConstString rule_name; + P4_Node *tmp_node = node; + while (tmp_node) { + P4_String node_string = P4_CopyNodeString(tmp_node); + rule_name = tmp_node->rule_name; + if(strcmp(rule_name, "choice") == 0) { + fprintf(stream, "%s", (sep_count++ ? sep : "")); + child_count = P4_GetNodeChildrenCount(tmp_node); + if(depth && child_count > 1) fprintf(stream, " ( "); + P4_NakedSourceAst(stream, tmp_node->head, depth+1, " / "); + if(depth && child_count > 1) fprintf(stream, " )"); + } + else if(strcmp(rule_name, "cut") == 0) { + fprintf(stream, " /*%s*/", node_string); + } + else if(strcmp(rule_name, "decorator") == 0) { + fprintf(stream, "/*%s*/ ", node_string); + } + else if(strcmp(rule_name, "decorators") == 0) { + P4_NakedSourceAst(stream, tmp_node->head, 0, " "); + } + else if(strcmp(rule_name, "dot") == 0) { + fprintf(stream, "."); + } + else if(strcmp(rule_name, "grammar") == 0) { + P4_NakedSourceAst(stream, tmp_node->head, 0, " "); + } + else if(strcmp(rule_name, "back_reference") == 0 + || strcmp(rule_name, "insensitive") == 0 + || strcmp(rule_name, "char") == 0 + || strcmp(rule_name, "reference") == 0 + || strcmp(rule_name, "number") == 0 + || strcmp(rule_name, "literal") == 0) { + fprintf(stream, "%s%s", (sep_count++ ? sep : ""), node_string); + } + else if(strcmp(rule_name, "left_recursion") == 0) { + P4_FREE(node_string); + node_string = P4_CopyNodeString(tmp_node->head); + fprintf(stream, "%s%s /*|*/ / ", (sep_count++ ? sep : ""), node_string); + P4_NakedSourceAst(stream, tmp_node->head->next, depth+1, " "); + } + else if(strcmp(rule_name, "name") == 0) { + fprintf(stream, "%s =\n\t", node_string); + sep_count = 0; + } + else if(strcmp(rule_name, "negative") == 0) { + fprintf(stream, "%s!", (sep_count++ ? sep : "")); + P4_NakedSourceAst(stream, tmp_node->head, depth+1, sep); + } + else if(strcmp(rule_name, "positive") == 0) { + fprintf(stream, "%s&", (sep_count++ ? sep : "")); + P4_NakedSourceAst(stream, tmp_node->head, depth+1, sep); + } + else if(strcmp(rule_name, "range") == 0) { + fprintf(stream, "%s[", (sep_count++ ? sep : "")); + P4_NakedSourceAst(stream, tmp_node->head, depth+1, "-"); + fprintf(stream, "]"); + } + else if(strcmp(rule_name, "range_category") == 0) { + fprintf(stream, "\\p{%s}", node_string); + } + else if(strcmp(rule_name, "repeat") == 0) { + fprintf(stream, "%s", (sep_count++ ? sep : "")); + P4_NakedSourceAst(stream, tmp_node->head, depth+1, " "); + } + else if(strcmp(rule_name, "rule") == 0) { + fprintf(stream, "\n"); + P4_NakedSourceAst(stream, tmp_node->head, depth+1, " "); + fprintf(stream, " ;"); + } + else if(strcmp(rule_name, "sequence") == 0) { + fprintf(stream, "%s", (sep_count++ ? sep : "")); + child_count = P4_GetNodeChildrenCount(tmp_node); + if(depth && child_count > 1) fprintf(stream, " ( "); + P4_NakedSourceAst(stream, tmp_node->head, depth+1, " "); + if(depth && child_count > 1) fprintf(stream, " )"); + } + else if(strcmp(rule_name, "onceormore") == 0) { fprintf(stream, "+ "); } + else if(strcmp(rule_name, "zeroormore") == 0) { fprintf(stream, "* "); } + else if(strcmp(rule_name, "zerooronce") == 0) { fprintf(stream, "? "); } + else if(strcmp(rule_name, "repeatexact") == 0) { + fprintf(stream, "{"); + P4_NakedSourceAst(stream, tmp_node->head, depth+1, " "); + fprintf(stream, "} "); + } + else if(strcmp(rule_name, "repeatmin") == 0) { + fprintf(stream, "{"); + P4_NakedSourceAst(stream, tmp_node->head, depth+1, " "); + fprintf(stream, ",} "); + } + else if(strcmp(rule_name, "repeatmax") == 0) { + fprintf(stream, "{,"); + P4_NakedSourceAst(stream, tmp_node->head, depth+1, " "); + fprintf(stream, "} "); + } + else if(strcmp(rule_name, "repeatminmax") == 0) { + fprintf(stream, "{"); + P4_NakedSourceAst(stream, tmp_node->head, depth+1, ","); + fprintf(stream, "} "); + } + else { + P4_FREE(node_string); + fprintf(stream, "\n\n***Unknown rule name: %s\n", rule_name); + return; + } + tmp_node = tmp_node->next; + P4_FREE(node_string); + } + if(depth == 0) + fprintf(stream, "\n"); +} P4_PUBLIC P4_Error P4_InspectSourceAst(P4_Node* node, void* userdata, P4_Error (*inspector)(P4_Node*, void*)) { diff --git a/peppa.h b/peppa.h index 0dc4e8a..7bcb1ef 100644 --- a/peppa.h +++ b/peppa.h @@ -1578,9 +1578,10 @@ P4_size_t P4_GetSourcePosition(P4_Source* source); * P4_Node* root = P4_GetSourceAst(source); * P4_JsonifySourceAst(stdout, root); */ -void P4_JsonifySourceAst(FILE* stream, P4_Node* node, P4_Formatter formatter); -void P4_Jsonify2SourceAst(FILE* stream, P4_Node* node, P4_Formatter formatter); -void P4_TxtSourceAst(FILE* stream, P4_Node* node, int depth); +void P4_JsonifySourceAst(FILE* stream, P4_Node* node, P4_Formatter formatter); +void P4_Jsonify2SourceAst(FILE* stream, P4_Node* node, P4_Formatter formatter); +void P4_TxtSourceAst(FILE* stream, P4_Node* node, int depth); +void P4_NakedSourceAst(FILE* stream, P4_Node* node, int depth, const char *sep); /** * @brief Inspect the node tree. diff --git a/shell.c b/shell.c index 3a76fa4..7a5660b 100644 --- a/shell.c +++ b/shell.c @@ -58,6 +58,7 @@ struct p4_args_t { bool json; bool json2; bool text; + bool naked; bool debug; }; @@ -78,6 +79,7 @@ static int subcommand_usage(const char* name) { #endif " --json/-j\t\tjson ast output\n" " --json2/-J\t\tjson ast output with only arrays\n" + " --naked/-n\t\tdump naked grammar\n" " --text/-t\t\ttext ast output [default]\n" " --quiet/-q\t\tno ast output\n" "\n" @@ -130,6 +132,8 @@ static int print_ast( P4_Jsonify2SourceAst(stdout, root, 0); else if(args->json) P4_JsonifySourceAst(stdout, root, 0); + else if(args->naked) + P4_NakedSourceAst(stdout, root, 0, " "); else P4_TxtSourceAst(stdout, root, 0); fprintf(stdout, "\n"); @@ -152,6 +156,7 @@ int init_args(p4_args_t* args, int argc, char* argv[]) { {"quiet", no_argument, 0, 'q'}, {"json", no_argument, 0, 'j'}, {"json2", no_argument, 0, 'J'}, + {"naked", no_argument, 0, 'n'}, {"text", no_argument, 0, 't'}, {"debug", no_argument, 0, 'd'}, {"grammar-entry", required_argument, 0, 'e'}, @@ -160,7 +165,7 @@ int init_args(p4_args_t* args, int argc, char* argv[]) { {0, 0, 0, 0} }; int option_index = 0; - c = getopt_long (argc, argv, "VhqjJtde:g:G:", long_options, &option_index); + c = getopt_long (argc, argv, "VhqjJntde:g:G:", long_options, &option_index); if (c == -1) break; switch (c) { case 0: @@ -183,6 +188,9 @@ int init_args(p4_args_t* args, int argc, char* argv[]) { case 'J': args->json2 = true; break; + case 'n': + args->naked = true; + break; case 't': args->text = true; break;