From 88051588549507efb0a9d3dcab45666bd92622be Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 25 Nov 2025 11:57:17 +0000 Subject: [PATCH 1/3] Initial plan From 24a57d10a4dbea720578c7ceceac61cf385c0bec Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 25 Nov 2025 12:05:51 +0000 Subject: [PATCH 2/3] Add binding for rename function for term and rule in apyds and atsds Co-authored-by: hzhangxyz <11623447+hzhangxyz@users.noreply.github.com> --- apyds/ds.cc | 20 ++++++++++++++++ apyds/rule_t.py | 29 +++++++++++++++++++++++ apyds/term_t.py | 27 +++++++++++++++++++++ atsds/ds.cc | 20 ++++++++++++++++ atsds/tsds.mts | 58 +++++++++++++++++++++++++++++++++++++++++++++ package-lock.json | 10 +------- tests/test_rule.mjs | 30 +++++++++++++++++++++++ tests/test_rule.py | 30 +++++++++++++++++++++++ tests/test_term.mjs | 30 +++++++++++++++++++++++ tests/test_term.py | 30 +++++++++++++++++++++++ 10 files changed, 275 insertions(+), 9 deletions(-) diff --git a/apyds/ds.cc b/apyds/ds.cc index 8926f96..169525d 100644 --- a/apyds/ds.cc +++ b/apyds/ds.cc @@ -90,6 +90,24 @@ auto rule_match(ds::rule_t* rule_1, ds::rule_t* rule_2, int length) -> std::uniq return std::unique_ptr(result); } +auto term_rename(ds::term_t* term, ds::term_t* prefix_and_suffix, int length) -> std::unique_ptr { + auto result = reinterpret_cast(operator new(length)); + if (result->rename(term, prefix_and_suffix, reinterpret_cast(result) + length) == nullptr) [[unlikely]] { + operator delete(result); + return std::unique_ptr(nullptr); + } + return std::unique_ptr(result); +} + +auto rule_rename(ds::rule_t* rule, ds::rule_t* prefix_and_suffix, int length) -> std::unique_ptr { + ds::rule_t* result = reinterpret_cast(operator new(length)); + if (result->rename(rule, prefix_and_suffix, reinterpret_cast(result) + length) == nullptr) [[unlikely]] { + operator delete(result); + return std::unique_ptr(nullptr); + } + return std::unique_ptr(result); +} + PYBIND11_MODULE(_ds, m) { auto string_t = py::class_(m, "String"); auto item_t = py::class_(m, "Item"); @@ -130,6 +148,8 @@ PYBIND11_MODULE(_ds, m) { term_t.def_static("ground", term_ground); rule_t.def_static("ground", rule_ground); rule_t.def_static("match", rule_match); + term_t.def_static("rename", term_rename); + rule_t.def_static("rename", rule_rename); auto search_t = py::class_(m, "Search"); search_t.def(py::init()); diff --git a/apyds/rule_t.py b/apyds/rule_t.py index d49d82a..0dfd8b0 100644 --- a/apyds/rule_t.py +++ b/apyds/rule_t.py @@ -111,5 +111,34 @@ def __matmul__(self, other: Rule) -> Rule | None: return None return Rule(rule, capacity) + def rename(self, prefix_and_suffix: Rule) -> Rule | None: + """Rename all variables in this rule by adding prefix and suffix. + + Args: + prefix_and_suffix: A rule with only a conclusion that is a list with two inner lists. + Each inner list contains 0 or 1 item representing the prefix and suffix. + Example: Rule("((pre_) (_suf))") adds "pre_" as prefix and "_suf" as suffix. + + Returns: + The renamed rule, or None if renaming fails. + + Example: + >>> a = Rule("`x") + >>> b = Rule("((pre_) (_suf))") + >>> str(a.rename(b)) + '----\\n`pre_x_suf\\n' + >>> + >>> # With empty prefix (only suffix) + >>> c = Rule("`x") + >>> d = Rule("(() (_suf))") + >>> str(c.rename(d)) + '----\\n`x_suf\\n' + """ + capacity = buffer_size() + rule = ds.Rule.rename(self.value, prefix_and_suffix.value, capacity) + if rule is None: + return None + return Rule(rule, capacity) + def __repr__(self) -> str: return f"Rule[\n{self}]" diff --git a/apyds/term_t.py b/apyds/term_t.py index a944d36..d23e43e 100644 --- a/apyds/term_t.py +++ b/apyds/term_t.py @@ -76,3 +76,30 @@ def ground(self, other: Term, scope: str | None = None) -> Term | None: if term is None: return None return Term(term, capacity) + + def rename(self, prefix_and_suffix: Term) -> Term | None: + """Rename all variables in this term by adding prefix and suffix. + + Args: + prefix_and_suffix: A term representing a list with two inner lists. + Each inner list contains 0 or 1 item representing the prefix and suffix. + Example: Term("((pre_) (_suf))") adds "pre_" as prefix and "_suf" as suffix. + + Returns: + The renamed term, or None if renaming fails. + + Example: + >>> a = Term("`x") + >>> b = Term("((pre_) (_suf))") + >>> str(a.rename(b)) # "`pre_x_suf" + >>> + >>> # With empty prefix (only suffix) + >>> c = Term("`x") + >>> d = Term("(() (_suf))") + >>> str(c.rename(d)) # "`x_suf" + """ + capacity = buffer_size() + term = ds.Term.rename(self.value, prefix_and_suffix.value, capacity) + if term is None: + return None + return Term(term, capacity) diff --git a/atsds/ds.cc b/atsds/ds.cc index 3fdc02b..d500eab 100644 --- a/atsds/ds.cc +++ b/atsds/ds.cc @@ -97,6 +97,24 @@ auto rule_match(ds::rule_t* rule_1, ds::rule_t* rule_2, int length) -> std::uniq return std::unique_ptr(result); } +auto term_rename(ds::term_t* term, ds::term_t* prefix_and_suffix, int length) -> std::unique_ptr { + auto result = reinterpret_cast(operator new(length)); + if (result->rename(term, prefix_and_suffix, reinterpret_cast(result) + length) == nullptr) [[unlikely]] { + operator delete(result); + return std::unique_ptr(nullptr); + } + return std::unique_ptr(result); +} + +auto rule_rename(ds::rule_t* rule, ds::rule_t* prefix_and_suffix, int length) -> std::unique_ptr { + ds::rule_t* result = reinterpret_cast(operator new(length)); + if (result->rename(rule, prefix_and_suffix, reinterpret_cast(result) + length) == nullptr) [[unlikely]] { + operator delete(result); + return std::unique_ptr(nullptr); + } + return std::unique_ptr(result); +} + auto search_add(ds::search_t* search, const std::string& text) -> bool { return search->add(text); } @@ -146,6 +164,8 @@ EMSCRIPTEN_BINDINGS(ds) { term_t.class_function("ground", term_ground, em::return_value_policy::take_ownership()); rule_t.class_function("ground", rule_ground, em::return_value_policy::take_ownership()); rule_t.class_function("match", rule_match, em::return_value_policy::take_ownership()); + term_t.class_function("rename", term_rename, em::return_value_policy::take_ownership()); + rule_t.class_function("rename", rule_rename, em::return_value_policy::take_ownership()); auto search_t = em::class_("Search"); search_t.constructor(); diff --git a/atsds/tsds.mts b/atsds/tsds.mts index 8a3e690..4409ba4 100644 --- a/atsds/tsds.mts +++ b/atsds/tsds.mts @@ -356,6 +356,35 @@ export class term_t extends _common_t { } return new term_t(term, capacity); } + + /** + * Rename all variables in this term by adding prefix and suffix. + * + * @param prefix_and_suffix - A term representing a list with two inner lists. + * Each inner list contains 0 or 1 item representing the prefix and suffix. + * Example: "((pre_) (_suf))" adds "pre_" as prefix and "_suf" as suffix. + * @returns The renamed term, or null if renaming fails. + * + * @example + * ```typescript + * const a = new term_t("`x"); + * const b = new term_t("((pre_) (_suf))"); + * console.log(a.rename(b).toString()); // "`pre_x_suf" + * + * // With empty prefix (only suffix) + * const c = new term_t("`x"); + * const d = new term_t("(() (_suf))"); + * console.log(c.rename(d).toString()); // "`x_suf" + * ``` + */ + rename(prefix_and_suffix: term_t): term_t | null { + const capacity = buffer_size(); + const term = ds.Term.rename(this.value, prefix_and_suffix.value, capacity); + if (term === null) { + return null; + } + return new term_t(term, capacity); + } } /** @@ -461,6 +490,35 @@ export class rule_t extends _common_t { } return new rule_t(rule, capacity); } + + /** + * Rename all variables in this rule by adding prefix and suffix. + * + * @param prefix_and_suffix - A rule with only a conclusion that is a list with two inner lists. + * Each inner list contains 0 or 1 item representing the prefix and suffix. + * Example: "((pre_) (_suf))" adds "pre_" as prefix and "_suf" as suffix. + * @returns The renamed rule, or null if renaming fails. + * + * @example + * ```typescript + * const a = new rule_t("`x"); + * const b = new rule_t("((pre_) (_suf))"); + * console.log(a.rename(b).toString()); // "----\n`pre_x_suf\n" + * + * // With empty prefix (only suffix) + * const c = new rule_t("`x"); + * const d = new rule_t("(() (_suf))"); + * console.log(c.rename(d).toString()); // "----\n`x_suf\n" + * ``` + */ + rename(prefix_and_suffix: rule_t): rule_t | null { + const capacity = buffer_size(); + const rule = ds.Rule.rename(this.value, prefix_and_suffix.value, capacity); + if (rule === null) { + return null; + } + return new rule_t(rule, capacity); + } } /** diff --git a/package-lock.json b/package-lock.json index cd154ba..46b4e33 100644 --- a/package-lock.json +++ b/package-lock.json @@ -53,7 +53,6 @@ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -1637,7 +1636,6 @@ "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~7.16.0" } @@ -2301,7 +2299,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", @@ -4160,7 +4157,6 @@ "integrity": "sha512-F26gjC0yWN8uAA5m5Ss8ZQf5nDHWGlN/xWZIh8S5SRbsEKBovwZhxGd6LJlbZYxBgCYOtreSUyb8hpXyGC5O4A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/core": "30.2.0", "@jest/types": "30.2.0", @@ -5846,7 +5842,6 @@ "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -6751,7 +6746,6 @@ "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -6795,8 +6789,7 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "dev": true, - "license": "0BSD", - "peer": true + "license": "0BSD" }, "node_modules/type-detect": { "version": "4.0.8", @@ -6905,7 +6898,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/tests/test_rule.mjs b/tests/test_rule.mjs index 373ce6e..dbcc897 100644 --- a/tests/test_rule.mjs +++ b/tests/test_rule.mjs @@ -91,3 +91,33 @@ test("match", () => { fail = new rule_t("(`q <- `p)"); expect(mp.match(fail)).toBeNull(); }); + +test("rename_simple", () => { + const a = new rule_t("`x"); + const b = new rule_t("((pre_) (_suf))"); + expect(a.rename(b).toString()).toBe("----\n`pre_x_suf\n"); +}); + +test("rename_empty_prefix", () => { + const a = new rule_t("`x"); + const b = new rule_t("(() (_suf))"); + expect(a.rename(b).toString()).toBe("----\n`x_suf\n"); +}); + +test("rename_empty_suffix", () => { + const a = new rule_t("`x"); + const b = new rule_t("((pre_) ())"); + expect(a.rename(b).toString()).toBe("----\n`pre_x\n"); +}); + +test("rename_with_premises", () => { + const a = new rule_t("`p\n`q\n----------\n`r\n"); + const b = new rule_t("((pre_) (_suf))"); + expect(a.rename(b).toString()).toBe("`pre_p_suf\n`pre_q_suf\n----------\n`pre_r_suf\n"); +}); + +test("rename_invalid", () => { + const a = new rule_t("`x"); + const b = new rule_t("item"); + expect(a.rename(b)).toBeNull(); +}); diff --git a/tests/test_rule.py b/tests/test_rule.py index 163015a..c6383a4 100644 --- a/tests/test_rule.py +++ b/tests/test_rule.py @@ -103,3 +103,33 @@ def test_match() -> None: fail = apyds.Rule("`q <- `p") assert mp @ fail is None + + +def test_rename_simple() -> None: + a = apyds.Rule("`x") + b = apyds.Rule("((pre_) (_suf))") + assert str(a.rename(b)) == "----\n`pre_x_suf\n" + + +def test_rename_empty_prefix() -> None: + a = apyds.Rule("`x") + b = apyds.Rule("(() (_suf))") + assert str(a.rename(b)) == "----\n`x_suf\n" + + +def test_rename_empty_suffix() -> None: + a = apyds.Rule("`x") + b = apyds.Rule("((pre_) ())") + assert str(a.rename(b)) == "----\n`pre_x\n" + + +def test_rename_with_premises() -> None: + a = apyds.Rule("`p\n`q\n----------\n`r\n") + b = apyds.Rule("((pre_) (_suf))") + assert str(a.rename(b)) == "`pre_p_suf\n`pre_q_suf\n----------\n`pre_r_suf\n" + + +def test_rename_invalid() -> None: + a = apyds.Rule("`x") + b = apyds.Rule("item") + assert a.rename(b) is None diff --git a/tests/test_term.mjs b/tests/test_term.mjs index 6829c39..bf8c9af 100644 --- a/tests/test_term.mjs +++ b/tests/test_term.mjs @@ -69,3 +69,33 @@ test("ground_scope", () => { const b = new term_t("((x y `a `b) (y x `b `c))"); expect(a.ground(b, "x").toString()).toBe("`c"); }); + +test("rename_simple", () => { + const a = new term_t("`x"); + const b = new term_t("((pre_) (_suf))"); + expect(a.rename(b).toString()).toBe("`pre_x_suf"); +}); + +test("rename_empty_prefix", () => { + const a = new term_t("`x"); + const b = new term_t("(() (_suf))"); + expect(a.rename(b).toString()).toBe("`x_suf"); +}); + +test("rename_empty_suffix", () => { + const a = new term_t("`x"); + const b = new term_t("((pre_) ())"); + expect(a.rename(b).toString()).toBe("`pre_x"); +}); + +test("rename_list", () => { + const a = new term_t("(`x `y)"); + const b = new term_t("((p_) (_s))"); + expect(a.rename(b).toString()).toBe("(`p_x_s `p_y_s)"); +}); + +test("rename_invalid", () => { + const a = new term_t("`x"); + const b = new term_t("item"); + expect(a.rename(b)).toBeNull(); +}); diff --git a/tests/test_term.py b/tests/test_term.py index 858caaf..6a0d507 100644 --- a/tests/test_term.py +++ b/tests/test_term.py @@ -80,3 +80,33 @@ def test_ground_scope() -> None: a = apyds.Term("`a") b = apyds.Term("((x y `a `b) (y x `b `c))") assert str(a.ground(b, "x")) == "`c" + + +def test_rename_simple() -> None: + a = apyds.Term("`x") + b = apyds.Term("((pre_) (_suf))") + assert str(a.rename(b)) == "`pre_x_suf" + + +def test_rename_empty_prefix() -> None: + a = apyds.Term("`x") + b = apyds.Term("(() (_suf))") + assert str(a.rename(b)) == "`x_suf" + + +def test_rename_empty_suffix() -> None: + a = apyds.Term("`x") + b = apyds.Term("((pre_) ())") + assert str(a.rename(b)) == "`pre_x" + + +def test_rename_list() -> None: + a = apyds.Term("(`x `y)") + b = apyds.Term("((p_) (_s))") + assert str(a.rename(b)) == "(`p_x_s `p_y_s)" + + +def test_rename_invalid() -> None: + a = apyds.Term("`x") + b = apyds.Term("item") + assert a.rename(b) is None From 4413f425fc32a29bf2ad0b79e2514fba9ebd8ca9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 25 Nov 2025 12:09:44 +0000 Subject: [PATCH 3/3] Fix code style: use auto keyword consistently in C++ binding files Co-authored-by: hzhangxyz <11623447+hzhangxyz@users.noreply.github.com> --- apyds/ds.cc | 2 +- atsds/ds.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apyds/ds.cc b/apyds/ds.cc index 169525d..14f8ea8 100644 --- a/apyds/ds.cc +++ b/apyds/ds.cc @@ -100,7 +100,7 @@ auto term_rename(ds::term_t* term, ds::term_t* prefix_and_suffix, int length) -> } auto rule_rename(ds::rule_t* rule, ds::rule_t* prefix_and_suffix, int length) -> std::unique_ptr { - ds::rule_t* result = reinterpret_cast(operator new(length)); + auto result = reinterpret_cast(operator new(length)); if (result->rename(rule, prefix_and_suffix, reinterpret_cast(result) + length) == nullptr) [[unlikely]] { operator delete(result); return std::unique_ptr(nullptr); diff --git a/atsds/ds.cc b/atsds/ds.cc index d500eab..ffb8b4c 100644 --- a/atsds/ds.cc +++ b/atsds/ds.cc @@ -107,7 +107,7 @@ auto term_rename(ds::term_t* term, ds::term_t* prefix_and_suffix, int length) -> } auto rule_rename(ds::rule_t* rule, ds::rule_t* prefix_and_suffix, int length) -> std::unique_ptr { - ds::rule_t* result = reinterpret_cast(operator new(length)); + auto result = reinterpret_cast(operator new(length)); if (result->rename(rule, prefix_and_suffix, reinterpret_cast(result) + length) == nullptr) [[unlikely]] { operator delete(result); return std::unique_ptr(nullptr);