Compare commits
	
		
			5 commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 577557146d | |||
| c6d280dfd8 | |||
| 4cb534838a | |||
| 7f0b77327e | |||
| ac29780e6b | 
					 11 changed files with 503 additions and 0 deletions
				
			
		
							
								
								
									
										0
									
								
								brainfuck/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								brainfuck/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										3
									
								
								brainfuck/admin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								brainfuck/admin.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,3 @@ | ||||||
|  | from django.contrib import admin | ||||||
|  | 
 | ||||||
|  | # Register your models here. | ||||||
							
								
								
									
										6
									
								
								brainfuck/apps.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								brainfuck/apps.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,6 @@ | ||||||
|  | from django.apps import AppConfig | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class BrainfuckConfig(AppConfig): | ||||||
|  |     default_auto_field = 'django.db.models.BigAutoField' | ||||||
|  |     name = 'brainfuck' | ||||||
							
								
								
									
										0
									
								
								brainfuck/migrations/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								brainfuck/migrations/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										3
									
								
								brainfuck/models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								brainfuck/models.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,3 @@ | ||||||
|  | from django.db import models | ||||||
|  | 
 | ||||||
|  | # Create your models here. | ||||||
							
								
								
									
										469
									
								
								brainfuck/templates/brainfuck/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										469
									
								
								brainfuck/templates/brainfuck/index.html
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,469 @@ | ||||||
|  | <!DOCTYPE html> | ||||||
|  | <html lang="en"> | ||||||
|  | <head> | ||||||
|  |   <meta charset="UTF-8"> | ||||||
|  |   <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||||
|  |   <title>Brainfuck Compiler</title> | ||||||
|  |   <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap" rel="stylesheet"> | ||||||
|  |   <style> | ||||||
|  |     :root { | ||||||
|  |       --bg: #f5f7fa; | ||||||
|  |       --card: #ffffff; | ||||||
|  |       --primary: #4f46e5; | ||||||
|  |       --primary-light: #6366f1; | ||||||
|  |       --text: #374151; | ||||||
|  |       --border: #e5e7eb; | ||||||
|  |       --highlight: #e0e7ff; | ||||||
|  |       --fade: rgba(0, 0, 0, 0.1); | ||||||
|  |     } | ||||||
|  |     * { box-sizing: border-box; margin: 0; padding: 0; } | ||||||
|  |     body { font-family: 'Inter', sans-serif; background: var(--bg); color: var(--text); } | ||||||
|  |     .container { max-width: 1100px; margin: 2rem auto; padding: 1rem; } | ||||||
|  |     h1 { text-align: center; margin-bottom: 1.5rem; font-size: 2rem; font-weight: 700; } | ||||||
|  |     .card { background: var(--card); border: 1px solid var(--border); border-radius: 0.5rem; padding: 1.5rem; box-shadow: 0 2px 4px rgba(0,0,0,0.05); margin-bottom: 1.5rem; } | ||||||
|  |     label { display: block; margin-bottom: 0.5rem; font-weight: 600; } | ||||||
|  |     textarea, input[type="text"] { | ||||||
|  |       width: 100%; padding: 0.75rem; border: 1px solid var(--border); border-radius: 0.375rem; font-family: monospace; font-size: 0.9rem; margin-bottom: 1rem; | ||||||
|  |     } | ||||||
|  |     textarea { resize: vertical; height: 200px; } | ||||||
|  |     .btn { | ||||||
|  |       display: inline-block; padding: 0.75rem 1.5rem; background: var(--primary); color: #fff; text-decoration: none; | ||||||
|  |       border: none; border-radius: 0.375rem; font-weight: 600; cursor: pointer; | ||||||
|  |       transition: background 0.2s ease; | ||||||
|  |     } | ||||||
|  |     .btn:hover:not([disabled]) { background: var(--primary-light); } | ||||||
|  |     .btn-stop { | ||||||
|  |       background: #ef4444; color: #fff; font-weight: 600; cursor: pointer; | ||||||
|  |       transition: background 0.2s ease; | ||||||
|  |       padding: 0.75rem 1.5rem; border: none; border-radius: 0.375rem; | ||||||
|  |       display: inline-block; text-decoration: none; | ||||||
|  |     } | ||||||
|  |     .btn-stop:hover:not([disabled]) { background: #dc2626; } | ||||||
|  |     .btn[disabled] { opacity: 0.6; cursor: not-allowed; } | ||||||
|  |     .spinner { | ||||||
|  |       border: 2px solid #f3f3f3; | ||||||
|  |       border-top: 2px solid #fff; | ||||||
|  |       border-radius: 50%; | ||||||
|  |       width: 14px; | ||||||
|  |       height: 14px; | ||||||
|  |       display: inline-block; | ||||||
|  |       vertical-align: middle; | ||||||
|  |       margin-right: 0.5rem; | ||||||
|  |       animation: spin 1s linear infinite; | ||||||
|  |     } | ||||||
|  |     .spinner::before, | ||||||
|  |     .spinner::after { | ||||||
|  |       content: ""; | ||||||
|  |       box-sizing: border-box; | ||||||
|  |       position: absolute; | ||||||
|  |       inset: 0; | ||||||
|  |       border-radius: 50%; | ||||||
|  |     } | ||||||
|  |     .spinner::before { | ||||||
|  |       border: 4px solid rgba(0,0,0,0.1); | ||||||
|  |     } | ||||||
|  |     .spinner::after { | ||||||
|  |       border: 4px solid transparent; | ||||||
|  |       border-top-color: #3498db; | ||||||
|  |       border-right-color: #8e44ad; | ||||||
|  |       box-shadow: 0 0 8px rgba(52, 152, 219, 0.6), | ||||||
|  |                   0 0 8px rgba(142, 68, 173, 0.6) inset; | ||||||
|  |       animation: spin 1s cubic-bezier(0.68, -0.55, 0.27, 1.55) infinite; | ||||||
|  |     } | ||||||
|  |     @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } | ||||||
|  |     .flex { display: flex; gap: 1rem; } | ||||||
|  |     .flex > div { flex: 1; } | ||||||
|  |     #output, #last_state { background: #1e293b; color: #d1d5db; height: 150px; overflow-y: auto; font-family: monospace; } | ||||||
|  |     .drop-zone { | ||||||
|  |       position: relative; | ||||||
|  |       padding: 1rem; | ||||||
|  |       border: 2px dashed var(--border); | ||||||
|  |       border-radius: 0.375rem; | ||||||
|  |       transition: background 0.2s ease, border-color 0.2s ease; | ||||||
|  |       text-align: center; | ||||||
|  |     } | ||||||
|  |     .drop-zone.active { | ||||||
|  |       background: var(--highlight); | ||||||
|  |       border-color: var(--primary); | ||||||
|  |     } | ||||||
|  |     .drop-zone input { | ||||||
|  |       position: absolute; | ||||||
|  |       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: 1.5rem; | ||||||
|  |     } | ||||||
|  |     .styled-select { | ||||||
|  |       padding: 0.75rem; | ||||||
|  |       border: 1px solid var(--border); | ||||||
|  |       border-radius: 0.375rem; | ||||||
|  |       background-color: var(--card); | ||||||
|  |       color: var(--text); | ||||||
|  |       font-family: 'Inter', sans-serif; | ||||||
|  |       font-size: 0.9rem; | ||||||
|  |       appearance: none; | ||||||
|  |       background-image: url("data:image/svg+xml;utf8,<svg fill='%23374151' height='20' viewBox='0 0 24 24' width='20' xmlns='http://www.w3.org/2000/svg'><path d='M7 10l5 5 5-5z'/></svg>"); | ||||||
|  |       background-repeat: no-repeat; | ||||||
|  |       background-position: right 0.75rem center; | ||||||
|  |       background-size: 1rem; | ||||||
|  |       padding-right: 2rem; | ||||||
|  |       padding-left: 1rem; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .styled-select:focus { | ||||||
|  |       outline: none; | ||||||
|  |       border-color: var(--primary); | ||||||
|  |       box-shadow: 0 0 0 2px rgba(79, 70, 229, 0.2); | ||||||
|  |     } | ||||||
|  |     .align-center { | ||||||
|  |       display: flex; | ||||||
|  |       align-items: center; | ||||||
|  |     } | ||||||
|  |     @media screen and (max-width: 768px) { | ||||||
|  |       .flex { flex-direction: column; } | ||||||
|  |       .flex > div { width: 100%; } | ||||||
|  |       .align-center { | ||||||
|  |         align-items: normal; | ||||||
|  |       } | ||||||
|  |       .styled-select { | ||||||
|  |         width: 100%; | ||||||
|  |         max-width: 500px !important; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |   </style> | ||||||
|  | </head> | ||||||
|  | <body> | ||||||
|  |   <div class="container"> | ||||||
|  |     <h1>Brainfuck Compiler for M&M</h1> | ||||||
|  |     <form id="bf-form" class="card"> | ||||||
|  |       <div class="flex"> | ||||||
|  |         <div> | ||||||
|  |           <label for="code">Code</label> | ||||||
|  |           <textarea id="code" placeholder="Enter Brainfuck code..."></textarea> | ||||||
|  |           <div id="code-drop" class="drop-zone"> | ||||||
|  |             <p id="code-drop-text">Drag & drop code file here or click to select</p> | ||||||
|  |             <input type="file" id="code-file" accept=".bf, .in, .txt"> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |         <div> | ||||||
|  |           <label for="input">Input</label> | ||||||
|  |           <textarea id="input" placeholder="Program input..."></textarea> | ||||||
|  |           <div id="input-drop" class="drop-zone"> | ||||||
|  |             <p id="input-drop-text">Drag & drop input file here or click to select</p> | ||||||
|  |             <input type="file" id="input-file" accept=".txt, .in"> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |       <div class="flex align-center" style="margin-top: 1rem; flex-wrap: wrap; gap: 1rem;"> | ||||||
|  |         <select id="speed" class="styled-select"> | ||||||
|  |           <option value="slllooow">pretty, pretty slow</option> | ||||||
|  |           <option value="med">medium</option> | ||||||
|  |           <option value="fast" selected>blazingly fast</option> | ||||||
|  |         </select> | ||||||
|  |         <button type="button" id="run-btn" class="btn">Run</button> | ||||||
|  |         <button type="button" id="stop-btn" class="btn-stop" style="display: none;">Stop</button> | ||||||
|  |       </div> | ||||||
|  |     </form> | ||||||
|  | 
 | ||||||
|  |     <div class="card"> | ||||||
|  |       <label for="output">Output</label> | ||||||
|  |       <pre id="output"></pre> | ||||||
|  |     </div> | ||||||
|  | 
 | ||||||
|  |     <div class="card"> | ||||||
|  |       <label for="last_state">State</label> | ||||||
|  |       <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'); | ||||||
|  |     const speedSelect = document.getElementById('speed'); | ||||||
|  | 
 | ||||||
|  |     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 = ""; | ||||||
|  |         this.steps = 0; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       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 } = {}) { | ||||||
|  |         let waitTime = 0; | ||||||
|  |         let breath = 0; | ||||||
|  |         const speed = speedSelect.value; | ||||||
|  |         if (speed === 'slllooow') { | ||||||
|  |           this.commandsPerSecond = 1; | ||||||
|  |           waitTime = 1 / this.commandsPerSecond; | ||||||
|  |           breath = 1; | ||||||
|  |         } else if (speed === 'med') { | ||||||
|  |           this.commandsPerSecond = 10; | ||||||
|  |           waitTime = 1 / this.commandsPerSecond; | ||||||
|  |           breath = 1 | ||||||
|  |         } else if (speed === 'fast') { | ||||||
|  |           this.commandsPerSecond = 1_000_000_000_000; | ||||||
|  |           waitTime = 0; | ||||||
|  |           breath = 100000; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         this.loadCodeFromString(codeString); | ||||||
|  |         this.userInput = userInput; | ||||||
|  |         let steps = 0; | ||||||
|  |         while (this.pc < this.code.length) { | ||||||
|  |           if (abortSignal) { | ||||||
|  |               console.log("Execution aborted"); | ||||||
|  |               this.savedOutput = "Execution aborted"; | ||||||
|  |               break; | ||||||
|  |             } | ||||||
|  |             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++; | ||||||
|  |             this.steps = steps; | ||||||
|  |             outElement.textContent = this.savedOutput; | ||||||
|  |             lastStateElement.textContent = this.getState(); | ||||||
|  |             // auto-scroll to the bottom | ||||||
|  |             outElement.scrollTop = outElement.scrollHeight; | ||||||
|  |             lastStateElement.scrollTop = lastStateElement.scrollHeight; | ||||||
|  |             if (steps % breath === 0) { | ||||||
|  |               await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         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); | ||||||
|  |       const fileInput = dropZone.querySelector('input'); | ||||||
|  |       const textarea = document.getElementById(textareaId); | ||||||
|  |       const dropText = document.getElementById(textId); | ||||||
|  | 
 | ||||||
|  |       ['dragenter', 'dragover'].forEach(evt => { | ||||||
|  |         dropZone.addEventListener(evt, e => { | ||||||
|  |           e.preventDefault(); dropZone.classList.add('active'); | ||||||
|  |         }); | ||||||
|  |       }); | ||||||
|  |       ['dragleave', 'drop'].forEach(evt => { | ||||||
|  |         dropZone.addEventListener(evt, e => { | ||||||
|  |           e.preventDefault(); dropZone.classList.remove('active'); | ||||||
|  |         }); | ||||||
|  |       }); | ||||||
|  |       dropZone.addEventListener('drop', e => { | ||||||
|  |         const file = e.dataTransfer.files[0]; | ||||||
|  |         if (!file) return; | ||||||
|  |         dropText.textContent = file.name; | ||||||
|  |         const reader = new FileReader(); | ||||||
|  |         reader.onload = () => textarea.value = reader.result; | ||||||
|  |         reader.readAsText(file); | ||||||
|  |       }); | ||||||
|  |       fileInput.addEventListener('change', e => { | ||||||
|  |         const file = e.target.files[0]; if (!file) return; | ||||||
|  |         dropText.textContent = file.name; | ||||||
|  |         const reader = new FileReader(); | ||||||
|  |         reader.onload = () => textarea.value = reader.result; | ||||||
|  |         reader.readAsText(file); | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     setupDropZone('code-drop', 'code', 'code-drop-text'); | ||||||
|  |     setupDropZone('input-drop', 'input', 'input-drop-text'); | ||||||
|  | 
 | ||||||
|  |     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 | ||||||
|  |     document.getElementById('run-btn').addEventListener('click', () => { | ||||||
|  |       const btn = document.getElementById('run-btn'); | ||||||
|  |       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); | ||||||
|  | 
 | ||||||
|  |       // 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> | ||||||
|  | </html> | ||||||
							
								
								
									
										3
									
								
								brainfuck/tests.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								brainfuck/tests.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,3 @@ | ||||||
|  | from django.test import TestCase | ||||||
|  | 
 | ||||||
|  | # Create your tests here. | ||||||
							
								
								
									
										7
									
								
								brainfuck/urls.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								brainfuck/urls.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,7 @@ | ||||||
|  | from django.urls import path | ||||||
|  | from . import views | ||||||
|  | 
 | ||||||
|  | urlpatterns = [ | ||||||
|  | 	path('', views.index), | ||||||
|  | ] | ||||||
|  | 
 | ||||||
							
								
								
									
										8
									
								
								brainfuck/views.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								brainfuck/views.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,8 @@ | ||||||
|  | from django.template.response import TemplateResponse | ||||||
|  | from django.http import JsonResponse as JSONResponse | ||||||
|  | from urllib.parse import parse_qsl, unquote_plus, unquote | ||||||
|  | from typing import Dict | ||||||
|  | 
 | ||||||
|  | def index(request): | ||||||
|  | 	args = {} | ||||||
|  | 	return TemplateResponse(request, 'brainfuck/index.html', args) | ||||||
|  | @ -152,6 +152,7 @@ INSTALLED_APPS = ( | ||||||
| 	'vyroci', | 	'vyroci', | ||||||
| 	'sifrovacka', | 	'sifrovacka', | ||||||
| 	'novinky', | 	'novinky', | ||||||
|  | 	'brainfuck', | ||||||
| 
 | 
 | ||||||
| 	# Admin upravy: | 	# Admin upravy: | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -62,6 +62,9 @@ urlpatterns = [ | ||||||
| 
 | 
 | ||||||
| 	# Miniapka na šifrovačku | 	# Miniapka na šifrovačku | ||||||
| 	path('sifrovacka/', include('sifrovacka.urls')), | 	path('sifrovacka/', include('sifrovacka.urls')), | ||||||
|  | 
 | ||||||
|  | 	# tematku brainfuck | ||||||
|  | 	path('brainfuck/', include('brainfuck.urls')), | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| # This is only needed when using runserver. | # This is only needed when using runserver. | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue