lovelyai999 commited on
Commit
d048ec1
·
verified ·
1 Parent(s): 4cacecd

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +592 -0
app.py ADDED
@@ -0,0 +1,592 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import ast
2
+ import io
3
+ import sys
4
+ import subprocess
5
+ import tempfile
6
+ import gradio as gr
7
+ import shutil
8
+ from contextlib import redirect_stdout
9
+
10
+ # =============================================================================
11
+ # Python To JavaScript 轉換器
12
+ # =============================================================================
13
+
14
+ class PythonToJavaScriptConverter(ast.NodeVisitor):
15
+ def __init__(self):
16
+ self.variables = set() # 用於追蹤已聲明的變量
17
+ self.current_scope = []
18
+ self.indent_level = 0
19
+
20
+ def indent(self):
21
+ return ' ' * self.indent_level
22
+
23
+ def visit_FunctionDef(self, node):
24
+ self.current_scope.append(node.name)
25
+ func_name = node.name
26
+ args = [arg.arg for arg in node.args.args]
27
+ defaults = [self.visit(d) for d in node.args.defaults]
28
+ args_list = []
29
+ num_args = len(args)
30
+ num_defaults = len(defaults)
31
+ for i, arg in enumerate(args):
32
+ if i >= num_args - num_defaults:
33
+ default = defaults[i - (num_args - num_defaults)]
34
+ args_list.append(f"{arg} = {default}")
35
+ else:
36
+ args_list.append(arg)
37
+ args_str = ", ".join(args_list)
38
+ self.indent_level += 1
39
+ body = "\n".join(self.visit(n) for n in node.body)
40
+ self.indent_level -= 1
41
+ self.current_scope.pop()
42
+ return f"{self.indent()}function {func_name}({args_str}) {{\n{body}\n{self.indent()}}}"
43
+
44
+ def visit_Return(self, node):
45
+ if node.value:
46
+ return f"{self.indent()}return {self.visit(node.value)};"
47
+ else:
48
+ return f"{self.indent()}return;"
49
+
50
+ def visit_Assign(self, node):
51
+ target = node.targets[0]
52
+ value = self.visit(node.value)
53
+ if isinstance(target, ast.Tuple):
54
+ targets = [self.visit(t) for t in target.elts]
55
+ if not isinstance(node.value, ast.Tuple):
56
+ value = f"[{value}]"
57
+ assignment = f"{self.indent()}let [{', '.join(targets)}] = {value};"
58
+ else:
59
+ target_str = self.visit(target)
60
+ if target_str not in self.variables:
61
+ declaration = "let "
62
+ self.variables.add(target_str)
63
+ else:
64
+ declaration = ""
65
+ assignment = f"{self.indent()}{declaration}{target_str} = {value};"
66
+ return assignment
67
+
68
+ def visit_If(self, node):
69
+ test = self.visit(node.test)
70
+ self.indent_level += 1
71
+ if_body = "\n".join(self.visit(n) for n in node.body)
72
+ self.indent_level -= 1
73
+ if node.orelse:
74
+ self.indent_level += 1
75
+ else_body = "\n".join(self.visit(n) for n in node.orelse)
76
+ self.indent_level -= 1
77
+ return f"{self.indent()}if (({test})) {{\n{if_body}\n{self.indent()}}} else {{\n{else_body}\n{self.indent()}}}"
78
+ else:
79
+ return f"{self.indent()}if (({test})) {{\n{if_body}\n{self.indent()}}}"
80
+
81
+ def visit_While(self, node):
82
+ test = self.visit(node.test)
83
+ self.indent_level += 1
84
+ body = "\n".join(self.visit(n) for n in node.body)
85
+ self.indent_level -= 1
86
+ return f"{self.indent()}while ({test}) {{\n{body}\n{self.indent()}}}"
87
+
88
+ def visit_For(self, node):
89
+ target = self.visit(node.target)
90
+ if isinstance(node.iter, ast.Call) and isinstance(node.iter.func, ast.Name) and node.iter.func.id == "range":
91
+ args = [self.visit(arg) for arg in node.iter.args]
92
+ if len(args) == 1:
93
+ iterable = f"Array.from({{length: {args[0]}}}, (_, {target}) => {target})"
94
+ elif len(args) == 2:
95
+ iterable = f"Array.from({{length: {args[1]} - {args[0]}}}, (_, {target}) => {target} + {args[0]})"
96
+ else:
97
+ iterable = f"Array.from({{length: 1}}, (_, {target}) => {target})"
98
+ else:
99
+ iterable = self.visit(node.iter)
100
+ self.indent_level += 1
101
+ body = "\n".join(self.visit(n) for n in node.body)
102
+ self.indent_level -= 1
103
+ return f"{self.indent()}for (let {target} of {iterable}) {{\n{body}\n{self.indent()}}}"
104
+
105
+ def visit_Try(self, node):
106
+ try_body = "\n".join(self.visit(n) for n in node.body)
107
+ catch_body = ""
108
+ if node.handlers:
109
+ handler_lines = []
110
+ for handler in node.handlers:
111
+ if handler.type:
112
+ type_str = self.visit(handler.type)
113
+ handler_body = "\n".join(self.visit(n) for n in handler.body)
114
+ clause = f"if (e instanceof {type_str}) {{\n{self.indent()} {handler_body}\n{self.indent()}}}"
115
+ handler_lines.append(clause)
116
+ else:
117
+ handler_body = "\n".join(self.visit(n) for n in handler.body)
118
+ handler_lines.append(f"{{\n{self.indent()} {handler_body}\n{self.indent()}}}")
119
+ catch_body = ("\n" + self.indent() + "else ").join(handler_lines)
120
+ else:
121
+ catch_body = f"{self.indent()}/* No exception handlers */"
122
+ finally_body = ""
123
+ if node.finalbody:
124
+ self.indent_level += 1
125
+ finally_body = "\n".join(self.visit(n) for n in node.finalbody)
126
+ self.indent_level -= 1
127
+ finally_body = f"\n{self.indent()}finally {{\n{finally_body}\n{self.indent()}}}"
128
+ return f"{self.indent()}try {{\n{try_body}\n{self.indent()}}} catch(e) {{\n{self.indent()} {catch_body}\n{self.indent()}}}{finally_body}"
129
+
130
+ def visit_With(self, node):
131
+ items = []
132
+ for item in node.items:
133
+ context_expr = self.visit(item.context_expr)
134
+ optional_vars = self.visit(item.optional_vars) if item.optional_vars else ""
135
+ items.append(f"{context_expr} as {optional_vars}" if optional_vars else context_expr)
136
+ with_comment = f"// with: {', '.join(items)}"
137
+ self.indent_level += 1
138
+ body = "\n".join(self.visit(n) for n in node.body)
139
+ self.indent_level -= 1
140
+ return f"{self.indent()}{with_comment}\n{body}"
141
+
142
+ def visit_Call(self, node):
143
+ func_name = ""
144
+ if isinstance(node.func, ast.Name):
145
+ func_name = node.func.id
146
+ if func_name == "print":
147
+ func = "console.log"
148
+ elif func_name == "range":
149
+ return self.handle_range(node)
150
+ elif func_name == "list":
151
+ arg = self.visit(node.args[0])
152
+ return f"Array.from({arg})"
153
+ else:
154
+ func = self.visit(node.func)
155
+ args = ", ".join(self.visit(arg) for arg in node.args)
156
+ return f"{func}({args})"
157
+
158
+ def handle_range(self, node):
159
+ args = [self.visit(arg) for arg in node.args]
160
+ if len(node.args) == 1:
161
+ return f"Array.from({{length: {args[0]}}}, (_, i) => i)"
162
+ elif len(node.args) == 2:
163
+ return f"Array.from({{length: {args[1]} - {args[0]}}}, (_, i) => i + {args[0]})"
164
+ return f"Array.from({{length: 1}})"
165
+
166
+ def visit_ListComp(self, node):
167
+ if len(node.generators) > 1:
168
+ return "/* Unsupported list comprehension with multiple generators */"
169
+ gen = node.generators[0]
170
+ target = self.visit(gen.target)
171
+ if_condition = None
172
+ if gen.ifs:
173
+ conditions = " && ".join(self.visit(cond) for cond in gen.ifs)
174
+ if_condition = conditions
175
+ iter_expr = gen.iter
176
+ if isinstance(iter_expr, ast.Call) and isinstance(iter_expr.func, ast.Name) and iter_expr.func.id == "range":
177
+ args = [self.visit(arg) for arg in iter_expr.args]
178
+ if len(iter_expr.args) == 1:
179
+ range_str = f"Array.from({{length: {args[0]}}}, (_, {target}) => {target})"
180
+ elif len(iter_expr.args) == 2:
181
+ range_str = f"Array.from({{length: {args[1]} - {args[0]}}}, (_, {target}) => {target} + {args[0]})"
182
+ else:
183
+ range_str = f"Array.from({{length: 1}}, (_, {target}) => {target})"
184
+ else:
185
+ range_str = self.visit(iter_expr)
186
+ elt = self.visit(node.elt)
187
+ if if_condition:
188
+ return f"{range_str}.filter(({target}) => {if_condition}).map(({target}) => ({elt}))"
189
+ else:
190
+ return f"{range_str}.map(({target}) => ({elt}))"
191
+
192
+ def visit_GeneratorExp(self, node):
193
+ if len(node.generators) > 1:
194
+ return "/* Unsupported generator expression with multiple generators */"
195
+ gen = node.generators[0]
196
+ target = self.visit(gen.target)
197
+ if_condition = None
198
+ if gen.ifs:
199
+ conditions = " && ".join(self.visit(cond) for cond in gen.ifs)
200
+ if_condition = conditions
201
+ iter_expr = gen.iter
202
+ if isinstance(iter_expr, ast.Call) and isinstance(iter_expr.func, ast.Name) and iter_expr.func.id == "range":
203
+ args = [self.visit(arg) for arg in iter_expr.args]
204
+ if len(iter_expr.args) == 1:
205
+ range_str = f"Array.from({{length: {args[0]}}}, (_, {target}) => {target})"
206
+ elif len(iter_expr.args) == 2:
207
+ range_str = f"Array.from({{length: {args[1]} - {args[0]}}}, (_, {target}) => {target} + {args[0]})"
208
+ else:
209
+ range_str = f"Array.from({{length: 1}}, (_, {target}) => {target})"
210
+ else:
211
+ range_str = self.visit(iter_expr)
212
+ elt = self.visit(node.elt)
213
+ if if_condition:
214
+ gen_body = (
215
+ f"for (let {target} of {range_str}) {{\n"
216
+ f" if (!({if_condition})) continue;\n"
217
+ f" yield ({elt});\n"
218
+ f"}}"
219
+ )
220
+ else:
221
+ gen_body = (
222
+ f"for (let {target} of {range_str}) {{\n"
223
+ f" yield ({elt});\n"
224
+ f"}}"
225
+ )
226
+ return f"(function* () {{\n{gen_body}\n}})()"
227
+
228
+ def visit_BinOp(self, node):
229
+ if isinstance(node.op, ast.FloorDiv):
230
+ left = self.visit(node.left)
231
+ right = self.visit(node.right)
232
+ return f"Math.floor({left} / {right})"
233
+ elif isinstance(node.op, ast.Pow):
234
+ left = self.visit(node.left)
235
+ right = self.visit(node.right)
236
+ return f"Math.pow({left}, {right})"
237
+ else:
238
+ left = self.visit(node.left)
239
+ op = self.visit(node.op)
240
+ right = self.visit(node.right)
241
+ return f"{left} {op} {right}"
242
+
243
+ def visit_Compare(self, node):
244
+ if len(node.ops) != 1 or len(node.comparators) != 1:
245
+ return "/* Unsupported comparison */"
246
+ left = self.visit(node.left)
247
+ op = self.visit(node.ops[0])
248
+ comparator = self.visit(node.comparators[0])
249
+ return f"{left} {op} {comparator}"
250
+
251
+ def visit_BoolOp(self, node):
252
+ if isinstance(node.op, ast.And):
253
+ op = "&&"
254
+ elif isinstance(node.op, ast.Or):
255
+ op = "||"
256
+ else:
257
+ op = "/* Unsupported BoolOp */"
258
+ values = [self.visit(v) for v in node.values]
259
+ return f"{f' {op} '.join(values)}"
260
+
261
+ def visit_UnaryOp(self, node):
262
+ if isinstance(node.op, ast.Not):
263
+ op = "!"
264
+ else:
265
+ op = "/* Unsupported UnaryOp */"
266
+ operand = self.visit(node.operand)
267
+ return f"{op}{operand}"
268
+
269
+ def visit_Attribute(self, node):
270
+ obj = self.visit(node.value)
271
+ return f"{obj}.{node.attr}"
272
+
273
+ def visit_Subscript(self, node):
274
+ value = self.visit(node.value)
275
+ index = self.visit(node.slice)
276
+ return f"{value}[{index}]"
277
+
278
+ def visit_Index(self, node):
279
+ return self.visit(node.value)
280
+
281
+ def visit_Name(self, node):
282
+ return node.id
283
+
284
+ def visit_Constant(self, node):
285
+ if node.value is None:
286
+ return "null"
287
+ elif isinstance(node.value, bool):
288
+ return "true" if node.value else "false"
289
+ elif isinstance(node.value, str):
290
+ return f'"{node.value}"'
291
+ return str(node.value)
292
+
293
+ def visit_List(self, node):
294
+ elements = [self.visit(e) for e in node.elts]
295
+ return f"[{', '.join(elements)}]"
296
+
297
+ def visit_Tuple(self, node):
298
+ elements = [self.visit(e) for e in node.elts]
299
+ return f"[{', '.join(elements)}]"
300
+
301
+ def visit_Dict(self, node):
302
+ pairs = []
303
+ for k, v in zip(node.keys, node.values):
304
+ key = self.visit(k) if k else ""
305
+ value = self.visit(v)
306
+ if isinstance(k, ast.Constant) and isinstance(k.value, str):
307
+ key = f'"{k.value}"'
308
+ pairs.append(f"{key}: {value}")
309
+ return f"{{{', '.join(pairs)}}}"
310
+
311
+ def visit_Expr(self, node):
312
+ expr = self.visit(node.value)
313
+ return f"{self.indent()}{expr};"
314
+
315
+ def visit_AugAssign(self, node):
316
+ target = self.visit(node.target)
317
+ op = self.visit(node.op)
318
+ value = self.visit(node.value)
319
+ return f"{self.indent()}{target} {op}= {value};"
320
+
321
+ def visit_Add(self, node):
322
+ return "+"
323
+
324
+ def visit_Sub(self, node):
325
+ return "-"
326
+
327
+ def visit_Mult(self, node):
328
+ return "*"
329
+
330
+ def visit_Div(self, node):
331
+ return "/"
332
+
333
+ def visit_Mod(self, node):
334
+ return "%"
335
+
336
+ def visit_Eq(self, node):
337
+ return "==="
338
+
339
+ def visit_NotEq(self, node):
340
+ return "!=="
341
+
342
+ def visit_Lt(self, node):
343
+ return "<"
344
+
345
+ def visit_LtE(self, node):
346
+ return "<="
347
+
348
+ def visit_Gt(self, node):
349
+ return ">"
350
+
351
+ def visit_GtE(self, node):
352
+ return ">="
353
+
354
+ def visit_ClassDef(self, node):
355
+ class_name = node.name
356
+ methods = []
357
+ for n in node.body:
358
+ if isinstance(n, ast.FunctionDef):
359
+ if n.name == "__init__":
360
+ method_code = self.visit_FunctionDef(n)
361
+ method_code = method_code.replace("function __init__", "constructor")
362
+ methods.append(method_code)
363
+ else:
364
+ methods.append(self.visit_FunctionDef(n))
365
+ else:
366
+ methods.append(self.visit(n))
367
+ body_str = "\n".join(methods)
368
+ return f"{self.indent()}class {class_name} {{\n{body_str}\n{self.indent()}}}"
369
+
370
+ def generic_visit(self, node):
371
+ return f"/* Unhandled node type: {type(node).__name__} */"
372
+
373
+ def convert_python_to_javascript(python_code):
374
+ tree = ast.parse(python_code)
375
+ converter = PythonToJavaScriptConverter()
376
+ return "\n".join(filter(None, (converter.visit(n) for n in tree.body)))
377
+
378
+ # =============================================================================
379
+ # 執行 Python 程式碼,並捕捉其輸出
380
+ # =============================================================================
381
+ def run_python_code(code):
382
+ stdout = io.StringIO()
383
+ try:
384
+ with redirect_stdout(stdout):
385
+ exec(code, {})
386
+ except Exception as e:
387
+ stdout.write(f"Error: {str(e)}")
388
+ return stdout.getvalue()
389
+
390
+ # =============================================================================
391
+ # 執行 JavaScript 程式碼(需要系統有 node.js 環境)
392
+ # =============================================================================
393
+ def run_js_code(js_code):
394
+ if not shutil.which("node"):
395
+ return "Error: Node.js is not installed or not available in the system PATH."
396
+ try:
397
+ with tempfile.NamedTemporaryFile("w", suffix=".js", delete=False) as temp_js:
398
+ temp_js.write(js_code)
399
+ temp_js.flush()
400
+ temp_js_name = temp_js.name
401
+ result = subprocess.run(["node", temp_js_name],
402
+ capture_output=True, text=True, timeout=5)
403
+ output = result.stdout
404
+ if result.stderr:
405
+ output += "\nError:\n" + result.stderr
406
+ except Exception as e:
407
+ output = f"Error executing JS code: {str(e)}"
408
+ return output
409
+
410
+ # =============================================================================
411
+ # 定義多組範例測試代碼 (覆蓋所有測試情況)
412
+ # =============================================================================
413
+ sample_dict = {
414
+ "Simple Function": '''\
415
+ def add(a, b):
416
+ return a + b
417
+ print(add(2, 3))
418
+ ''',
419
+
420
+ "If/Else": '''\
421
+ def max_num(a, b):
422
+ if a > b:
423
+ return a
424
+ else:
425
+ return b
426
+ print(max_num(10, 20))
427
+ ''',
428
+
429
+ "While Loop": '''\
430
+ i = 0
431
+ while i < 5:
432
+ print(i)
433
+ i += 1
434
+ ''',
435
+
436
+ "For Loop": '''\
437
+ for x in range(3):
438
+ print(x)
439
+ ''',
440
+
441
+ "List Comprehension (without if)": '''\
442
+ numbers = [x * 2 for x in range(5)]
443
+ print(numbers)
444
+ ''',
445
+
446
+ "List Comprehension (with if)": '''\
447
+ numbers = [x for x in range(10) if x % 2 == 0]
448
+ print(numbers)
449
+ ''',
450
+
451
+ "Dictionary Access": '''\
452
+ person = {"name": "Alice", "age": 30}
453
+ print(person["name"])
454
+ ''',
455
+
456
+ "Augmented Assignment": '''\
457
+ count = 10
458
+ count += 5
459
+ print(count)
460
+ ''',
461
+
462
+ "Multiple Assignments": '''\
463
+ a, b = 1, 2
464
+ print(a)
465
+ print(b)
466
+ ''',
467
+
468
+ "Boolean Operations": '''\
469
+ a = True
470
+ b = False
471
+ if a and not b:
472
+ print("Both conditions met")
473
+ ''',
474
+
475
+ "Nested Functions": '''\
476
+ def outer(x):
477
+ def inner(y):
478
+ return y * 2
479
+ return inner(x) + 1
480
+ print(outer(5))
481
+ ''',
482
+
483
+ "None and Boolean": '''\
484
+ result = None
485
+ flag = True
486
+ if flag:
487
+ result = "Success"
488
+ else:
489
+ result = "Failure"
490
+ print(result)
491
+ ''',
492
+
493
+ "Floor Div and Pow": '''\
494
+ a = 5 // 2
495
+ b = 2 ** 3
496
+ print(a)
497
+ print(b)
498
+ ''',
499
+
500
+ "Class Definition": '''\
501
+ class Person:
502
+ def __init__(self, name):
503
+ self.name = name
504
+ def greet(self):
505
+ print("Hello, " + self.name)
506
+ p = Person("Alice")
507
+ p.greet()
508
+ ''',
509
+
510
+ "Default Parameter": '''\
511
+ def greet(name="World"):
512
+ print("Hello, " + name)
513
+ greet()
514
+ ''',
515
+
516
+ "Nested If/Else": '''\
517
+ if a > b:
518
+ if b > c:
519
+ print("a > b > c")
520
+ else:
521
+ print("Not in order")
522
+ ''',
523
+
524
+ "Nested For Loops": '''\
525
+ for i in range(3):
526
+ for j in range(2):
527
+ print(i, j)
528
+ ''',
529
+
530
+ "Generator Expression": '''\
531
+ gen = (x * 2 for x in range(5))
532
+ print(list(gen))
533
+ '''
534
+ }
535
+
536
+ sample_choices = list(sample_dict.keys())
537
+
538
+ def load_sample(sample_name):
539
+ return sample_dict.get(sample_name, "")
540
+
541
+ # =============================================================================
542
+ # Gradio 介面主函數
543
+ # =============================================================================
544
+ def process_code(python_code):
545
+ try:
546
+ tree = ast.parse(python_code)
547
+ ast_tree = ast.dump(tree, indent=4)
548
+ except Exception as e:
549
+ ast_tree = f"AST Error: {e}"
550
+ python_output = run_python_code(python_code)
551
+ try:
552
+ js_code = convert_python_to_javascript(python_code)
553
+ except Exception as e:
554
+ js_code = f"Conversion Error: {e}"
555
+ js_output = run_js_code(js_code)
556
+ return ast_tree, python_output, js_code, js_output
557
+
558
+ # =============================================================================
559
+ # 建立 Gradio 介面,包含下拉選單、Code 輸入與多個輸出區塊
560
+ # =============================================================================
561
+ with gr.Blocks(css="#component-7 {font-family: 'Consolas', monospace;}") as demo:
562
+ gr.Markdown("# Python ↔ JavaScript 互動轉換器")
563
+ gr.Markdown("請從下拉選單中選取範例程式碼或直接貼上 Python 程式碼,即可看到其 AST、執行結果,以及轉換後的 JavaScript 程式碼與執行結果。")
564
+
565
+ with gr.Row():
566
+ with gr.Column(scale=1):
567
+ sample_dropdown = gr.Dropdown(label="Select a Sample", choices=sample_choices, value=sample_choices[0])
568
+ with gr.Column(scale=3):
569
+ python_code_input = gr.Code(
570
+ label="Python Code Input",
571
+ language="python",
572
+ value=sample_dict[sample_choices[0]],
573
+ interactive=True)
574
+
575
+ sample_dropdown.change(fn=load_sample, inputs=sample_dropdown, outputs=python_code_input)
576
+
577
+ run_button = gr.Button("Run Code")
578
+
579
+ with gr.Row():
580
+ with gr.Column():
581
+ ast_output = gr.Code(label="AST Tree", language="python", interactive=False)
582
+ python_exec_output = gr.Textbox(label="Python Execution Output", interactive=False)
583
+ with gr.Column():
584
+ js_code_output = gr.Code(label="Converted JavaScript Code", language="javascript", interactive=False)
585
+ js_exec_output = gr.Textbox(label="JavaScript Execution Output", interactive=False)
586
+
587
+ run_button.click(fn=process_code,
588
+ inputs=python_code_input,
589
+ outputs=[ast_output, python_exec_output, js_code_output, js_exec_output])
590
+
591
+ if __name__ == "__main__":
592
+ demo.launch()