From 575bcf24d839c91b81969936f94f11ef8fd4e5cb Mon Sep 17 00:00:00 2001 From: wangdequan Date: Sat, 23 May 2026 17:50:37 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E7=A4=BA=E8=AF=8D=EF=BC=9A=20?= =?UTF-8?q?=E5=AF=B9=E6=A0=87=20LinuxCNC=EF=BC=8C=E5=88=86=E9=98=B6?= =?UTF-8?q?=E6=AE=B5=E5=AE=8C=E5=96=84=20Web=20=E7=89=88=E6=95=B0=E6=8E=A7?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F=E4=BB=BF=E7=9C=9F=E7=9A=84=20WASM=20?= =?UTF-8?q?=E7=A8=8B=E5=BA=8F=EF=BC=8C=E7=BB=A7=E7=BB=AD=E8=A1=A5=E9=BD=90?= =?UTF-8?q?=20G=20=E4=BB=A3=E7=A0=81=E8=AF=AD=E6=B3=95=E5=B9=B6=E4=B8=A5?= =?UTF-8?q?=E6=A0=BC=E6=B5=8B=E8=AF=95=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 总结: - 对齐 LinuxCNC 对同一行重复非 G/M 字的拒绝行为,覆盖 G1 X1 X2 等重复轴字错误。 - 将 smoke parser 字母字归一为大写,补充 lowercase 普通字和命名 O-word 回归覆盖。 - 已通过 ./test-native.sh 和 ./test-linuxcnc-source-link.sh。 --- core/src/smoke_gcode_parser.cpp | 13 ++++++- core/tests/cnc_sim_api_smoke.cpp | 65 ++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 1 deletion(-) diff --git a/core/src/smoke_gcode_parser.cpp b/core/src/smoke_gcode_parser.cpp index 5c07a3e..12e31f2 100644 --- a/core/src/smoke_gcode_parser.cpp +++ b/core/src/smoke_gcode_parser.cpp @@ -354,13 +354,14 @@ bool looks_like_real_start(char ch) { bool parse_word_list_impl(const std::string &line, std::vector *words, std::string *error, bool strict) { words->clear(); + std::array seen_letters{}; const char *text = line.c_str(); for (size_t i = 0; text[i] != '\0';) { if (std::isspace(static_cast(text[i]))) { ++i; continue; } - char letter = text[i]; + char letter = static_cast(std::toupper(static_cast(text[i]))); if (!std::isalpha(static_cast(letter))) { ++i; continue; @@ -391,6 +392,16 @@ bool parse_word_list_impl(const std::string &line, std::vector *words, std } return false; } + if (letter != 'G' && letter != 'M') { + const size_t letter_index = static_cast(letter - 'A'); + if (strict && seen_letters[letter_index]) { + if (error) { + *error = "multiple " + std::string(1, letter) + " words on one line"; + } + return false; + } + seen_letters[letter_index] = true; + } words->push_back({letter, value}); i = next; } diff --git a/core/tests/cnc_sim_api_smoke.cpp b/core/tests/cnc_sim_api_smoke.cpp index 19047ea..194b236 100644 --- a/core/tests/cnc_sim_api_smoke.cpp +++ b/core/tests/cnc_sim_api_smoke.cpp @@ -242,6 +242,8 @@ int main() { bool saw_global_low_parameter_after_oword = false; bool saw_named_oword_call = false; bool saw_named_oword_return = false; + bool saw_lowercase_named_oword_call = false; + bool saw_lowercase_named_oword_return = false; bool saw_named_parameter_reference = false; bool saw_named_parameter_call_arg = false; bool saw_named_parameter_local = false; @@ -274,6 +276,7 @@ int main() { bool saw_word_unary_function_motion = false; bool saw_word_atan_function_motion = false; bool saw_signed_word_unary_function_motion = false; + bool saw_lowercase_motion = false; bool saw_trig_function_motion = false; bool saw_inverse_trig_function_motion = false; bool saw_atan_function_motion = false; @@ -3030,6 +3033,41 @@ int main() { ok &= expect(saw_named_oword_call, "expected named O-word subprogram call"); ok &= expect(saw_named_oword_return, "expected named O-word return to caller"); + const char lowercase_named_oword_subprogram[] = + "g21 g90\n" + "g0 x0 y0 z0\n" + "f100\n" + "o call [5]\n" + "g1 y3\n" + "m30\n" + "o sub\n" + "g1 x#1\n" + "o return\n" + "g1 x99\n" + "o endsub\n"; + events.clear(); + ok &= expect(cnc_sim_parse_program(sim, + lowercase_named_oword_subprogram, + sizeof(lowercase_named_oword_subprogram) - 1) == 0, + cnc_sim_last_error(sim)); + for (const auto &event : events) { + saw_lowercase_named_oword_call = saw_lowercase_named_oword_call || + (event.type == CNC_SIM_EVENT_LINEAR_FEED && + event.line == 8 && + event.end.x == 5.0); + saw_lowercase_named_oword_return = saw_lowercase_named_oword_return || + (event.type == CNC_SIM_EVENT_LINEAR_FEED && + event.line == 5 && + event.start.x == 5.0 && + event.end.y == 3.0); + ok &= expect(!(event.type == CNC_SIM_EVENT_LINEAR_FEED && + event.line == 10 && + event.end.x == 99.0), + "expected lowercase named O-word return to skip remaining subprogram body"); + } + ok &= expect(saw_lowercase_named_oword_call, "expected lowercase named O-word subprogram call"); + ok &= expect(saw_lowercase_named_oword_return, "expected lowercase named O-word return to caller"); + const char named_parameter_program[] = "# = 5\n" "# = [# + 2]\n" @@ -3524,6 +3562,16 @@ int main() { sizeof(g1_without_feed_program) - 1) != 0, "expected LinuxCNC to reject G1 with zero feed rate"); + const char duplicate_axis_word_program[] = + "G21 G90\n" + "F100\n" + "G1 X1 X2\n"; + events.clear(); + ok &= expect(cnc_sim_parse_program(sim, + duplicate_axis_word_program, + sizeof(duplicate_axis_word_program) - 1) != 0, + "expected LinuxCNC to reject multiple X words on one line"); + const char scientific_notation_word_program[] = "G21 G90\n" "F100\n" @@ -3597,6 +3645,23 @@ int main() { ok &= expect(saw_word_atan_function_motion, "expected LinuxCNC ATAN function as a word value"); ok &= expect(saw_signed_word_unary_function_motion, "expected signed LinuxCNC unary function as a word value"); + const char lowercase_word_program[] = + "g21 g90\n" + "f100\n" + "g1 x3\n"; + events.clear(); + ok &= expect(cnc_sim_parse_program(sim, + lowercase_word_program, + sizeof(lowercase_word_program) - 1) == 0, + cnc_sim_last_error(sim)); + for (const auto &event : events) { + saw_lowercase_motion = saw_lowercase_motion || + (event.type == CNC_SIM_EVENT_LINEAR_FEED && + event.line == 3 && + event.end.x == 3.0); + } + ok &= expect(saw_lowercase_motion, "expected LinuxCNC lowercase word parsing"); + const char additional_math_function_program[] = "#1 = [ASIN[0.5] + ACOS[0.5]]\n" "#2 = [FIX[1.9] + FUP[1.1] + ROUND[-1.5]]\n"