@pineforge/codegen-pyodide 0.7.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/glue.py +35 -0
- package/index.mjs +10 -0
- package/package.json +10 -3
- package/pineforge_codegen-0.7.1.tar.gz +0 -0
- package/release.json +2 -2
- package/tables.json +1 -1
- package/transpile.worker.mjs +84 -0
- package/pineforge_codegen-0.7.0.tar.gz +0 -0
package/glue.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# CANONICAL GLUE — runtime-equivalent to the body of PY_GLUE in
|
|
2
|
+
# pineforge-app/apps/web/lib/pyodide-transpiler/glue.ts (produces identical
|
|
3
|
+
# transpile_json output). The browser worker and this gate run the same logic,
|
|
4
|
+
# so the gate's parity guarantee reflects shipped behavior.
|
|
5
|
+
# (Phase 3: ship this from the npm package so there is one source of truth.)
|
|
6
|
+
import json
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
if "/codegen" not in sys.path:
|
|
10
|
+
sys.path.insert(0, "/codegen")
|
|
11
|
+
|
|
12
|
+
from pineforge_codegen import transpile
|
|
13
|
+
from pineforge_codegen.errors import CompileError
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def transpile_json(source: str) -> str:
|
|
17
|
+
try:
|
|
18
|
+
cpp = transpile(source)
|
|
19
|
+
except CompileError as e:
|
|
20
|
+
diags = []
|
|
21
|
+
for d in e.diagnostics:
|
|
22
|
+
loc = d.location
|
|
23
|
+
message = d.message + " — " + d.hint if getattr(d, "hint", None) else d.message
|
|
24
|
+
entry = {
|
|
25
|
+
"line": loc.line if loc else 1,
|
|
26
|
+
"col": loc.col if loc else 1,
|
|
27
|
+
"message": message,
|
|
28
|
+
"severity": getattr(d.level, "value", "error"),
|
|
29
|
+
}
|
|
30
|
+
end_col = getattr(loc, "end_col", None) if loc else None
|
|
31
|
+
if end_col is not None:
|
|
32
|
+
entry["endCol"] = end_col
|
|
33
|
+
diags.append(entry)
|
|
34
|
+
return json.dumps({"ok": False, "error": str(e), "diagnostics": diags})
|
|
35
|
+
return json.dumps({"ok": True, "cpp": cpp})
|
package/index.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Entry for @pineforge/codegen-pyodide. Resolves the packaged payload paths and
|
|
2
2
|
// metadata so consumers (the app's build + Node oracle/grammar tooling) read
|
|
3
3
|
// everything from this one dependency — no git submodule.
|
|
4
|
+
import { readFileSync } from "node:fs";
|
|
4
5
|
import { createRequire } from "node:module";
|
|
5
6
|
import { dirname, join } from "node:path";
|
|
6
7
|
import { fileURLToPath } from "node:url";
|
|
@@ -18,3 +19,12 @@ export const archivePath = join(HERE, `pineforge_codegen-${release.codegen}.tar.
|
|
|
18
19
|
// so `import pineforge_codegen` resolves (oracle tests, grammar gen).
|
|
19
20
|
export const sourceRoot = HERE;
|
|
20
21
|
export const codegenSourceDir = join(HERE, "pineforge_codegen");
|
|
22
|
+
|
|
23
|
+
// Absolute path to the shipped ESM module worker — consumers copy it into their
|
|
24
|
+
// served /pyodide/ dir and load it via `new Worker(url, { type: "module" })`.
|
|
25
|
+
export const workerPath = join(HERE, "transpile.worker.mjs");
|
|
26
|
+
|
|
27
|
+
// Canonical Pyodide glue source (single source: gate/glue.py) — runPython'd to
|
|
28
|
+
// define transpile_json. The worker embeds this verbatim; exported here too so
|
|
29
|
+
// Node consumers (parity tests) run the exact same glue.
|
|
30
|
+
export const glue = readFileSync(join(HERE, "glue.py"), "utf8");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pineforge/codegen-pyodide",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"description": "Gate-validated Pyodide payload for the PineScript v6 -> C++ transpiler: archive (run in Pyodide), unpacked source, introspected tables, and release metadata.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.mjs",
|
|
@@ -14,7 +14,9 @@
|
|
|
14
14
|
"pineforge_codegen-*.tar.gz",
|
|
15
15
|
"pineforge_codegen/**/*",
|
|
16
16
|
"tables.json",
|
|
17
|
-
"release.json"
|
|
17
|
+
"release.json",
|
|
18
|
+
"transpile.worker.mjs",
|
|
19
|
+
"glue.py"
|
|
18
20
|
],
|
|
19
21
|
"engines": {
|
|
20
22
|
"node": ">=20"
|
|
@@ -22,7 +24,12 @@
|
|
|
22
24
|
"peerDependencies": {
|
|
23
25
|
"pyodide": "314.0.0"
|
|
24
26
|
},
|
|
25
|
-
"keywords": [
|
|
27
|
+
"keywords": [
|
|
28
|
+
"pinescript",
|
|
29
|
+
"transpiler",
|
|
30
|
+
"pyodide",
|
|
31
|
+
"codegen"
|
|
32
|
+
],
|
|
26
33
|
"homepage": "https://github.com/pineforge-4pass/pineforge-codegen-oss",
|
|
27
34
|
"repository": {
|
|
28
35
|
"type": "git",
|
|
Binary file
|
package/release.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"codegen": "0.7.
|
|
2
|
+
"codegen": "0.7.1",
|
|
3
3
|
"pyodide": "314.0.0",
|
|
4
4
|
"python": "3.14.0",
|
|
5
5
|
"emscripten": "emscripten_5_0_3",
|
|
6
|
-
"sha256": "
|
|
6
|
+
"sha256": "51be5f3cdecf1ce87685bdd570d60f8c15e5fa0c4ce3b2e9e2594eefe89babd0"
|
|
7
7
|
}
|
package/tables.json
CHANGED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// GENERATED by scripts/build-npm-package.mjs from gate/glue.py — DO NOT EDIT.
|
|
2
|
+
// Canonical client-side Pyodide transpile worker, shipped by
|
|
3
|
+
// @pineforge/codegen-pyodide. Consumers serve it from /pyodide/ and load it via:
|
|
4
|
+
// new Worker("/pyodide/transpile.worker.mjs", { type: "module" })
|
|
5
|
+
// A real ESM module worker (string-URL load bypasses the bundler), so Pyodide
|
|
6
|
+
// 314's module-worker requirement is satisfied. Mirrors the old in-app
|
|
7
|
+
// transpile.worker.ts; protocol is identical (see app protocol.ts).
|
|
8
|
+
import { loadPyodide } from "/pyodide/pyodide.mjs";
|
|
9
|
+
|
|
10
|
+
const GLUE = `# CANONICAL GLUE — runtime-equivalent to the body of PY_GLUE in
|
|
11
|
+
# pineforge-app/apps/web/lib/pyodide-transpiler/glue.ts (produces identical
|
|
12
|
+
# transpile_json output). The browser worker and this gate run the same logic,
|
|
13
|
+
# so the gate's parity guarantee reflects shipped behavior.
|
|
14
|
+
# (Phase 3: ship this from the npm package so there is one source of truth.)
|
|
15
|
+
import json
|
|
16
|
+
import sys
|
|
17
|
+
|
|
18
|
+
if "/codegen" not in sys.path:
|
|
19
|
+
sys.path.insert(0, "/codegen")
|
|
20
|
+
|
|
21
|
+
from pineforge_codegen import transpile
|
|
22
|
+
from pineforge_codegen.errors import CompileError
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def transpile_json(source: str) -> str:
|
|
26
|
+
try:
|
|
27
|
+
cpp = transpile(source)
|
|
28
|
+
except CompileError as e:
|
|
29
|
+
diags = []
|
|
30
|
+
for d in e.diagnostics:
|
|
31
|
+
loc = d.location
|
|
32
|
+
message = d.message + " — " + d.hint if getattr(d, "hint", None) else d.message
|
|
33
|
+
entry = {
|
|
34
|
+
"line": loc.line if loc else 1,
|
|
35
|
+
"col": loc.col if loc else 1,
|
|
36
|
+
"message": message,
|
|
37
|
+
"severity": getattr(d.level, "value", "error"),
|
|
38
|
+
}
|
|
39
|
+
end_col = getattr(loc, "end_col", None) if loc else None
|
|
40
|
+
if end_col is not None:
|
|
41
|
+
entry["endCol"] = end_col
|
|
42
|
+
diags.append(entry)
|
|
43
|
+
return json.dumps({"ok": False, "error": str(e), "diagnostics": diags})
|
|
44
|
+
return json.dumps({"ok": True, "cpp": cpp})
|
|
45
|
+
`;
|
|
46
|
+
|
|
47
|
+
const post = (m) => self.postMessage(m);
|
|
48
|
+
let transpileJson = null;
|
|
49
|
+
|
|
50
|
+
async function init() {
|
|
51
|
+
try {
|
|
52
|
+
const pyodide = await loadPyodide({ indexURL: "/pyodide/" });
|
|
53
|
+
const manifestRes = await fetch("/pyodide/manifest.json", { cache: "no-cache" });
|
|
54
|
+
if (!manifestRes.ok) throw new Error(`fetch /pyodide/manifest.json: ${manifestRes.status}`);
|
|
55
|
+
const manifest = await manifestRes.json();
|
|
56
|
+
const archiveRes = await fetch(`/pyodide/${manifest.archive}`);
|
|
57
|
+
if (!archiveRes.ok) throw new Error(`fetch /pyodide/${manifest.archive}: ${archiveRes.status}`);
|
|
58
|
+
const buf = await archiveRes.arrayBuffer();
|
|
59
|
+
pyodide.unpackArchive(buf, "gztar", { extractDir: "/codegen" });
|
|
60
|
+
pyodide.runPython(GLUE);
|
|
61
|
+
const fn = pyodide.globals.get("transpile_json");
|
|
62
|
+
transpileJson = (source) => fn(source);
|
|
63
|
+
post({ type: "ready", codegenVersion: manifest.codegenVersion });
|
|
64
|
+
} catch (err) {
|
|
65
|
+
post({ type: "init-error", error: err instanceof Error ? err.message : String(err) });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const initPromise = init();
|
|
70
|
+
|
|
71
|
+
self.onmessage = async (ev) => {
|
|
72
|
+
const { id, source } = ev.data;
|
|
73
|
+
await initPromise;
|
|
74
|
+
if (!transpileJson) {
|
|
75
|
+
post({ type: "crash", id, error: "pyodide failed to initialize" });
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
const result = JSON.parse(transpileJson(source));
|
|
80
|
+
post({ type: "result", id, result });
|
|
81
|
+
} catch (err) {
|
|
82
|
+
post({ type: "crash", id, error: err instanceof Error ? err.message : String(err) });
|
|
83
|
+
}
|
|
84
|
+
};
|
|
Binary file
|