git @ Cat's Eye Technologies stringie / master src / stringie.c
master

Tree @master (Download .tar.gz)

stringie.c @masterraw · history · blame

/*
 * stringie.c -- a brain-freezingly pedantic implementation of Underload in C
 * (with all the limitations that that implies)
 * Chris Pressey, September 2010
 * August 2017: bug fix to avoid memory overrun in (). Thanks to @stasoid for finding and suggesting fix.
 * Summer 2018: own implementation of strdup to avoid warnings; ability to read from file; AUTOFLUSH; 1.0.
 * This work is in the public domain.
 */

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

struct stack {
    char *string;
    struct stack *next;
} *root;

void run(char *);

char *strdupe(const char *s)
{
    char *t = malloc(strlen(s) + 1);
    strcpy(t, s);
    return t;
}

char *pop(void)
{
    char *r;
    struct stack *old;
    r = root->string;
    old = root;
    root = root->next;
    free(old);
    return r;
}

void push(char *string)
{
    struct stack *s;
    s = malloc(sizeof(struct stack));
    s->next = root;
    s->string = string;
    root = s;
}

void dup(void)
{
    char *e, *f;
    e = pop();
    f = strdupe(e);
    push(e);
    push(f);
}

void swap(void)
{
    char *e, *f;
    e = pop();
    f = pop();
    push(e);
    push(f);
}

void eval(void)
{
    char *e;
    e = pop();
    run(e);
}

void concat(void)
{
    char *e, *f, *g;
    e = pop();
    f = pop();
    g = malloc(strlen(e) + strlen(f) + 1);
    strcpy(g, f);
    strcat(g, e);
    push(g);
    free(f);
    free(e);
}

void enclose(void)
{
    char *e, *g;
    e = pop();
    g = malloc(strlen(e) + 3);
    sprintf(g, "(%s)", e);
    push(g);
    free(e);
}

void output(void)
{
    char *e;
    e = pop();
    printf("%s", e);
#ifdef AUTOFLUSH
    fflush(stdout);
#endif
    free(e);
}

void dumpstack(void)
{
    struct stack *s;
    
    fprintf(stderr, "STACK: ");
    for (s = root; s != NULL; s = s->next)
    {
        fprintf(stderr, "(%s)", s->string);
    }
    fprintf(stderr, "!\n");
}

void run(char *program)
{
    int i = 0;
    int last_pos = strlen(program) - 1;
    for (i = 0; program[i] != '\0'; i++) {
        switch (program[i]) {
            case ':': dup(); break;
            case '!': pop(); break;
            case '^':
              {
                /* tail recursion */
                if (i == last_pos) {
                    i = -1;
                    free(program);
                    program = pop();
                    continue;
                } else {
                    eval();
                }
              }
              break;
            case '~': swap(); break;
            case '*': concat(); break;
            case 'S': output(); break;
            case 'a': enclose(); break;
            case '(':
              {
                int level = 0;
                int j = 0;
                int size = 256;
                char *t = malloc(size);

                i++;
                level++;
                while (level > 0) {
                    if (program[i] == '(')
                        level++;
                    else if (program[i] == ')')
                        level--;
                    if (level > 0) {
                        t[j] = program[i];
                        j++;
                        if (j >= size) {
                            size *= 2;
                            t = realloc(t, size);
                        }
                    }
                    i++;
                }
                i--;
                t[j] = '\0';
                push(t);
              }
              break;
        }
        /*dumpstack();*/
    }
    free(program);
}

char *readfile(FILE *f)
{
    char chunk[256];
    char *buffer;
    int size = 256;
    int len = 0;
    int n;

    buffer = malloc(size);

    n = fread(chunk, 1, 256, f);
    while (n > 0) {
        if (len + n > size) {
            size *= 2;
            buffer = realloc(buffer, size);
        }
        memcpy(buffer + len, chunk, n);
        len += n;
        n = fread(chunk, 256, 1, f);
    }

    /* NUL-terminate the buffer; but first, make sure the NUL can fit! */
    if (len + 1 > size) {
        size *= 2;
        buffer = realloc(buffer, size);
    }
    buffer[len + 1] = '\0';

    return buffer;
}

int main(int argc, char **argv)
{
    char *program;
    FILE *f;

    if (argc >= 1) {
        if (!strcmp(argv[1], "from") && argc >= 2) {
            if ((f = fopen(argv[2], "r")) == NULL) {
                exit(1);
            }
            program = readfile(f);
            fclose(f);
        } else {
            program = strdupe(argv[1]);
        }
    }
    root = NULL;
    run(program);
    exit(0);
}