Skip to content

Commit

Permalink
parsers: implemented uri module (#110)
Browse files Browse the repository at this point in the history
  • Loading branch information
zpl-zak authored Jul 13, 2023
1 parent e45ca57 commit e615951
Show file tree
Hide file tree
Showing 11 changed files with 453 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ misc/deploy/
.vscode
.vs
package-lock.json
build.bat
build.*

# workdir
work/
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
19.1.0 - parser: introduce a new URI parser module
19.0.4 - fix: zpl_buffer_copy_init missing macros (thanks Ed_ on Discord)
19.0.3 - fix: zpl_str_skip_literal reading before start of string (mbenniston)
19.0.2 - fixed ZPL_ISIZE_MIN and MAX for 32-bit architectures (mrossetti)
Expand Down
71 changes: 71 additions & 0 deletions code/apps/examples/uri.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#define ZPL_IMPLEMENTATION
#define ZPL_NANO
#define ZPL_ENABLE_PARSER
#include <zpl.h>

int main(void) {
zpl_file_contents fc;
fc = zpl_file_read_contents(zpl_heap(), true, "misc/data/uri_example.txt");

zpl_uri_object root = {0};

zpl_u8 err;
err = zpl_uri_parse(&root, (char *)fc.data, zpl_heap_allocator());

zpl_printf("Error code: %d\n", err);
zpl_json_write(zpl_file_get_standard(ZPL_FILE_STANDARD_OUTPUT), &root, 0);
zpl_uri_write(zpl_file_get_standard(ZPL_FILE_STANDARD_OUTPUT), &root);
zpl_uri_free(&root);
zpl_file_free_contents(&fc);
zpl_printf("\n");

{
zpl_uri_object custom = {0};
zpl_uri_init(&custom, "http://example.com", zpl_heap_allocator());
zpl_adt_append_str(&custom, "foo", "bar");
zpl_json_write(zpl_file_get_standard(ZPL_FILE_STANDARD_OUTPUT), &custom, 0);
zpl_uri_write(zpl_file_get_standard(ZPL_FILE_STANDARD_OUTPUT), &custom);
zpl_uri_free(&custom);
zpl_printf("\n");
}

{
zpl_uri_object custom = {0};
zpl_uri_init(&custom, NULL, zpl_heap_allocator());
zpl_adt_append_str(&custom, "foo", "bar");
zpl_json_write(zpl_file_get_standard(ZPL_FILE_STANDARD_OUTPUT), &custom, 0);
zpl_uri_write(zpl_file_get_standard(ZPL_FILE_STANDARD_OUTPUT), &custom);
zpl_uri_free(&custom);
zpl_printf("\n");
}

{
zpl_uri_object custom = {0};
zpl_uri_init(&custom, NULL, zpl_heap_allocator());
zpl_adt_append_str(&custom, "foo", "");
zpl_adt_append_str(&custom, "foo2", NULL);
zpl_json_write(zpl_file_get_standard(ZPL_FILE_STANDARD_OUTPUT), &custom, 0);
zpl_uri_write(zpl_file_get_standard(ZPL_FILE_STANDARD_OUTPUT), &custom);
zpl_uri_free(&custom);
zpl_printf("\n");
}

{
zpl_uri_object custom = {0};
zpl_uri_parse(&custom, "?bar=foo", zpl_heap_allocator());
zpl_json_write(zpl_file_get_standard(ZPL_FILE_STANDARD_OUTPUT), &custom, 0);
zpl_uri_write(zpl_file_get_standard(ZPL_FILE_STANDARD_OUTPUT), &custom);
zpl_uri_free(&custom);
zpl_printf("\n");
}

{
zpl_uri_object custom = {0};
zpl_uri_parse(&custom, "?bar=&foo=", zpl_heap_allocator());
zpl_json_write(zpl_file_get_standard(ZPL_FILE_STANDARD_OUTPUT), &custom, 0);
zpl_uri_write(zpl_file_get_standard(ZPL_FILE_STANDARD_OUTPUT), &custom);
zpl_uri_free(&custom);
zpl_printf("\n");
}
return 0;
}
19 changes: 19 additions & 0 deletions code/header/adt.h
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,16 @@ ZPL_DEF zpl_adt_node *zpl_adt_append_int(zpl_adt_node *parent, char const *name,
*/
ZPL_DEF char *zpl_adt_parse_number(zpl_adt_node *node, char* base);

/**
* @brief Parses a text and stores the result into an unitialised node.
* This function expects the entire input to be a number.
*
* @param node
* @param base
* @return*
*/
ZPL_DEF char *zpl_adt_parse_number_strict(zpl_adt_node *node, char* base_str);

/**
* @brief Parses and converts an existing string node into a number.
*
Expand All @@ -328,6 +338,15 @@ ZPL_DEF char *zpl_adt_parse_number(zpl_adt_node *node, char* base);
*/
ZPL_DEF zpl_adt_error zpl_adt_str_to_number(zpl_adt_node *node);

/**
* @brief Parses and converts an existing string node into a number.
* This function expects the entire input to be a number.
*
* @param node
* @return
*/
ZPL_DEF zpl_adt_error zpl_adt_str_to_number_strict(zpl_adt_node *node);

/**
* @brief Prints a number into a file stream.
*
Expand Down
20 changes: 20 additions & 0 deletions code/header/parsers/uri.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// file: header/parsers/uri.h


ZPL_BEGIN_C_DECLS

typedef zpl_adt_node zpl_uri_object;

typedef enum zpl_uri_error {
ZPL_URI_ERROR_NONE,
ZPL_URI_ERROR_INTERNAL,
} zpl_uri_error;

ZPL_DEF zpl_u8 zpl_uri_init(zpl_adt_node *root, char *origin, zpl_allocator a);
ZPL_DEF zpl_u8 zpl_uri_parse(zpl_adt_node *root, char *text, zpl_allocator a);
ZPL_DEF void zpl_uri_write(zpl_file *f, zpl_adt_node *obj);
ZPL_DEF zpl_string zpl_uri_write_string(zpl_allocator a, zpl_adt_node *obj);
ZPL_DEF void zpl_uri_free(zpl_adt_node *obj);

ZPL_END_C_DECLS

35 changes: 35 additions & 0 deletions code/source/adt.c
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,24 @@ zpl_adt_node *zpl_adt_append_int(zpl_adt_node *parent, char const *name, zpl_i64
}

/* parser helpers */
char *zpl_adt_parse_number_strict(zpl_adt_node *node, char* base_str) {
ZPL_ASSERT_NOT_NULL(node);
ZPL_ASSERT_NOT_NULL(base_str);
char *p = base_str, *e = p;

while (*e)
++e;

while (*p && (zpl_strchr("eE.+-", *p) || zpl_char_is_hex_digit(*p))) {
++p;
}

if (p >= e) {
return zpl_adt_parse_number(node, base_str);
}

return base_str;
}

char *zpl_adt_parse_number(zpl_adt_node *node, char* base_str) {
ZPL_ASSERT_NOT_NULL(node);
Expand Down Expand Up @@ -549,6 +567,10 @@ zpl_adt_error zpl_adt_print_string(zpl_file *file, zpl_adt_node *node, char cons

/* escape string */
char const* p = node->string, *b = p;

if (!p)
return ZPL_ADT_ERROR_NONE;

do {
p = zpl_str_skip_any(p, escaped_chars);
zpl__adt_fprintf(file, "%.*s", zpl_ptr_diff(b, p), b);
Expand All @@ -575,6 +597,19 @@ zpl_adt_error zpl_adt_str_to_number(zpl_adt_node *node) {
return ZPL_ADT_ERROR_NONE;
}

zpl_adt_error zpl_adt_str_to_number_strict(zpl_adt_node *node) {
ZPL_ASSERT(node);

if (node->type == ZPL_ADT_TYPE_REAL || node->type == ZPL_ADT_TYPE_INTEGER) return ZPL_ADT_ERROR_ALREADY_CONVERTED; /* this is already converted/parsed */
if (node->type != ZPL_ADT_TYPE_STRING && node->type != ZPL_ADT_TYPE_MULTISTRING) {
return ZPL_ADT_ERROR_INVALID_TYPE;
}

zpl_adt_parse_number_strict(node, (char *)node->string);

return ZPL_ADT_ERROR_NONE;
}

#undef zpl__adt_fprintf

ZPL_END_C_DECLS
198 changes: 198 additions & 0 deletions code/source/parsers/uri.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
// file: source/parsers/uri.c

ZPL_BEGIN_C_DECLS

void zpl__uri_decode_str(char *text) {
char buf[ZPL_PRINTF_MAXLEN] = {0}, *p = buf;
char *tp = text;

char ch = -1;
while (*tp) {
ch = *tp++;

if (ch != '%') {
if (ch == '+') {
*(p++) = ' ';
} else {
*(p++) = ch;
}
} else {
char hex[3] = {0};
hex[0] = tp[0];
hex[1] = tp[1];
char b = (char)zpl_str_to_i64(hex, NULL, 16);
*(p++) = b;
tp += 2;
}
}

zpl_strcpy(text, buf);
text[p-buf] = 0;
}

zpl_u8 zpl_uri_init(zpl_adt_node *root, char *origin, zpl_allocator a) {
zpl_u8 err_code = ZPL_URI_ERROR_NONE;
ZPL_ASSERT_NOT_NULL(root);
zpl_zero_item(root);
zpl_adt_set_obj(root, NULL, a);
if (origin)
zpl_adt_append_str(root, "__zpl_origin__", origin);
return err_code;
}

zpl_u8 zpl_uri_parse(zpl_adt_node *root, char *text, zpl_allocator a) {
zpl_u8 err_code = ZPL_URI_ERROR_NONE;
ZPL_ASSERT_NOT_NULL(root);
ZPL_ASSERT_NOT_NULL(text);
zpl_zero_item(root);
text = (char *)zpl_str_trim(text, false);

zpl_adt_set_obj(root, NULL, a);

char *p = text, *b = p;

if (*p == 0) {
return err_code;
}

// NOTE(zaklaus): grab URI origin
if (*p != '?') {
while (*p && *p != '?' && !zpl_char_is_space(*p)) {++p;}
char c = *p;
*p = 0;

zpl_adt_append_str(root, "__zpl_origin__", b);

if (!c) {
// NOTE(zaklaus): URI has no query params, bail
return err_code;
}
}

b = ++p;

// NOTE(zaklaus): extract query params
while (*p && !zpl_char_is_space(*p)) {
// NOTE(zaklaus): get param name
b = p;
while (*p && (*p != '&' && *p != '?' && *p != '=' && !zpl_char_is_space(*p))) { ++p; }
char c = *p;
*p = 0;

char *field_name = b;
char *field_value = "";
zpl__uri_decode_str(field_name);

if (c == '=') {
// NOTE(zaklaus): read param value
++p;
b = p;
while (*p && (*p != '&' && *p != '?' && !zpl_char_is_space(*p))) { ++p; }
c = *p;
*p = 0;

field_value = b;
zpl__uri_decode_str(field_value);
zpl_adt_node *obj = zpl_adt_append_str(root, field_name, field_value);
zpl_adt_str_to_number_strict(obj);
} else {
zpl_adt_node *obj = zpl_adt_append_flt(root, field_name, 1);
obj->props = ZPL_ADT_PROPS_TRUE;
}


if (!c) {
break;
}

++p;
}

return err_code;
}

zpl_adt_error zpl__uri_print_str(zpl_file *file, const char *text) {
ZPL_ASSERT_NOT_NULL(file);

if (!text) {
return ZPL_ADT_ERROR_NONE;
}

const char *p = text;
char buf[10] = {0};
zpl_u8 ch;

while (*p) {
ch = (zpl_u8) *p++;
if (ch == ' ') {
zpl_fprintf(file, "%s", "+");
} else if (zpl_char_is_alphanumeric(ch) || zpl_strchr("-_.~", ch)) {
zpl_fprintf(file, "%c", ch);
} else {
zpl_snprintf(buf, zpl_size_of(buf), "%02X", ch);
zpl_fprintf(file, "%c%s", '%', buf);
}
}

return ZPL_ADT_ERROR_NONE;
}

void zpl_uri_write(zpl_file *f, zpl_adt_node *obj) {
ZPL_ASSERT_NOT_NULL(f);
ZPL_ASSERT_NOT_NULL(obj);
ZPL_ASSERT(obj->type == ZPL_ADT_TYPE_OBJECT);

zpl_adt_node *origin = NULL;

// NOTE(zaklaus): write URI origin if available
{
origin = zpl_adt_query(obj, "__zpl_origin__");
if (origin) {
zpl_fprintf(f, origin->string);
}
}

// NOTE(zaklaus): write params
if (zpl_array_count(obj->nodes) > 0)
zpl_fprintf(f, "%s", "?");

for (zpl_isize i = 0; i < zpl_array_count(obj->nodes); i++) {
zpl_adt_node *n = (obj->nodes+i);
if (origin == n) continue;

zpl__uri_print_str(f, n->name);

if (n->type == ZPL_ADT_TYPE_STRING) {
zpl_fprintf(f, "%c", '=');
zpl__uri_print_str(f, n->string);
} else if (n->type == ZPL_ADT_TYPE_INTEGER || n->type == ZPL_ADT_TYPE_REAL) {
if (n->props != ZPL_ADT_PROPS_TRUE) {
zpl_fprintf(f, "%c", '=');
// TODO: ensure the output is URI-encoded
zpl_adt_print_number(f, n);
}
}

if (i+1 < zpl_array_count(obj->nodes)) {
zpl_fprintf(f, "%s", "&");
}
}
}

void zpl_uri_free(zpl_adt_node *obj) {
zpl_adt_destroy_branch(obj);
}

zpl_string zpl_uri_write_string(zpl_allocator a, zpl_adt_node *obj) {
zpl_file tmp;
zpl_file_stream_new(&tmp, a);
zpl_uri_write(&tmp, obj);
zpl_isize fsize;
zpl_u8* buf = zpl_file_stream_buf(&tmp, &fsize);
zpl_string output = zpl_string_make_length(a, (char *)buf, fsize);
zpl_file_close(&tmp);
return output;
}

ZPL_END_C_DECLS

Loading

0 comments on commit e615951

Please sign in to comment.