Created
August 19, 2023 18:15
-
-
Save langthom/9fa3357d064e81f2715ed1a4483b3c5d to your computer and use it in GitHub Desktop.
A toy parser for providing brainf*ck programs as string literals in C++.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* MIT License | |
* | |
* Copyright (c) 2023 Dr. Thomas Lang | |
* | |
* 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. | |
**/ | |
#ifndef BRAINFUCK_T__H | |
#define BRAINFUCK_T__H | |
#include <stack> | |
#include <string_view> | |
#include <vector> | |
namespace bft { | |
enum class OperationType { | |
INCREMENT, DECREMENT, | |
MOVE_RIGHT, MOVE_LEFT, | |
INPUT, OUTPUT, | |
LOOP | |
}; | |
class Operation; | |
class IncrementOperation; | |
class DecrementOperation; | |
class MoveLeftOperation; | |
class MoveRightOperation; | |
class InputOperation; | |
class OutputOperation; | |
class LoopOperation; | |
class OperationList; | |
namespace detail { | |
std::unique_ptr< Operation > parseNonLoopOperation(char identifier); | |
OperationList parseProgram(char const* program, std::size_t programLength); | |
} | |
} // namespace bft | |
// ================================================================================================ // | |
class bft::OperationList { | |
friend class bft::LoopOperation; | |
using OpList = std::vector< std::unique_ptr< bft::Operation > >; | |
protected: | |
OpList m_operations; | |
public: | |
using iterator = OpList::iterator; | |
using const_iterator = OpList::const_iterator; | |
OperationList() | |
: m_operations{} | |
{ | |
} | |
OperationList(OperationList&&) = default; | |
OperationList& operator=(OperationList&&) = default; | |
OperationList(OperationList const&) = delete; | |
OperationList& operator=(OperationList const&) = delete; | |
void reserve(std::size_t capacity) { | |
m_operations.reserve(capacity); | |
} | |
void addOperation(std::unique_ptr< bft::Operation >&& operation) { | |
m_operations.push_back(std::move(operation)); | |
} | |
iterator begin() noexcept { | |
return m_operations.begin(); | |
} | |
iterator end() noexcept { | |
return m_operations.end(); | |
} | |
const_iterator begin() const noexcept { | |
return m_operations.cbegin(); | |
} | |
const_iterator end() const noexcept { | |
return m_operations.cend(); | |
} | |
}; | |
class bft::Operation { | |
public: | |
bft::OperationType m_op; | |
}; | |
#define OPTYPE(clazz, type) public: clazz() { bft::Operation::m_op = type; } | |
class bft::IncrementOperation : public bft::Operation { OPTYPE(IncrementOperation, bft::OperationType::INCREMENT) }; | |
class bft::DecrementOperation : public bft::Operation { OPTYPE(DecrementOperation, bft::OperationType::DECREMENT) }; | |
class bft::MoveLeftOperation : public bft::Operation { OPTYPE(MoveLeftOperation, bft::OperationType::MOVE_LEFT) }; | |
class bft::MoveRightOperation : public bft::Operation { OPTYPE(MoveRightOperation, bft::OperationType::MOVE_RIGHT) }; | |
class bft::InputOperation : public bft::Operation { OPTYPE(InputOperation, bft::OperationType::INPUT) }; | |
class bft::OutputOperation : public bft::Operation { OPTYPE(OutputOperation, bft::OperationType::OUTPUT) }; | |
class bft::LoopOperation : public bft::Operation, public bft::OperationList { | |
public: | |
LoopOperation(bft::OperationList& ops) | |
: bft::OperationList() | |
{ | |
bft::Operation::m_op = bft::OperationType::LOOP; | |
m_operations.swap(ops.m_operations); | |
} | |
}; | |
#undef OPTYPE | |
// ================================================================================================ // | |
bft::OperationList operator""_bf(char const* program, std::size_t programLength) { | |
return bft::detail::parseProgram(program, programLength); | |
} | |
std::unique_ptr< bft::Operation > bft::detail::parseNonLoopOperation(char id) { | |
switch (id) { | |
case '>': return std::make_unique< bft::MoveRightOperation >(); break; | |
case '<': return std::make_unique< bft::MoveLeftOperation >(); break; | |
case '+': return std::make_unique< bft::IncrementOperation >(); break; | |
case '-': return std::make_unique< bft::DecrementOperation >(); break; | |
case '.': return std::make_unique< bft::OutputOperation >(); break; | |
case ',': return std::make_unique< bft::InputOperation >(); break; | |
} | |
/* Ignore all other characters. */ | |
} | |
bft::OperationList bft::detail::parseProgram(char const* program, std::size_t programLength) { | |
std::stack< bft::OperationList, std::vector< bft::OperationList > > scopes; | |
scopes.push(bft::OperationList{}); | |
char const* programEnd = program + programLength; | |
for (char const* instruction = program; instruction != programEnd; ++instruction) { | |
switch (*instruction) { | |
case '[': scopes.push(bft::OperationList{}); break; | |
case ']': { | |
auto loop = std::make_unique< bft::LoopOperation >( scopes.top() ); | |
scopes.pop(); | |
scopes.top().addOperation(std::move(loop)); | |
} break; | |
default: scopes.top().addOperation(bft::detail::parseNonLoopOperation(*instruction)); break; | |
} | |
} | |
if (scopes.size() != 1) { | |
throw std::invalid_argument("Some scope was not closed, invalid BF program."); | |
} | |
bft::OperationList parsedProgram = std::move(scopes.top()); | |
scopes.pop(); | |
return parsedProgram; | |
} | |
#endif // BRAINFUCK_T__H |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# MIT License | |
# | |
# Copyright (c) 2023 Dr. Thomas Lang | |
# | |
# 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. | |
cmake_minimum_required(VERSION 2.8) | |
project(BrainfuckT) | |
add_executable(BrainfuckT | |
src/BrainfuckT.h | |
src/Main.cpp | |
) | |
target_compile_features(BrainfuckT PRIVATE cxx_std_17) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* MIT License | |
* | |
* Copyright (c) 2023 Dr. Thomas Lang | |
* | |
* 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. | |
**/ | |
#include <cstdlib> | |
#include <iostream> | |
#include "BrainfuckT.h" | |
using Cells = std::vector< long long >; | |
using Pointer = Cells::iterator; | |
void Execute(Cells& cells, Pointer& position, bft::OperationList::const_iterator programStart, bft::OperationList::const_iterator programEnd) { | |
using OP = bft::OperationType; | |
auto executeNonLoop = [&](bft::Operation const& operation) { | |
switch (operation.m_op) { | |
case OP::INCREMENT: (*position)++; break; | |
case OP::DECREMENT: (*position)--; break; | |
case OP::MOVE_RIGHT: position++; break; | |
case OP::MOVE_LEFT: position--; break; | |
case OP::INPUT: { char c; std::cin >> c; *position = c; } break; | |
case OP::OUTPUT: std::cout << static_cast< char >(*position); break; | |
} | |
}; | |
for(auto operation = programStart; operation != programEnd; ++operation) { | |
if((*operation)->m_op == OP::LOOP) { | |
auto loop = static_cast< bft::LoopOperation* const >((*operation).get()); | |
while (*position > 0) { | |
Execute(cells, position, loop->begin(), loop->end()); | |
} | |
} else { | |
executeNonLoop(**operation); | |
} | |
} | |
} | |
void Execute(std::size_t numCells, bft::OperationList const& program) { | |
Cells cells(numCells, 0); | |
Pointer position = cells.begin(); | |
Execute(cells, position, program.begin(), program.end()); | |
} | |
void Print(bft::OperationList::const_iterator programStart, bft::OperationList::const_iterator programEnd, int indentSize = 0) { | |
using OP = bft::OperationType; | |
std::string indent = std::string(indentSize, ' '); | |
auto executeNonLoop = [&](bft::Operation const& operation) { | |
switch (operation.m_op) { | |
case OP::INCREMENT: std::cout << indent << "INCREMENT\n"; break; | |
case OP::DECREMENT: std::cout << indent << "DECREMENT\n"; break; | |
case OP::MOVE_RIGHT: std::cout << indent << "MOVE RIGHT\n"; break; | |
case OP::MOVE_LEFT: std::cout << indent << "MOVE LEFT\n"; break; | |
case OP::INPUT: std::cout << indent << "INPUT\n"; break; | |
case OP::OUTPUT: std::cout << indent << "OUTPUT\n"; break; | |
} | |
}; | |
for (auto operation = programStart; operation != programEnd; ++operation) { | |
if ((*operation)->m_op == OP::LOOP) { | |
std::cout << indent << "LOOP {\n"; | |
auto loop = static_cast< bft::LoopOperation* const >((*operation).get()); | |
Print(loop->begin(), loop->end(), indentSize + 2); | |
std::cout << indent << "}\n"; | |
} else { | |
executeNonLoop(**operation); | |
} | |
} | |
} | |
int main(int argc, char** argv) { | |
auto helloWorldProgram = "++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++."_bf; | |
Print(helloWorldProgram.begin(), helloWorldProgram.end()); | |
std::cout << "\n"; | |
Execute(100, helloWorldProgram); | |
auto fibonacci = "+++++++++++>+>>>>++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++<<<<<<[>[>>>>>>+>+<<<<<<<-]>>>>>>>[<<<<<<<+>>>>>>>-]<[>++++++++++[-<-[>>+>+<<<-]>>>[<<<+>>>-]+<[>[-]<[-]]>[<<[>>>+<<<-]>>[-]]<<]>>>[>>+>+<<<-]>>>[<<<+>>>-]+<[>[-]<[-]]>[<<+>>[-]]<<<<<<<]>>>>>[++++++++++++++++++++++++++++++++++++++++++++++++.[-]]++++++++++<[->-<]>++++++++++++++++++++++++++++++++++++++++++++++++.[-]<<<<<<<<<<<<[>>>+>+<<<<-]>>>>[<<<<+>>>>-]<-[>>.>.<<<[-]]<<[>>+>+<<<-]>>>[<<<+>>>-]<<[<+>-]>[<+>-]<<<-]"_bf; | |
Print(fibonacci.begin(), fibonacci.end()); | |
std::cout << "\n"; | |
Execute(500, fibonacci); | |
return EXIT_SUCCESS; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment