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; |       margin-top: 1rem; | ||||||
|     } |     } | ||||||
|     .btn:hover:not([disabled]) { background: var(--primary-light); } |     .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; } |     .btn[disabled] { opacity: 0.6; cursor: not-allowed; } | ||||||
|     .spinner { |     .spinner { | ||||||
|       border: 2px solid #f3f3f3; |       border: 2px solid #f3f3f3; | ||||||
|  | @ -66,6 +74,13 @@ | ||||||
|       top: 0; left: 0; width: 100%; height: 100%; opacity: 0; |       top: 0; left: 0; width: 100%; height: 100%; opacity: 0; | ||||||
|       cursor: pointer; |       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) { |     @media screen and (max-width: 768px) { | ||||||
|       .flex { flex-direction: column; } |       .flex { flex-direction: column; } | ||||||
|       .flex > div { width: 100%; } |       .flex > div { width: 100%; } | ||||||
|  | @ -74,7 +89,7 @@ | ||||||
| </head> | </head> | ||||||
| <body> | <body> | ||||||
|   <div class="container"> |   <div class="container"> | ||||||
|     <h1>Brainfuck Compiler</h1> |     <h1>Brainfuck Compiler for M&M</h1> | ||||||
|     <form id="bf-form" class="card"> |     <form id="bf-form" class="card"> | ||||||
|       <div class="flex"> |       <div class="flex"> | ||||||
|         <div> |         <div> | ||||||
|  | @ -95,20 +110,209 @@ | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|       <button type="button" id="run-btn" class="btn">Run</button> |       <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> |     </form> | ||||||
| 
 | 
 | ||||||
|     <div class="card"> |     <div class="card"> | ||||||
|       <label for="output">Output</label> |       <label for="output">Output</label> | ||||||
|       <div id="output"></div> |       <pre id="output"></pre> | ||||||
|     </div> |     </div> | ||||||
| 
 | 
 | ||||||
|     <div class="card"> |     <div class="card"> | ||||||
|       <label for="last_state">State</label> |       <label for="last_state">State</label> | ||||||
|       <div id="last_state"></div> |       <pre id="last_state"></pre> | ||||||
|     </div> |     </div> | ||||||
|  |     <div class="author">Created by TicVac 2025<br>vaclav.tichy.mam@gmail.com</div> | ||||||
|   </div> |   </div> | ||||||
| 
 | 
 | ||||||
|   <script> |   <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 |     // Setup drop zones with filename display | ||||||
|     function setupDropZone(dropZoneId, textareaId, textId) { |     function setupDropZone(dropZoneId, textareaId, textId) { | ||||||
|       const dropZone = document.getElementById(dropZoneId); |       const dropZone = document.getElementById(dropZoneId); | ||||||
|  | @ -146,63 +350,14 @@ | ||||||
|     setupDropZone('code-drop', 'code', 'code-drop-text'); |     setupDropZone('code-drop', 'code', 'code-drop-text'); | ||||||
|     setupDropZone('input-drop', 'input', 'input-drop-text'); |     setupDropZone('input-drop', 'input', 'input-drop-text'); | ||||||
| 
 | 
 | ||||||
|     // Brainfuck Interpreter unchanged |     function random_js(out, state) { | ||||||
|     function runBrainfuck(code, input) { |       console.log("random_js"); | ||||||
|       const tape = Array(30000).fill(0); |       const btn = document.getElementById('run-btn'); | ||||||
|       let ptr = 0, inputPtr = 0, output = ''; |       btn.removeChild(btn.firstChild); | ||||||
|       const loopStack = []; |       document.getElementById('output').textContent = out; | ||||||
|       for (let i = 0; i < code.length; i++) { |       document.getElementById('last_state').textContent = state; | ||||||
|         switch(code[i]) { |       btn.disabled = false; | ||||||
|           case '>': ptr++; break; |       stopBtn.style.display = 'none'; | ||||||
|           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; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Run button handler with spinner prepend |     // Run button handler with spinner prepend | ||||||
|  | @ -211,25 +366,23 @@ | ||||||
|       const code = document.getElementById('code').value; |       const code = document.getElementById('code').value; | ||||||
|       const input = document.getElementById('input').value; |       const input = document.getElementById('input').value; | ||||||
|       if (btn.disabled) return; |       if (btn.disabled) return; | ||||||
|  | 
 | ||||||
|  |       outElement.textContent = ""; | ||||||
|  |       lastStateElement.textContent = ""; | ||||||
|  |       abortSignal = false; | ||||||
|  |       stopBtn.style.display = 'inline-block'; | ||||||
|       btn.disabled = true; |       btn.disabled = true; | ||||||
|       const spinner = document.createElement('div'); spinner.className = 'spinner'; |       const spinner = document.createElement('div'); spinner.className = 'spinner'; | ||||||
|       btn.insertBefore(spinner, btn.firstChild); |       btn.insertBefore(spinner, btn.firstChild); | ||||||
| 
 | 
 | ||||||
|       const url = '/brainfuck/process_code?code=' + encodeURIComponent(code) + "&user_input=" + encodeURIComponent(input); |       // execution start | ||||||
|       fetch(url, { method: 'GET', headers: { 'Content-Type': 'application/json' } }) |       const interpreter = new Interpreter({ commandsPerSecond: 1_000_000_000, debug: false }); | ||||||
|         .then(response => response.json()) |       let out = interpreter.execute({ codeString: code, userInput: input, callback: random_js }); | ||||||
|         .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); |  | ||||||
|     }); |     }); | ||||||
|  | 
 | ||||||
|  |     stopBtn.addEventListener('click', () => { | ||||||
|  |       console.log("Stop button clicked"); | ||||||
|  |       abortSignal = true; | ||||||
|     }); |     }); | ||||||
|   </script> |   </script> | ||||||
| </body> | </body> | ||||||
|  |  | ||||||
|  | @ -3,6 +3,5 @@ from . import views | ||||||
| 
 | 
 | ||||||
| urlpatterns = [ | urlpatterns = [ | ||||||
| 	path('', views.index), | 	path('', views.index), | ||||||
| 	path('process_code', views.process_code), |  | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -7,42 +7,3 @@ from typing import Dict | ||||||
| def index(request): | def index(request): | ||||||
| 	args = {} | 	args = {} | ||||||
| 	return TemplateResponse(request, 'brainfuck/index.html', 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