diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cfd336d --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.idea/ + +env/ +**/__pycache__/ + +test.fbf \ No newline at end of file diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md new file mode 100644 index 0000000..3315752 --- /dev/null +++ b/DOCUMENTATION.md @@ -0,0 +1,87 @@ +#Documentation + +Hi, if you are reading this, you want to learn how to use FuckBrainFuck. + +First you need to know the classic BrainFuck syntax. +You should read this course: +- [Brainfuck Language (English) (by roachhd)](https://gist.github.com/roachhd/dce54bec8ba55fb17d3a) +- [Brainfuck Language (French) (by Astremy)](https://cdn.discordapp.com/attachments/815331771197030441/824402769397940234/brainfuck.pdf) + + +Now I'm going to explain how to use the new implementations of FuckBrainFuck. + +###FuckBrainFuck already offers: + +- "Safe" comments +- Functions + + +## Comments +Safe comments allow you to use any characters in the comments as opposed to the classic brainfuck comments +where you can't use the characters used by the language + +You can write comments between two `#` characters. + +Example: +``` +,# This is a safe comment. I can use <>+-[],. and +others characters of FuckBrainFuck here like :; +I can also skip lines. This program returns the character of the input #. +``` + + +## Functions +Functions are a way to use the same code multiple times. +Functions are stored in a different array than the classic BrainFuck array. + +You can define functions between `:` and `;`. To call it, use `x`. + +You can use recursive functions. +You can also define functions inside functions, +but you have to move the pointer to right or left +because you can't have multiple functions in the same cell. + + +Example: +``` +:,++.; # This functions asks the user and adds two to the cell and then prints the result. # +x # Execute the function. # +``` + +Nested Functions: +``` +: + , + >: + <++ + ; # This function go to left and adds 2 to the value of the cell.# + x. # Execute the function and display the result. + After executing the function, we are one square ahead. + Here, the function is obviously useless since we use it only once.# +; +x +``` +_In the example above, I indented the code. It makes it more readable, doesn't it?_ + +You can see that before declaring the second function, I moved my pointer to the right. +This allows me to avoid unexpected behavior. + + +Recursive Functions: +``` +: + [[->+>[->+>+<<]>>[-<<+>>]<<<<]>>[-]>[-<+>]<<[-<+>]<-x[-]] +; # This function calculates the factorial of a number. # +>>+<< ++++++ # Here is the input for the recursive function. # +x>>. # The output will be "x" because 120 is the ASCII value of "x". + The output is always the remainder of the factorial divided by 255. # +``` +_Thanks Astremy for the code!_ + + +I think you now know all about the FuckBrainFuck's syntax. +Thanks for reading the documentation. +Maybe the people in [the BrainFuck channel on Graven's server](https://discord.gg/DTtXYNc3ct) +will help you with FuckBrainFuck (in French). +I hope everything goes well for you, goodbye \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..0d6506d --- /dev/null +++ b/README.md @@ -0,0 +1,183 @@ +
+ + +[![Contributors][contributors-shield]][contributors-url] +[![Forks][forks-shield]][forks-url] +[![Stargazers][stars-shield]][stars-url] +[![Issues][issues-shield]][issues-url] +[![MIT License][license-shield]][license-url] + + + + +
+
+ + Logo + + +

FuckBrainFuck

+

+ Improved BrainFuck +
+ Explore the docs » +
+
+ Report Bug + · + Request Feature +

+
+ + + + +
+ Table of Contents +
    +
  1. + About The Project + +
  2. +
  3. + Getting Started + +
  4. +
  5. Usage
  6. +
  7. Contributing
  8. +
  9. License
  10. +
  11. Contact
  12. +
  13. Acknowledgments
  14. +
+
+ + + + +## About The Project + +It all started on the Discord server of Graven, on 26th June 2022. +It was a joke that ended up on this shit... + +So FuckBrainFuck was born. It is an improvement of the classic BrainFuck. + +We have already implemented: + +- Functions +- "Safe" comments + +

(back to top)

+ + + +### Built With + +* [![Python][python-shield]][python-url] + +

(back to top)

+ + + + +## Getting Started + +### Prerequisites + +You may need to install Python 3.6 or higher. + +### Installation + + + +1. Clone the repo + ```sh + git clone https://github.com/Sellig6792/FuckBrainFuck.git + ``` + + +

(back to top)

+ + + + +## Usage + +Soon... + +[//]: # (

(back to top)

) + + + +## Contributing + +Contributions are what make the open source community such an amazing place to learn, inspire, and create. +Any contributions you make are **greatly appreciated**. + +If you have a suggestion that would make this better, please fork the repo and create a pull request. +You can also simply open an issue with the tag "enhancement". +Don't forget to give the project a star! Thanks again! + +1. Fork the Project +2. Create your Feature Branch (`git flow feature start [feature name]`) +3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`) +4. Push to the Branch (`git flow feature publish [feature name]`) +5. Open a Pull Request on the develop branch + +

(back to top)

+ + + + +## License + +Distributed under the MIT License. See `LICENSE.txt` for more information. + +

(back to top)

+ + + + +## Contact + +Your Name - [@Sellig6792](https://twitter.com/Sellig6792) - sellig6792@gmail.com + +Project Link: [https://github.com/Sellig6792/FuckBrainFuck](https://github.com/Sellig6792/FuckBrainFuck) + +

(back to top)

+ + + + +## Acknowledgments + +* [Wikipedia - Brainfuck][wikipedia-brainfuck-url] +* [Astremy - Brainfuck Course (French)][astremy-brainfuck-pdf] +

(back to top)

+ + + + + +[contributors-shield]: https://img.shields.io/github/contributors/Sellig6792/FuckBrainFuck.svg?style=for-the-badge +[contributors-url]: https://github.com/Sellig6792/FuckBrainFuck/graphs/contributors +[forks-shield]: https://img.shields.io/github/forks/Sellig6792/FuckBrainFuck.svg?style=for-the-badge +[forks-url]: https://github.com/Sellig6792/FuckBrainFuck/network/members +[stars-shield]: https://img.shields.io/github/stars/Sellig6792/FuckBrainFuck.svg?style=for-the-badge +[stars-url]: https://github.com/Sellig6792/FuckBrainFuck/stargazers +[issues-shield]: https://img.shields.io/github/issues/Sellig6792/FuckBrainFuck.svg?style=for-the-badge +[issues-url]: https://github.com/Sellig6792/FuckBrainFuck/issues +[license-shield]: https://img.shields.io/github/license/Sellig6792/FuckBrainFuck.svg?style=for-the-badge +[license-url]: https://github.com/Sellig6792/FuckBrainFuck/blob/master/LICENSE.txt +[Python-url]: https://www.python.org/ +[Python-shield]: https://img.shields.io/badge/-Python-black.svg?style=for-the-badge&logo=python&colorB=555 +[BrainFuck-url]: https://en.wikipedia.org/wiki/Brainfuck +[BrainFuck-shield]: https://img.shields.io/badge/-BrainFuck-black.svg?style=for-the-badge&logo=brainfuck&colorB=555 + +[graven-discord-url]: https://discord.gg/graven +[astremy-brainfuck-pdf]: https://cdn.discordapp.com/attachments/815331771197030441/824402769397940234/brainfuck.pdf +[wikipedia-brainfuck-url]: https://en.wikipedia.org/wiki/Brainfuck diff --git a/assets/logo.png b/assets/logo.png new file mode 100644 index 0000000..b437e08 Binary files /dev/null and b/assets/logo.png differ diff --git a/fbf.py b/fbf.py new file mode 100644 index 0000000..872b10a --- /dev/null +++ b/fbf.py @@ -0,0 +1,38 @@ +import os +import sys +import argparse + +from interpreter import Interpreter + + +def get_usage() -> str: + return "fbf (path) [-h] [-c COMPILE] [-x EXECUTE]" + + +def get_args() -> argparse.Namespace: + if not sys.argv[1].startswith('-'): + path = sys.argv[1] + del sys.argv[1] + else: + path = None + + parser = argparse.ArgumentParser(prog="FuckBrainFuck", description="The improved version of BrainFuck", + usage=get_usage()) + + parser.add_argument("-c", "--compile", help="Compile the file") + parser.add_argument("-x", "--execute", help="Execute a string") + + _args = parser.parse_args() + setattr(_args, 'path', path) + return _args + + +if __name__ == '__main__': + args = get_args() + + if args.path: + with open(args.path, mode='r+') as file: + code = ''.join(file.readlines()) + Interpreter(code)() + if args.execute: + Interpreter(args.execute)() diff --git a/interpreter/__init__.py b/interpreter/__init__.py new file mode 100644 index 0000000..c893c2c --- /dev/null +++ b/interpreter/__init__.py @@ -0,0 +1,206 @@ +class Interpreter: + def __init__(self, + code: str, + pointer: int = 0, + + stdin: str = None, + stdout: str = '', + + stack: list = None, + cells: list[int] = None, + + function_cells: list[str] = None, + + is_function: bool = False, + ): + self.is_function = is_function + self.i = 0 + self.code = code.replace('\n', '').replace('\t', '').replace(' ', '') + if ',' in self.code and not stdin: + stdin = [*input()] + + self.stack = stack or [] + self.cells = cells or [0] * 30000 + + self.function_cells = function_cells or [''] * 30000 + self.opened_functions = [] + + self.stdin = stdin + self.stdout = stdout + + self.pointer = pointer + + self.writing_function: bool = False + self.in_comment: bool = False + + @property + def cell(self): + return self.cells[self.pointer] + + @cell.setter + def cell(self, value): + self.cells[self.pointer] = value + + @property + def function_cell(self): + return self.function_cells[self.pointer] + + @function_cell.setter + def function_cell(self, value): + self.function_cells[self.pointer] = value + + def __call__(self, *args, **kwargs) -> dict: + while self.i < len(self.code): + instruction = self.code[self.i] + + if instruction == '#': + self.i += 1 + self.in_comment = not self.in_comment + continue + if self.in_comment: + self.i += 1 + continue + + if self.writing_function: + if instruction == ';': + # Close the last function opened. If there is only one function opened left, end the function. + self.opened_functions.pop() + if len(self.opened_functions) == 0: + self.end_function() + self.i += 1 + continue + + self.function_cell += instruction + if instruction == ':': + self.opened_functions.append(self.i) + self.i += 1 + continue + + if instruction == '>': + self.right() + if instruction == '<': + self.left() + + if instruction == '+': + self.add() + if instruction == '-': + self.sub() + + if instruction == '.': + self.outp() + if instruction == ',': + self.inp() + + if instruction == '[': + self.start_loop() + if instruction == ']': + self.end_loop() + + if instruction == ':': + self.start_function() + + if instruction == 'x': + self.execute() + self.i += 1 + + if self.is_function: + return { + 'stdin': self.stdin, + 'stdout': self.stdout, + 'cells': self.cells, + 'function_cells': self.function_cells, + 'stack': self.stack, + 'pointer': self.pointer + } + else: + print(self.stdout) + + def right(self): + """ + Decrement the data pointer (to point to the next cell to the left). + """ + self.pointer = (self.pointer + 1) % 30000 + + def left(self): + """ + Decrement the data pointer (to point to the next cell to the left). + """ + self.pointer = (self.pointer - 1) % 30000 + + def add(self): + """ + Increment (increase by one) the byte at the data pointer. + """ + self.cell = (self.cell + 1) % 255 + + def sub(self): + """ + Decrement (decrease by one) the byte at the data pointer. + """ + self.cell = (self.cell - 1) % 255 + + def outp(self): + """ + Output the byte at the data pointer. + """ + self.stdout += chr(self.cell) + + def inp(self): + """ + Accept one byte of input, storing its value in the byte at the data pointer. + """ + char, *self.stdin = self.stdin + self.cell = ord(char) + + def start_loop(self): + """ + 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 ] command. + """ + if self.cell: + self.stack.append(self.i) + else: + n = -1 + while self.code[self.i] != ']' or n: + instruction = self.code[self.i] + if instruction == '[': + n += 1 + elif instruction == ']': + n -= 1 + self.i += 1 + + def end_loop(self): + """ + 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 [ command. + """ + self.i = self.stack.pop() - 1 + + def execute(self): + """ + Execute the function at the data pointer. + """ + function = Interpreter( + self.function_cell, self.pointer, + self.stdin, self.stdout, + self.stack, self.cells, + self.function_cells, True + ) + args = function() + self.__dict__.update(args) + + def start_function(self): + """ + Start writing a function. + """ + self.function_cell = '' + self.opened_functions.append(self.i) + self.writing_function = True + + def end_function(self): + """ + End writing a function + """ + self.writing_function = False