增加解释器WASM命名参数夹具

结论:解释器核心WASM新增带INI文件边界的运行入口,并覆盖namedparam_semantics夹具,INI/HAL解析继续直接来自LinuxCNC解释器源码。
This commit is contained in:
2026-06-08 05:25:45 +08:00
parent fd680f7515
commit 9c611b8a01
4 changed files with 60 additions and 8 deletions

View File

@@ -198,11 +198,12 @@ INI parser smoke harness and an initial interpreter-core canonical event smoke
for `minimal_linear`, `arc_semantics`, `length_units`, `modal_incremental`, for `minimal_linear`, `arc_semantics`, `length_units`, `modal_incremental`,
`plane_selection`, `coordinate_offsets`, `g53_machine_coordinates`, `plane_selection`, `coordinate_offsets`, `g53_machine_coordinates`,
`feed_control_modes`, `position_params`, `probe_semantics`, `spindle_orient`, `feed_control_modes`, `position_params`, `probe_semantics`, `spindle_orient`,
`comment_logging`, and `numbered_params`, plus the `g1_zero_feed`, `comment_logging`, `numbered_params`, and `namedparam_semantics`, plus the
`arc_radius_mismatch`, `arc_zero_radius`, and `g53_incremental` negative `g1_zero_feed`, `arc_radius_mismatch`, `arc_zero_radius`, and
fixtures. The WASM interpreter file path additionally covers `g53_incremental` negative fixtures. The WASM interpreter file path
`file_open_reset`, `percent_file_finish`, and `oword_subroutine`. OPFS additionally covers `file_open_reset`, `percent_file_finish`, and
validation is limited to the JavaScript host-boundary adapter plus the INI `oword_subroutine`. OPFS validation is limited to the JavaScript host-boundary
adapter plus the INI
browser smoke harness. Full browser coverage, full SDK coverage, and full browser smoke harness. Full browser coverage, full SDK coverage, and full
machine-session validation remain future work. machine-session validation remain future work.

View File

@@ -131,8 +131,14 @@ void append_events_and_state(std::ostringstream &output, const Interp &interp)
extern "C" { extern "C" {
EMSCRIPTEN_KEEPALIVE EMSCRIPTEN_KEEPALIVE
char *lcinterp_run_program(const char *program_text) char *lcinterp_run_program_with_ini(const char *program_text, const char *ini_path)
{ {
if (ini_path && ini_path[0] != '\0') {
setenv("INI_FILE_NAME", ini_path, 1);
} else {
unsetenv("INI_FILE_NAME");
}
Interp interp; Interp interp;
standalone::reset_canon_events(); standalone::reset_canon_events();
initialize_minimal_interp(interp); initialize_minimal_interp(interp);
@@ -160,9 +166,17 @@ char *lcinterp_run_program(const char *program_text)
return copy_result(output.str()); return copy_result(output.str());
} }
EMSCRIPTEN_KEEPALIVE
char *lcinterp_run_program(const char *program_text)
{
return lcinterp_run_program_with_ini(program_text, nullptr);
}
EMSCRIPTEN_KEEPALIVE EMSCRIPTEN_KEEPALIVE
char *lcinterp_run_file(const char *path) char *lcinterp_run_file(const char *path)
{ {
unsetenv("INI_FILE_NAME");
Interp interp; Interp interp;
standalone::reset_canon_events(); standalone::reset_canon_events();
initialize_minimal_interp(interp); initialize_minimal_interp(interp);

View File

@@ -27,6 +27,23 @@ function runProgram(mod, programText) {
} }
} }
function runProgramWithIni(mod, programText, iniPath) {
const programPtr = allocCString(mod, programText);
const iniPathPtr = allocCString(mod, iniPath);
let resultPtr = 0;
try {
resultPtr = mod._lcinterp_run_program_with_ini(programPtr, iniPathPtr);
assert.notEqual(resultPtr, 0);
return mod.UTF8ToString(resultPtr);
} finally {
if (resultPtr) {
mod._lcinterp_free_string(resultPtr);
}
mod._free(programPtr);
mod._free(iniPathPtr);
}
}
function ensureDir(mod, path) { function ensureDir(mod, path) {
try { try {
mod.FS.mkdir(path); mod.FS.mkdir(path);
@@ -114,6 +131,27 @@ for (const fixtureName of fixtureNames) {
verifyExpectedOutput(fixtureName, runProgram(interp, programText), expectedEvents); verifyExpectedOutput(fixtureName, runProgram(interp, programText), expectedEvents);
} }
ensureDir(interp, "/work");
const namedParamIniPath = "/work/namedparams.ini";
interp.FS.writeFile(
namedParamIniPath,
readFileSync(resolve(rootDir, "tests/fixtures/ini/namedparams.ini"), "utf8"),
{ encoding: "utf8" },
);
const namedParamProgramText = readFileSync(
resolve(rootDir, "tests/fixtures/gcode/namedparam_semantics.ngc"),
"utf8",
);
const namedParamExpected = readFileSync(
resolve(rootDir, "tests/fixtures/canon/namedparam_semantics.events"),
"utf8",
).trimEnd();
verifyExpectedOutput(
"namedparam_semantics",
runProgramWithIni(interp, namedParamProgramText, namedParamIniPath),
namedParamExpected,
);
const errorFixtureNames = [ const errorFixtureNames = [
"g1_zero_feed", "g1_zero_feed",
"arc_radius_mismatch", "arc_radius_mismatch",
@@ -140,7 +178,6 @@ const fileFixtureNames = [
"oword_subroutine", "oword_subroutine",
]; ];
ensureDir(interp, "/work");
for (const fixtureName of fileFixtureNames) { for (const fixtureName of fileFixtureNames) {
const programPath = `/work/${fixtureName}.ngc`; const programPath = `/work/${fixtureName}.ngc`;
const programText = readFileSync( const programText = readFileSync(

View File

@@ -95,5 +95,5 @@ link_wasm_module \
-s STACK_SIZE=2MB \ -s STACK_SIZE=2MB \
-s NO_EXIT_RUNTIME=1 \ -s NO_EXIT_RUNTIME=1 \
-s FORCE_FILESYSTEM=1 \ -s FORCE_FILESYSTEM=1 \
-s EXPORTED_FUNCTIONS='["_malloc","_free","_lcinterp_run_program","_lcinterp_run_file","_lcinterp_free_string"]' \ -s EXPORTED_FUNCTIONS='["_malloc","_free","_lcinterp_run_program","_lcinterp_run_program_with_ini","_lcinterp_run_file","_lcinterp_free_string"]' \
-s EXPORTED_RUNTIME_METHODS='["FS","UTF8ToString","stringToUTF8","lengthBytesUTF8"]' -s EXPORTED_RUNTIME_METHODS='["FS","UTF8ToString","stringToUTF8","lengthBytesUTF8"]'