typestar

value_interp.c in C

A tiny stack machine built on a tagged union, with typed arithmetic.

#include <stdio.h>
#include <string.h>

#define STACK_CAP 16

typedef enum { V_INT, V_DOUBLE, V_ERROR } Kind;

typedef struct {
    Kind kind;
    union {
        long as_int;
        double as_double;
        const char *as_error;
    } data;
} Value;

static Value make_int(long n) {
    Value v = {V_INT, {.as_int = n}};
    return v;
}

static Value make_double(double d) {
    Value v;
    v.kind = V_DOUBLE;
    v.data.as_double = d;
    return v;
}

static Value make_error(const char *message) {
    Value v;
    v.kind = V_ERROR;
    v.data.as_error = message;
    return v;
}

static double numeric(const Value *v, int *ok) {
    switch (v->kind) {
    case V_INT:
        return (double) v->data.as_int;
    case V_DOUBLE:
        return v->data.as_double;
    default:
        *ok = 0;
        return 0.0;
    }
}

static Value add(Value a, Value b) {
    if (a.kind == V_INT && b.kind == V_INT) {
        return make_int(a.data.as_int + b.data.as_int);
    }
    int ok = 1;
    double sum = numeric(&a, &ok) + numeric(&b, &ok);
    return ok ? make_double(sum) : make_error("cannot add these");
}

static void print_value(const Value *v) {
    switch (v->kind) {
    case V_INT:
        printf("int %ld\n", v->data.as_int);
        break;
    case V_DOUBLE:
        printf("double %.3f\n", v->data.as_double);
        break;
    default:
        printf("error: %s\n", v->data.as_error);
        break;
    }
}

int main(void) {
    Value stack[STACK_CAP];
    size_t depth = 0;

    stack[depth++] = make_int(20);
    stack[depth++] = make_int(22);
    stack[depth++] = make_double(0.5);
    stack[depth++] = make_error("bad input");

    while (depth >= 2) {
        Value b = stack[--depth];
        Value a = stack[--depth];
        Value result = add(a, b);
        print_value(&result);
        stack[depth++] = result;
        if (result.kind == V_ERROR) {
            break;
        }
    }

    printf("depth %zu, union size %zu\n", depth, sizeof(Value));
    return 0;
}

How it works

  1. Every value carries its kind, so the switch stays exhaustive.
  2. The stack is a fixed array with a depth counter.
  3. Type errors are reported rather than silently coerced.

Keywords and builtins used here

The run, in numbers

Lines
93
Characters to type
1661
Tokens
509
Three-star pace
110 tpm

At the three-star pace of 110 tokens a minute, this run takes about 278 seconds.

Type this snippet

Step 1 of 1 in Encore, step 13 of 13 in Unions, bitfields & undefined behavior.

← Previous