@@ -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.
0 commit comments