diff --git a/__pycache__/back.cpython-312.pyc b/__pycache__/back.cpython-312.pyc new file mode 100644 index 0000000..85c08c1 Binary files /dev/null and b/__pycache__/back.cpython-312.pyc differ diff --git a/app2.py b/app2.py index df942c8..db491ea 100644 --- a/app2.py +++ b/app2.py @@ -1,112 +1,142 @@ -import sys -import back -from flask import Flask, request, jsonify, send_from_directory +from flask import Flask, request, jsonify, send_from_directory, render_template from io import StringIO from multiprocessing import Process, Queue +import sys +import back import time - -app = Flask(__name__, template_folder='templates') +from threading import Thread +app = Flask(__name__) sessions = {} # 存放執行中的程式狀態 - class InputNeeded(Exception): - """ 自定義異常,用於標記需要輸入的情況 """ pass +class OutputWrapper: + def __init__(self, output_queue): + self.output_queue = output_queue + + def write(self, text): + if text.strip(): # 避免空內容 + self.output_queue.put({"output": text}) + + def flush(self): + pass + class InputMock: - """ 模擬 input() 來攔截 back.py 的輸入 """ def __init__(self, input_queue, output_queue): - self.input_queue = input_queue # 用於接收主進程的輸入 - self.output_queue = output_queue # 用於通知主進程需要輸入 + self.input_queue = input_queue + self.output_queue = output_queue def readline(self): - """ 模擬 input(),若無輸入則通知主進程並等待 """ self.output_queue.put({"needsInput": True}) - user_input = self.input_queue.get() # 等待主進程提供輸入 - return user_input + '\n' + return self.input_queue.get() + '\n' def run_in_process(session_id, code, input_queue, output_queue): - """ 在子進程中執行程式碼 """ sys.stdin = InputMock(input_queue, output_queue) - sys.stdout = StringIO() + sys.stdout = OutputWrapper(output_queue) try: executor = back.Executor() executor.execute(code) - output = sys.stdout.getvalue() - output_queue.put({"output": output, "completed": True}) + output_queue.put({"completed": True}) except InputNeeded: - output = sys.stdout.getvalue() - output_queue.put({"output": output, "needsInput": True}) + pass except Exception as e: output_queue.put({"output": str(e), "completed": True}) finally: sys.stdout = sys.__stdout__ sys.stdin = sys.__stdin__ +def output_listener(session_id, output_queue): + session = sessions[session_id] + while True: + try: + msg = output_queue.get(timeout=0.1) + if 'output' in msg: + session["output_buffer"] += msg["output"] + if 'needsInput' in msg: + print("needsInput", msg) + session["needs_input"] = True + # 這裡不跳出循環,繼續監聽 + if 'completed' in msg: + session["completed"] = True + break + except Exception as e: + if session['process'].exitcode is not None: + break + # 這裡加入一個小延遲,避免過度消耗CPU + time.sleep(0.01) + @app.route('/') def serve_index(): - return send_from_directory('templates', 'index2.html') + return render_template('index2.html') @app.route('/run', methods=['POST']) def run_code(): - """ 開始執行程式 """ - session_id = str(len(sessions)) # 產生唯一的 session ID - input_queue = Queue() # 子進程接收輸入的隊列 - output_queue = Queue() # 子進程發送結果的隊列 + session_id = str(len(sessions)) + input_queue = Queue() + output_queue = Queue() - # 儲存會話資訊 sessions[session_id] = { - "code": request.json['code'], - "output": "", + "output_buffer": "", # 用於儲存輸出內容 "input_queue": input_queue, "output_queue": output_queue, - "completed": False + "process": None, + "completed": False, + "needs_input": False } - # 啟動子進程 - process = Process(target=run_in_process, args=(session_id, sessions[session_id]["code"], input_queue, output_queue)) - sessions[session_id]["process"] = process - process.start() - - # 等待子進程的初步結果 - result = output_queue.get() - sessions[session_id]["output"] += result.get("output", "") - if result.get("completed", False): - sessions[session_id]["completed"] = True - process.join() # 清理已完成的進程 - del sessions[session_id]["process"] - - return jsonify({ - "sessionId": session_id, - "output": sessions[session_id]["output"], - "needsInput": result.get("needsInput", False) - }) + p = Process(target=run_in_process, args=(session_id, request.json['code'], input_queue, output_queue)) + sessions[session_id]["process"] = p + p.start() + + # 啟動輸出監聽線程 + listener_thread = Thread(target=output_listener, args=(session_id, output_queue)) + listener_thread.daemon = True # 設置為daemon thread,主程序結束時會自動終止 + listener_thread.start() + sessions[session_id]["listener_thread"] = listener_thread # 保存線程引用以便後續管理 + + return jsonify({"sessionId": session_id}) + +@app.route('/status/') +def get_status(session_id): + session = sessions.get(session_id) + if not session: + return jsonify({"error": "Session not found"}), 404 + + response = { + "output": session["output_buffer"], + "needsInput": session["needs_input"], + "completed": session["completed"] + } + + session["output_buffer"] = "" # 清空緩衝 + + return jsonify(response) @app.route('/submit_input', methods=['POST']) def submit_input(): - """ 接收使用者的輸入並繼續執行 """ - session_id = request.json['sessionId'] - user_input = request.json['input'] + data = request.get_json() + print("Received input data:", data) + if not data or 'sessionId' not in data or 'input' not in data: + return jsonify({"error": "無效的請求格式"}), 400 + + session_id = data['sessionId'] + user_input = data['input'] if session_id not in sessions: - return jsonify({"error": "Session not found"}), 400 + return jsonify({"error": "Session 不存在"}), 404 session = sessions[session_id] - session["input_queue"].put(user_input) # 將輸入發送到子進程 - - # 等待子進程的結果 - result = session["output_queue"].get() - session["output"] += result.get("output", "") + + # 檢查session是否真的需要輸入 + if not session.get("needs_input", False): + print("Session doesn't need input currently") + return jsonify({"error": "當前不需要輸入"}), 401 - if result.get("completed", False): - session["completed"] = True - session["process"].join() # 清理進程 - del session["process"] + print("Submitting input:", user_input) + session["input_queue"].put(user_input) # 將輸入發送到子進程 + session["needs_input"] = False # 標記輸入已完成 - return jsonify({ - "sessionId": session_id, - "output": session["output"], - "needsInput": result.get("needsInput", False) - }) + return jsonify({"message": "輸入已提交"}) if __name__ == '__main__': - app.run(debug=True, host='0.0.0.0', port=5000, threaded=False) # 使用多進程時不需要 threaded=True + app.run(debug=True, host='0.0.0.0', port=5000, threaded=True) # 使用threaded模式以提高並發能力 \ No newline at end of file diff --git a/back.py b/back.py index 533b27c..cf00f34 100644 --- a/back.py +++ b/back.py @@ -1,58 +1,10 @@ KEYWORDS = {'加上', '減去', '乘以', '除以', '取模', '等於', '大於', '小於', '轉型'} -COMMANDS = {'要求在終端機(或控制台)輸出': (lambda x: print(evaluate(x))), - '設定一指定名稱變數的值,名稱與值分別為': (lambda x: assign_variable(x)), - '初始化一個指定名稱和長度的陣列,名稱和長度分別為': (lambda x: init_array(x)), - '設定指定名稱陣列其中一項的值,名稱、項數與值分別為': (lambda x: assign_array(x)), - '讀取使用者輸入字串,並儲存至變數': (lambda x: get_user_input(x))} IF = '如果右方判斷式結果為真,執行以下特定操作' WHILE = '重複並持續驗證右方表達式之真實性,條件滿足時持續執行特定操作' BREAK = '無視迴圈判斷式要求,直接跳脫迴圈' CONTINUE = '捨棄以下迴圈內容,直接繼續下一輪迴圈' EXIT = '無視所有指令,直接退出程式' -variables = {} -arrays = {} - -def assign_variable(expression: str): - global variables - split_result = expression.split(',') - if split_result[0][0] != '「' or split_result[0][-1] != '」' or len(split_result[0]) == 2: - raise ValueError('變數名稱有誤:'+split_result[0]) - variables[split_result[0][1:-1]] = evaluate(split_result[1]) - -def init_array(expression: str): - global arrays - split_result = expression.split(',') - if split_result[0][0] != '「' or split_result[0][-1] != '」' or len(split_result[0]) == 2: - raise ValueError('陣列名稱有誤:' + split_result[0]) - arrays[split_result[0][1:-1]] = [None for _ in range(evaluate(split_result[1]))] - - -def assign_array(expression: str): - global arrays - split_result = expression.split(',') - if split_result[0][0] != '「' or split_result[0][-1] != '」' or len(split_result[0]) == 2: - raise ValueError('陣列名稱有誤:'+split_result[0]) - arrays[split_result[0][1:-1]][int(evaluate(split_result[1]))] = evaluate(split_result[2]) - - -def get_user_input(variable: str): - global variables - if variable[0] != '「' or variable[-1] != '」' or len(variable) == 2: - raise ValueError('變數名稱有誤:'+variable) - variables[variable[1:-1]] = input('') - -def precedence(op): - if op == '等於' or op == '大於' or op == '小於': - return 1 - if op == '加上' or op == '減去': - return 2 - if op == '乘以' or op == '除以' or op == '取模': - return 3 - if op == '轉型': - return 4 - return 0 - def apply(a, b, op): if op == '加上': @@ -75,102 +27,23 @@ def apply(a, b, op): return int(a < b) if op == '轉型': if b == '數字': - return int(a) + return float(a) elif b == '字串': return str(a) else: - raise ValueError('未知型態:'+b) - - -def evaluate(expression: str): - ops = [] - values = [] - i = 0 - while i < len(expression): - if expression[i] == ' ' or expression[i] == ' ': - pass - elif expression[i] == '(': - ops.append(expression[i]) - elif expression[i] == ')': - while len(ops) != 0 and ops[-1] != '(': - val2 = values.pop() - val1 = values.pop() - op = ops.pop() - values.append(apply(val1, val2, op)) - ops.pop() - elif expression[i].isdigit() or expression[i] == '.' or expression[i] == '-': - val = '' - while i < len(expression) and (expression[i].isdigit() or expression[i] == '.' or expression[i] == '-'): - val += expression[i] - i += 1 - i -= 1 - values.append(float(val)) - elif expression[i] == '「': - token = '' - i += 1 - while expression[i] != '」': - token += expression[i] - i += 1 - values.append(token) - elif expression[i:i + 3] == '變數「': - token = '' - i += 3 - while expression[i] != '」': - token += expression[i] - i += 1 - values.append(variables[token]) + raise ValueError('未知型態:' + b) - elif expression[i:i + 3] == '陣列「': - token = '' - i += 3 - while expression[i] != '」': - token += expression[i] - i += 1 - i += 1 - if expression[i:i + 4] != '的索引(': - raise ValueError('未知的算式:' + expression) - i += 4 - tmp_exp = '' - stk = 1 - while i < len(expression): - if expression[i] == '(': - stk += 1 - elif expression[i] == ')': - stk -= 1 - tmp_exp += expression[i] - i += 1 - if stk == 0: - break - i -= 1 - values.append(arrays[token][int(evaluate(tmp_exp[:-1]))]) - else: - if expression[i:i + 2] not in KEYWORDS: - raise ValueError('未知的運算子:' + expression[i]) - while len(ops) != 0 and precedence(ops[-1]) >= precedence(expression[i:i + 2]): - val2 = values.pop() - val1 = values.pop() - op = ops.pop() - values.append(apply(val1, val2, op)) - ops.append(expression[i:i + 2]) - i += 1 - i += 1 - while len(ops) != 0: - val2 = values.pop() - val1 = values.pop() - op = ops.pop() - values.append(apply(val1, val2, op)) - final = values[-1] - if type(final) == type(1.0) and final % 1.0 == 0: - return int(final) - return final - - -def run_command(command: str): - for i in COMMANDS.keys(): - if command[:len(i)] == i: - COMMANDS[i](command[len(i):]) - return - raise ValueError('未知指令:'+command) + +def precedence(op): + if op == '等於' or op == '大於' or op == '小於': + return 1 + if op == '加上' or op == '減去': + return 2 + if op == '乘以' or op == '除以' or op == '取模': + return 3 + if op == '轉型': + return 4 + return 0 def get_clause(lines: list[str], start_line: int): @@ -185,55 +58,180 @@ def get_clause(lines: list[str], start_line: int): return j -def exec_separate_lines(lines: list[str]): - i = 0 - while i < len(lines): - if lines[i] == BREAK: - return -1 - if lines[i] == CONTINUE: - return -2 - if lines[i] == EXIT: - return 0 - if lines[i][:len(IF)] == IF: - j = get_clause(lines, i) - if evaluate(lines[i][len(IF):]): - ret = exec_separate_lines(lines[i+1:j]) - if ret is not None: - return ret - i = j - elif lines[i][:len(WHILE)] == WHILE: - j = get_clause(lines, i) - while evaluate(lines[i][len(WHILE):]): - ret = exec_separate_lines(lines[i+1:j]) - if ret == -1: - break - if ret == 0: - return 0 - i = j - else: - run_command(lines[i]) - i += 1 - - -def execute(lines: str): - split_lines = lines.split('\n') - split_code = [] - for i in split_lines: - stripped = i.strip() - if stripped == '': - continue - if stripped[-1] != '。': - raise ValueError('你忘了加句點。') - split_code.append(stripped[:-1]) - exec_separate_lines(split_code) - -if __name__ == '__main__': - code = ''' -設定一指定名稱變數的值,名稱與值分別為「i」,0。 -重複並持續驗證右方表達式之真實性,條件滿足時持續執行特定操作(變數「i」小於10)。 -要求在終端機(或控制台)輸出「執行第 」加上變數「i」轉型「字串」加上「 次」。 -設定一指定名稱變數的值,名稱與值分別為「i」,變數「i」加上1。 -結束以上判斷式或迴圈。 - ''' - - execute(code) \ No newline at end of file +class Executor: + + def __init__(self): + self.COMMANDS = {'要求在終端機(或控制台)輸出': (lambda x: print(self.evaluate(x))), + '設定一指定名稱變數的值,名稱與值分別為': (lambda x: self.assign_variable(x)), + '初始化一個指定名稱和長度的陣列,名稱和長度分別為': (lambda x: self.init_array(x)), + '設定指定名稱陣列其中一項的值,名稱、項數與值分別為': (lambda x: self.assign_array(x)), + '讀取使用者輸入字串,並儲存至變數': (lambda x: self.get_user_input(x)), + '將一字串拆分成字元陣列,並將結果儲存至指定名稱的陣列,字串和陣列分別為': (lambda x: self.split_array(x))} + + self.variables = {} + self.arrays = {} + + def run_command(self, command: str): + for i in self.COMMANDS.keys(): + if command[:len(i)] == i: + self.COMMANDS[i](command[len(i):]) + return + raise ValueError('未知指令:' + command) + + def evaluate(self, expression: str): + ops = [] + values = [] + i = 0 + while i < len(expression): + if expression[i] == ' ' or expression[i] == ' ': + pass + elif expression[i] == '(': + ops.append(expression[i]) + elif expression[i] == ')': + while len(ops) != 0 and ops[-1] != '(': + val2 = values.pop() + val1 = values.pop() + op = ops.pop() + values.append(apply(val1, val2, op)) + ops.pop() + elif expression[i].isdigit() or expression[i] == '.' or expression[i] == '-': + val = '' + while i < len(expression) and (expression[i].isdigit() or expression[i] == '.' or expression[i] == '-'): + val += expression[i] + i += 1 + i -= 1 + values.append(float(val)) + elif expression[i] == '「': + token = '' + i += 1 + while expression[i] != '」': + token += expression[i] + i += 1 + values.append(token) + elif expression[i:i + 3] == '變數「': + token = '' + i += 3 + while expression[i] != '」': + token += expression[i] + i += 1 + values.append(self.variables[token]) + + elif expression[i:i + 3] == '陣列「': + token = '' + i += 3 + while expression[i] != '」': + token += expression[i] + i += 1 + i += 1 + if expression[i:i + 4] == '的索引(': + i += 4 + tmp_exp = '' + stk = 1 + while i < len(expression): + if expression[i] == '(': + stk += 1 + elif expression[i] == ')': + stk -= 1 + tmp_exp += expression[i] + i += 1 + if stk == 0: + break + i -= 1 + values.append(self.arrays[token][int(self.evaluate(tmp_exp[:-1]))]) + elif expression[i:i + 3] == '的長度': + i += 2 + values.append(len(self.arrays[token])) + else: + raise ValueError('未知的算式:' + expression) + else: + if expression[i:i + 2] not in KEYWORDS: + raise ValueError('未知的運算子:' + expression[i]) + while len(ops) != 0 and precedence(ops[-1]) >= precedence(expression[i:i + 2]): + val2 = values.pop() + val1 = values.pop() + op = ops.pop() + values.append(apply(val1, val2, op)) + ops.append(expression[i:i + 2]) + i += 1 + i += 1 + while len(ops) != 0: + val2 = values.pop() + val1 = values.pop() + op = ops.pop() + values.append(apply(val1, val2, op)) + final = values[-1] + if type(final) == type(1.0) and final % 1.0 == 0: + return int(final) + return final + + def assign_variable(self, expression: str): + split_result = expression.split(',') + if split_result[0][0] != '「' or split_result[0][-1] != '」' or len(split_result[0]) == 2: + raise ValueError('變數名稱有誤:' + split_result[0]) + self.variables[split_result[0][1:-1]] = self.evaluate(split_result[1]) + + def init_array(self, expression: str): + split_result = expression.split(',') + if split_result[0][0] != '「' or split_result[0][-1] != '」' or len(split_result[0]) == 2: + raise ValueError('陣列名稱有誤:' + split_result[0]) + self.arrays[split_result[0][1:-1]] = [None for _ in range(self.evaluate(split_result[1]))] + + def assign_array(self, expression: str): + split_result = expression.split(',') + if split_result[0][0] != '「' or split_result[0][-1] != '」' or len(split_result[0]) == 2: + raise ValueError('陣列名稱有誤:' + split_result[0]) + self.arrays[split_result[0][1:-1]][int(self.evaluate(split_result[1]))] = self.evaluate(split_result[2]) + + def get_user_input(self, variable: str): + if variable[0] != '「' or variable[-1] != '」' or len(variable) == 2: + raise ValueError('變數名稱有誤:' + variable) + self.variables[variable[1:-1]] = input('') + + def split_array(self, expression: str): + split_result = expression.split(',') + if split_result[1][0] != '「' or split_result[1][-1] != '」' or len(split_result[1]) == 2: + raise ValueError('陣列名稱有誤:' + split_result[1]) + self.arrays[split_result[1][1:-1]] = list(str(self.evaluate(split_result[0]))) + + def exec_separate_lines(self, lines: list[str]): + i = 0 + while i < len(lines): + if lines[i] == BREAK: + return -1 + if lines[i] == CONTINUE: + return -2 + if lines[i] == EXIT: + return 0 + if lines[i][:len(IF)] == IF: + j = get_clause(lines, i) + if self.evaluate(lines[i][len(IF):]): + ret = self.exec_separate_lines(lines[i + 1:j]) + if ret is not None: + return ret + i = j + elif lines[i][:len(WHILE)] == WHILE: + j = get_clause(lines, i) + while self.evaluate(lines[i][len(WHILE):]): + ret = self.exec_separate_lines(lines[i + 1:j]) + if ret == -1: + break + if ret == 0: + return 0 + i = j + else: + self.run_command(lines[i]) + i += 1 + + def execute(self, lines: str): + self.variables = {} + self.arrays = {} + split_lines = lines.split('\n') + split_code = [] + for i in split_lines: + stripped = i.strip() + if stripped == '': + continue + if stripped[-1] != '。': + raise ValueError('你忘了加句點。') + split_code.append(stripped[:-1]) + self.exec_separate_lines(split_code) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..f52ba16 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +flask +io +multiprocessing +threading \ No newline at end of file diff --git a/static/css/styles.css b/static/css/styles.css new file mode 100644 index 0000000..44da57f --- /dev/null +++ b/static/css/styles.css @@ -0,0 +1,176 @@ +body { + font-family: 'Microsoft JhengHei', Arial, sans-serif; + line-height: 1.6; + margin: 0; + padding: 20px; + background-color: #f5f5f5; +} + +.container { + max-width: 1000px; + margin: 0 auto; + background-color: white; + padding: 30px; + border-radius: 10px; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); +} + +h2 { + color: #2c3e50; + margin-bottom: 25px; + text-align: center; + font-size: 28px; +} + +#codeEditor { + width: 100%; + height: 300px; + padding: 15px; + border: 2px solid #e0e0e0; + border-radius: 8px; + font-family: 'Consolas', monospace; + font-size: 14px; + resize: vertical; + background-color: #f8f9fa; + transition: border-color 0.3s; +} + +#codeEditor:focus { + outline: none; + border-color: #4CAF50; + box-shadow: 0 0 5px rgba(76, 175, 80, 0.2); +} + +#runCodeButton { + background-color: #4CAF50; + color: white; + border: none; + padding: 12px 25px; + border-radius: 5px; + cursor: pointer; + font-size: 16px; + transition: background-color 0.3s; + margin: 20px 10px 20px 0; +} + +#runCodeButton:hover { + background-color: #45a049; +} + +#triangleExampleButton { + background-color: #3498db; + color: white; + border: none; + padding: 12px 25px; + border-radius: 5px; + cursor: pointer; + font-size: 16px; + transition: background-color 0.3s; + margin: 20px 10px; +} + +#triangleExampleButton:hover { + background-color: #2980b9; +} + +#quadraticExampleButton { + background-color: #e67e22; + color: white; + border: none; + padding: 12px 25px; + border-radius: 5px; + cursor: pointer; + font-size: 16px; + transition: background-color 0.3s; + margin: 20px 0; +} + +#quadraticExampleButton:hover { + background-color: #d35400; +} + +#executionResult { + background: #2c3e50; + color: #ecf0f1; + padding: 20px; + border-radius: 8px; + margin-top: 20px; + white-space: pre-wrap; + text-align: left; + font-family: 'Consolas', monospace; + font-size: 14px; + min-height: 100px; +} + +.loading { + opacity: 0.7; + pointer-events: none; +} + +.intro-section { + margin-bottom: 30px; + padding: 20px; + background-color: #f8f9fa; + border-radius: 8px; +} + +.syntax-table { + width: 100%; + border-collapse: collapse; + margin-top: 30px; +} + +.syntax-table th, +.syntax-table td { + border: 1px solid #e0e0e0; + padding: 10px; + text-align: left; +} + +.syntax-table th { + background-color: #2c3e50; + color: white; +} + +#inputSection { + margin-top: 10px; + display: none; +} + +#userInput { + width: 70%; + padding: 8px; + border: 2px solid #e0e0e0; + border-radius: 5px; + font-family: 'Consolas', monospace; +} + +#submitInput { + background-color: #4CAF50; + color: white; + border: none; + padding: 8px 15px; + border-radius: 5px; + cursor: pointer; + margin-left: 10px; +} + +#submitInput:hover { + background-color: #45a049; +} + +#twentyOneExampleButton { + background-color: #e4e412; + color: white; + border: none; + padding: 12px 25px; + border-radius: 5px; + cursor: pointer; + font-size: 16px; + transition: background-color 0.3s; + margin: 20px 10px; +} + +#twentyOneExampleButton:hover { + background-color: #d6d612; +} \ No newline at end of file diff --git a/static/js/script.js b/static/js/script.js new file mode 100644 index 0000000..825f1b2 --- /dev/null +++ b/static/js/script.js @@ -0,0 +1,240 @@ +const runButton = document.getElementById('runCodeButton'); +const triangleExampleButton = document.getElementById('triangleExampleButton'); +const quadraticExampleButton = document.getElementById('quadraticExampleButton'); +const twentyOneExampleButton = document.getElementById('twentyOneExampleButton'); // 新增按鈕 +const editor = document.getElementById('codeEditor'); +const result = document.getElementById('executionResult'); +const inputSection = document.getElementById('inputSection'); +const userInput = document.getElementById('userInput'); +const submitInput = document.getElementById('submitInput'); +const API_BASE_URL = "http://10.219.58.131:5000"; // 指定 API 伺服器的 IP 和端口 +let sessionId = null; +// 三角形範例程式碼 +const triangleExampleCode = ` +要求在終端機(或控制台)輸出「輸入三角形的三邊長(以換行隔開):」。 +讀取使用者輸入字串,並儲存至變數「a」。 +設定一指定名稱變數的值,名稱與值分別為「a」,變數「a」轉型「數字」。 +讀取使用者輸入字串,並儲存至變數「b」。 +設定一指定名稱變數的值,名稱與值分別為「b」,變數「b」轉型「數字」。 +讀取使用者輸入字串,並儲存至變數「c」。 +設定一指定名稱變數的值,名稱與值分別為「c」,變數「c」轉型「數字」。 +設定一指定名稱變數的值,名稱與值分別為「i」,0。 +重複並持續驗證右方表達式之真實性,條件滿足時持續執行特定操作(1)。 +如果右方判斷式結果為真,執行以下特定操作(變數「a」大於變數「b」)。 +設定一指定名稱變數的值,名稱與值分別為「t」,變數「a」。 +設定一指定名稱變數的值,名稱與值分別為「a」,變數「b」。 +設定一指定名稱變數的值,名稱與值分別為「b」,變數「t」。 +結束以上判斷式或迴圈。 +如果右方判斷式結果為真,執行以下特定操作(變數「i」大於1)。 +無視迴圈判斷式要求,直接跳脫迴圈。 +結束以上判斷式或迴圈。 +如果右方判斷式結果為真,執行以下特定操作(變數「b」大於變數「c」)。 +設定一指定名稱變數的值,名稱與值分別為「t」,變數「b」。 +設定一指定名稱變數的值,名稱與值分別為「b」,變數「c」。 +設定一指定名稱變數的值,名稱與值分別為「c」,變數「t」。 +結束以上判斷式或迴圈。 +設定一指定名稱變數的值,名稱與值分別為「i」,變數「i」加上1。 +結束以上判斷式或迴圈。 +要求在終端機(或控制台)輸出「排序後的三邊長:」。 +要求在終端機(或控制台)輸出變數「a」轉型「字串」加上「 」加上變數「b」轉型「字串」加上「 」加上變數「c」轉型「字串」加上「 」。 +如果右方判斷式結果為真,執行以下特定操作 1 減去(變數「a」加上變數「b」大於變數「c」)。 +要求在終端機(或控制台)輸出「不構成三角形。」。 +無視所有指令,直接退出程式。 +結束以上判斷式或迴圈。 +如果右方判斷式結果為真,執行以下特定操作(變數「a」乘以變數「a」加上變數「b」乘以變數「b」等於變數「c」乘以變數「c」)。 +要求在終端機(或控制台)輸出「直角三角形。」。 +結束以上判斷式或迴圈。 +如果右方判斷式結果為真,執行以下特定操作(變數「a」乘以變數「a」加上變數「b」乘以變數「b」小於變數「c」乘以變數「c」)。 +要求在終端機(或控制台)輸出「鈍角三角形。」。 +結束以上判斷式或迴圈。 +如果右方判斷式結果為真,執行以下特定操作(變數「a」乘以變數「a」加上變數「b」乘以變數「b」大於變數「c」乘以變數「c」)。 +要求在終端機(或控制台)輸出「銳角三角形。」。 +結束以上判斷式或迴圈。 +`.trim(); + +// 一元二次方程式範例程式碼 +const quadraticExampleCode = ` +要求在終端機(或控制台)輸出「請輸入 x^2 項係數 ,此項必須不為 0」。 +讀取使用者輸入字串,並儲存至變數「a」。 +設定一指定名稱變數的值,名稱與值分別為「a」,變數「a」轉型「數字」。 +如果右方判斷式結果為真,執行以下特定操作(變數「a」等於 0)。 +要求在終端機(或控制台)輸出「使用者輸入錯誤,導致 RE 」。 +無視所有指令,直接退出程式。 +結束以上判斷式或迴圈。 +要求在終端機(或控制台)輸出「請輸入 x 項係數」。 +讀取使用者輸入字串,並儲存至變數「b」。 +設定一指定名稱變數的值,名稱與值分別為「b」,變數「b」轉型「數字」。 +要求在終端機(或控制台)輸出「請輸入常數項係數」。 +讀取使用者輸入字串,並儲存至變數「c」。 +設定一指定名稱變數的值,名稱與值分別為「c」,變數「c」轉型「數字」。 +如果右方判斷式結果為真,執行以下特定操作 (變數「b」 乘以 變數「b」 大於 4 乘以 變數「a」 乘以 變數「c」)。 +要求在終端機(或控制台)輸出「方程式有兩個相異的實數解」。 +結束以上判斷式或迴圈。 +如果右方判斷式結果為真,執行以下特定操作 (變數「b」 乘以 變數「b」 等於 4 乘以 變數「a」 乘以 變數「c」)。 +要求在終端機(或控制台)輸出「方程式有兩個一樣的實數解」。 +結束以上判斷式或迴圈。 +如果右方判斷式結果為真,執行以下特定操作 (變數「b」 乘以 變數「b」 小於 4 乘以 變數「a」 乘以 變數「c」)。 +要求在終端機(或控制台)輸出「方程式有兩個虛數解」。 +結束以上判斷式或迴圈。 +`.trim(); + +// 搶21點範例程式碼 +const twentyOneExampleCode = ` +要求在終端機(或控制台)輸出「搶 21 遊戲,玩家先手,每次可選擇 1~3」。 +要求在終端機(或控制台)輸出「輪到玩家,目前剩下 21 」。 +設定一指定名稱變數的值,名稱與值分別為「x」,21。 +讀取使用者輸入字串,並儲存至變數「a」。 +設定一指定名稱變數的值,名稱與值分別為「a」,變數「a」轉型「數字」。 +重複並持續驗證右方表達式之真實性,條件滿足時持續執行特定操作((變數「a」小於 4)加上(變數「a」大於 0)加上 (變數「x」減去 變數「a」加上 1 大於 0)小於 3)。 +要求在終端機(或控制台)輸出「輸入無效,請重新輸入」。 +讀取使用者輸入字串,並儲存至變數「a」。 +設定一指定名稱變數的值,名稱與值分別為「a」,變數「a」轉型「數字」。 +結束以上判斷式或迴圈。 +設定一指定名稱變數的值,名稱與值分別為「x」,變數「x」減去 變數「a」。 +設定一指定名稱變數的值,名稱與值分別為「p」,1。 + +重複並持續驗證右方表達式之真實性,條件滿足時持續執行特定操作(變數「x」大於 0)。 +設定一指定名稱變數的值,名稱與值分別為「e」,0。 +如果右方判斷式結果為真,執行以下特定操作(變數「x」取模 4 等於 0)。 +要求在終端機(或控制台)輸出「電腦選擇 2 」。 +設定一指定名稱變數的值,名稱與值分別為「x」,變數「x」減去 2。 +設定一指定名稱變數的值,名稱與值分別為「e」,1。 +結束以上判斷式或迴圈。 +如果右方判斷式結果為真,執行以下特定操作(變數「e」等於 0)。 +設定一指定名稱變數的值,名稱與值分別為「c」,變數「x」取模 4。 +要求在終端機(或控制台)輸出(「電腦選擇 」加上(變數「c」轉型 「字串」))。 +設定一指定名稱變數的值,名稱與值分別為「x」,變數「x」減去 變數「c」。 +設定一指定名稱變數的值,名稱與值分別為「p」,0。 +結束以上判斷式或迴圈。 +如果右方判斷式結果為真,執行以下特定操作(變數「x」等於 0)。 +要求在終端機(或控制台)輸出「電腦獲勝」。 +無視所有指令,直接退出程式。 +結束以上判斷式或迴圈。 +要求在終端機(或控制台)輸出(「輪到玩家,目前剩下 」加上(變數「x」轉型「字串」))。 +讀取使用者輸入字串,並儲存至變數「a」。 +設定一指定名稱變數的值,名稱與值分別為「a」,變數「a」轉型「數字」。 +重複並持續驗證右方表達式之真實性,條件滿足時持續執行特定操作((變數「a」小於 4)加上(變數「a」大於 0)加上 (變數「x」減去 變數「a」加上 1 大於 0)小於 3)。 +要求在終端機(或控制台)輸出「輸入無效,請重新輸入」。 +讀取使用者輸入字串,並儲存至變數「a」。 +設定一指定名稱變數的值,名稱與值分別為「a」,變數「a」轉型「數字」。 +結束以上判斷式或迴圈。 +設定一指定名稱變數的值,名稱與值分別為「x」,變數「x」減去 變數「a」。 +設定一指定名稱變數的值,名稱與值分別為「p」,1。 +如果右方判斷式結果為真,執行以下特定操作(變數「x」等於 0)。 +要求在終端機(或控制台)輸出「玩家獲勝」。 +無視所有指令,直接退出程式。 +結束以上判斷式或迴圈。 +結束以上判斷式或迴圈。 +`.trim(); + +// 執行按鈕事件 +runButton.addEventListener('click', async () => { + const code = editor.value; + runButton.disabled = true; + runButton.textContent = '執行中...'; + result.textContent = '處理中...'; + inputSection.style.display = 'none'; + + try { + const response = await fetch('/run', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ code }) + }); + + const resultData = await response.json(); + sessionId = resultData.sessionId; + + // 開始輪詢狀態 + pollStatus(); + } catch (error) { + result.textContent = '前端請求錯誤或連線失敗。'; + resetButton(); + } +}); + +// 輪詢狀態 +const pollStatus = async () => { + if (!sessionId) return; + + try { + const response = await fetch(`/status/${sessionId}`); + const data = await response.json(); + + if (data.error) { + result.textContent = data.error; + resetButton(); + return; + } + + if (data.output) { + result.textContent += data.output; + } + + if (data.needsInput) { + inputSection.style.display = 'block'; + userInput.focus(); + } else if (data.completed) { + resetButton(); + } else { + setTimeout(pollStatus, 300); // 繼續輪詢 + } + } catch (error) { + result.textContent = '狀態輪詢失敗。'; + resetButton(); + } +}; + +// 提交輸入事件 +submitInput.addEventListener('click', async () => { + const inputValue = userInput.value.trim(); + if (!inputValue || !sessionId) { + alert('請輸入內容或確保 sessionId 存在'); + return; + } + + try { + const response = await fetch('/submit_input', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ sessionId, input: inputValue }) + }); + + if (!response.ok) { + throw new Error('提交輸入失敗'); + } + + userInput.value = ''; // 清空輸入框 + pollStatus(); // 繼續輪詢狀態 + } catch (error) { + result.textContent += '\n輸入處理失敗:' + error.message; + } +}); + +// 三角形範例按鈕事件 +triangleExampleButton.addEventListener('click', () => { + editor.value = triangleExampleCode; + result.textContent = '已載入三角形範例程式,請點擊「執行程式碼」運行。'; + editor.focus(); +}); + +// 一元二次方程式範例按鈕事件 +quadraticExampleButton.addEventListener('click', () => { + editor.value = quadraticExampleCode; + result.textContent = '已載入一元二次方程式範例程式,請點擊「執行程式碼」運行。'; + editor.focus(); +}); + +// 搶21點範例按鈕事件 +twentyOneExampleButton.addEventListener('click', () => { + editor.value = twentyOneExampleCode; + result.textContent = '已載入搶21點範例程式,請點擊「執行程式碼」運行。'; + editor.focus(); +}); + +// 重置按鈕狀態 +const resetButton = () => { + runButton.disabled = false; + runButton.textContent = '執行程式碼 ▶'; + sessionId = null; +}; \ No newline at end of file diff --git a/templates/index2.html b/templates/index2.html new file mode 100644 index 0000000..69452b0 --- /dev/null +++ b/templates/index2.html @@ -0,0 +1,98 @@ + + + + + + ACcode線上直譯器 + + + + +
+
+

基本介紹

+

這裡是基於python開發的直譯器。

+

如果有任何問題請看下方的對照表。

+
+ +

ACcode線上直譯器

+ + + + + +
執行結果將顯示在這裡...
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Python語法ACcode語法
print(...)要求在終端機(或控制台)輸出 ... 。
... = input()讀取使用者輸入字串,並儲存至變數「...」
... = ....設定一指定名稱變數的值,名稱與值分別為 ... , ....
... = [None for _ in range(....)]初始化一個指定名稱和長度的陣列,名稱和長度分別為 ... , ....
if ... :如果右方判斷式結果為真,執行以下特定操作
while ... :重複並持續驗證右方表達式之真實性,條件滿足時持續執行特定操作 ...
取消縮排結束以上判斷式或迴圈
break無視迴圈判斷式要求,直接跳脫迴圈
continue捨棄以下迴圈內容,直接繼續下一輪迴圈
exit()無視所有指令,直接退出程式
+, -, *, /, %加上, 減去, 乘以, 除以, 取模
=, >, <等於, 大於, 小於
float(...), str(...)... 轉型 「數字」, ... 轉型 「字串」
... (變數), ...[....](清單)變數「...」, 陣列「...」的索引(....)
+
+ + \ No newline at end of file