Skip to content

Instantly share code, notes, and snippets.

@langthom
Created August 19, 2023 18:15
Show Gist options
  • Save langthom/9fa3357d064e81f2715ed1a4483b3c5d to your computer and use it in GitHub Desktop.
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++.
/*
* 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
# 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)
/*
* 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