新增解释器核心WASM验证

结论:已建立基于LinuxCNC解释器源码的最小WASM核心构建和minimal_linear夹具验证,并接入host smoke。
This commit is contained in:
2026-06-08 04:56:45 +08:00
parent 1a10e4f260
commit caa35820cd
9 changed files with 395 additions and 3 deletions

View File

@@ -0,0 +1,158 @@
#define private public
#include "emc/rs274ngc/rs274ngc_interp.hh"
#undef private
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <string>
#include <vector>
#include <emscripten/emscripten.h>
#include "canon_event_sink.hh"
#include "linuxcnc_hal_adapter.hh"
#include "linuxcnc_tool_adapter.hh"
namespace {
void seed_hal_values()
{
standalone::reset_hal_adapter();
hal_data_u pin_bit{};
pin_bit.b = true;
standalone::set_hal_value(standalone::HalValueKind::Pin, "standalone.pin-bit", HAL_BIT,
pin_bit);
hal_data_u signal_float{};
signal_float.f = 98.25;
standalone::set_hal_value(standalone::HalValueKind::Signal, "standalone.signal-float",
HAL_FLOAT, signal_float);
hal_data_u param_s32{};
param_s32.s = -17;
standalone::set_hal_value(standalone::HalValueKind::Param, "standalone.param-s32", HAL_S32,
param_s32);
hal_data_u pin_u32{};
pin_u32.u = 123456789u;
standalone::set_hal_value(standalone::HalValueKind::Pin, "standalone.pin-u32", HAL_U32,
pin_u32);
hal_data_u signal_s64{};
signal_s64.ls = -9000000000LL;
standalone::set_hal_value(standalone::HalValueKind::Signal, "standalone.signal-s64", HAL_S64,
signal_s64);
hal_data_u param_u64{};
param_u64.lu = 9000000000ULL;
standalone::set_hal_value(standalone::HalValueKind::Param, "standalone.param-u64", HAL_U64,
param_u64);
hal_data_u disconnected{};
disconnected.f = 12.5;
standalone::set_hal_value(standalone::HalValueKind::Pin, "standalone.disconnected-float",
HAL_FLOAT, disconnected, false);
}
void seed_tool_values()
{
standalone::reset_tool_adapter();
CANON_TOOL_TABLE tool = standalone::tool_entry_init();
tool.toolno = 2;
tool.pocketno = 2;
tool.offset.tran.z = 1.25;
tool.diameter = 0.25;
standalone::set_tool_entry(2, tool);
}
void initialize_minimal_interp(Interp &interp)
{
seed_hal_values();
seed_tool_values();
interp._setup.length_units = CANON_UNITS_MM;
interp._setup.distance_mode = DISTANCE_MODE::ABSOLUTE;
interp._setup.ijk_distance_mode = DISTANCE_MODE::ABSOLUTE;
interp._setup.feed_mode = FEED_MODE::UNITS_PER_MINUTE;
interp._setup.plane = CANON_PLANE::XY;
interp._setup.motion_mode = G_0;
interp._setup.percent_flag = false;
interp._setup.sequence_number = 0;
interp._setup.parameter_occurrence = 0;
interp._setup.num_spindles = 1;
interp._setup.feature_set = FEATURE_INI_VARS | FEATURE_HAL_PIN_VARS;
interp._setup.parameter_g73_peck_clearance = 1.0;
interp._setup.parameter_g83_peck_clearance = 1.0;
interp._setup.parameters[5599] = 1.0;
interp.load_tool_table();
std::memcpy(interp._readers, Interp::default_readers, sizeof(Interp::default_readers));
}
std::vector<std::string> split_program_lines(const char *program_text)
{
std::vector<std::string> lines;
std::istringstream input(program_text ? program_text : "");
std::string line;
while (std::getline(input, line)) {
if (!line.empty()) {
lines.push_back(line + "\n");
}
}
return lines;
}
char *copy_result(const std::string &result)
{
char *out = static_cast<char *>(std::malloc(result.size() + 1));
if (!out) {
return nullptr;
}
std::memcpy(out, result.c_str(), result.size() + 1);
return out;
}
} // namespace
extern "C" {
EMSCRIPTEN_KEEPALIVE
char *lcinterp_run_program(const char *program_text)
{
Interp interp;
standalone::reset_canon_events();
initialize_minimal_interp(interp);
const std::vector<std::string> program = split_program_lines(program_text);
std::ostringstream output;
if (program.empty()) {
output << "error=empty fixture\n";
return copy_result(output.str());
}
for (std::size_t idx = 0; idx < program.size(); ++idx) {
interp._setup.sequence_number = static_cast<int>(idx + 1);
const int execute_rc = interp.execute(program[idx].c_str());
output << "execute_line_" << (idx + 1) << "=" << execute_rc << "\n";
if (execute_rc > INTERP_MIN_ERROR) {
char error_buf[LINELEN] = {0};
interp.error_text(execute_rc, error_buf, sizeof(error_buf));
output << "error_text=" << error_buf << "\n";
break;
}
}
for (const auto &event : standalone::canon_events()) {
output << "canon_event=" << event << "\n";
}
return copy_result(output.str());
}
EMSCRIPTEN_KEEPALIVE
void lcinterp_free_string(char *value)
{
std::free(value);
}
} // extern "C"

View File

@@ -0,0 +1,47 @@
#include <cstdlib>
#include <cstring>
#include <wordexp.h>
extern "C" {
int wordexp(const char *words, wordexp_t *pwordexp, int)
{
if (!words || !pwordexp) {
return WRDE_BADCHAR;
}
char **wordv = static_cast<char **>(std::calloc(2, sizeof(char *)));
if (!wordv) {
return WRDE_NOSPACE;
}
wordv[0] = static_cast<char *>(std::malloc(std::strlen(words) + 1));
if (!wordv[0]) {
std::free(wordv);
return WRDE_NOSPACE;
}
std::strcpy(wordv[0], words);
pwordexp->we_wordc = 1;
pwordexp->we_wordv = wordv;
pwordexp->we_offs = 0;
return 0;
}
void wordfree(wordexp_t *pwordexp)
{
if (!pwordexp || !pwordexp->we_wordv) {
return;
}
for (size_t index = 0; index < pwordexp->we_wordc; ++index) {
std::free(pwordexp->we_wordv[index]);
}
std::free(pwordexp->we_wordv);
pwordexp->we_wordc = 0;
pwordexp->we_wordv = nullptr;
pwordexp->we_offs = 0;
}
} // extern "C"

View File

@@ -0,0 +1,14 @@
#pragma once
namespace boost {
class noncopyable {
protected:
noncopyable() = default;
~noncopyable() = default;
noncopyable(const noncopyable &) = delete;
noncopyable &operator=(const noncopyable &) = delete;
};
} // namespace boost

View File

@@ -0,0 +1,8 @@
#pragma once
#include <libgen.h>
inline char *basename(const char *path)
{
return ::basename(const_cast<char *>(path));
}