Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
5 changes: 5 additions & 0 deletions include/MiniLua/sourcechange.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<string> default_source_change_label(const val& v);
vector<string> source_change_labels(const val& v);
optional<shared_ptr<SourceChange>> get_sc_for_hint(const val& v, const string& hint);

}
}

Expand Down
160 changes: 160 additions & 0 deletions src/core/sourcechange.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,5 +72,165 @@ 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.
*/

bool remove_alternative(shared_ptr<SourceChange>& changes) {
Comment thread
ThomasWitte marked this conversation as resolved.
Outdated
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;
}

/*
* 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.
*/
Comment thread
Lythenas marked this conversation as resolved.
Outdated

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