diff --git a/wasm-port/docs/compatibility-validation.md b/wasm-port/docs/compatibility-validation.md index b8f38ca..22246d2 100644 --- a/wasm-port/docs/compatibility-validation.md +++ b/wasm-port/docs/compatibility-validation.md @@ -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`, `plane_selection`, `coordinate_offsets`, `g53_machine_coordinates`, `feed_control_modes`, `position_params`, `probe_semantics`, `spindle_orient`, -`comment_logging`, and `numbered_params`, plus the `g1_zero_feed`, -`arc_radius_mismatch`, `arc_zero_radius`, and `g53_incremental` negative -fixtures. The WASM interpreter file path additionally covers -`file_open_reset`, `percent_file_finish`, and `oword_subroutine`. OPFS -validation is limited to the JavaScript host-boundary adapter plus the INI +`comment_logging`, `numbered_params`, and `namedparam_semantics`, plus the +`g1_zero_feed`, `arc_radius_mismatch`, `arc_zero_radius`, and +`g53_incremental` negative fixtures. The WASM interpreter file path +additionally covers `file_open_reset`, `percent_file_finish`, and +`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 machine-session validation remain future work. diff --git a/wasm-port/runtime/core/linuxcnc_wrap/linuxcnc_interp_wasm.cpp b/wasm-port/runtime/core/linuxcnc_wrap/linuxcnc_interp_wasm.cpp index 827f188..6b5dac8 100644 --- a/wasm-port/runtime/core/linuxcnc_wrap/linuxcnc_interp_wasm.cpp +++ b/wasm-port/runtime/core/linuxcnc_wrap/linuxcnc_interp_wasm.cpp @@ -131,8 +131,14 @@ void append_events_and_state(std::ostringstream &output, const Interp &interp) extern "C" { 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; standalone::reset_canon_events(); initialize_minimal_interp(interp); @@ -160,9 +166,17 @@ char *lcinterp_run_program(const char *program_text) 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 char *lcinterp_run_file(const char *path) { + unsetenv("INI_FILE_NAME"); + Interp interp; standalone::reset_canon_events(); initialize_minimal_interp(interp); diff --git a/wasm-port/tests/wasm/node/verify_interp_wasm.mjs b/wasm-port/tests/wasm/node/verify_interp_wasm.mjs index c5e2763..7c56c92 100644 --- a/wasm-port/tests/wasm/node/verify_interp_wasm.mjs +++ b/wasm-port/tests/wasm/node/verify_interp_wasm.mjs @@ -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) { try { mod.FS.mkdir(path); @@ -114,6 +131,27 @@ for (const fixtureName of fixtureNames) { 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 = [ "g1_zero_feed", "arc_radius_mismatch", @@ -140,7 +178,6 @@ const fileFixtureNames = [ "oword_subroutine", ]; -ensureDir(interp, "/work"); for (const fixtureName of fileFixtureNames) { const programPath = `/work/${fixtureName}.ngc`; const programText = readFileSync( diff --git a/wasm-port/tools/build_wasm_core.sh b/wasm-port/tools/build_wasm_core.sh index 3198db0..b85ae33 100755 --- a/wasm-port/tools/build_wasm_core.sh +++ b/wasm-port/tools/build_wasm_core.sh @@ -95,5 +95,5 @@ link_wasm_module \ -s STACK_SIZE=2MB \ -s NO_EXIT_RUNTIME=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"]'