aboutsummaryrefslogtreecommitdiff
path: root/src/stdlib
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2025-12-07 21:42:45 -0500
committerBruce Hill <bruce@bruce-hill.com>2025-12-07 21:42:45 -0500
commit544b1fb6a70d55bf368b827136cf0f37a26e8288 (patch)
tree52886c54934fed676263611bdb401dbeb80aac63 /src/stdlib
parentefbdf7b13e4b559958ed5b1b9ca9d772ae77d702 (diff)
Change Paths to be an enum of their different types.
Diffstat (limited to 'src/stdlib')
-rw-r--r--src/stdlib/datatypes.h42
-rw-r--r--src/stdlib/optionals.h2
-rw-r--r--src/stdlib/paths.c226
-rw-r--r--src/stdlib/paths.h7
4 files changed, 153 insertions, 124 deletions
diff --git a/src/stdlib/datatypes.h b/src/stdlib/datatypes.h
index fbd2ce73..49c4d835 100644
--- a/src/stdlib/datatypes.h
+++ b/src/stdlib/datatypes.h
@@ -75,8 +75,8 @@ typedef struct Present$$struct {
#define PRESENT_STRUCT ((Present$$type){})
typedef struct {
- bool has_value;
Present$$type value;
+ bool has_value;
} $OptionalPresent$$type;
#define NONE_PRESENT_STRUCT (($OptionalPresent$$type){.has_value = false})
@@ -111,14 +111,46 @@ typedef struct Text_s {
};
} Text_t;
-typedef enum PathEnum { PATHTYPE_NONE, PATHTYPE_RELATIVE, PATHTYPE_ABSOLUTE, PATHTYPE_HOME } PathType_t;
-#define OptionalPathType_t PathType_t
+typedef struct Path$AbsolutePath$$struct {
+ List_t components;
+} Path$AbsolutePath$$type;
typedef struct {
- PathType_t type;
+ Path$AbsolutePath$$type value;
+ bool has_value;
+} $OptionalPath$AbsolutePath$$type;
+
+typedef struct Path$RelativePath$$struct {
+ List_t components;
+} Path$RelativePath$$type;
+
+typedef struct {
+ Path$RelativePath$$type value;
+ bool has_value;
+} $OptionalPath$RelativePath$$type;
+
+typedef struct Path$HomePath$$struct {
List_t components;
+} Path$HomePath$$type;
+
+typedef struct {
+ Path$HomePath$$type value;
+ bool has_value;
+} $OptionalPath$HomePath$$type;
+
+#define Path$tagged$AbsolutePath(comps) ((Path_t){.$tag = Path$tag$AbsolutePath, .AbsolutePath.components = comps})
+#define Path$tagged$RelativePath(comps) ((Path_t){.$tag = Path$tag$RelativePath, .RelativePath.components = comps})
+#define Path$tagged$HomePath(comps) ((Path_t){.$tag = Path$tag$HomePath, .HomePath.components = comps})
+
+typedef struct {
+ enum { Path$tag$none, Path$tag$AbsolutePath, Path$tag$RelativePath, Path$tag$HomePath } $tag;
+ union {
+ Path$RelativePath$$type RelativePath;
+ Path$AbsolutePath$$type AbsolutePath;
+ Path$HomePath$$type HomePath;
+ List_t components;
+ };
} Path_t;
-#define OptionalPath_t Path_t
#define OptionalBool_t uint8_t
#define OptionalList_t List_t
diff --git a/src/stdlib/optionals.h b/src/stdlib/optionals.h
index d067ec94..985d6611 100644
--- a/src/stdlib/optionals.h
+++ b/src/stdlib/optionals.h
@@ -15,7 +15,7 @@
#define NONE_TABLE ((OptionalTable_t){.entries.data = NULL})
#define NONE_CLOSURE ((OptionalClosure_t){.fn = NULL})
#define NONE_TEXT ((OptionalText_t){.tag = TEXT_NONE})
-#define NONE_PATH ((Path_t){.type = PATHTYPE_NONE})
+#define NONE_PATH ((Path_t){.$tag = Path$tag$none})
PUREFUNC bool is_none(const void *obj, const TypeInfo_t *non_optional_type);
PUREFUNC uint64_t Optional$hash(const void *obj, const TypeInfo_t *type);
diff --git a/src/stdlib/paths.c b/src/stdlib/paths.c
index 6c99cf0b..9fb37ee3 100644
--- a/src/stdlib/paths.c
+++ b/src/stdlib/paths.c
@@ -29,11 +29,8 @@
#include "types.h"
#include "util.h"
-// Use inline version of the siphash code for performance:
-#include "siphash-internals.h"
-
-static const Path_t HOME_PATH = {.type = PATHTYPE_HOME}, ROOT_PATH = {.type = PATHTYPE_ABSOLUTE},
- CURDIR_PATH = {.type = PATHTYPE_RELATIVE};
+static const Path_t HOME_PATH = Path$tagged$HomePath(EMPTY_LIST), ROOT_PATH = Path$tagged$AbsolutePath(EMPTY_LIST),
+ CURDIR_PATH = Path$tagged$RelativePath(EMPTY_LIST);
static void clean_components(List_t *components) {
for (int64_t i = 0; i < (int64_t)components->length;) {
@@ -62,40 +59,41 @@ Path_t Path$from_str(const char *str) {
if (strchr(str, ';') != NULL) fail("Path has illegal character (semicolon): ", str);
- Path_t result = {.components = {}};
+ Path_t result = {};
if (str[0] == '/') {
- result.type = PATHTYPE_ABSOLUTE;
+ result.$tag = Path$tag$AbsolutePath;
str += 1;
} else if (str[0] == '~' && str[1] == '/') {
- result.type = PATHTYPE_HOME;
+ result.$tag = Path$tag$HomePath;
str += 2;
} else if (str[0] == '.' && str[1] == '/') {
- result.type = PATHTYPE_RELATIVE;
+ result.$tag = Path$tag$RelativePath;
str += 2;
} else {
- result.type = PATHTYPE_RELATIVE;
+ result.$tag = Path$tag$RelativePath;
}
+ List_t components = EMPTY_LIST;
while (str && *str) {
size_t component_len = strcspn(str, "/");
if (component_len > 0) {
if (component_len == 1 && str[0] == '.') {
// ignore /./
- } else if (component_len == 2 && strncmp(str, "..", 2) == 0 && result.components.length > 1
+ } else if (component_len == 2 && strncmp(str, "..", 2) == 0 && components.length > 1
&& !Text$equal_values(
Text(".."),
- *(Text_t *)(result.components.data
- + result.components.stride * ((int64_t)result.components.length - 1)))) {
+ *(Text_t *)(components.data + components.stride * ((int64_t)components.length - 1)))) {
// Pop off /foo/baz/.. -> /foo
- List$remove_at(&result.components, I((int64_t)result.components.length), I(1), sizeof(Text_t));
+ List$remove_at(&components, I((int64_t)components.length), I(1), sizeof(Text_t));
} else {
Text_t component = Text$from_strn(str, component_len);
- List$insert_value(&result.components, component, I(0), sizeof(Text_t));
+ List$insert_value(&components, component, I(0), sizeof(Text_t));
}
str += component_len;
}
str += strspn(str, "/");
}
+ result.components = components;
return result;
}
@@ -104,12 +102,12 @@ Path_t Path$from_text(Text_t text) { return Path$from_str(Text$as_c_string(text)
public
Path_t Path$expand_home(Path_t path) {
- if (path.type == PATHTYPE_HOME) {
+ if (path.$tag == Path$tag$HomePath) {
Path_t pwd = Path$from_str(getenv("HOME"));
- List_t components = List$concat(pwd.components, path.components, sizeof(Text_t));
- assert(components.length == path.components.length + pwd.components.length);
+ List_t components = List$concat(pwd.AbsolutePath.components, path.HomePath.components, sizeof(Text_t));
+ assert(components.length == path.HomePath.components.length + pwd.AbsolutePath.components.length);
clean_components(&components);
- path = (Path_t){.type = PATHTYPE_ABSOLUTE, .components = components};
+ path = Path$tagged$AbsolutePath(components);
}
return path;
}
@@ -120,7 +118,7 @@ Path_t Path$_concat(int n, Path_t items[n]) {
Path_t result = items[0];
LIST_INCREF(result.components);
for (int i = 1; i < n; i++) {
- if (items[i].type != PATHTYPE_RELATIVE)
+ if (items[i].$tag != Path$tag$RelativePath)
fail("Cannot concatenate an absolute or home-based path onto another path: (", items[i], ")");
List$insert_all(&result.components, items[i].components, I(0), sizeof(Text_t));
}
@@ -130,10 +128,12 @@ Path_t Path$_concat(int n, Path_t items[n]) {
public
Path_t Path$resolved(Path_t path, Path_t relative_to) {
- if (path.type == PATHTYPE_RELATIVE
- && !(relative_to.type == PATHTYPE_RELATIVE && relative_to.components.length == 0)) {
- Path_t result = {.type = relative_to.type};
- result.components = relative_to.components;
+ if (path.$tag == Path$tag$RelativePath
+ && !(relative_to.$tag == Path$tag$RelativePath && relative_to.components.length == 0)) {
+ Path_t result = {
+ .$tag = relative_to.$tag,
+ .components = relative_to.components,
+ };
LIST_INCREF(result.components);
List$insert_all(&result.components, path.components, I(0), sizeof(Text_t));
clean_components(&result.components);
@@ -144,11 +144,11 @@ Path_t Path$resolved(Path_t path, Path_t relative_to) {
public
Path_t Path$relative_to(Path_t path, Path_t relative_to) {
- if (path.type != relative_to.type)
+ if (path.$tag != relative_to.$tag)
fail("Cannot create a path relative to a different path with a mismatching type: (", path, ") relative to (",
relative_to, ")");
- Path_t result = {.type = PATHTYPE_RELATIVE};
+ Path_t result = Path$tagged$RelativePath(EMPTY_LIST);
int64_t shared = 0;
for (; shared < (int64_t)path.components.length && shared < (int64_t)relative_to.components.length; shared++) {
Text_t *p = (Text_t *)(path.components.data + shared * path.components.stride);
@@ -568,15 +568,15 @@ Path_t Path$write_unique(Path_t path, Text_t text) { return Path$write_unique_by
public
Path_t Path$parent(Path_t path) {
- if (path.type == PATHTYPE_ABSOLUTE && path.components.length == 0) {
+ if (path.$tag == Path$tag$AbsolutePath && path.components.length == 0) {
return path;
} else if (path.components.length > 0
&& !Text$equal_values(
*(Text_t *)(path.components.data + path.components.stride * ((int64_t)path.components.length - 1)),
Text(".."))) {
- return (Path_t){.type = path.type, .components = List$slice(path.components, I(1), I(-2))};
+ return (Path_t){.$tag = path.$tag, .components = List$slice(path.components, I(1), I(-2))};
} else {
- Path_t result = {.type = path.type, .components = path.components};
+ Path_t result = {.$tag = path.$tag, .components = path.components};
LIST_INCREF(result.components);
List$insert_value(&result.components, Text(".."), I(0), sizeof(Text_t));
return result;
@@ -587,8 +587,8 @@ public
PUREFUNC Text_t Path$base_name(Path_t path) {
if (path.components.length >= 1)
return *(Text_t *)(path.components.data + path.components.stride * ((int64_t)path.components.length - 1));
- else if (path.type == PATHTYPE_HOME) return Text("~");
- else if (path.type == PATHTYPE_RELATIVE) return Text(".");
+ else if (path.$tag == Path$tag$HomePath) return Text("~");
+ else if (path.$tag == Path$tag$RelativePath) return Text(".");
else return EMPTY_TEXT;
}
@@ -618,7 +618,7 @@ public
Path_t Path$child(Path_t path, Text_t name) {
if (Text$has(name, Text("/")) || Text$has(name, Text(";"))) fail("Path name has invalid characters: ", name);
Path_t result = {
- .type = path.type,
+ .$tag = path.$tag,
.components = path.components,
};
LIST_INCREF(result.components);
@@ -638,7 +638,7 @@ Path_t Path$with_extension(Path_t path, Text_t extension, bool replace) {
fail("Path extension has invalid characters: ", extension);
Path_t result = {
- .type = path.type,
+ .$tag = path.$tag,
.components = path.components,
};
LIST_INCREF(result.components);
@@ -760,55 +760,19 @@ Path_t Path$current_dir(void) {
}
public
-PUREFUNC uint64_t Path$hash(const void *obj, const TypeInfo_t *type) {
- (void)type;
- Path_t *path = (Path_t *)obj;
- siphash sh;
- siphashinit(&sh, (uint64_t)path->type);
- for (int64_t i = 0; i < (int64_t)path->components.length; i++) {
- uint64_t item_hash = Text$hash(path->components.data + i * path->components.stride, &Text$info);
- siphashadd64bits(&sh, item_hash);
- }
- return siphashfinish_last_part(&sh, (uint64_t)path->components.length);
-}
-
-public
-PUREFUNC int32_t Path$compare(const void *va, const void *vb, const TypeInfo_t *type) {
- (void)type;
- Path_t *a = (Path_t *)va, *b = (Path_t *)vb;
- int diff = ((int)a->type - (int)b->type);
- if (diff != 0) return diff;
- return List$compare(&a->components, &b->components, List$info(&Text$info));
-}
-
-public
-PUREFUNC bool Path$equal(const void *va, const void *vb, const TypeInfo_t *type) {
- (void)type;
- Path_t *a = (Path_t *)va, *b = (Path_t *)vb;
- if (a->type != b->type) return false;
- return List$equal(&a->components, &b->components, List$info(&Text$info));
-}
-
-public
-PUREFUNC bool Path$equal_values(Path_t a, Path_t b) {
- if (a.type != b.type) return false;
- return List$equal(&a.components, &b.components, List$info(&Text$info));
-}
-
-public
int Path$print(FILE *f, Path_t path) {
if (path.components.length == 0) {
- if (path.type == PATHTYPE_ABSOLUTE) return fputs("/", f);
- else if (path.type == PATHTYPE_RELATIVE) return fputs(".", f);
- else if (path.type == PATHTYPE_HOME) return fputs("~", f);
+ if (path.$tag == Path$tag$AbsolutePath) return fputs("/", f);
+ else if (path.$tag == Path$tag$RelativePath) return fputs(".", f);
+ else if (path.$tag == Path$tag$HomePath) return fputs("~", f);
}
int n = 0;
- if (path.type == PATHTYPE_ABSOLUTE) {
+ if (path.$tag == Path$tag$AbsolutePath) {
n += fputc('/', f);
- } else if (path.type == PATHTYPE_HOME) {
+ } else if (path.$tag == Path$tag$HomePath) {
n += fputs("~/", f);
- } else if (path.type == PATHTYPE_RELATIVE) {
+ } else if (path.$tag == Path$tag$RelativePath) {
if (!Text$equal_values(*(Text_t *)path.components.data, Text(".."))) n += fputs("./", f);
}
@@ -829,9 +793,9 @@ Text_t Path$as_text(const void *obj, bool color, const TypeInfo_t *type) {
if (!obj) return Text("Path");
Path_t *path = (Path_t *)obj;
Text_t text = Text$join(Text("/"), path->components);
- if (path->type == PATHTYPE_HOME) text = Text$concat(path->components.length > 0 ? Text("~/") : Text("~"), text);
- else if (path->type == PATHTYPE_ABSOLUTE) text = Text$concat(Text("/"), text);
- else if (path->type == PATHTYPE_RELATIVE
+ if (path->$tag == Path$tag$HomePath) text = Text$concat(path->components.length > 0 ? Text("~/") : Text("~"), text);
+ else if (path->$tag == Path$tag$AbsolutePath) text = Text$concat(Text("/"), text);
+ else if (path->$tag == Path$tag$RelativePath
&& (path->components.length == 0 || !Text$equal_values(*(Text_t *)(path->components.data), Text(".."))))
text = Text$concat(path->components.length > 0 ? Text("./") : Text("."), text);
@@ -841,52 +805,80 @@ Text_t Path$as_text(const void *obj, bool color, const TypeInfo_t *type) {
}
public
-CONSTFUNC bool Path$is_none(const void *obj, const TypeInfo_t *type) {
- (void)type;
- return ((Path_t *)obj)->type == PATHTYPE_NONE;
-}
+const TypeInfo_t Path$AbsolutePath$$info = {
+ .size = sizeof(Path$AbsolutePath$$type),
+ .align = __alignof__(Path$AbsolutePath$$type),
+ .metamethods = Struct$metamethods,
+ .tag = StructInfo,
+ .StructInfo =
+ {
+ .name = "AbsolutePath",
+ .num_fields = 1,
+ .fields = (NamedType_t[1]){{
+ .name = "components",
+ .type = List$info(&Text$info),
+ }},
+ },
+};
public
-void Path$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type) {
- (void)type;
- Path_t *path = (Path_t *)obj;
- fputc((int)path->type, out);
- List$serialize(&path->components, out, pointers, List$info(&Text$info));
-}
+const TypeInfo_t Path$RelativePath$$info = {
+ .size = sizeof(Path$RelativePath$$type),
+ .align = __alignof__(Path$RelativePath$$type),
+ .metamethods = Struct$metamethods,
+ .tag = StructInfo,
+ .StructInfo =
+ {
+ .name = "RelativePath",
+ .num_fields = 1,
+ .fields = (NamedType_t[1]){{
+ .name = "components",
+ .type = List$info(&Text$info),
+ }},
+ },
+};
public
-void Path$deserialize(FILE *in, void *obj, List_t *pointers, const TypeInfo_t *type) {
- (void)type;
- Path_t path = {};
- path.type = fgetc(in);
- List$deserialize(in, &path.components, pointers, List$info(&Text$info));
- *(Path_t *)obj = path;
-}
-
-public
-const TypeInfo_t Path$info = {.size = sizeof(Path_t),
- .align = __alignof__(Path_t),
- .tag = OpaqueInfo,
- .metamethods = {
- .as_text = Path$as_text,
- .hash = Path$hash,
- .compare = Path$compare,
- .equal = Path$equal,
- .is_none = Path$is_none,
- .serialize = Path$serialize,
- .deserialize = Path$deserialize,
- }};
-
-public
-const TypeInfo_t PathType$info = {
- .size = sizeof(PathType_t),
- .align = __alignof__(PathType_t),
- .metamethods = PackedDataEnum$metamethods,
+const TypeInfo_t Path$HomePath$$info = {
+ .size = sizeof(Path$HomePath$$type),
+ .align = __alignof__(Path$HomePath$$type),
+ .metamethods = Struct$metamethods,
+ .tag = StructInfo,
+ .StructInfo =
+ {
+ .name = "HomePath",
+ .num_fields = 1,
+ .fields = (NamedType_t[1]){{
+ .name = "components",
+ .type = List$info(&Text$info),
+ }},
+ },
+};
+
+public
+const TypeInfo_t Path$info = {
+ .size = sizeof(Path_t),
+ .align = __alignof__(Path_t),
.tag = EnumInfo,
.EnumInfo =
{
- .name = "PathType",
+ .name = "Path",
.num_tags = 3,
- .tags = ((NamedType_t[3]){{.name = "Relative"}, {.name = "Absolute"}, {.name = "Home"}}),
+ .tags =
+ (NamedType_t[3]){
+ {.name = "AbsolutePath", &Path$AbsolutePath$$info},
+ {.name = "RelativePath", &Path$RelativePath$$info},
+ {.name = "HomePath", &Path$HomePath$$info},
+ },
+ },
+ .metamethods =
+ {
+ .as_text = Path$as_text,
+ .compare = Enum$compare,
+ .equal = Enum$equal,
+ .hash = Enum$hash,
+ .is_none = Enum$is_none,
+ .serialize = Enum$serialize,
+ .deserialize = Enum$deserialize,
},
};
diff --git a/src/stdlib/paths.h b/src/stdlib/paths.h
index 677631b2..7a175099 100644
--- a/src/stdlib/paths.h
+++ b/src/stdlib/paths.h
@@ -12,6 +12,9 @@
Path_t Path$from_str(const char *str);
Path_t Path$from_text(Text_t text);
// int Path$print(FILE *f, Path_t path);
+// UNSAFE: this works because each type of path has a .components in the same place
+#define Path$components(path) ((path).components)
+// END UNSAFE
const char *Path$as_c_string(Path_t path);
#define Path(str) Path$from_str(str)
Path_t Path$_concat(int n, Path_t items[n]);
@@ -69,5 +72,7 @@ bool Path$is_none(const void *obj, const TypeInfo_t *type);
void Path$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type);
void Path$deserialize(FILE *in, void *obj, List_t *pointers, const TypeInfo_t *type);
+extern const TypeInfo_t Path$AbsolutePath$$info;
+extern const TypeInfo_t Path$RelativePath$$info;
+extern const TypeInfo_t Path$HomePath$$info;
extern const TypeInfo_t Path$info;
-extern const TypeInfo_t PathType$info;