Skip to content
Draft
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ fn main() {
.flag("-diag-suppress=177") // variable was declared but never referenced
.flag("-diag-suppress=550") // variable was set but never used
.flag("-diag-suppress=20039") // a __host__ function redeclared with __device__, hence treated as a __host__ __device__ function
.flag("-diag-suppress=68") // integer conversion resulted in a change of sign
.flag("-diag-suppress=2464") // conversion from a string literal to "char *" is deprecated
.compile("hvm-cu");

println!("cargo:rustc-cfg=feature=\"cuda\"");
Expand Down
4 changes: 2 additions & 2 deletions src/hvm.c
Original file line number Diff line number Diff line change
Expand Up @@ -809,7 +809,7 @@ static inline Port enter(Net* net, Port var) {
}

// Atomically Links `A ~ B`.
static inline void link(Net* net, TM* tm, Port A, Port B) {
static inline void link_ports(Net* net, TM* tm, Port A, Port B) {
// Attempts to directionally point `A ~> B`
while (true) {
// If `A` is NODE: swap `A` and `B`, and continue
Expand Down Expand Up @@ -842,7 +842,7 @@ static inline void link(Net* net, TM* tm, Port A, Port B) {

// Links `A ~ B` (as a pair).
static inline void link_pair(Net* net, TM* tm, Pair AB) {
link(net, tm, get_fst(AB), get_snd(AB));
link_ports(net, tm, get_fst(AB), get_snd(AB));
}

// Interactions
Expand Down
120 changes: 120 additions & 0 deletions src/run.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
#include <dlfcn.h>
#include <errno.h>
#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#include "hvm.c"

// Readback: λ-Encoded Ctr
Expand Down Expand Up @@ -716,6 +719,120 @@ Port io_dl_close(Net* net, Book* book, Port argm) {
return inject_ok(net, new_port(ERA, 0));
}

// Deletes a single file or an empty directory at the specified path.
// Returns Ok(None) if successful, or Err(reason) if an error occurs.
// This function attempts to remove both files and empty directories without
// first checking the type of the path.
// Returns: Result<*, IOError<i24>>
Port io_delete_file(Net* net, Book* book, Port argm) {
Str path = readback_str(net, book, argm);

int result = remove(path.buf);
free(path.buf);

if (result == 0) {
return inject_ok(net, new_port(ERA, 0));
} else {
return inject_io_err_inner(net, new_port(NUM, new_i24(errno)));
}
}

int delete_directory_recursive(const char* path) {
DIR *d = opendir(path);
size_t path_len = strlen(path);
int r = -1;

if (d) {
struct dirent *p;
r = 0;

while (!r && (p = readdir(d))) {
int r2 = -1;
char *buf;
size_t len;

if (!strcmp(p->d_name, ".") || !strcmp(p->d_name, "..")) {
continue;
}

len = path_len + strlen(p->d_name) + 2;
buf = malloc(len);

if (buf) {
struct stat statbuf;
snprintf(buf, len, "%s/%s", path, p->d_name);

if (!stat(buf, &statbuf)) {
if (S_ISDIR(statbuf.st_mode)) {
r2 = delete_directory_recursive(buf);
} else {
r2 = remove(buf);
}
}

free(buf);
}

r = r2;
}

closedir(d);
}

if (!r) {
r = rmdir(path);
}

return r;
}

// Deletes a directory at the specified path. If recursive is True,
// it will delete the directory and all its contents.
// Returns Ok(None) if successful, or Err(reason) if an error occurs.
// Note: For non-recursive deletion of an empty directory,
// this function behaves the same as delete_file(path).
// Returns: Result<*, IOError<i24>>
Port io_delete_directory(Net* net, Book* book, Port argm) {
Tup tup = readback_tup(net, book, argm, 2);
if (2 != tup.elem_len) {
return inject_io_err_type(net);
}

Str path = readback_str(net, book, tup.elem_buf[0]);
u32 rec = get_u24(get_val(tup.elem_buf[1]));

int res;
if (rec) {
res = delete_directory_recursive(path.buf);
} else {
res = rmdir(path.buf);
}
free(path.buf);

if (0 == res) {
return inject_ok(net, new_port(ERA, 0));
} else {
return inject_io_err_inner(net, new_port(NUM, new_i24(errno)));
}
}

// Creates a new directory with the given path.
// Returns Ok(None) if sucessfull, or Err(reason) if an error occurs.
// Returns: Result<*, IOError<i24>>
Port io_mkdir(Net* net, Book* book, Port argm) {
Str name = readback_str(net, book, argm);

const mode_t mode = 0777;
int status = mkdir(name.buf, mode);
free(name.buf);

if (status) {
return inject_io_err_inner(net, new_port(NUM, new_i24(errno)));
} else {
return inject_ok(net, new_port(ERA, 0));
}
}

// Book Loader
// -----------

Expand All @@ -731,6 +848,9 @@ void book_init(Book* book) {
book->ffns_buf[book->ffns_len++] = (FFn){"DL_OPEN", io_dl_open};
book->ffns_buf[book->ffns_len++] = (FFn){"DL_CALL", io_dl_call};
book->ffns_buf[book->ffns_len++] = (FFn){"DL_CLOSE", io_dl_open};
book->ffns_buf[book->ffns_len++] = (FFn){"DELETE_FILE", io_delete_file};
book->ffns_buf[book->ffns_len++] = (FFn){"DELETE_DIRECTORY", io_delete_directory};
book->ffns_buf[book->ffns_len++] = (FFn){"MKDIR", io_mkdir};
}

// Monadic IO Evaluator
Expand Down
122 changes: 122 additions & 0 deletions src/run.cu
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
#include <dlfcn.h>
#include <errno.h>
#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>
#include <string.h>
#include <unistd.h>
#include "hvm.cu"

// Readback: λ-Encoded Ctr
Expand Down Expand Up @@ -834,6 +838,121 @@ Port io_dl_close(GNet* gnet, Book* book, Port argm) {
return gnet_inject_ok(gnet, new_port(ERA, 0));
}

// Deletes a single file or an empty directory at the specified path.
// Returns Ok(None) if successful, or Err(reason) if an error occurs.
// This function attempts to remove both files and empty directories without
// first checking the type of the path.
// Returns: Result<*, IOError<i24>>
Port io_delete_file(GNet* gnet, Port argm) {
Str s = gnet_readback_str(gnet, argm);

int result = remove(s.buf);
free(s.buf);

if (result == 0) {
return gnet_inject_ok(gnet, new_port(ERA, 0));
} else {
return gnet_inject_io_err_inner(gnet, new_port(NUM, new_i24(errno)));
}
}

int delete_directory_recursive(const char* path) {
DIR* d = opendir(path);
size_t path_len = strlen(path);
int r = -1;

if (d) {
struct dirent *p;
r = 0;

while (!r && (p = readdir(d))) {
int r2 = -1;
char* buf;
size_t len;

if (!strcmp(p->d_name, ".") || !strcmp(p->d_name, "..")) {
continue;
}

len = path_len + strlen(p->d_name) + 2;
buf = (char*) malloc(len);

if (buf) {
struct stat statbuf;
snprintf(buf, len, "%s/%s", path, p->d_name);

if (!stat(buf, &statbuf)) {
if (S_ISDIR(statbuf.st_mode)) {
r2 = delete_directory_recursive(buf);
} else {
r2 = remove(buf);
}
}

free(buf);
}

r = r2;
}

closedir(d);
}

if (!r) {
r = rmdir(path);
}

return r;
}

// Deletes a directory at the specified path. If recursive is True,
// it will delete the directory and all its contents.
// Returns Ok(None) if successful, or Err(reason) if an error occurs.
// Note: For non-recursive deletion of an empty directory,
// this function behaves the same as delete_file(path).
// Returns: Result<*, IOError<i24>>
Port io_delete_directory(GNet* gnet, Port argm) {
Tup tup = gnet_readback_tup(gnet, argm, 2);
if (tup.elem_len != 2) {
fprintf(stderr, "io_delete_directory: expected tuple\n");

return gnet_inject_io_err_type(gnet);
}

Str path = gnet_readback_str(gnet, tup.elem_buf[0]);
u32 rec = get_u24(get_val(tup.elem_buf[1]));
int res;
if (rec) {
res = delete_directory_recursive(path.buf);
} else {
res = rmdir(path.buf);
}
free(path.buf);

if (res == 0) {
return gnet_inject_ok(gnet, new_port(ERA, 0));
} else {
return gnet_inject_io_err_inner(gnet, new_port(NUM, new_i24(errno)));
}
}

// Creates a new directory with the given path.
// Returns Ok(None) if sucessfull, or Err(reason) if an error occurs.
// Returns: Result<*, IOError<i24>>
Port io_mkdir(GNet* gnet, Port argm) {
Str name = gnet_readback_str(gnet, argm);

const mode_t mode = 0777;
int result = mkdir(name.buf, mode);
free(name.buf);

if (result) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we keep the var names the same? In the C runtime you called this status.

return gnet_inject_io_err_inner(gnet, new_port(NUM, new_i24(errno)));
} else {
return gnet_inject_ok(gnet, new_port(ERA, 0));
}
}

void book_init(Book* book) {
book->ffns_buf[book->ffns_len++] = (FFn){"READ", io_read};
book->ffns_buf[book->ffns_len++] = (FFn){"OPEN", io_open};
Expand All @@ -846,6 +965,9 @@ void book_init(Book* book) {
book->ffns_buf[book->ffns_len++] = (FFn){"DL_OPEN", io_dl_open};
book->ffns_buf[book->ffns_len++] = (FFn){"DL_CALL", io_dl_call};
book->ffns_buf[book->ffns_len++] = (FFn){"DL_CLOSE", io_dl_open};
book->ffns_buf[book->ffns_len++] = (FFn){"DELETE_FILE", io_delete_file};
book->ffns_buf[book->ffns_len++] = (FFn){"DELETE_DIRECTORY", io_delete_directory};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Following the convention of using the C/unix name for these functions, it would be better to call them "RM" and "RMDIR".

Also, if we're using "rmdir" we definitely should not also use "delete_directory", either go with one style or the other.

HVM in general prefers short names

book->ffns_buf[book->ffns_len++] = (FFn){"MKDIR", io_mkdir};

cudaMemcpyToSymbol(BOOK, book, sizeof(Book));
}
Expand Down
21 changes: 21 additions & 0 deletions tests/programs/io/create_directory.bend
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#{
Creates the batata directory and then deletes it.
#}

test-io = 1

IO/FS/mkdir path =
(call "MKDIR" path)

IO/FS/delete_directory path recursive =
(call "DELETE_DIRECTORY" (path, recursive))

False = 0

main =
let path = "./batata"
with IO {
ask * = (IO/FS/mkdir path)
ask s = (IO/FS/delete_directory path False)
(wrap s)
}
16 changes: 16 additions & 0 deletions tests/programs/io/delete_dir_file.bend
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#{
Calls the delete_directory function with a file path as argument.
#}

test-io = 1

IO/FS/delete_directory path recursive =
(call "DELETE_DIRECTORY" (path, recursive))

False = 0

main =
with IO {
ask s = (IO/FS/delete_directory "./delete_dir_file.bend" False)
(wrap s)
}
32 changes: 32 additions & 0 deletions tests/programs/io/delete_dir_recursive.bend
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#{
Creates the following tree structure and then deletes A and its children.
A
|-- a.txt
|-- AA
| `-- aa.txt
`-- AB
`-- ab.txt
#}

test-io = 1

IO/FS/mkdir path =
(call "MKDIR" path)

IO/FS/delete_directory path recursive =
(call "DELETE_DIRECTORY" (path, recursive))

True = 1

main =
with IO {
ask * = (IO/FS/mkdir "A")
ask * = (IO/FS/mkdir "A/AA")
ask * = (IO/FS/mkdir "A/AB")
ask * = (IO/FS/write_file "A/a.txt" (String/encode_utf8 "a"))
ask * = (IO/FS/write_file "A/AA/aa.txt" (String/encode_utf8 "aa"))
ask * = (IO/FS/write_file "A/AB/ab.txt" (String/encode_utf8 "ab"))

ask s = (IO/FS/delete_directory "A" True)
(wrap s)
}
Loading