Skip to content

Commit 5dbbebe

Browse files
committed
alien code interpretation
1 parent 83f73ed commit 5dbbebe

24 files changed

Lines changed: 1571 additions & 615 deletions

package-lock.json

Lines changed: 317 additions & 53 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@
1010
"preview": "vite preview"
1111
},
1212
"dependencies": {
13+
"@tailwindcss/vite": "^4.2.2",
1314
"react": "^19.2.4",
1415
"react-dom": "^19.2.4",
1516
"react-router": "^7.14.1",
17+
"tailwindcss": "^4.2.2",
1618
"tree-sitter-python": "^0.25.0",
1719
"web-tree-sitter": "^0.26.8"
1820
},

src/components/LevelComponent.tsx

Lines changed: 88 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,32 @@
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

310
export 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>

src/dev/debugRoundTrip.ts

Lines changed: 0 additions & 20 deletions
This file was deleted.

src/dev/debugTreeSitterAst.ts

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,29 @@
1+
import { generatePlanetLanguage } from "../language/generator/generatePlanetLanguage";
12
import { parsePythonWithTreeSitter } from "../language/parse/parsePythonWithTreeSitter";
23
import { renderAlien } from "../language/render/renderAlien";
4+
import { parseAlien } from "../language/parse/parseAlien";
35
import { renderPython } from "../language/render/renderPython";
4-
import { generatePlanetLanguage } from "../language/generator/generatePlanetLanguage";
56

67
async function run() {
7-
const source = `
8-
def max2(a, b):
9-
if a > b:
10-
return a
8+
const pythonSource = `
9+
def bigger(a, b):
10+
if a > b and a != 0:
11+
print(a)
12+
return len(range(a))
1113
else:
12-
return b
13-
`.trim();
14-
15-
const ast = await parsePythonWithTreeSitter(source);
16-
const lang = generatePlanetLanguage(1);
17-
18-
console.log("AST:");
19-
console.log(ast);
14+
return b + 1
15+
`.trim();
2016

21-
console.log("PYTHON:");
22-
console.log(renderPython(ast));
17+
const lang = generatePlanetLanguage(3812);
18+
const ast = await parsePythonWithTreeSitter(pythonSource);
19+
const alien = renderAlien(ast, lang);
20+
const roundTripAst = parseAlien(alien, lang);
21+
const pythonAgain = renderPython(roundTripAst);
2322

24-
console.log("ALIEN:");
25-
console.log(renderAlien(ast, lang));
23+
console.log("OPERATORS:", lang.operators);
24+
console.log("BUILTINS:", lang.builtins);
25+
console.log("ALIEN:\n", alien);
26+
console.log("PYTHON AGAIN:\n", pythonAgain);
2627
}
2728

2829
run().catch(console.error);

src/language/families/flowing.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,26 @@ export const flowingFamily: LanguageFamily = {
2020
alternatePrefixes: ["ve", "el"],
2121
iteratorSuffixes: ["li", "ri"],
2222
negationPrefixes: ["ne", "no"],
23-
},
2423

24+
arithmeticPrefixes: ["la", "so"],
25+
arithmeticSuffixes: ["ra", "ni"],
26+
27+
comparisonPrefixes: ["ve", "se"],
28+
comparisonSuffixes: ["la", "ri"],
29+
30+
logicPrefixes: ["el", "na"],
31+
logicSuffixes: ["ra", "li"],
32+
33+
builtinPrefixes: ["so", "ve"],
34+
builtinSuffixes: ["na", "ra"],
35+
36+
assignmentRoots: ["becomes", "flows", "turns", "luma", "vela"],
37+
assignmentPrefixes: ["", "a", "e"],
38+
assignmentSuffixes: ["", "ra", "na"],
39+
},
40+
2541
syntaxBias: {
26-
assignmentStyles: ["equals", "set_prefix"],
42+
assignmentStyles: ["word_infix", "word_prefix", "put_in"],
2743
functionStyles: ["keyword_name_params_block", "make_name_with_params_block"],
2844
blockStyles: ["indent"],
2945
conditionalStyles: ["when_expr_then"],

src/language/families/mechanical.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,24 @@ export const mechanicalFamily: LanguageFamily = {
2020
alternatePrefixes: ["ALT"],
2121
iteratorSuffixes: ["TH"],
2222
negationPrefixes: ["NEG"],
23-
},
23+
arithmeticPrefixes: ["OP"],
24+
arithmeticSuffixes: ["X"],
25+
26+
comparisonPrefixes: ["CMP"],
27+
comparisonSuffixes: ["Z"],
28+
29+
logicPrefixes: ["LOG"],
30+
logicSuffixes: ["Q"],
2431

32+
builtinPrefixes: ["SYS"],
33+
builtinSuffixes: ["M"],
34+
35+
assignmentRoots: ["LOAD", "SET", "STORE", "CFG"],
36+
assignmentPrefixes: [""],
37+
assignmentSuffixes: [""],
38+
},
2539
syntaxBias: {
26-
assignmentStyles: ["arrow"],
40+
assignmentStyles: ["arrow", "word_prefix", "word_infix"],
2741
functionStyles: ["keyword_name_square_params_block"],
2842
blockStyles: ["indent", "brace"],
2943
conditionalStyles: ["keyword_paren_expr_block"],

src/language/families/ritual.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,24 @@ export const ritualFamily: LanguageFamily = {
2020
alternatePrefixes: ["otha", "alt"],
2121
iteratorSuffixes: ["en"],
2222
negationPrefixes: ["un", "non"],
23-
},
23+
arithmeticPrefixes: ["tha", "vel"],
24+
arithmeticSuffixes: ["um", "ar"],
25+
26+
comparisonPrefixes: ["ora", "eth"],
27+
comparisonSuffixes: ["um", "en"],
28+
29+
logicPrefixes: ["vel", "th"],
30+
logicSuffixes: ["ar", "um"],
31+
32+
builtinPrefixes: ["ora", "vel"],
33+
builtinSuffixes: ["en", "um"],
2434

35+
assignmentRoots: ["invoke", "bind", "inscribe", "summon", "kora"],
36+
assignmentPrefixes: ["", "vel", "tha"],
37+
assignmentSuffixes: ["", "um", "en"],
38+
},
2539
syntaxBias: {
26-
assignmentStyles: ["put_in"],
40+
assignmentStyles: ["put_in", "word_prefix", "word_infix"],
2741
functionStyles: ["make_name_with_params_block"],
2842
blockStyles: ["then_end"],
2943
conditionalStyles: ["when_expr_then"],

src/language/families/sharp.ts

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,32 @@ export const sharpFamily: LanguageFamily = {
2020
literalSuffixes: ["ul", "um"],
2121
alternatePrefixes: ["va", "zu"],
2222
iteratorSuffixes: ["in", "ir"],
23-
negationPrefixes: ["ch", "n"],
23+
negationPrefixes: ["sh", "n"],
24+
25+
arithmeticPrefixes: ["ka", "ta"],
26+
arithmeticSuffixes: ["ix", "ak"],
27+
28+
comparisonPrefixes: ["ve", "zo"],
29+
comparisonSuffixes: ["et", "or"],
30+
31+
logicPrefixes: ["shi", "ra"],
32+
logicSuffixes: ["en", "ik"],
33+
34+
builtinPrefixes: ["ul", "tor"],
35+
builtinSuffixes: ["a", "um"],
36+
37+
assignmentRoots: ["bind", "lock", "vak", "tash"],
38+
assignmentPrefixes: ["", "z", "k"],
39+
assignmentSuffixes: ["", "ar", "ik"],
2440
},
2541

2642
syntaxBias: {
27-
assignmentStyles: ["equals", "arrow"],
28-
functionStyles: [
29-
"keyword_name_params_block",
30-
"keyword_name_square_params_block",
31-
],
32-
blockStyles: ["indent", "arrow_indent"],
33-
conditionalStyles: ["keyword_expr_block", "keyword_paren_expr_block"],
34-
elseStyles: ["keyword_block", "keyword_spaced_block"],
35-
forStyles: ["for_var_in_iter", "for_paren_var_in_iter"],
43+
assignmentStyles: ["word_infix", "word_suffix"],
44+
functionStyles: ["keyword_name_params_block"],
45+
blockStyles: ["indent"],
46+
conditionalStyles: ["keyword_expr_block"],
47+
elseStyles: ["keyword_block"],
48+
forStyles: ["for_var_in_iter"],
3649
},
3750

3851
visualStyle: {
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import type {
2+
BuiltinMap,
3+
LanguageFamily,
4+
SemanticRoots,
5+
ResolvedMorphology
6+
} from "../types";
7+
import { finishToken, joinParts } from "./tokenUtils"
8+
9+
export function generateBuiltins(
10+
family: LanguageFamily,
11+
roots: SemanticRoots,
12+
morphology: ResolvedMorphology,
13+
): BuiltinMap {
14+
const raw: BuiltinMap = {
15+
print: joinParts(morphology.builtinPrefix, roots.send, "out", morphology.builtinSuffix),
16+
len: joinParts(morphology.builtinPrefix, roots.membership, "len", morphology.builtinSuffix),
17+
range: joinParts(morphology.builtinPrefix, roots.loop, "range", morphology.builtinSuffix),
18+
};
19+
20+
const builtins: BuiltinMap = {
21+
print: finishToken(raw.print, family),
22+
len: finishToken(raw.len, family),
23+
range: finishToken(raw.range, family),
24+
};
25+
26+
const values = Object.values(builtins);
27+
if (new Set(values).size !== values.length) {
28+
throw new Error("Duplicate generated builtin tokens");
29+
}
30+
31+
return builtins;
32+
}

0 commit comments

Comments
 (0)