From c6d280dfd823af3f5c5167c318cfb1df9cbd7cbb Mon Sep 17 00:00:00 2001 From: ticvac Date: Wed, 7 May 2025 22:51:08 +0200 Subject: [PATCH] work async, js compiler --- brainfuck/interpreter.py | 218 ---------------- brainfuck/templates/brainfuck/index.html | 303 +++++++++++++++++------ brainfuck/urls.py | 1 - brainfuck/views.py | 39 --- 4 files changed, 228 insertions(+), 333 deletions(-) delete mode 100644 brainfuck/interpreter.py diff --git a/brainfuck/interpreter.py b/brainfuck/interpreter.py deleted file mode 100644 index 804d833e..00000000 --- a/brainfuck/interpreter.py +++ /dev/null @@ -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 < - -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() diff --git a/brainfuck/templates/brainfuck/index.html b/brainfuck/templates/brainfuck/index.html index 30593d2a..a46db285 100644 --- a/brainfuck/templates/brainfuck/index.html +++ b/brainfuck/templates/brainfuck/index.html @@ -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 @@
-

Brainfuck Compiler

+

Brainfuck Compiler for M&M

@@ -95,20 +110,209 @@
+
-
+

     
-
+

     
+
Created by TicVac 2025
vaclav.tichy.mam@gmail.com
diff --git a/brainfuck/urls.py b/brainfuck/urls.py index 3b4f273d..c40a59c1 100644 --- a/brainfuck/urls.py +++ b/brainfuck/urls.py @@ -3,6 +3,5 @@ from . import views urlpatterns = [ path('', views.index), - path('process_code', views.process_code), ] diff --git a/brainfuck/views.py b/brainfuck/views.py index 1e763940..ef7d5b5d 100644 --- a/brainfuck/views.py +++ b/brainfuck/views.py @@ -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, - })