From e716ece6a93f1a92df75995a705e31f53ac39082 Mon Sep 17 00:00:00 2001
From: anon <anon@anon.anon>
Date: Mon, 16 Sep 2024 16:53:48 +0200
Subject: [PATCH] fixed tests and memory management; added multi selector rule
 support

---
 Makefile                     |  3 +-
 source/main.c                | 22 ++++------
 source/rule.h                | 28 +++++++++++++
 source/tbsp.l                |  3 +-
 source/tbsp.y                | 78 ++++++++++++++++++++++--------------
 test/CMDTEST_tbsp.rb         | 48 +++++++++++++++++-----
 test/convert.tbsp            | 50 ++++++++++-------------
 test/double_selector.tbsp    | 36 +++++++++++++++++
 test/file2str.h              | 15 +++++++
 test/function_collector.tbsp | 21 +++++-----
 10 files changed, 206 insertions(+), 98 deletions(-)
 create mode 100644 source/rule.h
 create mode 100644 test/double_selector.tbsp
 create mode 100644 test/file2str.h

diff --git a/Makefile b/Makefile
index c48bf58..9324228 100644
--- a/Makefile
+++ b/Makefile
@@ -29,7 +29,7 @@ else
 endif
 
 CFLAGS   += -std=c2x -Wall -Wpedantic
-CPPFLAGS += -Iobject -Ilibrary
+CPPFLAGS += -Isource -Iobject -Ilibrary
 
 # --- Rule Section ---
 ${OUT}: ${GENSOURCE} ${GENOBJECT} ${OBJECT} ${LIBS}
@@ -60,3 +60,4 @@ clean:
 	-rm ${GENSOURCE}
 	-rm ${OBJECT}
 	-rm ${OUT}
+	-rm test/*.tb.*
diff --git a/source/main.c b/source/main.c
index 2994d5f..6356666 100644
--- a/source/main.c
+++ b/source/main.c
@@ -23,10 +23,6 @@ extern int tbsp_yy_deinit(void);
 extern int tbsp_c_yy_init(void);
 extern int tbsp_c_yy_deinit(void);
 
-char * language = NULL;
-char * verbatim = NULL;
-char * top = NULL;
-
 void yyerror(const char * const fmt, ...) {
     extern int yylineno;
     va_list args;
@@ -52,7 +48,7 @@ void dump_rule_table(const char * const name, rule_type_t type_mask) {
         sprint_r = asprintf(&sprint_buffer,
                                 TBSP_case,
                                 kv_A(rules, i).string,
-                                kv_A(rules, i).target
+                                kv_A(codes, kv_A(rules, i).code_index).number
         );
         fputs(sprint_buffer, yyout);
         free(sprint_buffer);
@@ -82,7 +78,7 @@ void dump_output(void) {
     dump_rule_table("leave_cases", LEAVE_RULE);
 
     fputs(TBSP_traverse_top, yyout);
-    for (int i = 0; i < kv_size(rules); i++) {
+    for (int i = 0; i < kv_size(codes); i++) {
         const char * const case_string = "\
             case %d: {\n\
                 %s\n\
@@ -90,8 +86,8 @@ void dump_output(void) {
         ";
         sprint_r = asprintf(&sprint_buffer,
                                 case_string,
-                                kv_A(rules, i).target,
-                                kv_A(rules, i).code
+                                kv_A(codes, i).number,
+                                kv_A(codes, i).code
         );
         fputs(sprint_buffer, yyout);
         free(sprint_buffer);
@@ -111,17 +107,13 @@ void init(void) {
 
 static inline
 void deinit(void) {
-    for (int i = 0; i < kv_size(rules); i++) {
-        free(kv_A(rules, i).string);
-        free(kv_A(rules, i).code);
-    }
+    fclose(yyin);
+    fclose(yyout);
+    tbsp_tab_deinit();
     tbsp_yy_deinit();
     tbsp_c_yy_deinit();
     free(output_file_name);
     free(input_file_name);
-    free(verbatim);
-    free(language);
-    free(top);
 }
 
 signed main(const int argc, const char * const * const argv) {
diff --git a/source/rule.h b/source/rule.h
new file mode 100644
index 0000000..59e2e53
--- /dev/null
+++ b/source/rule.h
@@ -0,0 +1,28 @@
+#ifndef RULE_H
+#define RULE_H
+
+#include <kvec.h>
+
+typedef enum {
+    ENTER_RULE = 0b0001,
+    LEAVE_RULE = 0b0010,
+} rule_type_t;
+
+typedef struct {
+    int number;
+    char * code;
+} code_t;
+
+typedef struct {
+    rule_type_t type;
+    char * string;
+    int code_index;
+} rule_t;
+
+typedef kvec_t(rule_t) rule_vector_t;
+extern rule_vector_t rules;
+
+typedef kvec_t(code_t) code_vector_t;
+extern code_vector_t codes;
+
+#endif
diff --git a/source/tbsp.l b/source/tbsp.l
index 7b87d34..220d41a 100644
--- a/source/tbsp.l
+++ b/source/tbsp.l
@@ -40,7 +40,7 @@ identifier [a-zA-z][-a-zA-z0-9_]*
                             return SEPARATOR;
                         }
 .                       {
-                            yyerror("unknown shit");
+                            yyerror("unknown expression in rule section");
                         }
 }
 
@@ -86,7 +86,6 @@ leave[[:space:]]    { return LEAVE; }
                     }
 
                     --code_nesting;
-                    ;
                 }
 .|\n            { buffer = sdscat(buffer, yytext); }
 }
diff --git a/source/tbsp.y b/source/tbsp.y
index 23534c3..f8a7874 100644
--- a/source/tbsp.y
+++ b/source/tbsp.y
@@ -1,36 +1,30 @@
 %{
     #include "tbsp.yy.h"
-
-    extern char * language;
-    extern char * top;
-    extern char * verbatim;
+    #include <kvec.h>
 
     int target_counter = 1;
 
+    kvec_t(char *) rule_selectors;
+
+    char * language = NULL;
+    char * verbatim = NULL;
+    char * top = NULL;
+
     #define COMA ,
 %}
 %code requires {
-    typedef enum {
-        ENTER_RULE = 0b0001,
-        LEAVE_RULE = 0b0010,
-    } rule_type_t;
-
-    typedef struct {
-        rule_type_t type;
-        int target;
-        char * string;
-        char * code;
-    } rule_t;
-
-    #include <kvec.h>
-    typedef kvec_t(rule_t) rule_vector_t;
-    extern rule_vector_t rules;
-
+    #include "rule.h"
     extern void yyerror(const char * const s, ...);
 }
 %code provides {
     void tbsp_tab_init(void);
     void tbsp_tab_deinit(void);
+
+    extern rule_vector_t rules;
+
+    extern char * language;
+    extern char * top;
+    extern char * verbatim;
 }
 %union{
     char * strval;
@@ -41,7 +35,6 @@
 %token ENTER LEAVE
 %token<strval> IDENTIFIER CODE_BLOB
 %type<ruleval> rule_type
-%type<strval> rule_selector
 %%
 document
     : %empty
@@ -91,16 +84,22 @@ rule
 
         char * code_blob_expanded = strdup(tbsp_c_expland_code($3));
 
-        kv_push(rule_t, rules, (rule_t) {
-            .type   = $1 COMA
-            .target = target_counter COMA
-            .string = $2 COMA
-            .code   = code_blob_expanded COMA
+        kv_push(code_t, codes, (code_t) {
+            .number = target_counter++ COMA
+            .code = code_blob_expanded COMA
         });
 
-        ++target_counter;
+        for (int i = 0; i < kv_size(rule_selectors); i++) {
+            kv_push(rule_t, rules, (rule_t) {
+                .type       = $1 COMA
+                .string     = kv_A(rule_selectors, i) COMA
+                .code_index = codes.n-1 COMA
+            });
+        }
 
+        rule_selectors.n = 0;
         tbsp_c_yy_reset();
+        free($3);
     }
     ;
 
@@ -111,7 +110,12 @@ rule_type
     ;
 
 rule_selector
-    : IDENTIFIER { $$ = $1; }
+    : IDENTIFIER { 
+        kv_push(char *, rule_selectors, $1);
+    }
+    | IDENTIFIER rule_selector {
+        kv_push(char *, rule_selectors, $1);
+    }
     ;
 
 
@@ -123,16 +127,32 @@ code_section
 %%
 
 rule_vector_t rules;
+code_vector_t codes;
 
 void tbsp_tab_init(void) {
     kv_init(rules);
+    kv_init(codes);
+    kv_init(rule_selectors);
 }
 
 void tbsp_tab_deinit(void) {
+    for (int i = 0; i < kv_size(rule_selectors); i++) {
+        free(kv_A(rule_selectors, i));
+    }
+
+    for (int i = 0; i < kv_size(codes); i++) {
+        free(kv_A(codes, i).code);
+    }
+
     for (int i = 0; i < kv_size(rules); i++) {
         free(kv_A(rules, i).string);
-        free(kv_A(rules, i).code);
     }
 
     kv_destroy(rules);
+    kv_destroy(codes);
+    kv_destroy(rule_selectors);
+
+    free(verbatim);
+    free(language);
+    free(top);
 }
diff --git a/test/CMDTEST_tbsp.rb b/test/CMDTEST_tbsp.rb
index bce3190..192b32b 100644
--- a/test/CMDTEST_tbsp.rb
+++ b/test/CMDTEST_tbsp.rb
@@ -1,25 +1,53 @@
 class CMDTEST_master_batch < Cmdtest::Testcase
+  def setup
+    import_file "test/file2str.h", "./"
+  end
+
   def test_converter
-    import_file "test/convert.tbsp", "./"
+    source = "convert"
+
+    import_file "test/#{source}.tbsp", "./"
     import_file "test/input.md", "./"
 
-    cmd "tbsp -o convert.tb.c convert.tbsp" do
-      created_files ["convert.tb.c"]
+    cmd "tbsp -o #{source}.tb.c #{source}.tbsp" do
+      created_files ["#{source}.tb.c"]
     end
-    shell "bake convert.tb.c"
-    cmd "./convert.tb.out input.md" do
+    cmd "gcc -w -o #{source}.out #{source}.tb.c $(pkg-config --cflags --libs tree-sitter) -ltree-sitter-markdown" do
+      created_files ["#{source}.out"]
+    end
+    cmd "./#{source}.out input.md" do
       stdout_equal /.+/
     end
   end
 
   def test_function_collector
-    import_file "test/function_collector.tbsp", "./"
+    source = "function_collector"
 
-    cmd "tbsp -o function_collector.tb.cpp function_collector.tbsp" do
-      created_files ["function_collector.tb.cpp"]
+    import_file "test/#{source}.tbsp", "./"
+
+    cmd "tbsp -o #{source}.tb.cpp #{source}.tbsp" do
+      created_files ["#{source}.tb.cpp"]
     end
-    shell "bake function_collector.tb.cpp"
-    cmd "./function_collector.tb.out function_collector.tb.cpp" do
+    cmd "g++ -w -o #{source}.out #{source}.tb.cpp $(pkg-config --cflags --libs tree-sitter tree-sitter-cpp)" do
+      created_files ["#{source}.out"]
+    end
+    cmd "./#{source}.out #{source}.tb.cpp" do
+      stdout_equal /.+/
+    end
+  end
+
+  def test_double_selector
+    source = "double_selector"
+
+    import_file "test/#{source}.tbsp", "./"
+
+    cmd "tbsp #{source}.tbsp" do
+      created_files ["#{source}.tb.c"]
+    end
+    cmd "g++ -w -o #{source}.out #{source}.tb.c $(pkg-config --cflags --libs tree-sitter tree-sitter-c)" do
+      created_files ["#{source}.out"]
+    end
+    cmd "./#{source}.out #{source}.tb.c" do
       stdout_equal /.+/
     end
   end
diff --git a/test/convert.tbsp b/test/convert.tbsp
index 8697b69..b0706af 100644
--- a/test/convert.tbsp
+++ b/test/convert.tbsp
@@ -14,6 +14,7 @@ leave section {
 }
 
 enter atx_heading {
+    puts("AAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
     printf("<h%d>\n", depth);
 }
 leave atx_heading {
@@ -21,66 +22,57 @@ leave atx_heading {
 }
 
 enter paragraph {
-    printf("<p>");
+    puts("<p>");
 }
 leave paragraph {
-    printf("</p>\n");
+    puts("</p>");
 }
 
 enter list {
-    printf("<ol>");
+    puts("<ol>");
 }
 leave list {
-    printf("</ol>\n");
+    puts("</ol>");
 }
 
 enter list_item {
-    printf("<li>");
+    puts("<li>");
 }
 leave list_item {
-    printf("</li>\n");
+    puts("</li>");
 }
 
 enter fenced_code_block {
-    printf("<pre>");
+    puts("<pre>");
 }
 leave fenced_code_block {
-    printf("</pre>\n");
+    puts("</pre>");
 }
 
 enter inline {
     char * text = tbget_text;
-    printf(text);
+    puts(text);
     free(text);
 }
 enter code_fence_content {
     char * text = tbget_text;
-    printf(text);
+    puts(text);
     free(text);
 }
 
 %%
-// @BAKE g++ -o $*.out $@ $(pkg-config --cflags --libs tree-sitter) -ltree-sitter-markdown -ggdb
+/* @BAKE
+    tbsp $@
+    gcc -o $*.out $*.tb.c $(pkg-config --cflags --libs tree-sitter) -ltree-sitter-markdown -ggdb
+    ./$*.out input.md
+   @STOP
+*/
+
+#include "file2str.h"
 
 signed main(int argc, char * * argv) {
-    if (argc < 2) {
-        return 1;
-    }
-
-	FILE* f = fopen(argv[1], "r");
-
-    if (!f) {
-        return 2;
-    }
-
-	fseek(f, 0, SEEK_END);
-	int flen = ftell(f);
-	rewind(f);
-	char fstr[flen+1];
-	fstr[flen] = '\00';
-	fread(fstr, flen, sizeof(char), f);
-
-	fclose(f);
+    if (argc < 2) { return 1; }
+    FILE2STR(fstr, argv[1]);
 
 	printf("-- meta: %d chars\n", flen);
 
diff --git a/test/double_selector.tbsp b/test/double_selector.tbsp
new file mode 100644
index 0000000..9509ba7
--- /dev/null
+++ b/test/double_selector.tbsp
@@ -0,0 +1,36 @@
+%language c
+%%
+
+enter function_definition declaration {
+    TSNode declarator = $$->"declarator";
+    TSNode next_declarator = declarator;
+    while (next_declarator = tbnode_child_by_field_name(next_declarator, "declarator"),
+            !ts_node_is_null(next_declarator)) {
+        declarator = next_declarator;
+    }
+
+    char * s = tbget_node_text(declarator);
+    puts(s);
+    free(s);
+}
+
+%%
+/* @BAKE
+    tbsp $@
+    gcc -o $*.out $*.tb.c $(pkg-config --cflags --libs tree-sitter tree-sitter-c) -ggdb
+    ./$*.out $.tb.c
+   @STOP
+*/
+
+#include "file2str.h"
+
+signed main(int argc, char * * argv) {
+    if (argc < 2) { return 1; }
+    FILE2STR(fstr, argv[1]);
+
+    puts(fstr);
+
+    tbtraverse(fstr);
+
+    return 0;
+}
diff --git a/test/file2str.h b/test/file2str.h
new file mode 100644
index 0000000..4d2ebd1
--- /dev/null
+++ b/test/file2str.h
@@ -0,0 +1,15 @@
+#ifndef FILE2STR_H
+#define FILE2STR_H
+
+#define FILE2STR(dest, filename) \
+    FILE* f = fopen(filename, "r"); \
+    if (!f) { return 1; } \
+    fseek(f, 0, SEEK_END); \
+    int flen = ftell(f); \
+    rewind(f); \
+    char fstr[flen+1]; \
+    dest[flen] = '\00'; \
+    fread(dest, flen, sizeof(char), f); \
+    fclose(f);
+
+#endif
diff --git a/test/function_collector.tbsp b/test/function_collector.tbsp
index e661979..a8ea814 100644
--- a/test/function_collector.tbsp
+++ b/test/function_collector.tbsp
@@ -19,22 +19,19 @@ enter function_definition {
 }
 
 %%
+/* @BAKE
+    tbsp -o $*.tb.cpp $@
+    g++ -o $*.out $*.tb.cpp $(pkg-config --cflags --libs tree-sitter tree-sitter-cpp) -ggdb
+    ./$*.out $@
+   @STOP
+*/
 
-// @BAKE g++ $@ -o $*.out $(pkg-config --cflags --libs tree-sitter tree-sitter-cpp)
+#include "file2str.h"
 
 signed main(int argc, char * * argv) {
-    if (argc < 2) {
-        return 1;
-    }
+    if (argc < 2) { return 1; }
 
-	FILE* f = fopen(argv[1], "r");
-	fseek(f, 0, SEEK_END);
-	int flen = ftell(f);
-	rewind(f);
-	char fstr[flen+1];
-	fstr[flen] = '\00';
-	fread(fstr, flen, sizeof(char), f);
-	fclose(f);
+    FILE2STR(fstr, argv[1]);
 
     tbtraverse(fstr);