Parsee/src/XEP-0393.c
LDA 408888ef67 [ADD/WIP] Congestion, basic ad-hoc commands
Woah, took me a while, eh? Next up, getting forms!
2024-07-13 16:26:33 +02:00

302 lines
6.7 KiB
C

#include <XEP393.h>
#include <Cytoplasm/Memory.h>
#include <Cytoplasm/Array.h>
#include <Cytoplasm/Log.h>
#include <string.h>
#include <ctype.h>
static XEP393Element *
CreateElementVessel(XEP393Element *parent, XEP393Type type)
{
XEP393Element *ret = Malloc(sizeof(*ret));
ret->parent = parent;
ret->type = type;
ret->children = ArrayCreate();
ret->text_data = NULL;
if (parent)
{
ArrayAdd(parent->children, ret);
}
return ret;
}
static void
XEP393FreeElementBase(XEP393Element *element, bool unlink)
{
size_t i, len;
if (!element)
{
return;
}
len = ArraySize(element->children);
for (i = 0; i < len; i++)
{
XEP393FreeElement(ArrayGet(element->children, i));
}
if (element->parent && unlink)
{
XEP393Element *parent = element->parent;
len = ArraySize(parent->children);
for (i = 0; i < len; i++)
{
XEP393Element *c = ArrayGet(parent->children, i);
if (c == element)
{
ArrayDelete(parent->children, i);
break;
}
}
}
ArrayFree(element->children);
Free(element->text_data);
Free(element);
}
void
XEP393FreeElement(XEP393Element *element)
{
XEP393FreeElementBase(element, false);
}
typedef struct StrView {
char *start;
char *end;
bool heap_free;
} StrView;
#define ViewLength(v) ((size_t) ((v.end) - (v.start)))
static char *
StringifyView(StrView v)
{
char *r;
size_t len;
if (!v.start || v.start > v.end)
{
return NULL;
}
len = ViewLength(v);
r = Malloc(len + 1);
memcpy(r, v.start, len);
r[len] = '\0';
return r;
}
static StrView
CreateStaticView(char *str)
{
StrView view = {
.start = str,
.end = str + strlen(str),
.heap_free = false
};
return view;
}
static bool
IdentifySpan(char span_tag, StrView in, StrView *view)
{
size_t length;
bool found = false;
char prev = '\0';
if (in.start >= in.end)
{
return false;
}
if (ViewLength(in) < 2)
{
return false;
}
if (*in.start != span_tag || isspace(*(in.start + 1)))
{
/* The opening styling directive MUST NOT be followed
* by a whitespace character */
return false;
}
view->start = in.start + 1;
in.start += 1;
for (length = 0; ViewLength(in) > 0; length++, in.start++)
{
if (*in.start == span_tag)
{
found = true;
break;
}
prev = *in.start;
}
if (!found || !length || (prev && isspace(prev)))
{
/* the closing styling directive MUST NOT be preceeded
* by a whitespace character. */
return false;
}
view->end = in.start;
return true;
}
static void
XEP393Decode(StrView view, XEP393Element *root)
{
StrView subview = view;
StrView textview = view;
XEP393Element *text, *span;
bool managed = false;
char prev = '\0', curr = '\0';
textview.end = subview.start;
for (; subview.start < subview.end; subview.start++)
{
StrView span_view;
managed = false;
curr = *subview.start;
if (prev == '\0' || prev == '\n')
{
/* TODO: Start of line, start parsing blocks. */
}
#define Spanify(xep_symbol) \
managed = true; \
textview.end = subview.start; \
text = CreateElementVessel( \
root, XEP393_TEXT \
); \
text->text_data = StringifyView(textview); \
\
/* Found a span. */ \
span = CreateElementVessel( \
root, xep_symbol \
); \
\
XEP393Decode(span_view, span); \
\
/* Update subview */ \
subview.start = span_view.end + 1; \
\
/* Update textview */ \
textview.start = subview.start; \
textview.end = subview.start
if (IdentifySpan('_', subview, &span_view))
{
Spanify(XEP393_ITALIC);
}
else if (IdentifySpan('*', subview, &span_view))
{
Spanify(XEP393_EMPH);
}
else if (IdentifySpan('`', subview, &span_view))
{
Spanify(XEP393_MONO);
}
else
{
/* Text character: update end */
textview.end = subview.start;
}
prev = curr;
}
if (!managed)
{
textview.end = subview.start;
text = CreateElementVessel(
root, XEP393_TEXT
);
text->text_data = StringifyView(textview);
}
}
XEP393Element *
XEP393(char *message)
{
StrView view = CreateStaticView(message);
XEP393Element *root = CreateElementVessel(NULL, XEP393_ROOT);
/* TODO: Parse blocks first, *then* spans. Considering the
* current architecture, this shouldn't be too hard to integrate,
* given how string views already manage boundaries, and elements
* can already be used to contain blocks I think.
*
* Actually, nevermind, these would be pure pain. Nested blocks,
* unterminated ones, QUOTES. Just hell. I hate parsing this shit. */
XEP393Decode(view, root);
return root;
}
#include <XML.h>
static void
ShoveXML(XEP393Element *element, XMLElement *xmlparent)
{
XMLElement *head = xmlparent;
size_t i;
if (!element || !xmlparent)
{
return;
}
switch (element->type)
{
case XEP393_ITALIC:
head = XMLCreateTag("i");
XMLAddChild(xmlparent, head);
break;
case XEP393_EMPH:
head = XMLCreateTag("strong");
XMLAddChild(xmlparent, head);
break;
case XEP393_MONO:
head = XMLCreateTag("code");
XMLAddChild(xmlparent, head);
break;
default: break;
}
if (ArraySize(element->children) == 0 && element->text_data)
{
XMLElement *text = XMLCreateText(element->text_data);
XMLAddChild(head, text);
}
for (i = 0; i < ArraySize(element->children); i++)
{
XEP393Element *sub = ArrayGet(element->children, i);
ShoveXML(sub, head);
}
}
#include <StringStream.h>
char *
XEP393ToXMLString(XEP393Element *xepd)
{
XMLElement *root;
Stream *writer;
char *ret = NULL;
size_t i;
if (!xepd)
{
return NULL;
}
root = XMLCreateTag("ROOT");
ShoveXML(xepd, root);
writer = StrStreamWriter(&ret);
for (i = 0; i < ArraySize(root->children); i++)
{
XMLEncode(writer, ArrayGet(root->children, i));
}
XMLFreeElement(root);
StreamFlush(writer);
StreamClose(writer);
return ret;
}