From 7420f2b7fc85d0c81c033531d7e2fadb72ecd9f1 Mon Sep 17 00:00:00 2001 From: Thomas Witte Date: Mon, 31 Aug 2020 17:33:53 +0200 Subject: [PATCH 1/3] SourceChange alternatives can be filtered to apply the change to a specific location --- include/sourcechange.h | 5 ++ src/core/sourcechange.cpp | 160 ++++++++++++++++++++++++++++++++++++++ src/core/sourceexp.cpp | 1 + 3 files changed, 166 insertions(+) diff --git a/include/sourcechange.h b/include/sourcechange.h index 8263f541..1236fb29 100644 --- a/include/sourcechange.h +++ b/include/sourcechange.h @@ -110,6 +110,11 @@ inline eval_result_t operator<< (const eval_result_t& lhs, const source_change_t return eval_success(get_val(lhs), get_sc(lhs) & rhs); } +// helper functions to name and choose the source change variants +optional default_source_change_label(const val& v); +vector source_change_labels(const val& v); +optional> get_sc_for_hint(const val& v, const string& hint); + } } diff --git a/src/core/sourcechange.cpp b/src/core/sourcechange.cpp index 387b6bb1..23c15d8a 100644 --- a/src/core/sourcechange.cpp +++ b/src/core/sourcechange.cpp @@ -72,5 +72,165 @@ vector ApplySCVisitor::apply_changes(const vector& tokens) { return new_tokens; } +optional default_source_change_label(const val& v) { + if (!v.source) + return nullopt; + + auto possible_changes = v.forceValue(v); + + if (!possible_changes) + return nullopt; + + ApplySCVisitor visitor; + (*possible_changes)->accept(visitor); + for (const auto& c : visitor.changes) { + if (c.hint != "" && c.hint != "?") + return c.to_string(); + } + + return visitor.changes.front().to_string(); +} + +vector source_change_labels(const val& v) { + if (!v.source) + return vector(); + + auto possible_changes = v.forceValue(v); + + if (!possible_changes) + return vector(); + + vector result; + + struct SCLabelVisior : ApplySCVisitor { + virtual void visit(const SourceChangeOr& sc_or) override { + for (const auto& c : sc_or.alternatives) { + c->accept(*this); + } + } + + virtual void visit(const SourceChangeAnd& sc_and) override { + if (!sc_and.changes.empty()) + sc_and.changes.back()->accept(*this); + } + + } visitor; + + (*possible_changes)->accept(visitor); + for (const auto& c : visitor.changes) { + result.push_back(c.to_string()); + } + + return result; +} + +/* + * removes the first alternative from a source change recursively. It does a + * depth first search for a source assignment and removes it, removing empty + * or/and nodes in the process. + * + * Precondition: the source changes must result from a call to x.forceValue(x) + * as it compares the match and replacement in the SourceAssignment to discard + * any SourceAssignments that do not influence the current source change. + * (probably not needed?) + * + * It returns true if a matching assignment was found, false otherwise. + */ + +bool remove_alternative(shared_ptr& changes) { + if (auto node = dynamic_pointer_cast(changes)) { + if (node->token.match == node->replacement) { + // the node is a possibility to change the source -> return true + return true; + } + } else if (auto node = dynamic_pointer_cast(changes)) { + for (unsigned i = 0; i < node->changes.size(); ++i) { + auto& c = node->changes[i]; + if (remove_alternative(c)) { + if (auto child_node = dynamic_pointer_cast(c); child_node && child_node->alternatives.empty()) { + node->changes.erase(begin(node->changes) + i); + } else if (auto child_node = dynamic_pointer_cast(c); child_node && child_node->changes.empty()) { + node->changes.erase(begin(node->changes) + i); + } else if (auto child_node = dynamic_pointer_cast(c); child_node) { + node->changes.erase(begin(node->changes) + i); + } + return true; + } + } + return false; + } else if (auto node = dynamic_pointer_cast(changes)) { + for (unsigned i = 0; i < node->alternatives.size(); ++i) { + auto& c = node->alternatives[i]; + if (remove_alternative(c)) { + if (auto child_node = dynamic_pointer_cast(c); child_node && child_node->alternatives.empty()) { + node->alternatives.erase(begin(node->alternatives) + i); + } else if (auto child_node = dynamic_pointer_cast(c); child_node && child_node->changes.empty()) { + node->alternatives.erase(begin(node->alternatives) + i); + } else if (auto child_node = dynamic_pointer_cast(c); child_node) { + node->alternatives.erase(begin(node->alternatives) + i); + } + return true; + } + } + return false; + } + return false; +} + +/* + * get the source change, so that modifications to v cause a change of the + * source identified by hint. This is done by inserting $ operator to direct the + * changes to the desired source and not other alternatives. + * + * Example: + * a = 5 + * b = 3 + * x = a+b + * + * get_sc_for_hint(x, "b") => change 5 -> $5 + * + * The hint is an attempt to enumerate the + * different alternatives for source changes. A more sophisticated structure + * than a string is probably necessary in the future. + */ + +optional> get_sc_for_hint(const val& v, const string& hint) { + if (!v.source) + return nullopt; + + auto possible_changes = v.forceValue(v); + + if (!possible_changes) + return nullopt; + + auto source_changes = make_shared(); + + for(;;) { + ApplySCVisitor visitor; + (*possible_changes)->accept(visitor); + for (const auto& c : visitor.changes) { + std::cout << c.hint << std::endl; + if (c.to_string() == hint) { + // we don't have to do more! + return source_changes; + } + } + + if (visitor.changes.empty()) + break; + + // remove current alternative and add it as a source change with $ to source_changes + remove_alternative(*possible_changes); + for (const auto& c : visitor.changes) { + if (c.token.match == c.replacement) { + source_changes->changes.push_back(SourceAssignment::create(c.token, "$" + c.replacement)); + } + } + } + + // We could not change the source to modify hint :-( + return nullopt; +} + } } diff --git a/src/core/sourceexp.cpp b/src/core/sourceexp.cpp index 0fb5a9e4..afc32d89 100644 --- a/src/core/sourceexp.cpp +++ b/src/core/sourceexp.cpp @@ -238,6 +238,7 @@ source_change_t sourceunop::forceValue(const val& new_v) const { auto res_and = make_shared(); res_and->changes.push_back(*result); res_and->changes.push_back(SourceAssignment::create(op, "")); + res_and->changes.back()->hint = identifier; res_or->alternatives.push_back(res_and); } From 74f7397db1c5ebb89aa65bd310e62f4a8d011a0e Mon Sep 17 00:00:00 2001 From: Thomas Witte Date: Wed, 9 Sep 2020 11:03:21 +0200 Subject: [PATCH 2/3] Apply suggested change making helper function static Co-authored-by: Matthias Seiffert --- src/core/sourcechange.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/sourcechange.cpp b/src/core/sourcechange.cpp index 1920089b..cff54b9a 100644 --- a/src/core/sourcechange.cpp +++ b/src/core/sourcechange.cpp @@ -137,7 +137,7 @@ vector source_change_labels(const val& v) { * It returns true if a matching assignment was found, false otherwise. */ -bool remove_alternative(shared_ptr& changes) { +static bool remove_alternative(shared_ptr& changes) { if (auto node = dynamic_pointer_cast(changes)) { if (node->token.match == node->replacement) { // the node is a possibility to change the source -> return true From 7b607d698c3a3e9c4ad166d21627001ba9e382e1 Mon Sep 17 00:00:00 2001 From: Thomas Witte Date: Wed, 9 Sep 2020 11:26:21 +0200 Subject: [PATCH 3/3] moved and added comments --- include/MiniLua/sourcechange.hpp | 38 ++++++++++++++++++++++++++++++++ src/core/sourcechange.cpp | 17 -------------- 2 files changed, 38 insertions(+), 17 deletions(-) diff --git a/include/MiniLua/sourcechange.hpp b/include/MiniLua/sourcechange.hpp index d2e1850c..7793f63e 100644 --- a/include/MiniLua/sourcechange.hpp +++ b/include/MiniLua/sourcechange.hpp @@ -111,8 +111,46 @@ inline eval_result_t operator<< (const eval_result_t& lhs, const source_change_t } // helper functions to name and choose the source change variants + +/* + * returns a label for the source change that is automatically chosen when v is + * changed. This can be used e.g. for a mouseover to show the user, what will be + * changed if the value is manipulated. As an additional hint, the name of the + * first variable binding of the source value is also encoded. The actual output + * format might be subject to change. + * + * Example (not the exact output): + * + * radius = 7.3 + * v = radius + 7 + * default_source_change_label(v) => "location 10,3 [hint: radius]" + */ optional default_source_change_label(const val& v); + +/* + * returns a list of labels of all possible variants of source changes. The + * alternatives are named after the location, that is changed when the changes + * are applied. As an additional hint, the name of the first variable binding of + * the source value is also encoded. + */ vector source_change_labels(const val& v); + +/* + * get the source change, so that modifications to v cause a change of the + * source identified by hint. This is done by inserting $ operator to direct the + * changes to the desired source and not other alternatives. + * + * Example: + * a = 5 + * b = 3 + * x = a+b + * + * get_sc_for_hint(x, "b") => change 5 -> $5 + * + * The hint is an attempt to enumerate the + * different alternatives for source changes. A more sophisticated structure + * than a string is probably necessary in the future. + */ optional> get_sc_for_hint(const val& v, const string& hint); } diff --git a/src/core/sourcechange.cpp b/src/core/sourcechange.cpp index cff54b9a..4886a901 100644 --- a/src/core/sourcechange.cpp +++ b/src/core/sourcechange.cpp @@ -177,23 +177,6 @@ static bool remove_alternative(shared_ptr& changes) { return false; } -/* - * get the source change, so that modifications to v cause a change of the - * source identified by hint. This is done by inserting $ operator to direct the - * changes to the desired source and not other alternatives. - * - * Example: - * a = 5 - * b = 3 - * x = a+b - * - * get_sc_for_hint(x, "b") => change 5 -> $5 - * - * The hint is an attempt to enumerate the - * different alternatives for source changes. A more sophisticated structure - * than a string is probably necessary in the future. - */ - optional> get_sc_for_hint(const val& v, const string& hint) { if (!v.source) return nullopt;