Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions include/MiniLua/sourcechange.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,49 @@ 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

/*
* 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<string> 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<string> 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<shared_ptr<SourceChange>> get_sc_for_hint(const val& v, const string& hint);

}
}

Expand Down
143 changes: 143 additions & 0 deletions src/core/sourcechange.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,5 +72,148 @@ vector<LuaToken> ApplySCVisitor::apply_changes(const vector<LuaToken>& tokens) {
return new_tokens;
}

optional<string> 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<string> source_change_labels(const val& v) {
if (!v.source)
return vector<string>();

auto possible_changes = v.forceValue(v);

if (!possible_changes)
return vector<string>();

vector<string> 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.
*/

static bool remove_alternative(shared_ptr<SourceChange>& changes) {
if (auto node = dynamic_pointer_cast<SourceAssignment>(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<SourceChangeAnd>(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<SourceChangeOr>(c); child_node && child_node->alternatives.empty()) {
node->changes.erase(begin(node->changes) + i);
} else if (auto child_node = dynamic_pointer_cast<SourceChangeAnd>(c); child_node && child_node->changes.empty()) {
node->changes.erase(begin(node->changes) + i);
} else if (auto child_node = dynamic_pointer_cast<SourceAssignment>(c); child_node) {
node->changes.erase(begin(node->changes) + i);
}
return true;
}
}
return false;
} else if (auto node = dynamic_pointer_cast<SourceChangeOr>(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<SourceChangeOr>(c); child_node && child_node->alternatives.empty()) {
node->alternatives.erase(begin(node->alternatives) + i);
} else if (auto child_node = dynamic_pointer_cast<SourceChangeAnd>(c); child_node && child_node->changes.empty()) {
node->alternatives.erase(begin(node->alternatives) + i);
} else if (auto child_node = dynamic_pointer_cast<SourceAssignment>(c); child_node) {
node->alternatives.erase(begin(node->alternatives) + i);
}
return true;
}
}
return false;
}
return false;
}

optional<shared_ptr<SourceChange>> 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<SourceChangeAnd>();

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;
}

}
}
1 change: 1 addition & 0 deletions src/core/sourceexp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ source_change_t sourceunop::forceValue(const val& new_v) const {
auto res_and = make_shared<SourceChangeAnd>();
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);
}

Expand Down