hellux/jotdown

Provide C API

sorairolake opened this issue · 2 comments

As far as I know, there is no C implementation of djot yet. So, I think it would be nice if there is C bindings of this library.

see also: https://github.com/eqrion/cbindgen

Yes, the thought has crossed my mind also. Question is, how should the
API look like? The current API is quite Rust-focused and at least some
manual work would be needed to make a reasonable C-API on top of it.

Exposing a function that converts djot directly to html would be simple,
but djot to events and events to html would be a lot more useful.

  • I guess c strings would not be an option, but rather custom types that
    work like &str (ptr + len) and Cow (&str or owned ptr + len +
    cap). Most strings are simply a view of the original source code, this
    is not possible with c-strings because then we would need to insert null
    values, overwriting the source code.

  • Each event is a variant of the Event ADT/sum type. I guess this could
    be implemented as a tag in addition to a union for the data. Something
    like

    struct djot_event {
      djot_event_tag tag;
      djot_event_data data;
    };
    
    enum djot_event_tag {
      DJOT_EVENT_START;
      DJOT_EVENT_END;
      DJOT_EVENT_STR;
      DJOT_EVENT_SYMBOL;
      ..
    };
    
    union djot_event_data {
      struct { djot_container container; djot_attributes attributes } start;
      struct { djot_container container } end;
      djot_cow_str str;
      djot_cow_str symbol;
      ..
    };
    
    struct djot_container {
      djot_container_tag tag,
      djot_container_data data,
    };
    
    ..
  • The parser is currently an iterator of Events, and the html renderer
    accepts any iterator of Events. We could provide the Parser::next
    function to allow pulling one event at a time from C. For the html
    rendering, the C implementation would need to implement a next
    function that allows the renderer to request the events. So, the C
    next function will be a callback that in turn calls Parser::next. Might
    look something like:

    jotdown.h:

    struct djot_parser;
    
    djot_event djot_parser_next(djot_parser*);
    
    typedef djot_event (*djot_next_event)(void *data);
    
    /* returned value is dynamically allocated, must be freed by caller. */
    char *djot_render_html(void *data, djot_next_event callback);

    some_client.c

    #include <jotdown.h>
    
    struct my_renderer {
      djot_parser parser;
      ..
    };
    
    my_renderer my_renderer_new() {
      ..
    }
    
    djot_event my_renderer_next_event(void *data) {
      struct my_renderer *r = (struct my_renderer*)data;
    
      struct djot_event ev = djot_parser_next(&r->parser);
    
      /* do something with event */
    
      return ev;
    }
    
    int main() {
      ..
      struct my_renderer r = my_renderer_new();
      char *html = djot_render_html(&r, my_renderer_next_event);
      ..
      free(html);
      ..
    }

I suppose it should be doable.

Actually, the lua reference implementation does have a minimal C API at https://github.com/jgm/djot.lua/tree/main/clib. It seems to expose direct rendering to HTML and json AST, and modification using lua filters.