From d2c2fbb83bcbdfa01f993c056754f345963e2161 Mon Sep 17 00:00:00 2001 From: John Viega Date: Tue, 9 Jul 2024 01:38:23 -0400 Subject: [PATCH] Initial table litmod done. --- include/con4m.h | 1 - include/con4m/datatypes/strings.h | 8 +- include/con4m/dict.h | 1 + include/con4m/gc.h | 2 +- include/con4m/grid.h | 68 +-------- include/con4m/string.h | 3 +- include/con4m/styledb.h | 4 + include/con4m/wrappers.h | 1 + src/con4m/compiler/check_pass.c | 125 +++++++++------- src/con4m/dict.c | 4 +- src/con4m/format.c | 8 -- src/con4m/grid.c | 228 +++++++++++++++++++++++++++--- src/con4m/object.c | 29 +++- src/con4m/streams.c | 2 - src/con4m/string.c | 219 ++++++++++++++++++++++------ src/con4m/styledb.c | 15 +- src/con4m/types.c | 16 ++- src/con4m/wrappers.c | 6 + src/tests/test.c | 111 +++++++++++++-- tests/rich.c4m | 70 +++++++++ todo/rich.c4m | 25 ---- 21 files changed, 703 insertions(+), 243 deletions(-) create mode 100644 tests/rich.c4m delete mode 100644 todo/rich.c4m diff --git a/include/con4m.h b/include/con4m.h index 20416f47..01107e61 100644 --- a/include/con4m.h +++ b/include/con4m.h @@ -2,7 +2,6 @@ // #define C4M_FULL_MEMCHECK // #define C4M_DEBUG -// #define C4M_GC_STATS // #define C4M_TRACE_GC // #define C4M_GCT_MOVE 1 diff --git a/include/con4m/datatypes/strings.h b/include/con4m/datatypes/strings.h index 9809c9f4..2573bb85 100644 --- a/include/con4m/datatypes/strings.h +++ b/include/con4m/datatypes/strings.h @@ -8,8 +8,8 @@ **/ #define C4M_STR_HASH_KEY_POINTER_OFFSET 0 -#define C4M_HASH_CACHE_OBJ_OFFSET (-4 * (int32_t)sizeof(uint64_t)) -#define C4M_HASH_CACHE_RAW_OFFSET (-2 * (int32_t)sizeof(uint64_t)) +#define C4M_HASH_CACHE_OBJ_OFFSET (-4 * (int32_t)sizeof(uint64_t)) +#define C4M_HASH_CACHE_RAW_OFFSET (-2 * (int32_t)sizeof(uint64_t)) typedef struct c4m_str_t { char *data; @@ -50,8 +50,8 @@ typedef enum { const struct c4m_internal_string_st _static_##id = { \ .base_data_type = (c4m_dt_info_t *)&c4m_base_type_info[C4M_T_UTF8], \ .concrete_type = (struct c4m_type_t *)&c4m_bi_types[C4M_T_UTF8], \ - .s.byte_len = sizeof(val), \ - .s.codepoints = sizeof(val), \ + .s.byte_len = sizeof(val) - 1, \ + .s.codepoints = sizeof(val) - 1, \ .s.styling = NULL, \ .s.data = val, \ }; \ diff --git a/include/con4m/dict.h b/include/con4m/dict.h index 89d21c4e..50c07a26 100644 --- a/include/con4m/dict.h +++ b/include/con4m/dict.h @@ -3,3 +3,4 @@ #include "con4m.h" #define c4m_dict(x, y) c4m_new(c4m_type_dict(x, y)) +c4m_dict_t *c4m_dict_copy(c4m_dict_t *dict); diff --git a/include/con4m/gc.h b/include/con4m/gc.h index 24e500a9..7cfba1ca 100644 --- a/include/con4m/gc.h +++ b/include/con4m/gc.h @@ -128,7 +128,7 @@ #ifndef C4M_DEFAULT_ARENA_SIZE // This is the size any test case that prints a thing grows to awfully fast. -#define C4M_DEFAULT_ARENA_SIZE (1 << 24) +#define C4M_DEFAULT_ARENA_SIZE (1 << 25) #endif // In the future, we would expect that a writer seeing the diff --git a/include/con4m/grid.h b/include/con4m/grid.h index d58a7644..d084f1f7 100644 --- a/include/con4m/grid.h +++ b/include/con4m/grid.h @@ -86,73 +86,7 @@ extern c4m_grid_t *c4m_grid_horizontal_flow(c4m_list_t *, char *, char *); -static inline void -c4m_grid_set_cell_contents(c4m_grid_t *g, int row, int col, c4m_obj_t item) -{ - c4m_renderable_t *cell; - - if (row >= g->num_rows) { - c4m_grid_expand_rows(g, row - (g->num_rows - 1)); - } - - switch (c4m_base_type(item)) { - case C4M_T_RENDERABLE: - cell = (c4m_renderable_t *)item; - break; - case C4M_T_GRID: { - c4m_grid_t *subobj = (c4m_grid_t *)item; - int tcells = subobj->num_rows * subobj->num_cols; - cell = subobj->self; - - for (int i = 0; i < tcells; i++) { - c4m_renderable_t *item = subobj->cells[i]; - if (item == NULL) { - continue; - } - c4m_obj_t sub = item->raw_item; - - if (c4m_base_type(sub) == C4M_T_GRID) { - c4m_layer_styles(g->self->current_style, - ((c4m_grid_t *)sub)->self->current_style); - } - } - - break; - } - case C4M_T_UTF8: - case C4M_T_UTF32: { - char *tag; - if (row < g->header_rows || col < g->header_cols) { - tag = c4m_get_th_tag(g); - } - else { - tag = c4m_get_td_tag(g); - } - - cell = c4m_new(c4m_type_renderable(), - c4m_kw("tag", - c4m_ka(tag), - "obj", - c4m_ka(item))); - break; - } - default: - abort(); - } - - c4m_layer_styles(g->self->current_style, cell->current_style); - c4m_install_renderable(g, cell, row, row + 1, col, col + 1); - if (row >= g->row_cursor) { - if (col + 1 == g->num_cols) { - g->row_cursor = row + 1; - g->col_cursor = 0; - } - else { - g->row_cursor = row; - g->col_cursor = col + 1; - } - } -} +extern void c4m_grid_set_cell_contents(c4m_grid_t *, int, int, c4m_obj_t); static inline void c4m_grid_add_cell(c4m_grid_t *grid, c4m_obj_t container) diff --git a/include/con4m/string.h b/include/con4m/string.h index 9940a5f0..a70fb559 100644 --- a/include/con4m/string.h +++ b/include/con4m/string.h @@ -13,7 +13,7 @@ extern c4m_utf8_t *c4m_utf8_repeat(c4m_codepoint_t, int64_t); extern c4m_utf32_t *c4m_utf32_repeat(c4m_codepoint_t, int64_t); extern c4m_utf32_t *_c4m_str_strip(const c4m_str_t *s, ...); extern c4m_str_t *_c4m_str_truncate(const c4m_str_t *s, int64_t, ...); -extern c4m_utf32_t *_c4m_str_join(c4m_list_t *, +extern c4m_str_t *_c4m_str_join(c4m_list_t *, const c4m_str_t *, ...); extern c4m_utf8_t *c4m_str_from_int(int64_t n); @@ -35,6 +35,7 @@ extern c4m_utf32_t *c4m_str_upper(c4m_str_t *); extern c4m_utf32_t *c4m_str_lower(c4m_str_t *); extern c4m_utf32_t *c4m_title_case(c4m_str_t *); extern c4m_str_t *c4m_str_pad(c4m_str_t *, int64_t); +extern c4m_utf8_t *c4m_str_to_hex(c4m_str_t *, bool); #define c4m_str_split(x, y) c4m_str_xsplit(x, y) // This is in richlit.c diff --git a/include/con4m/styledb.h b/include/con4m/styledb.h index 50e6db45..018cf455 100644 --- a/include/con4m/styledb.h +++ b/include/con4m/styledb.h @@ -11,6 +11,10 @@ c4m_copy_render_style(const c4m_render_style_t *style) { c4m_render_style_t *result = c4m_new(c4m_type_render_style()); + if (!style) { + return result; + } + memcpy(result, style, sizeof(c4m_render_style_t)); return result; diff --git a/include/con4m/wrappers.h b/include/con4m/wrappers.h index a06de684..305ef009 100644 --- a/include/con4m/wrappers.h +++ b/include/con4m/wrappers.h @@ -8,3 +8,4 @@ extern c4m_str_t *c4m_wrapper_os(); extern c4m_str_t *c4m_wrapper_arch(); extern c4m_str_t *c4m_wrapper_repr(c4m_obj_t); extern c4m_str_t *c4m_wrapper_to_str(c4m_obj_t); +extern void c4m_snap_column(c4m_grid_t *, int64_t); diff --git a/src/con4m/compiler/check_pass.c b/src/con4m/compiler/check_pass.c index a01c1420..97862a6a 100644 --- a/src/con4m/compiler/check_pass.c +++ b/src/con4m/compiler/check_pass.c @@ -222,7 +222,9 @@ type_check_node_vs_type_no_err(c4m_tree_node_t *n, c4m_type_t *t) } void -c4m_fold_container(c4m_tree_node_t *n, c4m_lit_info_t *li) +c4m_fold_container(c4m_tree_node_t *n, + c4m_lit_info_t *li, + c4m_list_t *item_types) { c4m_pnode_t *pn = c4m_get_pnode(n); @@ -234,10 +236,10 @@ c4m_fold_container(c4m_tree_node_t *n, c4m_lit_info_t *li) } } c4m_list_t *items = c4m_new(c4m_type_list(c4m_type_ref())); - c4m_list_t *tlist = li->type->details->items; + c4m_list_t *tlist = item_types; c4m_obj_t obj; - if (li->num_items == 1) { + if (li->num_items <= 1) { bool val_type = c4m_type_is_value_type(c4m_list_get(tlist, 0, NULL)); for (int i = 0; i < n->num_kids; i++) { @@ -251,8 +253,6 @@ c4m_fold_container(c4m_tree_node_t *n, c4m_lit_info_t *li) } } else { - c4m_list_t *item_types = li->type->details->items; - c4m_tuple_t *t = c4m_new(c4m_type_tuple_from_xlist(item_types)); for (int i = 0; i < n->num_kids; i++) { c4m_pnode_t *kid_pnode = c4m_get_pnode(n->children[i]); @@ -278,8 +278,9 @@ c4m_fold_container(c4m_tree_node_t *n, c4m_lit_info_t *li) static void calculate_container_type(pass2_ctx *ctx, c4m_tree_node_t *n) { - c4m_pnode_t *pn = c4m_get_pnode(n); - c4m_lit_info_t *li = (c4m_lit_info_t *)pn->extra_info; + c4m_pnode_t *pn = c4m_get_pnode(n); + c4m_lit_info_t *li = (c4m_lit_info_t *)pn->extra_info; + bool concrete = false; li->base_type = c4m_base_type_from_litmod(li->st, li->litmod); @@ -307,8 +308,14 @@ calculate_container_type(pass2_ctx *ctx, c4m_tree_node_t *n) return; } - li->type = c4m_new(c4m_type_typespec(), - li->base_type); + if (c4m_base_type_info[li->base_type].dt_kind == C4M_DT_KIND_primitive) { + li->type = c4m_bi_types[li->base_type]; + concrete = true; + } + else { + li->type = c4m_new(c4m_type_typespec(), + li->base_type); + } pn->type = li->type; if (pn->kind == c4m_nt_lit_empty_dict_or_set) { @@ -318,31 +325,41 @@ calculate_container_type(pass2_ctx *ctx, c4m_tree_node_t *n) return; } - c4m_list_t *items = li->type->details->items; - - switch (li->st) { - case ST_List: - li->num_items = 1; - break; + c4m_list_t *items; - case ST_Tuple: - li->num_items = n->num_kids; - break; + if (concrete) { + items = c4m_list(c4m_type_typespec()); + c4m_list_append(items, c4m_new_typevar()); + } + else { + items = li->type->details->items; + } - case ST_Dict: - if (pn->kind == c4m_nt_lit_set) { + if (!concrete) { + switch (li->st) { + case ST_List: li->num_items = 1; + break; + + case ST_Tuple: + li->num_items = n->num_kids; + break; + + case ST_Dict: + if (pn->kind == c4m_nt_lit_set) { + li->num_items = 1; + } + else { + li->num_items = 2; + } + break; + default: + c4m_unreachable(); } - else { - li->num_items = 2; - } - break; - default: - c4m_unreachable(); - } - for (int i = 0; i < li->num_items; i++) { - c4m_list_append(items, c4m_new_typevar()); + for (int i = 0; i < li->num_items; i++) { + c4m_list_append(items, c4m_new_typevar()); + } } for (int i = 0; i < n->num_kids; i++) { @@ -352,34 +369,40 @@ calculate_container_type(pass2_ctx *ctx, c4m_tree_node_t *n) ctx->node = n->children[i]; base_check_pass_dispatch(ctx); - if (merge_or_ret_ignore_err(t, kid_pnode->type)) { - if (c4m_can_coerce(kid_pnode->type, t)) { - c4m_lit_info_t *li = (c4m_lit_info_t *)kid_pnode->extra_info; - li->cast_to = t; - if (kid_pnode->value != NULL) { - kid_pnode->value = c4m_coerce(kid_pnode->value, - kid_pnode->type, - t); + if (!concrete) { + if (merge_or_ret_ignore_err(t, kid_pnode->type)) { + if (c4m_can_coerce(kid_pnode->type, t)) { + c4m_lit_info_t *li = kid_pnode->extra_info; + li->cast_to = t; + if (kid_pnode->value != NULL) { + kid_pnode->value = c4m_coerce(kid_pnode->value, + kid_pnode->type, + t); + } } - } - else { - char *p = (char *)c4m_base_type_info[li->base_type].name; - c4m_utf8_t *s = c4m_new_utf8(p); + else { + char *p = (char *)c4m_base_type_info[li->base_type].name; - c4m_add_error(ctx->file_ctx, - c4m_err_inconsistent_item_type, - n->children[i], - s, - t, - kid_pnode->type); - c4m_calculate_type_hash(li->type); - return; + c4m_utf8_t *s = c4m_new_utf8(p); + + c4m_add_error(ctx->file_ctx, + c4m_err_inconsistent_item_type, + n->children[i], + s, + t, + kid_pnode->type); + c4m_calculate_type_hash(li->type); + return; + } } } } - c4m_calculate_type_hash(li->type); - c4m_fold_container(n, li); + if (!concrete) { + c4m_calculate_type_hash(li->type); + } + + c4m_fold_container(n, li, items); } // This maps names to how many arguments the function takes. If diff --git a/src/con4m/dict.c b/src/con4m/dict.c index 719c897e..77c1c6fd 100644 --- a/src/con4m/dict.c +++ b/src/con4m/dict.c @@ -262,7 +262,7 @@ dict_coerce_to(c4m_dict_t *dict, c4m_type_t *dst_type) } c4m_dict_t * -dict_copy(c4m_dict_t *dict) +c4m_dict_copy(c4m_dict_t *dict) { return dict_coerce_to(dict, c4m_get_my_type(dict)); } @@ -343,7 +343,7 @@ const c4m_vtable_t c4m_dict_vtable = { [C4M_BI_UNMARSHAL] = (c4m_vtable_entry)c4m_dict_unmarshal, [C4M_BI_COERCIBLE] = (c4m_vtable_entry)dict_can_coerce_to, [C4M_BI_COERCE] = (c4m_vtable_entry)dict_coerce_to, - [C4M_BI_COPY] = (c4m_vtable_entry)dict_copy, + [C4M_BI_COPY] = (c4m_vtable_entry)c4m_dict_copy, [C4M_BI_ADD] = (c4m_vtable_entry)dict_plus, [C4M_BI_LEN] = (c4m_vtable_entry)dict_len, [C4M_BI_INDEX_GET] = (c4m_vtable_entry)dict_get, diff --git a/src/con4m/format.c b/src/con4m/format.c index 03b7181e..01c722d7 100644 --- a/src/con4m/format.c +++ b/src/con4m/format.c @@ -492,14 +492,6 @@ assemble_formatted_result(const c4m_str_t *fmt, c4m_list_t *arg_strings) arg = c4m_to_utf32(arg); argp = (c4m_codepoint_t *)arg->data; - // If we have a null string and there's a style around it, - // things are broken without this kludge. But to be quite - // honest, I don't understand why it's broken or why this - // kludge works?? - if (alen == 0) { - alen = 1; - } - style_adjustment(result, out_ix, alen - 2); for (int64_t j = 0; j < alen; j++) { diff --git a/src/con4m/grid.c b/src/con4m/grid.c index 9f818569..255e4a86 100644 --- a/src/con4m/grid.c +++ b/src/con4m/grid.c @@ -409,9 +409,6 @@ get_col_props(c4m_grid_t *grid, int col) return c4m_lookup_cell_style("td"); } -// Contents currently must be a list[list[c4m_obj_t]]. Supply -// properties separately; if you want something that spans you should -// instead void c4m_grid_set_all_contents(c4m_grid_t *g, c4m_list_t *rows) { @@ -1683,6 +1680,74 @@ c4m_grid_to_str(c4m_grid_t *g) c4m_kw("add_trailing", c4m_ka(true))); } +void +c4m_grid_set_cell_contents(c4m_grid_t *g, int row, int col, c4m_obj_t item) +{ + c4m_renderable_t *cell; + + if (row >= g->num_rows) { + c4m_grid_expand_rows(g, row - (g->num_rows - 1)); + } + + switch (c4m_base_type(item)) { + case C4M_T_RENDERABLE: + cell = (c4m_renderable_t *)item; + break; + case C4M_T_GRID: { + c4m_grid_t *subobj = (c4m_grid_t *)item; + int tcells = subobj->num_rows * subobj->num_cols; + cell = subobj->self; + + for (int i = 0; i < tcells; i++) { + c4m_renderable_t *item = subobj->cells[i]; + if (item == NULL) { + continue; + } + c4m_obj_t sub = item->raw_item; + + if (c4m_base_type(sub) == C4M_T_GRID) { + c4m_layer_styles(g->self->current_style, + ((c4m_grid_t *)sub)->self->current_style); + } + } + + break; + } + case C4M_T_UTF8: + case C4M_T_UTF32: { + char *tag; + if (row < g->header_rows || col < g->header_cols) { + tag = c4m_get_th_tag(g); + } + else { + tag = c4m_get_td_tag(g); + } + + cell = c4m_new(c4m_type_renderable(), + c4m_kw("tag", + c4m_ka(tag), + "obj", + c4m_ka(item))); + break; + } + default: + C4M_CRAISE("Item passed to grid is not renderable."); + } + + c4m_layer_styles(g->self->current_style, cell->current_style); + c4m_install_renderable(g, cell, row, row + 1, col, col + 1); + if (row >= g->row_cursor) { + if (col + 1 == g->num_cols) { + g->row_cursor = row + 1; + g->col_cursor = 0; + } + else { + g->row_cursor = row; + g->col_cursor = col + 1; + } + } +} + c4m_grid_t * _c4m_ordered_list(c4m_list_t *items, ...) { @@ -1903,6 +1968,68 @@ c4m_grid_marshal(c4m_grid_t *grid, c4m_sub_marshal(grid->self, s, memos, mid); } +static c4m_renderable_t * +c4m_renderable_copy(c4m_renderable_t *renderable) +{ + return renderable; +} + +static c4m_dict_t * +copy_props(c4m_dict_t *old) +{ + uint64_t n; + c4m_dict_t *res = c4m_dict(c4m_type_int(), c4m_type_ref()); + hatrack_dict_item_t *view = hatrack_dict_items_sort(old, &n); + + for (uint64_t i = 0; i < n; i++) { + hatrack_dict_item_t item = view[i]; + + hatrack_dict_add(res, item.key, item.value); + } + + return res; +} + +static c4m_grid_t * +c4m_grid_copy(c4m_grid_t *orig) +{ + c4m_grid_t *result = c4m_new(c4m_type_grid(), + c4m_kw("start_rows", + c4m_ka(orig->num_rows), + "start_cols", + c4m_ka(orig->num_cols), + "spare_rows", + c4m_ka(orig->spare_rows), + "header_rows", + c4m_ka(orig->header_rows), + "header_cols", + c4m_ka(orig->header_cols), + "stripe", + c4m_ka(orig->stripe))); + result->width = orig->width; + result->height = orig->height; + result->td_tag_name = orig->td_tag_name; + result->th_tag_name = orig->th_tag_name; + result->col_props = copy_props(orig->col_props); + result->row_props = copy_props(orig->row_props); + + size_t num_cells = (orig->num_rows + orig->spare_rows) * orig->num_cols; + result->cells = c4m_gc_array_alloc(c4m_renderable_t *, num_cells); + num_cells = orig->num_rows * orig->num_cols; + + for (unsigned int i = 0; i < num_cells; i++) { + c4m_renderable_t *r = orig->cells[i]; + + if (r) { + result->cells[i] = c4m_renderable_copy(r); + } + } + + result->self = c4m_renderable_copy(orig->self); + + return result; +} + static void c4m_grid_unmarshal(c4m_grid_t *grid, c4m_stream_t *s, c4m_dict_t *memos) { @@ -1933,6 +2060,12 @@ c4m_grid_unmarshal(c4m_grid_t *grid, c4m_stream_t *s, c4m_dict_t *memos) grid->self = c4m_sub_unmarshal(s, memos); } +extern void +c4m_style_marshal(c4m_render_style_t *obj, + c4m_stream_t *s, + c4m_dict_t *memos, + int64_t *mid); + static void c4m_renderable_marshal(c4m_renderable_t *r, c4m_stream_t *s, @@ -1941,7 +2074,7 @@ c4m_renderable_marshal(c4m_renderable_t *r, { c4m_sub_marshal(r->raw_item, s, memos, mid); c4m_marshal_cstring(r->container_tag, s); - c4m_sub_marshal(r->current_style, s, memos, mid); + c4m_style_marshal(r->current_style, s, memos, mid); c4m_marshal_u16(r->start_col, s); c4m_marshal_u16(r->start_row, s); c4m_marshal_u16(r->end_col, s); @@ -1951,14 +2084,20 @@ c4m_renderable_marshal(c4m_renderable_t *r, c4m_marshal_u16(r->render_height, s); } +extern void +c4m_style_unmarshal(c4m_render_style_t *obj, + c4m_stream_t *s, + c4m_dict_t *memos); + static void c4m_renderable_unmarshal(c4m_renderable_t *r, c4m_stream_t *s, c4m_dict_t *memos) { - r->raw_item = c4m_sub_unmarshal(s, memos); - r->container_tag = c4m_unmarshal_cstring(s); - r->current_style = c4m_sub_unmarshal(s, memos); + r->raw_item = c4m_sub_unmarshal(s, memos); + r->container_tag = c4m_unmarshal_cstring(s); + c4m_render_style_t *rs = c4m_new(c4m_type_render_style()); + c4m_style_unmarshal(rs, s, memos); r->start_col = c4m_unmarshal_u16(s); r->start_row = c4m_unmarshal_u16(s); r->end_col = c4m_unmarshal_u16(s); @@ -2116,8 +2255,7 @@ void c4m_set_column_props(c4m_grid_t *grid, int col, c4m_render_style_t *s) { if (grid->col_props == NULL) { - grid->col_props = c4m_new(c4m_type_dict(c4m_type_int(), - c4m_type_ref())); + grid->col_props = c4m_dict(c4m_type_int(), c4m_type_ref()); } hatrack_dict_put(grid->col_props, (void *)(int64_t)col, s); @@ -2127,8 +2265,7 @@ void c4m_set_row_props(c4m_grid_t *grid, int row, c4m_render_style_t *s) { if (grid->row_props == NULL) { - grid->row_props = c4m_new(c4m_type_dict(c4m_type_int(), - c4m_type_ref())); + grid->row_props = c4m_dict(c4m_type_int(), c4m_type_ref()); } hatrack_dict_put(grid->row_props, (void *)(int64_t)row, s); @@ -2253,17 +2390,73 @@ c4m_renderable_set_gc_bits(uint64_t *bitfield, int alloc_words) *bitfield |= (0x0f << ix); } +static c4m_grid_t * +c4m_to_grid_lit(c4m_type_t *objtype, c4m_list_t *items, c4m_utf8_t *litmod) +{ + if (!strcmp(litmod->data, "table")) { + int nrows = c4m_list_len(items); + int ncols = 0; + + if (!nrows) { + return c4m_new(c4m_type_grid(), + c4m_kw("start_rows", + c4m_ka(0), + "start_cols", + c4m_ka(0))); + } + + c4m_type_t *t = c4m_get_my_type(c4m_list_get(items, 0, NULL)); + if (c4m_types_are_compat(t, c4m_type_utf8(), NULL)) { + C4M_CRAISE("Not implemented yet."); + } + if (!c4m_types_are_compat(t, + c4m_type_list(c4m_type_utf8()), + NULL)) { + C4M_CRAISE("Currently only strings are supported in tables."); + } + for (int i = 0; i < nrows; i++) { + c4m_list_t *l = c4m_list_get(items, i, NULL); + int len = c4m_list_len(l); + + if (len > ncols) { + ncols = len; + } + } + + c4m_grid_t *result = c4m_new(c4m_type_grid(), + c4m_kw("start_rows", + c4m_ka(nrows), + "start_cols", + c4m_ka(ncols), + "header_rows", + c4m_ka(1), + "stripe", + c4m_ka(true))); + + for (int i = 0; i < nrows; i++) { + c4m_list_t *l = c4m_list_get(items, i, NULL); + c4m_grid_add_row(result, l); + } + + return result; + } + + C4M_CRAISE("Not implemented yet."); +} + const c4m_vtable_t c4m_grid_vtable = { .num_entries = C4M_BI_NUM_FUNCS, .methods = { - [C4M_BI_CONSTRUCTOR] = (c4m_vtable_entry)grid_init, - [C4M_BI_TO_STR] = (c4m_vtable_entry)c4m_grid_to_str, - [C4M_BI_MARSHAL] = (c4m_vtable_entry)c4m_grid_marshal, - [C4M_BI_UNMARSHAL] = (c4m_vtable_entry)c4m_grid_unmarshal, - [C4M_BI_GC_MAP] = (c4m_vtable_entry)c4m_grid_set_gc_bits, + [C4M_BI_CONSTRUCTOR] = (c4m_vtable_entry)grid_init, + [C4M_BI_TO_STR] = (c4m_vtable_entry)c4m_grid_to_str, + [C4M_BI_MARSHAL] = (c4m_vtable_entry)c4m_grid_marshal, + [C4M_BI_UNMARSHAL] = (c4m_vtable_entry)c4m_grid_unmarshal, + [C4M_BI_GC_MAP] = (c4m_vtable_entry)c4m_grid_set_gc_bits, + [C4M_BI_CONTAINER_LIT] = (c4m_vtable_entry)c4m_to_grid_lit, + [C4M_BI_COPY] = (c4m_vtable_entry)c4m_grid_copy, // Explicit because some compilers don't seem to always properly // zero it (Was sometimes crashing on a `c4m_stream_t` on my mac). - [C4M_BI_FINALIZER] = NULL, + [C4M_BI_FINALIZER] = NULL, }, }; @@ -2274,6 +2467,7 @@ const c4m_vtable_t c4m_renderable_vtable = { [C4M_BI_GC_MAP] = (c4m_vtable_entry)c4m_renderable_set_gc_bits, [C4M_BI_MARSHAL] = (c4m_vtable_entry)c4m_renderable_marshal, [C4M_BI_UNMARSHAL] = (c4m_vtable_entry)c4m_renderable_unmarshal, + [C4M_BI_COPY] = (c4m_vtable_entry)c4m_renderable_copy, [C4M_BI_FINALIZER] = NULL, }, }; diff --git a/src/con4m/object.c b/src/con4m/object.c index 92c4b775..7e11458b 100644 --- a/src/con4m/object.c +++ b/src/con4m/object.c @@ -557,7 +557,13 @@ c4m_copy_object(c4m_obj_t obj) c4m_copy_fn ptr = (c4m_copy_fn)c4m_vtable(obj)->methods[C4M_BI_COPY]; if (ptr == NULL) { - C4M_CRAISE("Copying for this object type not currently supported."); + c4m_utf8_t *err; + + err = c4m_cstr_format( + "Copying for '{}' objects is not " + "currently supported.", + c4m_get_my_type(obj)); + C4M_RAISE(err); } return (*ptr)(obj); @@ -818,8 +824,6 @@ c4m_lt(c4m_type_t *t, c4m_obj_t o1, c4m_obj_t o2) bool c4m_gt(c4m_type_t *t, c4m_obj_t o1, c4m_obj_t o2) { - c4m_print(t); - c4m_dt_info_t *info = c4m_type_get_data_type_info(t); c4m_vtable_t *vtbl = (c4m_vtable_t *)info->vtable; c4m_cmp_fn ptr = (c4m_cmp_fn)vtbl->methods[C4M_BI_GT]; @@ -898,10 +902,25 @@ c4m_container_literal(c4m_type_t *t, c4m_list_t *items, c4m_utf8_t *mod) ptr = (c4m_container_lit_fn)vtbl->methods[C4M_BI_CONTAINER_LIT]; if (ptr == NULL) { - C4M_CRAISE("Improper implementation; no literal fn defined."); + c4m_utf8_t *err = c4m_cstr_format( + "Improper implementation; no literal fn " + "defined for type '{}'.", + c4m_new_utf8(info->name)); + C4M_RAISE(err); } - return (*ptr)(t, items, mod); + c4m_obj_t result = (*ptr)(t, items, mod); + + if (result == NULL) { + c4m_utf8_t *err = c4m_cstr_format( + "Improper implementation; type '{}' did not instantiate " + "a literal for the literal modifier '{}'", + c4m_new_utf8(info->name), + mod); + C4M_RAISE(err); + } + + return result; } void diff --git a/src/con4m/streams.c b/src/con4m/streams.c index db779c9e..08a134e9 100644 --- a/src/con4m/streams.c +++ b/src/con4m/streams.c @@ -424,7 +424,6 @@ c4m_stream_read_all(c4m_stream_t *stream) break; } } - c4m_list_append(l, one); } if (outkind) { @@ -492,7 +491,6 @@ void c4m_stream_write_object(c4m_stream_t *stream, c4m_obj_t obj, bool ansi) { if (stream->flags & C4M_F_STREAM_CLOSED) { - abort(); C4M_CRAISE("Stream is already closed."); } diff --git a/src/con4m/string.c b/src/con4m/string.c index 33a41e9a..fe217943 100644 --- a/src/con4m/string.c +++ b/src/con4m/string.c @@ -15,8 +15,9 @@ c4m_internal_utf8_set_codepoint_count(c4m_utf8_t *instr) while (p < end) { instr->codepoints += 1; int n = utf8proc_iterate(p, 4, &cp); - if (n < 1) { - abort(); + if (n < 0) { + // Assume we have a partial code point at the end. + return; } p += n; } @@ -177,7 +178,7 @@ c4m_utf8_index(const c4m_utf8_t *s, int64_t n) for (int i = 0; i <= n; i++) { int val = utf8proc_iterate((uint8_t *)p, 4, &cp); if (val < 0) { - abort(); + C4M_CRAISE("Index out of bounds."); } p += val; } @@ -218,6 +219,35 @@ c4m_index(const c4m_str_t *s, int64_t i) } } +static char c4m_hex_map_lower[16] = "0123456789abcdef"; +static char c4m_hex_map_upper[16] = "0123456789ABCDEF"; + +c4m_utf8_t * +c4m_str_to_hex(c4m_str_t *s, bool upper) +{ + s = c4m_to_utf8(s); + + c4m_utf8_t *result = c4m_new(c4m_type_utf8(), + c4m_kw("length", + c4m_ka(s->byte_len * 2))); + char *map = upper ? c4m_hex_map_upper : c4m_hex_map_lower; + + char *src = s->data; + char *dst = result->data; + + for (int i = 0; i < s->byte_len; i++) { + char c = *src++; + *dst++ = map[c >> 4]; + *dst++ = map[c & 0x0f]; + } + *dst = 0; + + result->byte_len = 2 * s->byte_len; + result->codepoints = 2 * s->byte_len; + + return result; +} + c4m_utf32_t * _c4m_str_strip(const c4m_str_t *s, ...) { @@ -270,7 +300,9 @@ c4m_str_copy(const c4m_str_t *s) c4m_kw("length", c4m_ka(l))); res->codepoints = s->codepoints; - memcpy(res->data, s->data, s->byte_len); + if (s->byte_len) { + memcpy(res->data, s->data, s->byte_len + 1); + } c4m_copy_style_info(s, res); res->byte_len = s->byte_len; @@ -340,35 +372,75 @@ c4m_str_concat(const c4m_str_t *p1, const c4m_str_t *p2) return r; } -c4m_utf32_t * -_c4m_str_join(c4m_list_t *l, const c4m_str_t *joiner, ...) +static c4m_utf8_t * +c4m_utf8_join(c4m_list_t *l, const c4m_utf8_t *joiner, bool add_trailing) { - c4m_karg_only_init(joiner); + int64_t num_items = c4m_list_len(l); + int64_t new_len = 0; + int jlen = 0; + c4m_list_t *tmplist = c4m_new(c4m_type_list(c4m_type_utf8()), + c4m_kw("length", c4m_ka(num_items))); - bool add_trailing = false; + for (int i = 0; i < num_items; i++) { + c4m_str_t *s = c4m_to_utf8(c4m_list_get(l, i, NULL)); + new_len += s->byte_len; + c4m_list_append(tmplist, s); + } - c4m_kw_bool("add_trailing", add_trailing); + if (joiner != NULL) { + jlen = joiner->byte_len; + new_len += jlen * (num_items - (add_trailing ? 0 : 1)); + } - int64_t n_parts = c4m_list_len(l); - int64_t n_styles = 0; - int64_t joinlen = joiner ? c4m_str_codepoint_len(joiner) : 0; - int64_t len = joinlen * n_parts; // An overestimate when !add_trailing + c4m_utf8_t *result = c4m_new(c4m_type_utf8(), + c4m_kw("length", c4m_ka(new_len))); + char *p = result->data; + c4m_utf8_t *cur = c4m_list_get(tmplist, 0, NULL); + + memcpy(p, cur->data, cur->byte_len); + + for (int i = 1; i < num_items; i++) { + p += cur->byte_len; + + if (jlen != 0) { + memcpy(p, joiner->data, jlen); + p += jlen; + } + + cur = c4m_list_get(tmplist, i, NULL); + memcpy(p, cur->data, cur->byte_len); + } + + if (add_trailing && jlen != 0) { + p += cur->byte_len; + memcpy(p, joiner->data, jlen); + } + + result->byte_len = new_len; + result->data[new_len] = 0; + c4m_internal_utf8_set_codepoint_count(result); + + return result; +} + +static c4m_utf32_t * +c4m_utf32_join(c4m_list_t *l, const c4m_utf32_t *joiner, bool add_trailing) +{ + int64_t n_parts = c4m_list_len(l); + int64_t joinlen = joiner ? c4m_str_codepoint_len(joiner) : 0; + int64_t len = joinlen * n_parts; // An overestimate when !add_trailing for (int i = 0; i < n_parts; i++) { c4m_str_t *part = (c4m_str_t *)c4m_list_get(l, i, NULL); len += c4m_str_codepoint_len(part); - n_styles += c4m_style_num_entries(part); } - c4m_utf32_t *result = c4m_new(c4m_type_utf32(), + c4m_utf32_t *result = c4m_new(c4m_type_utf32(), c4m_kw("length", c4m_ka(len))); - c4m_codepoint_t *p = (c4m_codepoint_t *)result->data; - int txt_ix = 0; - int style_ix = 0; - c4m_utf32_t *j = joinlen ? c4m_to_utf32(joiner) : NULL; + c4m_codepoint_t *p = (c4m_codepoint_t *)result->data; + c4m_utf32_t *j = joinlen ? c4m_to_utf32(joiner) : NULL; result->codepoints = len; - c4m_alloc_styles(result, n_styles); if (!add_trailing) { --n_parts; // skip the last item during the loop. @@ -384,14 +456,10 @@ _c4m_str_join(c4m_list_t *l, const c4m_str_t *joiner, ...) } memcpy(p, part->data, n_cp * 4); p += n_cp; - style_ix = c4m_copy_and_offset_styles(part, result, style_ix, txt_ix); - txt_ix += n_cp; if (joinlen != 0) { memcpy(p, j->data, joinlen * 4); p += joinlen; - style_ix = c4m_copy_and_offset_styles(j, result, style_ix, txt_ix); - txt_ix += joinlen; } } @@ -405,11 +473,66 @@ _c4m_str_join(c4m_list_t *l, const c4m_str_t *joiner, ...) int64_t n_cp = c4m_str_codepoint_len(line); memcpy(p, line->data, n_cp * 4); - style_ix = c4m_copy_and_offset_styles(line, result, style_ix, txt_ix); } return result; } +c4m_str_t * +_c4m_str_join(c4m_list_t *l, const c4m_str_t *joiner, ...) +{ + c4m_karg_only_init(joiner); + + bool add_trailing = false; + c4m_str_t *result; + + c4m_kw_bool("add_trailing", add_trailing); + + if (joiner && joiner->utf32) { + result = c4m_utf32_join(l, joiner, add_trailing); + } + else { + result = c4m_utf8_join(l, joiner, add_trailing); + } + + int64_t n_parts = c4m_list_len(l); + int64_t n_styles = 0; + int64_t joinlen = joiner ? c4m_str_codepoint_len(joiner) : 0; + + for (int i = 0; i < n_parts; i++) { + c4m_str_t *part = (c4m_str_t *)c4m_list_get(l, i, NULL); + n_styles += c4m_style_num_entries(part); + } + + int txt_ix = 0; + int style_ix = 0; + + c4m_alloc_styles(result, n_styles); + + for (int i = 0; i < n_parts; i++) { + c4m_str_t *part = (c4m_str_t *)c4m_list_get(l, i, NULL); + int64_t n_cp = c4m_str_codepoint_len(part); + + if (!n_cp) { + continue; + } + style_ix = c4m_copy_and_offset_styles(part, result, style_ix, txt_ix); + txt_ix += n_cp; + + if (joinlen != 0) { + if (i + 1 == n_parts && !add_trailing) { + break; + } + style_ix = c4m_copy_and_offset_styles((c4m_str_t *)joiner, + result, + style_ix, + txt_ix); + txt_ix += joinlen; + } + } + + return result; +} + c4m_utf8_t * c4m_to_utf8(const c4m_utf32_t *inp) { @@ -425,7 +548,7 @@ c4m_to_utf8(const c4m_utf32_t *inp) // cases where UTF8 codepoints are above U+00ff. But nbd. c4m_utf8_t *res = c4m_new(c4m_type_utf8(), - c4m_kw("length", c4m_ka(inp->byte_len))); + c4m_kw("length", c4m_ka(inp->byte_len + 1))); c4m_codepoint_t *p = (c4m_codepoint_t *)inp->data; uint8_t *outloc = (uint8_t *)res->data; @@ -472,7 +595,8 @@ c4m_to_utf32(const c4m_utf8_t *instr) for (int i = 0; i < len; i++) { int val = utf8proc_iterate(inp, 4, outp + i); if (val < 0) { - abort(); + printf("i = %d\n", i); + C4M_CRAISE("Invalid utf8 in string when convering to utf32."); } inp += val; } @@ -731,7 +855,7 @@ c4m_str_render_len(const c4m_str_t *s) int val = utf8proc_iterate(p, 4, &cp); if (val < 0) { - abort(); + return result; } p += val; @@ -781,7 +905,7 @@ _c4m_str_truncate(const c4m_str_t *s, int64_t len, ...) for (int i = 0; i < n; i++) { int val = utf8proc_iterate(p, 4, &cp); if (val < 0) { - abort(); + break; } next = p + val; int w = c4m_codepoint_width(cp); @@ -830,7 +954,7 @@ _c4m_str_truncate(const c4m_str_t *s, int64_t len, ...) } int val = utf8proc_iterate(p, 4, &cp); if (val < 0) { - abort(); + break; } p += val; } @@ -1290,9 +1414,12 @@ c4m_str_lit(c4m_utf8_t *s_u8, char *s = s_u8->data; char *litmod = lit_u8->data; - if (*litmod == 0 || !strcmp(litmod, "u8") || !strcmp(litmod, "utf8")) { + // clang-format off + if (!litmod || *litmod == 0 || + !strcmp(litmod, "u8") || !strcmp(litmod, "utf8")) { return s_u8; } + // clang-format on if (!strcmp(litmod, "u32") || !strcmp(litmod, "utf32")) { return c4m_to_utf32(s_u8); @@ -1403,16 +1530,16 @@ c4m_str_upper(c4m_str_t *s) result = c4m_str_copy(s); } - c4m_codepoint_t *from = (c4m_codepoint_t *)s->data; - c4m_codepoint_t *to = (c4m_codepoint_t *)result->data; - int64_t n = c4m_str_codepoint_len(s); + c4m_codepoint_t *p = (c4m_codepoint_t *)result->data; + int64_t n = c4m_str_codepoint_len(s); for (int i = 0; i < n; i++) { - *to++ = utf8proc_toupper(*from++); + *p = utf8proc_toupper(*p); + p++; } result->codepoints = n; - return result; + return c4m_to_utf8(result); } c4m_utf32_t * @@ -1426,16 +1553,16 @@ c4m_str_lower(c4m_str_t *s) result = c4m_str_copy(s); } - c4m_codepoint_t *from = (c4m_codepoint_t *)s->data; - c4m_codepoint_t *to = (c4m_codepoint_t *)result->data; - int64_t n = c4m_str_codepoint_len(s); + c4m_codepoint_t *p = (c4m_codepoint_t *)result->data; + int64_t n = c4m_str_codepoint_len(s); for (int i = 0; i < n; i++) { - *to++ = utf8proc_tolower(*from++); + *p = utf8proc_tolower(*p); + p++; } result->codepoints = n; - return result; + return c4m_to_utf8(result); } c4m_utf32_t * @@ -1449,16 +1576,16 @@ c4m_str_title_case(c4m_str_t *s) result = c4m_str_copy(s); } - c4m_codepoint_t *from = (c4m_codepoint_t *)s->data; - c4m_codepoint_t *to = (c4m_codepoint_t *)result->data; - int64_t n = c4m_str_codepoint_len(s); + c4m_codepoint_t *p = (c4m_codepoint_t *)result->data; + int64_t n = c4m_str_codepoint_len(s); for (int i = 0; i < n; i++) { - *to++ = utf8proc_totitle(*from++); + *p = utf8proc_totitle(*p); + p++; } result->codepoints = n; - return result; + return c4m_to_utf8(result); } c4m_str_t * diff --git a/src/con4m/styledb.c b/src/con4m/styledb.c index 61c54114..4795603d 100644 --- a/src/con4m/styledb.c +++ b/src/con4m/styledb.c @@ -634,7 +634,7 @@ c4m_layer_styles(const c4m_render_style_t *base, c4m_render_style_t *cur) } } -static void +void c4m_style_marshal(c4m_render_style_t *obj, c4m_stream_t *s, c4m_dict_t *memos, @@ -645,7 +645,12 @@ c4m_style_marshal(c4m_render_style_t *obj, flags = (obj->pad_color_set << 6) | (obj->disable_wrap << 5) | (obj->tpad_set << 4) | (obj->bpad_set << 3) | (obj->lpad_set << 2) | (obj->rpad_set << 1) | obj->hang_set; c4m_marshal_cstring(obj->name, s); - c4m_marshal_cstring(obj->border_theme->name, s); + if (obj->border_theme) { + c4m_marshal_cstring(obj->border_theme->name, s); + } + else { + c4m_marshal_cstring("", s); + } c4m_marshal_u64(obj->base_style, s); c4m_marshal_i32(obj->pad_color, s); c4m_marshal_u64(obj->dims.units, s); @@ -660,7 +665,7 @@ c4m_style_marshal(c4m_render_style_t *obj, c4m_marshal_u8(flags, s); } -static void +void c4m_style_unmarshal(c4m_render_style_t *obj, c4m_stream_t *s, c4m_dict_t *memos) @@ -691,7 +696,9 @@ c4m_style_unmarshal(c4m_render_style_t *obj, obj->rpad_set = (flags >> 1) & 0x01; obj->hang_set = flags & 0x01; - c4m_set_border_theme(obj, theme); + if (theme && theme[0] != 0) { + c4m_set_border_theme(obj, theme); + } } bool diff --git a/src/con4m/types.c b/src/con4m/types.c index b4da933b..4ac50357 100644 --- a/src/con4m/types.c +++ b/src/con4m/types.c @@ -1763,8 +1763,22 @@ c4m_initialize_global_types() } #endif +c4m_type_t * +_c4m_type_list(c4m_type_t *sub, char *file, int line) +{ + c4m_type_t *result = _c4m_new(file, + line, + c4m_type_typespec(), + C4M_T_LIST); + c4m_list_t *items = result->details->items; + c4m_list_append(items, sub); + type_hash_and_dedupe(&result); + + return result; +} + DECLARE_ONE_PARAM_FN(flist, C4M_T_FLIST); -DECLARE_ONE_PARAM_FN(list, C4M_T_XLIST); +// DECLARE_ONE_PARAM_FN(list, C4M_T_XLIST); DECLARE_ONE_PARAM_FN(xlist, C4M_T_XLIST); DECLARE_ONE_PARAM_FN(tree, C4M_T_TREE); DECLARE_ONE_PARAM_FN(queue, C4M_T_QUEUE); diff --git a/src/con4m/wrappers.c b/src/con4m/wrappers.c index 3ea503dd..591a0736 100644 --- a/src/con4m/wrappers.c +++ b/src/con4m/wrappers.c @@ -51,3 +51,9 @@ c4m_wrapper_arch() return c4m_new_utf8(info.machine); } + +void +c4m_snap_column(c4m_grid_t *table, int64_t n) +{ + c4m_set_column_style(table, n, "snap"); +} diff --git a/src/tests/test.c b/src/tests/test.c index 21778d85..070711c0 100644 --- a/src/tests/test.c +++ b/src/tests/test.c @@ -9,6 +9,7 @@ typedef struct { c4m_utf8_t *expected_output; c4m_list_t *expected_errors; bool ignore_output; + bool in_hex; } c4m_test_kat; static void @@ -19,20 +20,85 @@ err_basic_usage(c4m_utf8_t *fname) "string may have 0 or 1 [em]$output[/] sections and 0 or 1 " "[em]$errors[/] sections ONLY. If neither are provided, " "then the harness expects no errors and ignores output. " - "There may be nothing else in the doc string except whitespace.", + "There may be nothing else in the doc string except whitespace." + "Also, instead of [em]$output[/] you may add a [em]$hex[/] section, " + "where the contents must be raw hex bytes.", fname); c4m_printf( "\n[i inv]Note: If you want to explicitly test for no output, then " "provide `$output:` with nothing following.\n"); } +static c4m_utf8_t * +process_hex(c4m_utf32_t *s) +{ + c4m_utf8_t *res = c4m_to_utf8(s); + + int n = 0; + + for (int i = 0; i < res->byte_len; i++) { + char c = res->data[i]; + + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + res->data[n++] = c; + continue; + case 'A': + res->data[n++] = 'a'; + continue; + case 'B': + res->data[n++] = 'b'; + continue; + case 'C': + res->data[n++] = 'c'; + continue; + case 'D': + res->data[n++] = 'd'; + continue; + case 'E': + res->data[n++] = 'e'; + continue; + case 'F': + res->data[n++] = 'f'; + continue; + default: + continue; + } + } + res->data[n] = 0; + res->byte_len = n; + res->codepoints = n; + + return res; +} + static void -extract_output(c4m_test_kat *kat, c4m_utf32_t *s, int64_t start, int64_t end) +extract_output(c4m_test_kat *kat, + c4m_utf32_t *s, + int64_t start, + int64_t end, + bool hex) { s = c4m_str_slice(s, start, end); s = c4m_str_strip(s); s = c4m_to_utf8(s); - kat->expected_output = s; + kat->expected_output = kat->in_hex ? process_hex(s) : s; + kat->in_hex = hex; } static void @@ -62,8 +128,16 @@ c4m_parse_kat(c4m_str_t *path, c4m_str_t *s) c4m_test_kat *result = c4m_gc_alloc(c4m_test_kat); c4m_utf8_t *output = c4m_new_utf8("$output:"); c4m_utf8_t *errors = c4m_new_utf8("$errors:"); + c4m_utf8_t *hex = c4m_new_utf8("$hex:"); int64_t outix = c4m_str_find(s, output); int64_t errix = c4m_str_find(s, errors); + int64_t hexix = c4m_str_find(s, hex); + bool ishex = false; + + if (hexix != -1) { + ishex = true; + outix = hexix; + } if (outix == -1 && errix == -1) { if (c4m_str_codepoint_len(s) != 0) { @@ -88,7 +162,7 @@ c4m_parse_kat(c4m_str_t *path, c4m_str_t *s) err_basic_usage(path); return NULL; } - extract_output(result, s, 9, c4m_str_codepoint_len(s)); + extract_output(result, s, 9, c4m_str_codepoint_len(s), ishex); return result; } @@ -98,12 +172,12 @@ c4m_parse_kat(c4m_str_t *path, c4m_str_t *s) } if (errix != 0) { - extract_output(result, s, 9, errix); + extract_output(result, s, 9, errix, ishex); extract_errors(result, s, errix + 9, c4m_str_codepoint_len(s)); } else { extract_errors(result, s, 9, outix); - extract_output(result, s, outix + 9, c4m_str_codepoint_len(s)); + extract_output(result, s, outix + 9, c4m_str_codepoint_len(s), ishex); } return result; @@ -335,6 +409,20 @@ show_err_diffs(c4m_utf8_t *fname, c4m_list_t *expected, c4m_list_t *actual) } } +static c4m_utf8_t * +line_strip(c4m_str_t *s) +{ + c4m_list_t *parts = c4m_str_split(s, c4m_new_utf8("\n")); + + for (int i = 0; i < c4m_list_len(parts); i++) { + c4m_utf8_t *item = c4m_str_strip(c4m_list_get(parts, i, NULL)); + item = c4m_to_utf8(item); + c4m_list_set(parts, i, item); + } + + return c4m_str_join(parts, c4m_new_utf8("\n")); +} + static bool compare_results(c4m_utf8_t *fname, c4m_test_kat *kat, @@ -367,7 +455,13 @@ compare_results(c4m_utf8_t *fname, if (c4m_str_codepoint_len(output) == 0) { goto empty_err; } - if (!c4m_str_eq(output, kat->expected_output)) { + + if (kat->in_hex) { + output = c4m_str_to_hex(output, false); + } + + if (!c4m_str_eq(line_strip(output), + line_strip(kat->expected_output))) { ret = false; c4m_printf( @@ -377,7 +471,7 @@ compare_results(c4m_utf8_t *fname, "[h1]Expected output[/]\n{}\n[h1]Actual[/]\n{}\n", kat->expected_output, output); -#if 0 +#if 1 c4m_printf( "[h2]Expected (Hex)[/]\n{}\n[h2]Actual (Hex)[/]\n{}\n", c4m_hex_dump(kat->expected_output->data, @@ -495,6 +589,7 @@ add_static_symbols() c4m_add_static_function(c4m_new_utf8("c4m_repr"), c4m_wrapper_repr); c4m_add_static_function(c4m_new_utf8("c4m_to_str"), c4m_wrapper_to_str); c4m_add_static_function(c4m_new_utf8("c4m_len"), c4m_len); + c4m_add_static_function(c4m_new_utf8("c4m_snap_column"), c4m_snap_column); } static int diff --git a/tests/rich.c4m b/tests/rich.c4m new file mode 100644 index 00000000..cd7bf277 --- /dev/null +++ b/tests/rich.c4m @@ -0,0 +1,70 @@ +""" +Basic grid; no casting yet. +""" +""" +$output: +Hello, world! Here's emphasis for my table: +┏┅┅┅┅┅┅┅┅┅┅┳┅┅┅┅┅┅┅┅┅┳┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┓ +┇Name ┇Country ┇City ┇ +┣┅┅┅┅┅┅┅┅┅┅╋┅┅┅┅┅┅┅┅┅╋┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┫ +┇John ┇USA ┇NYC ┇ +┣┅┅┅┅┅┅┅┅┅┅╋┅┅┅┅┅┅┅┅┅╋┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┫ +┇Brandon ┇USA ┇NYC ┇ +┣┅┅┅┅┅┅┅┅┅┅╋┅┅┅┅┅┅┅┅┅╋┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┫ +┇Liming ┇USA ┇NYC ┇ +┣┅┅┅┅┅┅┅┅┅┅╋┅┅┅┅┅┅┅┅┅╋┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┫ +┇Miroslav ┇USA ┇Lawn Guy Land ┇ +┣┅┅┅┅┅┅┅┅┅┅╋┅┅┅┅┅┅┅┅┅╋┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┫ +┇Rich ┇USA ┇Providence, RI ┇ +┣┅┅┅┅┅┅┅┅┅┅╋┅┅┅┅┅┅┅┅┅╋┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┫ +┇Matt ┇USA ┇Orange ┇ +┣┅┅┅┅┅┅┅┅┅┅╋┅┅┅┅┅┅┅┅┅╋┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┫ +┇Mark ┇UK ┇Brighton ┇ +┣┅┅┅┅┅┅┅┅┅┅╋┅┅┅┅┅┅┅┅┅╋┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┫ +┇Hugo ┇UK ┇Brighton ┇ +┣┅┅┅┅┅┅┅┅┅┅╋┅┅┅┅┅┅┅┅┅╋┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┫ +┇James ┇UK ┇Brighton ┇ +┣┅┅┅┅┅┅┅┅┅┅╋┅┅┅┅┅┅┅┅┅╋┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┫ +┇Max ┇UK ┇Brighton ┇ +┣┅┅┅┅┅┅┅┅┅┅╋┅┅┅┅┅┅┅┅┅╋┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┫ +┇Theo ┇Greece ┇Athens ┇ +┣┅┅┅┅┅┅┅┅┅┅╋┅┅┅┅┅┅┅┅┅╋┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┫ +┇Thomas ┇Greece ┇Athens ┇ +┣┅┅┅┅┅┅┅┅┅┅╋┅┅┅┅┅┅┅┅┅╋┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┫ +┇James II ┇Germany ┇Berlin ┇ +┗┅┅┅┅┅┅┅┅┅┅┻┅┅┅┅┅┅┅┅┅┻┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┛ +""" + +extern c4m_snap_column(ptr, i64) -> void { + local: snap(g: grid, column: int) -> void +} + +r = "[h2]Hello, world!"'r + + +n = [["Name", "Country", "City"], + ["[em]John"'r, "USA", "NYC"], + ["[em]Brandon"'r, "USA", "NYC"], + ["[em]Liming"'r, "USA", "NYC"], + ["[em]Miroslav"'r, "USA", "Lawn Guy Land"], + ["[em]Rich"'r, "USA", "Providence, RI"], + ["[em]Matt"'r, "USA", "Orange"], + ["[em]Mark"'r, "UK", "Brighton"], + ["[em]Hugo"'r, "UK", "Brighton"], + ["[em]James"'r, "UK", "Brighton"], + ["[em]Max"'r, "UK", "Brighton"], + ["[em]Theo"'r, "Greece", "Athens"], + ["[em]Thomas"'r, "Greece", "Athens"], + ["[em]James II"'r, "Germany", "Berlin"]]'table + +snap(n, 0) +snap(n, 1) +snap(n, 2) + +print(r + " Here's " + "[em]emphasis"'r + " for my table: ") +print(n) + +# also if I try to put the litmod on the n, that shouldn't crash either. +# Prob make 'em and 'h1 work. +# Maybe get « » working too, for rich strings? «[em] heya» + diff --git a/todo/rich.c4m b/todo/rich.c4m deleted file mode 100644 index 5235d21a..00000000 --- a/todo/rich.c4m +++ /dev/null @@ -1,25 +0,0 @@ -r = "[h2]Hello, world!"'r - - -n = [["Name", "Country", "City"], - ["[em]John"'r, "USA", "NYC"], - ["[em]Brandon"'r, "USA", "NYC"], - ["[em]Liming"'r, "USA", "NYC"], - ["[em]Miroslav"'r, "USA", "Lawn Guy Land"], - ["[em]Rich"'r, "USA", "Providence, RI"], - ["[em]Matt"'r, "USA", "Orange"], - ["[em]Mark"'r, "UK", "Brighton"], - ["[em]Hugo"'r, "UK", "Brighton"], - ["[em]James"'r, "UK", "Brighton"], - ["[em]Max"'r, "UK", "Brighton"], - ["[em]Theo"'r, "Greece", "Athens"], - ["[em]Thomas"'r, "Greece", "Athens"], - ["[em]James II"'r, "Germany", "Berlin"]] #'table - -print(r + " Here's " + "[em]emphasis"'r + " for my table: ") -print(n) - -# also if I try to put the litmod on the n, that shouldn't crash either. -# Prob make 'em and 'h1 work. -# Maybe get « » working too, for rich strings? «[em] heya» -