-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathlibbrainfunk.hpp
More file actions
271 lines (218 loc) · 8.86 KB
/
libbrainfunk.hpp
File metadata and controls
271 lines (218 loc) · 8.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
/* Neo_Chen's Brainfuck Interpreter — Modern C++20 Refactored */
#pragma once
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iostream>
#include <optional>
#include <sstream>
#include <span>
#include <string>
#include <string_view>
#include <variant>
#include <vector>
/* ================================================================
* Public type aliases (kept for backward compatibility)
* ================================================================ */
using addr_t = std::size_t;
using memory_t = std::uint8_t;
using offset_t = std::ptrdiff_t;
/* ================================================================
* Constants
* ================================================================ */
inline constexpr std::size_t MEMSIZE = 1 << 16; // Default memory size
/* I/O type numbers */
inline constexpr memory_t IO_IN = 0;
inline constexpr memory_t IO_OUT = 1;
/* Bitcode format constants (exported for external use) */
inline constexpr int BITCODE_FORMAT_C = 0;
inline constexpr int BITCODE_FORMAT_PLAIN = 1;
/* ================================================================
* Opcode – strongly typed enum (was: `enum opcodes`)
* ================================================================ */
enum class Opcode : std::uint8_t {
X, // (none) empty / invalid
A, // (imm) add
S, // (imm) set
MUL, // (dual) multiply-add
F, // (offset) scan-forward
M, // (offset) move pointer
JE, // (offset) jump-if-equal-zero
JN, // (offset) jump-if-not-zero
IO, // (imm) input / output
H, // (none) halt
_COUNT // sentinel – total number of opcodes
};
/* Friendly names – must match Opcode ordering */
inline constexpr const char* opcode_name(std::size_t i) noexcept {
constexpr const char* names[] = {
"X", "A", "S", "MUL", "F", "M", "JE", "JN", "IO", "H"
};
return (i < std::size(names)) ? names[i] : "???";
}
/* Operand type classifier:
* N – none, O – offset, M – dual (mul + offset), I – immediate */
inline constexpr char opcode_operand_type(std::size_t i) noexcept {
constexpr char types[] = {
'N', /* X */
'I', /* A */
'I', /* S */
'M', /* MUL */
'O', /* F */
'O', /* M */
'O', /* JE */
'O', /* JN */
'I', /* IO */
'N' /* H */
};
return (i < std::size(types)) ? types[i] : '?';
}
/* ================================================================
* Helper: operand storage
* ================================================================ */
struct DualOperand {
memory_t mul; // multiplication factor (mod 256)
offset_t offset; // pointer offset
};
using Operand = std::variant<std::monostate, memory_t, offset_t, DualOperand>;
/* ================================================================
* Exception
* ================================================================ */
class BrainfunkException : public std::exception {
public:
explicit BrainfunkException(const std::string& msg)
: msg_(msg) {}
[[nodiscard]] const char* what() const noexcept override {
return msg_.c_str();
}
private:
std::string msg_;
};
/* ================================================================
* Bitcode – single intermediate instruction
* ================================================================ */
class Bitcode {
public:
/* ---- constructors ---- */
Bitcode() noexcept
: opcode_(Opcode::X), operand_(std::monostate{}) {}
explicit Bitcode(Opcode op) // no operand
: opcode_(op), operand_(std::monostate{}) {}
Bitcode(Opcode op, memory_t imm) // immediate operand
: opcode_(op), operand_(imm) {}
Bitcode(Opcode op, offset_t off) // offset operand
: opcode_(op), operand_(off) {}
Bitcode(Opcode op, memory_t mul, offset_t off) // dual operand
: opcode_(op), operand_(DualOperand{mul, off}) {}
/* ---- accessors ---- */
[[nodiscard]] Opcode opcode() const noexcept { return opcode_; }
/* ---- formatting ---- */
[[nodiscard]] std::string to_string(int format = BITCODE_FORMAT_PLAIN) const;
/* ---- execution ---- */
/* Returns false when halt instruction is reached. */
[[nodiscard]] bool execute(
std::span<memory_t> memory,
addr_t& pc,
addr_t& ptr,
std::istream& is = std::cin,
std::ostream& os = std::cout
) const;
private:
Opcode opcode_;
Operand operand_;
};
/* ================================================================
* Brainfunk – the main interpreter / compiler
* ================================================================ */
enum class Format { BIT, C };
class Brainfunk {
public:
explicit Brainfunk(std::size_t memsize = MEMSIZE);
~Brainfunk();
Brainfunk(const Brainfunk&) = delete;
Brainfunk& operator=(const Brainfunk&) = delete;
Brainfunk(Brainfunk&&) = default;
Brainfunk& operator=(Brainfunk&&) = default;
/* Translate Brainfuck source code into internal bitcode */
void translate(std::string_view code);
/* Execute the translated program */
void run(std::istream& is = std::cin, std::ostream& os = std::cout);
/* Execute exactly one bitcode instruction. Returns false on halt. */
[[nodiscard]] bool step(std::istream& is = std::cin, std::ostream& os = std::cout);
/* Reset execution state (memory + pc + ptr) without clearing bitcode */
void reset_state();
/* Reset everything */
void clear();
/* ---- const accessors for TUI ---- */
[[nodiscard]] addr_t pc() const noexcept { return pc_; }
[[nodiscard]] addr_t ptr() const noexcept { return ptr_; }
[[nodiscard]] const std::vector<memory_t>& memory() const noexcept { return memory_; }
[[nodiscard]] const std::vector<Bitcode>& bitcode() const noexcept { return bitcode_; }
/* Dump bitcode to stream */
void dump(std::ostream& os, Format format = Format::BIT) const;
private:
addr_t ptr_ = 0;
addr_t pc_ = 0;
std::vector<memory_t> memory_;
std::vector<Bitcode> bitcode_;
/* ---- internal helpers for translate() ---- */
/* Try to parse a mul-offset loop like [->>++++<<] or [>>++++<<-].
* On success, appends bitcode and returns true. */
bool try_parse_mul_offset_loop_(std::string_view& code);
/* Try to parse a scan loop like [><] (F instruction). */
bool try_parse_scan_loop_(std::string_view& code);
/* Try to parse a set-to-zero loop like [+-] or [-+]. */
bool try_parse_set_zero_loop_(std::string_view& code);
};
/* ================================================================
* Free helpers (exposed for testing / other translation units)
* ================================================================ */
/* Count net occurrences of two complementary characters over the
* whole string. `sym[0]` counts positive, `sym[1]` counts negative.
* Requires `sym.size() == 2`. */
[[nodiscard]] constexpr std::ptrdiff_t count_net(std::string_view text,
std::string_view sym) noexcept {
std::ptrdiff_t ctr = 0;
for (auto ch : text) {
if (ch == sym[0]) ++ctr;
else if (ch == sym[1]) --ctr;
}
return ctr;
}
/* Scan the longest leading run of `sym[0]`/`sym[1]` characters.
* Returns the net count and the number of consumed characters. */
struct LeadingRun {
std::ptrdiff_t net; // net count of (+)-minus(-)
std::size_t length; // characters consumed
};
[[nodiscard]] constexpr LeadingRun leading_run(std::string_view text,
std::string_view sym) noexcept {
std::ptrdiff_t ctr = 0;
std::size_t i = 0;
for (; i < text.size(); ++i) {
if (text[i] == sym[0]) { ++ctr; }
else if (text[i] == sym[1]) { --ctr; }
else { break; }
}
return {ctr, i};
}
/* Modular-wrapping address arithmetic. */
[[nodiscard]] constexpr addr_t wrap_addr(addr_t address,
addr_t size) noexcept {
return (address < size) ? address : address % size;
}
[[nodiscard]] constexpr addr_t wrap_offset(addr_t address,
offset_t offset,
addr_t size) noexcept {
if (size == 0) return 0;
const auto base = address % size;
if (offset >= 0) {
return (base + (static_cast<addr_t>(offset) % size)) % size;
}
const auto backward = static_cast<addr_t>(-(offset + 1)) + 1;
return (base + size - (backward % size)) % size;
}