| @@ -21,6 +21,8 @@ struct l2_bufio_reader { | |||
| char buf[L2_IO_BUFSIZ]; | |||
| }; | |||
| int l2_io_printf(struct l2_io_writer *w, const char *fmt, ...); | |||
| void l2_bufio_reader_init(struct l2_bufio_reader *b, struct l2_io_reader *r); | |||
| void l2_bufio_shift(struct l2_bufio_reader *b); | |||
| int l2_bufio_shift_peek(struct l2_bufio_reader *b, size_t count); | |||
| @@ -5,6 +5,7 @@ | |||
| #include "../bytecode.h" | |||
| #include "../bitset.h" | |||
| #include "../io.h" | |||
| struct l2_vm; | |||
| struct l2_vm_array; | |||
| @@ -83,6 +84,9 @@ struct l2_vm { | |||
| size_t opcount; | |||
| l2_word iptr; | |||
| struct l2_io_writer *std_output; | |||
| struct l2_io_writer *std_error; | |||
| struct l2_vm_value *values; | |||
| size_t valuessize; | |||
| struct l2_bitset valueset; | |||
| @@ -1,6 +1,43 @@ | |||
| #include "io.h" | |||
| #include <stdlib.h> | |||
| #include <stdarg.h> | |||
| int l2_io_printf(struct l2_io_writer *w, const char *fmt, ...) { | |||
| char buf[256]; | |||
| va_list va; | |||
| va_start(va, fmt); | |||
| int n = vsnprintf(buf, sizeof(buf), fmt, va); | |||
| if (n < 0) { | |||
| va_end(va); | |||
| return n; | |||
| } else if (n + 1 < sizeof(buf)) { | |||
| w->write(w, buf, n); | |||
| va_end(va); | |||
| return n; | |||
| } | |||
| // Yeah, this is slower, but it's just a fallback for when | |||
| // the output of printf is stupidly long. That shouldn't happen much. | |||
| char *buf2 = malloc(n + 1); | |||
| if (buf2 == NULL) { | |||
| va_end(va); | |||
| return -1; | |||
| } | |||
| n = vsnprintf(buf2, n + 1, fmt, va); | |||
| if (n < 0) { | |||
| va_end(va); | |||
| free(buf2); | |||
| return -1; | |||
| } | |||
| w->write(w, buf2, n); | |||
| va_end(va); | |||
| free(buf2); | |||
| return n; | |||
| } | |||
| void l2_bufio_reader_init(struct l2_bufio_reader *b, struct l2_io_reader *r) { | |||
| b->r = r; | |||
| @@ -2,50 +2,50 @@ | |||
| #include <stdio.h> | |||
| static void print_val(struct l2_vm *vm, struct l2_vm_value *val) { | |||
| static void print_val(struct l2_vm *vm, struct l2_io_writer *out, struct l2_vm_value *val) { | |||
| switch (l2_vm_value_type(val)) { | |||
| case L2_VAL_TYPE_NONE: | |||
| printf("(none)"); | |||
| l2_io_printf(out, "(none)"); | |||
| break; | |||
| case L2_VAL_TYPE_ATOM: | |||
| printf("(atom %u)", val->atom); | |||
| l2_io_printf(out, "(atom %u)", val->atom); | |||
| break; | |||
| case L2_VAL_TYPE_REAL: | |||
| printf("%g", val->real); | |||
| l2_io_printf(out, "%g", val->real); | |||
| break; | |||
| case L2_VAL_TYPE_BUFFER: | |||
| if (val->buffer != NULL) { | |||
| fwrite(val->buffer->data, 1, val->buffer->len, stdout); | |||
| out->write(out, val->buffer->data, val->buffer->len); | |||
| } | |||
| break; | |||
| case L2_VAL_TYPE_ARRAY: | |||
| if (val->array == NULL) { | |||
| printf("[]"); | |||
| out->write(out, "[]", 2); | |||
| break; | |||
| } | |||
| putchar('['); | |||
| out->write(out, "[", 1); | |||
| for (size_t i = 0; i < val->array->len; ++i) { | |||
| if (i != 0) { | |||
| putchar(' '); | |||
| out->write(out, " ", 1); | |||
| } | |||
| print_val(vm, &vm->values[val->array->data[i]]); | |||
| print_val(vm, out, &vm->values[val->array->data[i]]); | |||
| } | |||
| putchar(']'); | |||
| out->write(out, "]", 1); | |||
| break; | |||
| case L2_VAL_TYPE_NAMESPACE: | |||
| printf("(namespace)"); | |||
| l2_io_printf(out, "(namespace)"); | |||
| break; | |||
| case L2_VAL_TYPE_FUNCTION: | |||
| case L2_VAL_TYPE_CFUNCTION: | |||
| printf("(function)"); | |||
| l2_io_printf(out, "(function)"); | |||
| break; | |||
| } | |||
| } | |||
| @@ -81,14 +81,14 @@ l2_word l2_builtin_div(struct l2_vm *vm, struct l2_vm_array *args) { | |||
| l2_word l2_builtin_print(struct l2_vm *vm, struct l2_vm_array *args) { | |||
| for (size_t i = 0; i < args->len; ++i) { | |||
| if (i != 0) { | |||
| putchar(' '); | |||
| vm->std_output->write(vm->std_output, " ", 1); | |||
| } | |||
| struct l2_vm_value *val = &vm->values[args->data[i]]; | |||
| print_val(vm, val); | |||
| print_val(vm, vm->std_output, val); | |||
| } | |||
| putchar('\n'); | |||
| vm->std_output->write(vm->std_output, "\n", 1); | |||
| return 0; | |||
| } | |||
| @@ -5,6 +5,10 @@ | |||
| #include "vm/builtins.h" | |||
| static int stdio_inited = 0; | |||
| static struct l2_io_file_writer std_output; | |||
| static struct l2_io_file_writer std_error; | |||
| static l2_word alloc_val(struct l2_vm *vm) { | |||
| size_t id = l2_bitset_set_next(&vm->valueset); | |||
| if (id >= vm->valuessize) { | |||
| @@ -123,6 +127,17 @@ static size_t gc_sweep(struct l2_vm *vm) { | |||
| } | |||
| void l2_vm_init(struct l2_vm *vm, l2_word *ops, size_t opcount) { | |||
| if (!stdio_inited) { | |||
| std_output.w.write = l2_io_file_write; | |||
| std_output.f = stdout; | |||
| std_error.w.write = l2_io_file_write; | |||
| std_error.f = stderr; | |||
| stdio_inited = 1; | |||
| } | |||
| vm->std_output = &std_output.w; | |||
| vm->std_error = &std_error.w; | |||
| vm->ops = ops; | |||
| vm->opcount = opcount; | |||
| vm->iptr = 0; | |||