diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index c0933053..316a678d 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -13,7 +13,6 @@ jobs: timeout-minutes: 10 steps: - uses: actions/checkout@v3 - - uses: dsherret/rust-toolchain-file@v1 - name: compare perf run: | git fetch origin main diff --git a/examples/demo_io/main.bend b/examples/demo_io/main.bend index 5d982f50..b9c22fab 100644 --- a/examples/demo_io/main.bend +++ b/examples/demo_io/main.bend @@ -1,4 +1,3 @@ -# run io on tests test-io = 1 type IO_T: @@ -18,12 +17,47 @@ def IO_T/bind(a, b): def call_io(func, argm): return IO_T/Call(IO_T/MAGIC, func, argm, lambda x: IO_T/Done(IO_T/MAGIC, x)) -def main: +def read_input(): with IO_T: - * <- call_io("WRITE", (1, "Hi! What's your name?\n")) - name <- call_io("READ_LINE", 0) - * <- call_io("WRITE", (1, "Your name is '")) - * <- call_io("WRITE", (1, name)) - * <- call_io("WRITE", (1, "'!\n")) + * <- call_io("WRITE", (1, "What is your name?\n")) + return call_io("READ", (0, 10)) + +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 main(): + with IO_T: + * <- write_to_file() + * <- read_from_file() + * <- call_io(write, one_newline_pair) return 42 diff --git a/examples/demo_io/main.hvm b/examples/demo_io/main.hvm index 53dcf595..f5f3da3f 100644 --- a/examples/demo_io/main.hvm +++ b/examples/demo_io/main.hvm @@ -33,43 +33,126 @@ @call_io__C0 = a & @IO_T/Done ~ (@IO_T/MAGIC a) -@main = a - & @IO_T/bind ~ (@main__C12 (@main__C11 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)) -@main__C0 = (((* 42) a) a) +@newline = a + & @String/Cons ~ (10 (@String/Nil a)) -@main__C1 = i - & @call_io ~ (e ((1 h) i)) +@one = 1 + +@one_newline_pair = (@one @newline) + +@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)) - & @String/Cons ~ (39 (g h)) - & @String/Cons ~ (33 (f g)) - & @String/Cons ~ (10 (@String/Nil f)) -@main__C10 = (* a) - & @IO_T/bind ~ (@main__C9 (@main__C8 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)) -@main__C11 = ((@main__C10 a) 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)) -@main__C12 = bb - & @call_io ~ (e ((1 ab) bb)) +@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 ~ (72 (z ab)) - & @String/Cons ~ (105 (y z)) - & @String/Cons ~ (33 (x y)) - & @String/Cons ~ (32 (w x)) - & @String/Cons ~ (87 (v w)) - & @String/Cons ~ (104 (u v)) - & @String/Cons ~ (97 (t u)) - & @String/Cons ~ (116 (s t)) - & @String/Cons ~ (39 (r s)) + & @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)) @@ -84,63 +167,75 @@ & @String/Cons ~ (63 (f g)) & @String/Cons ~ (10 (@String/Nil f)) -@main__C2 = (* a) - & @IO_T/bind ~ (@main__C1 (@main__C0 a)) +@test-io = 1 + +@undefer = (((a a) b) b) -@main__C3 = e +@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)) -@main__C4 = ((@main__C2 a) 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)) -@main__C5 = (a (* c)) - & @IO_T/bind ~ (b (@main__C4 c)) - & @call_io ~ (@main__C3 ((1 a) b)) +@write_to_file__C1 = a + & @String/Cons ~ (10 (@String/Nil a)) -@main__C6 = t - & @call_io ~ (e ((1 s) t)) +@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)) - & @String/Cons ~ (89 (r s)) - & @String/Cons ~ (111 (q r)) - & @String/Cons ~ (117 (p q)) - & @String/Cons ~ (114 (o p)) - & @String/Cons ~ (32 (n o)) - & @String/Cons ~ (110 (m n)) - & @String/Cons ~ (97 (l m)) - & @String/Cons ~ (109 (k l)) - & @String/Cons ~ (101 (j k)) - & @String/Cons ~ (32 (i j)) - & @String/Cons ~ (105 (h i)) - & @String/Cons ~ (115 (g h)) - & @String/Cons ~ (32 (f g)) - & @String/Cons ~ (39 (@String/Nil f)) - -@main__C7 = (a c) - & @IO_T/bind ~ (@main__C6 (((@main__C5 (a b)) b) c)) - -@main__C8 = ((@main__C7 a) a) - -@main__C9 = j - & @call_io ~ (i (0 j)) - & @String/Cons ~ (82 (h i)) - & @String/Cons ~ (69 (g h)) - & @String/Cons ~ (65 (f g)) - & @String/Cons ~ (68 (e f)) - & @String/Cons ~ (95 (d e)) - & @String/Cons ~ (76 (c d)) + +@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 ~ (78 (a b)) + & @String/Cons ~ (84 (a b)) & @String/Cons ~ (69 (@String/Nil a)) -@test-io = 1 - -@undefer = (((a a) b) b) +@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)) diff --git a/src/run.c b/src/run.c index d71687f5..dc1eb187 100644 --- a/src/run.c +++ b/src/run.c @@ -7,6 +7,12 @@ typedef struct Ctr { Port args_buf[16]; } Ctr; +// Readback: Tuples +typedef struct Tup { + u32 elem_len; + Port elem_buf[8]; +} Tup; + // Readback: λ-Encoded Str (UTF-32) // FIXME: this is actually ASCII :| // FIXME: remove len limit @@ -15,6 +21,14 @@ typedef struct Str { char text_buf[256]; } Str; +// Readback: λ-Encoded list of bytes +typedef struct Bytes { + u32 len; + char *buf; +} Bytes; + +#define MAX_BYTES 256 + // IO Magic Number #define IO_MAGIC_0 0xD0CA11 #define IO_MAGIC_1 0xFF1FF1 @@ -64,7 +78,27 @@ Ctr readback_ctr(Net* net, Book* book, Port port) { return ctr; } -// Converts a UTF-32 (truncated to 24 bits) string to a Port. +// Reads back a tuple of at most `size` elements. Tuples are +// (right-nested con nodes) (CON 1 (CON 2 (CON 3 (...)))) +// The provided `port` should be `expanded` before calling. +Tup readback_tup(Net* net, Book* book, Port port, u32 size) { + Tup tup; + tup.elem_len = 0; + + // Loads remaining arguments + while (get_tag(port) == CON && (tup.elem_len + 1 < size)) { + Pair node = node_load(net, get_val(port)); + tup.elem_buf[tup.elem_len++] = expand(net, book, get_fst(node)); + + port = expand(net, book, get_snd(node)); + } + + tup.elem_buf[tup.elem_len++] = port; + + return tup; +} + +// Converts a Port into a UTF-32 (truncated to 24 bits) string. // Since unicode scalars can fit in 21 bits, HVM's u24 // integers can contain any unicode scalar value. // Encoding: @@ -80,13 +114,9 @@ Str readback_str(Net* net, Book* book, Port port) { // Normalizes the net normalize(net, book); - //printf("reading str %s\n", show_port(peek(net, port)).x); - // Reads the λ-Encoded Ctr Ctr ctr = readback_ctr(net, book, peek(net, port)); - //printf("reading tag %d | len %d\n", ctr.tag, ctr.args_len); - // Reads string layer switch (ctr.tag) { case LIST_NIL: { @@ -96,7 +126,6 @@ Str readback_str(Net* net, Book* book, Port port) { if (ctr.args_len != 2) break; if (get_tag(ctr.args_buf[0]) != NUM) break; if (str.text_len >= 256) { printf("ERROR: for now, HVM can only readback strings of length <256."); break; } - //printf("reading chr %d\n", get_u24(get_val(ctr.args_buf[0]))); str.text_buf[str.text_len++] = get_u24(get_val(ctr.args_buf[0])); boot_redex(net, new_pair(ctr.args_buf[1], ROOT)); port = ROOT; @@ -111,8 +140,47 @@ Str readback_str(Net* net, Book* book, Port port) { return str; } +// Converts a Port into a list of bytes. +// Encoding: +// - λt (t NIL) +// - λt (((t CONS) head) tail) +Bytes readback_bytes(Net* net, Book* book, Port port) { + // Result + Bytes bytes; + bytes.buf = (char*) malloc(sizeof(char) * MAX_BYTES); + bytes.len = 0; + + // Readback loop + while (TRUE) { + // Normalizes the net + normalize(net, book); + + // Reads the λ-Encoded Ctr + Ctr ctr = readback_ctr(net, book, peek(net, port)); + + // Reads string layer + switch (ctr.tag) { + case LIST_NIL: { + break; + } + case LIST_CONS: { + if (ctr.args_len != 2) break; + if (get_tag(ctr.args_buf[0]) != NUM) break; + if (bytes.len >= MAX_BYTES) { printf("ERROR: for now, HVM can only readback list of bytes of length <%u.", MAX_BYTES); break; } + bytes.buf[bytes.len++] = get_u24(get_val(ctr.args_buf[0])); + boot_redex(net, new_pair(ctr.args_buf[1], ROOT)); + port = ROOT; + continue; + } + } + break; + } + + return bytes; +} + /// Returns a λ-Encoded Ctr for a NIL: λt (t NIL) -/// Should only be called within `inject_str`, as a previous call +/// Should only be called within `inject_bytes`, as a previous call /// to `get_resources` is expected. Port inject_nil(Net* net) { u32 v1 = tm[0]->vloc[0]; @@ -130,7 +198,7 @@ Port inject_nil(Net* net) { } /// Returns a λ-Encoded Ctr for a CONS: λt (((t CONS) head) tail) -/// Should only be called within `inject_str`, as a previous call +/// Should only be called within `inject_bytes`, as a previous call /// to `get_resources` is expected. /// The `char_idx` parameter is used to offset the vloc and nloc /// allocations, otherwise they would conflict with each other on @@ -154,27 +222,25 @@ Port inject_cons(Net* net, Port head, Port tail, u32 char_idx) { return new_port(CON, n4); } -// Converts a UTF-32 (truncated to 24 bits) string to a Port. -// Since unicode scalars can fit in 21 bits, HVM's u24 -// integers can contain any unicode scalar value. +// Converts a list of bytes to a Port. // Encoding: // - λt (t NIL) // - λt (((t CONS) head) tail) -Port inject_str(Net* net, Str *str) { +Port inject_bytes(Net* net, Bytes *bytes) { // Allocate all resources up front: // - NIL needs 2 nodes & 1 var // - CONS needs 4 nodes & 1 var - u32 len = str->text_len; + u32 len = bytes->len; if (!get_resources(net, tm[0], 0, 2 + 4 * len, 1 + len)) { - printf("inject_str: failed to get resources\n"); + fprintf(stderr, "inject_bytes: failed to get resources\n"); return new_port(ERA, 0); } Port port = inject_nil(net); for (u32 i = 0; i < len; i++) { - Port chr = new_port(NUM, new_u24(str->text_buf[len - i - 1])); - port = inject_cons(net, chr, port, i); + Port byte = new_port(NUM, new_u24(bytes->buf[len - i - 1])); + port = inject_cons(net, byte, port, i); } return port; @@ -213,61 +279,52 @@ FILE* readback_file(Port port) { return fp; } -// Reads a single char from `argm`. -Port io_read_char(Net* net, Book* book, Port argm) { - FILE* fp = readback_file(peek(net, argm)); - if (fp == NULL) { +// Reads from a file a specified number of bytes. +// `argm` is a tuple of (file_descriptor, num_bytes). +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); } - /// Read a string. - Str str; - - str.text_buf[0] = fgetc(fp); - str.text_buf[1] = 0; - str.text_len = 1; + FILE* fp = readback_file(tup.elem_buf[0]); + u32 num_bytes = get_u24(get_val(tup.elem_buf[1])); - return inject_str(net, &str); -} - -// Reads from `argm` at most 255 characters or until a newline is seen. -Port io_read_line(Net* net, Book* book, Port argm) { - FILE* fp = readback_file(peek(net, argm)); if (fp == NULL) { - fprintf(stderr, "io_read_line: invalid file descriptor\n"); + fprintf(stderr, "io_read: invalid file descriptor\n"); return new_port(ERA, 0); } /// Read a string. - Str str; + Bytes bytes; + bytes.buf = (char*) malloc(sizeof(char) * num_bytes); + bytes.len = fread(bytes.buf, sizeof(char), num_bytes, fp); - if (fgets(str.text_buf, sizeof(str.text_buf), fp) == NULL) { - fprintf(stderr, "io_read_line: failed to read\n"); - } - str.text_len = strlen(str.text_buf); - - // Strip any trailing newline. - if (str.text_len > 0 && str.text_buf[str.text_len - 1] == '\n') { - str.text_buf[str.text_len] = 0; - str.text_len--; + if ((bytes.len != num_bytes) && ferror(fp)) { + fprintf(stderr, "io_read: failed to read\n"); + free(bytes.buf); + return new_port(ERA, 0); } // Convert it to a port. - return inject_str(net, &str); + Port ret = inject_bytes(net, &bytes); + free(bytes.buf); + return ret; } // Opens a file with the provided mode. // `argm` is a tuple (CON node) of the // file name and mode as strings. -Port io_open_file(Net* net, Book* book, Port argm) { - if (get_tag(peek(net, argm)) != CON) { - fprintf(stderr, "io_open_file: expected tuple\n"); +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); } - Pair args = node_load(net, get_val(argm)); - Str name = readback_str(net, book, get_fst(args)); - Str mode = readback_str(net, book, get_snd(args)); + Str name = readback_str(net, book, tup.elem_buf[0]); + Str mode = readback_str(net, book, tup.elem_buf[1]); for (u32 fd = 3; fd < sizeof(FILE_POINTERS); fd++) { if (FILE_POINTERS[fd] == NULL) { @@ -276,52 +333,93 @@ Port io_open_file(Net* net, Book* book, Port argm) { } } - fprintf(stderr, "io_open_file: too many open files\n"); - + fprintf(stderr, "io_open: too many open files\n"); return new_port(ERA, 0); } // Closes a file, reclaiming the file descriptor. -Port io_close_file(Net* net, Book* book, Port argm) { - FILE* fp = readback_file(peek(net, argm)); +Port io_close(Net* net, Book* book, Port argm) { + FILE* fp = readback_file(argm); if (fp == NULL) { - fprintf(stderr, "io_close_file: failed to close\n"); + fprintf(stderr, "io_close: failed to close\n"); return new_port(ERA, 0); } int err = fclose(fp) != 0; if (err != 0) { - fprintf(stderr, "io_close_file: failed to close: %i\n", err); + fprintf(stderr, "io_close: failed to close: %i\n", err); return new_port(ERA, 0); } FILE_POINTERS[get_u24(get_val(argm))] = NULL; - return new_port(ERA, 0); } -// Writes a string to a file. +// Writes a list of bytes to a file. // `argm` is a tuple (CON node) of the -// file descriptor and string to write. +// file descriptor and list of bytes to write. Port io_write(Net* net, Book* book, Port argm) { - if (get_tag(peek(net, argm)) != CON) { - fprintf(stderr, "io_write: expected tuple, but got %u\n", get_tag(peek(net, 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); } - Pair args = node_load(net, get_val(argm)); - FILE* fp = readback_file(peek(net, get_fst(args))); - Str str = readback_str(net, book, get_snd(args)); + 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); } - if (fputs(str.text_buf, fp) == EOF) { + if (fwrite(bytes.buf, sizeof(char), bytes.len, fp) != bytes.len) { fprintf(stderr, "io_write: failed to write\n"); } + free(bytes.buf); + return new_port(ERA, 0); +} + +// Seeks to a position in a file. +// `argm` is a 3-tuple (CON fd (CON offset whence)), where +// - fd is a file descriptor +// - offset is a signed byte offset +// - whence is what that offset is relative to: +// - 0 (SEEK_SET): beginning of file +// - 1 (SEEK_CUR): current position of the file pointer +// - 2 (SEEK_END): end of the file +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); + } + + FILE* fp = readback_file(tup.elem_buf[0]); + i32 offset = get_i24(get_val(tup.elem_buf[1])); + 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); + } + + int cwhence; + switch (whence) { + case 0: cwhence = SEEK_SET; break; + case 1: cwhence = SEEK_CUR; break; + case 2: cwhence = SEEK_END; break; + default: + fprintf(stderr, "io_seek: invalid whence\n"); + return new_port(ERA, 0); + } + + if (fseek(fp, offset, cwhence) != 0) { + fprintf(stderr, "io_seek: failed to seek\n"); + } + return new_port(ERA, 0); } @@ -345,11 +443,17 @@ Port io_get_time(Net* net, Book* book, Port argm) { // `argm` is a tuple (CON node) of the high and low // 24 bits for a 48-bit duration in nanoseconds. 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); + } + // Get the sleep duration node Pair dur_node = node_load(net, get_val(argm)); // Get the high and low 24-bit parts of the duration - u32 dur_hi = get_u24(get_val(get_fst(dur_node))); - u32 dur_lo = get_u24(get_val(get_snd(dur_node))); + u32 dur_hi = get_u24(get_val(tup.elem_buf[0])); + u32 dur_lo = get_u24(get_val(tup.elem_buf[1])); // Combine into a 48-bit duration in nanoseconds u64 dur_ns = (((u64)dur_hi) << 24) | dur_lo; // Sleep for the specified duration @@ -365,11 +469,11 @@ Port io_sleep(Net* net, Book* book, Port argm) { // ----------- void book_init(Book* book) { - book->ffns_buf[book->ffns_len++] = (FFn){"READ_CHAR", io_read_char}; - book->ffns_buf[book->ffns_len++] = (FFn){"READ_LINE", io_read_line}; - book->ffns_buf[book->ffns_len++] = (FFn){"OPEN_FILE", io_open_file}; - book->ffns_buf[book->ffns_len++] = (FFn){"CLOSE_FILE", io_close_file}; + book->ffns_buf[book->ffns_len++] = (FFn){"READ", io_read}; + book->ffns_buf[book->ffns_len++] = (FFn){"OPEN", io_open}; + book->ffns_buf[book->ffns_len++] = (FFn){"CLOSE", io_close}; book->ffns_buf[book->ffns_len++] = (FFn){"WRITE", io_write}; + book->ffns_buf[book->ffns_len++] = (FFn){"SEEK", io_seek}; book->ffns_buf[book->ffns_len++] = (FFn){"GET_TIME", io_get_time}; book->ffns_buf[book->ffns_len++] = (FFn){"SLEEP", io_sleep}; } @@ -404,11 +508,6 @@ void do_run_io(Net* net, Book* book, Port port) { switch (ctr.tag) { case IO_CALL: { Str func = readback_str(net, book, ctr.args_buf[1]); - Port argm = ctr.args_buf[2]; - Port cont = ctr.args_buf[3]; - u32 lps = 0; - u32 loc = node_alloc_1(net, tm[0], &lps); - Port ret = new_port(ERA, 0); FFn* ffn = NULL; // FIXME: optimize this linear search for (u32 fid = 0; fid < book->ffns_len; ++fid) { @@ -418,16 +517,22 @@ void do_run_io(Net* net, Book* book, Port port) { } } if (ffn == NULL) { + fprintf(stderr, "Unknown IO func '%s'\n", func.text_buf); break; } - ret = ffn->func(net, book, argm); + + Port argm = ctr.args_buf[2]; + Port cont = ctr.args_buf[3]; + Port 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: { - printf("DONE\n"); break; } } diff --git a/src/run.cu b/src/run.cu index 8893e263..d8a7c000 100644 --- a/src/run.cu +++ b/src/run.cu @@ -7,6 +7,12 @@ struct Ctr { Port args_buf[16]; }; +// Readback: Tuples +struct Tup { + u32 elem_len; + Port elem_buf[8]; +}; + // Readback: λ-Encoded Str (UTF-32) // FIXME: this is actually ASCII :| // FIXME: remove len limit @@ -15,6 +21,14 @@ struct Str { char text_buf[256]; }; +// Readback: λ-Encoded list of bytes +typedef struct Bytes { + u32 len; + char *buf; +} Bytes; + +#define MAX_BYTES 256 + // IO Magic Number #define IO_MAGIC_0 0xD0CA11 #define IO_MAGIC_1 0xFF1FF1 @@ -64,6 +78,27 @@ Ctr gnet_readback_ctr(GNet* gnet, Port port) { return ctr; } +// Reads back a tuple of at most `size` elements. Tuples are +// (right-nested con nodes) (CON 1 (CON 2 (CON 3 (...)))) +// The provided `port` should be `expanded` before calling. +Tup gnet_readback_tup(GNet* gnet, Port port, u32 size) { + Tup tup; + tup.elem_len = 0; + + // Loads remaining arguments + while (get_tag(port) == CON && (tup.elem_len + 1 < size)) { + Pair node = gnet_node_load(gnet, get_val(port)); + tup.elem_buf[tup.elem_len++] = gnet_expand(gnet, get_fst(node)); + + port = gnet_expand(gnet, get_snd(node)); + } + + tup.elem_buf[tup.elem_len++] = port; + + return tup; +} + + // Reads back a UTF-32 (truncated to 24 bits) string. // Since unicode scalars can fit in 21 bits, HVM's u24 // integers can contain any unicode scalar value. @@ -91,7 +126,7 @@ Str gnet_readback_str(GNet* gnet, Port port) { case LIST_CONS: { if (ctr.args_len != 2) break; if (get_tag(ctr.args_buf[0]) != NUM) break; - if (str.text_len >= 256) { printf("ERROR: for now, HVM can only readback strings of length <256."); break; } + if (str.text_len > 256) { printf("ERROR: for now, HVM can only readback strings of length <256."); break; } str.text_buf[str.text_len++] = get_u24(get_val(ctr.args_buf[0])); gnet_boot_redex(gnet, new_pair(ctr.args_buf[1], ROOT)); @@ -107,8 +142,50 @@ Str gnet_readback_str(GNet* gnet, Port port) { return str; } +// Converts a Port into a list of bytes. +// Encoding: +// - λt (t NIL) +// - λt (((t CONS) head) tail) +Bytes gnet_readback_bytes(GNet* gnet, Port port) { + // Result + Bytes bytes; + bytes.buf = (char*) malloc(sizeof(char) * MAX_BYTES); + bytes.len = 0; + + // Readback loop + while (TRUE) { + // Normalizes the net + gnet_normalize(gnet); + + // Reads the λ-Encoded Ctr + Ctr ctr = gnet_readback_ctr(gnet, gnet_peek(gnet, port)); + + // Reads string layer + switch (ctr.tag) { + case LIST_NIL: { + break; + } + case LIST_CONS: { + if (ctr.args_len != 2) break; + if (get_tag(ctr.args_buf[0]) != NUM) break; + if (bytes.len >= MAX_BYTES) { printf("ERROR: for now, HVM can only readback list of bytes of length <%u.", MAX_BYTES); break; } + + bytes.buf[bytes.len++] = get_u24(get_val(ctr.args_buf[0])); + gnet_boot_redex(gnet, new_pair(ctr.args_buf[1], ROOT)); + port = ROOT; + continue; + } + } + break; + } + + bytes.buf[bytes.len] = '\0'; + + return bytes; +} + /// Returns a λ-Encoded Ctr for a NIL: λt (t NIL) -/// Should only be called within `inject_str`, as a previous call +/// Should only be called within `inject_bytes`, as a previous call /// to `get_resources` is expected. __device__ Port inject_nil(Net* net, TM* tm) { u32 v1 = tm->vloc[0]; @@ -126,7 +203,7 @@ __device__ Port inject_nil(Net* net, TM* tm) { } /// Returns a λ-Encoded Ctr for a CONS: λt (((t CONS) head) tail) -/// Should only be called within `inject_str`, as a previous call +/// Should only be called within `inject_bytes`, as a previous call /// to `get_resources` is expected. /// The `char_idx` parameter is used to offset the vloc and nloc /// allocations, otherwise they would conflict with each other on @@ -150,59 +227,58 @@ __device__ Port inject_cons(Net* net, TM* tm, Port head, Port tail, u32 char_idx return new_port(CON, n4); } -// Converts a UTF-32 (truncated to 24 bits) string to a Port. -// Since unicode scalars can fit in 21 bits, HVM's u24 -// integers can contain any unicode scalar value. +// Converts a list of bytes to a Port. // Encoding: // - λt (t NIL) // - λt (((t CONS) head) tail) -__device__ Port inject_str(Net* net, TM* tm, Str *str) { +__device__ Port inject_bytes(Net* net, TM* tm, Bytes *bytes) { // Allocate all resources up front: // - NIL needs 2 nodes & 1 var // - CONS needs 4 nodes & 1 var - u32 len = str->text_len; + u32 len = bytes->len; if (!get_resources(net, tm, 0, 2 + 4 * len, 1 + len)) { - printf("inject_str: failed to get resources\n"); + printf("inject_bytes: failed to get resources\n"); return new_port(ERA, 0); } Port port = inject_nil(net, tm); for (u32 i = 0; i < len; i++) { - Port chr = new_port(NUM, new_u24(str->text_buf[len - i - 1])); - port = inject_cons(net, tm, chr, port, i); + Port byte = new_port(NUM, new_u24(bytes->buf[len - i - 1])); + port = inject_cons(net, tm, byte, port, i); } return port; } -__global__ void make_str_port(GNet* gnet, Str *str, Port* ret) { +__global__ void make_bytes_port(GNet* gnet, Bytes bytes, Port* ret) { if (GID() == 0) { TM tm; Net net = vnet_new(gnet, NULL, gnet->turn); - *ret = inject_str(&net, &tm, str); + *ret = inject_bytes(&net, &tm, &bytes); } } -// Converts a UTF-32 (truncated to 24 bits) string to a Port. -// Since unicode scalars can fit in 21 bits, HVM's u24 -// integers can contain any unicode scalar value. +// Converts a list of bytes to a Port. // Encoding: // - λt (t NIL) // - λt (((t CONS) head) tail) -Port gnet_inject_str(GNet* gnet, Str *str) { +Port gnet_inject_bytes(GNet* gnet, Bytes *bytes) { Port* d_ret; cudaMalloc(&d_ret, sizeof(Port)); - Str* cu_str; - cudaMalloc(&cu_str, sizeof(Str)); - cudaMemcpy(cu_str, str, sizeof(Str), cudaMemcpyHostToDevice); + Bytes cu_bytes; + cu_bytes.len = bytes->len; + + cudaMalloc(&cu_bytes.buf, sizeof(char) * cu_bytes.len); + cudaMemcpy(cu_bytes.buf, bytes->buf, sizeof(char) * cu_bytes.len, cudaMemcpyHostToDevice); - make_str_port<<<1,1>>>(gnet, cu_str, d_ret); + make_bytes_port<<<1,1>>>(gnet, cu_bytes, d_ret); Port ret; cudaMemcpy(&ret, d_ret, sizeof(Port), cudaMemcpyDeviceToHost); cudaFree(d_ret); + cudaFree(cu_bytes.buf); return ret; } @@ -221,7 +297,7 @@ static FILE* FILE_POINTERS[256]; // Converts a NUM port (file descriptor) to file pointer. FILE* readback_file(Port port) { if (get_tag(port) != NUM) { - fprintf(stderr, "non-num where file descriptor was expected: %i\n", get_tag(port)); + fprintf(stderr, "non-num where file descriptor was expected: %s\n", show_port(port).x); return NULL; } @@ -240,61 +316,52 @@ FILE* readback_file(Port port) { return fp; } -// Reads a single char from `argm`. -Port io_read_char(GNet* gnet, Port argm) { - FILE* fp = readback_file(gnet_peek(gnet, argm)); - if (fp == NULL) { +// Reads from a file a specified number of bytes. +// `argm` is a tuple of (file_descriptor, num_bytes). +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); } - /// Read a string. - Str str; - - str.text_buf[0] = fgetc(fp); - str.text_buf[1] = 0; - str.text_len = 1; + FILE* fp = readback_file(tup.elem_buf[0]); + u32 num_bytes = get_u24(get_val(tup.elem_buf[1])); - return gnet_inject_str(gnet, &str); -} - -// Reads from `argm` at most 255 characters or until a newline is seen. -Port io_read_line(GNet* gnet, Port argm) { - FILE* fp = readback_file(gnet_peek(gnet, argm)); if (fp == NULL) { - fprintf(stderr, "io_read_line: invalid file descriptor\n"); + fprintf(stderr, "io_read: invalid file descriptor\n"); return new_port(ERA, 0); } /// Read a string. - Str str; + Bytes bytes; + bytes.buf = (char*) malloc(sizeof(char) * num_bytes); + bytes.len = fread(bytes.buf, sizeof(char), num_bytes, fp); - if (fgets(str.text_buf, sizeof(str.text_buf), fp) == NULL) { - fprintf(stderr, "io_read_line: failed to read\n"); - } - str.text_len = strlen(str.text_buf); - - // Strip any trailing newline. - if (str.text_len > 0 && str.text_buf[str.text_len - 1] == '\n') { - str.text_buf[str.text_len] = 0; - str.text_len--; + if ((bytes.len != num_bytes) && ferror(fp)) { + fprintf(stderr, "io_read: failed to read\n"); + free(bytes.buf); + return new_port(ERA, 0); } // Convert it to a port. - return gnet_inject_str(gnet, &str); + Port ret = gnet_inject_bytes(gnet, &bytes); + free(bytes.buf); + return ret; } // Opens a file with the provided mode. // `argm` is a tuple (CON node) of the // file name and mode as strings. -Port io_open_file(GNet* gnet, Port argm) { - if (get_tag(gnet_peek(gnet, argm)) != CON) { - fprintf(stderr, "io_open_file: expected tuple\n"); +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); } - Pair args = gnet_node_load(gnet, get_val(argm)); - Str name = gnet_readback_str(gnet, get_fst(args)); - Str mode = gnet_readback_str(gnet, get_snd(args)); + Str name = gnet_readback_str(gnet, tup.elem_buf[0]); + Str mode = gnet_readback_str(gnet, tup.elem_buf[1]); for (u32 fd = 3; fd < sizeof(FILE_POINTERS); fd++) { if (FILE_POINTERS[fd] == NULL) { @@ -303,52 +370,93 @@ Port io_open_file(GNet* gnet, Port argm) { } } - fprintf(stderr, "io_open_file: too many open files\n"); - + fprintf(stderr, "io_open: too many open files\n"); return new_port(ERA, 0); } // Closes a file, reclaiming the file descriptor. -Port io_close_file(GNet* gnet, Port argm) { - FILE* fp = readback_file(gnet_peek(gnet, argm)); +Port io_close(GNet* gnet, Port argm) { + FILE* fp = readback_file(argm); if (fp == NULL) { - fprintf(stderr, "io_close_file: failed to close\n"); + fprintf(stderr, "io_close: failed to close\n"); return new_port(ERA, 0); } int err = fclose(fp) != 0; if (err != 0) { - fprintf(stderr, "io_close_file: failed to close: %i\n", err); + fprintf(stderr, "io_close: failed to close: %i\n", err); return new_port(ERA, 0); } FILE_POINTERS[get_u24(get_val(argm))] = NULL; - return new_port(ERA, 0); } -// Writes a string to a file. +// Writes a list of bytes to a file. // `argm` is a tuple (CON node) of the -// file descriptor and string to write. +// file descriptor and list of bytes to write. Port io_write(GNet* gnet, Port argm) { - if (get_tag(gnet_peek(gnet, argm)) != CON) { - fprintf(stderr, "io_write: expected tuple, but got %u", get_tag(gnet_peek(gnet, 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); } - Pair args = gnet_node_load(gnet, get_val(argm)); - FILE* fp = readback_file(gnet_peek(gnet, get_fst(args))); - Str str = gnet_readback_str(gnet, get_snd(args)); + 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); } - if (fputs(str.text_buf, fp) == EOF) { + if (fwrite(bytes.buf, sizeof(char), bytes.len, fp) != bytes.len) { fprintf(stderr, "io_write: failed to write\n"); } + free(bytes.buf); + return new_port(ERA, 0); +} + +// Seeks to a position in a file. +// `argm` is a 3-tuple (CON fd (CON offset whence)), where +// - fd is a file descriptor +// - offset is a signed byte offset +// - whence is what that offset is relative to: +// - 0 (SEEK_SET): beginning of file +// - 1 (SEEK_CUR): current position of the file pointer +// - 2 (SEEK_END): end of the file +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); + } + + FILE* fp = readback_file(tup.elem_buf[0]); + i32 offset = get_i24(get_val(tup.elem_buf[1])); + 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); + } + + int cwhence; + switch (whence) { + case 0: cwhence = SEEK_SET; break; + case 1: cwhence = SEEK_CUR; break; + case 2: cwhence = SEEK_END; break; + default: + fprintf(stderr, "io_seek: invalid whence\n"); + return new_port(ERA, 0); + } + + if (fseek(fp, offset, cwhence) != 0) { + fprintf(stderr, "io_seek: failed to seek\n"); + } + return new_port(ERA, 0); } @@ -368,11 +476,17 @@ Port io_get_time(GNet* gnet, Port argm) { // `argm` is a tuple (CON node) of the high and low // 24 bits for a 48-bit duration in nanoseconds. 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); + } + // Get the sleep duration node Pair dur_node = gnet_node_load(gnet, get_val(argm)); // Get the high and low 24-bit parts of the duration - u32 dur_hi = get_u24(get_val(get_fst(dur_node))); - u32 dur_lo = get_u24(get_val(get_snd(dur_node))); + u32 dur_hi = get_u24(get_val(tup.elem_buf[0])); + u32 dur_lo = get_u24(get_val(tup.elem_buf[1])); // Combine into a 48-bit duration in nanoseconds u64 dur_ns = (((u64)dur_hi) << 24) | dur_lo; // Sleep for the specified duration @@ -385,11 +499,11 @@ Port io_sleep(GNet* gnet, Port argm) { } void book_init(Book* book) { - book->ffns_buf[book->ffns_len++] = (FFn){"READ_CHAR", io_read_char}; - book->ffns_buf[book->ffns_len++] = (FFn){"READ_LINE", io_read_line}; - book->ffns_buf[book->ffns_len++] = (FFn){"OPEN_FILE", io_open_file}; - book->ffns_buf[book->ffns_len++] = (FFn){"CLOSE_FILE", io_close_file}; + book->ffns_buf[book->ffns_len++] = (FFn){"READ", io_read}; + book->ffns_buf[book->ffns_len++] = (FFn){"OPEN", io_open}; + book->ffns_buf[book->ffns_len++] = (FFn){"CLOSE", io_close}; book->ffns_buf[book->ffns_len++] = (FFn){"WRITE", io_write}; + book->ffns_buf[book->ffns_len++] = (FFn){"SEEK", io_seek}; book->ffns_buf[book->ffns_len++] = (FFn){"GET_TIME", io_get_time}; book->ffns_buf[book->ffns_len++] = (FFn){"SLEEP", io_sleep}; @@ -435,7 +549,7 @@ void do_run_io(GNet* gnet, Book* book, Port port) { } } if (ffn == NULL) { - printf("FOUND NOTHING when looking for %s\n", func.text_buf); + fprintf(stderr, "Unknown IO func '%s'\n", func.text_buf); break; } @@ -449,7 +563,6 @@ void do_run_io(GNet* gnet, Book* book, Port port) { continue; } case IO_DONE: { - printf("DONE\n"); break; } } diff --git a/tests/programs/io/read_and_print.hvm b/tests/programs/io/read_and_print.hvm index d60a16b5..bcdc54cf 100644 --- a/tests/programs/io/read_and_print.hvm +++ b/tests/programs/io/read_and_print.hvm @@ -1,12 +1,17 @@ -@IO_T/Call = (a (b (c (d ((1 (a (b (c (d e))))) e))))) +@IO_T/Call = (a (b (c (d ((@IO_T/Call/tag (a (b (c (d e))))) e))))) -@IO_T/Done = (a (b ((0 (a (b c))) c))) +@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 = (* (a ((a b) b))) +@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)))) @@ -14,12 +19,13 @@ @IO_T/bind__C2 = (?((@IO_T/bind__C0 @IO_T/bind__C1) a) a) -@IO_T/wrap = a - & @IO_T/Done ~ (@IO_T/MAGIC a) +@String/Cons = (a (b ((@String/Cons/tag (a (b c))) c))) -@String/Cons = (a (b ((1 (a (b c))) c))) +@String/Cons/tag = 1 -@String/Nil = ((0 a) a) +@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)))) @@ -27,67 +33,113 @@ @call_io__C0 = a & @IO_T/Done ~ (@IO_T/MAGIC a) -@main = a - & @IO_T/bind ~ (@main__C7 (@main__C6 a)) - -@main__C0 = i - & @call_io ~ (e ((1 h) i)) +@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 ~ (39 (g h)) - & @String/Cons ~ (33 (f g)) & @String/Cons ~ (10 (@String/Nil f)) -@main__C1 = e +@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)) -@main__C2 = (* a) - & @IO_T/bind ~ (@main__C0 ((* 42) 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)) -@main__C3 = n - & @call_io ~ (e ((1 m) n)) +@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)) - & @String/Cons ~ (72 (l m)) - & @String/Cons ~ (101 (k l)) - & @String/Cons ~ (108 (j k)) - & @String/Cons ~ (108 (i j)) - & @String/Cons ~ (111 (h i)) - & @String/Cons ~ (44 (g h)) - & @String/Cons ~ (32 (f g)) - & @String/Cons ~ (39 (@String/Nil f)) - -@main__C4 = (a d) - & @IO_T/bind ~ (@main__C3 ((* c) d)) - & @IO_T/bind ~ (b (@main__C2 c)) - & @call_io ~ (@main__C1 ((1 a) b)) - -@main__C5 = j - & @call_io ~ (i (0 j)) - & @String/Cons ~ (82 (h i)) - & @String/Cons ~ (69 (g h)) - & @String/Cons ~ (65 (f g)) - & @String/Cons ~ (68 (e f)) - & @String/Cons ~ (95 (d e)) - & @String/Cons ~ (76 (c d)) - & @String/Cons ~ (73 (b c)) - & @String/Cons ~ (78 (a b)) - & @String/Cons ~ (69 (@String/Nil a)) -@main__C6 = (* a) - & @IO_T/bind ~ (@main__C5 (@main__C4 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) -@main__C7 = y +@read_input__C2 = y & @call_io ~ (e ((1 x) y)) & @String/Cons ~ (87 (d e)) & @String/Cons ~ (82 (c d)) @@ -115,3 +167,67 @@ & @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)) + + 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 f7f42b28..8503fe52 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,6 @@ source: tests/run.rs expression: c_output input_file: examples/demo_io/main.hvm --- -Hi! What's your name? -Your name is 'io from the tests'! +What is your name? +io fr from Result: 42 diff --git a/tests/snapshots/run__io_file@io__read_and_print.hvm.snap b/tests/snapshots/run__io_file@io__read_and_print.hvm.snap index e00390ac..8b7aabc9 100644 --- a/tests/snapshots/run__io_file@io__read_and_print.hvm.snap +++ b/tests/snapshots/run__io_file@io__read_and_print.hvm.snap @@ -4,5 +4,5 @@ expression: c_output input_file: tests/programs/io/read_and_print.hvm --- What is your name? -Hello, 'io from the tests'! +io fr from Result: 42