Skip to content

Commit e403c2c

Browse files
committed
Add Brainfuck converter module and fix copy button trailing whitespace
- New Brainfuck converter module (Convert menu) - Text to Brainfuck conversion - Brainfuck interpreter with 30,000 cell tape - Bracket matching validation and safety limits - Fixed copy button to trim trailing whitespace - Updated CHANGELOG to v1.2.4
1 parent 0849a65 commit e403c2c

4 files changed

Lines changed: 355 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,32 @@
1+
## **v1.2.4** (2025-12-29)
2+
3+
### 🎉 Major Features
4+
- **Brainfuck Converter** - Convert text to Brainfuck code or execute Brainfuck programs
5+
- **Copy Button Fix** - Fixed trailing whitespace issue when copying strings
6+
7+
<details class="changelog-details">
8+
<summary><strong>📋 Detailed Changes</strong> (click to expand)</summary>
9+
<div class="changelog-panel">
10+
11+
#### New Modules
12+
- **Brainfuck Converter** (Convert menu)
13+
- Text → Brainfuck: Convert any text to Brainfuck code that outputs that text
14+
- Brainfuck → Text: Execute Brainfuck code and capture the output
15+
- Full Brainfuck interpreter with 30,000 cell tape
16+
- Bracket matching validation
17+
- Safety limits to prevent infinite loops
18+
- Statistics display (code length, compression ratio)
19+
20+
#### Bug Fixes
21+
- **Copy to Clipboard** - Fixed trailing whitespace being copied
22+
- Added `.trim()` to `copyToClipboard()` function
23+
- Ensures clean text copying without extra spaces
24+
25+
</div>
26+
</details>
27+
28+
---
29+
130
## **v1.2.3** (2025-12-29)
231

332
### 🎉 Major Features

includes/handlers_functional.php

Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ function getHandlerRegistry(): array {
3737
'currency' => 'handle_currency',
3838
'qrcode' => 'handle_qrcode',
3939
'regex' => 'handle_regex',
40+
'brainfuck' => 'handle_brainfuck',
4041
];
4142
}
4243

@@ -963,5 +964,247 @@ function handle_regex(array $req): string {
963964
return $output;
964965
}
965966

967+
/**
968+
* Handle Brainfuck conversion requests
969+
*
970+
* Converts text to Brainfuck code or executes Brainfuck code to produce text output.
971+
* Supports two modes:
972+
* - text2bf: Converts text to Brainfuck code that outputs that text
973+
* - bf2text: Executes Brainfuck code and captures the output
974+
*
975+
* @param array $req Request array containing: 'brainfuck' (input), 'mode' (text2bf|bf2text)
976+
* @return string Formatted HTML with conversion result or error message
977+
*/
978+
function handle_brainfuck(array $req): string {
979+
$input = req_get($req, 'brainfuck', '');
980+
$mode = req_get($req, 'mode', 'text2bf');
981+
982+
if (empty($input)) {
983+
return formatOutput("Input cannot be empty.", type: "danger");
984+
}
985+
986+
// Validate input length
987+
if (strlen($input) > 100000) {
988+
return formatOutput("Input must be at most 100,000 characters.", type: "danger");
989+
}
990+
991+
try {
992+
if ($mode === 'text2bf') {
993+
// Convert text to Brainfuck code
994+
$bfCode = textToBrainfuck($input);
995+
$output = "<div style='margin-bottom: 20px;'>";
996+
$output .= "<div style='margin-bottom: 15px;'><strong>Text → Brainfuck</strong></div>";
997+
$output .= copyableOutput($bfCode);
998+
$output .= "<div style='margin-top: 15px; padding: 12px; background-color: rgba(255, 193, 7, 0.15); border-radius: 0.5rem;'>";
999+
$output .= "<strong>📊 Stats:</strong><br>";
1000+
$output .= "Input length: <code>" . strlen($input) . " characters</code><br>";
1001+
$output .= "Brainfuck code length: <code>" . strlen($bfCode) . " characters</code><br>";
1002+
$output .= "Ratio: <code>" . number_format(strlen($bfCode) / strlen($input), 2) . "x</code>";
1003+
$output .= "</div>";
1004+
$output .= "</div>";
1005+
return $output;
1006+
} elseif ($mode === 'bf2text') {
1007+
// Execute Brainfuck code
1008+
$result = executeBrainfuck($input);
1009+
if ($result['success']) {
1010+
$output = "<div style='margin-bottom: 20px;'>";
1011+
$output .= "<div style='margin-bottom: 15px;'><strong>Brainfuck → Text</strong></div>";
1012+
$output .= copyableOutput($result['output']);
1013+
if (!empty($result['warnings'])) {
1014+
$output .= "<div style='margin-top: 15px; padding: 12px; background-color: rgba(255, 193, 7, 0.15); border-radius: 0.5rem;'>";
1015+
$output .= "<strong>⚠️ Warnings:</strong><br>";
1016+
$output .= htmlspecialchars($result['warnings']);
1017+
$output .= "</div>";
1018+
}
1019+
$output .= "</div>";
1020+
return $output;
1021+
} else {
1022+
return formatOutput("Brainfuck execution error: " . htmlspecialchars($result['error']), type: "danger");
1023+
}
1024+
} else {
1025+
return formatOutput("Invalid mode specified.", type: "danger");
1026+
}
1027+
} catch (Exception $e) {
1028+
return formatOutput("Error: " . htmlspecialchars($e->getMessage()), type: "danger");
1029+
}
1030+
}
1031+
1032+
/**
1033+
* Convert text to Brainfuck code
1034+
*
1035+
* Generates Brainfuck code that outputs the given text by setting each cell
1036+
* to the ASCII value of each character and outputting it.
1037+
*
1038+
* @param string $text The text to convert
1039+
* @return string Brainfuck code that outputs the text
1040+
*/
1041+
function textToBrainfuck(string $text): string {
1042+
$bfCode = '';
1043+
$currentValue = 0;
1044+
1045+
for ($i = 0; $i < strlen($text); $i++) {
1046+
$targetValue = ord($text[$i]);
1047+
$diff = $targetValue - $currentValue;
1048+
1049+
if ($diff == 0) {
1050+
// Already at target value, just output
1051+
$bfCode .= '.';
1052+
} else {
1053+
// Calculate the most efficient way to reach target
1054+
// Use absolute value and determine direction
1055+
if (abs($diff) <= 10) {
1056+
// Small difference: just increment/decrement
1057+
if ($diff > 0) {
1058+
$bfCode .= str_repeat('+', $diff);
1059+
} else {
1060+
$bfCode .= str_repeat('-', -$diff);
1061+
}
1062+
} else {
1063+
// Large difference: reset to 0 and build up
1064+
// Reset current value to 0
1065+
if ($currentValue > 0) {
1066+
$bfCode .= str_repeat('-', $currentValue);
1067+
}
1068+
$currentValue = 0;
1069+
1070+
// Build up to target
1071+
$bfCode .= str_repeat('+', $targetValue);
1072+
}
1073+
1074+
$bfCode .= '.';
1075+
$currentValue = $targetValue;
1076+
}
1077+
1078+
// Move to next cell for next character (optional optimization)
1079+
// For simplicity, we'll reuse the same cell
1080+
}
1081+
1082+
return $bfCode;
1083+
}
1084+
1085+
/**
1086+
* Execute Brainfuck code and capture output
1087+
*
1088+
* Implements a Brainfuck interpreter with:
1089+
* - 30,000 cell tape (standard Brainfuck)
1090+
* - Cell values 0-255 (wrapping)
1091+
* - Input support (reads from empty string if not provided)
1092+
* - Loop support with bracket matching
1093+
*
1094+
* @param string $code The Brainfuck code to execute
1095+
* @param string $input Optional input string for ',' commands
1096+
* @return array ['success' => bool, 'output' => string, 'error' => string|null, 'warnings' => string]
1097+
*/
1098+
function executeBrainfuck(string $code, string $input = ''): array {
1099+
$tape = array_fill(0, 30000, 0);
1100+
$pointer = 0;
1101+
$output = '';
1102+
$inputIndex = 0;
1103+
$codeIndex = 0;
1104+
$codeLength = strlen($code);
1105+
$maxSteps = 10000000; // Safety limit to prevent infinite loops
1106+
$stepCount = 0;
1107+
$warnings = '';
1108+
1109+
// Validate brackets are balanced
1110+
$bracketCount = 0;
1111+
for ($i = 0; $i < $codeLength; $i++) {
1112+
if ($code[$i] === '[') $bracketCount++;
1113+
if ($code[$i] === ']') $bracketCount--;
1114+
if ($bracketCount < 0) {
1115+
return ['success' => false, 'output' => '', 'error' => 'Unmatched closing bracket at position ' . $i, 'warnings' => ''];
1116+
}
1117+
}
1118+
if ($bracketCount !== 0) {
1119+
return ['success' => false, 'output' => '', 'error' => 'Unmatched opening brackets', 'warnings' => ''];
1120+
}
1121+
1122+
// Precompute bracket pairs for efficient loop handling
1123+
$bracketPairs = [];
1124+
$stack = [];
1125+
for ($i = 0; $i < $codeLength; $i++) {
1126+
if ($code[$i] === '[') {
1127+
$stack[] = $i;
1128+
} elseif ($code[$i] === ']') {
1129+
if (empty($stack)) {
1130+
return ['success' => false, 'output' => '', 'error' => 'Unmatched closing bracket at position ' . $i, 'warnings' => ''];
1131+
}
1132+
$start = array_pop($stack);
1133+
$bracketPairs[$start] = $i;
1134+
$bracketPairs[$i] = $start;
1135+
}
1136+
}
1137+
1138+
// Execute the code
1139+
while ($codeIndex < $codeLength && $stepCount < $maxSteps) {
1140+
$stepCount++;
1141+
$command = $code[$codeIndex];
1142+
1143+
switch ($command) {
1144+
case '>':
1145+
$pointer++;
1146+
if ($pointer >= 30000) {
1147+
$pointer = 0; // Wrap around
1148+
}
1149+
break;
1150+
1151+
case '<':
1152+
$pointer--;
1153+
if ($pointer < 0) {
1154+
$pointer = 29999; // Wrap around
1155+
}
1156+
break;
1157+
1158+
case '+':
1159+
$tape[$pointer] = ($tape[$pointer] + 1) % 256;
1160+
break;
1161+
1162+
case '-':
1163+
$tape[$pointer] = ($tape[$pointer] - 1 + 256) % 256;
1164+
break;
1165+
1166+
case '.':
1167+
$output .= chr($tape[$pointer]);
1168+
break;
1169+
1170+
case ',':
1171+
if ($inputIndex < strlen($input)) {
1172+
$tape[$pointer] = ord($input[$inputIndex]);
1173+
$inputIndex++;
1174+
} else {
1175+
$tape[$pointer] = 0; // EOF: set to 0
1176+
}
1177+
break;
1178+
1179+
case '[':
1180+
if ($tape[$pointer] == 0) {
1181+
// Jump to matching ']'
1182+
$codeIndex = $bracketPairs[$codeIndex];
1183+
}
1184+
break;
1185+
1186+
case ']':
1187+
if ($tape[$pointer] != 0) {
1188+
// Jump back to matching '['
1189+
$codeIndex = $bracketPairs[$codeIndex];
1190+
}
1191+
break;
1192+
}
1193+
1194+
$codeIndex++;
1195+
}
1196+
1197+
if ($stepCount >= $maxSteps) {
1198+
$warnings = "Execution stopped after {$maxSteps} steps (possible infinite loop).";
1199+
}
1200+
1201+
return [
1202+
'success' => true,
1203+
'output' => $output,
1204+
'error' => null,
1205+
'warnings' => $warnings
1206+
];
1207+
}
1208+
9661209
// Additional handlers can be added here following the same pattern...
9671210
// handle_ip, handle_urlencode, handle_htmlentities, handle_minify, etc.

includes/navbar.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,11 @@
123123
"formalName" => "Currency Converter",
124124
"icon" => icon('currency-exchange')
125125
],
126+
"brainfuck" => [
127+
"name" => "brainfuck",
128+
"formalName" => "Brainfuck Converter",
129+
"icon" => icon('code-slash')
130+
],
126131
],
127132
],
128133
"misc" => [

modules/brainfuck.php

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<div id="brainfuck" class="content">
2+
<div class="card card-primary">
3+
<h1 class="card-header">🧠 Brainfuck Converter</h1>
4+
<div class="card card-body">
5+
<div class="alert alert-info mb-4">
6+
<strong>ℹ️ About Brainfuck</strong><br>
7+
Brainfuck is an esoteric programming language with only 8 commands: <code>&gt;</code> <code>&lt;</code> <code>+</code> <code>-</code> <code>.</code> <code>,</code> <code>[</code> <code>]</code>.
8+
This tool converts between text and Brainfuck code, or executes Brainfuck code.
9+
</div>
10+
11+
<form class="form" action="gen.php" method="POST" data-action="brainfuck">
12+
<?php
13+
$brainfuckInput = $_POST['brainfuck'] ?? '';
14+
$brainfuckMode = $_POST['mode'] ?? 'text2bf';
15+
?>
16+
17+
<!-- Mode Selection -->
18+
<div class="card border-primary mb-4" style="background-color: rgba(13, 110, 253, 0.05);">
19+
<div class="card-header bg-primary text-white">
20+
<strong>⚙️ Conversion Mode</strong>
21+
</div>
22+
<div class="card-body">
23+
<div class="form-check mb-2">
24+
<input class="form-check-input" type="radio" name="mode" id="modeText2Bf" value="text2bf" <?= $brainfuckMode === 'text2bf' ? 'checked' : '' ?>>
25+
<label class="form-check-label" for="modeText2Bf">
26+
<strong>Text → Brainfuck</strong><br>
27+
<small class="text-muted">Convert text to Brainfuck code that outputs that text</small>
28+
</label>
29+
</div>
30+
<div class="form-check mb-2">
31+
<input class="form-check-input" type="radio" name="mode" id="modeBf2Text" value="bf2text" <?= $brainfuckMode === 'bf2text' ? 'checked' : '' ?>>
32+
<label class="form-check-label" for="modeBf2Text">
33+
<strong>Brainfuck → Text</strong><br>
34+
<small class="text-muted">Execute Brainfuck code and show the output</small>
35+
</label>
36+
</div>
37+
</div>
38+
</div>
39+
40+
<!-- Input Section -->
41+
<div class="mb-4">
42+
<label for="brainfuckInput" class="form-label mb-3">
43+
<strong style="font-size: 1.1rem;">
44+
<span id="inputLabel">Input Text</span>
45+
</strong>
46+
</label>
47+
<textarea name="brainfuck" class="form-control" id="brainfuckInput"
48+
style="min-height: 300px; resize: vertical; font-family: monospace; font-size: 0.95rem; border: 2px solid #495057;"
49+
placeholder="Enter text or Brainfuck code..." required><?= htmlspecialchars($brainfuckInput) ?></textarea>
50+
</div>
51+
52+
<!-- Action Button -->
53+
<div class="d-flex gap-3 mb-4">
54+
<?= submitBtn("brainfuck", "action", "🧠 Convert", "arrow-repeat", "lg") ?>
55+
</div>
56+
57+
<!-- Output Section -->
58+
<div class="responseDiv" id="brainfuckresponse" style="border: 2px solid #495057; padding: 20px; min-height: 200px; max-height: 500px; overflow-y: auto; background: linear-gradient(135deg, rgba(102, 16, 242, 0.1) 0%, rgba(108, 92, 231, 0.05) 100%); border-radius: 0.5rem; font-family: monospace; font-size: 0.95rem; white-space: pre-wrap; word-break: break-all;">
59+
<div style="opacity: 0.5; text-align: center; padding-top: 50px;">
60+
<div style="font-size: 3rem; margin-bottom: 10px;">🧠</div>
61+
<div>Conversion result will appear here...</div>
62+
</div>
63+
</div>
64+
</form>
65+
</div>
66+
</div>
67+
</div>
68+
69+
<script>
70+
// Update input label based on mode
71+
$('input[name="mode"]').change(function() {
72+
const mode = $(this).val();
73+
const label = mode === 'text2bf' ? 'Input Text' : 'Brainfuck Code';
74+
$('#inputLabel').text(label);
75+
const placeholder = mode === 'text2bf' ? 'Enter text to convert to Brainfuck...' : 'Enter Brainfuck code to execute...';
76+
$('#brainfuckInput').attr('placeholder', placeholder);
77+
});
78+
</script>

0 commit comments

Comments
 (0)