Compare commits
4 Commits
5af36d220e
...
6428a39791
| Author | SHA1 | Date | |
|---|---|---|---|
| 6428a39791 | |||
| a8c5660460 | |||
| 683d45b865 | |||
| 5fa2d16ae1 |
@ -10,6 +10,7 @@ include_directories(include)
|
|||||||
add_executable(bfuck
|
add_executable(bfuck
|
||||||
src/main.cpp
|
src/main.cpp
|
||||||
src/lex.cpp
|
src/lex.cpp
|
||||||
|
src/interpret.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
# target_link_libraries(lasm Boost::program_options)
|
# target_link_libraries(lasm Boost::program_options)
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.+++.
|
++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.
|
||||||
|
|||||||
53
include/interpret.hpp
Normal file
53
include/interpret.hpp
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
#ifndef __INTERPRET_H_
|
||||||
|
#define __INTERPRET_H_
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
#include <map>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Interpreter for a BrainFuck program
|
||||||
|
*/
|
||||||
|
class Interpreter {
|
||||||
|
private:
|
||||||
|
const std::map<char, std::function<void(Interpreter *)>> instruction_map = {
|
||||||
|
{'>', &Interpreter::_increment_pointer},
|
||||||
|
{'<', &Interpreter::_decrement_pointer},
|
||||||
|
{'+', &Interpreter::_increment_value},
|
||||||
|
{'-', &Interpreter::_decrement_value},
|
||||||
|
{'.', &Interpreter::_print_value},
|
||||||
|
{',', &Interpreter::_read_value},
|
||||||
|
{'[', &Interpreter::_jump_forward_if_zero},
|
||||||
|
{']', &Interpreter::_jump_backward_if_nonzero},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Runtime VM
|
||||||
|
bool running;
|
||||||
|
const std::string program;
|
||||||
|
uint32_t program_counter;
|
||||||
|
uint32_t pointer;
|
||||||
|
std::vector<uint8_t> memory;
|
||||||
|
|
||||||
|
// These functions implement the BrainFuck statements
|
||||||
|
auto _increment_pointer() -> void; // >
|
||||||
|
auto _decrement_pointer() -> void; // <
|
||||||
|
auto _increment_value() -> void; // +
|
||||||
|
auto _decrement_value() -> void; // -
|
||||||
|
auto _print_value() -> void; // .
|
||||||
|
auto _read_value() -> void; // ,
|
||||||
|
auto _jump_forward_if_zero() -> void; // [
|
||||||
|
auto _jump_backward_if_nonzero() -> void; // ]
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit Interpreter(std::string_view program);
|
||||||
|
|
||||||
|
// These functions control the interpreter flow
|
||||||
|
auto abort() -> void;
|
||||||
|
auto step() -> void;
|
||||||
|
auto run() -> void;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -11,6 +11,6 @@
|
|||||||
*/
|
*/
|
||||||
bool lex_brainfuck_file(const std::string &path, std::string &tokens);
|
bool lex_brainfuck_file(const std::string &path, std::string &tokens);
|
||||||
|
|
||||||
bool token_string_valid(std::string_view tokens);
|
bool program_valid(const std::string_view tokens);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
153
src/interpret.cpp
Normal file
153
src/interpret.cpp
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
#include "interpret.hpp"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
//
|
||||||
|
// Private functions
|
||||||
|
//
|
||||||
|
|
||||||
|
static const constexpr uint8_t MIN_VALUE = static_cast<uint8_t>(0);
|
||||||
|
static const constexpr uint8_t MAX_VALUE = static_cast<uint8_t>(-1);
|
||||||
|
|
||||||
|
auto Interpreter::_increment_pointer() -> void {
|
||||||
|
if (pointer == std::numeric_limits<uint32_t>::max()) {
|
||||||
|
std::cout << "Can't increment pointer: Max size reached!" << std::endl;
|
||||||
|
abort();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pointer++;
|
||||||
|
|
||||||
|
// If our currently allocated memory is exceeded, double it
|
||||||
|
if (pointer == memory.size()) {
|
||||||
|
memory.resize(memory.size() * 2, MIN_VALUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Interpreter::_decrement_pointer() -> void {
|
||||||
|
if (pointer == 0) {
|
||||||
|
std::cout << "Can't decrement pointer: Min size reached!" << std::endl;
|
||||||
|
abort();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pointer--;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Interpreter::_increment_value() -> void {
|
||||||
|
if (memory[pointer] == MAX_VALUE) {
|
||||||
|
std::cout << "Can't increment value: Max value reached!" << std::endl;
|
||||||
|
abort();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memory[pointer]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Interpreter::_decrement_value() -> void {
|
||||||
|
if (memory[pointer] == MIN_VALUE) {
|
||||||
|
std::cout << "Can't decrement value: Min value reached!" << std::endl;
|
||||||
|
abort();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memory[pointer]--;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Interpreter::_print_value() -> void {
|
||||||
|
std::cout << static_cast<uint8_t>(memory[pointer]) << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Interpreter::_read_value() -> void {
|
||||||
|
char input;
|
||||||
|
std::cin.get(input);
|
||||||
|
|
||||||
|
if (static_cast<uint8_t>(input) < MIN_VALUE || static_cast<uint8_t>(input) > MAX_VALUE) {
|
||||||
|
std::cout << "Invalid input! Must be element of ["
|
||||||
|
<< MIN_VALUE << ", " << MAX_VALUE
|
||||||
|
<< "]!" << std::endl;
|
||||||
|
abort();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memory[pointer] = static_cast<uint8_t>(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Interpreter::_jump_forward_if_zero() -> void {
|
||||||
|
if (memory[pointer] != MIN_VALUE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the corresponding ']'
|
||||||
|
int32_t jump_counter = 0;
|
||||||
|
for (uint32_t index = program_counter; index < program.size(); ++index) {
|
||||||
|
if (program[index] == '[') {
|
||||||
|
++jump_counter;
|
||||||
|
} else if (program[index] == ']') {
|
||||||
|
--jump_counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jump_counter == 0) {
|
||||||
|
program_counter = index; // PC gets incremented later
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Interpreter::_jump_backward_if_nonzero() -> void {
|
||||||
|
if (memory[pointer] == MIN_VALUE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the corresponding '['
|
||||||
|
int32_t jump_counter = 0;
|
||||||
|
for (uint32_t index = program_counter; index > 0; --index) {
|
||||||
|
if (program[index] == '[') {
|
||||||
|
++jump_counter;
|
||||||
|
} else if (program[index] == ']') {
|
||||||
|
--jump_counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jump_counter == 0) {
|
||||||
|
program_counter = index; // PC gets incremented later
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Public functions
|
||||||
|
//
|
||||||
|
|
||||||
|
Interpreter::Interpreter(const std::string_view program)
|
||||||
|
: running(false), program(program), program_counter(0), pointer(0), memory(1024, MIN_VALUE) {
|
||||||
|
std::cout << "Initialized interpreter with program size " << program.size() << "!\n" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Interpreter::abort() -> void {
|
||||||
|
running = false;
|
||||||
|
std::cout << "Program aborted!" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Interpreter::step() -> void {
|
||||||
|
if (program_counter == program.size()) {
|
||||||
|
std::cout << "Program finished!" << std::endl;
|
||||||
|
running = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char instruction = program[program_counter];
|
||||||
|
|
||||||
|
// Execute the correct action in the VM
|
||||||
|
instruction_map.at(instruction)(this);
|
||||||
|
|
||||||
|
++program_counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Interpreter::run() -> void {
|
||||||
|
running = true;
|
||||||
|
while (running) {
|
||||||
|
step();
|
||||||
|
}
|
||||||
|
}
|
||||||
27
src/lex.cpp
27
src/lex.cpp
@ -3,6 +3,7 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <stack>
|
||||||
|
|
||||||
bool lex_brainfuck_file(const std::string &path, std::string &tokens) {
|
bool lex_brainfuck_file(const std::string &path, std::string &tokens) {
|
||||||
if (!tokens.empty()) {
|
if (!tokens.empty()) {
|
||||||
@ -29,19 +30,35 @@ bool lex_brainfuck_file(const std::string &path, std::string &tokens) {
|
|||||||
}
|
}
|
||||||
input_stream.close();
|
input_stream.close();
|
||||||
|
|
||||||
if (!token_string_valid(tokens)) {
|
if (!program_valid(tokens)) {
|
||||||
std::cout << "Program is invalid!" << std::endl;
|
std::cout << "Program is invalid!" << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "Lexed BrainFuck program:\n" << tokens << std::endl;
|
std::cout << "Lexed valid BrainFuck program:\n" << tokens << std::endl;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool token_string_valid(const std::string_view tokens) {
|
bool program_valid(const std::string_view tokens) {
|
||||||
|
// Check tokens
|
||||||
std::string valid_tokens = "><+-.,[]";
|
std::string valid_tokens = "><+-.,[]";
|
||||||
|
bool tokens_valid = std::ranges::all_of(tokens.begin(), tokens.end(), [&valid_tokens](const char c){
|
||||||
return std::ranges::all_of(tokens.begin(), tokens.end(), [&valid_tokens](const char c){
|
|
||||||
return valid_tokens.find(c) != std::string::npos;
|
return valid_tokens.find(c) != std::string::npos;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Check bracket balance
|
||||||
|
std::stack<char> brackets;
|
||||||
|
for (const char c : tokens) {
|
||||||
|
if (c == '[') {
|
||||||
|
brackets.push('[');
|
||||||
|
} else if (c == ']') {
|
||||||
|
if (brackets.top() == '[') {
|
||||||
|
brackets.pop();
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tokens_valid && brackets.empty();
|
||||||
}
|
}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
#include "lex.hpp"
|
#include "lex.hpp"
|
||||||
|
#include "interpret.hpp"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
@ -9,7 +10,12 @@ int main(int argc, char **argv) {
|
|||||||
std::cout << "Running " << argv[1] << "...\n" << std::endl;
|
std::cout << "Running " << argv[1] << "...\n" << std::endl;
|
||||||
|
|
||||||
std::string tokens;
|
std::string tokens;
|
||||||
lex_brainfuck_file(argv[1], tokens);
|
if (!lex_brainfuck_file(argv[1], tokens)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Interpreter interpreter(tokens);
|
||||||
|
interpreter.run();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user