From 44ed8f14222f18646608765b8c7729222158bc34 Mon Sep 17 00:00:00 2001 From: Enrico Zandomeni Borba Date: Wed, 24 Jul 2024 16:41:44 +0200 Subject: [PATCH 01/17] return Result from IO functions --- src/run.c | 192 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 138 insertions(+), 54 deletions(-) diff --git a/src/run.c b/src/run.c index b072a1fd..a6cd3d40 100644 --- a/src/run.c +++ b/src/run.c @@ -1,4 +1,6 @@ #include +#include +#include #include "hvm.c" // Readback: λ-Encoded Ctr @@ -35,7 +37,11 @@ typedef struct Bytes { #define IO_DONE 0 #define IO_CALL 1 -// List Type +// Result Tags +#define RESULT_OK 0 +#define RESULT_ERR 1 + +// List Tags #define LIST_NIL 0 #define LIST_CONS 1 @@ -225,6 +231,64 @@ Port inject_bytes(Net* net, Bytes *bytes) { return port; } +/// Returns a λ-Encoded Ctr for a RESULT_OK: λt ((t RESULT_OK) val) +Port inject_ok(Net* net, Port val) { + if (!get_resources(net, tm[0], 0, 3, 1)) { + fprintf(stderr, "inject_ok: failed to get resources\n"); + return new_port(ERA, 0); + } + + u32 v1 = tm[0]->vloc[0]; + + u32 n1 = tm[0]->nloc[0]; + u32 n2 = tm[0]->nloc[1]; + u32 n3 = tm[0]->nloc[2]; + + vars_create(net, v1, NONE); + Port var = new_port(VAR, v1); + + node_create(net, n1, new_pair(val, var)); + node_create(net, n2, new_pair(new_port(NUM, new_u24(RESULT_OK)), new_port(CON, n1))); + node_create(net, n3, new_pair(new_port(CON, n2), var)); + + return new_port(CON, n3); +} + +/// Returns a λ-Encoded Ctr for a RESULT_ERR: λt ((t RESULT_ERR) err) +Port inject_err(Net* net, Port err) { + if (!get_resources(net, tm[0], 0, 3, 1)) { + fprintf(stderr, "inject_err: failed to get resources\n"); + return new_port(ERA, 0); + } + + u32 v1 = tm[0]->vloc[0]; + + u32 n1 = tm[0]->nloc[0]; + u32 n2 = tm[0]->nloc[1]; + u32 n3 = tm[0]->nloc[2]; + + vars_create(net, v1, NONE); + Port var = new_port(VAR, v1); + + node_create(net, n1, new_pair(err, var)); + node_create(net, n2, new_pair(new_port(NUM, new_u24(RESULT_ERR)), new_port(CON, n1))); + node_create(net, n3, new_pair(new_port(CON, n2), var)); + + return new_port(CON, n3); +} + +/// Returns a λ-Encoded Ctr for a RESULT_ERR: λt ((t RESULT_ERR) err) +/// where the Error variant contains a String. +/// `err` must be `NUL`-terminated. +Port inject_err_str(Net* net, char* err) { + Bytes err_bytes; + err_bytes.buf = err; + err_bytes.len = strlen(err_bytes.buf); + Port err_port = inject_bytes(net, &err_bytes); + + return inject_err(net, err_port); +} + // Primitive IO Fns // ----------------- @@ -255,7 +319,6 @@ FILE* readback_file(Port port) { FILE* fp = FILE_POINTERS[idx]; if (fp == NULL) { - fprintf(stderr, "invalid file descriptor\n"); return NULL; } @@ -282,19 +345,19 @@ void* readback_dylib(Port port) { // Reads from a file a specified number of bytes. // `argm` is a tuple of (file_descriptor, num_bytes). +// Returns: Result Port io_read(Net* net, Book* book, Port argm) { Tup tup = readback_tup(net, book, argm, 2); if (tup.elem_len != 2) { fprintf(stderr, "io_read: expected 2-tuple\n"); - return new_port(ERA, 0); + return inject_err(net, new_port(ERA, 0)); } FILE* fp = readback_file(tup.elem_buf[0]); u32 num_bytes = get_u24(get_val(tup.elem_buf[1])); if (fp == NULL) { - fprintf(stderr, "io_read: invalid file descriptor\n"); - return new_port(ERA, 0); + return inject_err(net, new_port(NUM, new_i24(EBADF))); } /// Read a string. @@ -303,25 +366,27 @@ Port io_read(Net* net, Book* book, Port argm) { bytes.len = fread(bytes.buf, sizeof(char), num_bytes, fp); if ((bytes.len != num_bytes) && ferror(fp)) { - fprintf(stderr, "io_read: failed to read\n"); free(bytes.buf); - return new_port(ERA, 0); + return inject_err(net, new_port(NUM, new_i24(ferror(fp)))); } // Convert it to a port. Port ret = inject_bytes(net, &bytes); free(bytes.buf); - return ret; + + return inject_ok(net, ret); } // Opens a file with the provided mode. // `argm` is a tuple (CON node) of the // file name and mode as strings. +// Returns: Result Port io_open(Net* net, Book* book, Port argm) { Tup tup = readback_tup(net, book, argm, 2); if (tup.elem_len != 2) { fprintf(stderr, "io_open: expected 2-tuple\n"); - return new_port(ERA, 0); + + return inject_err(net, new_port(ERA, 0)); } Str name = readback_str(net, book, tup.elem_buf[0]); @@ -334,78 +399,82 @@ Port io_open(Net* net, Book* book, Port argm) { free(name.buf); free(mode.buf); - return new_port(NUM, new_u24(fd)); + if (FILE_POINTERS[fd] == NULL) { + return inject_err(net, new_port(NUM, new_i24(errno))); + } + + return inject_ok(net, new_port(NUM, new_u24(fd))); } } - fprintf(stderr, "io_open: too many open files\n"); - free(name.buf); free(mode.buf); - return new_port(ERA, 0); + fprintf(stderr, "io_open: returning ERR\n"); + + // too many open files + return inject_err(net, new_port(NUM, new_i24(EMFILE))); } // Closes a file, reclaiming the file descriptor. +// Returns: Result<*, i24> Port io_close(Net* net, Book* book, Port argm) { FILE* fp = readback_file(argm); if (fp == NULL) { - fprintf(stderr, "io_close: invalid file descriptor\n"); - return new_port(ERA, 0); + return inject_err(net, new_port(NUM, new_i24(EBADF))); } - int err = fclose(fp) != 0; - if (err != 0) { - fprintf(stderr, "io_close: failed to close: %i\n", err); - return new_port(ERA, 0); + if (fclose(fp) != 0) { + return inject_err(net, new_port(NUM, new_i24(ferror(fp)))); } FILE_POINTERS[get_u24(get_val(argm))] = NULL; - return new_port(ERA, 0); + + return inject_ok(net, new_port(ERA, 0)); } // Writes a list of bytes to a file. // `argm` is a tuple (CON node) of the // file descriptor and list of bytes to write. +// Returns: Result<*, i24 | *> Port io_write(Net* net, Book* book, Port argm) { Tup tup = readback_tup(net, book, argm, 2); if (tup.elem_len != 2) { fprintf(stderr, "io_write: expected 2-tuple\n"); - return new_port(ERA, 0); + return inject_err(net, new_port(ERA, 0)); } FILE* fp = readback_file(tup.elem_buf[0]); Bytes bytes = readback_bytes(net, book, tup.elem_buf[1]); if (fp == NULL) { - fprintf(stderr, "io_write: invalid file descriptor\n"); free(bytes.buf); - return new_port(ERA, 0); + + return inject_err(net, new_port(NUM, new_i24(EBADF))); } if (fwrite(bytes.buf, sizeof(char), bytes.len, fp) != bytes.len) { - fprintf(stderr, "io_write: failed to write\n"); + free(bytes.buf); + return inject_err(net, new_port(NUM, new_i24(ferror(fp)))); } free(bytes.buf); - return new_port(ERA, 0); + return inject_ok(net, new_port(ERA, 0)); } // Flushes an output stream. +// Returns: Result<*, i24> Port io_flush(Net* net, Book* book, Port argm) { FILE* fp = readback_file(argm); if (fp == NULL) { - fprintf(stderr, "io_flush: invalid file descriptor\n"); - return new_port(ERA, 0); + return inject_err(net, new_port(NUM, new_i24(EBADF))); } - int err = fflush(fp) != 0; - if (err != 0) { - fprintf(stderr, "io_flush: failed to flush: %i\n", err); - return new_port(ERA, 0); + if (fflush(fp) != 0) { + return inject_err(net, new_port(NUM, new_i24(ferror(fp)))); } - return new_port(ERA, 0); + return inject_ok(net, new_port(ERA, 0)); } // Seeks to a position in a file. @@ -416,11 +485,12 @@ Port io_flush(Net* net, Book* book, Port argm) { // - 0 (SEEK_SET): beginning of file // - 1 (SEEK_CUR): current position of the file pointer // - 2 (SEEK_END): end of the file +// Returns: Result<*, i24 | *> Port io_seek(Net* net, Book* book, Port argm) { Tup tup = readback_tup(net, book, argm, 3); if (tup.elem_len != 3) { fprintf(stderr, "io_seek: expected 3-tuple\n"); - return new_port(ERA, 0); + return inject_err(net, new_port(ERA, 0)); } FILE* fp = readback_file(tup.elem_buf[0]); @@ -428,8 +498,7 @@ Port io_seek(Net* net, Book* book, Port argm) { u32 whence = get_i24(get_val(tup.elem_buf[2])); if (fp == NULL) { - fprintf(stderr, "io_write: invalid file descriptor\n"); - return new_port(ERA, 0); + return inject_err(net, new_port(NUM, new_i24(EBADF))); } int cwhence; @@ -439,18 +508,19 @@ Port io_seek(Net* net, Book* book, Port argm) { case 2: cwhence = SEEK_END; break; default: fprintf(stderr, "io_seek: invalid whence\n"); - return new_port(ERA, 0); + return inject_err(net, new_port(ERA, 0)); } if (fseek(fp, offset, cwhence) != 0) { - fprintf(stderr, "io_seek: failed to seek\n"); + return inject_err(net, new_port(NUM, new_i24(ferror(fp)))); } - return new_port(ERA, 0); + return inject_ok(net, new_port(ERA, 0)); } // Returns the current time as a tuple of the high // and low 24 bits of a 48-bit nanosecond timestamp. +// Returns: (u24, u24) Port io_get_time(Net* net, Book* book, Port argm) { // Get the current time in nanoseconds u64 time_ns = time64(); @@ -461,18 +531,19 @@ Port io_get_time(Net* net, Book* book, Port argm) { u32 lps = 0; u32 loc = node_alloc_1(net, tm[0], &lps); node_create(net, loc, new_pair(new_port(NUM, new_u24(time_hi)), new_port(NUM, new_u24(time_lo)))); - // Return the encoded time + return new_port(CON, loc); } // Sleeps. // `argm` is a tuple (CON node) of the high and low // 24 bits for a 48-bit duration in nanoseconds. +// Returns: Result<*, *> Port io_sleep(Net* net, Book* book, Port argm) { Tup tup = readback_tup(net, book, argm, 2); if (tup.elem_len != 2) { fprintf(stderr, "io_sleep: expected 2-tuple\n"); - return new_port(ERA, 0); + return inject_err(net, new_port(ERA, 0)); } // Get the sleep duration node @@ -487,14 +558,15 @@ Port io_sleep(Net* net, Book* book, Port argm) { ts.tv_sec = dur_ns / 1000000000; ts.tv_nsec = dur_ns % 1000000000; nanosleep(&ts, NULL); - // Return an eraser - return new_port(ERA, 0); + + return inject_ok(net, new_port(ERA, 0)); } // Opens a dylib at the provided path. // `argm` is a tuple of `filename` and `lazy`. // `filename` is a λ-encoded string. // `lazy` is a `bool` indicating if functions should be lazily loaded. +// Returns: Result Port io_dl_open(Net* net, Book* book, Port argm) { Tup tup = readback_tup(net, book, argm, 2); Str str = readback_str(net, book, tup.elem_buf[0]); @@ -506,17 +578,20 @@ Port io_dl_open(Net* net, Book* book, Port argm) { if (DYLIBS[dl] == NULL) { DYLIBS[dl] = dlopen(str.buf, flags); if (DYLIBS[dl] == NULL) { - fprintf(stderr, "failed to open dylib '%s': %s\n", str.buf, dlerror()); + Bytes err_bytes; + err_bytes.buf = dlerror(); + err_bytes.len = strlen(err_bytes.buf); + Port err_port = inject_bytes(net, &err_bytes); + free(err_bytes.buf); - return new_port(ERA, 0); + return inject_err(net, err_port); } - return new_port(NUM, new_u24(dl)); + return inject_ok(net, new_port(NUM, new_u24(dl))); } } - fprintf(stderr, "io_dl_open: too many open dylibs\n"); - return new_port(ERA, 0); + return inject_err_str(net, "too many open dylibs"); } // Calls a function from a loaded dylib. @@ -524,11 +599,17 @@ Port io_dl_open(Net* net, Book* book, Port argm) { // `dylib_handle` is the numeric node returned from a `DL_OPEN` call. // `symbol` is a λ-encoded string of the symbol name. // `args` is the argument to be provided to the dylib symbol. +// +// This function returns a Result with an Ok variant containing an +// arbitrary type. +// +// Returns Result Port io_dl_call(Net* net, Book* book, Port argm) { Tup tup = readback_tup(net, book, argm, 3); if (tup.elem_len != 3) { fprintf(stderr, "io_dl_call: expected 3-tuple\n"); - return new_port(ERA, 0); + + return inject_err(net, new_port(ERA, 0)); } void* dl = readback_dylib(tup.elem_buf[0]); @@ -538,28 +619,31 @@ Port io_dl_call(Net* net, Book* book, Port argm) { Port (*func)(Net*, Book*, Port) = dlsym(dl, symbol.buf); char* error = dlerror(); if (error != NULL) { - fprintf(stderr, "io_dl_call: failed to get symbol '%s': %s\n", symbol.buf, error); + return inject_err_str(net, error); } - return func(net, book, tup.elem_buf[2]); + return inject_ok(net, func(net, book, tup.elem_buf[2])); } // Closes a loaded dylib, reclaiming the handle. +// +// Returns: Result<*, String | *> Port io_dl_close(Net* net, Book* book, Port argm) { void* dl = readback_dylib(argm); if (dl == NULL) { fprintf(stderr, "io_dl_close: invalid handle\n"); - return new_port(ERA, 0); + + return inject_err(net, new_port(ERA, 0)); } int err = dlclose(dl) != 0; if (err != 0) { - fprintf(stderr, "io_dl_close: failed to close: %i\n", err); - return new_port(ERA, 0); + return inject_err_str(net, dlerror()); } DYLIBS[get_u24(get_val(argm))] = NULL; - return new_port(ERA, 0); + + return inject_ok(net, new_port(ERA, 0)); } // Book Loader From 909d468b6ceb56bcf6fad3c8f24f9e7fb04bffd0 Mon Sep 17 00:00:00 2001 From: Enrico Zandomeni Borba Date: Thu, 25 Jul 2024 07:25:14 +0200 Subject: [PATCH 02/17] (cuda attempt) return Result from IO functions --- src/run.c | 15 ++- src/run.cu | 269 +++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 214 insertions(+), 70 deletions(-) diff --git a/src/run.c b/src/run.c index a6cd3d40..e8c81883 100644 --- a/src/run.c +++ b/src/run.c @@ -400,7 +400,7 @@ Port io_open(Net* net, Book* book, Port argm) { free(mode.buf); if (FILE_POINTERS[fd] == NULL) { - return inject_err(net, new_port(NUM, new_i24(errno))); + return inject_err(net, new_port(NUM, new_i24(errno))); } return inject_ok(net, new_port(NUM, new_u24(fd))); @@ -455,10 +455,12 @@ Port io_write(Net* net, Book* book, Port argm) { if (fwrite(bytes.buf, sizeof(char), bytes.len, fp) != bytes.len) { free(bytes.buf); + return inject_err(net, new_port(NUM, new_i24(ferror(fp)))); } free(bytes.buf); + return inject_ok(net, new_port(ERA, 0)); } @@ -577,14 +579,11 @@ Port io_dl_open(Net* net, Book* book, Port argm) { for (u32 dl = 0; dl < sizeof(DYLIBS); dl++) { if (DYLIBS[dl] == NULL) { DYLIBS[dl] = dlopen(str.buf, flags); - if (DYLIBS[dl] == NULL) { - Bytes err_bytes; - err_bytes.buf = dlerror(); - err_bytes.len = strlen(err_bytes.buf); - Port err_port = inject_bytes(net, &err_bytes); - free(err_bytes.buf); - return inject_err(net, err_port); + free(str.buf); + + if (DYLIBS[dl] == NULL) { + return inject_err_str(net, dlerror()); } return inject_ok(net, new_port(NUM, new_u24(dl))); diff --git a/src/run.cu b/src/run.cu index 3350d9f2..408b29ac 100644 --- a/src/run.cu +++ b/src/run.cu @@ -35,6 +35,10 @@ typedef struct Bytes { #define IO_DONE 0 #define IO_CALL 1 +// Result Tags +#define RESULT_OK 0 +#define RESULT_ERR 1 + // List Type #define LIST_NIL 0 #define LIST_CONS 1 @@ -264,6 +268,135 @@ extern "C" Port gnet_inject_bytes(GNet* gnet, Bytes *bytes) { return ret; } +/// Returns a λ-Encoded Ctr for a RESULT_OK: λt ((t RESULT_OK) val) +Port inject_ok(Net* net, TM* tm, Port val) { + if (!get_resources(net, tm, 0, 3, 1)) { + fprintf(stderr, "inject_ok: failed to get resources\n"); + return new_port(ERA, 0); + } + + u32 v1 = tm->vloc[0]; + + u32 n1 = tm->nloc[0]; + u32 n2 = tm->nloc[1]; + u32 n3 = tm->nloc[2]; + + vars_create(net, v1, NONE); + Port var = new_port(VAR, v1); + + node_create(net, n1, new_pair(val, var)); + node_create(net, n2, new_pair(new_port(NUM, new_u24(RESULT_OK)), new_port(CON, n1))); + node_create(net, n3, new_pair(new_port(CON, n2), var)); + + return new_port(CON, n3); +} + +__global__ void make_ok_port(GNet* gnet, Port val, Port* ret) { + if (GID() == 0) { + TM tm = tmem_new(); + Net net = vnet_new(gnet, NULL, gnet->turn); + *ret = inject_ok(&net, &tm, val); + } +} + +extern "C" Port gnet_inject_ok(GNet* gnet, Port val) { + Port* d_ret; + cudaMalloc(&d_ret, sizeof(Port)); + + make_ok_port<<<1,1>>>(gnet, val, d_ret); + + Port ret; + cudaMemcpy(&ret, d_ret, sizeof(Port), cudaMemcpyDeviceToHost); + cudaFree(d_ret); + + return ret; +} + +/// Returns a λ-Encoded Ctr for a RESULT_ERR: λt ((t RESULT_ERR) err) +Port inject_err(Net* net, TM* tm, Port err) { + if (!get_resources(net, tm, 0, 3, 1)) { + fprintf(stderr, "inject_err: failed to get resources\n"); + return new_port(ERA, 0); + } + + u32 v1 = tm->vloc[0]; + + u32 n1 = tm->nloc[0]; + u32 n2 = tm->nloc[1]; + u32 n3 = tm->nloc[2]; + + vars_create(net, v1, NONE); + Port var = new_port(VAR, v1); + + node_create(net, n1, new_pair(err, var)); + node_create(net, n2, new_pair(new_port(NUM, new_u24(RESULT_ERR)), new_port(CON, n1))); + node_create(net, n3, new_pair(new_port(CON, n2), var)); + + return new_port(CON, n3); +} + + +__global__ void make_err_port(GNet* gnet, Port val, Port* ret) { + if (GID() == 0) { + TM tm = tmem_new(); + Net net = vnet_new(gnet, NULL, gnet->turn); + *ret = inject_err(&net, &tm, val); + } +} + +extern "C" Port gnet_inject_err(GNet* gnet, Port val) { + Port* d_ret; + cudaMalloc(&d_ret, sizeof(Port)); + + make_err_port<<<1,1>>>(gnet, val, d_ret); + + Port ret; + cudaMemcpy(&ret, d_ret, sizeof(Port), cudaMemcpyDeviceToHost); + cudaFree(d_ret); + + return ret; +} + +/// Returns a λ-Encoded Ctr for a RESULT_ERR: λt ((t RESULT_ERR) err) +/// where the Error variant contains a String. +/// `err` must be `NUL`-terminated. +Port inject_err_str(Net* net, TM *tm, char* err) { + Bytes err_bytes; + err_bytes.buf = err; + err_bytes.len = strlen(err_bytes.buf); + Port err_port = inject_bytes(net, tm, &err_bytes); + + return inject_err(net, tm, err_port); +} + +__global__ void make_err_str_port(GNet* gnet, char* err, Port* ret) { + if (GID() == 0) { + TM tm = tmem_new(); + Net net = vnet_new(gnet, NULL, gnet->turn); + *ret = inject_err_str(&net, &tm, err); + } +} + +extern "C" Port gnet_inject_err_str(GNet* gnet, char* err) { + Port* d_ret; + cudaMalloc(&d_ret, sizeof(Port)); + + char* err_cu; + cudaMalloc(&d_ret, sizeof(char) * strlen(err)); + cudaMemcpy(err_cu, err, sizeof(char) * strlen(err), cudaMemcpyHostToDevice); + + make_err_str_port<<<1,1>>>(gnet, err, d_ret); + + cudaFree(err_cu); + + Port ret; + cudaMemcpy(&ret, d_ret, sizeof(Port), cudaMemcpyDeviceToHost); + cudaFree(d_ret); + + return ret; +} + + // Primitive IO Fns // ----------------- @@ -321,19 +454,19 @@ void* readback_dylib(Port port) { // Reads from a file a specified number of bytes. // `argm` is a tuple of (file_descriptor, num_bytes). +// Returns: Result Port io_read(GNet* gnet, Port argm) { Tup tup = gnet_readback_tup(gnet, argm, 2); if (tup.elem_len != 2) { fprintf(stderr, "io_read: expected 2-tuple\n"); - return new_port(ERA, 0); + return gnet_inject_err(gnet, new_port(ERA, 0)); } FILE* fp = readback_file(tup.elem_buf[0]); u32 num_bytes = get_u24(get_val(tup.elem_buf[1])); if (fp == NULL) { - fprintf(stderr, "io_read: invalid file descriptor\n"); - return new_port(ERA, 0); + return gnet_inject_err(gnet, new_port(NUM, new_i24(EBADF))); } /// Read a string. @@ -344,23 +477,26 @@ Port io_read(GNet* gnet, Port argm) { if ((bytes.len != num_bytes) && ferror(fp)) { fprintf(stderr, "io_read: failed to read\n"); free(bytes.buf); - return new_port(ERA, 0); + return gnet_inject_err(gnet, new_port(NUM, new_i24(ferror(fp)))); } // Convert it to a port. Port ret = gnet_inject_bytes(gnet, &bytes); free(bytes.buf); - return ret; + + return gnet_inject_ok(gnet, ret); } // Opens a file with the provided mode. // `argm` is a tuple (CON node) of the // file name and mode as strings. +// Returns: Result Port io_open(GNet* gnet, Port argm) { Tup tup = gnet_readback_tup(gnet, argm, 2); if (tup.elem_len != 2) { fprintf(stderr, "io_open: expected 2-tuple\n"); - return new_port(ERA, 0); + + return gnet_inject_err(gnet, new_port(ERA, 0)); } Str name = gnet_readback_str(gnet, tup.elem_buf[0]); @@ -373,78 +509,84 @@ Port io_open(GNet* gnet, Port argm) { free(name.buf); free(mode.buf); - return new_port(NUM, new_u24(fd)); + if (FILE_POINTERS[fd] == NULL) { + return gnet_inject_err(gnet, new_port(NUM, new_i24(errno))); + } + + return gnet_inject_ok(gnet, new_port(NUM, new_u24(fd))); } } - fprintf(stderr, "io_open: too many open files\n"); - free(name.buf); free(mode.buf); - return new_port(ERA, 0); + fprintf(stderr, "io_open: too many open files\n"); + + // too many open files + return gnet_inject_err(net, new_port(NUM, new_i24(EMFILE))); } // Closes a file, reclaiming the file descriptor. +// Returns: Result<*, i24> Port io_close(GNet* gnet, Port argm) { FILE* fp = readback_file(argm); if (fp == NULL) { - fprintf(stderr, "io_close: invalid file descriptor\n"); - return new_port(ERA, 0); + return gnet_inject_err(gnet, new_port(NUM, new_i24(EBADF))); } - int err = fclose(fp) != 0; - if (err != 0) { - fprintf(stderr, "io_close: failed to close: %i\n", err); - return new_port(ERA, 0); + if (fclose(fp) != 0) { + return gnet_inject_err(gnet, new_port(NUM, new_i24(ferror(fp)))); } FILE_POINTERS[get_u24(get_val(argm))] = NULL; - return new_port(ERA, 0); -} -// Flushes an output stream. -Port io_flush(GNet* gnet, Port argm) { - FILE* fp = readback_file(argm); - if (fp == NULL) { - fprintf(stderr, "io_flush: invalid file descriptor\n"); - return new_port(ERA, 0); - } - - int err = fflush(fp) != 0; - if (err != 0) { - fprintf(stderr, "io_flush: failed to flush: %i\n", err); - return new_port(ERA, 0); - } - - return new_port(ERA, 0); + return gnet_inject_ok(gnet, new_port(ERA, 0)); } // Writes a list of bytes to a file. // `argm` is a tuple (CON node) of the // file descriptor and list of bytes to write. +// Returns: Result<*, i24 | *> Port io_write(GNet* gnet, Port argm) { Tup tup = gnet_readback_tup(gnet, argm, 2); if (tup.elem_len != 2) { fprintf(stderr, "io_write: expected 2-tuple\n"); - return new_port(ERA, 0); + return gnet_inject_err(gnet, new_port(ERA, 0)); } FILE* fp = readback_file(tup.elem_buf[0]); Bytes bytes = gnet_readback_bytes(gnet, tup.elem_buf[1]); if (fp == NULL) { - fprintf(stderr, "io_write: invalid file descriptor\n"); free(bytes.buf); - return new_port(ERA, 0); + + return gnet_inject_err(gnet, new_port(NUM, new_i24(EBADF))); } if (fwrite(bytes.buf, sizeof(char), bytes.len, fp) != bytes.len) { - fprintf(stderr, "io_write: failed to write\n"); + free(bytes.buf); + + return gnet_inject_err(gnet, new_port(NUM, new_i24(ferror(fp)))); } free(bytes.buf); - return new_port(ERA, 0); + + return gnet_inject_ok(gnet, new_port(ERA, 0)); +} + +// Flushes an output stream. +// Returns: Result<*, i24> +Port io_flush(GNet* gnet, Port argm) { + FILE* fp = readback_file(argm); + if (fp == NULL) { + return gnet_inject_err(gnet, new_port(NUM, new_i24(EBADF))); + } + + if (fflush(fp) != 0) { + return gnet_inject_err(gnet, new_port(NUM, new_i24(ferror(fp)))); + } + + return gnet_inject_ok(gnet, new_port(ERA, 0)); } // Seeks to a position in a file. @@ -455,11 +597,12 @@ Port io_write(GNet* gnet, Port argm) { // - 0 (SEEK_SET): beginning of file // - 1 (SEEK_CUR): current position of the file pointer // - 2 (SEEK_END): end of the file +// Returns: Result<*, i24 | *> Port io_seek(GNet* gnet, Port argm) { Tup tup = gnet_readback_tup(gnet, argm, 3); if (tup.elem_len != 3) { fprintf(stderr, "io_seek: expected 3-tuple\n"); - return new_port(ERA, 0); + return gnet_inject_err(gnet, new_port(ERA, 0)); } FILE* fp = readback_file(tup.elem_buf[0]); @@ -467,8 +610,7 @@ Port io_seek(GNet* gnet, Port argm) { u32 whence = get_i24(get_val(tup.elem_buf[2])); if (fp == NULL) { - fprintf(stderr, "io_write: invalid file descriptor\n"); - return new_port(ERA, 0); + return gnet_inject_err(gnet, new_port(NUM, new_i24(EBADF))); } int cwhence; @@ -478,18 +620,19 @@ Port io_seek(GNet* gnet, Port argm) { case 2: cwhence = SEEK_END; break; default: fprintf(stderr, "io_seek: invalid whence\n"); - return new_port(ERA, 0); + return gnet_inject_err(gnet, new_port(ERA, 0)); } if (fseek(fp, offset, cwhence) != 0) { - fprintf(stderr, "io_seek: failed to seek\n"); + return gnet_inject_err(gnet, new_port(NUM, new_i24(ferror(fp)))); } - return new_port(ERA, 0); + return gnet_inject_ok(gnet, new_port(ERA, 0)); } // Returns the current time as a tuple of the high // and low 24 bits of a 48-bit nanosecond timestamp. +// Returns: (u24, u24) Port io_get_time(GNet* gnet, Port argm) { // Get the current time in nanoseconds u64 time_ns = time64(); @@ -503,11 +646,12 @@ Port io_get_time(GNet* gnet, Port argm) { // Sleeps. // `argm` is a tuple (CON node) of the high and low // 24 bits for a 48-bit duration in nanoseconds. +// Returns: Result<*, *> Port io_sleep(GNet* gnet, Port argm) { Tup tup = gnet_readback_tup(gnet, argm, 2); if (tup.elem_len != 2) { fprintf(stderr, "io_sleep: expected 3-tuple\n"); - return new_port(ERA, 0); + return gnet_inject_err(gnet, new_port(ERA, 0)); } // Get the sleep duration node @@ -522,14 +666,15 @@ Port io_sleep(GNet* gnet, Port argm) { ts.tv_sec = dur_ns / 1000000000; ts.tv_nsec = dur_ns % 1000000000; nanosleep(&ts, NULL); - // Return an eraser - return new_port(ERA, 0); + + return gnet_inject_ok(gnet, new_port(ERA, 0)); } // Opens a dylib at the provided path. // `argm` is a tuple of `filename` and `lazy`. // `filename` is a λ-encoded string. // `lazy` is a `bool` indicating if functions should be lazily loaded. +// Returns: Result Port io_dl_open(GNet* gnet, Port argm) { Tup tup = gnet_readback_tup(gnet, argm, 2); Str str = gnet_readback_str(gnet, tup.elem_buf[0]); @@ -540,20 +685,18 @@ Port io_dl_open(GNet* gnet, Port argm) { for (u32 dl = 0; dl < sizeof(DYLIBS); dl++) { if (DYLIBS[dl] == NULL) { DYLIBS[dl] = dlopen(str.buf, flags); - if (DYLIBS[dl] == NULL) { - fprintf(stderr, "failed to open dylib '%s': %s\n", str.buf, dlerror()); - return new_port(ERA, 0); - } else { - fprintf(stderr, "opened dylib '%s'\n", str.buf); + free(str.buf); + + if (DYLIBS[dl] == NULL) { + return gnet_inject_err_str(net, dlerror()); } - return new_port(NUM, new_u24(dl)); + return gnet_inject_ok(gnet, new_port(NUM, new_u24(dl))); } } - fprintf(stderr, "io_dl_open: too many open dylibs\n"); - return new_port(ERA, 0); + return gnet_inject_err_str(net, "too many open dylibs"); } // Calls a function from a loaded dylib. @@ -565,7 +708,8 @@ Port io_dl_call(GNet* gnet, Port argm) { Tup tup = gnet_readback_tup(gnet, argm, 3); if (tup.elem_len != 3) { fprintf(stderr, "io_dl_call: expected 3-tuple\n"); - return new_port(ERA, 0); + + return gnet_inject_err(gnet, new_port(ERA, 0)); } void* dl = readback_dylib(tup.elem_buf[0]); @@ -575,10 +719,10 @@ Port io_dl_call(GNet* gnet, Port argm) { Port (*func)(GNet*, Port) = (Port (*)(GNet*, Port)) dlsym(dl, symbol.buf); char* error = dlerror(); if (error != NULL) { - fprintf(stderr, "io_dl_call: failed to get symbol '%s': %s\n", symbol.buf, error); + return gnet_inject_err_str(gnet, error); } - return func(gnet, tup.elem_buf[2]); + return gnet_inject_ok(gnet, func(gnet, tup.elem_buf[2])); } // Closes a loaded dylib, reclaiming the handle. @@ -586,17 +730,18 @@ Port io_dl_close(Net* net, Book* book, Port argm) { void* dl = readback_dylib(argm); if (dl == NULL) { fprintf(stderr, "io_dl_close: invalid handle\n"); - return new_port(ERA, 0); + + return gnet_inject_err(gnet, new_port(ERA, 0)); } int err = dlclose(dl) != 0; if (err != 0) { - fprintf(stderr, "io_dl_close: failed to close: %i\n", err); - return new_port(ERA, 0); + return gnet_inject_err_str(gnet, dlerror()); } DYLIBS[get_u24(get_val(argm))] = NULL; - return new_port(ERA, 0); + + return gnet_inject_ok(gnet, new_port(ERA, 0)); } void book_init(Book* book) { From 27774331d50061df99640bc30ced29c56a994ea3 Mon Sep 17 00:00:00 2001 From: Enrico Zandomeni Borba Date: Thu, 1 Aug 2024 15:49:35 +0200 Subject: [PATCH 03/17] fix WORK mode not executing high-priority redexes because of the original form of this if-statement, threads that were in WORK mode and that had a high-priority redex but no low priority redexes would not do any work. this led to a bug where `save_redexes` was called while high-priority redexes were still present. --- src/hvm.cu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hvm.cu b/src/hvm.cu index 1ddb631b..5804531b 100644 --- a/src/hvm.cu +++ b/src/hvm.cu @@ -1714,7 +1714,7 @@ __global__ void evaluator(GNet* gnet) { u32 bag = tm.mode == SEED ? transpose(GID(), TPB, BPG) : GID(); u32 rpos = gnet->rbag_pos[bag]; for (tick = 0; tick < 1 << 9; ++tick) { - if (tm.rbag.lo_end > rpos) { + if (tm.rbag.lo_end > rpos || rbag_has_highs(&tm.rbag)) { if (interact(&net, &tm, pop_redex(&tm), gnet->turn)) { while (rbag_has_highs(&tm.rbag)) { if (!interact(&net, &tm, pop_redex(&tm), gnet->turn)) break; From ed069c881f77fcc31fa7ef39e09a594162006d6e Mon Sep 17 00:00:00 2001 From: Enrico Zandomeni Borba Date: Thu, 1 Aug 2024 15:53:45 +0200 Subject: [PATCH 04/17] remove redundant new_port(.., get_val(..)) --- src/hvm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hvm.c b/src/hvm.c index 8ef9f407..8d116de9 100644 --- a/src/hvm.c +++ b/src/hvm.c @@ -1234,7 +1234,7 @@ Port expand(Net* net, Book* book, Port port) { Port old = vars_load(net, get_val(ROOT)); Port got = peek(net, port); while (get_tag(got) == REF) { - boot_redex(net, new_pair(new_port(REF,get_val(got)), ROOT)); + boot_redex(net, new_pair(got, ROOT)); normalize(net, book); got = peek(net, vars_load(net, get_val(ROOT))); } From 1e5b9f52afc85c945c9cae93f97e3f3df137f781 Mon Sep 17 00:00:00 2001 From: Enrico Zandomeni Borba Date: Thu, 1 Aug 2024 15:55:45 +0200 Subject: [PATCH 05/17] (cuda) return result from io functions returns Result for appropriate io functions, matches up with C implementation --- src/run.cu | 68 +++++++++++++++++++++++++----------------------------- 1 file changed, 32 insertions(+), 36 deletions(-) diff --git a/src/run.cu b/src/run.cu index 408b29ac..33cbaefc 100644 --- a/src/run.cu +++ b/src/run.cu @@ -1,4 +1,6 @@ #include +#include +#include #include "hvm.cu" // Readback: λ-Encoded Ctr @@ -252,26 +254,26 @@ extern "C" Port gnet_inject_bytes(GNet* gnet, Bytes *bytes) { Port* d_ret; cudaMalloc(&d_ret, sizeof(Port)); - Bytes cu_bytes; - cu_bytes.len = bytes->len; + Bytes bytes_cu; + bytes_cu.len = bytes->len; - cudaMalloc(&cu_bytes.buf, sizeof(char) * cu_bytes.len); - cudaMemcpy(cu_bytes.buf, bytes->buf, sizeof(char) * cu_bytes.len, cudaMemcpyHostToDevice); + cudaMalloc(&bytes_cu.buf, sizeof(char) * bytes_cu.len); + cudaMemcpy(bytes_cu.buf, bytes->buf, sizeof(char) * bytes_cu.len, cudaMemcpyHostToDevice); - make_bytes_port<<<1,1>>>(gnet, cu_bytes, d_ret); + make_bytes_port<<<1,1>>>(gnet, bytes_cu, d_ret); Port ret; cudaMemcpy(&ret, d_ret, sizeof(Port), cudaMemcpyDeviceToHost); cudaFree(d_ret); - cudaFree(cu_bytes.buf); + cudaFree(bytes_cu.buf); return ret; } /// Returns a λ-Encoded Ctr for a RESULT_OK: λt ((t RESULT_OK) val) -Port inject_ok(Net* net, TM* tm, Port val) { +__device__ Port inject_ok(Net* net, TM* tm, Port val) { if (!get_resources(net, tm, 0, 3, 1)) { - fprintf(stderr, "inject_ok: failed to get resources\n"); + printf("inject_ok: failed to get resources\n"); return new_port(ERA, 0); } @@ -313,9 +315,9 @@ extern "C" Port gnet_inject_ok(GNet* gnet, Port val) { } /// Returns a λ-Encoded Ctr for a RESULT_ERR: λt ((t RESULT_ERR) err) -Port inject_err(Net* net, TM* tm, Port err) { +__device__ Port inject_err(Net* net, TM* tm, Port err) { if (!get_resources(net, tm, 0, 3, 1)) { - fprintf(stderr, "inject_err: failed to get resources\n"); + printf("inject_err: failed to get resources\n"); return new_port(ERA, 0); } @@ -360,34 +362,28 @@ extern "C" Port gnet_inject_err(GNet* gnet, Port val) { /// Returns a λ-Encoded Ctr for a RESULT_ERR: λt ((t RESULT_ERR) err) /// where the Error variant contains a String. /// `err` must be `NUL`-terminated. -Port inject_err_str(Net* net, TM *tm, char* err) { - Bytes err_bytes; - err_bytes.buf = err; - err_bytes.len = strlen(err_bytes.buf); - Port err_port = inject_bytes(net, tm, &err_bytes); +extern "C" Port gnet_inject_err_str(GNet* gnet, char* err) { + Port* d_bytes_port; + cudaMalloc(&d_bytes_port, sizeof(Port)); - return inject_err(net, tm, err_port); -} + Bytes bytes_cu; + bytes_cu.len = strlen(err); -__global__ void make_err_str_port(GNet* gnet, char* err, Port* ret) { - if (GID() == 0) { - TM tm = tmem_new(); - Net net = vnet_new(gnet, NULL, gnet->turn); - *ret = inject_err_str(&net, &tm, err); - } -} + cudaMalloc(&bytes_cu.buf, sizeof(char) * bytes_cu.len); + cudaMemcpy(bytes_cu.buf, err, sizeof(char) * bytes_cu.len, cudaMemcpyHostToDevice); -extern "C" Port gnet_inject_err_str(GNet* gnet, char* err) { - Port* d_ret; - cudaMalloc(&d_ret, sizeof(Port)); + make_bytes_port<<<1,1>>>(gnet, bytes_cu, d_bytes_port); - char* err_cu; - cudaMalloc(&d_ret, sizeof(char) * strlen(err)); - cudaMemcpy(err_cu, err, sizeof(char) * strlen(err), cudaMemcpyHostToDevice); + Port bytes_port; + cudaMemcpy(&bytes_port, d_bytes_port, sizeof(Port), cudaMemcpyDeviceToHost); + cudaFree(d_bytes_port); - make_err_str_port<<<1,1>>>(gnet, err, d_ret); + cudaFree(bytes_cu.buf); + + Port* d_ret; + cudaMalloc(&d_ret, sizeof(Port)); - cudaFree(err_cu); + make_err_port<<<1,1>>>(gnet, bytes_port, d_ret); Port ret; cudaMemcpy(&ret, d_ret, sizeof(Port), cudaMemcpyDeviceToHost); @@ -523,7 +519,7 @@ Port io_open(GNet* gnet, Port argm) { fprintf(stderr, "io_open: too many open files\n"); // too many open files - return gnet_inject_err(net, new_port(NUM, new_i24(EMFILE))); + return gnet_inject_err(gnet, new_port(NUM, new_i24(EMFILE))); } // Closes a file, reclaiming the file descriptor. @@ -689,14 +685,14 @@ Port io_dl_open(GNet* gnet, Port argm) { free(str.buf); if (DYLIBS[dl] == NULL) { - return gnet_inject_err_str(net, dlerror()); + return gnet_inject_err_str(gnet, dlerror()); } return gnet_inject_ok(gnet, new_port(NUM, new_u24(dl))); } } - return gnet_inject_err_str(net, "too many open dylibs"); + return gnet_inject_err_str(gnet, "too many open dylibs"); } // Calls a function from a loaded dylib. @@ -726,7 +722,7 @@ Port io_dl_call(GNet* gnet, Port argm) { } // Closes a loaded dylib, reclaiming the handle. -Port io_dl_close(Net* net, Book* book, Port argm) { +Port io_dl_close(GNet* gnet, Book* book, Port argm) { void* dl = readback_dylib(argm); if (dl == NULL) { fprintf(stderr, "io_dl_close: invalid handle\n"); From d029c7f03f40e353513044eef2caa43ca514642e Mon Sep 17 00:00:00 2001 From: Enrico Zandomeni Borba Date: Thu, 1 Aug 2024 15:57:21 +0200 Subject: [PATCH 06/17] remove redundant new_port(.., get_val(..)) --- src/hvm.cu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hvm.cu b/src/hvm.cu index 5804531b..ffa3401b 100644 --- a/src/hvm.cu +++ b/src/hvm.cu @@ -1873,7 +1873,7 @@ Port gnet_expand(GNet* gnet, Port port) { Port got = gnet_peek(gnet, port); //printf("expand %s\n", show_port(got).x); while (get_tag(got) == REF) { - gnet_boot_redex(gnet, new_pair(new_port(REF,get_val(got)), ROOT)); + gnet_boot_redex(gnet, new_pair(got, ROOT)); gnet_normalize(gnet); got = gnet_peek(gnet, gnet_vars_load(gnet, get_val(ROOT))); } From f93b294f8b6ddb185d357bb4efc16fb2391c9b1b Mon Sep 17 00:00:00 2001 From: Enrico Zandomeni Borba Date: Mon, 5 Aug 2024 11:32:51 +0200 Subject: [PATCH 07/17] return Result> from IO functions Since some IO errors are shared across IO functions (invalid argument type, unexpected IO function name), we add a custom IOError ADT. Now, every IO function (even those that cannot fail) will return a Result>. When attempting to invoke an IO function with an invalid name, an IOError/Name is returned. --- src/run.c | 195 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 133 insertions(+), 62 deletions(-) diff --git a/src/run.c b/src/run.c index e8c81883..c03491e1 100644 --- a/src/run.c +++ b/src/run.c @@ -37,10 +37,24 @@ typedef struct Bytes { #define IO_DONE 0 #define IO_CALL 1 -// Result Tags +// Result Tags = Result #define RESULT_OK 0 #define RESULT_ERR 1 +// IOError = { +// Type, -- a type error +// Name, -- invalid io func name +// Inner {val: T}, -- an error while calling an io func +// } +#define IO_ERR_TYPE 0 +#define IO_ERR_NAME 1 +#define IO_ERR_INNER 2 + +typedef struct IOError { + u32 tag; + Port val; +} IOError; + // List Tags #define LIST_NIL 0 #define LIST_CONS 1 @@ -277,16 +291,85 @@ Port inject_err(Net* net, Port err) { return new_port(CON, n3); } -/// Returns a λ-Encoded Ctr for a RESULT_ERR: λt ((t RESULT_ERR) err) -/// where the Error variant contains a String. +/// Returns a λ-Encoded Ctr for a Result/Err(IOError(..)) +Port inject_io_err(Net* net, IOError err) { + if (err.tag <= IO_ERR_NAME) { + if (!get_resources(net, tm[0], 0, 2, 1)) { + fprintf(stderr, "inject_bytes: failed to get resources\n"); + return new_port(ERA, 0); + } + + u32 v1 = tm[0]->vloc[0]; + + u32 n1 = tm[0]->nloc[0]; + u32 n2 = tm[0]->nloc[1]; + + vars_create(net, v1, NONE); + Port var = new_port(VAR, v1); + + node_create(net, n1, new_pair(new_port(NUM, new_u24(err.tag)), var)); + node_create(net, n2, new_pair(new_port(CON, n1), var)); + + return inject_err(net, new_port(CON, n2)); + } + + if (!get_resources(net, tm[0], 0, 3, 1)) { + fprintf(stderr, "inject_ok: failed to get resources\n"); + return new_port(ERA, 0); + } + + u32 v1 = tm[0]->vloc[0]; + + u32 n1 = tm[0]->nloc[0]; + u32 n2 = tm[0]->nloc[1]; + u32 n3 = tm[0]->nloc[2]; + + vars_create(net, v1, NONE); + Port var = new_port(VAR, v1); + + node_create(net, n1, new_pair(err.val, var)); + node_create(net, n2, new_pair(new_port(NUM, new_u24(IO_ERR_INNER)), new_port(CON, n1))); + node_create(net, n3, new_pair(new_port(CON, n2), var)); + + return inject_err(net, new_port(CON, n3)); +} + +/// Returns a λ-Encoded Ctr for a Result/Err(IOError/Type) +Port inject_io_err_type(Net* net) { + IOError io_error = { + .tag = IO_ERR_TYPE, + }; + + return inject_io_err(net, io_error); +} + +/// Returns a λ-Encoded Ctr for a Result/Err(IOError/Name) +Port inject_io_err_name(Net* net) { + IOError io_error = { + .tag = IO_ERR_NAME, + }; + + return inject_io_err(net, io_error); +} + +Port inject_io_err_inner(Net* net, Port val) { + IOError io_error = { + .tag = IO_ERR_INNER, + .val = val, + }; + + return inject_io_err(net, io_error); +} + +/// Returns a λ-Encoded Ctr for an Result> /// `err` must be `NUL`-terminated. -Port inject_err_str(Net* net, char* err) { +Port inject_io_err_str(Net* net, char* err) { Bytes err_bytes; err_bytes.buf = err; err_bytes.len = strlen(err_bytes.buf); Port err_port = inject_bytes(net, &err_bytes); - return inject_err(net, err_port); + return inject_io_err_inner(net, err_port); } // Primitive IO Fns @@ -345,19 +428,18 @@ void* readback_dylib(Port port) { // Reads from a file a specified number of bytes. // `argm` is a tuple of (file_descriptor, num_bytes). -// Returns: Result +// Returns: Result> Port io_read(Net* net, Book* book, Port argm) { Tup tup = readback_tup(net, book, argm, 2); if (tup.elem_len != 2) { - fprintf(stderr, "io_read: expected 2-tuple\n"); - return inject_err(net, new_port(ERA, 0)); + return inject_io_err_type(net); } FILE* fp = readback_file(tup.elem_buf[0]); u32 num_bytes = get_u24(get_val(tup.elem_buf[1])); if (fp == NULL) { - return inject_err(net, new_port(NUM, new_i24(EBADF))); + return inject_io_err_inner(net, new_port(NUM, new_i24(EBADF))); } /// Read a string. @@ -367,7 +449,7 @@ Port io_read(Net* net, Book* book, Port argm) { if ((bytes.len != num_bytes) && ferror(fp)) { free(bytes.buf); - return inject_err(net, new_port(NUM, new_i24(ferror(fp)))); + return inject_io_err_inner(net, new_port(NUM, new_i24(ferror(fp)))); } // Convert it to a port. @@ -380,13 +462,11 @@ Port io_read(Net* net, Book* book, Port argm) { // Opens a file with the provided mode. // `argm` is a tuple (CON node) of the // file name and mode as strings. -// Returns: Result +// Returns: Result> Port io_open(Net* net, Book* book, Port argm) { Tup tup = readback_tup(net, book, argm, 2); if (tup.elem_len != 2) { - fprintf(stderr, "io_open: expected 2-tuple\n"); - - return inject_err(net, new_port(ERA, 0)); + return inject_io_err_type(net); } Str name = readback_str(net, book, tup.elem_buf[0]); @@ -400,7 +480,7 @@ Port io_open(Net* net, Book* book, Port argm) { free(mode.buf); if (FILE_POINTERS[fd] == NULL) { - return inject_err(net, new_port(NUM, new_i24(errno))); + return inject_io_err_inner(net, new_port(NUM, new_i24(errno))); } return inject_ok(net, new_port(NUM, new_u24(fd))); @@ -410,22 +490,20 @@ Port io_open(Net* net, Book* book, Port argm) { free(name.buf); free(mode.buf); - fprintf(stderr, "io_open: returning ERR\n"); - // too many open files - return inject_err(net, new_port(NUM, new_i24(EMFILE))); + return inject_io_err_inner(net, new_port(NUM, new_i24(EMFILE))); } // Closes a file, reclaiming the file descriptor. -// Returns: Result<*, i24> +// Returns: Result<*, IOError> Port io_close(Net* net, Book* book, Port argm) { FILE* fp = readback_file(argm); if (fp == NULL) { - return inject_err(net, new_port(NUM, new_i24(EBADF))); + return inject_io_err_inner(net, new_port(NUM, new_i24(EBADF))); } if (fclose(fp) != 0) { - return inject_err(net, new_port(NUM, new_i24(ferror(fp)))); + return inject_io_err_inner(net, new_port(NUM, new_i24(ferror(fp)))); } FILE_POINTERS[get_u24(get_val(argm))] = NULL; @@ -436,12 +514,11 @@ Port io_close(Net* net, Book* book, Port argm) { // Writes a list of bytes to a file. // `argm` is a tuple (CON node) of the // file descriptor and list of bytes to write. -// Returns: Result<*, i24 | *> +// Returns: Result<*, IOError> Port io_write(Net* net, Book* book, Port argm) { Tup tup = readback_tup(net, book, argm, 2); if (tup.elem_len != 2) { - fprintf(stderr, "io_write: expected 2-tuple\n"); - return inject_err(net, new_port(ERA, 0)); + return inject_io_err_type(net); } FILE* fp = readback_file(tup.elem_buf[0]); @@ -450,13 +527,13 @@ Port io_write(Net* net, Book* book, Port argm) { if (fp == NULL) { free(bytes.buf); - return inject_err(net, new_port(NUM, new_i24(EBADF))); + return inject_io_err_inner(net, new_port(NUM, new_i24(EBADF))); } if (fwrite(bytes.buf, sizeof(char), bytes.len, fp) != bytes.len) { free(bytes.buf); - return inject_err(net, new_port(NUM, new_i24(ferror(fp)))); + return inject_io_err_inner(net, new_port(NUM, new_i24(ferror(fp)))); } free(bytes.buf); @@ -465,15 +542,15 @@ Port io_write(Net* net, Book* book, Port argm) { } // Flushes an output stream. -// Returns: Result<*, i24> +// Returns: Result<*, IOError> Port io_flush(Net* net, Book* book, Port argm) { FILE* fp = readback_file(argm); if (fp == NULL) { - return inject_err(net, new_port(NUM, new_i24(EBADF))); + return inject_io_err_inner(net, new_port(NUM, new_i24(EBADF))); } if (fflush(fp) != 0) { - return inject_err(net, new_port(NUM, new_i24(ferror(fp)))); + return inject_io_err_inner(net, new_port(NUM, new_i24(ferror(fp)))); } return inject_ok(net, new_port(ERA, 0)); @@ -487,12 +564,11 @@ Port io_flush(Net* net, Book* book, Port argm) { // - 0 (SEEK_SET): beginning of file // - 1 (SEEK_CUR): current position of the file pointer // - 2 (SEEK_END): end of the file -// Returns: Result<*, i24 | *> +// Returns: Result<*, IOError> Port io_seek(Net* net, Book* book, Port argm) { Tup tup = readback_tup(net, book, argm, 3); if (tup.elem_len != 3) { - fprintf(stderr, "io_seek: expected 3-tuple\n"); - return inject_err(net, new_port(ERA, 0)); + return inject_io_err_type(net); } FILE* fp = readback_file(tup.elem_buf[0]); @@ -500,7 +576,7 @@ Port io_seek(Net* net, Book* book, Port argm) { u32 whence = get_i24(get_val(tup.elem_buf[2])); if (fp == NULL) { - return inject_err(net, new_port(NUM, new_i24(EBADF))); + return inject_io_err_inner(net, new_port(NUM, new_i24(EBADF))); } int cwhence; @@ -509,12 +585,11 @@ Port io_seek(Net* net, Book* book, Port argm) { case 1: cwhence = SEEK_CUR; break; case 2: cwhence = SEEK_END; break; default: - fprintf(stderr, "io_seek: invalid whence\n"); - return inject_err(net, new_port(ERA, 0)); + return inject_io_err_type(net); } if (fseek(fp, offset, cwhence) != 0) { - return inject_err(net, new_port(NUM, new_i24(ferror(fp)))); + return inject_io_err_inner(net, new_port(NUM, new_i24(ferror(fp)))); } return inject_ok(net, new_port(ERA, 0)); @@ -522,7 +597,7 @@ Port io_seek(Net* net, Book* book, Port argm) { // Returns the current time as a tuple of the high // and low 24 bits of a 48-bit nanosecond timestamp. -// Returns: (u24, u24) +// Returns: Result<(u24, u24), IOError<*>> Port io_get_time(Net* net, Book* book, Port argm) { // Get the current time in nanoseconds u64 time_ns = time64(); @@ -534,18 +609,17 @@ Port io_get_time(Net* net, Book* book, Port argm) { u32 loc = node_alloc_1(net, tm[0], &lps); node_create(net, loc, new_pair(new_port(NUM, new_u24(time_hi)), new_port(NUM, new_u24(time_lo)))); - return new_port(CON, loc); + return inject_ok(net, new_port(CON, loc)); } // Sleeps. // `argm` is a tuple (CON node) of the high and low // 24 bits for a 48-bit duration in nanoseconds. -// Returns: Result<*, *> +// Returns: Result<*, IOError<*>> Port io_sleep(Net* net, Book* book, Port argm) { Tup tup = readback_tup(net, book, argm, 2); if (tup.elem_len != 2) { - fprintf(stderr, "io_sleep: expected 2-tuple\n"); - return inject_err(net, new_port(ERA, 0)); + return inject_io_err_type(net); } // Get the sleep duration node @@ -568,7 +642,7 @@ Port io_sleep(Net* net, Book* book, Port argm) { // `argm` is a tuple of `filename` and `lazy`. // `filename` is a λ-encoded string. // `lazy` is a `bool` indicating if functions should be lazily loaded. -// Returns: Result +// Returns: Result> Port io_dl_open(Net* net, Book* book, Port argm) { Tup tup = readback_tup(net, book, argm, 2); Str str = readback_str(net, book, tup.elem_buf[0]); @@ -583,14 +657,14 @@ Port io_dl_open(Net* net, Book* book, Port argm) { free(str.buf); if (DYLIBS[dl] == NULL) { - return inject_err_str(net, dlerror()); + return inject_io_err_str(net, dlerror()); } return inject_ok(net, new_port(NUM, new_u24(dl))); } } - return inject_err_str(net, "too many open dylibs"); + return inject_io_err_str(net, "too many open dylibs"); } // Calls a function from a loaded dylib. @@ -602,13 +676,11 @@ Port io_dl_open(Net* net, Book* book, Port argm) { // This function returns a Result with an Ok variant containing an // arbitrary type. // -// Returns Result +// Returns Result> Port io_dl_call(Net* net, Book* book, Port argm) { Tup tup = readback_tup(net, book, argm, 3); if (tup.elem_len != 3) { - fprintf(stderr, "io_dl_call: expected 3-tuple\n"); - - return inject_err(net, new_port(ERA, 0)); + return inject_io_err_type(net); } void* dl = readback_dylib(tup.elem_buf[0]); @@ -618,7 +690,7 @@ Port io_dl_call(Net* net, Book* book, Port argm) { Port (*func)(Net*, Book*, Port) = dlsym(dl, symbol.buf); char* error = dlerror(); if (error != NULL) { - return inject_err_str(net, error); + return inject_io_err_str(net, error); } return inject_ok(net, func(net, book, tup.elem_buf[2])); @@ -626,18 +698,16 @@ Port io_dl_call(Net* net, Book* book, Port argm) { // Closes a loaded dylib, reclaiming the handle. // -// Returns: Result<*, String | *> +// Returns: Result<*, IOError> Port io_dl_close(Net* net, Book* book, Port argm) { void* dl = readback_dylib(argm); if (dl == NULL) { - fprintf(stderr, "io_dl_close: invalid handle\n"); - - return inject_err(net, new_port(ERA, 0)); + return inject_io_err_type(net); } int err = dlclose(dl) != 0; if (err != 0) { - return inject_err_str(net, dlerror()); + return inject_io_err_str(net, dlerror()); } DYLIBS[get_u24(get_val(argm))] = NULL; @@ -703,27 +773,28 @@ void do_run_io(Net* net, Book* book, Port port) { break; } } - if (ffn == NULL) { - fprintf(stderr, "Unknown IO func '%s'\n", func.buf); - - free(func.buf); - - break; - } free(func.buf); Port argm = ctr.args_buf[2]; Port cont = ctr.args_buf[3]; - Port ret = ffn->func(net, book, argm); + + Port ret; + if (ffn == NULL) { + ret = inject_io_err_name(net); + } else { + ret = ffn->func(net, book, argm); + }; u32 lps = 0; u32 loc = node_alloc_1(net, tm[0], &lps); node_create(net, loc, new_pair(ret, ROOT)); boot_redex(net, new_pair(new_port(CON, loc), cont)); port = ROOT; + continue; } + case IO_DONE: { break; } From aa9f15c920159e132d033221082070c9213e25a5 Mon Sep 17 00:00:00 2001 From: Enrico Zandomeni Borba Date: Mon, 5 Aug 2024 11:34:36 +0200 Subject: [PATCH 08/17] additional IO tests, along with their Bend implementations Add io programs that test basic functionality, including failures. This also updates the "smoke test" file, that does a sequence of open, read, write, close io calls on a file. --- tests/programs/io/basic.bend | 18 +++ tests/programs/io/basic.hvm | 95 +++++++++++ tests/programs/io/invalid-name.bend | 5 + tests/programs/io/invalid-name.hvm | 69 ++++++++ tests/programs/io/open1.bend | 5 + tests/programs/io/open1.hvm | 61 +++++++ tests/programs/io/open2.bend | 5 + tests/programs/io/open2.hvm | 59 +++++++ tests/programs/io/open3.bend | 6 + tests/programs/io/open3.hvm | 49 ++++++ tests/programs/io/read_and_print.hvm | 233 --------------------------- 11 files changed, 372 insertions(+), 233 deletions(-) create mode 100644 tests/programs/io/basic.bend create mode 100644 tests/programs/io/basic.hvm create mode 100644 tests/programs/io/invalid-name.bend create mode 100644 tests/programs/io/invalid-name.hvm create mode 100644 tests/programs/io/open1.bend create mode 100644 tests/programs/io/open1.hvm create mode 100644 tests/programs/io/open2.bend create mode 100644 tests/programs/io/open2.hvm create mode 100644 tests/programs/io/open3.bend create mode 100644 tests/programs/io/open3.hvm delete mode 100644 tests/programs/io/read_and_print.hvm diff --git a/tests/programs/io/basic.bend b/tests/programs/io/basic.bend new file mode 100644 index 00000000..df93e7e1 --- /dev/null +++ b/tests/programs/io/basic.bend @@ -0,0 +1,18 @@ +def unwrap(res): + match res: + case Result/Ok: + return res.val + case Result/Err: + return res.val + +def main(): + with IO: + f <- call("OPEN", ("./README.md", "r")) + f = unwrap(f) + bytes <- call("READ", (f, 30)) + bytes = unwrap(bytes) + * <- call("WRITE", (1, bytes)) + * <- call("WRITE", (1, "\n")) + res <- call("CLOSE", f) + + return wrap(res) diff --git a/tests/programs/io/basic.hvm b/tests/programs/io/basic.hvm new file mode 100644 index 00000000..ee07a4d8 --- /dev/null +++ b/tests/programs/io/basic.hvm @@ -0,0 +1,95 @@ +@IO/Call = (a (b (c (d ((@IO/Call/tag (a (b (c (d e))))) e))))) + +@IO/Call/tag = 1 + +@IO/Done = (a (b ((@IO/Done/tag (a (b c))) c))) + +@IO/Done/tag = 0 + +@IO/MAGIC = (13683217 16719857) + +@IO/bind = ((@IO/bind__C2 a) a) + +@IO/bind__C0 = (* (b (a c))) + & @undefer ~ (a (b c)) + +@IO/bind__C1 = (* (* (a (b ((c d) (e g)))))) + & @IO/Call ~ (@IO/MAGIC (a (b ((c f) g)))) + & @IO/bind ~ (d (e f)) + +@IO/bind__C2 = (?((@IO/bind__C0 @IO/bind__C1) a) a) + +@IO/wrap = a + & @IO/Done ~ (@IO/MAGIC a) + +@String/Cons = (a (b ((@String/Cons/tag (a (b c))) c))) + +@String/Cons/tag = 1 + +@String/Nil = ((@String/Nil/tag a) a) + +@String/Nil/tag = 0 + +@call = (a (b c)) + & @IO/Call ~ (@IO/MAGIC (a (b (@call__C0 c)))) + +@call__C0 = a + & @IO/Done ~ (@IO/MAGIC a) + +@main = mc + & @IO/bind ~ (q ((((ic (r kc)) (@IO/wrap lc)) lc) mc)) + & @call ~ (d ((o p) q)) + & @String/Cons ~ (79 (c d)) + & @String/Cons ~ (80 (b c)) + & @String/Cons ~ (69 (a b)) + & @String/Cons ~ (78 (@String/Nil a)) + & @String/Cons ~ (46 (n o)) + & @String/Cons ~ (47 (m n)) + & @String/Cons ~ (82 (l m)) + & @String/Cons ~ (69 (k l)) + & @String/Cons ~ (65 (j k)) + & @String/Cons ~ (68 (i j)) + & @String/Cons ~ (77 (h i)) + & @String/Cons ~ (69 (g h)) + & @String/Cons ~ (46 (f g)) + & @String/Cons ~ (109 (e f)) + & @String/Cons ~ (100 (@String/Nil e)) + & @String/Cons ~ (114 (@String/Nil p)) + & @IO/bind ~ (x ((((dc (ec (db gc))) (hc (ic jc))) jc) kc)) + & @unwrap ~ (r {w hc}) + & @call ~ (v ((w 30) x)) + & @String/Cons ~ (82 (u v)) + & @String/Cons ~ (69 (t u)) + & @String/Cons ~ (65 (s t)) + & @String/Cons ~ (68 (@String/Nil s)) + & @IO/bind ~ (fb ((((zb (ac (* cc))) (dc (ec fc))) fc) gc)) + & @call ~ (cb ((1 eb) fb)) + & @String/Cons ~ (87 (bb cb)) + & @String/Cons ~ (82 (ab bb)) + & @String/Cons ~ (73 (z ab)) + & @String/Cons ~ (84 (y z)) + & @String/Cons ~ (69 (@String/Nil y)) + & @unwrap ~ (db eb) + & @IO/bind ~ (mb ((((sb (wb (* yb))) (zb (ac bc))) bc) cc)) + & @call ~ (kb ((1 lb) mb)) + & @String/Cons ~ (87 (jb kb)) + & @String/Cons ~ (82 (ib jb)) + & @String/Cons ~ (73 (hb ib)) + & @String/Cons ~ (84 (gb hb)) + & @String/Cons ~ (69 (@String/Nil gb)) + & @String/Cons ~ (10 (@String/Nil lb)) + & @IO/bind ~ (tb ((((ub ub) (wb xb)) xb) yb)) + & @call ~ (rb (sb tb)) + & @String/Cons ~ (67 (qb rb)) + & @String/Cons ~ (76 (pb qb)) + & @String/Cons ~ (79 (ob pb)) + & @String/Cons ~ (83 (nb ob)) + & @String/Cons ~ (69 (@String/Nil nb)) + +@undefer = (((a a) b) b) + +@unwrap = ((@unwrap__C0 a) a) + +@unwrap__C0 = (?(((a a) (* (b b))) c) c) + + diff --git a/tests/programs/io/invalid-name.bend b/tests/programs/io/invalid-name.bend new file mode 100644 index 00000000..7b211f64 --- /dev/null +++ b/tests/programs/io/invalid-name.bend @@ -0,0 +1,5 @@ +def main(): + with IO: + f <- call("INVALID-NAME", ("./README.md", "r")) + + return wrap(f) diff --git a/tests/programs/io/invalid-name.hvm b/tests/programs/io/invalid-name.hvm new file mode 100644 index 00000000..fae3385f --- /dev/null +++ b/tests/programs/io/invalid-name.hvm @@ -0,0 +1,69 @@ +@IO/Call = (a (b (c (d ((@IO/Call/tag (a (b (c (d e))))) e))))) + +@IO/Call/tag = 1 + +@IO/Done = (a (b ((@IO/Done/tag (a (b c))) c))) + +@IO/Done/tag = 0 + +@IO/MAGIC = (13683217 16719857) + +@IO/bind = ((@IO/bind__C2 a) a) + +@IO/bind__C0 = (* (b (a c))) + & @undefer ~ (a (b c)) + +@IO/bind__C1 = (* (* (a (b ((c d) (e g)))))) + & @IO/Call ~ (@IO/MAGIC (a (b ((c f) g)))) + & @IO/bind ~ (d (e f)) + +@IO/bind__C2 = (?((@IO/bind__C0 @IO/bind__C1) a) a) + +@IO/wrap = a + & @IO/Done ~ (@IO/MAGIC a) + +@String/Cons = (a (b ((@String/Cons/tag (a (b c))) c))) + +@String/Cons/tag = 1 + +@String/Nil = ((@String/Nil/tag a) a) + +@String/Nil/tag = 0 + +@call = (a (b c)) + & @IO/Call ~ (@IO/MAGIC (a (b (@call__C0 c)))) + +@call__C0 = a + & @IO/Done ~ (@IO/MAGIC a) + +@main = cb + & @IO/bind ~ (y ((((z z) (@IO/wrap bb)) bb) cb)) + & @call ~ (l ((w x) y)) + & @String/Cons ~ (73 (k l)) + & @String/Cons ~ (78 (j k)) + & @String/Cons ~ (86 (i j)) + & @String/Cons ~ (65 (h i)) + & @String/Cons ~ (76 (g h)) + & @String/Cons ~ (73 (f g)) + & @String/Cons ~ (68 (e f)) + & @String/Cons ~ (45 (d e)) + & @String/Cons ~ (78 (c d)) + & @String/Cons ~ (65 (b c)) + & @String/Cons ~ (77 (a b)) + & @String/Cons ~ (69 (@String/Nil a)) + & @String/Cons ~ (46 (v w)) + & @String/Cons ~ (47 (u v)) + & @String/Cons ~ (82 (t u)) + & @String/Cons ~ (69 (s t)) + & @String/Cons ~ (65 (r s)) + & @String/Cons ~ (68 (q r)) + & @String/Cons ~ (77 (p q)) + & @String/Cons ~ (69 (o p)) + & @String/Cons ~ (46 (n o)) + & @String/Cons ~ (109 (m n)) + & @String/Cons ~ (100 (@String/Nil m)) + & @String/Cons ~ (114 (@String/Nil x)) + +@undefer = (((a a) b) b) + + diff --git a/tests/programs/io/open1.bend b/tests/programs/io/open1.bend new file mode 100644 index 00000000..e4db0873 --- /dev/null +++ b/tests/programs/io/open1.bend @@ -0,0 +1,5 @@ +def main(): + with IO: + f <- call("OPEN", ("./README.md", "r")) + + return wrap(f) diff --git a/tests/programs/io/open1.hvm b/tests/programs/io/open1.hvm new file mode 100644 index 00000000..dfb5b097 --- /dev/null +++ b/tests/programs/io/open1.hvm @@ -0,0 +1,61 @@ +@IO/Call = (a (b (c (d ((@IO/Call/tag (a (b (c (d e))))) e))))) + +@IO/Call/tag = 1 + +@IO/Done = (a (b ((@IO/Done/tag (a (b c))) c))) + +@IO/Done/tag = 0 + +@IO/MAGIC = (13683217 16719857) + +@IO/bind = ((@IO/bind__C2 a) a) + +@IO/bind__C0 = (* (b (a c))) + & @undefer ~ (a (b c)) + +@IO/bind__C1 = (* (* (a (b ((c d) (e g)))))) + & @IO/Call ~ (@IO/MAGIC (a (b ((c f) g)))) + & @IO/bind ~ (d (e f)) + +@IO/bind__C2 = (?((@IO/bind__C0 @IO/bind__C1) a) a) + +@IO/wrap = a + & @IO/Done ~ (@IO/MAGIC a) + +@String/Cons = (a (b ((@String/Cons/tag (a (b c))) c))) + +@String/Cons/tag = 1 + +@String/Nil = ((@String/Nil/tag a) a) + +@String/Nil/tag = 0 + +@call = (a (b c)) + & @IO/Call ~ (@IO/MAGIC (a (b (@call__C0 c)))) + +@call__C0 = a + & @IO/Done ~ (@IO/MAGIC a) + +@main = u + & @IO/bind ~ (q ((((r r) (@IO/wrap t)) t) u)) + & @call ~ (d ((o p) q)) + & @String/Cons ~ (79 (c d)) + & @String/Cons ~ (80 (b c)) + & @String/Cons ~ (69 (a b)) + & @String/Cons ~ (78 (@String/Nil a)) + & @String/Cons ~ (46 (n o)) + & @String/Cons ~ (47 (m n)) + & @String/Cons ~ (82 (l m)) + & @String/Cons ~ (69 (k l)) + & @String/Cons ~ (65 (j k)) + & @String/Cons ~ (68 (i j)) + & @String/Cons ~ (77 (h i)) + & @String/Cons ~ (69 (g h)) + & @String/Cons ~ (46 (f g)) + & @String/Cons ~ (109 (e f)) + & @String/Cons ~ (100 (@String/Nil e)) + & @String/Cons ~ (114 (@String/Nil p)) + +@undefer = (((a a) b) b) + + diff --git a/tests/programs/io/open2.bend b/tests/programs/io/open2.bend new file mode 100644 index 00000000..8f9ed487 --- /dev/null +++ b/tests/programs/io/open2.bend @@ -0,0 +1,5 @@ +def main(): + with IO: + f <- call("OPEN", ("fake-file", "r")) + + return wrap(f) diff --git a/tests/programs/io/open2.hvm b/tests/programs/io/open2.hvm new file mode 100644 index 00000000..3f953e26 --- /dev/null +++ b/tests/programs/io/open2.hvm @@ -0,0 +1,59 @@ +@IO/Call = (a (b (c (d ((@IO/Call/tag (a (b (c (d e))))) e))))) + +@IO/Call/tag = 1 + +@IO/Done = (a (b ((@IO/Done/tag (a (b c))) c))) + +@IO/Done/tag = 0 + +@IO/MAGIC = (13683217 16719857) + +@IO/bind = ((@IO/bind__C2 a) a) + +@IO/bind__C0 = (* (b (a c))) + & @undefer ~ (a (b c)) + +@IO/bind__C1 = (* (* (a (b ((c d) (e g)))))) + & @IO/Call ~ (@IO/MAGIC (a (b ((c f) g)))) + & @IO/bind ~ (d (e f)) + +@IO/bind__C2 = (?((@IO/bind__C0 @IO/bind__C1) a) a) + +@IO/wrap = a + & @IO/Done ~ (@IO/MAGIC a) + +@String/Cons = (a (b ((@String/Cons/tag (a (b c))) c))) + +@String/Cons/tag = 1 + +@String/Nil = ((@String/Nil/tag a) a) + +@String/Nil/tag = 0 + +@call = (a (b c)) + & @IO/Call ~ (@IO/MAGIC (a (b (@call__C0 c)))) + +@call__C0 = a + & @IO/Done ~ (@IO/MAGIC a) + +@main = s + & @IO/bind ~ (o ((((p p) (@IO/wrap r)) r) s)) + & @call ~ (d ((m n) o)) + & @String/Cons ~ (79 (c d)) + & @String/Cons ~ (80 (b c)) + & @String/Cons ~ (69 (a b)) + & @String/Cons ~ (78 (@String/Nil a)) + & @String/Cons ~ (102 (l m)) + & @String/Cons ~ (97 (k l)) + & @String/Cons ~ (107 (j k)) + & @String/Cons ~ (101 (i j)) + & @String/Cons ~ (45 (h i)) + & @String/Cons ~ (102 (g h)) + & @String/Cons ~ (105 (f g)) + & @String/Cons ~ (108 (e f)) + & @String/Cons ~ (101 (@String/Nil e)) + & @String/Cons ~ (114 (@String/Nil n)) + +@undefer = (((a a) b) b) + + diff --git a/tests/programs/io/open3.bend b/tests/programs/io/open3.bend new file mode 100644 index 00000000..ec73ab04 --- /dev/null +++ b/tests/programs/io/open3.bend @@ -0,0 +1,6 @@ +def main(): + with IO: + # calling open with an unexpected type of arg + f <- call("OPEN", 123) + + return wrap(f) diff --git a/tests/programs/io/open3.hvm b/tests/programs/io/open3.hvm new file mode 100644 index 00000000..5a6ad10d --- /dev/null +++ b/tests/programs/io/open3.hvm @@ -0,0 +1,49 @@ +@IO/Call = (a (b (c (d ((@IO/Call/tag (a (b (c (d e))))) e))))) + +@IO/Call/tag = 1 + +@IO/Done = (a (b ((@IO/Done/tag (a (b c))) c))) + +@IO/Done/tag = 0 + +@IO/MAGIC = (13683217 16719857) + +@IO/bind = ((@IO/bind__C2 a) a) + +@IO/bind__C0 = (* (b (a c))) + & @undefer ~ (a (b c)) + +@IO/bind__C1 = (* (* (a (b ((c d) (e g)))))) + & @IO/Call ~ (@IO/MAGIC (a (b ((c f) g)))) + & @IO/bind ~ (d (e f)) + +@IO/bind__C2 = (?((@IO/bind__C0 @IO/bind__C1) a) a) + +@IO/wrap = a + & @IO/Done ~ (@IO/MAGIC a) + +@String/Cons = (a (b ((@String/Cons/tag (a (b c))) c))) + +@String/Cons/tag = 1 + +@String/Nil = ((@String/Nil/tag a) a) + +@String/Nil/tag = 0 + +@call = (a (b c)) + & @IO/Call ~ (@IO/MAGIC (a (b (@call__C0 c)))) + +@call__C0 = a + & @IO/Done ~ (@IO/MAGIC a) + +@main = i + & @IO/bind ~ (e ((((f f) (@IO/wrap h)) h) i)) + & @call ~ (d (123 e)) + & @String/Cons ~ (79 (c d)) + & @String/Cons ~ (80 (b c)) + & @String/Cons ~ (69 (a b)) + & @String/Cons ~ (78 (@String/Nil a)) + +@undefer = (((a a) b) b) + + diff --git a/tests/programs/io/read_and_print.hvm b/tests/programs/io/read_and_print.hvm deleted file mode 100644 index bcdc54cf..00000000 --- a/tests/programs/io/read_and_print.hvm +++ /dev/null @@ -1,233 +0,0 @@ -@IO_T/Call = (a (b (c (d ((@IO_T/Call/tag (a (b (c (d e))))) e))))) - -@IO_T/Call/tag = 1 - -@IO_T/Done = (a (b ((@IO_T/Done/tag (a (b c))) c))) - -@IO_T/Done/tag = 0 - -@IO_T/MAGIC = (13683217 16719857) - -@IO_T/bind = ((@IO_T/bind__C2 a) a) - -@IO_T/bind__C0 = (* (b (a c))) - & @undefer ~ (a (b c)) - -@IO_T/bind__C1 = (* (* (a (b ((c d) (e g)))))) - & @IO_T/Call ~ (@IO_T/MAGIC (a (b ((c f) g)))) - & @IO_T/bind ~ (d (e f)) - -@IO_T/bind__C2 = (?((@IO_T/bind__C0 @IO_T/bind__C1) a) a) - -@String/Cons = (a (b ((@String/Cons/tag (a (b c))) c))) - -@String/Cons/tag = 1 - -@String/Nil = ((@String/Nil/tag a) a) - -@String/Nil/tag = 0 - -@call_io = (a (b c)) - & @IO_T/Call ~ (@IO_T/MAGIC (a (b (@call_io__C0 c)))) - -@call_io__C0 = a - & @IO_T/Done ~ (@IO_T/MAGIC a) - -@main = m - & @IO_T/bind ~ (@write_to_file ((((* k) l) l) m)) - & @IO_T/bind ~ (@read_from_file ((((* i) j) j) k)) - & @IO_T/bind ~ (g ((((* 42) h) h) i)) - & @call_io ~ (e ((1 f) g)) - & @String/Cons ~ (87 (d e)) - & @String/Cons ~ (82 (c d)) - & @String/Cons ~ (73 (b c)) - & @String/Cons ~ (84 (a b)) - & @String/Cons ~ (69 (@String/Nil a)) - & @String/Cons ~ (10 (@String/Nil f)) - -@read_from_file = a - & @IO_T/bind ~ (@read_from_file__C11 (@read_from_file__C10 a)) - -@read_from_file__C0 = (f (* g)) - & @call_io ~ (e (f g)) - & @String/Cons ~ (67 (d e)) - & @String/Cons ~ (76 (c d)) - & @String/Cons ~ (79 (b c)) - & @String/Cons ~ (83 (a b)) - & @String/Cons ~ (69 (@String/Nil a)) - -@read_from_file__C1 = e - & @String/Cons ~ (87 (d e)) - & @String/Cons ~ (82 (c d)) - & @String/Cons ~ (73 (b c)) - & @String/Cons ~ (84 (a b)) - & @String/Cons ~ (69 (@String/Nil a)) - -@read_from_file__C10 = ((@read_from_file__C9 a) a) - -@read_from_file__C11 = q - & @call_io ~ (d ((o p) q)) - & @String/Cons ~ (79 (c d)) - & @String/Cons ~ (80 (b c)) - & @String/Cons ~ (69 (a b)) - & @String/Cons ~ (78 (@String/Nil a)) - & @String/Cons ~ (116 (n o)) - & @String/Cons ~ (101 (m n)) - & @String/Cons ~ (115 (l m)) - & @String/Cons ~ (116 (k l)) - & @String/Cons ~ (105 (j k)) - & @String/Cons ~ (110 (i j)) - & @String/Cons ~ (103 (h i)) - & @String/Cons ~ (46 (g h)) - & @String/Cons ~ (116 (f g)) - & @String/Cons ~ (120 (e f)) - & @String/Cons ~ (116 (@String/Nil e)) - & @String/Cons ~ (114 (@String/Nil p)) - -@read_from_file__C2 = (c (a e)) - & @IO_T/bind ~ (b (((@read_from_file__C0 (c d)) d) e)) - & @call_io ~ (@read_from_file__C1 ((1 a) b)) - -@read_from_file__C3 = d - & @String/Cons ~ (82 (c d)) - & @String/Cons ~ (69 (b c)) - & @String/Cons ~ (65 (a b)) - & @String/Cons ~ (68 (@String/Nil a)) - -@read_from_file__C4 = ({a c} (* e)) - & @IO_T/bind ~ (b (((@read_from_file__C2 (c d)) d) e)) - & @call_io ~ (@read_from_file__C3 ((a 5) b)) - -@read_from_file__C5 = d - & @String/Cons ~ (83 (c d)) - & @String/Cons ~ (69 (b c)) - & @String/Cons ~ (69 (a b)) - & @String/Cons ~ (75 (@String/Nil a)) - -@read_from_file__C6 = ({a c} (* e)) - & @IO_T/bind ~ (b (((@read_from_file__C4 (c d)) d) e)) - & @call_io ~ (@read_from_file__C5 ((a (2 0)) b)) - -@read_from_file__C7 = e - & @String/Cons ~ (87 (d e)) - & @String/Cons ~ (82 (c d)) - & @String/Cons ~ (73 (b c)) - & @String/Cons ~ (84 (a b)) - & @String/Cons ~ (69 (@String/Nil a)) - -@read_from_file__C8 = (c (a e)) - & @IO_T/bind ~ (b (((@read_from_file__C6 (c d)) d) e)) - & @call_io ~ (@read_from_file__C7 ((1 a) b)) - -@read_from_file__C9 = ({e g} i) - & @IO_T/bind ~ (f (((@read_from_file__C8 (g h)) h) i)) - & @call_io ~ (d ((e 5) f)) - & @String/Cons ~ (82 (c d)) - & @String/Cons ~ (69 (b c)) - & @String/Cons ~ (65 (a b)) - & @String/Cons ~ (68 (@String/Nil a)) - -@read_input = a - & @IO_T/bind ~ (@read_input__C2 (@read_input__C1 a)) - -@read_input__C0 = (* e) - & @call_io ~ (d ((0 10) e)) - & @String/Cons ~ (82 (c d)) - & @String/Cons ~ (69 (b c)) - & @String/Cons ~ (65 (a b)) - & @String/Cons ~ (68 (@String/Nil a)) - -@read_input__C1 = ((@read_input__C0 a) a) - -@read_input__C2 = y - & @call_io ~ (e ((1 x) y)) - & @String/Cons ~ (87 (d e)) - & @String/Cons ~ (82 (c d)) - & @String/Cons ~ (73 (b c)) - & @String/Cons ~ (84 (a b)) - & @String/Cons ~ (69 (@String/Nil a)) - & @String/Cons ~ (87 (w x)) - & @String/Cons ~ (104 (v w)) - & @String/Cons ~ (97 (u v)) - & @String/Cons ~ (116 (t u)) - & @String/Cons ~ (32 (s t)) - & @String/Cons ~ (105 (r s)) - & @String/Cons ~ (115 (q r)) - & @String/Cons ~ (32 (p q)) - & @String/Cons ~ (121 (o p)) - & @String/Cons ~ (111 (n o)) - & @String/Cons ~ (117 (m n)) - & @String/Cons ~ (114 (l m)) - & @String/Cons ~ (32 (k l)) - & @String/Cons ~ (110 (j k)) - & @String/Cons ~ (97 (i j)) - & @String/Cons ~ (109 (h i)) - & @String/Cons ~ (101 (g h)) - & @String/Cons ~ (63 (f g)) - & @String/Cons ~ (10 (@String/Nil f)) - -@test-io = 1 - -@undefer = (((a a) b) b) - -@write_to_file = a - & @IO_T/bind ~ (@write_to_file__C8 (@write_to_file__C7 a)) - -@write_to_file__C0 = (f (* g)) - & @call_io ~ (e (f g)) - & @String/Cons ~ (67 (d e)) - & @String/Cons ~ (76 (c d)) - & @String/Cons ~ (79 (b c)) - & @String/Cons ~ (83 (a b)) - & @String/Cons ~ (69 (@String/Nil a)) - -@write_to_file__C1 = a - & @String/Cons ~ (10 (@String/Nil a)) - -@write_to_file__C2 = e - & @String/Cons ~ (87 (d e)) - & @String/Cons ~ (82 (c d)) - & @String/Cons ~ (73 (b c)) - & @String/Cons ~ (84 (a b)) - & @String/Cons ~ (69 (@String/Nil a)) - -@write_to_file__C3 = ({a c} (* e)) - & @IO_T/bind ~ (b (((@write_to_file__C0 (c d)) d) e)) - & @call_io ~ (@write_to_file__C2 ((a @write_to_file__C1) b)) - -@write_to_file__C4 = e - & @String/Cons ~ (87 (d e)) - & @String/Cons ~ (82 (c d)) - & @String/Cons ~ (73 (b c)) - & @String/Cons ~ (84 (a b)) - & @String/Cons ~ (69 (@String/Nil a)) - -@write_to_file__C5 = ({a d} (b f)) - & @IO_T/bind ~ (c (((@write_to_file__C3 (d e)) e) f)) - & @call_io ~ (@write_to_file__C4 ((a b) c)) - -@write_to_file__C6 = (a c) - & @IO_T/bind ~ (@read_input (((@write_to_file__C5 (a b)) b) c)) - -@write_to_file__C7 = ((@write_to_file__C6 a) a) - -@write_to_file__C8 = q - & @call_io ~ (d ((o p) q)) - & @String/Cons ~ (79 (c d)) - & @String/Cons ~ (80 (b c)) - & @String/Cons ~ (69 (a b)) - & @String/Cons ~ (78 (@String/Nil a)) - & @String/Cons ~ (116 (n o)) - & @String/Cons ~ (101 (m n)) - & @String/Cons ~ (115 (l m)) - & @String/Cons ~ (116 (k l)) - & @String/Cons ~ (105 (j k)) - & @String/Cons ~ (110 (i j)) - & @String/Cons ~ (103 (h i)) - & @String/Cons ~ (46 (g h)) - & @String/Cons ~ (116 (f g)) - & @String/Cons ~ (120 (e f)) - & @String/Cons ~ (116 (@String/Nil e)) - & @String/Cons ~ (119 (@String/Nil p)) - - From bc66920797493bcc6825869f0658b0b8c0196f9d Mon Sep 17 00:00:00 2001 From: Enrico Zandomeni Borba Date: Mon, 5 Aug 2024 12:10:50 +0200 Subject: [PATCH 09/17] fix resource allocation failure print --- src/run.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/run.c b/src/run.c index c03491e1..86dffa38 100644 --- a/src/run.c +++ b/src/run.c @@ -295,7 +295,7 @@ Port inject_err(Net* net, Port err) { Port inject_io_err(Net* net, IOError err) { if (err.tag <= IO_ERR_NAME) { if (!get_resources(net, tm[0], 0, 2, 1)) { - fprintf(stderr, "inject_bytes: failed to get resources\n"); + fprintf(stderr, "inject_io_err: failed to get resources\n"); return new_port(ERA, 0); } @@ -314,7 +314,7 @@ Port inject_io_err(Net* net, IOError err) { } if (!get_resources(net, tm[0], 0, 3, 1)) { - fprintf(stderr, "inject_ok: failed to get resources\n"); + fprintf(stderr, "inject_io_err: failed to get resources\n"); return new_port(ERA, 0); } From 352e2e2f864158cc6b25ad1e84250f85c5387a38 Mon Sep 17 00:00:00 2001 From: Enrico Zandomeni Borba Date: Mon, 5 Aug 2024 12:36:16 +0200 Subject: [PATCH 10/17] reduce num nodes in basic.bend::main basic.bend couldn't be run in CUDA as the main definition was too large --- tests/programs/io/basic.bend | 25 ++++++++--- tests/programs/io/basic.hvm | 84 ++++++++++++++++++++++-------------- 2 files changed, 72 insertions(+), 37 deletions(-) diff --git a/tests/programs/io/basic.bend b/tests/programs/io/basic.bend index df93e7e1..b80f1ad6 100644 --- a/tests/programs/io/basic.bend +++ b/tests/programs/io/basic.bend @@ -5,14 +5,29 @@ def unwrap(res): case Result/Err: return res.val +def open(): + return call("OPEN", ("./README.md", "r")) + +def read(f): + return call("READ", (f, 30)) + +def print(bytes): + with IO: + * <- call("WRITE", (1, bytes)) + * <- call("WRITE", (1, "\n")) + + return wrap(*) + +def close(f): + return call("CLOSE", f) + def main(): with IO: - f <- call("OPEN", ("./README.md", "r")) + f <- open() f = unwrap(f) - bytes <- call("READ", (f, 30)) + bytes <- read(f) bytes = unwrap(bytes) - * <- call("WRITE", (1, bytes)) - * <- call("WRITE", (1, "\n")) - res <- call("CLOSE", f) + * <- print(bytes) + res <- close(f) return wrap(res) diff --git a/tests/programs/io/basic.hvm b/tests/programs/io/basic.hvm index ee07a4d8..31a2898e 100644 --- a/tests/programs/io/basic.hvm +++ b/tests/programs/io/basic.hvm @@ -36,8 +36,26 @@ @call__C0 = a & @IO/Done ~ (@IO/MAGIC a) -@main = mc - & @IO/bind ~ (q ((((ic (r kc)) (@IO/wrap lc)) lc) mc)) +@close = f + & @call ~ (e f) + & @String/Cons ~ (67 (d e)) + & @String/Cons ~ (76 (c d)) + & @String/Cons ~ (79 (b c)) + & @String/Cons ~ (83 (a b)) + & @String/Cons ~ (69 (@String/Nil a)) + +@main = w + & @IO/bind ~ (@open ((((s (a u)) (@IO/wrap v)) v) w)) + & @IO/bind ~ (c ((((n (o (d q))) (r (s t))) t) u)) + & @unwrap ~ (a {b r}) + & @read ~ (b c) + & @IO/bind ~ (f ((((g (k (* m))) (n (o p))) p) q)) + & @print ~ (e f) + & @unwrap ~ (d e) + & @IO/bind ~ (h ((((i i) (k l)) l) m)) + & @close ~ (g h) + +@open = q & @call ~ (d ((o p) q)) & @String/Cons ~ (79 (c d)) & @String/Cons ~ (80 (b c)) @@ -55,36 +73,38 @@ & @String/Cons ~ (109 (e f)) & @String/Cons ~ (100 (@String/Nil e)) & @String/Cons ~ (114 (@String/Nil p)) - & @IO/bind ~ (x ((((dc (ec (db gc))) (hc (ic jc))) jc) kc)) - & @unwrap ~ (r {w hc}) - & @call ~ (v ((w 30) x)) - & @String/Cons ~ (82 (u v)) - & @String/Cons ~ (69 (t u)) - & @String/Cons ~ (65 (s t)) - & @String/Cons ~ (68 (@String/Nil s)) - & @IO/bind ~ (fb ((((zb (ac (* cc))) (dc (ec fc))) fc) gc)) - & @call ~ (cb ((1 eb) fb)) - & @String/Cons ~ (87 (bb cb)) - & @String/Cons ~ (82 (ab bb)) - & @String/Cons ~ (73 (z ab)) - & @String/Cons ~ (84 (y z)) - & @String/Cons ~ (69 (@String/Nil y)) - & @unwrap ~ (db eb) - & @IO/bind ~ (mb ((((sb (wb (* yb))) (zb (ac bc))) bc) cc)) - & @call ~ (kb ((1 lb) mb)) - & @String/Cons ~ (87 (jb kb)) - & @String/Cons ~ (82 (ib jb)) - & @String/Cons ~ (73 (hb ib)) - & @String/Cons ~ (84 (gb hb)) - & @String/Cons ~ (69 (@String/Nil gb)) - & @String/Cons ~ (10 (@String/Nil lb)) - & @IO/bind ~ (tb ((((ub ub) (wb xb)) xb) yb)) - & @call ~ (rb (sb tb)) - & @String/Cons ~ (67 (qb rb)) - & @String/Cons ~ (76 (pb qb)) - & @String/Cons ~ (79 (ob pb)) - & @String/Cons ~ (83 (nb ob)) - & @String/Cons ~ (69 (@String/Nil nb)) + +@print = (f h) + & @IO/bind ~ (g (@print__C3 h)) + & @call ~ (e ((1 f) g)) + & @String/Cons ~ (87 (d e)) + & @String/Cons ~ (82 (c d)) + & @String/Cons ~ (73 (b c)) + & @String/Cons ~ (84 (a b)) + & @String/Cons ~ (69 (@String/Nil a)) + +@print__C0 = ((* a) (* a)) + +@print__C1 = g + & @call ~ (e ((1 f) g)) + & @String/Cons ~ (87 (d e)) + & @String/Cons ~ (82 (c d)) + & @String/Cons ~ (73 (b c)) + & @String/Cons ~ (84 (a b)) + & @String/Cons ~ (69 (@String/Nil a)) + & @String/Cons ~ (10 (@String/Nil f)) + +@print__C2 = (a (* c)) + & @IO/bind ~ (@print__C1 (((@print__C0 (a b)) b) c)) + +@print__C3 = ((@print__C2 (@IO/wrap a)) a) + +@read = (e f) + & @call ~ (d ((e 30) f)) + & @String/Cons ~ (82 (c d)) + & @String/Cons ~ (69 (b c)) + & @String/Cons ~ (65 (a b)) + & @String/Cons ~ (68 (@String/Nil a)) @undefer = (((a a) b) b) From be6ad6f815b831727aeecedcf540cd627c322325 Mon Sep 17 00:00:00 2001 From: Enrico Zandomeni Borba Date: Mon, 5 Aug 2024 12:39:19 +0200 Subject: [PATCH 11/17] align cuda with c --- src/run.c | 1 + src/run.cu | 220 +++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 157 insertions(+), 64 deletions(-) diff --git a/src/run.c b/src/run.c index 86dffa38..51505156 100644 --- a/src/run.c +++ b/src/run.c @@ -352,6 +352,7 @@ Port inject_io_err_name(Net* net) { return inject_io_err(net, io_error); } +/// Returns a λ-Encoded Ctr for a Result/Err(IOError/Inner(val)) Port inject_io_err_inner(Net* net, Port val) { IOError io_error = { .tag = IO_ERR_INNER, diff --git a/src/run.cu b/src/run.cu index 33cbaefc..def5cf84 100644 --- a/src/run.cu +++ b/src/run.cu @@ -41,6 +41,20 @@ typedef struct Bytes { #define RESULT_OK 0 #define RESULT_ERR 1 +// IOError = { +// Type, -- a type error +// Name, -- invalid io func name +// Inner {val: T}, -- an error while calling an io func +// } +#define IO_ERR_TYPE 0 +#define IO_ERR_NAME 1 +#define IO_ERR_INNER 2 + +typedef struct IOError { + u32 tag; + Port val; +} IOError; + // List Type #define LIST_NIL 0 #define LIST_CONS 1 @@ -359,10 +373,99 @@ extern "C" Port gnet_inject_err(GNet* gnet, Port val) { return ret; } -/// Returns a λ-Encoded Ctr for a RESULT_ERR: λt ((t RESULT_ERR) err) -/// where the Error variant contains a String. +/// Returns a λ-Encoded Ctr for a Result/Err(IOError(..)) +__device__ Port inject_io_err(Net* net, TM* tm, IOError err) { + if (err.tag <= IO_ERR_NAME) { + if (!get_resources(net, tm, 0, 2, 1)) { + return new_port(ERA, 0); + } + + u32 v1 = tm->vloc[0]; + + u32 n1 = tm->nloc[0]; + u32 n2 = tm->nloc[1]; + + vars_create(net, v1, NONE); + Port var = new_port(VAR, v1); + + node_create(net, n1, new_pair(new_port(NUM, new_u24(err.tag)), var)); + node_create(net, n2, new_pair(new_port(CON, n1), var)); + + return inject_err(net, tm, new_port(CON, n2)); + } + + if (!get_resources(net, tm, 0, 3, 1)) { + return new_port(ERA, 0); + } + + u32 v1 = tm->vloc[0]; + + u32 n1 = tm->nloc[0]; + u32 n2 = tm->nloc[1]; + u32 n3 = tm->nloc[2]; + + vars_create(net, v1, NONE); + Port var = new_port(VAR, v1); + + node_create(net, n1, new_pair(err.val, var)); + node_create(net, n2, new_pair(new_port(NUM, new_u24(IO_ERR_INNER)), new_port(CON, n1))); + node_create(net, n3, new_pair(new_port(CON, n2), var)); + + return inject_err(net, tm, new_port(CON, n3)); +} + +__global__ void make_io_err_port(GNet* gnet, IOError err, Port* ret) { + if (GID() == 0) { + TM tm = tmem_new(); + Net net = vnet_new(gnet, NULL, gnet->turn); + *ret = inject_io_err(&net, &tm, err); + } +} + +extern "C" Port gnet_inject_io_err(GNet* gnet, IOError err) { + Port* d_ret; + cudaMalloc(&d_ret, sizeof(Port)); + + make_io_err_port<<<1,1>>>(gnet, err, d_ret); + + Port ret; + cudaMemcpy(&ret, d_ret, sizeof(Port), cudaMemcpyDeviceToHost); + cudaFree(d_ret); + + return ret; +} + +/// Returns a λ-Encoded Ctr for a Result/Err(IOError/Type) +extern "C" Port gnet_inject_io_err_type(GNet* gnet) { + IOError io_error = { + .tag = IO_ERR_TYPE, + }; + + return gnet_inject_io_err(gnet, io_error); +} + +/// Returns a λ-Encoded Ctr for a Result/Err(IOError/Name) +extern "C" Port gnet_inject_io_err_name(GNet* gnet) { + IOError io_error = { + .tag = IO_ERR_NAME, + }; + + return gnet_inject_io_err(gnet, io_error); +} + +/// Returns a λ-Encoded Ctr for a Result/Err(IOError/Inner(val)) +extern "C" Port gnet_inject_io_err_inner(GNet* gnet, Port val) { + IOError io_error = { + .tag = IO_ERR_INNER, + .val = val, + }; + + return gnet_inject_io_err(gnet, io_error); +} + +/// Returns a λ-Encoded Ctr for an Result> /// `err` must be `NUL`-terminated. -extern "C" Port gnet_inject_err_str(GNet* gnet, char* err) { +extern "C" Port gnet_inject_io_err_str(GNet* gnet, char* err) { Port* d_bytes_port; cudaMalloc(&d_bytes_port, sizeof(Port)); @@ -380,16 +483,7 @@ extern "C" Port gnet_inject_err_str(GNet* gnet, char* err) { cudaFree(bytes_cu.buf); - Port* d_ret; - cudaMalloc(&d_ret, sizeof(Port)); - - make_err_port<<<1,1>>>(gnet, bytes_port, d_ret); - - Port ret; - cudaMemcpy(&ret, d_ret, sizeof(Port), cudaMemcpyDeviceToHost); - cudaFree(d_ret); - - return ret; + return gnet_inject_io_err_inner(gnet, bytes_port); } @@ -450,19 +544,19 @@ void* readback_dylib(Port port) { // Reads from a file a specified number of bytes. // `argm` is a tuple of (file_descriptor, num_bytes). -// Returns: Result +// Returns: Result> Port io_read(GNet* gnet, Port argm) { Tup tup = gnet_readback_tup(gnet, argm, 2); if (tup.elem_len != 2) { fprintf(stderr, "io_read: expected 2-tuple\n"); - return gnet_inject_err(gnet, new_port(ERA, 0)); + return gnet_inject_io_err_type(gnet); } FILE* fp = readback_file(tup.elem_buf[0]); u32 num_bytes = get_u24(get_val(tup.elem_buf[1])); if (fp == NULL) { - return gnet_inject_err(gnet, new_port(NUM, new_i24(EBADF))); + return gnet_inject_io_err_inner(gnet, new_port(NUM, new_i24(EBADF))); } /// Read a string. @@ -473,7 +567,7 @@ Port io_read(GNet* gnet, Port argm) { if ((bytes.len != num_bytes) && ferror(fp)) { fprintf(stderr, "io_read: failed to read\n"); free(bytes.buf); - return gnet_inject_err(gnet, new_port(NUM, new_i24(ferror(fp)))); + return gnet_inject_io_err_inner(gnet, new_port(NUM, new_i24(ferror(fp)))); } // Convert it to a port. @@ -486,13 +580,11 @@ Port io_read(GNet* gnet, Port argm) { // Opens a file with the provided mode. // `argm` is a tuple (CON node) of the // file name and mode as strings. -// Returns: Result +// Returns: Result> Port io_open(GNet* gnet, Port argm) { Tup tup = gnet_readback_tup(gnet, argm, 2); if (tup.elem_len != 2) { - fprintf(stderr, "io_open: expected 2-tuple\n"); - - return gnet_inject_err(gnet, new_port(ERA, 0)); + return gnet_inject_io_err_type(gnet); } Str name = gnet_readback_str(gnet, tup.elem_buf[0]); @@ -506,7 +598,7 @@ Port io_open(GNet* gnet, Port argm) { free(mode.buf); if (FILE_POINTERS[fd] == NULL) { - return gnet_inject_err(gnet, new_port(NUM, new_i24(errno))); + return gnet_inject_io_err_inner(gnet, new_port(NUM, new_i24(errno))); } return gnet_inject_ok(gnet, new_port(NUM, new_u24(fd))); @@ -516,22 +608,20 @@ Port io_open(GNet* gnet, Port argm) { free(name.buf); free(mode.buf); - fprintf(stderr, "io_open: too many open files\n"); - // too many open files - return gnet_inject_err(gnet, new_port(NUM, new_i24(EMFILE))); + return gnet_inject_io_err_inner(gnet, new_port(NUM, new_i24(EMFILE))); } // Closes a file, reclaiming the file descriptor. -// Returns: Result<*, i24> +// Returns: Result<*, IOError> Port io_close(GNet* gnet, Port argm) { FILE* fp = readback_file(argm); if (fp == NULL) { - return gnet_inject_err(gnet, new_port(NUM, new_i24(EBADF))); + return gnet_inject_io_err_inner(gnet, new_port(NUM, new_i24(EBADF))); } if (fclose(fp) != 0) { - return gnet_inject_err(gnet, new_port(NUM, new_i24(ferror(fp)))); + return gnet_inject_io_err_inner(gnet, new_port(NUM, new_i24(ferror(fp)))); } FILE_POINTERS[get_u24(get_val(argm))] = NULL; @@ -542,12 +632,11 @@ Port io_close(GNet* gnet, Port argm) { // Writes a list of bytes to a file. // `argm` is a tuple (CON node) of the // file descriptor and list of bytes to write. -// Returns: Result<*, i24 | *> +// Returns: Result<*, IOError> Port io_write(GNet* gnet, Port argm) { Tup tup = gnet_readback_tup(gnet, argm, 2); if (tup.elem_len != 2) { - fprintf(stderr, "io_write: expected 2-tuple\n"); - return gnet_inject_err(gnet, new_port(ERA, 0)); + return gnet_inject_io_err_type(gnet); } FILE* fp = readback_file(tup.elem_buf[0]); @@ -556,13 +645,13 @@ Port io_write(GNet* gnet, Port argm) { if (fp == NULL) { free(bytes.buf); - return gnet_inject_err(gnet, new_port(NUM, new_i24(EBADF))); + return gnet_inject_io_err_inner(gnet, new_port(NUM, new_i24(EBADF))); } if (fwrite(bytes.buf, sizeof(char), bytes.len, fp) != bytes.len) { free(bytes.buf); - return gnet_inject_err(gnet, new_port(NUM, new_i24(ferror(fp)))); + return gnet_inject_io_err_inner(gnet, new_port(NUM, new_i24(ferror(fp)))); } free(bytes.buf); @@ -571,15 +660,15 @@ Port io_write(GNet* gnet, Port argm) { } // Flushes an output stream. -// Returns: Result<*, i24> +// Returns: Result<*, IOError> Port io_flush(GNet* gnet, Port argm) { FILE* fp = readback_file(argm); if (fp == NULL) { - return gnet_inject_err(gnet, new_port(NUM, new_i24(EBADF))); + return gnet_inject_io_err_inner(gnet, new_port(NUM, new_i24(EBADF))); } if (fflush(fp) != 0) { - return gnet_inject_err(gnet, new_port(NUM, new_i24(ferror(fp)))); + return gnet_inject_io_err_inner(gnet, new_port(NUM, new_i24(ferror(fp)))); } return gnet_inject_ok(gnet, new_port(ERA, 0)); @@ -593,12 +682,12 @@ Port io_flush(GNet* gnet, Port argm) { // - 0 (SEEK_SET): beginning of file // - 1 (SEEK_CUR): current position of the file pointer // - 2 (SEEK_END): end of the file -// Returns: Result<*, i24 | *> +// Returns: Result<*, IOError> Port io_seek(GNet* gnet, Port argm) { Tup tup = gnet_readback_tup(gnet, argm, 3); if (tup.elem_len != 3) { fprintf(stderr, "io_seek: expected 3-tuple\n"); - return gnet_inject_err(gnet, new_port(ERA, 0)); + return gnet_inject_io_err_type(gnet); } FILE* fp = readback_file(tup.elem_buf[0]); @@ -606,7 +695,7 @@ Port io_seek(GNet* gnet, Port argm) { u32 whence = get_i24(get_val(tup.elem_buf[2])); if (fp == NULL) { - return gnet_inject_err(gnet, new_port(NUM, new_i24(EBADF))); + return gnet_inject_io_err_inner(gnet, new_port(NUM, new_i24(EBADF))); } int cwhence; @@ -615,12 +704,11 @@ Port io_seek(GNet* gnet, Port argm) { case 1: cwhence = SEEK_CUR; break; case 2: cwhence = SEEK_END; break; default: - fprintf(stderr, "io_seek: invalid whence\n"); - return gnet_inject_err(gnet, new_port(ERA, 0)); + return gnet_inject_io_err_type(gnet); } if (fseek(fp, offset, cwhence) != 0) { - return gnet_inject_err(gnet, new_port(NUM, new_i24(ferror(fp)))); + return gnet_inject_io_err_inner(gnet, new_port(NUM, new_i24(ferror(fp)))); } return gnet_inject_ok(gnet, new_port(ERA, 0)); @@ -628,7 +716,7 @@ Port io_seek(GNet* gnet, Port argm) { // Returns the current time as a tuple of the high // and low 24 bits of a 48-bit nanosecond timestamp. -// Returns: (u24, u24) +// Returns: Result<(u24, u24), IOError<*>> Port io_get_time(GNet* gnet, Port argm) { // Get the current time in nanoseconds u64 time_ns = time64(); @@ -642,12 +730,11 @@ Port io_get_time(GNet* gnet, Port argm) { // Sleeps. // `argm` is a tuple (CON node) of the high and low // 24 bits for a 48-bit duration in nanoseconds. -// Returns: Result<*, *> +// Returns: Result<*, IOError<*>> Port io_sleep(GNet* gnet, Port argm) { Tup tup = gnet_readback_tup(gnet, argm, 2); if (tup.elem_len != 2) { - fprintf(stderr, "io_sleep: expected 3-tuple\n"); - return gnet_inject_err(gnet, new_port(ERA, 0)); + return gnet_inject_io_err_type(gnet); } // Get the sleep duration node @@ -670,7 +757,7 @@ Port io_sleep(GNet* gnet, Port argm) { // `argm` is a tuple of `filename` and `lazy`. // `filename` is a λ-encoded string. // `lazy` is a `bool` indicating if functions should be lazily loaded. -// Returns: Result +// Returns: Result> Port io_dl_open(GNet* gnet, Port argm) { Tup tup = gnet_readback_tup(gnet, argm, 2); Str str = gnet_readback_str(gnet, tup.elem_buf[0]); @@ -685,14 +772,14 @@ Port io_dl_open(GNet* gnet, Port argm) { free(str.buf); if (DYLIBS[dl] == NULL) { - return gnet_inject_err_str(gnet, dlerror()); + return gnet_inject_io_err_str(gnet, dlerror()); } return gnet_inject_ok(gnet, new_port(NUM, new_u24(dl))); } } - return gnet_inject_err_str(gnet, "too many open dylibs"); + return gnet_inject_io_err_str(gnet, "too many open dylibs"); } // Calls a function from a loaded dylib. @@ -700,12 +787,17 @@ Port io_dl_open(GNet* gnet, Port argm) { // `dylib_handle` is the numeric node returned from a `DL_OPEN` call. // `symbol` is a λ-encoded string of the symbol name. // `args` is the argument to be provided to the dylib symbol. +// +// This function returns a Result with an Ok variant containing an +// arbitrary type. +// +// Returns Result> Port io_dl_call(GNet* gnet, Port argm) { Tup tup = gnet_readback_tup(gnet, argm, 3); if (tup.elem_len != 3) { fprintf(stderr, "io_dl_call: expected 3-tuple\n"); - return gnet_inject_err(gnet, new_port(ERA, 0)); + return gnet_inject_io_err_type(gnet); } void* dl = readback_dylib(tup.elem_buf[0]); @@ -715,24 +807,26 @@ Port io_dl_call(GNet* gnet, Port argm) { Port (*func)(GNet*, Port) = (Port (*)(GNet*, Port)) dlsym(dl, symbol.buf); char* error = dlerror(); if (error != NULL) { - return gnet_inject_err_str(gnet, error); + return gnet_inject_io_err_str(gnet, error); } return gnet_inject_ok(gnet, func(gnet, tup.elem_buf[2])); } // Closes a loaded dylib, reclaiming the handle. +// +// Returns: Result<*, IOError> Port io_dl_close(GNet* gnet, Book* book, Port argm) { void* dl = readback_dylib(argm); if (dl == NULL) { fprintf(stderr, "io_dl_close: invalid handle\n"); - return gnet_inject_err(gnet, new_port(ERA, 0)); + return gnet_inject_io_err_type(gnet); } int err = dlclose(dl) != 0; if (err != 0) { - return gnet_inject_err_str(gnet, dlerror()); + return gnet_inject_io_err_str(gnet, dlerror()); } DYLIBS[get_u24(get_val(argm))] = NULL; @@ -798,27 +892,25 @@ void do_run_io(GNet* gnet, Book* book, Port port) { } } - if (ffn == NULL) { - fprintf(stderr, "Unknown IO func '%s'\n", func.buf); - - free(func.buf); - - break; - } - - debug("running io func '%s'\n", func.buf); - free(func.buf); Port argm = ctr.args_buf[2]; Port cont = ctr.args_buf[3]; - Port ret = ffn->func(gnet, argm); + + Port ret; + if (ffn == NULL) { + ret = gnet_inject_io_err_name(gnet); + } else { + ret = ffn->func(gnet, argm); + } Port p = gnet_make_node(gnet, CON, ret, ROOT); gnet_boot_redex(gnet, new_pair(p, cont)); port = ROOT; + continue; } + case IO_DONE: { break; } From 09b314d79a17fc67a9bb9166d9d8ca57475d8857 Mon Sep 17 00:00:00 2001 From: Enrico Zandomeni Borba Date: Mon, 5 Aug 2024 12:44:57 +0200 Subject: [PATCH 12/17] add test-io to io files otherwise the rust impl will run on these --- tests/programs/io/basic.bend | 2 ++ tests/programs/io/basic.hvm | 2 ++ tests/programs/io/invalid-name.bend | 2 ++ tests/programs/io/invalid-name.hvm | 2 ++ tests/programs/io/open1.bend | 2 ++ tests/programs/io/open1.hvm | 2 ++ tests/programs/io/open2.bend | 2 ++ tests/programs/io/open2.hvm | 2 ++ tests/programs/io/open3.bend | 2 ++ tests/programs/io/open3.hvm | 2 ++ 10 files changed, 20 insertions(+) diff --git a/tests/programs/io/basic.bend b/tests/programs/io/basic.bend index b80f1ad6..6c2d0124 100644 --- a/tests/programs/io/basic.bend +++ b/tests/programs/io/basic.bend @@ -1,3 +1,5 @@ +test-io = 1 + def unwrap(res): match res: case Result/Ok: diff --git a/tests/programs/io/basic.hvm b/tests/programs/io/basic.hvm index 31a2898e..02e41ffa 100644 --- a/tests/programs/io/basic.hvm +++ b/tests/programs/io/basic.hvm @@ -106,6 +106,8 @@ & @String/Cons ~ (65 (a b)) & @String/Cons ~ (68 (@String/Nil a)) +@test-io = 1 + @undefer = (((a a) b) b) @unwrap = ((@unwrap__C0 a) a) diff --git a/tests/programs/io/invalid-name.bend b/tests/programs/io/invalid-name.bend index 7b211f64..6bc96c84 100644 --- a/tests/programs/io/invalid-name.bend +++ b/tests/programs/io/invalid-name.bend @@ -1,3 +1,5 @@ +test-io = 1 + def main(): with IO: f <- call("INVALID-NAME", ("./README.md", "r")) diff --git a/tests/programs/io/invalid-name.hvm b/tests/programs/io/invalid-name.hvm index fae3385f..e73aa507 100644 --- a/tests/programs/io/invalid-name.hvm +++ b/tests/programs/io/invalid-name.hvm @@ -64,6 +64,8 @@ & @String/Cons ~ (100 (@String/Nil m)) & @String/Cons ~ (114 (@String/Nil x)) +@test-io = 1 + @undefer = (((a a) b) b) diff --git a/tests/programs/io/open1.bend b/tests/programs/io/open1.bend index e4db0873..5778b86e 100644 --- a/tests/programs/io/open1.bend +++ b/tests/programs/io/open1.bend @@ -1,3 +1,5 @@ +test-io = 1 + def main(): with IO: f <- call("OPEN", ("./README.md", "r")) diff --git a/tests/programs/io/open1.hvm b/tests/programs/io/open1.hvm index dfb5b097..d7c6f058 100644 --- a/tests/programs/io/open1.hvm +++ b/tests/programs/io/open1.hvm @@ -56,6 +56,8 @@ & @String/Cons ~ (100 (@String/Nil e)) & @String/Cons ~ (114 (@String/Nil p)) +@test-io = 1 + @undefer = (((a a) b) b) diff --git a/tests/programs/io/open2.bend b/tests/programs/io/open2.bend index 8f9ed487..2d06ae9e 100644 --- a/tests/programs/io/open2.bend +++ b/tests/programs/io/open2.bend @@ -1,3 +1,5 @@ +test-io = 1 + def main(): with IO: f <- call("OPEN", ("fake-file", "r")) diff --git a/tests/programs/io/open2.hvm b/tests/programs/io/open2.hvm index 3f953e26..9c790cda 100644 --- a/tests/programs/io/open2.hvm +++ b/tests/programs/io/open2.hvm @@ -54,6 +54,8 @@ & @String/Cons ~ (101 (@String/Nil e)) & @String/Cons ~ (114 (@String/Nil n)) +@test-io = 1 + @undefer = (((a a) b) b) diff --git a/tests/programs/io/open3.bend b/tests/programs/io/open3.bend index ec73ab04..518b98da 100644 --- a/tests/programs/io/open3.bend +++ b/tests/programs/io/open3.bend @@ -1,3 +1,5 @@ +test-io = 1 + def main(): with IO: # calling open with an unexpected type of arg diff --git a/tests/programs/io/open3.hvm b/tests/programs/io/open3.hvm index 5a6ad10d..d6e02fce 100644 --- a/tests/programs/io/open3.hvm +++ b/tests/programs/io/open3.hvm @@ -44,6 +44,8 @@ & @String/Cons ~ (69 (a b)) & @String/Cons ~ (78 (@String/Nil a)) +@test-io = 1 + @undefer = (((a a) b) b) From b802f0bef2f0986d54fb0167d986603b10145d93 Mon Sep 17 00:00:00 2001 From: Enrico Zandomeni Borba Date: Mon, 5 Aug 2024 12:45:17 +0200 Subject: [PATCH 13/17] update demo_io files they were stale, and didn't represent the shortest bend code that can invoke io functions --- examples/demo_io/main.bend | 80 ++++-------- examples/demo_io/main.hvm | 248 ++++++++++--------------------------- 2 files changed, 88 insertions(+), 240 deletions(-) diff --git a/examples/demo_io/main.bend b/examples/demo_io/main.bend index b9c22fab..6c2d0124 100644 --- a/examples/demo_io/main.bend +++ b/examples/demo_io/main.bend @@ -1,63 +1,35 @@ test-io = 1 -type IO_T: - Done { magic, expr } - Call { magic, func, argm, cont } +def unwrap(res): + match res: + case Result/Ok: + return res.val + case Result/Err: + return res.val -def IO_T/MAGIC: - return (0xD0CA11, 0xFF1FF1) +def open(): + return call("OPEN", ("./README.md", "r")) -def IO_T/bind(a, b): - match a: - case IO_T/Done: - return (undefer(b))(a.expr) - case IO_T/Call: - return IO_T/Call(IO_T/MAGIC, a.func, a.argm, lambda x: IO_T/bind(a.cont(x), b)) +def read(f): + return call("READ", (f, 30)) -def call_io(func, argm): - return IO_T/Call(IO_T/MAGIC, func, argm, lambda x: IO_T/Done(IO_T/MAGIC, x)) +def print(bytes): + with IO: + * <- call("WRITE", (1, bytes)) + * <- call("WRITE", (1, "\n")) -def read_input(): - with IO_T: - * <- call_io("WRITE", (1, "What is your name?\n")) - return call_io("READ", (0, 10)) + return wrap(*) -def write_to_file(): - with IO_T: - fp <- call_io("OPEN", ("testing.txt", "w")) - input <- read_input() - * <- call_io("WRITE", (fp, input)) - * <- call_io("WRITE", (fp, "\n")) - - return call_io("CLOSE", fp) - -def read_from_file(): - with IO_T: - fp <- call_io("OPEN", ("testing.txt", "r")) - bytes <- call_io("READ", (fp, 5)) - * <- call_io("WRITE", (1, bytes)) - * <- call_io("SEEK", (fp, 2, 0)) - bytes <- call_io("READ", (fp, 5)) - * <- call_io("WRITE", (1, bytes)) - - return call_io("CLOSE", fp) - -def write: - return "WRITE" - -def one: - return 1 - -def newline: - return "\n" - -def one_newline_pair: - return (one, newline) +def close(f): + return call("CLOSE", f) def main(): - with IO_T: - * <- write_to_file() - * <- read_from_file() - * <- call_io(write, one_newline_pair) - - return 42 + with IO: + f <- open() + f = unwrap(f) + bytes <- read(f) + bytes = unwrap(bytes) + * <- print(bytes) + res <- close(f) + + return wrap(res) diff --git a/examples/demo_io/main.hvm b/examples/demo_io/main.hvm index f5f3da3f..02e41ffa 100644 --- a/examples/demo_io/main.hvm +++ b/examples/demo_io/main.hvm @@ -1,23 +1,26 @@ -@IO_T/Call = (a (b (c (d ((@IO_T/Call/tag (a (b (c (d e))))) e))))) +@IO/Call = (a (b (c (d ((@IO/Call/tag (a (b (c (d e))))) e))))) -@IO_T/Call/tag = 1 +@IO/Call/tag = 1 -@IO_T/Done = (a (b ((@IO_T/Done/tag (a (b c))) c))) +@IO/Done = (a (b ((@IO/Done/tag (a (b c))) c))) -@IO_T/Done/tag = 0 +@IO/Done/tag = 0 -@IO_T/MAGIC = (13683217 16719857) +@IO/MAGIC = (13683217 16719857) -@IO_T/bind = ((@IO_T/bind__C2 a) a) +@IO/bind = ((@IO/bind__C2 a) a) -@IO_T/bind__C0 = (* (b (a c))) +@IO/bind__C0 = (* (b (a c))) & @undefer ~ (a (b c)) -@IO_T/bind__C1 = (* (* (a (b ((c d) (e g)))))) - & @IO_T/Call ~ (@IO_T/MAGIC (a (b ((c f) g)))) - & @IO_T/bind ~ (d (e f)) +@IO/bind__C1 = (* (* (a (b ((c d) (e g)))))) + & @IO/Call ~ (@IO/MAGIC (a (b ((c f) g)))) + & @IO/bind ~ (d (e f)) -@IO_T/bind__C2 = (?((@IO_T/bind__C0 @IO_T/bind__C1) a) a) +@IO/bind__C2 = (?((@IO/bind__C0 @IO/bind__C1) a) a) + +@IO/wrap = a + & @IO/Done ~ (@IO/MAGIC a) @String/Cons = (a (b ((@String/Cons/tag (a (b c))) c))) @@ -27,215 +30,88 @@ @String/Nil/tag = 0 -@call_io = (a (b c)) - & @IO_T/Call ~ (@IO_T/MAGIC (a (b (@call_io__C0 c)))) - -@call_io__C0 = a - & @IO_T/Done ~ (@IO_T/MAGIC a) - -@main = g - & @IO_T/bind ~ (@write_to_file ((((* e) f) f) g)) - & @IO_T/bind ~ (@read_from_file ((((* c) d) d) e)) - & @IO_T/bind ~ (a ((((* 42) b) b) c)) - & @call_io ~ (@write (@one_newline_pair a)) - -@newline = a - & @String/Cons ~ (10 (@String/Nil a)) - -@one = 1 +@call = (a (b c)) + & @IO/Call ~ (@IO/MAGIC (a (b (@call__C0 c)))) -@one_newline_pair = (@one @newline) +@call__C0 = a + & @IO/Done ~ (@IO/MAGIC a) -@read_from_file = a - & @IO_T/bind ~ (@read_from_file__C11 (@read_from_file__C10 a)) - -@read_from_file__C0 = (f (* g)) - & @call_io ~ (e (f g)) +@close = f + & @call ~ (e f) & @String/Cons ~ (67 (d e)) & @String/Cons ~ (76 (c d)) & @String/Cons ~ (79 (b c)) & @String/Cons ~ (83 (a b)) & @String/Cons ~ (69 (@String/Nil a)) -@read_from_file__C1 = e - & @String/Cons ~ (87 (d e)) - & @String/Cons ~ (82 (c d)) - & @String/Cons ~ (73 (b c)) - & @String/Cons ~ (84 (a b)) - & @String/Cons ~ (69 (@String/Nil a)) - -@read_from_file__C10 = ((@read_from_file__C9 a) a) - -@read_from_file__C11 = q - & @call_io ~ (d ((o p) q)) +@main = w + & @IO/bind ~ (@open ((((s (a u)) (@IO/wrap v)) v) w)) + & @IO/bind ~ (c ((((n (o (d q))) (r (s t))) t) u)) + & @unwrap ~ (a {b r}) + & @read ~ (b c) + & @IO/bind ~ (f ((((g (k (* m))) (n (o p))) p) q)) + & @print ~ (e f) + & @unwrap ~ (d e) + & @IO/bind ~ (h ((((i i) (k l)) l) m)) + & @close ~ (g h) + +@open = q + & @call ~ (d ((o p) q)) & @String/Cons ~ (79 (c d)) & @String/Cons ~ (80 (b c)) & @String/Cons ~ (69 (a b)) & @String/Cons ~ (78 (@String/Nil a)) - & @String/Cons ~ (116 (n o)) - & @String/Cons ~ (101 (m n)) - & @String/Cons ~ (115 (l m)) - & @String/Cons ~ (116 (k l)) - & @String/Cons ~ (105 (j k)) - & @String/Cons ~ (110 (i j)) - & @String/Cons ~ (103 (h i)) - & @String/Cons ~ (46 (g h)) - & @String/Cons ~ (116 (f g)) - & @String/Cons ~ (120 (e f)) - & @String/Cons ~ (116 (@String/Nil e)) + & @String/Cons ~ (46 (n o)) + & @String/Cons ~ (47 (m n)) + & @String/Cons ~ (82 (l m)) + & @String/Cons ~ (69 (k l)) + & @String/Cons ~ (65 (j k)) + & @String/Cons ~ (68 (i j)) + & @String/Cons ~ (77 (h i)) + & @String/Cons ~ (69 (g h)) + & @String/Cons ~ (46 (f g)) + & @String/Cons ~ (109 (e f)) + & @String/Cons ~ (100 (@String/Nil e)) & @String/Cons ~ (114 (@String/Nil p)) -@read_from_file__C2 = (c (a e)) - & @IO_T/bind ~ (b (((@read_from_file__C0 (c d)) d) e)) - & @call_io ~ (@read_from_file__C1 ((1 a) b)) - -@read_from_file__C3 = d - & @String/Cons ~ (82 (c d)) - & @String/Cons ~ (69 (b c)) - & @String/Cons ~ (65 (a b)) - & @String/Cons ~ (68 (@String/Nil a)) - -@read_from_file__C4 = ({a c} (* e)) - & @IO_T/bind ~ (b (((@read_from_file__C2 (c d)) d) e)) - & @call_io ~ (@read_from_file__C3 ((a 5) b)) - -@read_from_file__C5 = d - & @String/Cons ~ (83 (c d)) - & @String/Cons ~ (69 (b c)) - & @String/Cons ~ (69 (a b)) - & @String/Cons ~ (75 (@String/Nil a)) - -@read_from_file__C6 = ({a c} (* e)) - & @IO_T/bind ~ (b (((@read_from_file__C4 (c d)) d) e)) - & @call_io ~ (@read_from_file__C5 ((a (2 0)) b)) - -@read_from_file__C7 = e +@print = (f h) + & @IO/bind ~ (g (@print__C3 h)) + & @call ~ (e ((1 f) g)) & @String/Cons ~ (87 (d e)) & @String/Cons ~ (82 (c d)) & @String/Cons ~ (73 (b c)) & @String/Cons ~ (84 (a b)) & @String/Cons ~ (69 (@String/Nil a)) -@read_from_file__C8 = (c (a e)) - & @IO_T/bind ~ (b (((@read_from_file__C6 (c d)) d) e)) - & @call_io ~ (@read_from_file__C7 ((1 a) b)) - -@read_from_file__C9 = ({e g} i) - & @IO_T/bind ~ (f (((@read_from_file__C8 (g h)) h) i)) - & @call_io ~ (d ((e 5) f)) - & @String/Cons ~ (82 (c d)) - & @String/Cons ~ (69 (b c)) - & @String/Cons ~ (65 (a b)) - & @String/Cons ~ (68 (@String/Nil a)) - -@read_input = a - & @IO_T/bind ~ (@read_input__C2 (@read_input__C1 a)) - -@read_input__C0 = (* e) - & @call_io ~ (d ((0 10) e)) - & @String/Cons ~ (82 (c d)) - & @String/Cons ~ (69 (b c)) - & @String/Cons ~ (65 (a b)) - & @String/Cons ~ (68 (@String/Nil a)) - -@read_input__C1 = ((@read_input__C0 a) a) +@print__C0 = ((* a) (* a)) -@read_input__C2 = y - & @call_io ~ (e ((1 x) y)) +@print__C1 = g + & @call ~ (e ((1 f) g)) & @String/Cons ~ (87 (d e)) & @String/Cons ~ (82 (c d)) & @String/Cons ~ (73 (b c)) & @String/Cons ~ (84 (a b)) & @String/Cons ~ (69 (@String/Nil a)) - & @String/Cons ~ (87 (w x)) - & @String/Cons ~ (104 (v w)) - & @String/Cons ~ (97 (u v)) - & @String/Cons ~ (116 (t u)) - & @String/Cons ~ (32 (s t)) - & @String/Cons ~ (105 (r s)) - & @String/Cons ~ (115 (q r)) - & @String/Cons ~ (32 (p q)) - & @String/Cons ~ (121 (o p)) - & @String/Cons ~ (111 (n o)) - & @String/Cons ~ (117 (m n)) - & @String/Cons ~ (114 (l m)) - & @String/Cons ~ (32 (k l)) - & @String/Cons ~ (110 (j k)) - & @String/Cons ~ (97 (i j)) - & @String/Cons ~ (109 (h i)) - & @String/Cons ~ (101 (g h)) - & @String/Cons ~ (63 (f g)) & @String/Cons ~ (10 (@String/Nil f)) -@test-io = 1 - -@undefer = (((a a) b) b) - -@write = e - & @String/Cons ~ (87 (d e)) - & @String/Cons ~ (82 (c d)) - & @String/Cons ~ (73 (b c)) - & @String/Cons ~ (84 (a b)) - & @String/Cons ~ (69 (@String/Nil a)) - -@write_to_file = a - & @IO_T/bind ~ (@write_to_file__C8 (@write_to_file__C7 a)) - -@write_to_file__C0 = (f (* g)) - & @call_io ~ (e (f g)) - & @String/Cons ~ (67 (d e)) - & @String/Cons ~ (76 (c d)) - & @String/Cons ~ (79 (b c)) - & @String/Cons ~ (83 (a b)) - & @String/Cons ~ (69 (@String/Nil a)) - -@write_to_file__C1 = a - & @String/Cons ~ (10 (@String/Nil a)) - -@write_to_file__C2 = e - & @String/Cons ~ (87 (d e)) - & @String/Cons ~ (82 (c d)) - & @String/Cons ~ (73 (b c)) - & @String/Cons ~ (84 (a b)) - & @String/Cons ~ (69 (@String/Nil a)) +@print__C2 = (a (* c)) + & @IO/bind ~ (@print__C1 (((@print__C0 (a b)) b) c)) -@write_to_file__C3 = ({a c} (* e)) - & @IO_T/bind ~ (b (((@write_to_file__C0 (c d)) d) e)) - & @call_io ~ (@write_to_file__C2 ((a @write_to_file__C1) b)) +@print__C3 = ((@print__C2 (@IO/wrap a)) a) -@write_to_file__C4 = e - & @String/Cons ~ (87 (d e)) +@read = (e f) + & @call ~ (d ((e 30) f)) & @String/Cons ~ (82 (c d)) - & @String/Cons ~ (73 (b c)) - & @String/Cons ~ (84 (a b)) - & @String/Cons ~ (69 (@String/Nil a)) + & @String/Cons ~ (69 (b c)) + & @String/Cons ~ (65 (a b)) + & @String/Cons ~ (68 (@String/Nil a)) -@write_to_file__C5 = ({a d} (b f)) - & @IO_T/bind ~ (c (((@write_to_file__C3 (d e)) e) f)) - & @call_io ~ (@write_to_file__C4 ((a b) c)) +@test-io = 1 -@write_to_file__C6 = (a c) - & @IO_T/bind ~ (@read_input (((@write_to_file__C5 (a b)) b) c)) +@undefer = (((a a) b) b) -@write_to_file__C7 = ((@write_to_file__C6 a) a) +@unwrap = ((@unwrap__C0 a) a) -@write_to_file__C8 = q - & @call_io ~ (d ((o p) q)) - & @String/Cons ~ (79 (c d)) - & @String/Cons ~ (80 (b c)) - & @String/Cons ~ (69 (a b)) - & @String/Cons ~ (78 (@String/Nil a)) - & @String/Cons ~ (116 (n o)) - & @String/Cons ~ (101 (m n)) - & @String/Cons ~ (115 (l m)) - & @String/Cons ~ (116 (k l)) - & @String/Cons ~ (105 (j k)) - & @String/Cons ~ (110 (i j)) - & @String/Cons ~ (103 (h i)) - & @String/Cons ~ (46 (g h)) - & @String/Cons ~ (116 (f g)) - & @String/Cons ~ (120 (e f)) - & @String/Cons ~ (116 (@String/Nil e)) - & @String/Cons ~ (119 (@String/Nil p)) +@unwrap__C0 = (?(((a a) (* (b b))) c) c) From 9c58be271e71cff38b436130245f12f8d799b439 Mon Sep 17 00:00:00 2001 From: Enrico Zandomeni Borba Date: Mon, 5 Aug 2024 12:45:58 +0200 Subject: [PATCH 14/17] add test snapshots --- tests/snapshots/run__io_file@demo_io__main.hvm.snap | 5 ++--- tests/snapshots/run__io_file@io__basic.hvm.snap | 7 +++++++ tests/snapshots/run__io_file@io__invalid-name.hvm.snap | 6 ++++++ tests/snapshots/run__io_file@io__open1.hvm.snap | 6 ++++++ tests/snapshots/run__io_file@io__open2.hvm.snap | 6 ++++++ tests/snapshots/run__io_file@io__open3.hvm.snap | 6 ++++++ 6 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 tests/snapshots/run__io_file@io__basic.hvm.snap create mode 100644 tests/snapshots/run__io_file@io__invalid-name.hvm.snap create mode 100644 tests/snapshots/run__io_file@io__open1.hvm.snap create mode 100644 tests/snapshots/run__io_file@io__open2.hvm.snap create mode 100644 tests/snapshots/run__io_file@io__open3.hvm.snap diff --git a/tests/snapshots/run__io_file@demo_io__main.hvm.snap b/tests/snapshots/run__io_file@demo_io__main.hvm.snap index 8503fe52..2d0372ed 100644 --- a/tests/snapshots/run__io_file@demo_io__main.hvm.snap +++ b/tests/snapshots/run__io_file@demo_io__main.hvm.snap @@ -3,6 +3,5 @@ source: tests/run.rs expression: c_output input_file: examples/demo_io/main.hvm --- -What is your name? -io fr from -Result: 42 +Higher-order Virtual Machine 2 +Result: ((@IO/Done/tag (@IO/MAGIC (((0 (* x0)) x0) x1))) x1) diff --git a/tests/snapshots/run__io_file@io__basic.hvm.snap b/tests/snapshots/run__io_file@io__basic.hvm.snap new file mode 100644 index 00000000..ea9d9a01 --- /dev/null +++ b/tests/snapshots/run__io_file@io__basic.hvm.snap @@ -0,0 +1,7 @@ +--- +source: tests/run.rs +expression: c_output +input_file: tests/programs/io/basic.hvm +--- +Higher-order Virtual Machine 2 +Result: ((@IO/Done/tag (@IO/MAGIC (((0 (* x0)) x0) x1))) x1) diff --git a/tests/snapshots/run__io_file@io__invalid-name.hvm.snap b/tests/snapshots/run__io_file@io__invalid-name.hvm.snap new file mode 100644 index 00000000..4fa1086c --- /dev/null +++ b/tests/snapshots/run__io_file@io__invalid-name.hvm.snap @@ -0,0 +1,6 @@ +--- +source: tests/run.rs +expression: c_output +input_file: tests/programs/io/invalid-name.hvm +--- +Result: ((@IO/Done/tag (@IO/MAGIC (((1 (((1 x0) x0) x1)) x1) x2))) x2) diff --git a/tests/snapshots/run__io_file@io__open1.hvm.snap b/tests/snapshots/run__io_file@io__open1.hvm.snap new file mode 100644 index 00000000..ad0cdc85 --- /dev/null +++ b/tests/snapshots/run__io_file@io__open1.hvm.snap @@ -0,0 +1,6 @@ +--- +source: tests/run.rs +expression: c_output +input_file: tests/programs/io/open1.hvm +--- +Result: ((@IO/Done/tag (@IO/MAGIC (((0 (3 x0)) x0) x1))) x1) diff --git a/tests/snapshots/run__io_file@io__open2.hvm.snap b/tests/snapshots/run__io_file@io__open2.hvm.snap new file mode 100644 index 00000000..9db2db58 --- /dev/null +++ b/tests/snapshots/run__io_file@io__open2.hvm.snap @@ -0,0 +1,6 @@ +--- +source: tests/run.rs +expression: c_output +input_file: tests/programs/io/open2.hvm +--- +Result: ((@IO/Done/tag (@IO/MAGIC (((1 (((2 (+2 x0)) x0) x1)) x1) x2))) x2) diff --git a/tests/snapshots/run__io_file@io__open3.hvm.snap b/tests/snapshots/run__io_file@io__open3.hvm.snap new file mode 100644 index 00000000..166b324e --- /dev/null +++ b/tests/snapshots/run__io_file@io__open3.hvm.snap @@ -0,0 +1,6 @@ +--- +source: tests/run.rs +expression: c_output +input_file: tests/programs/io/open3.hvm +--- +Result: ((@IO/Done/tag (@IO/MAGIC (((1 (((0 x0) x0) x1)) x1) x2))) x2) From 6506a986fb6dd331b47da36fd171cd0083efbbd8 Mon Sep 17 00:00:00 2001 From: Enrico Zandomeni Borba Date: Mon, 5 Aug 2024 19:09:29 +0200 Subject: [PATCH 15/17] read LICENSE instead of README.md in tests the README is more likely to change and break tests than the LICENSE file. --- examples/demo_io/main.bend | 4 +-- examples/demo_io/main.hvm | 26 +++++++++---------- tests/programs/io/basic.bend | 4 +-- tests/programs/io/basic.hvm | 26 +++++++++---------- tests/programs/io/open1.bend | 2 +- tests/programs/io/open1.hvm | 26 +++++++++---------- .../run__io_file@demo_io__main.hvm.snap | 2 +- .../snapshots/run__io_file@io__basic.hvm.snap | 2 +- 8 files changed, 43 insertions(+), 49 deletions(-) diff --git a/examples/demo_io/main.bend b/examples/demo_io/main.bend index 6c2d0124..bce1eada 100644 --- a/examples/demo_io/main.bend +++ b/examples/demo_io/main.bend @@ -8,10 +8,10 @@ def unwrap(res): return res.val def open(): - return call("OPEN", ("./README.md", "r")) + return call("OPEN", ("./LICENSE", "r")) def read(f): - return call("READ", (f, 30)) + return call("READ", (f, 47)) def print(bytes): with IO: diff --git a/examples/demo_io/main.hvm b/examples/demo_io/main.hvm index 02e41ffa..c4b72892 100644 --- a/examples/demo_io/main.hvm +++ b/examples/demo_io/main.hvm @@ -55,24 +55,22 @@ & @IO/bind ~ (h ((((i i) (k l)) l) m)) & @close ~ (g h) -@open = q - & @call ~ (d ((o p) q)) +@open = o + & @call ~ (d ((m n) o)) & @String/Cons ~ (79 (c d)) & @String/Cons ~ (80 (b c)) & @String/Cons ~ (69 (a b)) & @String/Cons ~ (78 (@String/Nil a)) - & @String/Cons ~ (46 (n o)) - & @String/Cons ~ (47 (m n)) - & @String/Cons ~ (82 (l m)) - & @String/Cons ~ (69 (k l)) - & @String/Cons ~ (65 (j k)) - & @String/Cons ~ (68 (i j)) - & @String/Cons ~ (77 (h i)) + & @String/Cons ~ (46 (l m)) + & @String/Cons ~ (47 (k l)) + & @String/Cons ~ (76 (j k)) + & @String/Cons ~ (73 (i j)) + & @String/Cons ~ (67 (h i)) & @String/Cons ~ (69 (g h)) - & @String/Cons ~ (46 (f g)) - & @String/Cons ~ (109 (e f)) - & @String/Cons ~ (100 (@String/Nil e)) - & @String/Cons ~ (114 (@String/Nil p)) + & @String/Cons ~ (78 (f g)) + & @String/Cons ~ (83 (e f)) + & @String/Cons ~ (69 (@String/Nil e)) + & @String/Cons ~ (114 (@String/Nil n)) @print = (f h) & @IO/bind ~ (g (@print__C3 h)) @@ -100,7 +98,7 @@ @print__C3 = ((@print__C2 (@IO/wrap a)) a) @read = (e f) - & @call ~ (d ((e 30) f)) + & @call ~ (d ((e 47) f)) & @String/Cons ~ (82 (c d)) & @String/Cons ~ (69 (b c)) & @String/Cons ~ (65 (a b)) diff --git a/tests/programs/io/basic.bend b/tests/programs/io/basic.bend index 6c2d0124..bce1eada 100644 --- a/tests/programs/io/basic.bend +++ b/tests/programs/io/basic.bend @@ -8,10 +8,10 @@ def unwrap(res): return res.val def open(): - return call("OPEN", ("./README.md", "r")) + return call("OPEN", ("./LICENSE", "r")) def read(f): - return call("READ", (f, 30)) + return call("READ", (f, 47)) def print(bytes): with IO: diff --git a/tests/programs/io/basic.hvm b/tests/programs/io/basic.hvm index 02e41ffa..c4b72892 100644 --- a/tests/programs/io/basic.hvm +++ b/tests/programs/io/basic.hvm @@ -55,24 +55,22 @@ & @IO/bind ~ (h ((((i i) (k l)) l) m)) & @close ~ (g h) -@open = q - & @call ~ (d ((o p) q)) +@open = o + & @call ~ (d ((m n) o)) & @String/Cons ~ (79 (c d)) & @String/Cons ~ (80 (b c)) & @String/Cons ~ (69 (a b)) & @String/Cons ~ (78 (@String/Nil a)) - & @String/Cons ~ (46 (n o)) - & @String/Cons ~ (47 (m n)) - & @String/Cons ~ (82 (l m)) - & @String/Cons ~ (69 (k l)) - & @String/Cons ~ (65 (j k)) - & @String/Cons ~ (68 (i j)) - & @String/Cons ~ (77 (h i)) + & @String/Cons ~ (46 (l m)) + & @String/Cons ~ (47 (k l)) + & @String/Cons ~ (76 (j k)) + & @String/Cons ~ (73 (i j)) + & @String/Cons ~ (67 (h i)) & @String/Cons ~ (69 (g h)) - & @String/Cons ~ (46 (f g)) - & @String/Cons ~ (109 (e f)) - & @String/Cons ~ (100 (@String/Nil e)) - & @String/Cons ~ (114 (@String/Nil p)) + & @String/Cons ~ (78 (f g)) + & @String/Cons ~ (83 (e f)) + & @String/Cons ~ (69 (@String/Nil e)) + & @String/Cons ~ (114 (@String/Nil n)) @print = (f h) & @IO/bind ~ (g (@print__C3 h)) @@ -100,7 +98,7 @@ @print__C3 = ((@print__C2 (@IO/wrap a)) a) @read = (e f) - & @call ~ (d ((e 30) f)) + & @call ~ (d ((e 47) f)) & @String/Cons ~ (82 (c d)) & @String/Cons ~ (69 (b c)) & @String/Cons ~ (65 (a b)) diff --git a/tests/programs/io/open1.bend b/tests/programs/io/open1.bend index 5778b86e..3353c50d 100644 --- a/tests/programs/io/open1.bend +++ b/tests/programs/io/open1.bend @@ -2,6 +2,6 @@ test-io = 1 def main(): with IO: - f <- call("OPEN", ("./README.md", "r")) + f <- call("OPEN", ("./LICENSE", "r")) return wrap(f) diff --git a/tests/programs/io/open1.hvm b/tests/programs/io/open1.hvm index d7c6f058..2b433d47 100644 --- a/tests/programs/io/open1.hvm +++ b/tests/programs/io/open1.hvm @@ -36,25 +36,23 @@ @call__C0 = a & @IO/Done ~ (@IO/MAGIC a) -@main = u - & @IO/bind ~ (q ((((r r) (@IO/wrap t)) t) u)) - & @call ~ (d ((o p) q)) +@main = s + & @IO/bind ~ (o ((((p p) (@IO/wrap r)) r) s)) + & @call ~ (d ((m n) o)) & @String/Cons ~ (79 (c d)) & @String/Cons ~ (80 (b c)) & @String/Cons ~ (69 (a b)) & @String/Cons ~ (78 (@String/Nil a)) - & @String/Cons ~ (46 (n o)) - & @String/Cons ~ (47 (m n)) - & @String/Cons ~ (82 (l m)) - & @String/Cons ~ (69 (k l)) - & @String/Cons ~ (65 (j k)) - & @String/Cons ~ (68 (i j)) - & @String/Cons ~ (77 (h i)) + & @String/Cons ~ (46 (l m)) + & @String/Cons ~ (47 (k l)) + & @String/Cons ~ (76 (j k)) + & @String/Cons ~ (73 (i j)) + & @String/Cons ~ (67 (h i)) & @String/Cons ~ (69 (g h)) - & @String/Cons ~ (46 (f g)) - & @String/Cons ~ (109 (e f)) - & @String/Cons ~ (100 (@String/Nil e)) - & @String/Cons ~ (114 (@String/Nil p)) + & @String/Cons ~ (78 (f g)) + & @String/Cons ~ (83 (e f)) + & @String/Cons ~ (69 (@String/Nil e)) + & @String/Cons ~ (114 (@String/Nil n)) @test-io = 1 diff --git a/tests/snapshots/run__io_file@demo_io__main.hvm.snap b/tests/snapshots/run__io_file@demo_io__main.hvm.snap index 2d0372ed..66b51367 100644 --- a/tests/snapshots/run__io_file@demo_io__main.hvm.snap +++ b/tests/snapshots/run__io_file@demo_io__main.hvm.snap @@ -3,5 +3,5 @@ source: tests/run.rs expression: c_output input_file: examples/demo_io/main.hvm --- -Higher-order Virtual Machine 2 + Apache License Result: ((@IO/Done/tag (@IO/MAGIC (((0 (* x0)) x0) x1))) x1) diff --git a/tests/snapshots/run__io_file@io__basic.hvm.snap b/tests/snapshots/run__io_file@io__basic.hvm.snap index ea9d9a01..1de699b5 100644 --- a/tests/snapshots/run__io_file@io__basic.hvm.snap +++ b/tests/snapshots/run__io_file@io__basic.hvm.snap @@ -3,5 +3,5 @@ source: tests/run.rs expression: c_output input_file: tests/programs/io/basic.hvm --- -Higher-order Virtual Machine 2 + Apache License Result: ((@IO/Done/tag (@IO/MAGIC (((0 (* x0)) x0) x1))) x1) From 7846b50caeabaaace9384b566aff2d56937d1bb0 Mon Sep 17 00:00:00 2001 From: Enrico Zandomeni Borba Date: Mon, 5 Aug 2024 19:18:39 +0200 Subject: [PATCH 16/17] update .h and .cuh interfaces to reflect Str changes the Str struct was stale in the header files --- src/hvm.cuh | 4 ++-- src/hvm.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/hvm.cuh b/src/hvm.cuh index f4d73ea2..1c07a6c3 100644 --- a/src/hvm.cuh +++ b/src/hvm.cuh @@ -194,8 +194,8 @@ typedef struct Tup { extern Tup gnet_readback_tup(GNet* gnet, Port port, u32 size); typedef struct Str { - u32 text_len; - char text_buf[256]; + u32 len; + char *buf; } Str; // Reads a constructor-encoded string (of length at most 255 characters), diff --git a/src/hvm.h b/src/hvm.h index b4930e25..b9b59500 100644 --- a/src/hvm.h +++ b/src/hvm.h @@ -206,8 +206,8 @@ typedef struct Tup { extern Tup readback_tup(Net* net, Book* book, Port port, u32 size); typedef struct Str { - u32 text_len; - char text_buf[256]; + u32 len; + char *buf; } Str; // Reads a constructor-encoded string (of length at most 255 characters), From 888b2a4b13675695c8843ff1bc2455d5ccbf433d Mon Sep 17 00:00:00 2001 From: Enrico Zandomeni Borba Date: Tue, 6 Aug 2024 20:12:23 +0200 Subject: [PATCH 17/17] check ctr sizes in do_run_io amazingly this bug hadn't been caught before. we were not checking the number of arguments in `ctr` before accessing them, therefore accessing meaningless memory regions, which could lead to infinite loops as nothing would be reduced and the same "interaction" would be attempted endlessly. --- src/run.c | 7 ++++++- src/run.cu | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/run.c b/src/run.c index 51505156..13b3fa94 100644 --- a/src/run.c +++ b/src/run.c @@ -752,7 +752,7 @@ void do_run_io(Net* net, Book* book, Port port) { Ctr ctr = readback_ctr(net, book, peek(net, port)); // Checks if IO Magic Number is a CON - if (get_tag(ctr.args_buf[0]) != CON) { + if (ctr.args_len < 1 || get_tag(ctr.args_buf[0]) != CON) { break; } @@ -765,6 +765,11 @@ void do_run_io(Net* net, Book* book, Port port) { switch (ctr.tag) { case IO_CALL: { + if (ctr.args_len != 4) { + fprintf(stderr, "invalid IO_CALL: args_len = %u\n", ctr.args_len); + break; + } + Str func = readback_str(net, book, ctr.args_buf[1]); FFn* ffn = NULL; // FIXME: optimize this linear search diff --git a/src/run.cu b/src/run.cu index def5cf84..970f8a01 100644 --- a/src/run.cu +++ b/src/run.cu @@ -869,7 +869,7 @@ void do_run_io(GNet* gnet, Book* book, Port port) { Ctr ctr = gnet_readback_ctr(gnet, gnet_peek(gnet, port)); // Checks if IO Magic Number is a CON - if (get_tag(ctr.args_buf[0]) != CON) { + if (ctr.args_len < 1 || get_tag(ctr.args_buf[0]) != CON) { break; } @@ -882,6 +882,11 @@ void do_run_io(GNet* gnet, Book* book, Port port) { switch (ctr.tag) { case IO_CALL: { + if (ctr.args_len != 4) { + fprintf(stderr, "invalid IO_CALL: args_len = %u\n", ctr.args_len); + break; + } + Str func = gnet_readback_str(gnet, ctr.args_buf[1]); FFn* ffn = NULL; // FIXME: optimize this linear search