correct a test
[brainfuck.git] / bf.c
diff --git a/bf.c b/bf.c
index e687f94b1724f0a2721cb5c277df04a64241a893..552c5b0bc30dd992a67495446bf7b3d51d8c33f7 100644 (file)
--- a/bf.c
+++ b/bf.c
@@ -2,7 +2,7 @@
 /* cflags: */
 /* linker: debug.o */
 
-#include <assert.h>
+#include <errno.h>
 #include <getopt.h>
 #include <malloc.h>
 #include <stdio.h>
@@ -14,6 +14,7 @@
 /* macros */
 
 #define BUFSIZE 256
+#define MAXJUMP 4
 #define MEMSIZE 10
 
 /* type definition */
 char *progname = NULL;
 int p = 0;
 char mem[MEMSIZE] = {0};
+int j = 0;
+int jump[MAXJUMP] = {0};
 
 /* help function */
 
 int usage (int ret)
 {
     FILE *fd = ret ? stderr : stdout;
-    fprintf (fd, "usage: %s [-i file] [-h] [-m k&r|ansi|c99] [-o file] [-v]\n", progname);
+    fprintf (fd, "usage: %s [-i file] [-h] [-m memory] [-o file] [-v]\n", progname);
     fprintf (fd, " -i : input file\n");
     fprintf (fd, " -h : help message\n");
-    fprintf (fd, " -m : indent mode\n");
+    fprintf (fd, " -m : memory [0..0]\n");
     fprintf (fd, " -o : output file\n");
     fprintf (fd, " -v : verbose level (%d)\n", verbose);
 
     return ret;
 }
 
+/* main process */
+
+int process (char *buffer, int nb, FILE *out) {
+    int i;
+
+    for (i = 0; i < nb; i++) {
+        if (buffer[i] == 0) {
+            break;
+        }
+
+        VERBOSE (DEBUG, fprintf (stderr, "%s: p=%d, buffer='%s', memory=[ ", progname, p, buffer + i); int _i; for (_i = 0; _i < MEMSIZE; _i++) fprintf (stderr," %d", mem[_i]); fprintf (stderr," ]\n"));
+
+        switch (buffer[i]) {
+            case '>': /* increase pointer */
+                p++;
+                break;
+            case '<': /* decrease pointer */
+                p--;
+                break;
+            case '+': /* increase pointer value */
+                if ((p >= 0) && (p < MEMSIZE)) {
+                    mem[p]++;
+                } else {
+                    VERBOSE (ERROR, fprintf (stderr, "%s: invalid address (%d)\n", progname, p));
+                    return 1;
+                }
+                break;
+            case '-': /* decrease pointer value */
+                if ((p >= 0) && (p < MEMSIZE)) {
+                    mem[p]--;
+                } else {
+                    VERBOSE (ERROR, fprintf (stderr, "%s: invalid address (%d)\n", progname, p));
+                    return 1;
+                }
+                break;
+            case '.': /* output pointer value */
+                if ((p >= 0) && (p < MEMSIZE)) {
+                    fprintf (out, "%c", mem[p]);
+                } else {
+                    VERBOSE (ERROR, fprintf (stderr, "%s: invalid address (%d)\n", progname, p));
+                    return 1;
+                }
+                break;
+            case ',': /* read a byte and store it in memory */
+                if ((p >= 0) && (p < MEMSIZE)) {
+                    int c = getchar ();
+                    mem[p] = (c > 0) ? c : 0;
+                } else {
+                    VERBOSE (ERROR, fprintf (stderr, "%s: invalid address (%d)\n", progname, p));
+                    return 1;
+                }
+                break;
+            case '[': /* jump to right bracket if pointer is set to 0 */
+                if ((p < 0) || (p >= MEMSIZE)) {
+                    VERBOSE (ERROR, fprintf (stderr, "%s: invalid address (%d)\n", progname, p));
+                    return 1;
+                }
+                if (mem[p] == 0) {
+                    int bracket = 1;
+                    while ((++i < nb) && (bracket > 0)) {
+                        bracket += (buffer[i] == '[') ? +1 : (buffer[i] == ']') ? -1 :0;
+                    }
+                    if (bracket) {
+                        VERBOSE (ERROR, fprintf (stderr, "%s: brace not closed\n", progname));
+                        return 1;
+                    }
+                    i--;
+                } else {
+                    if (j >= MAXJUMP) {
+                        VERBOSE (ERROR, fprintf (stderr, "%s: too many jump\n", progname));
+                        return 1;
+                    }
+                    jump[j++] = i;
+                }
+                break;
+            case ']': /* jump to left bracket if pointer is different to 0 */
+                if (j <= 0) {
+                    VERBOSE (ERROR, fprintf (stderr, "%s: can't jump back\n", progname));
+                    return 1;
+                }
+                i = jump[--j] - 1;
+                break;
+            case ' ':
+            case '\t':
+            case '\n':
+            case '\r':
+                break;
+            default:
+                VERBOSE (WARNING, fprintf (stderr, "%s: can't understand '%c'\n", progname, buffer[i]));
+        }
+        
+        //VERBOSE (DEBUG, int _i; fprintf (stderr, "%s: p: %d mem:", progname, p); for (_i = 0; _i < MEMSIZE; _i++) fprintf (stderr, " %d", mem[_i]); fprintf (stderr, "\n"));
+
+    }
+
+    return 0;
+}
+
 /* main function */
 
 int main (int argc, char *argv[]) 
@@ -46,6 +147,7 @@ int main (int argc, char *argv[])
     char *input = NULL;
     char *output = NULL;
     char *buffer = NULL;
+    int i;
     int n = 0;
     int size = 0;
     FILE *fid = NULL;
@@ -60,11 +162,34 @@ int main (int argc, char *argv[])
     }
 
     int c;
-    while ((c = getopt(argc, argv, "i:o:hv:")) != EOF) {
+    while ((c = getopt(argc, argv, "e:i:m:o:hv:")) != EOF) {
         switch (c) {
+        case 'e':
+            if (buffer) {
+                free (buffer);
+            }
+            buffer = strdup (optarg);
+            if (buffer == NULL) {
+                VERBOSE (ERROR, fprintf (stderr, "%s: can't allocate memory\n", progname));
+                return 1;
+            }
+            size = strlen (buffer) + 1;
+            break;
         case 'i':
             input = optarg;
             break;
+        case 'm':
+            for (i = 0; i < MEMSIZE; i++) {
+                if (optarg) {
+                    mem[i] = strtol (optarg, &optarg, 10);
+                    VERBOSE (DEBUG, fprintf (stderr, "%s: mem[%d] = %d\n", progname, i, mem[i]));
+                }
+            }
+            if (*optarg != 0) {
+                VERBOSE (WARNING, fprintf (stderr, "%s: too many memory values or incorrect value\n", progname));
+                return 1;
+            }
+            break;
         case 'o':
             output = optarg;
             break;
@@ -88,35 +213,42 @@ int main (int argc, char *argv[])
             VERBOSE (ERROR, fprintf (stderr, "%s: can't open file '%s' for reading\n", progname, input));
             return 1;
         }
-    } else {
+    } else if (buffer == NULL) {
         fid = stdin;
     }
 
     /* read input file */
-    while (!feof (fid)) {
-        buffer = (char *) realloc (buffer, size + BUFSIZE);
-        if (buffer == NULL) {
-            VERBOSE (ERROR, fprintf (stderr, "%s: can't allocate memory\n", progname));
-            return 1;
+    if (fid) {
+        while (!feof (fid)) {
+            buffer = (char *) realloc (buffer, size + BUFSIZE);
+            if (buffer == NULL) {
+                VERBOSE (ERROR, fprintf (stderr, "%s: can't allocate memory\n", progname));
+                return 1;
+            }
+            memset (buffer + size, 0, BUFSIZE);
+            n = fread (buffer + size, 1, BUFSIZE, fid);
+            if (errno != 0) {
+                VERBOSE (ERROR, fprintf (stderr, "%s: can't read data from file '%s'\n", progname, input));
+                return 1;
+            }
+            size += BUFSIZE;
         }
-        memset (buffer + size, 0, BUFSIZE);
-        n = fread (buffer + size, 1, BUFSIZE, fid);
-        if (errno != 0) {
-            VERBOSE (ERROR, fprintf (stderr, "%s: can't read data from file '%s'\n", progname, input));
-            return 1;
+
+        /* close input file */
+        if (fid != stdin) {
+            fclose (fid);
         }
-        size += BUFSIZE;
+        VERBOSE (DEBUG, fprintf (stderr, "%s: read %d bytes\n", progname, size + n - BUFSIZE));
+    } else {
+        VERBOSE (DEBUG, fprintf (stderr, "%s: prog %d bytes\n", progname, size -1));
     }
 
-    /* close input file */
-    fclose (fid);
-    VERBOSE (DEBUG, fprintf (stderr, "%s: read %d bytes\n", progname, size + n - BUFSIZE));
-
     /* check output file */
     if (output) {
         fid = fopen (output, "w");
         if (fid == NULL) {
             VERBOSE (ERROR, fprintf (stderr, "%s: can't open file '%s' for writing\n", progname, output));
+            if (buffer) free (buffer);
             return 1;
         }
     } else {
@@ -127,96 +259,55 @@ int main (int argc, char *argv[])
     int rc = process (buffer, size, fid);
 
     /* close output file */
-    fclose (fid);
-
-    return rc;
-}
-
-/* main process */
-int process (char *buffer, int nb, FILE *out) {
-    int i;
-
-    for (i = 0; (i < nb) && (buffer[i] != 0); i++) {
-
-        VERBOSE (DEBUG, fprintf (stderr, "%s: read '%c' (%u)\n", progname, buffer[i], buffer[i]));
-        switch (buffer[i]) {
-            case '>': /* increase pointer */
-                p++;
-                break;
-            case '<': /* decrease pointer */
-                p--;
-                break;
-            case '+': /* increase pointer value */
-                if ((p >= 0) && (p < MEMSIZE)) {
-                    mem[p]++;
-                } else {
-                    VERBOSE (ERROR, fprintf (stderr, "%s: invalid address (%d)\n", progname, p));
-                    return 1;
-                }
-                break;
-            case '-': /* decrease pointer value */
-                if ((p >= 0) && (p < MEMSIZE)) {
-                    mem[p]--;
-                } else {
-                    VERBOSE (ERROR, fprintf (stderr, "%s: invalid address (%d)\n", progname, p));
-                    return 1;
-                }
-                break;
-            case '.': /* output pointer value */
-                if ((p >= 0) && (p < MEMSIZE)) {
-                    fprintf (out, "%c", mem[p]);
-                } else {
-                    VERBOSE (ERROR, fprintf (stderr, "%s: invalid address (%d)\n", progname, p));
-                    return 1;
-                }
-                break;
-            case ',': /* read a byte and store it in memory */
-                if ((p >= 0) && (p < MEMSIZE)) {
-                    mem[p] = buffer[++i];
-                } else {
-                    VERBOSE (ERROR, fprintf (stderr, "%s: invalid address (%d)\n", progname, p));
-                    return 1;
-                }
-                break;
-            case '[': /* jump to right bracket if pointer is set to 0 */
-                if (mem[p] == 0) {
-                    int bracket = 1;
-                    while ((++i < nb) && (bracket > 0)) {
-                        bracket += (buffer[i] == '[') ? +1 : (buffer[i] == ']') ? -1 :0;
-                    }
-                    if (bracket) {
-                        VERBOSE (ERROR, fprintf (stderr, "%s: brace not closed\n", progname));
-                        return 1;
-                    }
-                } else {
-                    return process (buffer + i + 1, nb - i - 1, out);
-                }
-                break;
-            case ']': /* jump to left bracket if pointer is different to 0 */
-                if (mem[p] != 0) {
-                    i = -1;
-                }
-                break;
-            case '\n':
-            case '\r':
-                break;
-            default:
-                VERBOSE (WARNING, fprintf (stderr, "%s: can't understand '%c'\n", progname, buffer[i]));
-        }
-        
-        //VERBOSE (DEBUG, int _i; fprintf (stderr, "%s: p: %d mem:", progname, p); for (_i = 0; _i < MEMSIZE; _i++) fprintf (stderr, " %d", mem[_i]); fprintf (stderr, "\n"));
+    if (fid != stdout) {
+        fclose (fid);
+    }
 
+    /* free buffer */
+    if (buffer) {
+        free (buffer);
     }
 
-    return 0;
+    VERBOSE (INFO, fprintf (stdout, "\nmemory:"); int _i; for (_i = 0; _i < MEMSIZE; _i++) fprintf (stdout," %d", mem[_i]); fprintf (stdout,"\n"));
+
+    return rc;
 }
 
 // test: bf.exe -h
 // test: bf.exe -h | awk '/usage:/ { rc=1 } END { exit (1-rc) }'
+
 // test: bf.exe -_ 2> /dev/null | awk 'END { if (NR == 0) { exit(0) } else exit (1) }'
 // test: bf.exe -_ 2>&1 | awk '/usage:/ { rc=1 } END { exit (1-rc) }'
-// test: echo '++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.' | bf.exe
-// test: echo '++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>++++++++++++++.>+++++++++++++++++.<<++.>+++++++++++++.>--.<<.>+++.+.--.>----.++++++.+.<++.>----.++.<<.>>+.-------.<<.>>++.<.>+++++.<<.>-.+.<.>---.>---.<-.++++++++.>----.<---.>+++++++.<---.++++++++.' | bf.exe
-// test: echo ',3>,2>>++++++++++++++++[-<+++<---<--->>>]<<[<[>>+>+<<<-]>>>[<<<+>>>-]<<-]>.' | bf.exe
+// test: bf.exe error 2>&1 | awk '/usage:/ { rc=1 } END { exit (1-rc) }'
+
+// test: bf.exe -i error.b 2>&1 | grep "can't open" | grep -q "reading"
+// test: echo ">>." | bf.exe -o error/error.b 2>&1 | grep "can't open" | grep -q "writing"
+// test: echo "error" | bf.exe -v1 2>&1 | grep -q "can't understand"
+// test: echo "<+" | bf.exe -v1 2>&1 | grep -q "invalid address"
+// test: echo "<-" | bf.exe -v1 2>&1 | grep -q "invalid address"
+// test: echo "<." | bf.exe -v1 2>&1 | grep -q "invalid address"
+// test: echo "<," | bf.exe -v1 2>&1 | grep -q "invalid address"
+// test: echo "<[" | bf.exe -v1 2>&1 | grep -q "invalid address"
+// test: echo "[" | bf.exe -v1 2>&1 | grep -q "brace not closed"
+// test: echo "+[[[[[]]]]]" | bf.exe -v1 2>&1 | grep -q "too many jump"
+// test: bf.exe -m "0 1 2 3 4 5 6 7 8 9 10" -e '.' -v1 2>&1 | grep -q "too many memory values"
+// test: bf.exe -m "0 error" -e '.' -v1 2>&1 | grep -q "incorrect value"
+// test: echo "]" | bf.exe -v1 2>&1 | grep -q "can't jump back"
+
+// test: echo '+++>++>>-<--' | bf.exe -v2 | grep -q "memory: 3 2 -2 -1 0"
+// test: bf.exe -e '+++' -e '+++>++>>-<--' -v2 | grep -q "memory: 3 2 -2 -1 0"
+// test: bf.exe -e '+++>++>>-<--' -v2 | grep -q "memory: 3 2 -2 -1 0"
+// test: bf.exe -m "51 50 49" -e '.>.>.' |grep -q "321"
+// test: bf.exe -m "51 50 49" -e '.>.>.' -o test.log && grep -q "321" test.log; rc=$?; rm test.log; test $rc -eq 0
+// test: echo '+++>++>>-<--' > test.b; bf.exe -i test.b | grep -q "memory: 3 2 -2 -1 0"; rc=$?; rm test.b; test $rc -eq 0
+
+// test: echo '++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.' | bf.exe -v1 | grep -q "Hello World!"
+// test: echo '++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>++++++++++++++.>+++++++++++++++++.<<++.>+++++++++++++.>--.<<.>+++.+.--.>----.++++++.+.<++.>----.++.<<.>>+.-------.<<.>>++.<.>+++++.<<.>-.+.<.>---.>---.<-.++++++++.>----.<---.>+++++++.<---.++++++++.' | bf.exe -v1 | grep -q "Tu as decouvert un peu de brainfuck"
+
+// test: echo -e "123\0" | bf.exe -e ',[>,]' -v2 | grep -q "memory: 49 50 51 0"
+// test: echo -e "4+3\0" | bf.exe -e ',>++++++[<-------->-],,[<+>-]<.' -v1 | grep -q 7
+// test: echo -e "1+7\0" | bf.exe -e ',>++++++[<-------->-],,[<+>-]<.' -v1 | grep -q 8
+// test: echo -e "3*2\0" | bf.exe -e ',>,,>++++++++[<------<------>>-]<<[>[>+>+<<-]>>[<<+>>-]<<<-]>>>++++++[<++++++++>-]<.' -v1 | grep -q 6
+// test: echo -e "1*7\0" | bf.exe -e ',>,,>++++++++[<------<------>>-]<<[>[>+>+<<-]>>[<<+>>-]<<<-]>>>++++++[<++++++++>-]<.' -v1 | grep -q 7
 
 /* vim: set ts=4 sw=4 et: */