diff --git a/Makefile b/Makefile index ef0190e..d9e51f7 100644 --- a/Makefile +++ b/Makefile @@ -25,10 +25,11 @@ endif clean: -rm -f rosella *.o *.gcda *.gcno -rosella: rosella.o gc.o builtin.o interp.o reader.o +rosella: rosella.o gc.o builtin.o interp.o reader.o io_builtin.o rosella.o: rosella.c gc.h builtin.h interp.h gc.o: gc.c gc.h -builtin.o: builtin.c builtin.h gc.h +builtin.o: builtin.c builtin.h interp.h gc.h interp.o: interp.c interp.h gc.h builtin.h reader.o: reader.c reader.h gc.h builtin.h +io_builtin.o: io_builtin.c io_builtin.h builtin.h interp.h gc.h diff --git a/gc.h b/gc.h index 534d381..f8e0fa2 100644 --- a/gc.h +++ b/gc.h @@ -259,6 +259,11 @@ static inline fixnum_t _get_fixnum(value_t n) return ((fixnum_t)n) >> 1; } +static inline bool is_valid_fixnum(fixnum_t n) +{ + return _get_fixnum(fixnum_value(n)) == n; +} + static inline value_t object_value(void *obj) { assert((uintptr_t)obj >= 4096); diff --git a/io_builtin.c b/io_builtin.c new file mode 100644 index 0000000..3de740d --- /dev/null +++ b/io_builtin.c @@ -0,0 +1,219 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "gc.h" +#include "builtin.h" +#include "interp.h" +#include "io_builtin.h" + +static void bi_posix_open(interp_state_t *state); +//static void bi_posix_openat(interp_state_t *state); +static void bi_posix_dup(interp_state_t *state); +static void bi_posix_dup2(interp_state_t *state); +static void bi_posix_read(interp_state_t *state); +static void bi_posix_write(interp_state_t *state); +static void bi_posix_lseek(interp_state_t *state); +static void bi_posix_close(interp_state_t *state); + +void io_builtin_init(void) +{ + register_builtin(BI_IO_POSIX_OPEN, make_builtin_fn(bi_posix_open)); + //register_builtin(BI_IO_POSIX_OPENAT, make_builtin_fn(bi_posix_openat)); + register_builtin(BI_IO_POSIX_OPENAT, UNDEFINED); + + register_builtin(BI_IO_POSIX_DUP, make_builtin_fn(bi_posix_dup)); + register_builtin(BI_IO_POSIX_DUP2, make_builtin_fn(bi_posix_dup2)); + + register_builtin(BI_IO_POSIX_READ, make_builtin_fn(bi_posix_read)); + register_builtin(BI_IO_POSIX_WRITE, make_builtin_fn(bi_posix_write)); + register_builtin(BI_IO_POSIX_LSEEK, make_builtin_fn(bi_posix_lseek)); + + register_builtin(BI_IO_POSIX_CLOSE, make_builtin_fn(bi_posix_close)); +} + +static void simple_return(interp_state_t *state, value_t rval) +{ + state->lambda.value = state->k.value; + state->argv.value = rval; + state->k.value = FALSE_VALUE; + state->ctx.value = FALSE_VALUE; +} + +static void simple_return2(interp_state_t *state, value_t v1, value_t v2) +{ + simple_return(state, cons(v1, cons(v2, NIL))); +} + +static void bi_posix_open(interp_state_t *state) +{ + char *pathname; + int flags; + mode_t mode; + int fd; + int saved_errno; + + release_assert(is_nil(CDDR(state->argv.value)) || is_nil(CDR(_CDDR(state->argv.value)))); + + pathname = value_to_string(CAR(state->argv.value)); + flags = get_fixnum(CAR(_CDR(state->argv.value))); + + if (!is_nil(_CDDR(state->argv.value))) + { + mode = get_fixnum(CAR(_CDDR(state->argv.value))); + } + else + { + release_assert((flags & O_CREAT) == 0); + mode = 0; + } + + errno = 0; + fd = open(pathname, flags, mode); + saved_errno = errno; + free(pathname); + + release_assert(is_valid_fixnum(fd)); + release_assert(is_valid_fixnum(saved_errno)); + + simple_return2(state, (fd >= 0) ? fixnum_value(fd) : FALSE_VALUE, + fixnum_value(saved_errno)); +} + +//static void bi_posix_openat(interp_state_t *state) {} + +static void bi_posix_dup(interp_state_t *state) +{ + int oldfd = get_fixnum(CAR(state->argv.value)); + int newfd; + int saved_errno; + + release_assert(is_nil(_CDR(state->argv.value))); + + errno = 0; + newfd = dup(oldfd); + saved_errno = errno; + + release_assert(is_valid_fixnum(newfd)); + release_assert(is_valid_fixnum(saved_errno)); + + simple_return2(state, (newfd >= 0) ? fixnum_value(newfd) : FALSE_VALUE, + fixnum_value(saved_errno)); +} + +static void bi_posix_dup2(interp_state_t *state) +{ + int oldfd = get_fixnum(CAR(state->argv.value)); + int newfd = get_fixnum(CAR(_CDR(state->argv.value))); + int saved_errno; + + release_assert(is_nil(_CDDR(state->argv.value))); + + errno = 0; + newfd = dup2(oldfd, newfd); + saved_errno = errno; + + release_assert(is_valid_fixnum(newfd)); + release_assert(is_valid_fixnum(saved_errno)); + + simple_return2(state, (newfd >= 0) ? fixnum_value(newfd) : FALSE_VALUE, + fixnum_value(saved_errno)); +} + +static void bi_posix_read(interp_state_t *state) +{ + int fd = get_fixnum(CAR(state->argv.value)); + value_t str = CAR(_CDR(state->argv.value)); + fixnum_t count = get_fixnum(CAR(_CDDR(state->argv.value))); + ssize_t result; + int saved_errno; + + release_assert(is_byte_string(str)); + release_assert(is_nil(_CDR(_CDDR(state->argv.value)))); + release_assert((0 <= count) && (count <= _get_byte_string(str)->size)); + + errno = 0; + result = read(fd, _get_byte_string(str)->bytes, count); + saved_errno = errno; + + release_assert(is_valid_fixnum(result)); + release_assert(is_valid_fixnum(saved_errno)); + + simple_return2(state, (result >= 0) ? fixnum_value(result) : FALSE_VALUE, + fixnum_value(saved_errno)); +} + +static void bi_posix_write(interp_state_t *state) +{ + int fd = get_fixnum(CAR(state->argv.value)); + value_t str = CAR(_CDR(state->argv.value)); + fixnum_t count = get_fixnum(CAR(_CDDR(state->argv.value))); + ssize_t result; + int saved_errno; + + release_assert(is_byte_string(str)); + release_assert(is_nil(_CDR(_CDDR(state->argv.value)))); + release_assert((0 <= count) && (count <= _get_byte_string(str)->size)); + + errno = 0; + result = write(fd, _get_byte_string(str)->bytes, count); + saved_errno = errno; + + release_assert(is_valid_fixnum(result)); + release_assert(is_valid_fixnum(saved_errno)); + + simple_return2(state, (result >= 0) ? fixnum_value(result) : FALSE_VALUE, + fixnum_value(saved_errno)); +} + +static void bi_posix_lseek(interp_state_t *state) +{ + int fd = get_fixnum(CAR(state->argv.value)); + fixnum_t off = get_fixnum(CAR(_CDR(state->argv.value))); + fixnum_t whence = get_fixnum(CAR(_CDDR(state->argv.value))); + off_t result; + int saved_errno; + + release_assert(is_nil(_CDR(_CDDR(state->argv.value)))); + + errno = 0; + result = lseek(fd, off, whence); + saved_errno = errno; + + release_assert(is_valid_fixnum(saved_errno)); + + if (result == (off_t)(-1)) + { + simple_return2(state, FALSE_VALUE, fixnum_value(saved_errno)); + } + else + { + release_assert((result >= 0) && (result <= FIXNUM_MAX)); + simple_return2(state, fixnum_value(result), fixnum_value(saved_errno)); + } +} + +static void bi_posix_close(interp_state_t *state) +{ + int fd = get_fixnum(CAR(state->argv.value)); + ssize_t result; + int saved_errno; + + release_assert(is_nil(_CDR(state->argv.value))); + + errno = 0; + result = close(fd); + saved_errno = errno; + + simple_return2(state, boolean_value(!result), fixnum_value(saved_errno)); +} + +/* vim:set sw=2 expandtab: */ diff --git a/io_builtin.h b/io_builtin.h new file mode 100644 index 0000000..16fdd32 --- /dev/null +++ b/io_builtin.h @@ -0,0 +1,22 @@ +#ifndef IO_BUILTIN_H_a9dd5b4902aa45e05dcf2b0fdb33ec74 +#define IO_BUILTIN_H_a9dd5b4902aa45e05dcf2b0fdb33ec74 + +#include +#include + +#define BI_IO_POSIX_OPEN "posix-open" +#define BI_IO_POSIX_OPENAT "posix-openat" + +#define BI_IO_POSIX_DUP "posix-dup" +#define BI_IO_POSIX_DUP2 "posix-dup2" + +#define BI_IO_POSIX_READ "posix-read" +#define BI_IO_POSIX_WRITE "posix-write" +#define BI_IO_POSIX_LSEEK "posix-lseek" + +#define BI_IO_POSIX_CLOSE "posix-close" + +void io_builtin_init(void); + +#endif +/* vim:set sw=2 expandtab: */ diff --git a/rosella.c b/rosella.c index bf6bf5e..7a63c9a 100644 --- a/rosella.c +++ b/rosella.c @@ -11,6 +11,7 @@ #include "builtin.h" #include "interp.h" #include "reader.h" +#include "io_builtin.h" static void test_builtins(void); static void test_weak_boxes_and_wills(void); @@ -34,6 +35,7 @@ int main(int argc, char **argv) gc_init(256*1024, 1024*1024); builtin_init(); interpreter_init(); + io_builtin_init(); if (argc < 2 || (strcmp(argv[1], "-k") == 0)) {