andy0130tw
5/28/2017 - 7:15 PM

minimal syntax checker and simulator for IOTM (brainfuck-like language)

minimal syntax checker and simulator for IOTM (brainfuck-like language)

#ifndef _H_BRAINFUCK
#define _H_BRAINFUCK

#define MEMORY_SIZE 4096
#define CODE_LEN_LIMIT 65536

#endif  // _H_BRAINFUCK
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include "brainfuck.h"

#define eprintf(format, ...) \
        fprintf(stderr, "%s: " format, __func__,## __VA_ARGS__)

// expand our syntax sugar (similar to run-length encoding) to traditional
// brainfuck program; return NULL if the code exceeds the limit
//
// in order to know how long it will be, the expansion will be done in two
// passes: decide the length the first pass, allocate a space to be filled,
// and fill it in the second pass.
char* bfexpand(char* code) {
    char* p = code;
    int count = 0;
    // first pass
    int actlen = 0;
    while (*p) {
        if (isdigit(*p)) {
            if (count == 0 && *p == '0') {
                eprintf("expected positive integer, got 0\n");
                return NULL;
            }
            count = count * 10 + *p - '0';
        } else {
            if (count != 0) {
                actlen += count;
                count = 0;
            } else actlen++;
        }
        p++;
    }

    // reject
    if (count != 0) {
        eprintf("stray number after code\n");
        return NULL;
    }
    printf("expanded len = %d\n", actlen);

    if (actlen > CODE_LEN_LIMIT) {
        eprintf("code too long\n");
        return NULL;
    }

    // second pass
    p = code;
    char* ret = (char*) malloc(actlen + 1);
    int filled = 0;
    while (*p) {
        if (isdigit(*p)) {
            count = count * 10 + *p - '0';
        } else {
            if (count != 0) {
                while (--count) {
                    ret[filled++] = *p;
                }
            }
            ret[filled++] = *p;
        }
        p++;
    }
    ret[actlen] = '\0';
    return ret;
}

// run on some brainfuck code
// return 0 if success,
//        1 if runtime error,
//        2 if tick limit exceeded
int brainfuck(char* code, const char* input, char** output,
              size_t* output_sz, size_t* tick_lim) {
    char memory[MEMORY_SIZE] = {};
    int memptr = 0;
    int stack[32];  // XXX: what is the stack size?
    int stsize = 0;
    int jmptable[CODE_LEN_LIMIT];  // XXX: can be improved by using map

    size_t tick_cnt = 0;
    int do_tick_check = tick_lim && *tick_lim > 0;

    // syntax checking; build jump table
    int pos = 0;
    while (code[pos]) {
        if (code[pos] == '[') {
            stack[stsize++] = pos;
        } else if (code[pos] == ']') {
            if (stsize == 0) {
                // stack underflow owo
                eprintf("unexpected ] at position %d\n", pos + 1);
                return 1;
            }
            // fill it to jump table; bi-directional
            stsize--;
            jmptable[pos] = stack[stsize];
            jmptable[stack[stsize]] = pos;
        } else if (strchr(".,<>+-", code[pos]) == NULL) {
            // the character is not in allowing list of characters
            eprintf("unallowed character '%c' at position %d\n",
                code[pos], pos + 1);
            return 1;
        }
        pos++;
    }

    if (stsize > 0) {
        printf("expected ] at end\n");
        return 1;
    }

    // syntax checked; start execution
    int len = pos;
    size_t output_len = 0;
    pos = 0;
    while (pos != len) {
        char c = code[pos];
        switch (c) {
        case '<':  // move left
            if (memptr == 0) {
                eprintf("attempting to move left at the left end of memory"
                        " at position %d\n", pos + 1);
            }
            memptr--;
            break;
        case '>':  // move right
            if (memptr == MEMORY_SIZE - 1) {
                eprintf("attempting to move right at the right end of memory"
                        " at position %d\n", pos + 1);
            }
            memptr++;
            break;
        case '+':  // increment
            memory[memptr]++; tick_cnt++; break;
        case '-':  // decrement
            memory[memptr]--; tick_cnt++; break;
        case ',':  // read from input; no-op if exhausted
            if (*input) {
                memory[memptr] = *input;
                input++;
            }
            tick_cnt++; break;
        case '.':  // write to output
            if (memory[memptr] == 0) {
                eprintf("attempting to print zero character to output"
                        " at position %d\n", pos + 1);
            }
            (*output)[output_len++] = memory[memptr];
            // if output is longer than expected, the space is relocated
            if (output_len == *output_sz) {
                *output_sz *= 2;
                // printf("relocating memory to expand output to"
                //        " size %zd\n", *output_sz);
                *output = (char*) realloc(*output, *output_sz);
            }
            tick_cnt++; break;
        case '[':  // loop construct left
            if (memory[memptr] == 0) {
                pos = jmptable[pos];
            }
            tick_cnt++; break;
        case ']':  // loop construct right
            if (memory[memptr] != 0) {
                pos = jmptable[pos];
            }
            tick_cnt++; break;
        }

        // check tick limit
        if (do_tick_check && tick_cnt >= *tick_lim) {
            eprintf("tick limit exceeded\n");
            return 2;
        }
        pos++;
    }

    // zero-terminate the output string
    (*output)[output_len] = '\0';
    if (tick_lim) {
        *tick_lim = tick_cnt;
    }
    return 0;
}

int main() {
    char* line = NULL;
    size_t n = 0;
    ssize_t len = 0;
    while ((len = getline(&line, &n, stdin)) >= 0) {
        if (len > 0 && line[len - 1] == '\n')
            line[len - 1] = '\0';
        printf("code: '%s'\n", line);
        char* expanded = bfexpand(line);
        if (!expanded) {
            printf("syntax error\n");
        } else {
            printf("expanded: '%s'\n", expanded);
        }

        if (!expanded) continue;

        const char* input = "100 ";
        size_t output_sz = 4096;
        size_t tick = 0;
        char* output_ptr = (char*) malloc(output_sz);
        int result = brainfuck(expanded, input,
                               &output_ptr, &output_sz, &tick);

        if (result == 0) {
            printf("success\n");
            printf("output buffer len = %zd\n", output_sz);
            printf("output: [%s]\n", output_ptr);
            printf("tick used: [%zd]\n", tick);
        } else if (result == 1) {
            printf("runtime error\n");
        } else if (result == 2) {
            printf("tick limit (%zd) exceeded\n", tick);
        }

        free(output_ptr);
        free(expanded);
    }
    free(line);
}
#include <stdio.h>
#include <string.h>

int main() {
    char c;
    while ((c = getchar()) > 0) {
        if (strchr("<>[],.+-", c) != NULL)
            putchar(c);
    }
}
#include <stdio.h>

int main() {
    char c, prev = '\0';
    int count = 1;
    while (c = getchar(), c > 0) {
        if (c == prev) {
            count++;
        } else if (prev) {
            if (count == 1) {
                putchar(prev);
            } else {
                printf("%d%c", count, prev);
            }
            count = 1;
        }
        if (c == '\n') {
            puts("");
            prev = '\0';
            continue;
        }
        prev = c;
    }
}
ECHO  = /bin/echo -e
CC    = gcc
CSRCS = $(wildcard *.c)

%: %.c
	@$(ECHO) "compiling $< -> $@"
	@$(CC) -std=c99 -O2 -o $@ $<

all: bfcomp bfconcat brainfuck

clean:
	rm -f bfcomp bfconcat brainfuck 
>++++++++[<++++++++>-]<++++++++++++++++.[-]>++++++++++[<++++++++++>-]<++++++++++++++.[-]>++++++++++[<++++++++++>-]<+++++.[-]>++++++++++[<++++++++++>-]<+++++++++.[-]>++++++++++[<++++++++++>-]<+.[-]>++++++++++[<++++++++++>-]<+++++++++++++++.[-]>+++++[<+++++>-]<+++++++.[-]>++++++++++[<++++++++++>-]<+++++++++++++++++.[-]>++++++++++[<++++++++++>-]<++++++++++++.[-]>+++++[<+++++>-]<+++++++.[-]>++++++++++[<++++++++++>-]<++++++++++++++++.[-]>++++++++++[<++++++++++>-]<+++++++++++.[-]>+++++++[<+++++++>-]<+++++++++.[-]>+++++[<+++++>-]<+++++++.[-]+[->,--------------------------------[<+>---------------->[>+>+<<-]>>[<<+>>-]<>>>+++++++++[<<<[>+>+<<-]>>[<<+>>-]<[<<+>>-]>>-]<<<[-]<<[>+<-]]<]>>[<<+>>-]<<>+<-[>+[>+>+<<-]>>[<<+>>-]<>+<-->>>>>>>>+<<<<<<<<[>+<-<[>>>+>+<<<<-]>>>>[<<<<+>>>>-]<<<>[>>+>+<<<-]>>>[<<<+>>>-]<<<<>>>[>+>+<<-]>>[<<+>>-]<<<[>>>>>+<<<[>+>+<<-]>>[<<+>>-]<[>>[-]<<-]>>[<<<<[>+>+<<-]>>[<<+>>-]<>>>-]<<<-<<-]+>>[<<[-]>>-]<<>[-]<[>>>>>>[-]<<<<<<-]<<>>[-]>[-]<<<]>>>>>>>>[-<<<<<<<[-]<<[>>+>+<<<-]>>>[<<<+>>>-]<<<>>[>+<-]>[[>+>+<<-]>>[<<+>>-]<>+++++++++<[>>>+<<[>+>[-]<<-]>[<+>-]>[<<++++++++++>>-]<<-<-]+++++++++>[<->-]<[>+<-]<[>+<-]<[>+<-]>>>[<<<+>>>-]<>+++++++++<[>>>+<<[>+>[-]<<-]>[<+>-]>[<<++++++++++>>>+<-]<<-<-]>>>>[<<<<+>>>>-]<<<<>[-]<<+>]<[[>+<-]+++++++[<+++++++>-]<-><.[-]>>[<<+>>-]<<-]>++++[<++++++++>-]<.[-]>>>>>>>]<<<<<<<<>[-]<[-]<<-]
++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.