version one works, dont look at the, code, only output
This commit is contained in:
		
							parent
							
								
									8bde5c7e4b
								
							
						
					
					
						commit
						ac29780e6b
					
				
					 12 changed files with 447 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' | ||||
							
								
								
									
										218
									
								
								brainfuck/interpreter.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										218
									
								
								brainfuck/interpreter.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,218 @@ | |||
| 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() | ||||
							
								
								
									
										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. | ||||
							
								
								
									
										153
									
								
								brainfuck/templates/brainfuck/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								brainfuck/templates/brainfuck/index.html
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,153 @@ | |||
| <!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; | ||||
|     } | ||||
|     * { 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"], input[type="file"] { | ||||
|       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 { background: var(--primary-light); } | ||||
|     .flex { display: flex; gap: 1rem; } | ||||
|     .flex .card { flex: 1; } | ||||
|     #output, #last_state { background: #1e293b; color: #d1d5db; height: 150px; overflow-y: auto; } | ||||
|   </style> | ||||
| </head> | ||||
| <body> | ||||
|   <div class="container"> | ||||
|     <h1>Brainfuck Compiler</h1> | ||||
|     <form id="bf-form" class="card"> | ||||
|       <div class="flex"> | ||||
|         <div> | ||||
|           <label for="code">Code</label> | ||||
|           <textarea id="code" placeholder="Enter Brainfuck code, upload .bf, .in, or .txt file..."></textarea> | ||||
|           <input type="file" id="code-file" accept=".bf, .in, .txt"> | ||||
|         </div> | ||||
|         <div> | ||||
|           <label for="input">Input</label> | ||||
|           <textarea id="input" placeholder="Program input, upload .in or .txt..."></textarea> | ||||
|           <input type="file" id="input-file" accept=".txt, .in"> | ||||
|         </div> | ||||
|       </div> | ||||
|       <button type="button" id="run-btn" class="btn">Run</button> | ||||
|     </form> | ||||
| 
 | ||||
|     <div class="card"> | ||||
|       <label for="output">Output</label> | ||||
|       <div id="output"></div> | ||||
|     </div> | ||||
| 
 | ||||
|     <div class="card"> | ||||
|       <label for="last_state">Stav</label> | ||||
|       <div id="last_state"></div> | ||||
|     </div> | ||||
|   </div> | ||||
| 
 | ||||
|   <script> | ||||
|     // Load file into textarea | ||||
|     document.getElementById('code-file').addEventListener('change', function(e) { | ||||
|       const file = e.target.files[0]; | ||||
|       if (!file) return; | ||||
|       const reader = new FileReader(); | ||||
|       reader.onload = () => document.getElementById('code').value = reader.result; | ||||
|       reader.readAsText(file); | ||||
|     }); | ||||
|     document.getElementById('input-file').addEventListener('change', function(e) { | ||||
|       const file = e.target.files[0]; | ||||
|       if (!file) return; | ||||
|       const reader = new FileReader(); | ||||
|       reader.onload = () => document.getElementById('input').value = reader.result; | ||||
|       reader.readAsText(file); | ||||
|     }); | ||||
| 
 | ||||
|     // Brainfuck Interpreter | ||||
|     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; | ||||
|     } | ||||
| 
 | ||||
|     document.getElementById('run-btn').addEventListener('click', () => { | ||||
|       const code = document.getElementById('code').value; | ||||
|       const input = document.getElementById('input').value; | ||||
|       // const output = runBrainfuck(code, input); | ||||
|       // here compilation... | ||||
|       url = '/brainfuck/process_code?code=' + encodeURI(code) + "&user_input=" + encodeURI(input); | ||||
|       console.log(url); | ||||
|       console.log(encodeURI(code)); | ||||
|       fetch(url, { | ||||
|         method: 'GET', | ||||
|         headers: { 'Content-Type': 'application/json' }, | ||||
|       }).then(Response => Response.json()) | ||||
|       .then(data => { | ||||
|         console.log(data); | ||||
|         document.getElementById('output').textContent = data.output; | ||||
|         document.getElementById('last_state').textContent = data.end_state; | ||||
|       }) | ||||
| 
 | ||||
|        | ||||
|       // fetch('/brainfuck/', { | ||||
|       //   method: 'POST', | ||||
|       //   headers: { 'Content-Type': 'application/json' }, | ||||
|       //   body: body | ||||
|       // }).then(Response => Response.json()) | ||||
|       // .then(data => { | ||||
|       //   console.log(data); | ||||
|       // }) | ||||
|     }); | ||||
|   </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. | ||||
							
								
								
									
										8
									
								
								brainfuck/urls.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								brainfuck/urls.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,8 @@ | |||
| from django.urls import path | ||||
| from . import views | ||||
| 
 | ||||
| urlpatterns = [ | ||||
| 	path('', views.index), | ||||
| 	path('process_code', views.process_code), | ||||
| ] | ||||
| 
 | ||||
							
								
								
									
										49
									
								
								brainfuck/views.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								brainfuck/views.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,49 @@ | |||
| from django.template.response import TemplateResponse | ||||
| from django.http import JsonResponse as JSONResponse | ||||
| from .interpreter import Interpreter | ||||
| from urllib.parse import parse_qsl, unquote_plus, unquote | ||||
| 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) | ||||
| 	# should be try/except? | ||||
| 	try: | ||||
| 		interpreter.execute_loaded_code() | ||||
| 		out = interpreter.saved_output	 | ||||
| 	except Exception as e: | ||||
| 		print('Error:', e) | ||||
| 		out = 'Error: Code execution failed' | ||||
| 	 | ||||
| 	end_state = interpreter.print_data(interpreter.last_step) | ||||
| 	 | ||||
| 	return JSONResponse({ | ||||
| 		'code': code, | ||||
| 		'input_data': input_data, | ||||
| 		'output': out, | ||||
| 		'end_state': end_state, | ||||
| 	}) | ||||
|  | @ -152,6 +152,7 @@ INSTALLED_APPS = ( | |||
| 	'vyroci', | ||||
| 	'sifrovacka', | ||||
| 	'novinky', | ||||
| 	'brainfuck', | ||||
| 
 | ||||
| 	# Admin upravy: | ||||
| 
 | ||||
|  |  | |||
|  | @ -62,6 +62,9 @@ urlpatterns = [ | |||
| 
 | ||||
| 	# Miniapka na šifrovačku | ||||
| 	path('sifrovacka/', include('sifrovacka.urls')), | ||||
| 
 | ||||
| 	# tematku brainfuck | ||||
| 	path('brainfuck/', include('brainfuck.urls')), | ||||
| ] | ||||
| 
 | ||||
| # This is only needed when using runserver. | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue