Explorar o código

fix & feat: 修复内存问题以及添加新功能

修复解析代码时时size遗漏自增造成的内存bug
添加新命令行参数和菜单操作
SongZihuan %!s(int64=3) %!d(string=hai) anos
pai
achega
f581c21f9f
Modificáronse 8 ficheiros con 211 adicións e 33 borrados
  1. 13 6
      README.md
  2. 1 0
      include/brainfuck.h
  3. 117 8
      main.c
  4. 1 1
      src/CMakeLists.txt
  5. 2 1
      src/_brainfuck.h
  6. 60 17
      src/brainfuck.c
  7. 9 0
      src/mem.c
  8. 8 0
      src/mem.h

+ 13 - 6
README.md

@@ -6,9 +6,13 @@ Brainfuck,是一种极小化的程序语言,它是由Urban Müller在1993年
 ```
 使用: BrainFuck [参数] 文件..  
 参数:  
--e --eval              从一个字符串中运行代码  
--v --version           显示版本信息  
--h --help              显示帮助信息  
+ -e --eval             从一个字符串中运行代码  
+ -v --version          显示版本信息 
+ -h --help             显示帮助信息 
+ -s --step             以步进模式运行
+ -i --information      以详细信息模式运行
+ -n --noninit          每次运行命令行参数指定的文件时不复位读取头
+ -p --print            默认每次解析完指令后总显示出来
 ```
 ## 支持的语法
 ```
@@ -40,12 +44,15 @@ Brainfuck,是一种极小化的程序语言,它是由Urban Müller在1993年
 v       显示版本号
 h       显示帮助
 m       显示菜单
+d       开启/关闭 每次解析完指令后总显示出来
 w       显示详细信息
 p       显示纸带信息
 r       显示读取头信息
-s       开启/关闭步进模式 (默认关闭)
-i       开启/关闭详细信息模式 (默认关闭)
-c       清空屏幕 (调用系统clear指令)
+n       重置环境
+f       运行指定文件
+s       开启/关闭 步进模式 (默认关闭)
+i       开启/关闭 详细信息模式 (默认关闭)
+c       清空屏幕 (调用系统clear命令)
 q       退出
 e       退出菜单
 ```

+ 1 - 0
include/brainfuck.h

@@ -34,6 +34,7 @@ char *bf_getVersion(void);  // 获取版本
 // bf_env指针通常可以使用void *来代替
 bf_env *bf_setEnv(void);  // 设置一个运行环境(返回一个指针)
 void bf_freeEnv(bf_env *env);  // 释放一个环境
+void bf_resetEnv(bf_env *env);
 void bf_initEnv(bf_env *env);  // 复位读取头到初始位置
 
 // bf_code指针通常可以使用void *来代替

+ 117 - 8
main.c

@@ -6,15 +6,22 @@
 
 #include "brainfuck.h"
 #define COMMAND_LINE_STR_SIZE (20)
+#define FILE_NAME_SIZE (20)
 
 static bf_env *global_env;
 static struct option long_options[] = {
         {"version", no_argument, 0, 'v'},
         {"help", no_argument, 0, 'h'},
         {"eval", required_argument, 0, 'e'},
+        {"print", no_argument, 0, 'p'},
+        {"step", no_argument, 0, 's'},
+        {"information", no_argument, 0, 'i'},
+        {"noninit", no_argument, 0, 'n'},
         {0, 0, 0, 0}
 };
 char *program_name;
+bool print_code = false;
+bool initEnv = true;  // 每次执行命令行参数指定的文件时都重置读取头位置
 
 void printUsage(void);
 void printVersion(void);
@@ -36,7 +43,7 @@ int main(int argc, char **argv){
 
     while (1) {
         option_index = 0;
-        int c = getopt_long (argc, argv, "vhe:", long_options, &option_index);
+        int c = getopt_long (argc, argv, "vhsie:", long_options, &option_index);
         if (c == -1)
             break;
 
@@ -51,12 +58,32 @@ int main(int argc, char **argv){
                 goto end;
             case 'e': {
                 bf_code code;
-                code = bf_parserBrainFuck_Str((char *) optarg);
+                code = bf_parserBrainFuck_Str((char *) optarg);  // 不会回到头部重新执行
+
+                if (print_code) {
+                    printf("code: ");
+                    bf_printBrainFuck(code);
+                    printf("\n");
+                }
+
                 bf_runBrainFuck(code, global_env);
                 bf_printError("eval error", global_env);
                 bf_freeBrainFuck(code);
+                printf("\n");
                 break;
             }
+            case 's':
+                bf_setEnvMode(global_env, step, 1);
+                break;
+            case 'i':
+                bf_setEnvMode(global_env, information, 1);
+                break;
+            case 'p':
+                print_code = true;
+                break;
+            case 'n':
+                initEnv = false;
+                break;
             default:
             case '?':
                 printUsage();
@@ -89,9 +116,13 @@ int main(int argc, char **argv){
 void printUsage(void) {
     printf("Usage: %s[options] file..\n", program_name);
     printf("Options: \n");
-    printf(" -e --eval\t\tRun code in a string\n");
-    printf(" -v --version\t\tshow version\n");
-    printf(" -h --help\t\tshow help\n\n");
+    printf(" -e --eval\t\t\tRun code in a string\n");
+    printf(" -v --version\t\t\tShow version\n");
+    printf(" -h --help\t\t\tShow help\n");
+    printf(" -s --step\t\t\tRun with step mode\n");
+    printf(" -i --information\t\tRun with information mode\n");
+    printf(" -n --noninit\t\t\tDon't init env after run file\n\n");
+    printf(" -p --print\t\t\tPrint code after parser\n\n");
 
     printf("CommandLine Options: \n");
     printf(" v show version\n");
@@ -124,11 +155,14 @@ void printMenu(void) {
     printf("+ v + show version               +\n");
     printf("+ h + show help                  +\n");
     printf("+ m + show menu                  +\n");
+    printf("+ d + print code after parser    +\n");
     printf("+ w + print env information      +\n");
     printf("+ p + print paper tape           +\n");
     printf("+ r + print read head            +\n");
     printf("+ s + run in step model          +\n");
     printf("+ i + run with information       +\n");
+    printf("+ n + set new env                +\n");
+    printf("+ f + run file                   +\n");
     printf("+ c + clear screen(May not work) +\n");
     printf("+ q + quit                       +\n");
     printf("+ e + exit the menu              +\n");
@@ -146,12 +180,21 @@ int runFile(int argc, char **argv, bf_env *env) {
         }
 
         bf_code code;
+        if (initEnv)
+            bf_initEnv(env);  // 每次执行文件都回到头部重新执行
         code = bf_parserBrainFuck_File(file);
+
+        if (print_code) {
+            printf("code: ");
+            bf_printBrainFuck(code);
+            printf("\n");
+        }
+
         bf_runBrainFuck(code, env);
         bf_printError("run error", env);
-        bf_initEnv(env);
         bf_freeBrainFuck(code);
         fclose(file);
+        printf("\n");
 
         optind++;
     }
@@ -173,7 +216,7 @@ int clInformation(int ch, bf_env *env) {
         case 'h':
             printUsage();
             break;
-        case 'm':
+        case 'm': {
             printMenu();
             printf("Enter the operation:");
 
@@ -184,7 +227,16 @@ int clInformation(int ch, bf_env *env) {
             ch = getc(stdin);
             return_ = clInformation(ch, env);
             goto NOT_CLEAR;  // 不用清除stdin
+        }
         case 'q':  // 退出菜单
+            return_ = 1;
+            break;
+        case 'd':
+            print_code = !print_code;
+            if (print_code)
+                printf("print code mode on\n");
+            else
+                printf("print code mode off\n");
             break;
         case 'w':
             bf_printEnvWithMode(env);
@@ -209,6 +261,54 @@ int clInformation(int ch, bf_env *env) {
             else
                 printf("information mode off\n");
             break;
+        case 'n':
+            bf_resetEnv(global_env);
+            break;
+        case 'f': {
+            int size = FILE_NAME_SIZE;
+            char *str = calloc(size + 1, sizeof(char ));
+
+            int del_ch;
+            while ((del_ch = getc(stdin) != '\n') && del_ch != EOF)
+                continue;
+
+            printf("Enter file address:");
+            fgets(str, FILE_NAME_SIZE + 1, stdin);
+            while (!strchr(str, '\n') && !feof(stdin) && !ferror(stdin)) {
+                char *new = calloc(size + FILE_NAME_SIZE + 1, sizeof(char ));
+                strcpy(new, str);
+                fgets(new + size, FILE_NAME_SIZE + 1, stdin);
+                free(str);
+                str = new;
+                size += FILE_NAME_SIZE;
+            }
+
+            *strchr(str, '\n') = '\0';  // 去掉回车
+            FILE *file = fopen(str, "r");
+
+            if (file == NULL) {
+                printf("file = '%s'\n", str);
+                perror("read file error222");
+                return -1;
+            }
+
+            bf_code code;
+            code = bf_parserBrainFuck_File(file);
+
+            if (print_code) {
+                printf("code: ");
+                bf_printBrainFuck(code);
+                printf("\n");
+            }
+
+            return_ = bf_runBrainFuck(code, env);
+            bf_printError("run error", env);
+            bf_freeBrainFuck(code);
+            fclose(file);
+            free(str);
+            printf("\n");
+            goto NOT_CLEAR;
+        }
         case 'c':
             system("clear");  // 清空
             break;
@@ -248,7 +348,8 @@ int runCommandLine(bf_env *env) {
             continue;
         } else
             ungetc(ch, stdin);
-        runCommandLine_(env);
+        if (runCommandLine_(env) == -2)
+            return 0;  // 退出
     }
 }
 
@@ -269,10 +370,18 @@ int runCommandLine_(bf_env *env) {
 
     bf_code code;
     code = bf_parserBrainFuck_Str(str);
+
+    if (print_code) {
+        printf("code: ");
+        bf_printBrainFuck(code);
+        printf("\n");
+    }
+
     status = bf_runBrainFuck(code, env);
     bf_printError("command line error", env);
     bf_freeBrainFuck(code);
     free(str);
+    printf("\n");
     return status;
 }
 

+ 1 - 1
src/CMakeLists.txt

@@ -2,7 +2,7 @@ CMAKE_MINIMUM_REQUIRED(VERSION 3.19)
 SET(CMAKE_C_STANDARD 11)
 PROJECT(BrainFuck_Lib C)
 
-ADD_LIBRARY(BrainFuck_Lib SHARED brainfuck.c)
+ADD_LIBRARY(BrainFuck_Lib SHARED brainfuck.c mem.c)
 SET_TARGET_PROPERTIES(BrainFuck_Lib PROPERTIES PUBLIC_HEADER ${CMAKE_SOURCE_DIR}/include/brainfuck.h)
 INSTALL(TARGETS BrainFuck_Lib
         PUBLIC_HEADER DESTINATION include)

+ 2 - 1
src/_brainfuck.h

@@ -5,6 +5,7 @@
 #define TABLE_SIZE (20)
 #define FILE_READ_SIZE (20)
 
+typedef signed char bf_data;
 typedef char *bf_byte;
 typedef struct bf_item bf_item;
 typedef struct bf_pitem bf_pitem;
@@ -30,7 +31,7 @@ struct bf_code {
 };
 
 struct bf_item {
-    int data[TABLE_SIZE];
+    bf_data data[TABLE_SIZE];
     struct bf_item *prev;
     struct bf_item *next;
 };

+ 60 - 17
src/brainfuck.c

@@ -6,6 +6,7 @@
 
 #include "brainfuck.h"
 #include "_brainfuck.h"
+#include "mem.h"
 
 #ifndef BF_VERSION
 #define BF_VERSION "Magic Version"
@@ -20,28 +21,34 @@ static void freeItem(bf_item *item);
 static bf_item *makeItem(void);
 static void addItem(bf_item *global);
 
+
 char *bf_getVersionInfo(void) {
     return BF_VERSION "\n" BF_VERSION_INFO "\n";
 }
 
+
 char *bf_getVersion(void) {
     return BF_VERSION;
 }
 
+
 static bf_code make_bf_code(void) {
     bf_code code = calloc(1, sizeof(struct bf_code));
     return code;
 }
 
+
 static void free_bf_code(bf_code code) {
     free(code->byte);
     free(code);
 }
 
+
 void bf_freeBrainFuck(bf_code code) {
     free_bf_code(code);
 }
 
+
 bf_code bf_parserBrainFuck_Str(const char *str) {
     char ch;
     int size = FILE_READ_SIZE;
@@ -64,11 +71,12 @@ bf_code bf_parserBrainFuck_Str(const char *str) {
             continue;  // 其他内容也被忽略
         }
 
-        if (index > size) {
+        if (index >= size) {
             bf_byte byte = calloc(size + FILE_READ_SIZE + 1, sizeof(bf_byte));
             strcpy(byte, code->byte);
             free(code->byte);
             code->byte = byte;
+            size += FILE_READ_SIZE;
         }
 
         code->byte[index] = (char)ch;
@@ -78,6 +86,7 @@ bf_code bf_parserBrainFuck_Str(const char *str) {
     return code;
 }
 
+
 bf_code bf_parserBrainFuck_File(FILE *file) {
     int ch;
     int size = FILE_READ_SIZE;
@@ -103,6 +112,7 @@ bf_code bf_parserBrainFuck_File(FILE *file) {
             strcpy(byte, code->byte);
             free(code->byte);
             code->byte = byte;
+            size += FILE_READ_SIZE;
         }
 
         code->byte[index] = (char)ch;
@@ -112,15 +122,18 @@ bf_code bf_parserBrainFuck_File(FILE *file) {
     return code;
 }
 
+
 void bf_printBrainFuck(bf_code code) {
     printf("%s", code->byte);
 }
 
+
 static bf_item *makeItem(void) {
     bf_item *item = calloc(1, sizeof(bf_item));
     return item;
 }
 
+
 static void freeItem(bf_item *item) {
     bf_item *bak;
     while (item != NULL) {
@@ -130,6 +143,7 @@ static void freeItem(bf_item *item) {
     }
 }
 
+
 bf_env *bf_setEnv(void) {
     bf_env *env = calloc(1, sizeof(bf_env));
     env->item = makeItem();
@@ -142,17 +156,33 @@ bf_env *bf_setEnv(void) {
     return env;
 }
 
+
 void bf_freeEnv(bf_env *env) {
     freeItem(env->item);
     free(env);
 }
 
+
+void bf_resetEnv(bf_env *env) {
+    freeItem(env->item);
+
+    env->item = makeItem();
+    env->pitem.item = env->item;
+    env->pitem.index = 0;
+    env->pitem.base = env->item;
+    env->error_info = NULL;
+    env->step_mode = false;
+    env->information_mode = false;
+}
+
+
 void bf_initEnv(bf_env *env) {
     env->pitem.item = env->pitem.base;
     env->pitem.index = 0;
     env->error_info = NULL;
 }
 
+
 static void addItem(bf_item *global) {
     assert(global != NULL);
     while (global->next != NULL)
@@ -161,18 +191,20 @@ static void addItem(bf_item *global) {
     global->next->prev = global;
 }
 
+
 int bf_runBrainFuck(bf_code code, bf_env *env) {
     bf_byte byte = code->byte;
     int status = runBrainFuck(&byte, env);
 
     if (status != 0)
-        return 1;  // 返回1表示错误
+        return status;  // 返回1表示错误
     else {
         env->error_info = NULL;
         return 0;
     }
 }
 
+
 static int runBrainFuck(bf_byte *byte, bf_env *env) {
     while (**byte != '\0') {
         int status = runBrainFuck_(byte, env);
@@ -182,6 +214,7 @@ static int runBrainFuck(bf_byte *byte, bf_env *env) {
     return 0;
 }
 
+
 static int runBrainFuck_(bf_byte *byte, bf_env *env) {
     bf_pitem *pitem = &(env->pitem);
     char *b = *byte;
@@ -196,7 +229,7 @@ static int runBrainFuck_(bf_byte *byte, bf_env *env) {
             int times = 0;
             if (isalnum(*(*byte + 1))) {
                 bf_byte new;
-                num = strtol((*byte + 1), &new, 10);
+                num = (int)strtol((*byte + 1), &new, 10);
                 *byte = new - 1;
                 times = num / 20;
                 num = num % 20;
@@ -215,6 +248,8 @@ static int runBrainFuck_(bf_byte *byte, bf_env *env) {
                     addItem(pitem->item);
                 pitem->item = pitem->item->next;
             }
+
+            assert(pitem->index >= 0);
             break;
         }
         case '<': {
@@ -222,7 +257,7 @@ static int runBrainFuck_(bf_byte *byte, bf_env *env) {
             int times = 0;
             if (isalnum(*(*byte + 1))) {
                 bf_byte new;
-                num = strtol((*byte + 1), &new, 10);
+                num = (int)strtol((*byte + 1), &new, 10);
                 *byte = new - 1;
                 times = num / 20;
                 num = num % 20;
@@ -239,7 +274,7 @@ static int runBrainFuck_(bf_byte *byte, bf_env *env) {
             }
 
             pitem->index -= num;
-            if (pitem->index < -1) {
+            if (pitem->index < 0) {
                 pitem->index = (TABLE_SIZE + pitem->index);  // 如 pitem->index = -1, 即代表获取最后一个元素, 下标为 TABLE_SIZE - 1
                 pitem->item = pitem->item->prev;
                 if (pitem->item == NULL) {
@@ -249,13 +284,15 @@ static int runBrainFuck_(bf_byte *byte, bf_env *env) {
                     env->item = pitem->item;
                 }
             }
+
+            assert(pitem->index >= 0);
             break;
         }
         case '+': {
             int num = 1;
             if (isalnum(*(*byte + 1))) {
                 bf_byte new;
-                num = strtol((*byte + 1), &new, 10);
+                num = (int)strtol((*byte + 1), &new, 10);
                 *byte = new - 1;
             }
             pitem->item->data[pitem->index] += num;
@@ -265,7 +302,7 @@ static int runBrainFuck_(bf_byte *byte, bf_env *env) {
             int num = 1;
             if (isalnum(*(*byte + 1))) {
                 bf_byte new;
-                num = strtol((*byte + 1), &new, 10);
+                num = (int)strtol((*byte + 1), &new, 10);
                 *byte = new - 1;
             }
             pitem->item->data[pitem->index] -= num;
@@ -296,9 +333,9 @@ static int runBrainFuck_(bf_byte *byte, bf_env *env) {
                     continue;
             }
 
-            pitem->item->data[pitem->index] = strtol(num, &end, 10);
+            pitem->item->data[pitem->index] = (int)strtol(num, &end, 10);
             if (*end != '\0') {
-                env->error_info = "':' instruction encountered an illegal number";
+                env->error_info = "';' instruction encountered an illegal number";
                 return -1;
             }
 
@@ -312,7 +349,7 @@ static int runBrainFuck_(bf_byte *byte, bf_env *env) {
             break;
         case '[':
             while (1) {
-                if (pitem->item->data[pitem->index] == 0) {  // 跳过代码直到遇到对应的]
+                 if (pitem->item->data[pitem->index] == 0) {  // 跳过代码直到遇到对应的]
                     int count = 1;
                     while (count > 0) {
                         (*byte)++;  // 读取下一个指令
@@ -362,10 +399,8 @@ static int runBrainFuck_(bf_byte *byte, bf_env *env) {
                 printf("continue...\n");
                 break;
             } else if (ch == 'm') {  // 进入菜单
-                if (env->step_func(env) != 0) {  // 调用函数 (stdin在该函数中清空)
-                    env->error_info = "step mode func error";
-                    return -1;
-                }
+                if (env->step_func(env) != 0)  // 调用函数 (stdin在该函数中清空)
+                    return -2;  // 直接退出执行
             }
         }
 
@@ -377,11 +412,13 @@ static int runBrainFuck_(bf_byte *byte, bf_env *env) {
     return 0;
 }
 
+
 void bf_printError(char *info, bf_env *env) {
     if (env->error_info != NULL)
-        printf("%s : %s\n", info, env->error_info);
+        fprintf(stderr, "%s : %s\n", info, env->error_info);
 }
 
+
 void bf_printPaperTape(bf_env *env) {
     unsigned count = 0;
     bf_item *item = env->item;
@@ -407,12 +444,14 @@ void bf_printPaperTape(bf_env *env) {
     }
 }
 
+
 void bf_printHead(bf_env *env) {
     printf("head data: %d", env->pitem.item->data[env->pitem.index]);
     if (isprint(env->pitem.item->data[env->pitem.index]))
         printf("'%c'", env->pitem.item->data[env->pitem.index]);
 }
 
+
 void bf_printEnv(bf_env *env) {
     bf_printPaperTape(env);
     printf("\n");
@@ -420,29 +459,32 @@ void bf_printEnv(bf_env *env) {
     printf("\n");
 }
 
+
 void bf_printEnvWithMode(bf_env *env) {
     bf_printPaperTape(env);
     printf("\n");
     bf_printHead(env);
     printf("\n");
 
-    if (env->step_mode)  // 不修改实际值
+    if (env->step_mode)
         printf("step mode on\n");
     else
         printf("step mode off\n");
 
-    if (env->information_mode)  // 不修改实际值
+    if (env->information_mode)
         printf("information mode on\n");
     else
         printf("information mode off\n");
 }
 
+
 bf_STEP_FUNC bf_setEnvStepFunc(bf_env *env, bf_STEP_FUNC step_func) {
     bf_STEP_FUNC bak = env->step_func;
     env->step_func = step_func;
     return bak;
 }
 
+
 #define bf_setEnvModeFunc(name) bool bf_setEnv##name##Mode(bf_env *env, int mode) { \
 switch (mode) { \
 case 1:env->name##_mode = true;break; \
@@ -453,5 +495,6 @@ default:break; \
 return env->name##_mode; \
 }
 
+
 bf_setEnvModeFunc(step)
 bf_setEnvModeFunc(information)

+ 9 - 0
src/mem.c

@@ -0,0 +1,9 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+void *bf_safe_calloc(size_t n, size_t size) {
+    void *dest = calloc(n, size);
+    if (dest == NULL)
+        exit(1);
+    return dest;
+}

+ 8 - 0
src/mem.h

@@ -0,0 +1,8 @@
+#ifndef BRAINFUCK_MEM_H
+#define BRAINFUCK_MEM_H
+
+#define calloc(n, size) (bf_safe_calloc((n), (size)))
+#define free(p) ((p != NULL) ? free(p) : NULL)
+void *bf_safe_calloc(size_t n, size_t size);
+
+#endif //BRAINFUCK_MEM_H