diff options
Diffstat (limited to 'builtins')
| -rw-r--r-- | builtins/array.c | 4 | ||||
| -rw-r--r-- | builtins/array.h | 2 | ||||
| -rw-r--r-- | builtins/bool.c | 4 | ||||
| -rw-r--r-- | builtins/color.c | 2 | ||||
| -rw-r--r-- | builtins/files.c | 320 | ||||
| -rw-r--r-- | builtins/files.h | 42 | ||||
| -rw-r--r-- | builtins/functions.c | 12 | ||||
| -rw-r--r-- | builtins/halfsiphash.h | 22 | ||||
| -rw-r--r-- | builtins/integers.c | 2 | ||||
| -rw-r--r-- | builtins/macros.h | 31 | ||||
| -rw-r--r-- | builtins/memory.c | 4 | ||||
| -rw-r--r-- | builtins/nums.c | 4 | ||||
| -rw-r--r-- | builtins/pointer.c | 8 | ||||
| -rw-r--r-- | builtins/table.c | 6 | ||||
| -rw-r--r-- | builtins/text.c | 2 | ||||
| -rw-r--r-- | builtins/tomo.h | 50 | ||||
| -rw-r--r-- | builtins/types.c | 8 | ||||
| -rw-r--r-- | builtins/util.c | 84 | ||||
| -rw-r--r-- | builtins/util.h | 59 |
19 files changed, 637 insertions, 29 deletions
diff --git a/builtins/array.c b/builtins/array.c index 07814f66..2fd66f58 100644 --- a/builtins/array.c +++ b/builtins/array.c @@ -11,8 +11,8 @@ #include "array.h" #include "types.h" #include "functions.h" -#include "../SipHash/halfsiphash.h" -#include "../util.h" +#include "halfsiphash.h" +#include "util.h" static inline size_t get_item_size(const TypeInfo *info) { diff --git a/builtins/array.h b/builtins/array.h index 52fd65e3..d36db573 100644 --- a/builtins/array.h +++ b/builtins/array.h @@ -2,7 +2,7 @@ #include <stdbool.h> #include <gc/cord.h> -#include "../util.h" +#include "util.h" #include "datatypes.h" #include "functions.h" #include "types.h" diff --git a/builtins/bool.c b/builtins/bool.c index 327a101d..35eaafec 100644 --- a/builtins/bool.c +++ b/builtins/bool.c @@ -8,9 +8,9 @@ #include <sys/param.h> #include <err.h> -#include "../SipHash/halfsiphash.h" -#include "../util.h" +#include "util.h" #include "bool.h" +#include "halfsiphash.h" #include "types.h" public CORD Bool__as_text(const bool *b, bool colorize, const TypeInfo *type) diff --git a/builtins/color.c b/builtins/color.c index b84d8a71..4732382a 100644 --- a/builtins/color.c +++ b/builtins/color.c @@ -4,7 +4,7 @@ #include <string.h> #include <unistd.h> -#include "../util.h" +#include "util.h" #include "color.h" public bool USE_COLOR = true; diff --git a/builtins/files.c b/builtins/files.c new file mode 100644 index 00000000..9fe9b916 --- /dev/null +++ b/builtins/files.c @@ -0,0 +1,320 @@ +// +// files.c - Implementation of some file loading functionality. +// + +#include <err.h> +#include <fcntl.h> +#include <gc.h> +#include <libgen.h> +#include <limits.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <sys/param.h> + +#include "files.h" +#include "util.h" + +static const int tabstop = 4; + +public char *resolve_path(const char *path, const char *relative_to, const char *system_path) +{ + if (!relative_to || streq(relative_to, "/dev/stdin")) relative_to = "."; + if (!path || strlen(path) == 0) return NULL; + + // Resolve the path to an absolute path, assuming it's relative to the file + // it was found in: + char buf[PATH_MAX] = {0}; + if (streq(path, "~") || strncmp(path, "~/", 2) == 0) { + char *resolved = realpath(heap_strf("%s%s", getenv("HOME"), path+1), buf); + if (resolved) return heap_str(resolved); + } else if (streq(path, ".") || strncmp(path, "./", 2) == 0) { + char *relative_dir = dirname(heap_str(relative_to)); + char *resolved = realpath(heap_strf("%s/%s", relative_dir, path), buf); + if (resolved) return heap_str(resolved); + } else if (path[0] == '/') { + // Absolute path: + char *resolved = realpath(path, buf); + if (resolved) return heap_str(resolved); + } else { + // Relative path: + char *relative_dir = dirname(heap_str(relative_to)); + if (!system_path) system_path = "."; + char *copy = strdup(system_path); + for (char *dir, *pos = copy; (dir = strsep(&pos, ":")); ) { + if (dir[0] == '/') { + char *resolved = realpath(heap_strf("%s/%s", dir, path), buf); + if (resolved) return heap_str(resolved); + } else if (dir[0] == '~' && (dir[1] == '\0' || dir[1] == '/')) { + char *resolved = realpath(heap_strf("%s%s/%s", getenv("HOME"), dir, path), buf); + if (resolved) return heap_str(resolved); + } else if (streq(dir, ".") || strncmp(dir, "./", 2) == 0) { + char *resolved = realpath(heap_strf("%s/%s", relative_dir, path), buf); + if (resolved) return heap_str(resolved); + } else if (streq(dir, ".") || streq(dir, "..") || strncmp(dir, "./", 2) == 0 || strncmp(dir, "../", 3) == 0) { + char *resolved = realpath(heap_strf("%s/%s/%s", relative_dir, dir, path), buf); + if (resolved) return heap_str(resolved); + } else { + char *resolved = realpath(heap_strf("%s/%s", dir, path), buf); + if (resolved) return heap_str(resolved); + } + } + free(copy); + } + return NULL; +} + +static file_t *_load_file(const char* filename, FILE *file) +{ + if (!file) return NULL; + + file_t *ret = new(file_t, .filename=filename); + + size_t file_size = 0, line_cap = 0; + char *file_buf = NULL, *line_buf = NULL; + FILE *mem = open_memstream(&file_buf, &file_size); + int64_t line_len = 0; + while ((line_len = getline(&line_buf, &line_cap, file)) >= 0) { + file_line_t line_info = {.offset=file_size, .indent=0, .is_empty=false}; + char *p; + for (p = line_buf; *p == '\t'; ++p) + line_info.indent += 1; + line_info.is_empty = *p != '\r' && *p != '\n'; + if (ret->line_capacity <= ret->num_lines) { + ret->lines = GC_REALLOC(ret->lines, sizeof(file_line_t)*(ret->line_capacity += 32)); + } + ret->lines[ret->num_lines++] = line_info; + fwrite(line_buf, sizeof(char), line_len, mem); + fflush(mem); + } + fclose(file); + + char *copy = GC_MALLOC_ATOMIC(file_size+1); + memcpy(copy, file_buf, file_size); + copy[file_size] = '\0'; + ret->text = copy; + ret->len = file_size; + fclose(mem); + + free(file_buf); + ret->relative_filename = filename; + if (filename && filename[0] != '<' && !streq(filename, "/dev/stdin")) { + filename = resolve_path(filename, ".", "."); + // Convert to relative path (if applicable) + char buf[PATH_MAX]; + char *cwd = getcwd(buf, sizeof(buf)); + int64_t cwd_len = strlen(cwd); + if (strncmp(cwd, filename, cwd_len) == 0 && filename[cwd_len] == '/') + ret->relative_filename = &filename[cwd_len+1]; + } + return ret; +} + +// +// Read an entire file into memory. +// +public file_t *load_file(const char* filename) +{ + FILE *file = filename[0] ? fopen(filename, "r") : stdin; + return _load_file(filename, file); +} + +// +// Create a virtual file from a string. +// +public file_t *spoof_file(const char* filename, const char *text) +{ + FILE *file = fmemopen((char*)text, strlen(text)+1, "r"); + return _load_file(filename, file); +} + +// +// Given a pointer, determine which line number it points to (1-indexed) +// +public int64_t get_line_number(file_t *f, const char *p) +{ + // Binary search: + int64_t lo = 0, hi = (int64_t)f->num_lines-1; + if (p < f->text) return 0; + int64_t offset = (int64_t)(p - f->text); + while (lo <= hi) { + int64_t mid = (lo + hi) / 2; + file_line_t *line = &f->lines[mid]; + if (line->offset == offset) + return mid + 1; + else if (line->offset < offset) + lo = mid + 1; + else if (line->offset > offset) + hi = mid - 1; + } + return lo; // Return the line number whose line starts closest before p +} + +// +// Given a pointer, determine which line column it points to. +// +public int64_t get_line_column(file_t *f, const char *p) +{ + int64_t line_no = get_line_number(f, p); + file_line_t *line = &f->lines[line_no-1]; + return 1 + (int64_t)(p - (f->text + line->offset)); +} + +// +// Given a pointer, get the indentation of the line it's on. +// +public int64_t get_indent(file_t *f, const char *p) +{ + int64_t line_no = get_line_number(f, p); + file_line_t *line = &f->lines[line_no-1]; + return line->indent; +} + +// +// Return a pointer to the line with the specified line number (1-indexed) +// +public const char *get_line(file_t *f, int64_t line_number) +{ + if (line_number == 0 || line_number > (int64_t)f->num_lines) return NULL; + file_line_t *line = &f->lines[line_number-1]; + return f->text + line->offset; +} + +// +// Return a value like /foo:line:col +// +public const char *get_file_pos(file_t *f, const char *p) +{ + return heap_strf("%s:%ld:%ld", f->filename, get_line_number(f, p), get_line_column(f, p)); +} + +static int fputc_column(FILE *out, char c, char print_char, int *column) +{ + int printed = 0; + if (print_char == '\t') print_char = ' '; + if (c == '\t') { + for (int to_fill = tabstop - (*column % tabstop); to_fill > 0; --to_fill) { + printed += fputc(print_char, out); + ++*column; + } + } else { + printed += fputc(print_char, out); + ++*column; + } + return printed; +} + +// +// Print a span from a file +// +public int fprint_span(FILE *out, file_t *file, const char *start, const char *end, const char *hl_color, int64_t context_lines, bool use_color) +{ + if (!file) return 0; + + // Handle spans that come from multiple files: + if (start < file->text || start > file->text + file->len) + start = end; + if (end < file->text || end > file->text + file->len) + end = start; + // Just in case neither end of the span came from this file: + if (end < file->text || end > file->text + file->len) + start = end = file->text; + + const char *lineno_fmt, *normal_color, *empty_marker; + bool print_carets = false; + int printed = 0; + if (use_color) { + lineno_fmt = "\x1b[0;2m%*lu\x1b(0\x78\x1b(B\x1b[m "; + normal_color = "\x1b[m"; + empty_marker = "\x1b(0\x61\x1b(B"; + printed += fprintf(out, "\x1b[33;4;1m%s\x1b[m\n", file->relative_filename); + } else { + lineno_fmt = "%*lu| "; + hl_color = ""; + normal_color = ""; + empty_marker = " "; + print_carets = true; + printed += fprintf(out, "%s\n", file->relative_filename); + } + + if (context_lines == 0) + return fprintf(out, "%s%.*s%s", hl_color, (int)(end - start), start, normal_color); + + int64_t start_line = get_line_number(file, start), + end_line = get_line_number(file, end); + + int64_t first_line = start_line - (context_lines - 1), + last_line = end_line + (context_lines - 1); + + if (first_line < 1) first_line = 1; + if (last_line > file->num_lines) last_line = file->num_lines; + + int digits = 1; + for (int64_t i = last_line; i > 0; i /= 10) ++digits; + + for (int64_t line_no = first_line; line_no <= last_line; ++line_no) { + if (line_no > first_line + 5 && line_no < last_line - 5) { + if (use_color) + printed += fprintf(out, "\x1b[0;2;3;4m ... %ld lines omitted ... \x1b[m\n", (last_line - first_line) - 11); + else + printed += fprintf(out, " ... %ld lines omitted ...\n", (last_line - first_line) - 11); + line_no = last_line - 6; + continue; + } + + printed += fprintf(out, lineno_fmt, digits, line_no); + const char *line = get_line(file, line_no); + if (!line) break; + + int column = 0; + const char *p = line; + // Before match + for (; *p && *p != '\r' && *p != '\n' && p < start; ++p) + printed += fputc_column(out, *p, *p, &column); + + // Zero-width matches + if (p == start && start == end) { + printed += fprintf(out, "%s%s%s", hl_color, empty_marker, normal_color); + column += 1; + } + + // Inside match + if (start <= p && p < end) { + printed += fputs(hl_color, out); + for (; *p && *p != '\r' && *p != '\n' && p < end; ++p) + printed += fputc_column(out, *p, *p, &column); + printed += fputs(normal_color, out); + } + + // After match + for (; *p && *p != '\r' && *p != '\n'; ++p) + printed += fputc_column(out, *p, *p, &column); + + printed += fprintf(out, "\n"); + + const char *eol = strchrnul(line, '\n'); + if (print_carets && start >= line && start < eol && line <= start) { + for (int num = 0; num < digits; num++) + printed += fputc(' ', out); + printed += fputs(": ", out); + int column = 0; + for (const char *sp = line; *sp && *sp != '\n'; ++sp) { + char print_char; + if (sp < start) + print_char = ' '; + else if (sp == start && sp == end) + print_char = '^'; + else if (sp >= start && sp < end) + print_char = '-'; + else + print_char = ' '; + printed += fputc_column(out, *sp, print_char, &column); + } + printed += fputs("\n", out); + } + } + fflush(out); + return printed; +} + +// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/builtins/files.h b/builtins/files.h new file mode 100644 index 00000000..79dd48cd --- /dev/null +++ b/builtins/files.h @@ -0,0 +1,42 @@ +// +// files.h - Definitions of an API for loading files. +// +#pragma once + +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <unistd.h> + +typedef struct { + int64_t offset; + int64_t indent:63; + bool is_empty:1; +} file_line_t; + +typedef struct { + const char *filename, *relative_filename; + const char *text; + int64_t len; + int64_t num_lines, line_capacity; + file_line_t *lines; +} file_t; + +char *resolve_path(const char *path, const char *relative_to, const char *system_path); +__attribute__((nonnull)) +file_t *load_file(const char *filename); +__attribute__((nonnull, returns_nonnull)) +file_t *spoof_file(const char *filename, const char *text); +__attribute__((pure, nonnull)) +int64_t get_line_number(file_t *f, const char *p); +__attribute__((pure, nonnull)) +int64_t get_line_column(file_t *f, const char *p); +__attribute__((pure, nonnull)) +int64_t get_indent(file_t *f, const char *p); +__attribute__((pure, nonnull)) +const char *get_line(file_t *f, int64_t line_number); +__attribute__((pure, nonnull)) +const char *get_file_pos(file_t *f, const char *p); +int fprint_span(FILE *out, file_t *file, const char *start, const char *end, const char *hl_color, int64_t context_lines, bool use_color); + +// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/builtins/functions.c b/builtins/functions.c index 448b7758..ae67fcb9 100644 --- a/builtins/functions.c +++ b/builtins/functions.c @@ -7,15 +7,15 @@ #include <sys/param.h> #include <uninorm.h> -#include "../SipHash/halfsiphash.h" -#include "../files.h" -#include "../util.h" -#include "functions.h" +#include "files.h" +#include "util.h" #include "array.h" -#include "table.h" -#include "text.h" +#include "functions.h" +#include "halfsiphash.h" #include "pointer.h" #include "string.h" +#include "table.h" +#include "text.h" #include "types.h" extern bool USE_COLOR; diff --git a/builtins/halfsiphash.h b/builtins/halfsiphash.h new file mode 100644 index 00000000..a1af8cd2 --- /dev/null +++ b/builtins/halfsiphash.h @@ -0,0 +1,22 @@ +/* + SipHash reference C implementation + + Copyright (c) 2012-2021 Jean-Philippe Aumasson + <jeanphilippe.aumasson@gmail.com> + Copyright (c) 2012-2014 Daniel J. Bernstein <djb@cr.yp.to> + + To the extent possible under law, the author(s) have dedicated all copyright + and related and neighboring rights to this software to the public domain + worldwide. This software is distributed without any warranty. + + You should have received a copy of the CC0 Public Domain Dedication along + with + this software. If not, see + <http://creativecommons.org/publicdomain/zero/1.0/>. + */ + +#include <inttypes.h> +#include <string.h> + +int halfsiphash(const void *in, const size_t inlen, const void *k, uint8_t *out, + const size_t outlen); diff --git a/builtins/integers.c b/builtins/integers.c index 6de85c3e..4116879e 100644 --- a/builtins/integers.c +++ b/builtins/integers.c @@ -4,9 +4,9 @@ #include <stdint.h> #include <stdlib.h> -#include "../SipHash/halfsiphash.h" #include "array.h" #include "datatypes.h" +#include "halfsiphash.h" #include "integers.h" #include "string.h" #include "types.h" diff --git a/builtins/macros.h b/builtins/macros.h new file mode 100644 index 00000000..c6f474ec --- /dev/null +++ b/builtins/macros.h @@ -0,0 +1,31 @@ +#pragma once + +#include <gc.h> +#include <gc/cord.h> +#include <signal.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#define $heap(x) (__typeof(x)*)memcpy(GC_MALLOC(sizeof(x)), (__typeof(x)[1]){x}, sizeof(x)) +#define $stack(x) (__typeof(x)*)((__typeof(x)[1]){x}) +#define $tagged(obj_expr, type_name, tag_name) ({ __typeof(obj_expr) $obj = obj_expr; \ + $obj.$tag == $tag$##type_name##$##tag_name ? &$obj.tag_name : NULL; }) + + +#define not(x) _Generic(x, bool: (bool)!(x), int64_t: ~(x), int32_t: ~(x), int16_t: ~(x), int8_t: ~(x), \ + array_t: ((x).length == 0), table_t: ((x).entries.length == 0), CORD: ((x) == CORD_EMPTY), \ + default: _Static_assert(0, "Not supported")) +#define Bool(x) _Generic(x, bool: (bool)(x), int64_t: (x != 0), int32_t: (x != 0), int16_t: (x != 0), int8_t: (x != 0), CORD: ((x) == CORD_EMPTY), \ + array_t: ((x).length > 0), table_t: ((x).entries.length > 0), CORD: ((x) != CORD_EMPTY), \ + default: _Static_assert(0, "Not supported")) +#define and(x, y) _Generic(x, bool: (bool)((x) && (y)), default: ((x) & (y))) +#define or(x, y) _Generic(x, bool: (bool)((x) || (y)), default: ((x) | (y))) +#define xor(x, y) _Generic(x, bool: (bool)((x) ^ (y)), default: ((x) ^ (y))) +#define mod(x, n) ((x) % (n)) +#define mod1(x, n) (((x) % (n)) + (__typeof(x))1) +#define $cmp(x, y, info) (_Generic(x, int8_t: (x>0)-(y>0), int16_t: (x>0)-(y>0), int32_t: (x>0)-(y>0), int64_t: (x>0)-(y>0), bool: (x>0)-(y>0), \ + CORD: CORD_cmp((CORD)x, (CORD)y), char*: strcmp((char*)x, (char*)y), default: generic_compare($stack(x), $stack(y), info))) diff --git a/builtins/memory.c b/builtins/memory.c index d4af4cc2..f33196be 100644 --- a/builtins/memory.c +++ b/builtins/memory.c @@ -8,8 +8,8 @@ #include <sys/param.h> #include <err.h> -#include "../util.h" -#include "../SipHash/halfsiphash.h" +#include "util.h" +#include "halfsiphash.h" #include "memory.h" #include "types.h" diff --git a/builtins/nums.c b/builtins/nums.c index 32ec89fb..b05c6fab 100644 --- a/builtins/nums.c +++ b/builtins/nums.c @@ -1,13 +1,13 @@ +#include <float.h> #include <gc.h> #include <gc/cord.h> -#include <float.h> #include <math.h> #include <stdbool.h> #include <stdint.h> #include <stdlib.h> -#include "../SipHash/halfsiphash.h" #include "array.h" +#include "halfsiphash.h" #include "nums.h" #include "string.h" #include "types.h" diff --git a/builtins/pointer.c b/builtins/pointer.c index 54bab4b3..d490485f 100644 --- a/builtins/pointer.c +++ b/builtins/pointer.c @@ -1,16 +1,16 @@ +#include <ctype.h> +#include <err.h> #include <gc.h> #include <gc/cord.h> #include <stdbool.h> #include <stdint.h> #include <stdlib.h> -#include <ctype.h> #include <sys/param.h> -#include <err.h> -#include "../util.h" -#include "../SipHash/halfsiphash.h" +#include "util.h" #include "functions.h" +#include "halfsiphash.h" #include "types.h" typedef struct recursion_s { diff --git a/builtins/table.c b/builtins/table.c index 5631a7fb..be4444ac 100644 --- a/builtins/table.c +++ b/builtins/table.c @@ -17,13 +17,13 @@ #include <string.h> #include <sys/param.h> -#include "../SipHash/halfsiphash.h" -#include "../util.h" +#include "util.h" #include "array.h" #include "datatypes.h" +#include "halfsiphash.h" #include "memory.h" -#include "text.h" #include "table.h" +#include "text.h" #include "types.h" // #define DEBUG_TABLES diff --git a/builtins/text.c b/builtins/text.c index 4641bc1d..e443da33 100644 --- a/builtins/text.c +++ b/builtins/text.c @@ -14,9 +14,9 @@ #include <uninorm.h> #include <unistr.h> -#include "../SipHash/halfsiphash.h" #include "array.h" #include "functions.h" +#include "halfsiphash.h" #include "text.h" #include "types.h" diff --git a/builtins/tomo.h b/builtins/tomo.h new file mode 100644 index 00000000..dd00c628 --- /dev/null +++ b/builtins/tomo.h @@ -0,0 +1,50 @@ +#pragma once + +#include <err.h> +#include <gc.h> +#include <gc/cord.h> +#include <math.h> +#include <signal.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "array.h" +#include "bool.h" +#include "color.h" +#include "datatypes.h" +#include "functions.h" +#include "halfsiphash.h" +#include "integers.h" +#include "macros.h" +#include "memory.h" +#include "nums.h" +#include "pointer.h" +#include "table.h" +#include "text.h" +#include "types.h" + +#define $heap(x) (__typeof(x)*)memcpy(GC_MALLOC(sizeof(x)), (__typeof(x)[1]){x}, sizeof(x)) +#define $stack(x) (__typeof(x)*)((__typeof(x)[1]){x}) +#define $tagged(obj_expr, type_name, tag_name) ({ __typeof(obj_expr) $obj = obj_expr; \ + $obj.$tag == $tag$##type_name##$##tag_name ? &$obj.tag_name : NULL; }) + + +#define not(x) _Generic(x, bool: (bool)!(x), int64_t: ~(x), int32_t: ~(x), int16_t: ~(x), int8_t: ~(x), \ + array_t: ((x).length == 0), table_t: ((x).entries.length == 0), CORD: ((x) == CORD_EMPTY), \ + default: _Static_assert(0, "Not supported")) +#define Bool(x) _Generic(x, bool: (bool)(x), int64_t: (x != 0), int32_t: (x != 0), int16_t: (x != 0), int8_t: (x != 0), CORD: ((x) == CORD_EMPTY), \ + array_t: ((x).length > 0), table_t: ((x).entries.length > 0), CORD: ((x) != CORD_EMPTY), \ + default: _Static_assert(0, "Not supported")) +#define and(x, y) _Generic(x, bool: (bool)((x) && (y)), default: ((x) & (y))) +#define or(x, y) _Generic(x, bool: (bool)((x) || (y)), default: ((x) | (y))) +#define xor(x, y) _Generic(x, bool: (bool)((x) ^ (y)), default: ((x) ^ (y))) +#define mod(x, n) ((x) % (n)) +#define mod1(x, n) (((x) % (n)) + (__typeof(x))1) +#define $cmp(x, y, info) (_Generic(x, int8_t: (x>0)-(y>0), int16_t: (x>0)-(y>0), int32_t: (x>0)-(y>0), int64_t: (x>0)-(y>0), bool: (x>0)-(y>0), \ + CORD: CORD_cmp((CORD)x, (CORD)y), char*: strcmp((char*)x, (char*)y), default: generic_compare($stack(x), $stack(y), info))) + +// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/builtins/types.c b/builtins/types.c index 59bf5c47..72be5afa 100644 --- a/builtins/types.c +++ b/builtins/types.c @@ -1,16 +1,16 @@ // Generic type constructor #include <err.h> #include <gc.h> -#include <string.h> #include <stdlib.h> +#include <string.h> #include <sys/param.h> +#include "util.h" #include "array.h" -#include "table.h" +#include "halfsiphash.h" #include "pointer.h" +#include "table.h" #include "types.h" -#include "../util.h" -#include "../SipHash/halfsiphash.h" public CORD Type__as_text(const void *typeinfo, bool colorize, const TypeInfo *type) { diff --git a/builtins/util.c b/builtins/util.c new file mode 100644 index 00000000..16ef7aaf --- /dev/null +++ b/builtins/util.c @@ -0,0 +1,84 @@ +#include <ctype.h> +#include <gc.h> +#include <gc/cord.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "util.h" + +public char *heap_strn(const char *str, size_t len) +{ + if (!str) return NULL; + if (len == 0) return ""; + char *heaped = GC_MALLOC_ATOMIC(len + 1); + memcpy(heaped, str, len); + heaped[len] = '\0'; + return heaped; +} + +public char *heap_str(const char *str) +{ + if (!str) return NULL; + return heap_strn(str, strlen(str)); +} + +public char *heap_strf(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + char *tmp = NULL; + int len = vasprintf(&tmp, fmt, args); + if (len < 0) return NULL; + va_end(args); + char *ret = heap_strn(tmp, (size_t)len); + free(tmp); + return ret; +} + +// Name mangling algorithm to produce valid identifiers: +// Escape individual chars as "_x%02X" +// Things being escaped: +// - Leading digit +// - Non alphanumeric/non-underscore characters +// - "_" when followed by "x" and two uppercase hex digits +public char *mangle(const char *name) +{ + size_t len = 0; + for (const char *p = name; *p; p++) { + if ((!isalnum(*p) && *p != '_') // Non-identifier character + || (p == name && isdigit(*p)) // Leading digit + || (p[0] == '_' && p[1] == 'x' && strspn(p+2, "ABCDEF0123456789") >= 2)) { // Looks like hex escape + len += strlen("_x00"); // Hex escape + } else { + len += 1; + } + } + char *mangled = GC_MALLOC_ATOMIC(len + 1); + char *dest = mangled; + for (const char *src = name; *src; src++) { + if ((!isalnum(*src) && *src != '_') // Non-identifier character + || (src == name && isdigit(*src)) // Leading digit + || (src[0] == '_' && src[1] == 'x' && strspn(src+2, "ABCDEF0123456789") >= 2)) { // Looks like hex escape + dest += sprintf(dest, "_x%02X", *src); // Hex escape + } else { + *(dest++) = *src; + } + } + mangled[len] = '\0'; + return mangled; +} + +CORD CORD_asprintf(CORD fmt, ...) +{ + va_list args; + va_start(args, fmt); + CORD c = NULL; + CORD_vsprintf(&c, fmt, args); + va_end(args); + return c; +} + + +// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/builtins/util.h b/builtins/util.h new file mode 100644 index 00000000..c858f6ce --- /dev/null +++ b/builtins/util.h @@ -0,0 +1,59 @@ +#pragma once + +#include <assert.h> +#include <gc.h> +#include <gc/cord.h> +#include <stdio.h> +#include <string.h> +#include <err.h> + +#define streq(a, b) (((a) == NULL && (b) == NULL) || (((a) == NULL) == ((b) == NULL) && strcmp(a, b) == 0)) +#define new(t, ...) ((t*)memcpy(GC_MALLOC(sizeof(t)), &(t){__VA_ARGS__}, sizeof(t))) +#define copy(obj_ptr) ((__typeof(obj_ptr))memcpy(GC_MALLOC(sizeof(*(obj_ptr))), obj_ptr, sizeof(*(obj_ptr)))) +#define grow(arr, new_size) ((typeof (arr))GC_REALLOC(arr, (sizeof(arr[0]))*(new_size))) +#define Match(x, _tag) ((x)->tag == _tag ? &(x)->__data._tag : (errx(1, __FILE__ ":%d This was supposed to be a " # _tag "\n", __LINE__), &(x)->__data._tag)) +#define Tagged(t, _tag, ...) new(t, .tag=_tag, .__data._tag={__VA_ARGS__}) + +#ifndef auto +#define auto __auto_type +#endif + +#ifndef public +#define public __attribute__ ((visibility ("default"))) +#endif + +char *heap_strn(const char *str, size_t len); +char *heap_str(const char *str); +char *heap_strf(const char *fmt, ...); +CORD CORD_asprintf(CORD fmt, ...); +#define CORD_appendf(cord, fmt, ...) CORD_sprintf(cord, "%r" fmt, *(cord) __VA_OPT__(,) __VA_ARGS__) +#define CORD_all(...) CORD_catn(sizeof((CORD[]){__VA_ARGS__})/sizeof(CORD), __VA_ARGS__) + +#define asprintfa(...) ({ char *_buf = alloca(snprintf(0, 0, __VA_ARGS__)); sprintf(_buf, __VA_ARGS__); _buf; }) + +#define REVERSE_LIST(list) do { \ + __typeof(list) _prev = NULL; \ + __typeof(list) _next = NULL; \ + auto _current = list; \ + while (_current != NULL) { \ + _next = _current->next; \ + _current->next = _prev; \ + _prev = _current; \ + _current = _next; \ + } \ + list = _prev; \ +} while(0) + +#define LIST_MAP(src, var, ...) ({\ + __typeof(src) __mapped = NULL; \ + __typeof(src) *__next = &__mapped; \ + for (__typeof(src) var = src; var; var = var->next) { \ + *__next = GC_MALLOC(sizeof(__typeof(*(src)))); \ + **__next = *var; \ + **__next = (__typeof(*(src))){__VA_ARGS__}; \ + __next = &((*__next)->next); \ + } \ + __mapped; }) + + +// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 |
