Преглед на файлове

feat: 添加了新的菜单操作

添加了新的菜单操作
添加了新的API
cmake构建添加了build rpath选项
SongZihuan преди 3 години
родител
ревизия
297dceabf2
променени са 8 файла, в които са добавени 141 реда и са изтрити 26 реда
  1. 15 3
      CMakeLists.txt
  2. 20 2
      README.md
  3. 4 2
      include/brainfuck.h
  4. 5 0
      main.c
  5. 1 1
      src/_brainfuck.h
  6. 65 18
      src/brainfuck.c
  7. 14 0
      test/CMakeLists.txt
  8. 17 0
      test/test_str.c

+ 15 - 3
CMakeLists.txt

@@ -2,8 +2,11 @@ CMAKE_MINIMUM_REQUIRED(VERSION 3.19)
 SET(CMAKE_C_STANDARD 11)
 PROJECT(BrainFuck C)
 
-ADD_DEFINITIONS(-DBF_VERSION="1.1.1 Version")
-ADD_DEFINITIONS(-DBF_VERSION_INFO="Support stable operation. It is the basis for future versions.")
+OPTION(build_with_rpath "Set CMAKE_BUILD_WITH_INSTALL_RPATH on" OFF)
+OPTION(build_test "Build test program" ON)
+
+ADD_DEFINITIONS(-DBF_VERSION="1.2.0 Version")
+ADD_DEFINITIONS(-DBF_VERSION_INFO="Icing on the cake.")
 
 SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin)
 SET(LIBRARY_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}/lib)  # 设置输出路
@@ -11,7 +14,10 @@ SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_PATH})
 SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_PATH})
 
 SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")  # 设定rpath
-SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
+IF (build_with_rpath)
+    SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
+    MESSAGE("SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) success!")
+ENDIF ()
 
 INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include)
 
@@ -19,4 +25,10 @@ ADD_SUBDIRECTORY(${CMAKE_SOURCE_DIR}/src)
 ADD_EXECUTABLE(BrainFuck main.c)
 TARGET_LINK_LIBRARIES(BrainFuck BrainFuck_Lib)
 
+IF (build_test)
+    ENABLE_TESTING()
+    ADD_SUBDIRECTORY(${CMAKE_SOURCE_DIR}/test)
+    MESSAGE("Build test program")
+ENDIF()
+
 INSTALL(TARGETS BrainFuck)

+ 20 - 2
README.md

@@ -37,6 +37,17 @@ Brainfuck,是一种极小化的程序语言,它是由Urban Müller在1993年
 ]       循环结束位置
 ?       读取头回到初始位置
 ```
+### demo程序
+使用BrainFuck实现的quine程序。
+```brainfuck
+->+>+++>>+>++>+>+++>>+>++>>>+>+>+>++>+>>>>+++>+>>++>+>+++>>++>++>>+>>+>++>++>+>>>>+++>+>>>>++>++>>>>+>>++>+>+++>>>++>>++++++>>+>>++>+>>>>+++>>+++++>>+>+++>>>++>>++>>+>>++>+>+++>>>++>>+++++++++++++>>+>>++>+>+++>+>+++>>>++>>++++>>+>>++>+>>>>+++>>+++++>>>>++>>>>+>+>++>>+++>+>>>>+++>+>>>>+++>+>>>>+++>>++>++>+>+++>+>++>++>>>>>>++>+>+++>>>>>+++>>>++>+>+++>+>+>++>>>>>>++>>>+>>>++>+>>>>+++>+>>>+>>++>+>++++++++++++++++++>>>>+>+>>>+>>++>+>+++>>>++>>++++++++>>+>>++>+>>>>+++>>++++++>>>+>++>>+++>+>+>++>+>+++>>>>>+++>>>+>+>>++>+>+++>>>++>>++++++++>>+>>++>+>>>>+++>>++++>>+>+++>>>>>>++>+>+++>>+>++>>>>+>+>++>+>>>>+++>>+++>>>+[[->>+<<]<+]+++++[->+++++++++<]>.[+]>>[<<+++++++[->+++++++++<]>-.------------------->-[-<.<+>>]<[+]<+>>>]<<<[-[-[-[>>+<++++++[->+++++<]]>++++++++++++++<]>+++<]++++++[->+++++++<]>+<<<-[->>>++<<<]>[->>.<<]<<]
+```
+使用BrainFuckPro实现的HelloWorld程序。
+```brainfuck
+++++++++++[>+++++++>++++++++++>+++>+<<<<-]
+>++.>+.+++++++..+++.>++.<<+++++++++++++++.
+>.+++.------.--------.>+.>.
+```
 ## 命令行模式
 执行完成指定文件后,BrainFuckPro进入命令行交互模式。使用者可以通过stdin即时写入指令操控读取头。
 命令行菜单支持的操作(同时为步进模式菜单)
@@ -46,7 +57,8 @@ h       显示帮助
 m       显示菜单
 d       开启/关闭 每次解析完指令后总显示出来
 w       显示详细信息
-p       显示纸带信息
+p       显示完整的纸带信息
+t       显示读取头前后20个格子的信息
 r       显示读取头信息
 n       重置环境
 f       运行指定文件
@@ -71,6 +83,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);  // 重启env,清空一切数据
 void bf_initEnv(bf_env *env);  // 复位读取头到初始位置
 
 // bf_code指针通常可以使用void *来代替
@@ -79,7 +92,7 @@ bf_code bf_parserBrainFuck_File(FILE *file);  // 读取文件并生成代码(返
 bf_code bf_parserBrainFuck_Str(const char *str); // 读取字符串并生成代码(返回一个code)
 void bf_printBrainFuck(bf_code code); // 打印代码
 int bf_runBrainFuck(bf_code code, bf_env *env); // 在指定环境中执行代码 (返回0表示无异常)
-void bf_printError(char *info, bf_env *env);  // 打印错误信息, 若无错误则不执行
+char *bf_printError(char *info, bf_env *env);  // 打印错误信息, 若无错误则不执行, 返回错误信息(若无则返回NULL)
 void bf_printPaperTape(bf_env *env);  // 打印纸带
 void bf_printHead(bf_env *env);  // 打印读取头信息
 void bf_printEnv(bf_env *env);  // 打印env信息
@@ -102,6 +115,11 @@ $ cmake ..
 ```shell
 $ cmake .. -G "MinGW Makefiles"
 ```
+cmake编译支持如下选项
+```
+build_with_rpath    构建项目时生成的可执行文件的rpath设置为为install时的rpath,若只构建不安装程序请设置为OFF。默认为OFF
+build_test          开启test,可使用CTest测试项目
+```
 使用make编译
 ```shell
 $ make

+ 4 - 2
include/brainfuck.h

@@ -1,5 +1,6 @@
 #ifndef BRAINFUCK_BRAINFUCK_H
 #define BRAINFUCK_BRAINFUCK_H
+#include <stdio.h>
 #include <stdbool.h>
 
 #ifndef TYPEDEF_bf_code
@@ -34,7 +35,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_resetEnv(bf_env *env);  // 重启env,清空一切数据
 void bf_initEnv(bf_env *env);  // 复位读取头到初始位置
 
 // bf_code指针通常可以使用void *来代替
@@ -43,8 +44,9 @@ bf_code bf_parserBrainFuck_File(FILE *file);  // 读取文件并生成代码(返
 bf_code bf_parserBrainFuck_Str(const char *str); // 读取字符串并生成代码(返回一个code)
 void bf_printBrainFuck(bf_code code); // 打印代码
 int bf_runBrainFuck(bf_code code, bf_env *env); // 在指定环境中执行代码 (返回0表示无异常)
-void bf_printError(char *info, bf_env *env);  // 打印错误信息, 若无错误则不执行
+char *bf_printError(char *info, bf_env *env);  // 打印错误信息, 若无错误则不执行, 返回错误信息(若无则返回NULL)
 void bf_printPaperTape(bf_env *env);  // 打印纸带
+void bf_printPaperTapeNear(bf_env *env);  // 打印前后20个
 void bf_printHead(bf_env *env);  // 打印读取头信息
 void bf_printEnv(bf_env *env);  // 打印env信息
 void bf_printEnvWithMode(bf_env *env);  // 打印env信息和env的mode信息

+ 5 - 0
main.c

@@ -158,6 +158,7 @@ void printMenu(void) {
     printf("+ d + print code after parser    +\n");
     printf("+ w + print env information      +\n");
     printf("+ p + print paper tape           +\n");
+    printf("+ t + print paper tape little    +\n");
     printf("+ r + print read head            +\n");
     printf("+ s + run in step model          +\n");
     printf("+ i + run with information       +\n");
@@ -245,6 +246,10 @@ int clInformation(int ch, bf_env *env) {
             bf_printPaperTape(env);
             printf("\n");
             break;
+        case 't':
+            bf_printPaperTapeNear(env);
+            printf("\n");
+            break;
         case 'r':
             bf_printHead(env);
             printf("\n");

+ 1 - 1
src/_brainfuck.h

@@ -2,7 +2,7 @@
 #define BRAINFUCK__BRAINFUCK_H
 #include <stdbool.h>
 
-#define TABLE_SIZE (20)
+#define TABLE_SIZE (3000) /* 默认3000个格子 */
 #define FILE_READ_SIZE (20)
 
 typedef signed char bf_data;

+ 65 - 18
src/brainfuck.c

@@ -289,27 +289,27 @@ static int runBrainFuck_(bf_byte *byte, bf_env *env) {
             break;
         }
         case '+': {
-            int num = 1;
+            bf_data num = 1;
             if (isalnum(*(*byte + 1))) {
                 bf_byte new;
-                num = (int)strtol((*byte + 1), &new, 10);
+                num = (bf_data)strtol((*byte + 1), &new, 10);
                 *byte = new - 1;
             }
             pitem->item->data[pitem->index] += num;
             break;
         }
         case '-':{
-            int num = 1;
+            bf_data num = 1;
             if (isalnum(*(*byte + 1))) {
                 bf_byte new;
-                num = (int)strtol((*byte + 1), &new, 10);
+                num = (bf_data)strtol((*byte + 1), &new, 10);
                 *byte = new - 1;
             }
             pitem->item->data[pitem->index] -= num;
             break;
         }
         case ',':  // 输入
-            pitem->item->data[pitem->index] = getc(stdin);
+            pitem->item->data[pitem->index] = (bf_data)getc(stdin);
             if (pitem->item->data[pitem->index] != '\n' && pitem->item->data[pitem->index] != EOF) {
                 int ch;
                 while ((ch = getc(stdin)) != '\n' && ch != EOF)
@@ -317,7 +317,7 @@ static int runBrainFuck_(bf_byte *byte, bf_env *env) {
             }
             break;
         case '.': {  // 输出
-            int data = pitem->item->data[pitem->index];
+            bf_data data = pitem->item->data[pitem->index];
             putc(data, stdout);
             fflush(stdout);
             break;
@@ -333,7 +333,7 @@ static int runBrainFuck_(bf_byte *byte, bf_env *env) {
                     continue;
             }
 
-            pitem->item->data[pitem->index] = (int)strtol(num, &end, 10);
+            pitem->item->data[pitem->index] = (bf_data)strtol(num, &end, 10);
             if (*end != '\0') {
                 env->error_info = "';' instruction encountered an illegal number";
                 return -1;
@@ -413,25 +413,31 @@ static int runBrainFuck_(bf_byte *byte, bf_env *env) {
 }
 
 
-void bf_printError(char *info, bf_env *env) {
+char *bf_printError(char *info, bf_env *env) {
     if (env->error_info != NULL)
         fprintf(stderr, "%s : %s\n", info, env->error_info);
+    return env->error_info;
+}
+
+
+static void printTape(bf_env *env, bf_item *item, int i, long long count){
+    printf("[(%d)%d", count, item->data[i]);
+    if (isprint(item->data[i]))
+        printf("'%c'", item->data[i]);
+    if (i == env->pitem.index && item == env->pitem.item)
+        printf("(head)");
+    if (i == 0 && item == env->pitem.base)
+        printf("(base)");
+    printf("]");
 }
 
 
 void bf_printPaperTape(bf_env *env) {
-    unsigned count = 0;
+    long long count = 0;
     bf_item *item = env->item;
     while (true) {
         for (int i = 0; i < TABLE_SIZE; i++) {
-            printf("[(%d)%d", count, item->data[i]);
-            if (isprint(item->data[i]))
-                printf("'%c'", item->data[i]);
-            if (i == env->pitem.index && item == env->pitem.item)
-                printf("(head)");
-            if (i == 0 && item == env->pitem.base)
-                printf("(base)");
-            printf("]");
+            printTape(env, item, i, count);
             if (i != TABLE_SIZE - 1)  // 不是最后一个
                 printf("-");
             count++;
@@ -445,6 +451,47 @@ void bf_printPaperTape(bf_env *env) {
 }
 
 
+void bf_printPaperTapeNear(bf_env *env) {
+    int first_index;
+    int count;
+    bf_item *item;
+
+    count = -20;
+    item = env->pitem.item;
+    first_index = env->pitem.index - 20;
+    if (first_index < 0) {
+        if (item->prev != NULL) {
+            first_index = TABLE_SIZE + first_index;
+            item = item->prev;
+        } else {
+            first_index = 0;
+            printf("env->pitem.index = %d\n", env->pitem.index);
+            count = -(env->pitem.index);  // 保证第env->pitem.index时count为0
+            printf("count = %d\n", count);
+        }
+    }
+
+    for (int flat = 0; count <= 20; flat++) {
+        int i = 0;
+        if (flat == 0)
+            i = first_index;
+
+        for (; i < TABLE_SIZE && count <= 20; i++) {
+            printTape(env, item, i, count);
+            if (i != TABLE_SIZE - 1 && count != 20)  // 不是最后一个
+                printf("-");
+            count++;
+        }
+        item = item->next;
+        if (item == NULL)
+            break;
+        else if (count != 20)  // 不是最后一个
+            printf("-");  // 换行接着输出
+    }
+
+}
+
+
 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]))
@@ -453,7 +500,7 @@ void bf_printHead(bf_env *env) {
 
 
 void bf_printEnv(bf_env *env) {
-    bf_printPaperTape(env);
+    bf_printPaperTapeNear(env);
     printf("\n");
     bf_printHead(env);
     printf("\n");

+ 14 - 0
test/CMakeLists.txt

@@ -0,0 +1,14 @@
+SET(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
+
+FUNCTION(ADD_BF_TEST TEST_NAME TEST_SOURCE)
+    ADD_EXECUTABLE(bf_test_${TEST_NAME} ${TEST_SOURCE})
+    TARGET_LINK_LIBRARIES(bf_test_${TEST_NAME} BrainFuck_Lib)
+    ADD_TEST(NAME bf_test_${TEST_NAME} COMMAND $<TARGET_FILE:bf_test_${TEST_NAME}>)
+ENDFUNCTION()
+
+FUNCTION(SET_BF_TEST_PASS TEST_NAME PASS_STR)
+    SET_TESTS_PROPERTIES(bf_test_${TEST_NAME} PROPERTIES PASS_REGULAR_EXPRESSION ${PASS_STR})
+ENDFUNCTION()
+
+ADD_BF_TEST(str test_str.c)
+SET_BF_TEST_PASS(str "Hello World!")

+ 17 - 0
test/test_str.c

@@ -0,0 +1,17 @@
+#include <stdlib.h>
+#include "brainfuck.h"
+
+
+int main() {
+    int status;
+    bf_env *env = bf_setEnv();
+    bf_code code = bf_parserBrainFuck_Str("++++++++++[>+++++++>++++++++++>+++>+<<<<-]\n"
+                                          ">++.>+.+++++++..+++.>++.<<+++++++++++++++.\n"
+                                          ">.+++.------.--------.>+.>.");
+    status = bf_runBrainFuck(code, env);
+    bf_printError("error", env);
+    bf_freeBrainFuck(code);
+
+    bf_freeEnv(env);
+    return status;
+}