For parsing paths, use nested parens: (./foo), also add some methods
This commit is contained in:
parent
6752c60f32
commit
1fbe2cb5dd
@ -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),
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
29
parse.c
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user