• EasyBf - EasyBrainfuck Interpreter
    38 replies, posted
[QUOTE=Eudoxia;17767045]I've been using Geany to compile this experimental interpreter, but DAMN it doesn't work:[/QUOTE] Do you mean it doesn't compile, or doesn't work? Because it compiles file with CodeBlocks, shows a little prompt and all
The commands, they don't respond ;_; For example, if I type: :q It should terminate the program (Obvious label is obvious), but it doesn't respond. [editline]05:18AM[/editline] Shit... The problem is in cin >> whatever. I wanted to include as many arguments as possible, but the program requires you to enter ALL of them to perform an action..
Then just do cin >> var when you need it (e.g. when keyword == "out"). Also, there is no argument[4], only argument[0-3]. [QUOTE=jivemasta;17759298]I'd say for chars you just do the letter. Then for strings you do "string". " would be an operator that automatically adds all the chars to the memory until it gets to a second " and then it adds a 0 to the end. So, for example: [code] "Hello" [/code] Would work exactly the same as: [code] H>e>l>l>o>0 [/code] But look less confusing. Then you could also have $ be a operator to print a string starting at a the current memory index until it gets to a 0 or null.[/QUOTE] By prefixing chars I wanted to differentiate between the char 1 (which is 31 in ASCII) and the number 1. Also, the current specs say, that the memory-index shall return to the first letter. I think it should stay where it is after insertion, so you can put more stuff behind the string.
[QUOTE=ZeekyHBomb;17771539]By prefixing chars I wanted to differentiate between the char 1 (which is 31 in ASCII) and the number 1.[/QUOTE] There could be an operator too I guess, for that sort of thing, I wasn't thinking of that. Maybe do ' for a single char as in '1 or '+. Then "string" for strings like I said before. I still think the automatic 0 after a string input is a good idea so you can do $ to print a string. One problem that comes to mind is if you append to a string, you would overwrite the 0 and could cause huge errors and massive headaches, which goes against the easy brainfuck idea.
One should also differentiate between user input as chars and integers. I'd say a single , is integer and ', would be asking for a char. Maybe EBf just shouldn't have real strings (e.g. end with 0). That datatype is just too complex to be built in and safe for such a language, I guess. EDIT: Maybe the programmer should just null-terminate the string him/herself. I mean, how many people are going to use this language anyways? Also, what shall happen if you get something like that: "@(a)" or "@()" or @ with incomplete or no parenthesis at all? Same question with !. And what happens if the specified cell is not available (e.g. @(55) or !(55), but there are only 10 cells)? And shouldn't a \ be put into memory with "'\\", not "'\"? (Using "EBf code" to not waste space by code-tags .. and because of lazyness to write them)
[QUOTE=ZeekyHBomb;17771539]Then just do cin >> var when you need it (e.g. when keyword == "out"). Also, there is no argument[4], only argument[0-3]. By prefixing chars I wanted to differentiate between the char 1 (which is 31 in ASCII) and the number 1. Also, the current specs say, that the memory-index shall return to the first letter. I think it should stay where it is after insertion, so you can put more stuff behind the string.[/QUOTE] Yeah, but to print, say, "hello", the user would have to type: print [press return] hello [press return] I wanted to make the interpreter a command.line interpreter :saddowns: Well, I'm guessing that will have to wait. Or I could code that loop I designed, but no, it's too boring.
And.. err.. what do you want? print hello [press return]? Then you could do [cpp]std::string line; std::cin >> line; std::vector<std::string> > commands(std::count(line.begin(), line.end(), ' ')+1); if(commands.size() == 1) { //only one command commands[0] = line; } else { std::string::size_type pos1 = 0, pos2 = line.find_first_of(' '); assert(pos2 != std::string::npos); for(std::vector::size_t i = 0; i != commands.length(); ++i) { commands[i] = line.substr(pos1, pos2); pos1 = pos2; pos2 = line.find_first_of(' '); } assert(pos1 != std::string::npos && pos2 == std::string::npos); }[/cpp] and each element in the vector will be one single command/argument.
[QUOTE=ZeekyHBomb;17775469]And.. err.. what do you want? print hello [press return]? Then you could do [cpp]std::string line; std::cin >> line; std::vector<std::string> > commands(std::count(line.begin(), line.end(), ' ')+1); if(commands.size() == 1) { //only one command commands[0] = line; } else { std::string::size_type pos1 = 0, pos2 = line.find_first_of(' '); assert(pos2 != std::string::npos); for(std::vector::size_t i = 0; i != commands.length(); ++i) { commands[i] = line.substr(pos1, pos2); pos1 = pos2; pos2 = line.find_first_of(' '); } assert(pos1 != std::string::npos && pos2 == std::string::npos); }[/cpp] and each element in the vector will be one single command/argument.[/QUOTE] Thanks, I'll see if I can implement that.
Alright, here's my crack at "EBF" parsing, it's not actually the same syntax since I haven't been keeping check on this thread, so I might just rename it. But here is the current syntax, along with the rest of the help file. [cpp] NAME: easybf - Easy Brainfuck Interpreter SYNOPSIS: easybf [OPTION]... [FILE]... DESCRIPTION -d Debug mode, verbose, outputs all important debug information -h Help, displays this message --length=###### Sets the maximum size of the memory to work on SYNTAX > Increase index position by 1 < Decrease index position by 1 0-9 Assigns the number to current memory a-zA-Z Assigns the integer value of the letter (or space) to current memory '##' Assigns ## (Any length number) to current memory +-/* Perform operation on current memory and next memory value @ Copy current memory to next position # Copy current memory to previous position . Print current memory contents as integer : Print current memory contents as character , Get single character input and store it in current memory | Get one number input (any length) and store in current memory ! Set index location to current memory value {} Loop contents until current memory = 0 [] Skip contents if current memory = 0 $ Store current memory ~*~ Anything between two ~'s is ignored completely % Copy stored memory to current memory NOTES Spaces are considered a valid input, so the program "1 >" would set the first memory block to the number 10 (ASCII for space). AUTHOR Written by Jake Woods REPORTING BUGS Report bugs to <vodurden@gmail.com> [/cpp] (cpp tags used to preserve indention) Notes: Currently it only works under linux, the code is 100% portable (as far as I am aware) but I haven't compiled a windows version yet nor have I included cross-compilation into my makefile. The current version was created using vim, make and g++ (4.3.3) [b]Edit:[/b] Slight mistake in the documentation, the ASCII code for space is not 10, it's 32. 10 is a new line. Examples: [cpp]H>e>l>o> >W>r>d>0!:>:>::>:>:>:<<:>>>:<<<<:>>>>>:[/cpp] Output: Hello World [cpp]E>n>t>e>r> >a>i>m>'10'>0! :>:>:>:>:>:~Enter~ >:<:>~a~ <<<<:>>>>>:>:<<<<<:>>>>>>:>:~time\n~ |>1<{.-<:>}~Counts down~[/cpp] Output: Enter a Time [User Number here] [Countdown from $USERNUMBER to 0] Here's the release link, enjoy and let me know about the bugs you will inevitably find. [url]http://www.electronicfiles.net/files/11823/easybf.rar[/url] For those who don't want to download the entire release, here's the code: [b]main.cpp[/b] [cpp] #include <iostream> #include <map> #include <cstring> #include <sstream> #include <vector> #include "FlagMachine.h" #include "EasyBrainfuckMachine.h" #include "tools.h" int main(int argc, char** argv) { FlagMachine flags("dlh", argc, argv); bool debug = (flags.getFlag('d') == FlagMachine::FLAG_TRUE) ? true : false; unsigned int length = (flags.getFlag('l') == FlagMachine::FLAG_FALSE) ? 200 : from_string<int>(flags.getFlag('l').c_str()); if(flags.getFlag('h') == FlagMachine::FLAG_TRUE) { std::cout << "NAME: easybf - Easy Brainfuck Interpreter" << std::endl << "SYNOPSIS: easybf [OPTION]... [FILE]..." << std::endl << std::endl << "DESCRIPTION" << std::endl << "\t-d\n\t\tDebug mode, verbose, outputs all important debug information" << std::endl << "\t-h\n\t\tHelp, displays this message" << std::endl << "\t--length=######\n\t\tSets the maximum size of the memory to work on" << std::endl << std::endl << "SYNTAX" << std::endl << "\t>\n\t\tIncrease index position by 1" << std::endl << "\t<\n\t\tDecrease index position by 1" << std::endl << "\t0-9\n\t\tAssigns the number to current memory" << std::endl << "\ta-zA-Z \n\t\tAssigns the integer value of the letter (or space) to current memory" << std::endl << "\t\'##\'\n\t\tAssigns ## (Any length number) to current memory" << std::endl << "\t+-/*\n\t\tPerform operation on current memory and next memory value" << std::endl << "\t@\n\t\tCopy current memory to next position" << std::endl << "\t#\n\t\tCopy current memory to previous position" << std::endl << "\t.\n\t\tPrint current memory contents as integer" << std::endl << "\t:\n\t\tPrint current memory contents as character" << std::endl << "\t,\n\t\tGet single character input and store it in current memory" << std::endl << "\t|\n\t\tGet one number input (any length) and store in current memory" << std::endl << "\t!\n\t\tSet index location to current memory value" << std::endl << "\t{}\n\t\tLoop contents until current memory = 0" << std::endl << "\t[]\n\t\tSkip contents if current memory = 0" << std::endl << "\t$\n\t\tStore current memory" << std::endl << "\t~*~\n\t\tAnything between two ~'s is ignored completely" << std::endl << "\t%\n\t\tCopy stored memory to current memory" << std::endl << std::endl << "NOTES" << std::endl << "\tSpaces are considered a valid input, so the program \"1 >\" would set the first memory\n\tblock to the number 10 (ASCII for space)." << std::endl << std::endl << "AUTHOR" << std::endl << "\tWritten by Jake Woods" << std::endl << std::endl << "REPORTING BUGS" << std::endl << "\tReport bugs to <vodurden@gmail.com>" << std::endl; return 0; } std::vector<std::string> programs; for(int arg = 1; arg < argc; ++arg) { if (argv[arg][0] != '-') { std::string program = argv[arg]; programs.push_back(program); } } EasyBrainfuckMachine interpreter(debug, length); for(std::vector<std::string>::iterator mIter = programs.begin(); mIter != programs.end(); ++mIter) { interpreter.execute((*mIter)); } if(programs.size() == 0) { std::cout << "No input files specified" << std::endl; } } [/cpp] [b]EasyBrainfuckMachine.h[/b] [cpp] #ifndef EASYBRAINFUCKMACHINE_H_INCLUDED #define EASYBRAINFUCKMACHINE_H_INCLUDED #include <fstream> #include <string> #include <vector> #include <iostream> #include "tools.h" class EasyBrainfuckMachine { private: bool m_debug; unsigned int m_indexPointer; unsigned int m_storage; std::string m_program; std::vector<unsigned int> m_dataspace; void debugMessage(const std::string& message); void parseLoop(const std::string& loop, std::string::iterator &commandIter); void parseCommand(const char command, std::string::iterator &commandIter); void loadProgram(const std::string& filepath); public: EasyBrainfuckMachine(bool debug=false, int length=200); void execute(const std::string& filepath); void printMemory(const int begin=0, const int end=20); }; #endif [/cpp] [b]EasyBrainfuckMachine.cpp[/b] [cpp] #include "EasyBrainfuckMachine.h" EasyBrainfuckMachine::EasyBrainfuckMachine(bool debug, int length) : m_debug(debug), m_dataspace(length), m_indexPointer(0) { } void EasyBrainfuckMachine::loadProgram(const std::string& filepath) { m_indexPointer = 0; m_program = ""; std::ifstream file(filepath.c_str()); if (!file) { std::cerr << "Unable to open file " << filepath << std::endl; } else { std::string command; while(std::getline(file,command)) { m_program += command; } for(std::vector<unsigned int>::iterator memory = m_dataspace.begin(); memory != m_dataspace.end(); ++memory) { (*memory) = 0; } } } void EasyBrainfuckMachine::execute(const std::string& filepath) { loadProgram(filepath); for(std::string::iterator commandIter = m_program.begin(); commandIter != m_program.end(); ++commandIter) { char command = (*commandIter); parseCommand(command, commandIter); } std::cout << std::endl; } void EasyBrainfuckMachine::debugMessage(const std::string& message) { if(m_debug) { std::cout << message << std::endl; int section = m_indexPointer / 4; printMemory(section*4, (section*4)+4); } } void EasyBrainfuckMachine::printMemory(const int begin, const int end) { int pos = begin; std::string numberline = ""; std::string memoryline = ""; for(std::vector<unsigned int>::iterator memory = m_dataspace.begin() + begin; memory != m_dataspace.begin() + (end+1); ++memory) { std::string decoration = " "; std::string curnumber = to_string<int>(*memory); if(pos == m_indexPointer) { decoration = " >"; } std::cout << decoration << pos << ": " << curnumber << std::endl; pos = pos + 1; } } void EasyBrainfuckMachine::parseCommand(const char command, std::string::iterator& commandIter) { if(command == '<') { m_indexPointer -= 1; debugMessage("Decreased Index"); } else if(command == '>') { m_indexPointer += 1; debugMessage("Increased Index"); } else if(command == ':') { debugMessage("Printing Character"); std::cout << char(m_dataspace[m_indexPointer]); } else if(command == '.') { debugMessage("Printing Number"); std::cout << int(m_dataspace[m_indexPointer]); } else if(command == '+') { m_dataspace[m_indexPointer] += m_dataspace[m_indexPointer+1]; debugMessage("Memory Addition"); } else if(command == '-') { m_dataspace[m_indexPointer] -= m_dataspace[m_indexPointer+1]; debugMessage("Memory Subtraction"); } else if(command == '/') //Rounds up to nearest whole number { float result = float(m_dataspace[m_indexPointer]) / float(m_dataspace[m_indexPointer+1]); m_dataspace[m_indexPointer] = static_cast<int>(result + (result > 0.0 ? 0.5 : -0.5)); debugMessage("Memory Division"); } else if(command == '$') { m_storage = m_dataspace[m_indexPointer]; debugMessage("Stored Memory"); } else if(command =='%') { m_dataspace[m_indexPointer] = m_storage; debugMessage("Copied to memory from storage"); } else if(command == '{') { debugMessage("Entering Loop"); std::string loopstr = ""; loopstr += command; do { commandIter++; loopstr += (*commandIter); } while((*commandIter) != '}'); parseLoop(loopstr, commandIter); } else if(command == '\'') { char curCommand; std::string input = ""; do { commandIter++; curCommand = (*commandIter); if(is_number(curCommand)) { input += curCommand; } else if(curCommand != '\'') { std::cout << "Error invalid number: " << curCommand << " found in number sequence: " << input << std::endl; } } while ( curCommand != '\'' ); m_dataspace[m_indexPointer] = from_string<int>(input); debugMessage("Set Memory (Number String)"); } else if(command == '[') { if ( m_dataspace[m_indexPointer] == 0 ) { char curCommand; int leftBracketCount = 1; do { commandIter++; curCommand = (*commandIter); if(curCommand == ']') { leftBracketCount -= 1; } else if(curCommand == '[') { leftBracketCount += 1; } } while(leftBracketCount > 0); debugMessage("Skipping Block"); } } else if(command == '~') { char curCommand; do { commandIter++; curCommand = (*commandIter); } while (curCommand != '~'); } else if(command == '*') { m_dataspace[m_indexPointer] *= m_dataspace[m_indexPointer+1]; debugMessage("Memory Multiplication"); } else if(command == '!') { m_indexPointer = m_dataspace[m_indexPointer]; debugMessage("Index Jump"); } else if(command == ',') { char data; std::cin >> data; if(is_number(data)) { m_dataspace[m_indexPointer] = (int(data) - int('0')); } else { m_dataspace[m_indexPointer] = int(data); } debugMessage("Input Value"); } else if(command == '|') { int data; std::cin >> data; m_dataspace[m_indexPointer] = data; } else if(command == '#') { m_dataspace[m_indexPointer-1] = m_dataspace[m_indexPointer]; debugMessage("Copied value backwards"); } else if(command == '@') { m_dataspace[m_indexPointer+1] = m_dataspace[m_indexPointer]; debugMessage("Copied value forwards"); } else if(is_valid_input(command)) { if(is_number(command)) { m_dataspace[m_indexPointer] = int(command) - int('0'); } else { m_dataspace[m_indexPointer] = int(command); } debugMessage("Set Memory"); } } void EasyBrainfuckMachine::parseLoop(const std::string& loop, std::string::iterator &commandIter) { std::string loopstr = loop; loopstr.erase(0, 1); size_t len = loopstr.length(); loopstr.erase(len-1, len); int stringIndex = 0; bool looping = true; while(looping) { parseCommand(loopstr[stringIndex], commandIter); stringIndex += 1; if(stringIndex > len) { if(m_dataspace[m_indexPointer] == 0) { looping = false; } stringIndex = 0; } } debugMessage("Exiting Loop"); } [/cpp] [b]FlagMachine.h[/b] [cpp] #ifndef FLAGMACHINE_H_INCLUDED #define FLAGMACHINE_H_INCLUDED #include <map> #include <string> #include <cstring> #include <iostream> class FlagMachine { private: std::string m_validflags; std::map<const char, std::string> m_flags; public: static const std::string FLAG_TRUE; static const std::string FLAG_FALSE; FlagMachine(const std::string& validflags, int argc, char** argv); ~FlagMachine(); void addValidFlags(const std::string& validflags); void parseFlagString(int argc, char** argv); void setFlag(const char flag, const std::string& data = FLAG_TRUE); void printFlags(); const std::string& getFlag(const char); }; #endif [/cpp] [b]FlagMachine.cpp[/b] [cpp] #include "FlagMachine.h" const std::string FlagMachine::FLAG_TRUE = "1"; const std::string FlagMachine::FLAG_FALSE = "0"; FlagMachine::FlagMachine(const std::string& validflags, int argc, char** argv) : m_validflags(validflags) { addValidFlags(validflags); parseFlagString(argc, argv); } FlagMachine::~FlagMachine() { } void FlagMachine::addValidFlags(const std::string& validflags) { for(std::string::iterator mIter = m_validflags.begin(); mIter != m_validflags.end(); ++mIter) { m_flags[(*mIter)] = FLAG_FALSE; } } void FlagMachine::parseFlagString(int argc, char** argv) { for(int arg = 1; arg < argc; ++arg) { if ( strlen( argv[arg] ) > 2 && (argv[arg][0] == '-' && argv[arg][1] == '-')) { std::string flagString(argv[arg]); flagString.erase(0, 2); size_t split = flagString.find('='); std::string lhs = flagString.substr(0, split); std::string rhs = flagString.substr(split+1, flagString.length()); setFlag(lhs[0], rhs); } else if(argv[arg][0] == '-') { std::string flagString(argv[arg]); flagString.erase(0, 1); for(std::string::iterator flag = flagString.begin(); flag != flagString.end(); ++ flag) { setFlag((*flag)); } } } } void FlagMachine::setFlag(const char flag, const std::string& data) { if(m_flags.find(flag) != m_flags.end()) { m_flags[flag] = data; } else { std::cout << "Invalid flag: " << flag << ", ignoring" << std::endl; } } void FlagMachine::printFlags() { for (std::map<const char, std::string>::iterator mIter = m_flags.begin(); mIter != m_flags.end(); ++mIter) { std::cout << mIter->first << ": " << mIter->second << std::endl; } } const std::string& FlagMachine::getFlag(const char flag) { return m_flags[flag]; } [/cpp] [b]tools.h[/b] [cpp] #ifndef TOOLS_H_INCLUDED #define TOOLS_H_INCLUDED #include <string> #include <sstream> template <class T> T from_string(const std::string& s) { std::string str(s); std::istringstream stream(str); T type; stream >> type; return type; } template <class T> std::string to_string(const T& v) { std::ostringstream stream; stream << v; std::string str = stream.str(); return str; } bool is_number(const char number); bool is_number(const std::string& number); bool is_valid_input(const char character); #endif [/cpp] [b]tools.cpp[/b] [cpp] #include "tools.h" bool is_number(const char number) { return(int(number) >= 48 && int(number <= 57)); } bool is_number(const std::string& number) { return 1; } bool is_valid_input(const char character) { if(int(character) >= 48 && int(character<= 57)) { return true; } else if(int(character) >= 65 && int(character) <= 90) { return true; } else if(int(character) >= 97 && int(character) <= 122) { return true; } else if(int(character) == 32) { return true; } return false; } [/cpp]
Sorry, you need to Log In to post a reply to this thread.