Edit

kc3-lang/kc3/ekc3/embed.c

Branch :

  • ekc3/embed.c
  • /* kc3
     * Copyright from 2022 to 2025 kmx.io <contact@kmx.io>
     *
     * Permission is hereby granted to use this software granted the above
     * copyright notice and this permission paragraph are included in all
     * copies and substantial portions of this software.
     *
     * THIS SOFTWARE IS PROVIDED "AS-IS" WITHOUT ANY GUARANTEE OF
     * PURPOSE AND PERFORMANCE. IN NO EVENT WHATSOEVER SHALL THE
     * AUTHOR BE CONSIDERED LIABLE FOR THE USE AND PERFORMANCE OF
     * THIS SOFTWARE.
     */
    #include <string.h>
    #include "../libkc3/kc3.h"
    #include "embed.h"
    
    s_tag * embed_parse_template (s_buf *input, s_tag *dest)
    {
      s_buf_save input_save = {0};
      character c = 0;
      sw level = 0;
     const char *p = NULL;
      sw r;
      e_embed_state state = EMBED_STATE_RAW;
      s_str str = {0};
      p_list *tail = NULL;
      p_list template = NULL;
      s_tag tmp = {0};
      s_buf token_buf = {0};
      s8    token_position = 0;
      if (! input || ! dest) {
        err_puts("embed_parse_template: NULL argument");
        return NULL;
      }
      tail = &template;
      if (! (*tail = list_new_str_1(NULL, "fn () do str([", NULL))) {
        buf_clean(&token_buf);
        return NULL;
      }
      tail = &(*tail)->next.data.plist;
      if (! buf_init_alloc(&token_buf, BUF_SIZE)) {
        err_puts("embed_parse_template: buf_init_alloc failed");
        return NULL;
      }
      while (buf_read_character_utf8(input, &c) > 0) {
        switch (state) {
        case EMBED_STATE_VOID:
          err_puts("embed_parse_template: EMBED_STATE_VOID 1");
          assert(! "embed_parse_template: EMBED_STATE_VOID 1");
          return NULL;
        case EMBED_STATE_RAW:
          switch (token_position) {
          case 0:
            if (c == '<')
              token_position = 1;
            else
              buf_write_character_utf8(&token_buf, c);
            break;
          case 1:
            if (c == '%')
              token_position = 2;
            else {
              token_position = 0;
              buf_write_character_utf8(&token_buf, '<');
              buf_write_character_utf8(&token_buf, c);
            }
            break;
          case 2:
            if (token_buf.wpos > 0) {
              p = ",\n  ";
              if (! level) {
                level++;
                p++;
              }
              if (! (*tail = list_new_str_1(NULL, p, NULL))) {
                list_delete_all(template);
                buf_clean(&token_buf);
                return NULL;
              }
              tail = &(*tail)->next.data.plist;
              if (buf_read_to_str(&token_buf, &str) <= 0) {
                list_delete_all(template);
                buf_clean(&token_buf);
                return NULL;
              }
              if (! (*tail = list_new_str_inspect_str(&str, NULL))) {
                list_delete_all(template);
                str_clean(&str);
                buf_clean(&token_buf);
                return NULL;
              }
              str_clean(&str);
              tail = &(*tail)->next.data.plist;
              buf_empty(&token_buf);
            }
            if (c == '=') {
              token_position = 0;
              buf_save_init(input, &input_save);
              if (buf_ignore_spaces(input) < 0 ||
                  (r = buf_read_1(input, "raw")) < 0 ||
                  (r && (r = buf_ignore_spaces(input)) < 0)) {
                buf_save_clean(input, &input_save);
                list_delete_all(template);
                buf_clean(&token_buf);
                return NULL;
              }
              if (r)
                state = EMBED_STATE_VERBOSE_RAW;
              else {
                state = EMBED_STATE_VERBOSE;
                buf_save_restore_rpos(input, &input_save);
              }
              buf_save_clean(input, &input_save);
            } else {
              state = EMBED_STATE_SILENT;
              token_position = 0;
              buf_write_character_utf8(&token_buf, c);
            }
            break;
          }
          break;
        case EMBED_STATE_SILENT:
        case EMBED_STATE_VERBOSE:
        case EMBED_STATE_VERBOSE_RAW:
          switch (token_position) {
          case 0:
            if (c == '%')
              token_position = 1;
            else
              buf_write_character_utf8(&token_buf, c);
            break;
          case 1:
            if (c == '>') {
              if (token_buf.wpos > 0) {
                switch (state) {
                case EMBED_STATE_SILENT:      p = ",\n  do ";         break;
                case EMBED_STATE_VERBOSE:     p = ",\n  HTML.escape(";
                                                                      break;
                case EMBED_STATE_VERBOSE_RAW: p = ",\n (Str) ";       break;
                case EMBED_STATE_RAW:
                case EMBED_STATE_VOID:
                  err_puts("embed_parse_template: invalid state");
                  assert(! "embed_parse_template: invalid state");
                  abort();
                }
                if (! level) {
                  level++;
                  p++;
                }
                if (! (*tail = list_new_str_1(NULL, p, NULL))) {
                  list_delete_all(template);
                  buf_clean(&token_buf);
                  return NULL;
                }
                tail = &(*tail)->next.data.plist;
                if (buf_read_to_str(&token_buf, &str) <= 0) {
                  list_delete_all(template);
                  buf_clean(&token_buf);
                  return NULL;
                }
                if (! (*tail = list_new_str(str.free.pchar, str.size,
                                            str.ptr.pchar, NULL))) {
                  list_delete_all(template);
                  buf_clean(&token_buf);
                  return NULL;
                }
                tail = &(*tail)->next.data.plist;
                buf_empty(&token_buf);
                switch (state) {
                case EMBED_STATE_SILENT:      p = "\n    \"\"\n  end";
                                                                break;
                case EMBED_STATE_VERBOSE:     p = ")";          break;
                case EMBED_STATE_VERBOSE_RAW: p = NULL;         break;
                case EMBED_STATE_RAW:
                case EMBED_STATE_VOID:
                  err_puts("embed_parse_template: EMBED_STATE_VOID 2");
                  assert(! "embed_parse_template: EMBED_STATE_VOID 2");
                  return NULL;
                }
                if (p) {
                  if (! (*tail = list_new_str_1(NULL, p, NULL))) {
                    list_delete_all(template);
                    buf_clean(&token_buf);
                    return NULL;
                  }
                  tail = &(*tail)->next.data.plist;
                }
              }
              level++;
              state = EMBED_STATE_RAW;
              token_position = 0;
            }
            else {
              buf_write_character_utf8(&token_buf, '%');
              buf_write_character_utf8(&token_buf, c);
              token_position = 0;
            }
            break;
          }
          break;
        }
      }
      if (token_buf.wpos > 0) {
        switch (state) {
        case EMBED_STATE_RAW:
          p = ",\n  ";
          if (! level)
            p++;
          if (! (*tail = list_new_str_1(NULL, p, NULL))) {
            list_delete_all(template);
            buf_clean(&token_buf);
            return NULL;
          }
          tail = &(*tail)->next.data.plist;
          break;
        case EMBED_STATE_SILENT:
        case EMBED_STATE_VERBOSE:
        case EMBED_STATE_VERBOSE_RAW:
          err_puts("embed_parse_template: unterminated code section");
          assert(! "embed_parse_template: unterminated code section");
          list_delete_all(template);
          buf_clean(&token_buf);
          return NULL;
        case EMBED_STATE_VOID:
          err_puts("embed_parse_template: EMBED_STATE_VOID 3");
          assert(! "embed_parse_template: EMBED_STATE_VOID 3");
          return NULL;
        }
        if (buf_read_to_str(&token_buf, &str) <= 0) {
          list_delete_all(template);
          buf_clean(&token_buf);
          return NULL;
        }
        if (! (*tail = list_new_str_inspect_str(&str, NULL))) {
          list_delete_all(template);
          str_clean(&str);
          buf_clean(&token_buf);
          return NULL;
        }
        str_clean(&str);
        tail = &(*tail)->next.data.plist;
      }
      buf_clean(&token_buf);
      if (! (*tail = list_new_str_1(NULL, " ])\nend", NULL))) {
        list_delete_all(template);
        return NULL;
      }
      tail = &(*tail)->next.data.plist;
      if (! str_init_concatenate_list(&str, template)) {
        list_delete_all(template);
        return NULL;
      }
      list_delete_all(template);
      if (! tag_init_from_str(&tmp, &str)) {
        tmp = (s_tag) {0};
        tmp.type = TAG_STR;
        tmp.data.str = str;
      }
      else
        str_clean(&str);
      *dest = tmp;
      return dest;
    }
    
    s_tag * embed_parse_template_1 (const char *input, s_tag *dest)
    {
      s_buf input_buf;
      if (! input || ! dest) {
        err_puts("embed_parse_template_1: NULL argument");
        return NULL;
      }
      if (! buf_init_const(&input_buf, strlen(input), input)) {
        err_puts("embed_parse_template_1: buf_init_const failed");
        return NULL;
      }
      return embed_parse_template(&input_buf, dest);
    }
    
    s_tag * embed_parse_template_file (const s_str *path, s_tag *dest)
    {
      s_buf buf = {0};
      FILE *fp = NULL;
      s_tag tmp = {0};
      if (! path || ! dest) {
        err_puts("embed_parse_template_file: invalid argument");
        assert(! "embed_parse_template_file: invalid argument");
        return NULL;
      }
      if (! buf_init_alloc(&buf, BUF_SIZE))
        return NULL;
      if (! (fp = file_open(path, "r"))) {
        buf_clean(&buf);
        return NULL;
      }
      if (! buf_file_open_r(&buf, fp)) {
        fclose(fp);
        buf_clean(&buf);
      }
      if (! embed_parse_template(&buf, &tmp)) {
        buf_file_close(&buf);
        fclose(fp);
        buf_clean(&buf);
        return NULL;
      }
      buf_file_close(&buf);
      fclose(fp);
      buf_clean(&buf);
      *dest = tmp;
      return dest;
    }
    
    s_tag * embed_parse_template_str (const s_str *input, s_tag *dest)
    {
      s_buf buf = {0};
      if (! input || ! dest) {
        err_puts("embed_parse_template_str: invalid argument");
        assert(! "embed_parse_template_str: invalid argument");
        return NULL;
      }
      if (! buf_init_str_const(&buf, input)) {
        err_puts("embed_parse_template_str: buf_init_str");
        assert(! "embed_parse_template_str: buf_init_str");
        return NULL;
      }
      return embed_parse_template(&buf, dest);
    }