view src/fueue.c @ 9285:8320c9c4620f

<oerjan> learn Umlaut is German for "hum aloud", an important feature of the German language. It is indicated by putting two dots over the vowel of the syllable.
author HackBot
date Sat, 15 Oct 2016 00:04:47 +0000
parents bd08a41fcbe2
children
line wrap: on
line source

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TRUE 1
#define FALSE 0

/*
 * Fueue Interpreter in language C
 *   the esoteric programming language Fueue was designed in 2012 by Taneb.
 *
 * The following program was written by Stephan Kunne on august 29 2012.
 * It is public domain; you are free to use it, copy it, distribute it,
 * or do whatever you'd like with it.
 * Credit and feedback are always appreciated ; you can email me at
 * firstname dot name at gmail dot com with all your questions or remarks.
 *
 * Examples:
 *
 *    ./fueuec '72 101 108 108 111 44 32 119 111 114 108 100 33 10 H'
 *    Hello, world!
 *
 *    ./fueuec --print '):[):]'
 *    ):[):]
 *    :[):])
 *    )[):][):]
 *    [):]):
 *    ):[):]
 *    (...and so on)
 */

enum Typet
{
    NUM, FUN, BLOCK
};

typedef struct Queue Queue;
struct Queue
{
    int size;
    struct Token *top;
    struct Token *bottom;
};

union value
{
    int num;
    char fun;
    Queue block;
};

struct Token
{
    enum Typet what;
    union value val;
    struct Token *next;
};


int is_empty(const Queue *q);  // bool
void initQueue(Queue *q);  // make it an empty queue
void push(struct Token *x, Queue *q);
struct Token* copyToken(const struct Token *x);
Queue copyQueue(const Queue *q);
void initToken(struct Token *x);
void pushnum(int num, Queue* q);  // create NUM token and pushes it
void pushfun(char f, Queue* q);  // create FUN token and pushes it
void pushblock(Queue newq, Queue* q);  // create BLOCK token and pushes it
void deletetop(Queue* q);  // suppose q not empty
void deleteQueue(Queue *q);
void sendback(Queue* q);  // suppose q not empty, pop then push
struct Token* pop(Queue* q);  // suppose q not empty
void append(Queue *q, const Queue *r);
int matchwhat(const Queue* q, const char s[]);  // bool peek at first 2 "nn" "n" "." ".." "n." "b." "b"
void processFueue(Queue* q, int printmode);  // the recursive function that does everything
Queue strtoqueue(const char s[], int *k);  // transforms a string program into a queue program
void print_queue(const Queue *q);  // prints a queue program
void error_empty(const char s[]);  // raised by functions that "suppose q not empty" when q is empty


void processFueue(Queue* q, int printmode)
{
    int time = 0;  // catching input
    int i = 0;  // to be used for input
    int a = 0, b = 0; // to be used for some FUN
    struct Token* p = NULL; // even more FUN
    Queue newq;  // having FUN with BLOCKs (specifically '(')
    initQueue(&newq);  // this is done at every iteration where newq is used, though

    while (TRUE)  // stops thanks to a return; when 'H' is met
    {
    
        if (printmode)
        {
            print_queue(q);
            printf("\n");
        }

        if (time == q->size)  // if time == q->size then input char and push ascii/unicode value
        {
            fflush(stdout);
            i = getchar();
            pushnum(i, q);
            time = 0;
        }

        if (q->top->what == NUM) // q not empty because of the time != q->size requirement
        {
            // print char with ascii/unicode value q->top->val.num
            printf("%c", (char) q->top->val.num);
            fflush(stdout);
            deletetop(q);
            time = 0;
        }
        else if (q->top->what == FUN)
        {
            char op = q->top->val.fun;
            deletetop(q);
            switch (op)
            {
                case '+':
                case '*':
                case '/':
                    if (matchwhat(q, "nn"))
                    {
                        a = q->top->val.num;
                        deletetop(q);
                        b = q->top->val.num;
                        deletetop(q);
                        if (op == '+')
                            { pushnum(a+b, q); }
                        else if (op == '*')
                            { pushnum(a*b, q); }
                        else // op == '/'
                            { pushnum(a/b, q); }
                        time = 0;
                    }
                    else
                    {
                        pushfun(op, q);
                        time++;
                    }
                    break;
                case '-':
                case '%':
                    if (matchwhat(q, "n"))
                    {
                        a = q->top->val.num;
                        deletetop(q);
                        pushnum( ((op == '-')?(-a):(!a)) , q);
                        time = 0;
                    }
                    else
                    {
                        pushfun(op, q);
                        time++;
                    }
                    break;
                case ':':
                    if (!is_empty(q))
                    {
                        push(copyToken(q->top), q);  // push copy
                        sendback(q);  // push original
                        time = 0;
                    }
                    else
                    {
                        pushfun(op, q);
                        time++;
                    }
                    break;
                case '~':
                    if (matchwhat(q, "..")) // if q has at least two items
                    {
                        p = pop(q);
                        sendback(q);
                        push(p, q);
                        time = 0;
                    }
                    else
                    {
                        pushfun(op, q);
                        time++;
                    }
                    break;
                case '!':
                    if (is_empty(q))
                    {
                        pushfun(op, q);
                        time++;
                    }
                    else
                    {
                        deletetop(q);
                        time = 0;
                    }
                    break;
                case '$':
                    if (matchwhat(q, "n."))
                    {
                        a = q->top->val.num;
                        deletetop(q);
                        for (; a > 0; a--)
                        {
                            push(copyToken(q->top), q);
                        }
                        
                        deletetop(q);
                        
                        time = 0;
                    }
                    else
                    {
                        pushfun(op, q);
                        time++;
                    }
                    break;
                case '(':
                    if (is_empty(q))
                    {
                        pushfun(op, q);
                        time++;
                    }
                    else
                    {
                        initQueue(&newq);
                        push(pop(q), &newq);
                        pushblock(newq, q);
                        time = 0;
                        // newq is the queue inside the block
                    }
                    break;
                case '<':
                    if (matchwhat(q, "b."))
                    {
                        sendback(q);
                        push(pop(q), &(q->bottom->val.block));
                        time = 0;
                    }
                    else
                    {
                        pushfun(op, q);
                        time++;
                    }
                    break;
                case ')':
                    if (matchwhat(q, "b"))
                    {
                        append(q, &(q->top->val.block));
                        initQueue(&(q->top->val.block));  // mandatory since
                        deletetop(q);  // deletetop does destroy the block it contains
                        time = 0;
                    }
                    else
                    {
                        pushfun(op, q);
                        time++;
                    }
                    break;
                case 'H':
                    // don't forget to delete the remaining of the queue here, will you?
                    return;
                default:
                    // raise an error
                    break;
            }
        }
        else // if q->top->what == BLOCK
        {
            sendback(q);
            time++;
        }
    }
}



int main(int argc, char *argv[])
{
    Queue q;
    initQueue(&q);  // q is empty now
    char s[10000] = "72 101 108 108 111 44 32 119 111 114 108 100 33 10 H";
    int printmode = FALSE;  // a debug mode that will print the fueue program at each step
    int k = 0;

    switch (argc)
    {
        case 1:
            break;
        case 2:
            if (strcmp(argv[1], "--print") == 0)
            {
                printmode = TRUE;
            }
            else
            {
                strncpy(s, argv[1], 10000);
            }
            break;
        case 3:
            strncpy(s, argv[2], 10000);
            printmode = (strcmp(argv[1], "--print") == 0);
            break;
        default:
            fprintf(stderr, "Error: %s received too many arguments. The Hello world program\n", argv[0]);
            break;
    }

    q = strtoqueue(s, &k);

    processFueue(&q, printmode);
    return 0;
}


Queue strtoqueue(const char s[], int *k)  // takes a fueue program as a string, and gives a queue
{
    // *k is loop counter
    Queue q;  // the queue to be returned
    initQueue(&q);
    int n = 0;  // decimals (usually n * 10 + 0-9)
    int intmode = FALSE;  // bool "we're reading a number right now"

    if (*k == -1)
    {
        printf("FUEUE: UNMATCHED OPENING SQUARE BRACKET PROBABLY FORGOT A CLOSING SQUARE BRACKET\n");
        return q;
    }

    while (s[*k] != '\0' && s[*k] != ']')
    {
        if (intmode && (s[*k] > '9' || s[*k] < '0'))  // if intmode ends
        {
            pushnum(n, &q);
            n = 0;
            intmode = FALSE;
        }

        switch (s[*k])
        {
            case '+':
            case '-':
            case '*':
            case '/':
            case '%':
            case ':':
            case '~':
            case '!':
            case '$':
            case '(':
            case '<':
            case ')':
            case 'H':
                pushfun(s[*k], &q);
                (*k)++;
                break;
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
                n = n * 10 + (int) (s[*k] - '0');
                intmode = TRUE;
                (*k)++;
                break;
            case '\n':
            case '\t':
            case ' ':  // whitespace
                (*k)++;
                break;
            case '[':
                (*k)++;
                pushblock(strtoqueue(s, k), &q);
                (*k)++;
                break;
            default:
                printf("FUEUE: UNKNOWN %c OP\n", s[*k]);
                (*k)++;
                break;
        }
    }
    if (intmode)
    {
        pushnum(n, &q);
    }

    if (s[*k] == '\0')
        *k = -1;
    return q;
}

void print_queue(const Queue *q)
{
    struct Token *ptmp = q->top;
    while (ptmp != NULL)
    {
        if (ptmp->what == NUM)
        {
            printf(" %d", ptmp->val.num);
        }
        else if (ptmp->what == FUN)
        {
            printf("%c", ptmp->val.fun);
        }
        else if (ptmp->what == BLOCK)
        {
            printf("[");
            print_queue(&(ptmp->val.block));
            printf("]");
        }
        else
        {
            printf("That's impossible...Neither num nor fun nor block...\n");
        }
        ptmp = ptmp->next;
    }
}




int is_empty(const Queue *q) // bool
{
    if (q->top == NULL)
    {
        if (q->bottom == NULL && q->size == 0)
        {
            return TRUE;
        }
        else
        {
            error_empty("is_empty");
        }
    }
    return FALSE;
}

void initQueue(Queue *q)
{
    q->size = 0;
    q->top = NULL;
    q->bottom = NULL;
}

void push(struct Token *x, Queue *q)
{
    if (is_empty(q))
    {
        q->top = x;
    }
    else
    {
        q->bottom->next = x;
    }
    q->bottom = x;
    q->size++;
    x->next = NULL;  // just in case
}

Queue copyQueue(const Queue *q)
{
    Queue c;
    struct Token* ptmp = q->top;
    initQueue(&c);

    while (ptmp != NULL)
    {
        push(copyToken(ptmp), &c);
        ptmp = ptmp->next;
    }

    return c;
}

void initToken(struct Token *x)
{
    x->what = NUM;
    x->val.num = 0;
    x->val.fun = '\0';
    initQueue(&(x->val.block));
    x->next = NULL;
}

struct Token* copyToken(const struct Token *x)
{
    struct Token *c = malloc(sizeof(struct Token));
    c->what = x->what;
    
    switch (x->what)
    {
        case NUM:
        case FUN:
            c->val = x->val;
            break;
        case BLOCK:
            c->val.block = copyQueue(&(x->val.block));
            break;
        default:
            fprintf(stderr, "Error: found a %d in my soup\n", x->what);
            break;
    }

    c->next = NULL;
    return c;
}

void pushnum(int num, Queue* q)
{
    struct Token *t = malloc(sizeof(struct Token));
    initToken(t);
    t->what = NUM;
    t->val.num = num;
    
    push(t, q);
}

void pushfun(char f, Queue* q)
{
    struct Token *t = malloc(sizeof(struct Token));
    initToken(t);
    t->what = FUN;
    t->val.fun = f;
    push(t, q);
}

void pushblock(Queue newq, Queue* q)
{
    struct Token *t = malloc(sizeof(struct Token));
    initToken(t);
    t->what = BLOCK;
    t->val.block = newq;
    push(t, q);
}

void deletetop(Queue* q)  // suppose q not empty
{
    if (is_empty(q))
        error_empty("deletetop");

    struct Token *todelete = NULL;

    if (q->top->what == BLOCK)  // has to free the Queue inside
    {
        deleteQueue(&(q->top->val.block));
    }

    if (q->top->next == NULL)
    {
        free(q->top);
        q->top = NULL;
        q->bottom = NULL;
    }
    else
    {
        todelete = q->top;
        q->top = q->top->next;
        free(todelete);
    }
    q->size--;
}

void deleteQueue(Queue *q)
{
    while (!is_empty(q))
    {
        deletetop(q);
    }
}

void sendback(Queue* q) // suppose q not empty, pop then push
{
    if (is_empty(q))
        error_empty("sendback");

    q->bottom->next = q->top;
    q->top = q->top->next;
    q->bottom = q->bottom->next;
    q->bottom->next = NULL;
}

struct Token* pop(Queue* q) // suppose q not empty
{
    if (is_empty(q))
        error_empty("sendback");

    struct Token* t = q->top;  // note that t->next is equal to q->top->next now
    q->top = q->top->next;
    q->size--;

    return t;    
}

void append(Queue *q, const Queue *r)
{
    if (is_empty(q))
    {
        q->top = r->top;
        q->bottom = r->bottom;
    }
    else if (!is_empty(r))
    {
        q->bottom->next = r->top;
        q->bottom = r->bottom;
    }
    q->size += r->size;
}

int matchwhat(const Queue* q, const char s[])  // bool "nn" "n" "." ".." "n." "b." "b"
{
    int itsok = TRUE;
    if ((s[0] != '\0') && !(is_empty(q)))  // if neither s nor q is empty
    {
        if (s[0] == 'n' && q->top->what != NUM) // if top should be num
            itsok = FALSE;
        if (s[0] == 'b' && q->top->what != BLOCK) // if top should be block
            itsok = FALSE;
        if (s[1] != '\0' && q->top->next == NULL) // if should have second element
        {
            itsok = FALSE;
        }
        else  // so it indeed has a second element, or it doesn't need to have one
        {
            if (s[1] == 'n' && q->top->next->what != NUM) // second should be num
                itsok = FALSE;
            if (s[1] == 'b' && q->top->next->what != BLOCK) // second should be block
                itsok = FALSE;
        }
    }
    else
    {
        if (s[0] != '\0') // if s is not empty but q is
            itsok = FALSE;
    }

    // printf("matchwhat: %s\n", (itsok?"TRUE":"FALSE"));
    return itsok;
}

void error_empty(const char s[])
{
    fprintf(stderr, "Error: queue was empty in %s\n", s);
    exit(EXIT_FAILURE);
}