#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
#include <stdint.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "runtime.h"
_Noreturn void
halt(void)
{
exit(1);
}
_Noreturn void
die(const char *s)
{
fprintf(stderr, "Fatal: %s\n", s);
halt();
}
static void *
xmalloc(size_t size)
{
void *x = malloc(size);
if (x == NULL) halt();
return x;
}
_Bool
has_tag(struct value x, uint32_t tag)
{
return (x.bits & TAG_MASK) == tag;
}
_Bool
is_number(struct value x)
{
return (x.bits & 1) == 0;
}
void *
address(struct heap *heap, struct value x)
{
return heap->values + (x.bits >> 4);
}
struct value
reference_value(uint32_t offset, uint32_t tag)
{
return value((offset << 4) | tag);
}
int32_t
value_unbox_int32(struct value x)
{
if (!is_number(x)) halt();
return ((int32_t)x.bits)/2;
}
struct value
labeled_empty_tuple(uint16_t label)
{
struct value x = { .bits = label };
x.bits <<= 16;
x.bits |= (SECOND_TAG_LABELED | TAG_IMMED);
return x;
}
struct value
alloc_tuple(struct heap *heap, struct value *values, uint16_t size)
{
return alloc_module(heap, values, size, UINT16_MAX);
}
struct value
alloc_labeled_value(struct heap *heap, uint16_t label, struct value value)
{
if (value.bits == empty_tuple.bits) {
struct value x = { .bits = label };
x.bits <<= 16;
x.bits |= (SECOND_TAG_LABELED | TAG_IMMED);
return x;
}
struct labeled_value *labeled_value =
(struct labeled_value *)(heap->values + heap->top);
struct value x = reference_value(heap->top, TAG_LABELED);
heap->top += sizeof(struct labeled_value);
labeled_value->label = label;
labeled_value->value = value;
return x;
}
uint16_t
value_label(struct heap *heap, struct value x)
{
if (has_tag(x, TAG_IMMED)) {
uint16_t label = (uint16_t)(x.bits >> 16);
uint32_t second_tag = x.bits & 0xfff0;
if (second_tag != SECOND_TAG_LABELED) halt();
return label;
}
if (!has_tag(x, TAG_LABELED)) halt();
struct labeled_value *value = (struct labeled_value *)address(heap, x);
return value->label;
}
void
open_labeled_value(struct heap *heap, struct value *frame, struct value x,
int n)
{
if (has_tag(x, TAG_IMMED)) {
uint32_t second_tag = x.bits & 0xfff0;
if (second_tag != SECOND_TAG_LABELED) halt();
if (n != 0) halt();
return;
}
if (!has_tag(x, TAG_LABELED)) halt();
struct labeled_value *value = (struct labeled_value *)address(heap, x);
if (n == 1) {
frame[0] = value->value;
return;
}
if (!has_tag(value->value, TAG_MODULE)) halt();
struct module *module = (struct module *)address(heap, value->value);
if (module->size != n) halt();
memmove(frame, &module->entries, n*sizeof(struct value));
}
struct value
remove_label(struct heap *heap, struct value x)
{
if (has_tag(x, TAG_IMMED)) {
uint32_t second_tag = x.bits & 0xfff0;
if (second_tag != SECOND_TAG_LABELED) halt();
return empty_tuple;
}
if (!has_tag(x, TAG_LABELED)) halt();
struct labeled_value *value = (struct labeled_value *)address(heap, x);
return value->value;
}
void
open_tuple(struct heap *heap, struct value *frame, struct value x, int n)
{
if (x.bits == empty_tuple.bits) {
if (n != 0) halt();
return;
}
if (!has_tag(x, TAG_MODULE)) halt();
struct module *tuple = (struct module *)address(heap, x);
if (tuple->index_begin != UINT16_MAX) halt();
if (tuple->size != n) halt();
memmove(frame, &tuple->entries, n*sizeof(struct value));
}
struct value
alloc_closure(struct heap *heap, int8_t num_params,
uint32_t env_size, int32_t code_offset)
{
struct closure *c = (struct closure *)(heap->values + heap->top);
struct value x = reference_value(heap->top, TAG_CLOSURE);
heap->top += sizeof(struct closure) + sizeof(struct value)*env_size;
c->num_params = num_params;
c->env_size = env_size;
c->code_offset = code_offset;
return x;
}
void
closure_store(struct heap *heap, struct value x, uint32_t i,
struct value value)
{
if (!has_tag(x, TAG_CLOSURE)) halt();
struct closure *closure = (struct closure *)address(heap, x);
closure->free_values[i] = value;
}
static uint32_t
string_size(struct heap *heap, struct value s)
{
if (!has_tag(s, TAG_STRING)) halt();
struct string *string = (struct string *)address(heap, s);
return string->size;
}
static uint8_t *
string_bytes(struct heap *heap, struct value s)
{
if (!has_tag(s, TAG_STRING)) halt();
struct string *string = (struct string *)address(heap, s);
return &string->bytes[0];
}
static const char *
string_chars(struct heap *heap, struct value s)
{
return (const char *)string_bytes(heap, s);
}
struct value
alloc_string(struct heap *heap, const char *s)
{
size_t text_size = strlen(s);
size_t object_size = sizeof(struct string) + 4*(text_size/4+1);
struct string *string = (struct string *)(heap->values + heap->top);
struct value x = reference_value(heap->top, TAG_STRING);
heap->top += object_size;
string->size = text_size;
memmove((void *)string + sizeof(struct string), s, text_size);
memset((void *)string + sizeof(struct string) + text_size, 0,
object_size - (sizeof(struct string) + text_size));
return x;
}
struct value
number(int32_t n)
{
if (n < (INT32_MIN/2) || (INT32_MAX/2) < n) halt();
return value(2*n);
}
struct value
prim_die(struct heap *heap, struct value s)
{
if (!has_tag(s, TAG_STRING)) halt();
struct string *string = (struct string *)address(heap, s);
die((const char *)string->bytes);
return empty_tuple;
}
struct value
prim_print_line(struct heap *heap, struct value s)
{
if (!has_tag(s, TAG_STRING)) halt();
struct string *string = (struct string *)address(heap, s);
printf("%s\n", string->bytes);
return empty_tuple;
}
struct value
prim_file_create(struct heap *heap, struct value name)
{
FILE *stream = fopen(string_chars(heap, name), "w");
if (stream == NULL) halt();
struct file *heap_file = (struct file *)(heap->values + heap->top);
struct value x = reference_value(heap->top, TAG_FILE);
heap->top += sizeof(struct file);
heap_file->stream = stream;
return x;
}
struct value
prim_file_open(struct heap *heap, struct value name)
{
FILE *stream = fopen(string_chars(heap, name), "r");
if (stream == NULL) halt();
struct file *heap_file = (struct file *)(heap->values + heap->top);
struct value x = reference_value(heap->top, TAG_FILE);
heap->top += sizeof(struct file);
heap_file->stream = stream;
return x;
}
struct value
prim_file_close(struct heap *heap, struct value file)
{
if (!has_tag(file, TAG_FILE)) halt();
struct file *heap_file = (struct file *)address(heap, file);
fclose(heap_file->stream);
return empty_tuple;
}
struct value
prim_file_read_all(struct heap *heap, struct value file)
{
if (!has_tag(file, TAG_FILE)) halt();
struct file *heap_file = (struct file *)address(heap, file);
struct stat statbuf;
if (-1 == fstat(fileno(heap_file->stream), &statbuf)) halt();
unsigned size = (unsigned)statbuf.st_size;
char *source = xmalloc(size+1);
if (1 != fread(source, size, 1, heap_file->stream)) halt();
source[size] = 0;
struct value s = alloc_string(heap, source);
free(source);
return s;
}
struct value
prim_file_write(struct heap *heap, struct value file, struct value str)
{
if (!has_tag(file, TAG_FILE)) halt();
if (!has_tag(str, TAG_STRING)) halt();
struct file *heap_file = (struct file *)address(heap, file);
size_t ret = fwrite(string_chars(heap, str), string_size(heap, str), 1,
heap_file->stream);
if (ret != 1) halt();
return empty_tuple;
}
struct value
prim_show_integer(struct heap *heap, struct value n)
{
if (!is_number(n)) halt();
char text[16];
size_t size = sizeof(text);
if (snprintf(text, size, "%d", (int)value_unbox_int32(n)) >= size) halt();
return alloc_string(heap, text);
}
struct value
prim_bits(struct heap *heap, struct value x)
{
return number(x.bits);
}
struct value
prim_multiply(struct heap *heap, struct value a, struct value b)
{
// TODO Overflow and stuff?
return number(value_unbox_int32(a) * value_unbox_int32(b));
}
struct value
prim_add(struct heap *heap, struct value a, struct value b)
{
// TODO Overflow and stuff?
struct value n = number(value_unbox_int32(a) + value_unbox_int32(b));
return n;
}
struct value
prim_negate(struct heap *heap, struct value n)
{
// TODO Overflow and stuff?
return number(-value_unbox_int32(n));
}
struct value
prim_equal(struct heap *heap, struct value a, struct value b)
{
if (!is_number(a) || !is_number(b)) halt();
if (a.bits == b.bits)
return true_value;
return false_value;
}
struct value
prim_less(struct heap *heap, struct value a, struct value b)
{
if ((int32_t)a.bits < (int32_t)b.bits)
return true_value;
return false_value;
}
struct value
prim_less_or_equal(struct heap *heap, struct value a, struct value b)
{
if ((int32_t)a.bits <= (int32_t)b.bits)
return true_value;
return false_value;
}
struct value
prim_greater(struct heap *heap, struct value a, struct value b)
{
if ((int32_t)a.bits > (int32_t)b.bits)
return true_value;
return false_value;
}
struct value
prim_greater_or_equal(struct heap *heap, struct value a, struct value b)
{
if ((int32_t)a.bits >= (int32_t)b.bits)
return true_value;
return false_value;
}
struct value
prim_string_length(struct heap *heap, struct value s)
{
// TODO Which is it, "size" or "length"?
return number(string_size(heap, s));
}
struct value
prim_string_fetch(struct heap *heap, struct value s, struct value i)
{
const char *chars = string_chars(heap, s);
int32_t ii = value_unbox_int32(i);
if (ii < 0 || string_size(heap, s) <= ii) halt();
return number(chars[ii]);
}
struct value
prim_string_compare(struct heap *heap, struct value s1, struct value s2)
{
// I tried using strcmp first but it was not working out for me so I wrote
// my own. :P
//
// return number(strcmp(string_bytes(heap, s1), string_bytes(heap, s2)));
//
// TODO What is the deal with strcmp? I was seeing
// "STDIO" < "dummy" and "dummy" < "STDIO"
const char *p1 = string_chars(heap, s1);
const char *p2 = string_chars(heap, s2);
for (;;) {
char a = *p1;
char b = *p2;
if (a == 0) {
if (b == 0) return number(0);
return number(-1);
}
if (b == 0) return number(1);
if (a < b) return number(-1);
if (a > b) return number(1);
p1++;
p2++;
}
return number(0); // Unreachable.
}
struct value
prim_string_equal(struct heap *heap, struct value s1, struct value s2)
{
if (strcmp(string_chars(heap, s1), string_chars(heap, s2)))
return false_value;
return true_value;
}
struct value
prim_string_append(struct heap *heap, struct value s1, struct value s2)
{
uint32_t s1_size = string_size(heap, s1);
uint32_t size = s1_size + string_size(heap, s2);
char *fresh = xmalloc(size+1);
memmove(fresh, string_bytes(heap, s1), s1_size);
memmove(fresh+s1_size, string_bytes(heap, s2), size+1-s1_size);
struct value s = alloc_string(heap, fresh);
free(fresh);
return s;
}
struct value
prim_string_clip(struct heap *heap, struct value s, struct value begin,
struct value end)
{
if (!has_tag(s, TAG_STRING)) halt();
if (!is_number(begin)) halt();
if (!is_number(end)) halt();
int32_t b = value_unbox_int32(begin);
int32_t e = value_unbox_int32(end);
if (b < 0 || e < 0 || e < b) halt();
uint32_t s_size = string_size(heap, s);
if (b >= s_size || e > s_size) halt();
size_t text_size = (size_t)(e-b);
size_t object_size = sizeof(struct string) + 4*(text_size/4+1);
struct string *string = (struct string *)(heap->values + heap->top);
struct value x = reference_value(heap->top, TAG_STRING);
heap->top += object_size;
string->size = text_size;
const char *s_chars = string_chars(heap, s);
memmove((void *)string + sizeof(struct string), s_chars+b, text_size);
memset((void *)string + sizeof(struct string) + text_size, 0,
object_size - (sizeof(struct string) + text_size));
return x;
}
struct value
prim_ref_new(struct heap *heap, struct value x)
{
struct ref *ref = (struct ref *)(heap->values + heap->top);
struct value r = reference_value(heap->top, TAG_REF);
heap->top += sizeof(struct ref);
ref->x = x;
return r;
}
struct value
prim_ref_store(struct heap *heap, struct value r, struct value x)
{
if (!has_tag(r, TAG_REF)) halt();
struct ref *ref = (struct ref *)address(heap, r);
ref->x = x;
return x;
}
struct value
prim_ref_fetch(struct heap *heap, struct value r)
{
if (!has_tag(r, TAG_REF)) halt();
struct ref *ref = (struct ref *)address(heap, r);
return ref->x;
}
struct value
alloc_module(struct heap *heap, struct value *values, uint16_t size, uint16_t index_begin)
{
struct module *module = (struct module *)(heap->values + heap->top);
struct value x = reference_value(heap->top, TAG_MODULE);
heap->top += sizeof(struct module) + size*sizeof(struct value);
module->size = size;
module->index_begin = index_begin;
for (int i = 0; i < size; i++)
module->entries[i] = values[i];
return x;
}
void
module_store(struct heap *heap, struct value x, uint16_t i, struct value value)
{
if (!has_tag(x, TAG_MODULE)) halt();
struct module *module = (struct module *)address(heap, x);
module->entries[i] = value;
}
struct value
tuple_fetch(struct heap *heap, struct value x, uint16_t i)
{
if (!has_tag(x, TAG_MODULE)) halt();
struct module *module = (struct module *)address(heap, x);
if (module->index_begin != UINT16_MAX) halt();
return module->entries[i];
}
struct value
module_fetch(struct heap *heap, struct value x, uint16_t label)
{
if (!has_tag(x, TAG_MODULE)) halt();
struct module *module = (struct module *)address(heap, x);
if (module->index_begin == UINT16_MAX) halt();
uint16_t begin = module->index_begin;
for (uint16_t i = begin; module_indices[i] != UINT16_MAX; i++) {
if (module_indices[i] == label)
return module->entries[i-begin];
}
halt();
}
int
main(int argc, const char *argv[])
{
run_machine(argc, argv);
return 0;
}