For parsing paths, use nested parens: (./foo), also add some methods

This commit is contained in:
Bruce Hill 2024-09-09 02:02:08 -04:00
parent 6752c60f32
commit 1fbe2cb5dd
4 changed files with 45 additions and 19 deletions

View File

@ -220,7 +220,7 @@ public void Path$create_directory(Path_t path, int permissions)
fail("Could not create directory: %k (%s)", &path, strerror(errno));
}
public Array_t Path$children(Path_t path, bool include_hidden)
static Array_t _filtered_children(Path_t path, bool include_hidden, mode_t filter)
{
if (Text$matches(path, Pattern("~/{..}")))
path = Paths(Text$format("%s", getenv("HOME")), Text$slice(path, I(2), I(-1)));
@ -240,13 +240,36 @@ public Array_t Path$children(Path_t path, bool include_hidden)
continue;
if (streq(dir->d_name, ".") || streq(dir->d_name, ".."))
continue;
Path_t child = Text$format("%.*s/%s", path_len, path_str, dir->d_name);
const char *child_str = heap_strf("%.*s/%s", path_len, path_str, dir->d_name);
struct stat sb;
if (stat(child_str, &sb) != 0)
continue;
if (!((sb.st_mode & S_IFMT) & filter))
continue;
Path_t child = Text$format("%s%s", child_str, ((sb.st_mode & S_IFMT) == S_IFDIR) ? "/" : ""); // Trailing slash for dirs
Array$insert(&children, &child, I(0), sizeof(Path_t));
}
closedir(d);
return children;
}
public Array_t Path$children(Path_t path, bool include_hidden)
{
return _filtered_children(path, include_hidden, (mode_t)-1);
}
public Array_t Path$files(Path_t path, bool include_hidden)
{
return _filtered_children(path, include_hidden, S_IFREG);
}
public Array_t Path$subdirectories(Path_t path, bool include_hidden)
{
return _filtered_children(path, include_hidden, S_IFDIR);
}
public const TypeInfo Path$info = {
.size=sizeof(Path_t),
.align=__alignof__(Path_t),

View File

@ -28,6 +28,8 @@ Text_t Path$read(Path_t path);
void Path$remove(Path_t path, bool ignore_missing);
void Path$create_directory(Path_t path, int permissions);
Array_t Path$children(Path_t path, bool include_hidden);
Array_t Path$files(Path_t path, bool include_hidden);
Array_t Path$subdirectories(Path_t path, bool include_hidden);
extern const TypeInfo Path$info;

View File

@ -254,14 +254,16 @@ env_t *new_compilation_unit(CORD *libname)
{"create_directory", "Path$create_directory", "func(path:Path, permissions=0o644_i32)"},
{"escape_text", "Path$escape_text", "func(text:Text)->Path"},
{"exists", "Path$exists", "func(path:Path)->Bool"},
{"files", "Path$children", "func(path:Path, include_hidden=no)->[Path]"},
{"is_directory", "Path$is_directory", "func(path:Path, follow_symlinks=yes)->Bool"},
{"is_file", "Path$is_file", "func(path:Path, follow_symlinks=yes)->Bool"},
{"is_socket", "Path$is_socket", "func(path:Path, follow_symlinks=yes)->Bool"},
{"is_symlink", "Path$is_symlink", "func(path:Path)->Bool"},
{"read", "Path$read", "func(path:Path)->Text"},
{"relative", "Path$relative", "func(path:Path, relative_to=./)->Path"},
{"relative", "Path$relative", "func(path:Path, relative_to=(./))->Path"},
{"remove", "Path$remove", "func(path:Path, ignore_missing=no)"},
{"resolved", "Path$resolved", "func(path:Path, relative_to=./)->Path"},
{"resolved", "Path$resolved", "func(path:Path, relative_to=(./))->Path"},
{"subdirectories", "Path$children", "func(path:Path, include_hidden=no)->[Path]"},
{"write", "Path$write", "func(path:Path, text:Text, permissions=0o644_i32)"},
)},
{"Shell", Type(TextType, .lang="Shell", .env=namespace_env(env, "Shell")), "Shell_t", "Shell$info", TypedArray(ns_entry_t,

29
parse.c
View File

@ -1297,22 +1297,21 @@ PARSER(parse_text) {
}
PARSER(parse_path) {
// ("~/" / "./" / "../" / "/") *([^ \t\r\n\\;] / "\" .)
// "(" ("~/" / "./" / "../" / "/") ... ")"
const char *start = pos;
if (!(match(&pos, "~/")
|| match(&pos, "./")
|| match(&pos, "../")
|| match(&pos, "/")))
if (!(match(&pos, "(~/")
|| match(&pos, "(./")
|| match(&pos, "(../")
|| match(&pos, "(/")))
return NULL;
const char *chunk_start = start;
const char *chunk_start = start + 1;
ast_list_t *chunks = NULL;
CORD chunk_text = CORD_EMPTY;
int depths[] = {[(int)'('] = 0, [(int)'{'] = 0, [(int)'['] = 0};
int paren_depth = 1;
while (pos < ctx->file->text + ctx->file->len) {
switch (*pos) {
case '\r': case '\n': case ';': case ':': goto end_of_path;
case '\\': {
++pos;
chunk_text = CORD_asprintf("%r%.*s%c", chunk_text, (size_t)(pos - chunk_start), chunk_start, *pos);
@ -1338,15 +1337,15 @@ PARSER(parse_path) {
chunk_start = pos;
continue;
}
case ')': case '}': case ']': {
if (depths[(int)*pos] <= 0)
goto end_of_path;
depths[(int)*pos] -= 1;
case '(': {
paren_depth += 1;
++pos;
continue;
}
case '(': case '{': case '[': {
depths[(int)*pos] += 1;
case ')': {
paren_depth -= 1;
if (paren_depth == 0)
goto end_of_path;
++pos;
continue;
}
@ -1363,7 +1362,7 @@ PARSER(parse_path) {
chunks = new(ast_list_t, .ast=literal, .next=chunks);
}
match(&pos, ";"); // optional trailing semicolon
expect_closing(ctx, &pos, ")", "I was expecting a ')' to finish this path");
REVERSE_LIST(chunks);
return NewAST(ctx->file, start, pos, TextJoin, .lang="Path", .children=chunks);