alecAbstraction Layer for Escape Codes | 
          
| git clone git://git.dimitrijedobrota.com/alec.git | 
| Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING | 
| commit | d0bd51c29c05b227b28d71c59c6caf7f9742df8d | 
| parent | 9dc03661ab1baccce726bd37b704d7ae34a4caa5 | 
| author | Dimitrije Dobrota < mail@dimitrijedobrota.com > | 
| date | Tue, 27 Feb 2024 22:58:45 +0000 | 
Code generation works!!!
* devide document into 3 sections with %%, same as with flex and bison
    ~ first section is copied over, as is, before the generated code
    ~ second section contains the rules for generating
    ~ third section is copied over, as is, after the generated code
* Configure cmake to generate alec.hpp
| M | CMakeLists.txt | | | + - | 
| M | src/CMakeLists.txt | | | ++++++++++++++++++ ---- | 
| D | src/alec.hpp | | | --------------------------------------------------------------------------------- | 
| A | src/alec.rules.hpp | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | 
| M | src/lexer.l | | | +++++++++++++++++ ------ | 
| M | src/parser.y | | | +++++++++++++++++++++++++++++++++ ------------ | 
| D | src/rules | | | --------------------------------------------------------------------------------- | 
7 files changed, 394 insertions(+), 580 deletions(-)
diff --git a/ CMakeLists.txt b/ CMakeLists.txt
          @@ -3,7 +3,7 @@ 
          set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
        
        
          project(
              Alec
          	VERSION 0.0.10
          	VERSION 0.0.11
              DESCRIPTION "Abstraction Layer for Escape Codes"
          	HOMEPAGE_URL https://git.dimitrijedobrota.com/alec.git
              LANGUAGES C CXX
        
        diff --git a/ src/CMakeLists.txt b/ src/CMakeLists.txt
          @@ -17,19 +17,33 @@ 
          ADD_FLEX_BISON_DEPENDENCY(LEXER PARSER)
        
        
          add_executable(generator "${LEXER_OUT}" "${PARSER_OUT}")
          target_include_directories(generator PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}")
          set(GENERATE_OUT "${CMAKE_BINARY_DIR}/bin")
          set_target_properties(generator PROPERTIES
              VERSION ${PROJECT_VERSION}
              SOVERSION ${PROJECT_VERSION_MAJOR}
              RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
              RUNTIME_OUTPUT_DIRECTORY "${GENERATE_OUT}"
          )
          set(RULES_NAME "alec.rules.hpp")
          set(RULES_FILE "${CMAKE_BINARY_DIR}/${RULES_NAME}")
          configure_file(alec.rules.hpp ${RULES_FILE} COPYONLY)
          add_custom_command(
              OUTPUT ${GENERATE_OUT}/alec.hpp
              COMMAND generator ${RULES_FILE} > ${GENERATE_OUT}/alec.hpp
              DEPENDS generator ${RULES_NAME}
              COMMENT "Generating include file"
          )
          add_library(alec INTERFACE)
          target_include_directories(alec INTERFACE .)
          add_library(alec INTERFACE ${GENERATE_OUT}/alec.hpp)
          target_include_directories(alec INTERFACE ${GENERATE_OUT})
          set_target_properties(alec PROPERTIES
              VERSION ${PROJECT_VERSION}
              SOVERSION ${PROJECT_VERSION_MAJOR}
              PUBLIC_HEADER alec.hpp
              PUBLIC_HEADER ${GENERATE_OUT}/alec.hpp
          )
          install(TARGETS alec
        
        diff --git a/ src/alec.hpp b/ src/alec.hpp
@@ -1,374 +0,0 @@
#ifndef ALEC_ALEC_H
          #define ALEC_ALEC_H
          #include <algorithm>
          #include <array>
          #include <assert.h>
          #include <cstdint>
          #include <string>
          #include <type_traits>
          namespace ALEC {
          enum CTRL {
              BELL = 0x07,
              BS = 0x08,
              HT = 0x09,
              LF = 0x0A,
              VT = 0x0B,
              FF = 0x0C,
              CR = 0x0D,
              ESC = 0x1B,
              DEL = 0x7F,
          };
          enum class COLOR {
              BLACK = 0,
              RED = 1,
              GREEN = 2,
              YELLOW = 3,
              BLUE = 4,
              MAGENTA = 5,
              CYAN = 6,
              WHITE = 7,
              DEFAULT = 9,
          };
          enum class DECOR {
              RESET = 0,
              BOLD = 1,
              DIM = 2,
              ITALIC = 3,
              UNDERLINE = 4,
              BLINK = 5,
              INVERSE = 7,
              HIDE = 8,
              STRIKE = 9,
          };
          enum class MOTION {
              END = 0,
              BEGIN = 1,
              WHOLE = 2,
          };
          namespace details {
          template <std::size_t N> struct string_literal {
              consteval string_literal(const char (&str)[N]) { std::copy_n(str, N, value); }
              consteval std::size_t size() const { return N; }
              char value[N];
          };
          struct helper {
              template <typename T> static consteval std::size_t size(T val);
              template <typename T> static constexpr char *append(char *ptr, T val);
              template <std::size_t N> static constexpr std::size_t size(string_literal<N> val) { return val.size(); }
              static constexpr std::size_t size(char val) { return 1; }
              static constexpr std::size_t size(int val) {
                  std::size_t len = 1;
                  while (val /= 10)
                      len++;
                  return len;
              }
              template <std::size_t N> static constexpr char *append(char *ptr, string_literal<N> val) {
                  std::copy_n(val.value, N, ptr);
                  return ptr + N;
              }
              static constexpr char *append(char *ptr, char val) {
                  *ptr++ = val;
                  return ptr;
              }
              static constexpr char *append(char *ptr, int val) {
                  char *tmp = ptr += size(val);
                  do {
                      *--tmp = '0' + (val % 10);
                  } while (val /= 10);
                  return ptr;
              }
              static const std::string make(auto... args) {
                  std::size_t len = (helper::size(args) + ... + 2);
                  std::string res(len, 'a');
                  res[0] = CTRL::ESC, res[1] = '[';
                  auto map = [ptr = res.data() + 2](auto const &s) mutable { ptr = helper::append(ptr, s); };
                  (map(args), ...);
                  res[len] = 0;
                  return res;
              }
          };
          template <auto... Args> struct escape_t {
              static constexpr const auto value = []() {
                  constexpr std::size_t len = (helper::size(Args) + ... + 2);
                  std::array<char, len + 1> arr{CTRL::ESC, '[', 0};
                  auto map = [ptr = arr.data() + 2](auto const &s) mutable { ptr = helper::append(ptr, s); };
                  (map(Args), ...);
                  arr[len] = 0;
                  return arr;
              }();
          };
          template <auto... Strs> static constexpr auto escape = escape_t<Strs...>::value.data();
          template <details::string_literal... Strs> static constexpr auto escape_literal = escape<Strs...>;
          } // namespace details
          // Tamplate parameter constraints
          template <int n>
          concept limit_256_v = n >= 0 && n < 256;
          template <int n>
          concept limit_pos_v = n >= 0;
          static inline bool limit_pos(int n) { return n >= 0; };
          static inline bool limit_256(int n) { return n >= 0 && n < 256; };
          /* Template compile-time variables */
          // Move cursor up/down/frwd/back
          template <int n>
              requires limit_pos_v<n>
          static constexpr auto cursor_up_v = details::escape<n, 'A'>;
          template <int n>
              requires limit_pos_v<n>
          static constexpr auto cursor_down_v = details::escape<n, 'B'>;
          template <int n>
              requires limit_pos_v<n>
          static constexpr auto cursor_frwd_v = details::escape<n, 'C'>;
          template <int n>
              requires limit_pos_v<n>
          static constexpr auto cursor_back_v = details::escape<n, 'D'>;
          // Move cursor to the next/prev line
          template <int n>
              requires limit_pos_v<n>
          static constexpr auto cursor_line_next_v = details::escape<n, 'E'>;
          template <int n>
              requires limit_pos_v<n>
          static constexpr auto cursor_line_prev_v = details::escape<n, 'F'>;
          // Set cursor to specific column
          template <int n>
              requires limit_pos_v<n>
          static constexpr auto cursor_column_v = details::escape<n, 'G'>;
          // Erase functions
          template <MOTION m> static constexpr auto erase_display_v = details::escape<(int)m, 'J'>;
          template <MOTION m> static constexpr auto erase_line_v = details::escape<(int)m, 'K'>;
          // Scroll up/down
          template <int n>
              requires limit_pos_v<n>
          static constexpr auto scroll_up_v = details::escape<n, 'S'>;
          template <int n>
              requires limit_pos_v<n>
          static constexpr auto scroll_down_v = details::escape<n, 'T'>;
          // Set cursor to a specific position
          template <int n, int m>
              requires limit_pos_v<n> && limit_pos_v<m>
          static constexpr auto cursor_position_v = details::escape<n, ';', m, 'H'>;
          // color
          template <auto... val> static const char *foreground_v;
          template <auto... val> static const char *background_v;
          // palet colors
          template <COLOR color> static constexpr auto foreground_v<color> = details::escape<(int)color + 30, 'm'>;
          template <COLOR color> static constexpr auto background_v<color> = details::escape<(int)color + 40, 'm'>;
          // 256-color palette
          template <int idx>
              requires limit_256_v<idx>
          static constexpr auto foreground_v<idx> = details::escape<38, ';', 5, ';', idx, 'm'>;
          template <int idx>
              requires limit_256_v<idx>
          static constexpr auto background_v<idx> = details::escape<48, ';', 5, ';', idx, 'm'>;
          // RGB colors
          template <int R, int G, int B>
              requires limit_256_v<R> && limit_256_v<G> && limit_256_v<B>
          static constexpr auto foreground_v<R, G, B> =
              details::escape<38, ';', 5, ';', R, ';', G, ';', B, 'm'>;
          template <int R, int G, int B>
              requires limit_256_v<R> && limit_256_v<G> && limit_256_v<B>
          static constexpr auto background_v<R, G, B> =
              details::escape<48, ';', 5, ';', R, ';', G, ';', B, 'm'>;
          // Set/reset text decorators
          template <DECOR decor> static constexpr auto decor_set_v = details::escape<(int)decor, 'm'>;
          template <DECOR decor> static constexpr auto decor_reset_v = details::escape<(int)decor + 20, 'm'>;
          // Save/load cursor position;
          static constexpr auto cursor_save_v = details::escape<'s'>;
          static constexpr auto cursor_load_v = details::escape<'u'>;
          // Set screen modes
          template <int n>
              requires limit_pos_v<n>
          static constexpr auto screen_mode_set_v = details::escape<'=', n, 'h'>;
          template <int n>
              requires limit_pos_v<n>
          static constexpr auto screen_mode_reset_v = details::escape<'=', n, 'l'>;
          // Private screen modes supported by most terminals
          // Save/load screen
          static constexpr auto screen_show_v = details::escape_literal<"?47h">;
          static constexpr auto screen_hide_v = details::escape_literal<"?47l">;
          // Show/hide cursor
          static constexpr auto cursor_show_v = details::escape_literal<"?25h">;
          static constexpr auto cursor_hide_v = details::escape_literal<"?25l">;
          // Show/hide alternate buffer
          static constexpr auto abuf_show_v = details::escape_literal<"?1049h">;
          static constexpr auto abuf_hide_v = details::escape_literal<"?1049l">;
          /* Run-time functions */
          // Move cursor up/down/frwd/back
          static constexpr auto cursor_up(int n) {
              assert(limit_pos(n));
              return details::helper::make(n, 'A');
          }
          static constexpr auto cursor_down(int n) {
              assert(limit_pos(n));
              return details::helper::make(n, 'B');
          }
          static constexpr auto cursor_frwd(int n) {
              assert(limit_pos(n));
              return details::helper::make(n, 'C');
          }
          static constexpr auto cursor_back(int n) {
              assert(limit_pos(n));
              return details::helper::make(n, 'D');
          }
          // Move cursor to the next/prev line
          static constexpr auto cursor_line_next(int n) {
              assert(limit_pos(n));
              return details::helper::make(n, 'E');
          }
          static constexpr auto cursor_line_prev(int n) {
              assert(limit_pos(n));
              return details::helper::make(n, 'F');
          }
          // Set cursor to specific column
          static constexpr auto cursor_column(int n) {
              assert(limit_pos(n));
              return details::helper::make(n, 'G');
          }
          // Erase functions
          static constexpr auto erase_display(MOTION m) { return details::helper::make((int)m, 'J'); }
          static constexpr auto erase_line(MOTION m) { return details::helper::make((int)m, 'K'); }
          // Scroll up/down
          static constexpr auto scroll_up(int n) {
              assert(limit_pos(n));
              return details::helper::make(n, 'S');
          }
          static constexpr auto scroll_down(int n) {
              assert(limit_pos(n));
              return details::helper::make(n, 'T');
          }
          // Set cursor to a specific position
          static constexpr auto cursor_position(int n, int m) {
              assert(limit_pos(n));
              assert(limit_pos(m));
              return details::helper::make(n, ';', m, 'H');
          }
          // color
          // palet colors
          static constexpr auto foreground(COLOR color) { return details::helper::make((int)color + 30, 'm'); }
          static constexpr auto background(COLOR color) { return details::helper::make((int)color + 40, 'm'); }
          // 256-color palette
          static constexpr auto foreground(int idx) {
              assert(limit_256(idx));
              return details::helper::make(38, ';', 5, ';', idx, 'm');
          }
          static constexpr auto background(int idx) {
              assert(limit_256(idx));
              return details::helper::make(48, ';', 5, ';', idx, 'm');
          }
          // RGB colors
          static constexpr auto foreground(int R, int G, int B) {
              assert(limit_256(R));
              assert(limit_256(G));
              assert(limit_256(B));
              return details::helper::make(38, ';', 5, ';', R, ';', G, ';', B, 'm');
          }
          static constexpr auto background(int R, int G, int B) {
              assert(limit_256(R));
              assert(limit_256(G));
              assert(limit_256(B));
              return details::helper::make(48, ';', 5, ';', R, ';', G, ';', B, 'm');
          }
          // Set/reset text decorators
          static constexpr auto decor_set(DECOR decor) { return details::helper::make((int)decor, 'm'); }
          static constexpr auto decor_reset(DECOR decor) { return details::helper::make((int)decor + 20, 'm'); }
          // Save/load cursor position;
          static constexpr auto cursor_save() { return cursor_save_v; }
          static constexpr auto cursor_load() { return cursor_load_v; }
          // Set screen modes
          static constexpr auto screen_mode_set(int n) {
              assert(limit_pos(n));
              return details::helper::make('=', n, 'h');
          }
          static constexpr auto screen_mode_reset(int n) {
              assert(limit_pos(n));
              return details::helper::make('=', n, 'l');
          }
          // Private screen modes supported by most terminals
          // Save/load screen
          static constexpr auto screen_show() { return screen_show_v; }
          static constexpr auto screen_hide() { return screen_hide_v; }
          // Show/hide cursor
          static constexpr auto cursor_show() { return cursor_show_v; }
          static constexpr auto cursor_hide() { return cursor_hide_v; }
          // Show/hide alternate buffer
          static constexpr auto abuf_show() { return abuf_show_v; }
          static constexpr auto abuf_hide() { return abuf_hide_v; }
          // Keyboard string TODO
          } // namespace ALEC
          #endif
        
        diff --git a/ src/alec.rules.hpp b/ src/alec.rules.hpp
@@ -0,0 +1,325 @@
#ifndef ALEC_ALEC_H
          #define ALEC_ALEC_H
          #include <algorithm>
          #include <array>
          #include <assert.h>
          #include <cstdint>
          #include <string>
          #include <type_traits>
          namespace ALEC {
          enum CTRL {
              BELL = 0x07,
              BS = 0x08,
              HT = 0x09,
              LF = 0x0A,
              VT = 0x0B,
              FF = 0x0C,
              CR = 0x0D,
              ESC = 0x1B,
              DEL = 0x7F,
          };
          enum class COLOR {
              BLACK = 0,
              RED = 1,
              GREEN = 2,
              YELLOW = 3,
              BLUE = 4,
              MAGENTA = 5,
              CYAN = 6,
              WHITE = 7,
              DEFAULT = 9,
          };
          enum class DECOR {
              RESET = 0,
              BOLD = 1,
              DIM = 2,
              ITALIC = 3,
              UNDERLINE = 4,
              BLINK = 5,
              INVERSE = 7,
              HIDE = 8,
              STRIKE = 9,
          };
          enum class MOTION {
              END = 0,
              BEGIN = 1,
              WHOLE = 2,
          };
          namespace details {
          template <std::size_t N> struct string_literal {
              consteval string_literal(const char (&str)[N]) { std::copy_n(str, N, value); }
              consteval std::size_t size() const { return N; }
              char value[N];
          };
          struct helper {
              template <typename T> static consteval std::size_t size(T val);
              template <typename T> static constexpr char *append(char *ptr, T val);
              template <std::size_t N> static constexpr std::size_t size(string_literal<N> val) { return val.size(); }
              static constexpr std::size_t size(char val) { return 1; }
              static constexpr std::size_t size(int val) {
                  std::size_t len = 1;
                  while (val /= 10)
                      len++;
                  return len;
              }
              template <std::size_t N> static constexpr char *append(char *ptr, string_literal<N> val) {
                  std::copy_n(val.value, N, ptr);
                  return ptr + N;
              }
              static constexpr char *append(char *ptr, char val) {
                  *ptr++ = val;
                  return ptr;
              }
              static constexpr char *append(char *ptr, int val) {
                  char *tmp = ptr += size(val);
                  do {
                      *--tmp = '0' + (val % 10);
                  } while (val /= 10);
                  return ptr;
              }
              static const std::string make(auto... args) {
                  std::size_t len = (helper::size(args) + ... + 2);
                  std::string res(len, 'a');
                  res[0] = CTRL::ESC, res[1] = '[';
                  auto map = [ptr = res.data() + 2](auto const &s) mutable { ptr = helper::append(ptr, s); };
                  (map(args), ...);
                  res[len] = 0;
                  return res;
              }
          };
          template <auto... Args> struct escape_t {
              static constexpr const auto value = []() {
                  constexpr std::size_t len = (helper::size(Args) + ... + 2);
                  std::array<char, len + 1> arr{CTRL::ESC, '[', 0};
                  auto map = [ptr = arr.data() + 2](auto const &s) mutable { ptr = helper::append(ptr, s); };
                  (map(Args), ...);
                  arr[len] = 0;
                  return arr;
              }();
          };
          template <auto... Strs> static constexpr auto escape = escape_t<Strs...>::value.data();
          template <details::string_literal... Strs> static constexpr auto escape_literal = escape<Strs...>;
          } // namespace details
          // Tamplate parameter constraints
          template <int n>
          concept limit_256_v = n >= 0 && n < 256;
          template <int n>
          concept limit_pos_v = n >= 0;
          static inline bool limit_pos(int n) { return n >= 0; };
          static inline bool limit_256(int n) { return n >= 0 && n < 256; };
          %%
          // Move cursor up/down/frwd/back
              cursor_up
              int n
              limit_pos
              n, 'A'
              cursor_down
              int n
              limit_pos
              n, 'B'
              cursor_frwd
              int n
              limit_pos
              n, 'C'
              cursor_back
              int n
              limit_pos
              n, 'D'
          // Move cursor to the next/prev line
              cursor_line_next
              int n
              limit_pos
              n, 'E'
              cursor_line_prev
              int n
              limit_pos
              n, 'F'
          // Set cursor to specific column
              cursor_column
              int n
              limit_pos
              n, 'G'
          // Erase functions
              erase_display
              MOTION m
              |
              (int)m, 'J'
              erase_line
              MOTION m
              |
              (int)m, 'K'
          // Scroll up/down
              scroll_up
              int n
              limit_pos
              n, 'S'
              scroll_down
              int n
              limit_pos
              n, 'T'
          // Set cursor to a specific position
              cursor_position
              int n, int m
              limit_pos
              n, ';', m, 'H'
          // color
          // palet colors
              foreground
              COLOR color
              |
              (int)color + 30, 'm'
              background
              COLOR color
              |
              (int)color + 40, 'm'
          // 256-color palette
              foreground
              int idx
              limit_256
              38, ';', 5, ';', idx, 'm'
              background
              int idx
              limit_256
              48, ';', 5, ';', idx, 'm'
          // RGB colors
              foreground
              int R, int G, int B
              limit_256
              38, ';', 5, ';', R, ';', G, ';', B, 'm'
              background
              int R, int G, int B
              limit_256
              48, ';', 5, ';', R, ';', G, ';', B, 'm'
          // Set/reset text decorators
              decor_set
              DECOR decor
              |
              (int)decor, 'm'
              decor_reset
              DECOR decor
              |
              (int)decor + 20, 'm'
          // Save/load cursor position;
              cursor_save
              |
              |
              's'
              cursor_load
              |
              |
              'u'
          // Set screen modes
              screen_mode_set
              int n
              limit_pos
              '=', n, 'h'
              screen_mode_reset
              int n
              limit_pos
              '=', n, 'l'
          // Private screen modes supported by most terminals
          // Save/load screen
              screen_show
              |
              |
              "?47h"
              screen_hide
              |
              |
              "?47l"
          // Show/hide cursor
              cursor_show
              |
              |
              "?25h"
              cursor_hide
              |
              |
              "?25l"
          // Show/hide alternate buffer
              abuf_show
              |
              |
              "?1049h"
              abuf_hide
              |
              |
              "?1049l"
          %%
          // Keyboard string TODO
          } // namespace ALEC
          #endif
        
        diff --git a/ src/lexer.l b/ src/lexer.l
@@ -8,22 +8,33 @@
LINE_END (\n|\r|\r\n)
          %x GEN
          %x LAST
          %%
          {LINE_END}                  { return EOL; }
          ^[\t ]*{LINE_END}           { return EOL; }
          ^[\t ]*\|*[\t ]*{LINE_END}  { return EOL; }
          ^[\t ]*"//".* { yylval.n = strdup(yytext); return COMMENT; }
          "%%"{LINE_END}     { BEGIN GEN; return SWITCH; }
          .*{LINE_END}       { yylval.n = strdup(yytext); return BEFORE; }
          <GEN>{LINE_END}                  { return EOL; }
          <GEN>^[\t ]*{LINE_END}           { return EOL; }
          <GEN>^[\t ]*\|*[\t ]*{LINE_END}  { return EOL; }
          <GEN>^[\t ]*"//".* { yylval.n = strdup(yytext); return COMMENT; }
          , { return COMMA; }
          <GEN>, { return COMMA; }
          [^,\n]*                  {
          <GEN>[^,\n]*                  {
              char *p = yytext + strlen(yytext) - 1;
              while(isspace(*p)) *p-- = '\0';
          	while(*yytext && isspace(*yytext)) yytext++;
          	yylval.n = strdup(yytext); return LITERAL;
          }
          <GEN>"%%"{LINE_END}        { BEGIN LAST; return SWITCH; }
          <LAST>.*{LINE_END}       { yylval.n = strdup(yytext); return AFTER; }
          %%
          diff --git a/ src/parser.y b/ src/parser.y
@@ -7,7 +7,9 @@
	void yyrestart(FILE *);
          	int yylex_destroy(void);
          	struct list *records;
          	struct list *records = NULL;
          	struct list *before = NULL;
          	struct list *after = NULL;
          %}
          %union {
        
        @@ -16,8 +18,8 @@
    char *n;
          }
          %token <n> LITERAL COMMENT
          %token EOL COMMA
          %token <n> LITERAL COMMENT BEFORE AFTER
          %token EOL COMMA SWITCH
          %type <r> record
          %type <l> list
        
        @@ -27,15 +29,23 @@
%%
          document: %empty
                  | EOL document 
                  | record document { records = list_new((char *)$1, records); }
                  | COMMENT document {
                      records = list_new((char *)record_new(
                          $1, NULL, NULL, NULL
                      ), records);
                  }
                  ;
          document: before SWITCH mid SWITCH after
          before: %empty
                | BEFORE before { before = list_new($1, before); }
          after: %empty
               | AFTER after { after = list_new($1, after); }
          mid: %empty
             | EOL mid 
             | record mid { records = list_new((char *)$1, records); }
             | COMMENT mid {
                 records = list_new((char *)record_new(
                     $1, NULL, NULL, NULL
                 ), records);
             }
             ;
          record: name list list list { $$ = record_new($1, $2, $3, $4); }
                ;
        
        
          @@ -73,6 +83,12 @@ 
          int main(const int argc, char *argv[]) {
        
        
                  fclose(f);
              }
              // print before section
          	for(struct list *p = before; p; p = p->next) {
          		printf("%s", p->data);
          	}
              struct list *dupes = record_dupes(records);
              record_print_dupes(dupes);
          
          @@ -88,6 +104,11 @@ 
          int main(const int argc, char *argv[]) {
        
        
          		record_print_function(r);
          	}
              // print after section
          	for(struct list *p = after; p; p = p->next) {
          		printf("%s", p->data);
          	}
          	list_free(records, record_free);
          	list_free(dupes, 0);
          diff --git a/ src/rules b/ src/rules
@@ -1,183 +0,0 @@
// Move cursor up/down/frwd/back
              cursor_up
              int n
              limit_pos
              n, 'A'
              cursor_down
              int n
              limit_pos
              n, 'B'
              cursor_frwd
              int n
              limit_pos
              n, 'C'
              cursor_back
              int n
              limit_pos
              n, 'D'
          // Move cursor to the next/prev line
              cursor_line_next
              int n
              limit_pos
              n, 'E'
              cursor_line_prev
              int n
              limit_pos
              n, 'F'
          // Set cursor to specific column
              cursor_column
              int n
              limit_pos
              n, 'G'
          // Erase functions
              erase_display
              MOTION m
              |
              (int)m, 'J'
              erase_line
              MOTION m
              |
              (int)m, 'K'
          // Scroll up/down
              scroll_up
              int n
              limit_pos
              n, 'S'
              scroll_down
              int n
              limit_pos
              n, 'T'
          // Set cursor to a specific position
              cursor_position
              int n, int m
              limit_pos
              n, ';', m, 'H'
          // color
          // palet colors
              foreground
              COLOR color
              |
              (int)color + 30, 'm'
              background
              COLOR color
              |
              (int)color + 40, 'm'
          // 256-color palette
              foreground
              int idx
              limit_256
              38, ';', 5, ';', idx, 'm'
              background
              int idx
              limit_256
              48, ';', 5, ';', idx, 'm'
          // RGB colors
              foreground
              int R, int G, int B
              limit_256
              38, ';', 5, ';', R, ';', G, ';', B, 'm'
              background
              int R, int G, int B
              limit_256
              48, ';', 5, ';', R, ';', G, ';', B, 'm'
          // Set/reset text decorators
              decor_set
              DECOR decor
              |
              (int)decor, 'm'
              decor_reset
              DECOR decor
              |
              (int)decor + 20, 'm'
          // Save/load cursor position;
              cursor_save
              |
              |
              's'
              cursor_load
              |
              |
              'u'
          // Set screen modes
              screen_mode_set
              int n
              limit_pos
              '=', n, 'h'
              screen_mode_reset
              int n
              limit_pos
              '=', n, 'l'
          // Private screen modes supported by most terminals
          // Save/load screen
              screen_show
              |
              |
              "?47h"
              screen_hide
              |
              |
              "?47l"
          // Show/hide cursor
              cursor_show
              |
              |
              "?25h"
              cursor_hide
              |
              |
              "?25l"
          // Show/hide alternate buffer
              abuf_show
              |
              |
              "?1049h"
              abuf_hide
              |
              |
              "?1049l"