diff --git a/.gitignore b/.gitignore index 022a2cf..5834aca 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.idea # .gitignore for brainfuck. # ignore the .cproject and .project file generated by Eclipse. diff --git a/CMakeLists.txt b/CMakeLists.txt old mode 100644 new mode 100755 index dcd102d..f94b17f --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,12 +9,12 @@ set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) ## Compile-time options ## option(ENABLE_CLI "Enable the command line interface" ON) -option(ENABLE_EDITLINE "Enable GNU readline functionality provided by the editline library" ON) option(ENABLE_EXTENSION_DEBUG "Enable the debug extension for brainfuck") option(INSTALL_EXAMPLES "Install the examples") ## Target ## -set(BRAINFUCK_H include/brainfuck.h) +set(BRAINFUCK_H include/brainfuck.h + src/brainfuck.c) set(BRAINFUCK_C_FLAGS $<$,$,$>: -Wall -Wextra> @@ -49,9 +49,7 @@ if(ENABLE_CLI) target_compile_options(brainfuck-cli PRIVATE "${BRAINFUCK_C_FLAGS}") if(ENABLE_EDITLINE) - find_package(editline REQUIRED) target_compile_definitions(brainfuck-cli PRIVATE BRAINFUCK_EDITLINE_LIB) - target_link_libraries(brainfuck-cli PRIVATE editline) endif() endif() diff --git a/LICENSE.txt b/LICENSE.txt old mode 100644 new mode 100755 diff --git a/README.md b/README.md old mode 100644 new mode 100755 index a17bd95..667ed22 --- a/README.md +++ b/README.md @@ -1,8 +1,31 @@ brainfuck [![Build Status](https://github.com/fabianishere/brainfuck/workflows/Build/badge.svg)](https://github.com/fabianishere/brainfuck/actions?query=workflow%3ABuild) -=========== Brainfuck interpreter written in C. - +## Update +1. Added a double slash '//' comment function. +2. Added a shebang '#!' line function. +3. Added a function that allows letters to replace symbols + - default replacing mothed + - + | origin | optional | + |--------|-----------| + | '+' | 'o' , 'O' | + | '-' | 'i' , 'I' | + | '<' | 'a' , 'A' | + | '>' | 'e' , 'E' | + | '.' | '.' , '.' | + | ',' | ',' , ',' | + | '[' | 'u' , 'U' | + | ']' | 'y' , 'Y' | + | '#' | '?' , '!' | + + - you can change them in `src/brainfuck.c:other_char_table`.\ + - I love these vowels, reading them is like listening to a baby trying to speak but only babbling. +4. Added two tools transLetter.py and transLetters.sh to help users automatically convert letters to others. +5. Add a tools folder to store transLetter.py and transLetters.sh.\ +You can compare the instruction parameters in transLetters.sh to customize the conversion file. +6. Removed the `editline` library dependency,\ +because it was interfering with the NVIDIA 4060 GPU driver on my Ubuntu PC. ## Usage brainfuck [-veh] file... -e --eval run code directly @@ -43,29 +66,42 @@ $ git clone https://github.com/fabianishere/brainfuck.git or simply [grab](https://github.com/fabianishere/brainfuck/archive/master.zip) a copy of the source code as a Zip file. ## Building -Create the build directory. -```sh -$ mkdir build -$ cd build -``` -Brainfuck requires CMake and a C compiler (e.g. Clang or GCC) in order to run. It also depends on [libedit](http://thrysoee.dk/editline/), which is available in the main repositories of most Linux distributions (e.g. as [libedit-dev](https://packages.debian.org/stretch/libedit-dev) on Debian/Ubuntu) and comes with the macOS XCode command line tools. +1. Create the build directory. + ```sh + $ mkdir build + $ cd build + ``` +2. Brainfuck requires CMake and a C compiler (e.g. Clang or GCC) in order to run. It also depends on [libedit](http://thrysoee.dk/editline/), which is available in the main repositories of most Linux distributions (e.g. as [libedit-dev](https://packages.debian.org/stretch/libedit-dev) on Debian/Ubuntu) and comes with the macOS XCode command line tools. Then, simply create the Makefiles: -```sh -$ cmake .. -``` -and finally, build it using the building system you chose (e.g. Make): -```sh -$ make -``` + ```sh + $ cmake .. + ``` +3. and finally, build it using the building system you chose (e.g. Make): + ```sh + $ make + ``` -After the build has been finished, you may install the binaries to your local system (see [CMAKE\_INSTALL\_PREFIX](https://cmake.org/cmake/help/v3.0/variable/CMAKE_INSTALL_PREFIX.html) for information about the install prefix): -```sh -$ make install -``` -Alternatively, you may run the interpreter directly without installation, for instance: -```sh -$ ./brainfuck ../examples/hello.bf -``` +4. After the build has been finished, you may install the binaries to your local system (see [CMAKE\_INSTALL\_PREFIX](https://cmake.org/cmake/help/v3.0/variable/CMAKE_INSTALL_PREFIX.html) for information about the install prefix): + ```sh + $ make install + ``` +5. Alternatively, you may run the interpreter directly without installation, for instance: + ```sh + $ ./brainfuck ../examples/hello.bf + ``` +6. Because some new features are added, you can add `#!brainfuck` and change you symbols +like the `hello.bf`: + ```brainfuck + #!/usr/bin/env brainfuck + // hello + ooooooooueooooueooeoooeoooeoaaaaiyeoeoeieeouayaiyee.eiii.ooooooo..ooo.ee.ai.a.oo + o.iiiiii.iiiiiiii.eeo.eoo. + ``` + Then run the file directly in the terminal\ + ```sh + sudo chmod 755 hello.bf + hello.bf + ``` ## License The code is released under the Apache License version 2.0. See [LICENSE.txt](/LICENSE.txt). diff --git a/cmake/Findeditline.cmake b/cmake/Findeditline.cmake deleted file mode 100644 index 0c51231..0000000 --- a/cmake/Findeditline.cmake +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright (c) 2020 Fabian Mastenbroek -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -find_path(EDITLINE_INCLUDE_DIR editline/readline.h) -find_library(EDITLINE_LIBRARY NAMES edit) - -set(EDITLINE_LIBRARIES ${EDITLINE_LIBRARY}) -set(EDITLINE_INCLUDE_DIRS ${EDITLINE_INCLUDE_DIR}) - -include(FindPackageHandleStandardArgs) - -# handle the QUIETLY and REQUIRED arguments and set EDITLINE_FOUND to TRUE -# if all listed variables are TRUE -find_package_handle_standard_args(editline DEFAULT_MSG EDITLINE_INCLUDE_DIR EDITLINE_LIBRARY) - -mark_as_advanced(EDITLINE_INCLUDE_DIR EDITLINE_LIBRARY) - -if(EDITLINE_FOUND) - add_library(editline SHARED IMPORTED GLOBAL) - set_property(TARGET editline PROPERTY IMPORTED_LOCATION ${EDITLINE_LIBRARY}) - set_property(TARGET editline PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${EDITLINE_INCLUDE_DIRS}) -endif() diff --git a/examples/196-commented.bf b/examples/196-commented.bf old mode 100644 new mode 100755 index 1082a17..86e41e9 --- a/examples/196-commented.bf +++ b/examples/196-commented.bf @@ -1,3 +1,4 @@ +#!/home/mango/CLionProjects/brainfuck/build/brainfuck [ The 196-algorithm implemented in brainfuck by Mats Linander. diff --git a/examples/196.bf b/examples/196.bf old mode 100644 new mode 100755 diff --git a/examples/2d_table.bf b/examples/2d_table.bf old mode 100644 new mode 100755 index bc3c317..8a06d9b --- a/examples/2d_table.bf +++ b/examples/2d_table.bf @@ -1,4 +1,4 @@ - +#!/home/mango/CLionProjects/brainfuck/build/brainfuck 2D Table reading routine for BrainFuck Made By: NYYRIKKI 2001 diff --git a/examples/666.bf b/examples/666.bf old mode 100644 new mode 100755 index d0a2d51..986ef3e --- a/examples/666.bf +++ b/examples/666.bf @@ -1,3 +1,4 @@ + >+++++++++[<++++++>-]<... >++++++++++. diff --git a/examples/asciiart/asciiart.bf b/examples/asciiart/asciiart.bf old mode 100644 new mode 100755 index f871dcd..7c68c2c --- a/examples/asciiart/asciiart.bf +++ b/examples/asciiart/asciiart.bf @@ -1,3 +1,4 @@ +#!/usr/local/bin/brainfuck >>+++[<+++++[<+++++++>-]>-]<<.>>>+++++[<+++++++++++[<++>-]>-]<<.>++++> >++++[<++++[<+++++++>-]>-]<<.>++>+++++[<++++++>-]<.>+>>++++[<+++++++++ [<+++>-]>-]<<.>+>>++++[<++++[<++++++>-]>-]<<.<<<<<.>.>>>>>+>++++++++++ diff --git a/examples/asciiart/chess.bf b/examples/asciiart/chess.bf old mode 100644 new mode 100755 index c6d2683..dc41d85 --- a/examples/asciiart/chess.bf +++ b/examples/asciiart/chess.bf @@ -1,5 +1,4 @@ - - +#!brainfuck +++++++++++++++[>++>+++>+++>++++++>++>+++++++>++++>+<<<<<<<<-]>>--.>.... ...<.>.......<.>.......<.>.......<.>.......<.>.......<.>.......<.>...... .<.>>>>>>-----.<<+++++++++++++++++++.<++.<+.++++.>>.<<.--.>.>.>--..<<<++ diff --git a/examples/asciiart/text.bf b/examples/asciiart/text.bf old mode 100644 new mode 100755 diff --git a/examples/asciiart/triangle.bf b/examples/asciiart/triangle.bf old mode 100644 new mode 100755 index b03e5ab..1f1aacc --- a/examples/asciiart/triangle.bf +++ b/examples/asciiart/triangle.bf @@ -1,3 +1,4 @@ +#!/home/mango/CLionProjects/brainfuck/build/brainfuck [ This program prints Sierpinski triangle on 80-column display. ] > + + @@ -14,7 +15,7 @@ > > + < < < < < < < < < < [ - [ - > + < - ] > [ - < + > > > . < < ] > > > + ] > [ - < + > > > . < < ] > > > // dt crraa [ [ - > + + + + + + @@ -30,4 +31,4 @@ [ - < - > ] < < < ] < < < < ] + + + + + + + + + + . + + + . [ - ] < ] + + + + + - * * * * * M a d e * B y : * N Y Y R I K K I * 2 0 0 2 * * * * * \ No newline at end of file + * * * * * * * * * * * * * * * * * * * * * * * 2 0 0 2 * * * * * \ No newline at end of file diff --git a/examples/asciiart/xmastree.bf b/examples/asciiart/xmastree.bf old mode 100644 new mode 100755 index 8ff3260..c959396 --- a/examples/asciiart/xmastree.bf +++ b/examples/asciiart/xmastree.bf @@ -1,3 +1,4 @@ +#!/home/mango/CLionProjects/brainfuck/build/brainfuck [xmastree.b -- print Christmas tree (c) 2016 Daniel B. Cristofani http://brainfuck.org/] diff --git a/examples/atoi.bf b/examples/atoi.bf old mode 100644 new mode 100755 diff --git a/examples/bench/bench-1.bf b/examples/bench/bench-1.bf old mode 100644 new mode 100755 index 208069a..0b86c11 --- a/examples/bench/bench-1.bf +++ b/examples/bench/bench-1.bf @@ -1,2 +1,3 @@ +#!/usr/local/bin/brainfuck ++++++++[->-[->-[->-[-]<]<]<] >++++++++[<++++++++++>-]<[>+>+<<-]>-.>-----.> diff --git a/examples/bench/bench-2.bf b/examples/bench/bench-2.bf old mode 100644 new mode 100755 diff --git a/examples/bench/easy-opt.bf b/examples/bench/easy-opt.bf old mode 100644 new mode 100755 diff --git a/examples/bench/endtest.bf b/examples/bench/endtest.bf old mode 100644 new mode 100755 diff --git a/examples/bench/long.bf b/examples/bench/long.bf old mode 100644 new mode 100755 diff --git a/examples/bench/prttab.bf b/examples/bench/prttab.bf old mode 100644 new mode 100755 diff --git a/examples/bench/skiploop.bf b/examples/bench/skiploop.bf old mode 100644 new mode 100755 index db9bda0..0ae09e4 --- a/examples/bench/skiploop.bf +++ b/examples/bench/skiploop.bf @@ -1,3 +1,4 @@ +#!/home/mango/CLionProjects/brainfuck/build/brainfuck [ This program tests how well the interpreter reacts to large runs of '>' and '<'. This is the source RLE-encoded. diff --git a/examples/bench/tests.bf b/examples/bench/tests.bf old mode 100644 new mode 100755 diff --git a/examples/bottles-1.bf b/examples/bottles-1.bf old mode 100644 new mode 100755 diff --git a/examples/bottles-2.bf b/examples/bottles-2.bf old mode 100644 new mode 100755 diff --git a/examples/bottles-3.bf b/examples/bottles-3.bf old mode 100644 new mode 100755 diff --git a/examples/busy-beaver-overflow.bf b/examples/busy-beaver-overflow.bf old mode 100644 new mode 100755 diff --git a/examples/busy-beaver.bf b/examples/busy-beaver.bf old mode 100644 new mode 100755 diff --git a/examples/cat-1.bf b/examples/cat-1.bf old mode 100644 new mode 100755 diff --git a/examples/cat-2.bf b/examples/cat-2.bf old mode 100644 new mode 100755 diff --git a/examples/cat-3.bf b/examples/cat-3.bf old mode 100644 new mode 100755 diff --git a/examples/cell-size-1.bf b/examples/cell-size-1.bf old mode 100644 new mode 100755 index 690f4d4..7f45c11 --- a/examples/cell-size-1.bf +++ b/examples/cell-size-1.bf @@ -1,4 +1,4 @@ - +#!/home/mango/CLionProjects/brainfuck/build/brainfuck // Calculate (16*16*16*16)^128 AKA 2^2048 ++[>++++++++<-]>[>++++++++<-]>[<< diff --git a/examples/cell-size-2.bf b/examples/cell-size-2.bf old mode 100644 new mode 100755 diff --git a/examples/cell-size-3.bf b/examples/cell-size-3.bf old mode 100644 new mode 100755 diff --git a/examples/cell-size-4.bf b/examples/cell-size-4.bf old mode 100644 new mode 100755 diff --git a/examples/cell-size-5.bf b/examples/cell-size-5.bf old mode 100644 new mode 100755 diff --git a/examples/char.bf b/examples/char.bf old mode 100644 new mode 100755 diff --git a/examples/counter.bf b/examples/counter.bf old mode 100644 new mode 100755 diff --git a/examples/css.bf b/examples/css.bf old mode 100644 new mode 100755 diff --git a/examples/decss.bf b/examples/decss.bf old mode 100644 new mode 100755 diff --git a/examples/dvorak.bf b/examples/dvorak.bf old mode 100644 new mode 100755 diff --git a/examples/echo.bf b/examples/echo.bf old mode 100644 new mode 100755 diff --git a/examples/gameoflife.bf b/examples/gameoflife.bf old mode 100644 new mode 100755 diff --git a/examples/hanoi-opt.bf b/examples/hanoi-opt.bf old mode 100644 new mode 100755 diff --git a/examples/hanoi.bf b/examples/hanoi.bf old mode 100644 new mode 100755 diff --git a/examples/head.bf b/examples/head.bf old mode 100644 new mode 100755 diff --git a/examples/hello-short.bf b/examples/hello-short.bf old mode 100644 new mode 100755 diff --git a/examples/hello.bf b/examples/hello.bf old mode 100644 new mode 100755 index 41a69fe..22816d4 --- a/examples/hello.bf +++ b/examples/hello.bf @@ -1,2 +1,3 @@ +#!/usr/bin/env brainfuck ++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.++ +.------.--------.>>+.>++. \ No newline at end of file diff --git a/examples/htmlconv.bf b/examples/htmlconv.bf old mode 100644 new mode 100755 diff --git a/examples/jabh.bf b/examples/jabh.bf old mode 100644 new mode 100755 diff --git a/examples/ks.bf b/examples/ks.bf old mode 100644 new mode 100755 diff --git a/examples/lost-kingdom.bf b/examples/lost-kingdom.bf old mode 100644 new mode 100755 diff --git a/examples/mac2unix.bf b/examples/mac2unix.bf old mode 100644 new mode 100755 diff --git a/examples/numwarp.bf b/examples/numwarp.bf old mode 100644 new mode 100755 diff --git a/examples/oobrain.bf b/examples/oobrain.bf old mode 100644 new mode 100755 diff --git a/examples/order.bf b/examples/order.bf old mode 100644 new mode 100755 diff --git a/examples/packbits.bf b/examples/packbits.bf old mode 100644 new mode 100755 diff --git a/examples/password-vault.bf b/examples/password-vault.bf old mode 100644 new mode 100755 diff --git a/examples/reverse.bf b/examples/reverse.bf old mode 100644 new mode 100755 diff --git a/examples/rot13.bf b/examples/rot13.bf old mode 100644 new mode 100755 diff --git a/examples/rpn.bf b/examples/rpn.bf old mode 100644 new mode 100755 diff --git a/examples/short.bf b/examples/short.bf old mode 100644 new mode 100755 diff --git a/examples/si.bf b/examples/si.bf old mode 100644 new mode 100755 diff --git a/examples/sisihi.bf b/examples/sisihi.bf old mode 100644 new mode 100755 diff --git a/examples/utm.bf b/examples/utm.bf old mode 100644 new mode 100755 diff --git a/examples/wc.bf b/examples/wc.bf old mode 100644 new mode 100755 diff --git a/include/brainfuck.h b/include/brainfuck.h index 0bc3826..8209463 100644 --- a/include/brainfuck.h +++ b/include/brainfuck.h @@ -26,8 +26,14 @@ #define BRAINFUCK_TOKEN_NEXT '>' #define BRAINFUCK_TOKEN_OUTPUT '.' #define BRAINFUCK_TOKEN_INPUT ',' + #define BRAINFUCK_TOKEN_LOOP_START '[' #define BRAINFUCK_TOKEN_LOOP_END ']' + +// extern const char char_table[][3]; +// extern const int char_table_size; + +#define BRAINFUCK_EXTENSION_DEBUG #ifdef BRAINFUCK_EXTENSION_DEBUG # define BRAINFUCK_TOKEN_BREAK '#' #else diff --git a/man/brainfuck.1 b/man/brainfuck.1 index beabfb6..2693dba 100644 --- a/man/brainfuck.1 +++ b/man/brainfuck.1 @@ -28,23 +28,23 @@ A brainfuck interpreter written in C. The brainfuck syntax is described below: .Pp .Bl -tag -width -indent -.It Sy > -Increment the data pointer (to point to the next cell to the right). -.It Sy < +.It Sy < , a , A Decrement the data pointer (to point to the next cell to the left). -.It Sy + -Increment (increase by one) the byte at the data pointer. -.It Sy - +.It Sy > , e , E +Increment the data pointer (to point to the next cell to the right). +.It Sy - , i , I Decrement (decrease by one) the byte at the data pointer. -.It Sy \&. -Output the byte at the data pointer as an ASCII encoded character. +.It Sy + , o , O +Increment (increase by one) the byte at the data pointer. .It Sy \&, Accept one byte of input, storing its value in the byte at the data pointer. -.It Sy \&[ +.It Sy \&. +Output the byte at the data pointer as an ASCII encoded character. +.It Sy \&[ , y , Y If the byte at the data pointer is zero, then instead of moving the instruction pointer forward to the next command, jump it forward to the command after the matching .Sy \&] token. -.It Sy \&] +.It Sy \&] , u , U If the byte at the data pointer is nonzero, then instead of moving the instruction pointer forward to the next command, jump it back to the command after the matching .Sy \&[ token. diff --git a/src/brainfuck.c b/src/brainfuck.c index c62c11f..7045084 100644 --- a/src/brainfuck.c +++ b/src/brainfuck.c @@ -12,7 +12,7 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - */ +*/ #include #include @@ -21,6 +21,62 @@ #include #include +const char other_char_table[][5] = { + {BRAINFUCK_TOKEN_PLUS , '+', 'o', 'O'}, + {BRAINFUCK_TOKEN_MINUS , '-', 'i', 'I'}, + {BRAINFUCK_TOKEN_PREVIOUS , '<', 'a', 'A'}, + {BRAINFUCK_TOKEN_NEXT , '>', 'e', 'E'}, + {BRAINFUCK_TOKEN_OUTPUT , '.', '.', '.'}, + {BRAINFUCK_TOKEN_INPUT , ',', ',', ','}, + {BRAINFUCK_TOKEN_LOOP_START , '[', 'u', 'U'}, + {BRAINFUCK_TOKEN_LOOP_END , ']', 'y', 'Y'}, + {BRAINFUCK_TOKEN_BREAK , '#', '?', '!'}, +}; +const int other_char_table_size = sizeof(other_char_table) / sizeof(other_char_table[0]); // 计算子数组的数量 + +char preprocessing_char(char ch, FILE *stream) { + while (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t') { + ch = fgetc(stream); + } + + // Skip "//.*" + if (ch == '/') { + char next = fgetc(stream); + if (next == '/') { + // Skip the entire line + while ((ch = fgetc(stream)) != '\n' && ch != EOF) { + } + return ch; + } + ungetc(next, stream); + } + + // Skip "^#!.*", but fist line of a file + static int first_line = 1; + if (first_line && ch == '#') { + first_line = 0; + char next = fgetc(stream); + if (next == '!') { + // Skip the entire line + while ((ch = fgetc(stream)) != '\n' && ch != EOF) { + } + return ch; + } + ungetc(next, stream); + } + + for (int i = 0; i < other_char_table_size; ++i) { + const char *table = other_char_table[i]; + for (int j = 0; table[j] != '\0'; ++j) {// Iterate until a null character is encountered. + if (ch == table[j]) { + ch = table[0]; // Replace ch with the first element of the subarray. + break; // Exit the inner loop once a match is found. + } + } + } + + return ch; +} /** * Creates a new state. @@ -219,48 +275,53 @@ BrainfuckInstruction * brainfuck_parse_stream_until(FILE *stream, const int unti char temp; while ((ch = fgetc(stream)) != until) { if (ch == EOF || feof(stream)) { break; } + ch = preprocessing_char(ch, stream); + if (ch == EOF || ch == '\n') { + continue; + } + instruction->type = ch; instruction->difference = 1; switch(ch) { - case BRAINFUCK_TOKEN_PLUS: - case BRAINFUCK_TOKEN_MINUS: - while ((temp = fgetc(stream)) != until && (temp == BRAINFUCK_TOKEN_PLUS - || temp == BRAINFUCK_TOKEN_MINUS)) { - if (temp == ch) { - instruction->difference++; - } else { - instruction->difference--; + case BRAINFUCK_TOKEN_PLUS: + case BRAINFUCK_TOKEN_MINUS: + while ((temp = fgetc(stream)) != until + && (temp == BRAINFUCK_TOKEN_PLUS || temp == BRAINFUCK_TOKEN_MINUS)) { + if (temp == ch) { + instruction->difference++; + } else { + instruction->difference--; + } } - } - ungetc(temp, stream); - break; - case BRAINFUCK_TOKEN_NEXT: - case BRAINFUCK_TOKEN_PREVIOUS: - while ((temp = fgetc(stream)) != until && (temp == BRAINFUCK_TOKEN_NEXT - || temp == BRAINFUCK_TOKEN_PREVIOUS)) { - if (temp == ch) { - instruction->difference++; - } else { - instruction->difference--; + ungetc(temp, stream); + break; + case BRAINFUCK_TOKEN_NEXT: + case BRAINFUCK_TOKEN_PREVIOUS: + while ((temp = fgetc(stream)) != until + && (temp == BRAINFUCK_TOKEN_NEXT || temp == BRAINFUCK_TOKEN_PREVIOUS)) { + if (temp == ch) { + instruction->difference++; + } else { + instruction->difference--; + } } - } - ungetc(temp, stream); - break; - case BRAINFUCK_TOKEN_OUTPUT: - case BRAINFUCK_TOKEN_INPUT: - while ((temp = fgetc(stream)) != until && temp == ch) - instruction->difference++; - ungetc(temp, stream); - break; - case BRAINFUCK_TOKEN_LOOP_START: - instruction->loop = brainfuck_parse_stream_until(stream, until); - break; - case BRAINFUCK_TOKEN_LOOP_END: - return root; - case BRAINFUCK_TOKEN_BREAK: - break; - default: - continue; + ungetc(temp, stream); + break; + case BRAINFUCK_TOKEN_OUTPUT: + case BRAINFUCK_TOKEN_INPUT: + while ((temp = fgetc(stream)) != until && temp == ch) + instruction->difference++; + ungetc(temp, stream); + break; + case BRAINFUCK_TOKEN_LOOP_START: + instruction->loop = brainfuck_parse_stream_until(stream, until); + break; + case BRAINFUCK_TOKEN_LOOP_END: + return root; + case BRAINFUCK_TOKEN_BREAK: + break; + default: + continue; } instruction->next = (BrainfuckInstruction *) malloc(sizeof(BrainfuckInstruction)); instruction->next->next = 0; diff --git a/src/main.c b/src/main.c index ee4f18d..6ac6709 100644 --- a/src/main.c +++ b/src/main.c @@ -19,52 +19,98 @@ #include #include -#ifdef BRAINFUCK_EDITLINE_LIB - #include -#endif - #if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__) #include - #define isatty _isatty - #define STDIN_FILENO 0 + #define isatty _isatty + #define STDIN_FILENO 0 #else - #include + #include #endif #include +#define HISTORY_SIZE 100 +char *history[HISTORY_SIZE]; +int history_index = 0; + /** * Print the usage message of this program. * * @param name The name of this program (given by argv[0]). */ void print_usage(char *name) { - fprintf(stderr, "usage: %s [-evh] [file...]\n", name); - fprintf(stderr, "\t-e --eval\t\trun code directly\n"); - fprintf(stderr, "\t-v --version\t\tshow version information\n"); - fprintf(stderr, "\t-h --help\t\tshow a help message\n"); + fprintf(stderr, "usage: %s [-evh] [file...]\n", name); + fprintf(stderr, "\t-e --eval\t\trun code directly\n"); + fprintf(stderr, "\t-v --version\t\tshow version information\n"); + fprintf(stderr, "\t-h --help\t\tshow a help message\n"); } /** * Print the version information. */ void print_version() { - fprintf(stderr, "brainfuck %d.%d.%d (%s, %s)\n", BRAINFUCK_VERSION_MAJOR, - BRAINFUCK_VERSION_MINOR, BRAINFUCK_VERSION_PATCH, __DATE__, __TIME__); - fprintf(stderr, "Copyright (c) 2016 Fabian Mastenbroek.\n"); - fprintf(stderr, "Distributed under the Apache License Version 2.0.\n"); + fprintf(stderr, "brainfuck %d.%d.%d (%s, %s)\n", BRAINFUCK_VERSION_MAJOR, + BRAINFUCK_VERSION_MINOR, BRAINFUCK_VERSION_PATCH, __DATE__, __TIME__); + fprintf(stderr, "Copyright (c) 2016 Fabian Mastenbroek.\n"); + fprintf(stderr, "Distributed under the Apache License Version 2.0.\n"); } -#ifdef BRAINFUCK_EDITLINE_LIB /** - * Readline initialization calls, in this case disabling autocompletion and - * enabling user input history. + * Initialize readline functionality. */ void initialize_readline() { - rl_bind_key ('\t', rl_insert); /* disable tab autocompletion */ - stifle_history(READLINE_HIST_SIZE); /* set history size */ + // 初始化历史记录索引 + history_index = 0; +} + +/** + * Read a line from the user input. + * + * @param prompt The prompt to display to the user. + * @return A dynamically allocated string containing the user input. + */ +char *readline(const char *prompt) { + static char line[1024]; + if (prompt) { + printf("%s", prompt); + fflush(stdout); + } + + if (fgets(line, sizeof(line), stdin) == NULL) { + return NULL; + } + + // 移除换行符 + line[strcspn(line, "\n")] = 0; + + return strdup(line); // 返回一个动态分配的副本 +} + +/** + * Add a line to the history. + * + * @param line The line to add to the history. + */ +void add_history(const char *line) { + if (line == NULL) { + return; + } + + // 移除重复的历史记录 + if (history_index > 0 && strcmp(history[history_index - 1], line) == 0) { + return; + } + + if (history_index < HISTORY_SIZE) { + history[history_index++] = strdup(line); + } else { + free(history[0]); + for (int i = 0; i < HISTORY_SIZE - 1; i++) { + history[i] = history[i + 1]; + } + history[HISTORY_SIZE - 1] = strdup(line); + } } -#endif /** * Run the given brainfuck file. @@ -73,19 +119,19 @@ void initialize_readline() { * @return EXIT_SUCCESS if no errors are encountered, otherwise EXIT_FAILURE. */ int run_file(FILE *file) { - BrainfuckState *state = brainfuck_state(); - BrainfuckExecutionContext *context = brainfuck_context(BRAINFUCK_TAPE_SIZE); - if (file == NULL) { - brainfuck_destroy_context(context); - brainfuck_destroy_state(state); - return EXIT_FAILURE; - } - brainfuck_add(state, brainfuck_parse_stream(file)); - brainfuck_execute(state->root, context); - brainfuck_destroy_context(context); - brainfuck_destroy_state(state); - fclose(file); - return EXIT_SUCCESS; + BrainfuckState *state = brainfuck_state(); + BrainfuckExecutionContext *context = brainfuck_context(BRAINFUCK_TAPE_SIZE); + if (file == NULL) { + brainfuck_destroy_context(context); + brainfuck_destroy_state(state); + return EXIT_FAILURE; + } + brainfuck_add(state, brainfuck_parse_stream(file)); + brainfuck_execute(state->root, context); + brainfuck_destroy_context(context); + brainfuck_destroy_state(state); + fclose(file); + return EXIT_SUCCESS; } /** @@ -95,76 +141,58 @@ int run_file(FILE *file) { * @return EXIT_SUCCESS if no errors are encountered, otherwise EXIT_FAILURE. */ int run_string(char *code) { - BrainfuckState *state = brainfuck_state(); - BrainfuckExecutionContext *context = brainfuck_context(BRAINFUCK_TAPE_SIZE); - BrainfuckInstruction *instruction = brainfuck_parse_string(code); - brainfuck_add(state, instruction); - brainfuck_execute(state->root, context); - brainfuck_destroy_context(context); - brainfuck_destroy_state(state); - return EXIT_SUCCESS; + BrainfuckState *state = brainfuck_state(); + BrainfuckExecutionContext *context = brainfuck_context(BRAINFUCK_TAPE_SIZE); + BrainfuckInstruction *instruction = brainfuck_parse_string(code); + brainfuck_add(state, instruction); + brainfuck_execute(state->root, context); + brainfuck_destroy_context(context); + brainfuck_destroy_state(state); + return EXIT_SUCCESS; } /** * Run the brainfuck interpreter in interactive mode. */ void run_interactive_console() { - printf("brainfuck %d.%d.%d (%s, %s)\n", BRAINFUCK_VERSION_MAJOR, - BRAINFUCK_VERSION_MINOR, BRAINFUCK_VERSION_PATCH, __DATE__, __TIME__); + printf("brainfuck %d.%d.%d (%s, %s)\n", BRAINFUCK_VERSION_MAJOR, + BRAINFUCK_VERSION_MINOR, BRAINFUCK_VERSION_PATCH, __DATE__, __TIME__); #ifdef BRAINFUCK_EXTENSION_DEBUG - printf("Use # to inspect tape\n"); -#endif - BrainfuckState *state = brainfuck_state(); - BrainfuckExecutionContext *context = brainfuck_context(BRAINFUCK_TAPE_SIZE); - BrainfuckInstruction *instruction; -#ifdef BRAINFUCK_EDITLINE_LIB - char *line; - - initialize_readline(); - while(1) { - line = readline(">> "); - if (line) { - /* Empty string crashes on some OSs/versions of editline */ - if (line[0] == '\0') { - free(line); - continue; - } - - char* expansion; - int result; - - result = history_expand(line, &expansion); - if (result >= 0 && result != 2 ) { add_history(expansion); } - free(expansion); - } else { - /* EOF */ - break; - } - instruction = brainfuck_parse_string(line); - free(line); - brainfuck_add(state, instruction); - brainfuck_execute(instruction, context); - } -#else - printf(">> "); - while(1) { - fflush(stdout); - instruction = brainfuck_parse_stream_until(stdin, '\n'); - if (feof(stdin)) { break; } - fflush(stdin); - brainfuck_add(state, instruction); - brainfuck_execute(instruction, context); - printf(">> "); - } + printf("Use # to inspect tape\n"); #endif + BrainfuckState *state = brainfuck_state(); + BrainfuckExecutionContext *context = brainfuck_context(BRAINFUCK_TAPE_SIZE); + BrainfuckInstruction *instruction; + char *line; + + initialize_readline(); + while(1) { + line = readline(">> "); + if (line) { + /* Empty string crashes on some OSs/versions of editline */ + if (line[0] == '\0') { + free(line); + continue; + } + + add_history(line); + instruction = brainfuck_parse_string(line); + brainfuck_add(state, instruction); + brainfuck_execute(state->root, context); + free(line); + } else { + /* EOF */ + break; + } + } } /* Command line options */ static struct option long_options[] = { - {"help", no_argument, 0, 'h'}, - {"eval", required_argument, 0, 'e'}, - {"version", no_argument, 0, 'v'}, - {0, 0, 0, 0} + {"help", no_argument, 0, 'h'}, + {"eval", required_argument, 0, 'e'}, + {"version", no_argument, 0, 'v'}, + {0, 0, 0, 0} }; /** @@ -174,49 +202,49 @@ static struct option long_options[] = { * @param argv The array with arguments. */ int main(int argc, char *argv[]) { - int c; - int i = 1; - int option_index = 0; - - while (1) { - option_index = 0; - c = getopt_long (argc, argv, "vhe:", - long_options, &option_index); - if (c == -1) - break; - - switch (c) { - case 0: - if (long_options[option_index].flag != 0) - break; - break; - case 'h': - print_usage(argv[0]); - return EXIT_SUCCESS; - case 'v': - print_version(); - return EXIT_SUCCESS; - case 'e': - return run_string((char *) optarg); - case '?': - print_usage(argv[0]); - return EXIT_FAILURE; - default: - abort(); - } - } - if (argc > 1) { - while (i < argc) - if (run_file(fopen(argv[i++], "r")) == EXIT_FAILURE) - fprintf(stderr, "error: failed to read file %s\n", argv[i - 1]); - } else { - /* Check if someone is piping code or just calling it the normal way */ - if (isatty(STDIN_FILENO)) { - run_interactive_console(); - } else { - if (run_file(stdin) == EXIT_FAILURE) - fprintf(stderr, "error: failed to read from stdin\n"); - } - } - return EXIT_SUCCESS; -} + int c; + int i = 1; + int option_index = 0; + + while (1) { + option_index = 0; + c = getopt_long (argc, argv, "vhe:", + long_options, &option_index); + if (c == -1) + break; + + switch (c) { + case 0: + if (long_options[option_index].flag != 0) + break; + break; + case 'h': + print_usage(argv[0]); + return EXIT_SUCCESS; + case 'v': + print_version(); + return EXIT_SUCCESS; + case 'e': + return run_string((char *) optarg); + case '?': + print_usage(argv[0]); + return EXIT_FAILURE; + default: + abort(); + } + } + if (argc > 1) { + while (i < argc) + if (run_file(fopen(argv[i++], "r")) == EXIT_FAILURE) + fprintf(stderr, "error: failed to read file %s\n", argv[i - 1]); + } else { + /* Check if someone is piping code or just calling it the normal way */ + if (isatty(STDIN_FILENO)) { + run_interactive_console(); + } else { + if (run_file(stdin) == EXIT_FAILURE) + fprintf(stderr, "error: failed to read from stdin\n"); + } + } + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/tools/hello.bf b/tools/hello.bf new file mode 100755 index 0000000..f2285e6 --- /dev/null +++ b/tools/hello.bf @@ -0,0 +1,4 @@ +#!/usr/bin/env brainfuck +// hello +++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.++ ++.------.--------.>>+.>++. \ No newline at end of file diff --git a/tools/output.bf b/tools/output.bf new file mode 100755 index 0000000..e266ca6 --- /dev/null +++ b/tools/output.bf @@ -0,0 +1,4 @@ +#!/usr/bin/env brainfuck +// hello +ooooooooueooooueooeoooeoooeoaaaaiyeoeoeieeouayaiyee.eiii.ooooooo..ooo.ee.ai.a.oo +o.iiiiii.iiiiiiii.eeo.eoo. \ No newline at end of file diff --git a/tools/transLetter.py b/tools/transLetter.py new file mode 100755 index 0000000..fc1b1cd --- /dev/null +++ b/tools/transLetter.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python +import argparse + +output_file: str = "output.bf" +input_file:str = "input.bf" + +import io + +# 与 C 中常量保持一致,可自行修改 +BRAINFUCK_TOKEN_PLUS = 'o' # '+' , 'O' , 'o' +BRAINFUCK_TOKEN_MINUS = 'i' # '-' , 'I' , 'i' +BRAINFUCK_TOKEN_PREVIOUS = 'a' # '<' , 'A' , 'a' +BRAINFUCK_TOKEN_NEXT = 'e' # '>' , 'E' , 'e' +BRAINFUCK_TOKEN_OUTPUT = '.' # '.' , '.' , '.' +BRAINFUCK_TOKEN_INPUT = ',' # ',' , ',' , ',' +BRAINFUCK_TOKEN_LOOP_START = 'u' # '[' , 'U' , 'u' +BRAINFUCK_TOKEN_LOOP_END = 'y' # ']' , 'Y' , 'y' +BRAINFUCK_TOKEN_BREAK = '#' # '#' , '!' , '#' + +# 字符映射表:每组第 0 个元素是目标字符 +OTHER_CHAR_TABLE = [ + [BRAINFUCK_TOKEN_PLUS , '+', 'o', 'O'], + [BRAINFUCK_TOKEN_MINUS , '-', 'i', 'I'], + [BRAINFUCK_TOKEN_PREVIOUS , '<', 'a', 'A'], + [BRAINFUCK_TOKEN_NEXT , '>', 'e', 'E'], + [BRAINFUCK_TOKEN_OUTPUT , '.', '.', '.',], + [BRAINFUCK_TOKEN_INPUT , ',', ',', ',',], + [BRAINFUCK_TOKEN_LOOP_START , '[', 'u', 'U'], + [BRAINFUCK_TOKEN_LOOP_END , ']', 'y', 'Y'], + [BRAINFUCK_TOKEN_BREAK , '#', '?', '!'], +] + +# 扁平化为 dict:{ 源字符 : 目标字符 } +_TRANSLATE = {} +for row in OTHER_CHAR_TABLE: + target, *sources = row + for s in sources: + _TRANSLATE[s] = target + + +def head_preprocessing_line(_line: str) -> (str, bool): + if _line.startswith("#!"): + return _line, True + return _line, False + +def preprocessing_line(_line: str) -> str: + tmp_strings = _line.split("//", 1) + remain_string = tmp_strings[0] + tmp_line = "" + for _char in remain_string: + tmp_char = _TRANSLATE.get(_char) + if tmp_char: + tmp_line += tmp_char + else: + tmp_line += _char + if len(tmp_strings) == 1: + return tmp_line + else: + annotation_string = tmp_strings[1] + return tmp_line + "//" + annotation_string + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description="letters to others") + parser.add_argument("-i", required=False, help="input file path") + parser.add_argument("-o", required=False, help="output file path") + args = parser.parse_args() + if args.i is not None: + input_file = args.i + if args.o is not None: + output_file = args.o + + lines: list[str] = [] + with io.open(input_file, "r", encoding="utf-8") as f: + for l in f: + lines.append(l) + + index: int = 0 + for i in range(len(lines)): + null, isHead = head_preprocessing_line(l) + if not isHead: + index = i + 1 + break + for i in range(index, len(lines)): + lines[i] = preprocessing_line(lines[i]) + + print("".join(lines)) + + with io.open(output_file, "w", encoding="utf-8") as f: + for l in lines: + f.write(l) diff --git a/tools/transLetter.sh b/tools/transLetter.sh new file mode 100755 index 0000000..e2b645a --- /dev/null +++ b/tools/transLetter.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +~/CLionProjects/brainfuck/tools/transLetter.py -i hello.bf -o output.bf \ No newline at end of file