按规划继续工作

结论:解释器 WASM、SDK 统一入口、浏览器解释器 smoke 与兼容性文档已闭环,native 和 host/WASM/browser 验证全部通过。
This commit is contained in:
2026-06-08 07:24:06 +08:00
parent c28b629ff6
commit 706fd1e775
20 changed files with 629 additions and 122 deletions

View File

@@ -0,0 +1,41 @@
# LinuxCNC WASM SDK
This SDK exposes browser/Node JavaScript wrappers for standalone WASM modules
built from vendored LinuxCNC source.
## Boundary
The SDK is a host-boundary layer only. It may:
- load generated Emscripten modules;
- allocate and free C strings;
- write text files into the Emscripten filesystem;
- call exported C ABI functions;
- return LinuxCNC-produced text output to JavaScript callers.
The SDK must not implement G-code interpretation, canonical motion behavior,
tool semantics, parameter semantics, kinematics, or planner behavior. Those
behaviors must continue to come from vendored LinuxCNC source through the C ABI.
## Entrypoints
Use `src/index.js` for stable imports:
```js
import {
createLinuxCncIniSdk,
createLinuxCncInterpSdk,
} from "./src/index.js";
```
`createLinuxCncIniSdk()` wraps the INI parser module built from vendored
LinuxCNC `inifile.cc`.
`createLinuxCncInterpSdk()` wraps the interpreter-core module built from
vendored LinuxCNC RS274NGC sources and exposes:
- `runProgram(programText)`
- `runProgramWithIni(programText, iniPath)`
- `runFile(path)`
- `runFileWithIni(path, iniPath)`
- `writeTextFile(path, text)`

View File

@@ -0,0 +1,2 @@
export { createLinuxCncIniSdk } from "./linuxcnc-ini.js";
export { createLinuxCncInterpSdk } from "./linuxcnc-interp.js";

View File

@@ -0,0 +1,73 @@
import createLinuxCncInterpModule from "../../../build/wasm/core/linuxcnc_interp.js";
function allocCString(mod, value) {
const bytes = mod.lengthBytesUTF8(value) + 1;
const ptr = mod._malloc(bytes);
mod.stringToUTF8(value, ptr, bytes);
return ptr;
}
function ensureParentPath(mod, path) {
const parts = path.split("/").filter(Boolean);
let current = "";
for (const part of parts.slice(0, -1)) {
current += `/${part}`;
try {
mod.FS.mkdir(current);
} catch {
// Directory already exists.
}
}
}
function copyResultString(mod, resultPtr, functionName) {
if (!resultPtr) {
throw new Error(`${functionName} returned null`);
}
return mod.UTF8ToString(resultPtr);
}
function callStringResult(mod, functionName, ...values) {
const ptrs = values.map((value) => allocCString(mod, value));
let resultPtr = 0;
try {
resultPtr = mod[`_${functionName}`](...ptrs);
return copyResultString(mod, resultPtr, functionName);
} finally {
if (resultPtr) {
mod._lcinterp_free_string(resultPtr);
}
for (const ptr of ptrs) {
mod._free(ptr);
}
}
}
export async function createLinuxCncInterpSdk(moduleOptions = {}) {
const mod = await createLinuxCncInterpModule(moduleOptions);
return {
module: mod,
writeTextFile(path, text) {
ensureParentPath(mod, path);
mod.FS.writeFile(path, text, { encoding: "utf8" });
},
runProgram(programText) {
return callStringResult(mod, "lcinterp_run_program", programText);
},
runProgramWithIni(programText, iniPath) {
return callStringResult(mod, "lcinterp_run_program_with_ini", programText, iniPath);
},
runFile(path) {
return callStringResult(mod, "lcinterp_run_file", path);
},
runFileWithIni(path, iniPath) {
return callStringResult(mod, "lcinterp_run_file_with_ini", path, iniPath);
},
};
}