1- import React , { useState , useEffect , useRef } from 'react'
1+ import React , { useState , useEffect , useRef , useMemo } from "react" ;
2+ import { generatePlanetLanguage } from "../language/generator/generatePlanetLanguage" ;
3+ import { parseAlien } from "../language/parse/parseAlien" ;
4+ import { renderPython } from "../language/render/renderPython" ;
5+ import { renderAlien } from "../language/render/renderAlien" ;
6+ import { parsePythonWithTreeSitter } from "../language/parse/parsePythonWithTreeSitter" ;
7+ import type { ProgramNode } from "../language/types" ;
8+ import { validateAlienSource } from "../language/validate/validateAlienSource" ;
29
310export default function LevelComponent ( ) {
411 const [ code , setCode ] = useState ( "" )
512 const [ output , setOutput ] = useState ( "" )
13+ const [ translatedPython , setTranslatedPython ] = useState ( "" ) ;
14+ const [ exampleAst , setExampleAst ] = useState < ProgramNode | null > ( null ) ;
15+ const [ exampleAlien , setExampleAlien ] = useState ( "" ) ;
16+ const [ exampleError , setExampleError ] = useState ( "" ) ;
617 const [ pyodideReady , setPyodideReady ] = useState ( false )
718 const pyodideRef = useRef < any > ( null )
19+ const sourcePython = `
20+ nums = [1,2,3]
21+ sum = 0
22+ for i in nums:
23+ sum += i
24+ print(sum)
25+ ` . trim ( ) ;
26+
27+
28+ // Use a fixed seed for this level for now
29+ const lang = useMemo ( ( ) => generatePlanetLanguage ( 2 ) , [ ] ) ;
830
931 useEffect ( ( ) => {
1032 const loadPyodide = async ( ) => {
@@ -15,20 +37,74 @@ export default function LevelComponent() {
1537 loadPyodide ( )
1638 } , [ ] )
1739
40+ useEffect ( ( ) => {
41+ let cancelled = false ;
42+
43+ const parseExample = async ( ) => {
44+ try {
45+ setExampleError ( "" ) ;
46+
47+ const ast = await parsePythonWithTreeSitter ( sourcePython ) ;
48+ if ( cancelled ) return ;
49+
50+ setExampleAst ( ast ) ;
51+ setExampleAlien ( renderAlien ( ast , lang ) ) ;
52+ } catch ( err ) {
53+ if ( cancelled ) return ;
54+ setExampleError ( err instanceof Error ? err . message : String ( err ) ) ;
55+ }
56+ } ;
57+
58+ parseExample ( ) ;
59+
60+ return ( ) => {
61+ cancelled = true ;
62+ } ;
63+ } , [ sourcePython , lang ] ) ;
64+
1865 const runCode = async ( ) => {
1966 if ( ! pyodideRef . current ) return
20- await pyodideRef . current . runPythonAsync ( `
21- import sys, io
22- sys.stdout = io.StringIO()
23- ` )
67+ if ( ! pyodideRef . current ) return ;
68+
69+ const validation = validateAlienSource ( code , lang ) ;
70+
71+ if ( ! validation . isValid ) {
72+ setTranslatedPython ( "" ) ;
73+ setOutput (
74+ validation . issues
75+ . map (
76+ ( issue ) =>
77+ `Line ${ issue . line } , Col ${ issue . column } : ${ issue . message } ` ,
78+ )
79+ . join ( "\n" ) ,
80+ ) ;
81+ return ;
82+ }
2483 try {
25- await pyodideRef . current . runPythonAsync ( code )
26- const result = await pyodideRef . current . runPythonAsync ( `sys.stdout.getvalue()` )
27- setOutput ( result )
84+ // 1. Alien -> AST
85+ const ast = parseAlien ( code , lang ) ;
86+ console . log ( ast ) ;
87+ // 2. AST -> Python
88+ const pythonCode = renderPython ( ast ) ;
89+ setTranslatedPython ( pythonCode ) ;
90+
91+
92+ // 3. Reset stdout
93+ await pyodideRef . current . runPythonAsync ( `
94+ import sys, io
95+ sys.stdout = io.StringIO()
96+ ` ) ;
97+
98+ // 4. Run translated Python
99+ await pyodideRef . current . runPythonAsync ( pythonCode ) ;
100+
101+ // 5. Get output
102+ const result = await pyodideRef . current . runPythonAsync ( `sys.stdout.getvalue()` ) ;
103+ setOutput ( String ( result ) ) ;
28104 } catch ( err : any ) {
29- setOutput ( err . message )
105+ setOutput ( err ? .message ?? String ( err ) ) ;
30106 }
31- }
107+ } ;
32108
33109 const handleKeyDown = ( e : React . KeyboardEvent < HTMLTextAreaElement > ) => {
34110 if ( e . key === "Tab" ) {
@@ -63,11 +139,7 @@ export default function LevelComponent() {
63139 *** PYTHON CODE ***
64140 </ div >
65141 < div className = "bg-[#4a8be0] m-2 rounded-xl p-3" >
66- < pre className = "text-white text-xs leading-relaxed m-0" > { `nums = [1,2,3]
67- sum = 0
68- for i in nums:
69- sum += i
70- print(sum)` } </ pre >
142+ < pre className = "text-white text-xs leading-relaxed m-0" > { sourcePython } </ pre >
71143 </ div >
72144 </ div >
73145
@@ -77,11 +149,7 @@ print(sum)`}</pre>
77149 *** ALIEN CODE ***
78150 </ div >
79151 < div className = "bg-[#3a9447] m-2 rounded-xl p-3" >
80- < pre className = "text-white text-xs leading-relaxed m-0" > { `nums eats [1,2,3]
81- sum eats 0
82- i eats nums slowly:
83- sum eats more i
84- print eats sum` } </ pre >
152+ < pre className = "text-white text-xs leading-relaxed m-0" > { exampleError ? `Error: ${ exampleError } ` : exampleAlien || "Loading..." } </ pre >
85153 </ div >
86154 </ div >
87155 </ div >
0 commit comments