work async, js compiler
This commit is contained in:
		
							parent
							
								
									4cb534838a
								
							
						
					
					
						commit
						c6d280dfd8
					
				
					 4 changed files with 228 additions and 333 deletions
				
			
		|  | @ -1,218 +0,0 @@ | |||
| import sys | ||||
| import re | ||||
| import time | ||||
| 
 | ||||
| class Interpreter: | ||||
|     def __init__(self, commands_per_second=1_000_000, debug=False, comma_standart_input=True): | ||||
|         self.debug = debug | ||||
|         self.commands = ['+', '-', '>', '<', '.', ',', '[', ']'] | ||||
|         self.mp = 0 # memory pointer | ||||
|         self.pc = 0 # program counter | ||||
|         self.data = [0] * 2 | ||||
|         ''' | ||||
|         chceme mít k dispozici i konec datového pole, (abychom mohli zavolat '<' na 0) | ||||
|         -> konec pole budeme mít jako self.data_end a kde jsme, poznáme podle self.is_at_end... | ||||
|         pak můžeme podle potřeby zvětšovat pole ze začátku nebo z konce | ||||
| 
 | ||||
|         !poznámka autora: dalo by se také udělat s jedním polem, tak že bychom 'posouvali' index 0... | ||||
|         to mě ale nenapadlo, když jsem to psal, tak jsem to udělal takhle... | ||||
|         ''' | ||||
|         self.data_end = [0] * 2 # tail is reversed | ||||
|         self.is_at_end = False | ||||
|         self.code = "" | ||||
|         self.commands_per_second = commands_per_second | ||||
|         self.MAX = 2**32 - 1 | ||||
|          | ||||
|         self.user_input = "" | ||||
|         self.user_input_index = 0 | ||||
|         self.saved_output = "" | ||||
|         self.comma_standart_input = comma_standart_input | ||||
|         self.last_step = 0 | ||||
| 
 | ||||
|     def our_i32(self, value): | ||||
|         if value == -1: | ||||
|             return self.MAX | ||||
|         if value == self.MAX + 1: | ||||
|             return 0 | ||||
|         return value | ||||
| 
 | ||||
|     def load_code_from_input(self): | ||||
|         def expand_numbers(match): | ||||
|             count = int(match.group(1)) | ||||
|             char = match.group(2) | ||||
|             return char * count | ||||
| 
 | ||||
|         self.code = "" | ||||
|         code_filename = sys.argv[1] | ||||
|         with open(code_filename) as code_file: | ||||
|             for line in code_file: | ||||
|                 line = line.strip() | ||||
|                 line = line.split('//')[0].strip() | ||||
|                 line = re.sub(r'(\d+)(.)', expand_numbers, line)  | ||||
|                 line = ''.join(filter(lambda c: c in self.commands, line)) | ||||
|                 self.code += line | ||||
|      | ||||
|     def load_code_from_string(self, code): | ||||
|         def expand_numbers(match): | ||||
|             count = int(match.group(1)) | ||||
|             char = match.group(2) | ||||
|             return char * count | ||||
| 
 | ||||
|         self.code = "" | ||||
|         code = code.strip() | ||||
|         code = re.sub(r'(\d+)(.)', expand_numbers, code)  | ||||
|         code = ''.join(filter(lambda c: c in self.commands, code)) | ||||
|         self.code += code | ||||
|      | ||||
|     def print_overwrite(self, message, prev_len=[0]): | ||||
|         sys.stdout.write('\r' + message + ' ' * max(prev_len[0] - len(message), 0)) | ||||
|         sys.stdout.flush() | ||||
|         prev_len[0] = len(message) | ||||
| 
 | ||||
|     def print_data(self, steps): | ||||
|         to_print = "step: " + str(steps) + " " | ||||
|         to_print += "mp: " + str(self.mp) + " | " | ||||
|         for i in range(len(self.data)): | ||||
|             to_print += str(self.data[i]) + " " | ||||
|         to_print += "|" | ||||
|         to_print += " end: | " | ||||
|         for i in range(len(self.data_end)).__reversed__(): | ||||
|             to_print += str(self.data_end[i]) + " " | ||||
|         to_print += "|" | ||||
|         self.print_overwrite(to_print) | ||||
|         return to_print | ||||
| 
 | ||||
|     def get_value_at_mp(self): | ||||
|         if not self.is_at_end: | ||||
|             return self.data[self.mp] | ||||
|         else: | ||||
|             return self.data_end[self.MAX - self.mp] | ||||
|          | ||||
|     def set_value_at_mp(self, value): | ||||
|         if not self.is_at_end: | ||||
|             self.data[self.mp] = value | ||||
|         else: | ||||
|             self.data_end[self.MAX - self.mp] = value | ||||
| 
 | ||||
|     def handle_plus(self): | ||||
|         self.set_value_at_mp(self.our_i32(self.get_value_at_mp() + 1)) | ||||
| 
 | ||||
|     def handle_minus(self): | ||||
|         self.set_value_at_mp(self.our_i32(self.get_value_at_mp() - 1)) | ||||
|      | ||||
|     def handle_greater(self): | ||||
|         temp = self.mp | ||||
|         self.mp = self.our_i32(self.mp + 1) | ||||
|         # kontrola, jesstli jsem ne přeskočil konec | ||||
|         if self.mp > self.MAX - len(self.data_end): | ||||
|             self.is_at_end = True | ||||
|             return | ||||
|         if temp > self.mp: | ||||
|             self.is_at_end = False | ||||
|         # rozšíření datového pole | ||||
|         if self.mp >= len(self.data): | ||||
|             self.data += [0] * (len(self.data))  | ||||
|      | ||||
|     def handle_less(self): | ||||
|         temp = self.mp | ||||
|         self.mp = self.our_i32(self.mp - 1) | ||||
|         # kontrola, jestli jsem ne přeskočil začátek | ||||
|         if self.mp < len(self.data): | ||||
|             self.is_at_end = False | ||||
|             return | ||||
|         if temp < self.mp: | ||||
|             self.is_at_end = True | ||||
|         if self.MAX - self.mp >= len(self.data_end): | ||||
|             self.data_end += [0] * (len(self.data_end)) | ||||
| 
 | ||||
|     def handle_dot(self): | ||||
|         sys.stdout.write(chr(self.get_value_at_mp())) | ||||
|         sys.stdout.flush() | ||||
|         self.saved_output += chr(self.get_value_at_mp()) | ||||
|      | ||||
|     def handle_comma(self): | ||||
|         if self.comma_standart_input: | ||||
|             input_char = sys.stdin.read(1) | ||||
|         else: | ||||
|             if self.user_input_index < len(self.user_input): | ||||
|                 input_char = self.user_input[self.user_input_index] | ||||
|                 self.user_input_index += 1 | ||||
|             else: | ||||
|                 input_char = None | ||||
|         if not input_char: | ||||
|             self.set_value_at_mp(0) | ||||
|         else: | ||||
|             self.set_value_at_mp(ord(input_char)) | ||||
| 
 | ||||
|     def handle_left_bracket(self): | ||||
|         if self.data[self.mp] == 0: # jump to the matching ] | ||||
|             open_brackets = 1 | ||||
|             while open_brackets > 0: | ||||
|                 self.pc += 1 | ||||
|                 if self.pc >= len(self.code): | ||||
|                     raise SyntaxError("Unmatched \"[\"") | ||||
|                 if self.code[self.pc] == "[": | ||||
|                     open_brackets += 1 | ||||
|                 elif self.code[self.pc] == "]": | ||||
|                     open_brackets -= 1 | ||||
|      | ||||
|     def handle_right_bracket(self): | ||||
|         if self.data[self.mp] != 0: | ||||
|             close_brackets = 1 | ||||
|             while close_brackets > 0: | ||||
|                 self.pc -= 1 | ||||
|                 if self.pc < 0: | ||||
|                     raise SyntaxError("Unmatched \"]\"") | ||||
|                 if self.code[self.pc] == "]": | ||||
|                     close_brackets += 1 | ||||
|                 elif self.code[self.pc] == "[": | ||||
|                     close_brackets -= 1 | ||||
|      | ||||
|     def execute_loaded_code(self): | ||||
|         steps = 0 | ||||
|         print("|Executing code|") | ||||
|         while True: | ||||
|             if self.pc >= len(self.code): | ||||
|                 break | ||||
|             com = self.code[self.pc] | ||||
|             if com == "+": | ||||
|                 self.handle_plus() | ||||
|             elif com == "-": | ||||
|                 self.handle_minus() | ||||
|             elif com == ">": | ||||
|                 self.handle_greater() | ||||
|             elif com == "<": | ||||
|                 self.handle_less() | ||||
|             elif com == ".": | ||||
|                 self.handle_dot() | ||||
|             elif com == ",": | ||||
|                 self.handle_comma() | ||||
|             elif com == "[": | ||||
|                 self.handle_left_bracket() | ||||
|             elif com == "]": | ||||
|                 self.handle_right_bracket() | ||||
|             # debug print | ||||
|             if self.debug: | ||||
|                 self.print_data(steps) | ||||
|             time.sleep(1 / self.commands_per_second) | ||||
|             self.pc += 1 | ||||
|             steps += 1 | ||||
|             self.last_step = steps | ||||
|         print("\n|Execution done|") | ||||
| 
 | ||||
| ''' | ||||
| Usage: | ||||
| python interpreter.py <code_file> < <user_input> | ||||
| 
 | ||||
| nastavte si DEBUG na True, pokud chcete vidět debugovací výstup, pak ale nebude správně fungovat "."... | ||||
| nastavte si commands_per_second na 2, pokud chcete dva kroky za sekundu | ||||
| ''' | ||||
| 
 | ||||
| DEBUG = True | ||||
| COMMANDS_PER_SECOND = 1000 | ||||
| 
 | ||||
| if __name__ == "__main__": | ||||
|     interpreter = Interpreter(commands_per_second=COMMANDS_PER_SECOND, debug=DEBUG) | ||||
|     interpreter.load_code_from_input() | ||||
|     print("Loaded code:", interpreter.code, "length:", len(interpreter.code)) | ||||
|     interpreter.execute_loaded_code() | ||||
|  | @ -33,6 +33,14 @@ | |||
|       margin-top: 1rem; | ||||
|     } | ||||
|     .btn:hover:not([disabled]) { background: var(--primary-light); } | ||||
|     .btn-stop { | ||||
|       background: #ef4444; color: #fff; font-weight: 600; cursor: pointer; | ||||
|       transition: background 0.2s ease; | ||||
|       margin-top: 1rem; padding: 0.75rem 1.5rem; border: none; border-radius: 0.375rem; | ||||
|       display: inline-block; text-decoration: none; | ||||
|       margin-left: 1rem; | ||||
|     } | ||||
|     .btn-stop:hover:not([disabled]) { background: #dc2626; } | ||||
|     .btn[disabled] { opacity: 0.6; cursor: not-allowed; } | ||||
|     .spinner { | ||||
|       border: 2px solid #f3f3f3; | ||||
|  | @ -66,6 +74,13 @@ | |||
|       top: 0; left: 0; width: 100%; height: 100%; opacity: 0; | ||||
|       cursor: pointer; | ||||
|     } | ||||
|     .author { | ||||
|       text-align: center; | ||||
|       font-size: 0.8rem; | ||||
|       color: var(--text); | ||||
|       margin-top: 1rem; | ||||
|       line-height: 2em; | ||||
|     } | ||||
|     @media screen and (max-width: 768px) { | ||||
|       .flex { flex-direction: column; } | ||||
|       .flex > div { width: 100%; } | ||||
|  | @ -74,7 +89,7 @@ | |||
| </head> | ||||
| <body> | ||||
|   <div class="container"> | ||||
|     <h1>Brainfuck Compiler</h1> | ||||
|     <h1>Brainfuck Compiler for M&M</h1> | ||||
|     <form id="bf-form" class="card"> | ||||
|       <div class="flex"> | ||||
|         <div> | ||||
|  | @ -95,20 +110,209 @@ | |||
|         </div> | ||||
|       </div> | ||||
|       <button type="button" id="run-btn" class="btn">Run</button> | ||||
|       <button type="button" id="stop-btn" class="btn-stop" style="display: none;">Stop</button> | ||||
|     </form> | ||||
| 
 | ||||
|     <div class="card"> | ||||
|       <label for="output">Output</label> | ||||
|       <div id="output"></div> | ||||
|       <pre id="output"></pre> | ||||
|     </div> | ||||
| 
 | ||||
|     <div class="card"> | ||||
|       <label for="last_state">State</label> | ||||
|       <div id="last_state"></div> | ||||
|       <pre id="last_state"></pre> | ||||
|     </div> | ||||
|     <div class="author">Created by TicVac 2025<br>vaclav.tichy.mam@gmail.com</div> | ||||
|   </div> | ||||
| 
 | ||||
|   <script> | ||||
|     let abortSignal = false; | ||||
|     const outElement = document.getElementById('output'); | ||||
|     const lastStateElement = document.getElementById('last_state'); | ||||
| 
 | ||||
|     class Interpreter { | ||||
|       constructor({ commandsPerSecond = 1_000_000_000, debug = false} = {}) { | ||||
|         this.debug = debug; | ||||
|         this.commands = ['+', '-', '>', '<', '.', ',', '[', ']']; | ||||
|         this.mp = 0; // memory pointer | ||||
|         this.pc = 0; // program counter | ||||
|         this.data = [0, 0]; | ||||
|         this.dataEnd = [0, 0]; // tail reversed | ||||
|         this.isAtEnd = false; | ||||
|         this.code = ""; | ||||
|         this.commandsPerSecond = commandsPerSecond; | ||||
|         this.MAX = 2 ** 32 - 1; | ||||
| 
 | ||||
|         this.userInput = ""; | ||||
|         this.userInputIndex = 0; | ||||
|         this.savedOutput = ""; | ||||
|       } | ||||
| 
 | ||||
|       ourI32(value) { | ||||
|         if (value === -1) return this.MAX; | ||||
|         if (value === this.MAX + 1) return 0; | ||||
|         return value; | ||||
|       } | ||||
| 
 | ||||
|       loadCodeFromString(code) { | ||||
|         const expandNumbers = (match, count, char) => { | ||||
|             return char.repeat(parseInt(count, 10)); | ||||
|         }; | ||||
|         let cleaned = code.trim(); | ||||
|         cleaned = cleaned.replace(/(\d+)(.)/g, expandNumbers); | ||||
|         cleaned = cleaned.split('').filter(c => this.commands.includes(c)).join(''); | ||||
|         this.code = cleaned; | ||||
|       } | ||||
| 
 | ||||
|       getValueAtMp() { | ||||
|         if (!this.isAtEnd) { | ||||
|             return this.data[this.mp]; | ||||
|         } else { | ||||
|             return this.dataEnd[this.MAX - this.mp]; | ||||
|         } | ||||
|       } | ||||
| 
 | ||||
|       setValueAtMp(value) { | ||||
|         if (!this.isAtEnd) { | ||||
|             this.data[this.mp] = value; | ||||
|         } else { | ||||
|             this.dataEnd[this.MAX - this.mp] = value; | ||||
|         } | ||||
|       } | ||||
| 
 | ||||
|       handlePlus() { | ||||
|           this.setValueAtMp(this.ourI32(this.getValueAtMp() + 1)); | ||||
|       } | ||||
| 
 | ||||
|       handleMinus() { | ||||
|           this.setValueAtMp(this.ourI32(this.getValueAtMp() - 1)); | ||||
|       } | ||||
| 
 | ||||
|       handleGreater() { | ||||
|         const temp = this.mp; | ||||
|         this.mp = this.ourI32(this.mp + 1); | ||||
|         if (this.mp > this.MAX - this.dataEnd.length) { | ||||
|             this.isAtEnd = true; | ||||
|             return; | ||||
|         } | ||||
|         if (temp > this.mp) { | ||||
|             this.isAtEnd = false; | ||||
|         } | ||||
|         if (this.mp >= this.data.length) { | ||||
|             this.data = this.data.concat(new Array(this.data.length).fill(0)); | ||||
|         } | ||||
|       } | ||||
| 
 | ||||
|       handleLess() { | ||||
|         const temp = this.mp; | ||||
|         this.mp = this.ourI32(this.mp - 1); | ||||
|         if (this.mp < this.data.length) { | ||||
|             this.isAtEnd = false; | ||||
|             return; | ||||
|         } | ||||
|         if (temp < this.mp) { | ||||
|             this.isAtEnd = true; | ||||
|         } | ||||
|         if (this.MAX - this.mp >= this.dataEnd.length) { | ||||
|             this.dataEnd = this.dataEnd.concat(new Array(this.dataEnd.length).fill(0)); | ||||
|         } | ||||
|       } | ||||
| 
 | ||||
|       handleDot() { | ||||
|         const ch = String.fromCharCode(this.getValueAtMp()); | ||||
|         this.savedOutput += ch; | ||||
|       } | ||||
| 
 | ||||
|       handleComma() { | ||||
|         let inputChar; | ||||
|         if (this.commaStandardInput) { | ||||
|             // Standard input ignored in this variant | ||||
|             inputChar = null; | ||||
|         } else { | ||||
|             if (this.userInputIndex < this.userInput.length) { | ||||
|                 inputChar = this.userInput[this.userInputIndex++]; | ||||
|             } else { | ||||
|                 inputChar = null; | ||||
|             } | ||||
|         } | ||||
|         if (!inputChar) { | ||||
|             this.setValueAtMp(0); | ||||
|         } else { | ||||
|             this.setValueAtMp(inputChar.charCodeAt(0)); | ||||
|         } | ||||
|       } | ||||
| 
 | ||||
|       handleLeftBracket() { | ||||
|         if (this.getValueAtMp() === 0) { | ||||
|             let openBrackets = 1; | ||||
|             while (openBrackets > 0) { | ||||
|                 this.pc++; | ||||
|                 if (this.pc >= this.code.length) { | ||||
|                     throw new SyntaxError('Unmatched "["'); | ||||
|                 } | ||||
|                 if (this.code[this.pc] === '[') openBrackets++; | ||||
|                 if (this.code[this.pc] === ']') openBrackets--; | ||||
|             } | ||||
|         } | ||||
|       } | ||||
| 
 | ||||
|       handleRightBracket() { | ||||
|         if (this.getValueAtMp() !== 0) { | ||||
|             let closeBrackets = 1; | ||||
|             while (closeBrackets > 0) { | ||||
|                 this.pc--; | ||||
|                 if (this.pc < 0) { | ||||
|                     throw new SyntaxError('Unmatched "]"'); | ||||
|                 } | ||||
|                 if (this.code[this.pc] === ']') closeBrackets++; | ||||
|                 if (this.code[this.pc] === '[') closeBrackets--; | ||||
|             } | ||||
|         } | ||||
|       } | ||||
| 
 | ||||
|       getState() { | ||||
|         const dataMain = this.data.join(' '); | ||||
|         const dataTail = [...this.dataEnd].reverse().join(' '); | ||||
|         return `step: ${this.steps} mp: ${this.mp} | ${dataMain} | end: | ${dataTail} |`; | ||||
|       } | ||||
| 
 | ||||
|       async execute({ codeString, userInput = '', callback } = {}) { | ||||
|         this.loadCodeFromString(codeString); | ||||
|         this.userInput = userInput; | ||||
|         let steps = 0; | ||||
|         let breath = 100; | ||||
|         while (this.pc < this.code.length) { | ||||
|             const com = this.code[this.pc]; | ||||
|             switch (com) { | ||||
|                 case '+': this.handlePlus(); break; | ||||
|                 case '-': this.handleMinus(); break; | ||||
|                 case '>': this.handleGreater(); break; | ||||
|                 case '<': this.handleLess(); break; | ||||
|                 case '.': this.handleDot(); break; | ||||
|                 case ',': this.handleComma(); break; | ||||
|                 case '[': this.handleLeftBracket(); break; | ||||
|                 case ']': this.handleRightBracket(); break; | ||||
|             } | ||||
|             this.pc++; | ||||
|             steps++; | ||||
|             if (steps % breath === 0) { | ||||
|               await new Promise(resolve => setTimeout(resolve, 0)); | ||||
|             } | ||||
|             if (abortSignal) { | ||||
|               console.log("Execution aborted"); | ||||
|               this.savedOutput = "Execution aborted"; | ||||
|               break; | ||||
|             } | ||||
|             outElement.textContent = this.savedOutput; | ||||
|             lastStateElement.textContent = this.getState(); | ||||
|         } | ||||
|         callback(this.savedOutput, this.getState()); | ||||
|         return this.savedOutput; | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     const stopBtn = document.getElementById('stop-btn'); | ||||
| 
 | ||||
|     // Setup drop zones with filename display | ||||
|     function setupDropZone(dropZoneId, textareaId, textId) { | ||||
|       const dropZone = document.getElementById(dropZoneId); | ||||
|  | @ -146,63 +350,14 @@ | |||
|     setupDropZone('code-drop', 'code', 'code-drop-text'); | ||||
|     setupDropZone('input-drop', 'input', 'input-drop-text'); | ||||
| 
 | ||||
|     // Brainfuck Interpreter unchanged | ||||
|     function runBrainfuck(code, input) { | ||||
|       const tape = Array(30000).fill(0); | ||||
|       let ptr = 0, inputPtr = 0, output = ''; | ||||
|       const loopStack = []; | ||||
|       for (let i = 0; i < code.length; i++) { | ||||
|         switch(code[i]) { | ||||
|           case '>': ptr++; break; | ||||
|           case '<': ptr--; break; | ||||
|           case '+': tape[ptr] = (tape[ptr] + 1) % 256; break; | ||||
|           case '-': tape[ptr] = (tape[ptr] + 255) % 256; break; | ||||
|           case '.': output += String.fromCharCode(tape[ptr]); break; | ||||
|           case ',': tape[ptr] = inputPtr < input.length ? input.charCodeAt(inputPtr++) : 0; break; | ||||
|           case '[': | ||||
|             if (tape[ptr] === 0) { | ||||
|               let open = 1; | ||||
|               while (open) { | ||||
|                 i++; | ||||
|                 if (code[i] === '[') open++; | ||||
|                 if (code[i] === ']') open--; | ||||
|               } | ||||
|             } else { | ||||
|               loopStack.push(i); | ||||
|             } | ||||
|             break; | ||||
|           case ']': | ||||
|             if (tape[ptr] !== 0) { i = loopStack[loopStack.length - 1]; } | ||||
|             else { loopStack.pop(); } | ||||
|             break; | ||||
|           default: break; | ||||
|         } | ||||
|       } | ||||
|       return output; | ||||
|     } | ||||
| 
 | ||||
|     function formatForHTML(text, tabWidth = 4, spaces = true) { | ||||
|       // 1) Escape &, <, >, " and ' | ||||
|       let s = text | ||||
|         .replace(/&/g, "&") | ||||
|         .replace(/</g, "<") | ||||
|         .replace(/>/g, ">") | ||||
|         .replace(/"/g, """) | ||||
|         .replace(/'/g, "'"); | ||||
| 
 | ||||
|       // 2) Convert tabs → a run of non-breaking spaces | ||||
|       const nbspTab = " ".repeat(tabWidth); | ||||
|       s = s.replace(/\t/g, nbspTab); | ||||
| 
 | ||||
|       // 3) Optionally convert every single space →   | ||||
|       if (spaces) { | ||||
|         s = s.replace(/ /g, " "); | ||||
|       } | ||||
| 
 | ||||
|       // 4) Convert newlines → <br> | ||||
|       s = s.replace(/\r?\n/g, "<br>\n"); | ||||
| 
 | ||||
|       return s; | ||||
|     function random_js(out, state) { | ||||
|       console.log("random_js"); | ||||
|       const btn = document.getElementById('run-btn'); | ||||
|       btn.removeChild(btn.firstChild); | ||||
|       document.getElementById('output').textContent = out; | ||||
|       document.getElementById('last_state').textContent = state; | ||||
|       btn.disabled = false; | ||||
|       stopBtn.style.display = 'none'; | ||||
|     } | ||||
| 
 | ||||
|     // Run button handler with spinner prepend | ||||
|  | @ -211,25 +366,23 @@ | |||
|       const code = document.getElementById('code').value; | ||||
|       const input = document.getElementById('input').value; | ||||
|       if (btn.disabled) return; | ||||
| 
 | ||||
|       outElement.textContent = ""; | ||||
|       lastStateElement.textContent = ""; | ||||
|       abortSignal = false; | ||||
|       stopBtn.style.display = 'inline-block'; | ||||
|       btn.disabled = true; | ||||
|       const spinner = document.createElement('div'); spinner.className = 'spinner'; | ||||
|       btn.insertBefore(spinner, btn.firstChild); | ||||
| 
 | ||||
|       const url = '/brainfuck/process_code?code=' + encodeURIComponent(code) + "&user_input=" + encodeURIComponent(input); | ||||
|       fetch(url, { method: 'GET', headers: { 'Content-Type': 'application/json' } }) | ||||
|         .then(response => response.json()) | ||||
|         .then(data => { | ||||
|           // escape HTML entities in output | ||||
|           console.log(data); | ||||
|           document.getElementById('output').textContent = data.output; | ||||
|           document.getElementById('last_state').textContent = data.end_state; | ||||
|           document.getElementById('output').innerHTML = formatForHTML(data.output); | ||||
|         }) | ||||
|         .catch(err => console.error(err)) | ||||
|         .finally(() => { | ||||
|           btn.disabled = false; | ||||
|           btn.removeChild(spinner); | ||||
|         }); | ||||
|       // execution start | ||||
|       const interpreter = new Interpreter({ commandsPerSecond: 1_000_000_000, debug: false }); | ||||
|       let out = interpreter.execute({ codeString: code, userInput: input, callback: random_js }); | ||||
|     }); | ||||
| 
 | ||||
|     stopBtn.addEventListener('click', () => { | ||||
|       console.log("Stop button clicked"); | ||||
|       abortSignal = true; | ||||
|     }); | ||||
|   </script> | ||||
| </body> | ||||
|  |  | |||
|  | @ -3,6 +3,5 @@ from . import views | |||
| 
 | ||||
| urlpatterns = [ | ||||
| 	path('', views.index), | ||||
| 	path('process_code', views.process_code), | ||||
| ] | ||||
| 
 | ||||
|  |  | |||
|  | @ -7,42 +7,3 @@ from typing import Dict | |||
| def index(request): | ||||
| 	args = {} | ||||
| 	return TemplateResponse(request, 'brainfuck/index.html', args) | ||||
| 
 | ||||
| def process_code(request): | ||||
| 	qs = request.META['QUERY_STRING'] | ||||
| 	params: Dict[str, str] = {} | ||||
| 	for pair in qs.split('&'): | ||||
| 		if not pair: | ||||
| 			continue | ||||
| 		key, sep, val = pair.partition('=') | ||||
| 		# decode key (still treat '+'→space in parameter names) | ||||
| 		decoded_key = unquote_plus(key) | ||||
| 		# decode value: unquote (leaves '+' intact), then percent-decode | ||||
| 		decoded_val = unquote(val) | ||||
| 		params[decoded_key] = decoded_val | ||||
| 	 | ||||
| 	print('params:', params) | ||||
| 
 | ||||
| 	code = params.get('code', '') | ||||
| 	input_data = params.get('user_input', '')  | ||||
| 	# interpreter | ||||
| 	interpreter = Interpreter(commands_per_second=1_000_000_000_000, debug=True, comma_standart_input=False) | ||||
| 	interpreter.user_input = input_data | ||||
| 	interpreter.load_code_from_string(code) | ||||
| 	# TODO kill the process if it takes too long? | ||||
| 	try: | ||||
| 		interpreter.execute_loaded_code() | ||||
| 		out = interpreter.saved_output	 | ||||
| 	except Exception as e: | ||||
| 		print('Error:', e) | ||||
| 		out = 'Error: Code execution failed - ' + str(e) | ||||
| 	 | ||||
| 	end_state = interpreter.print_data(interpreter.last_step) | ||||
| 
 | ||||
| 	 | ||||
| 	return JSONResponse({ | ||||
| 		'code': code, | ||||
| 		'input_data': input_data, | ||||
| 		'output': out, | ||||
| 		'end_state': end_state, | ||||
| 	}) | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue