#include "Action.hpp" Action::Action(Action::Type type, std::function<void(Config & config, Action & action)> apply, std::function<void(Config & config, Action & action)> undo, std::function<bool(const Config & config, const Action & action)> appliable) { this->type = type; this->apply = apply; this->undo = undo; this->appliable = appliable; } Action Action::addLinesIfNeeded(int nbLines) { auto apply = [nbLines](Config & config, Action &) { while (!config.has(0, config.getWordIndex()+nbLines, 0)) config.addLines(1); }; auto undo = [](Config &, Action &) { }; auto appliable = [](const Config &, const Action &) { return true; }; return {Type::AddLines, apply, undo, appliable}; } Action Action::moveWordIndex(int movement) { auto apply = [movement](Config & config, Action &) { config.moveWordIndex(movement); }; auto undo = [movement](Config & config, Action &) { config.moveWordIndex(-movement); }; auto appliable = [movement](const Config & config, const Action &) { return config.canMoveWordIndex(movement); }; return {Type::MoveWord, apply, undo, appliable}; } Action Action::setMultiwordId(int multiwordSize) { auto apply = [multiwordSize](Config & config, Action & a) { addHypothesisRelative(Config::idColName, Object::Buffer, 0, fmt::format("{}-{}", config.getCurrentWordId()+1, config.getCurrentWordId()+multiwordSize)).apply(config, a); }; auto undo = [](Config & config, Action &) { config.getLastNotEmpty(Config::idColName, config.getWordIndex()) = ""; }; auto appliable = [](const Config &, const Action &) { return true; }; return {Type::Write, apply, undo, appliable}; } Action Action::consumeCharacterIndex(std::string consumed) { auto apply = [consumed](Config & config, Action &) { config.moveCharacterIndex(consumed.size()); }; auto undo = [consumed](Config & config, Action &) { config.moveCharacterIndex(-consumed.size()); }; auto appliable = [consumed](const Config & config, const Action &) { if (!config.canMoveCharacterIndex(consumed.size())) return false; for (unsigned int i = 0; i < consumed.size(); i++) if (!config.hasCharacter(config.getCharacterIndex()+i) or config.getLetter(config.getCharacterIndex()+i) != consumed[i]) return false; return true; }; return {Type::MoveWord, apply, undo, appliable}; } Action Action::moveCharacterIndex(int movement) { auto apply = [movement](Config & config, Action &) { config.moveCharacterIndex(movement); }; auto undo = [movement](Config & config, Action &) { config.moveCharacterIndex(-movement); }; auto appliable = [movement](const Config & config, const Action &) { return config.canMoveCharacterIndex(movement); }; return {Type::MoveChar, apply, undo, appliable}; } Action Action::addHypothesis(const std::string & colName, std::size_t lineIndex, const std::string & hypothesis) { auto apply = [colName, lineIndex, hypothesis](Config & config, Action &) { config.getFirstEmpty(colName, lineIndex) = hypothesis; }; auto undo = [colName, lineIndex](Config & config, Action &) { config.getLastNotEmpty(colName, lineIndex) = ""; }; auto appliable = [colName, lineIndex](const Config & config, const Action &) { return config.has(colName, lineIndex, 0); }; return {Type::Write, apply, undo, appliable}; } Action Action::addToHypothesis(const std::string & colName, std::size_t lineIndex, const std::string & addition) { auto apply = [colName, lineIndex, addition](Config & config, Action &) { auto & current = config.getLastNotEmptyHyp(colName, lineIndex); current = util::isEmpty(current) ? addition : current.get() + '|' + addition; }; auto undo = [colName, lineIndex](Config & config, Action &) { std::string newValue = config.getLastNotEmpty(colName, lineIndex); while (!newValue.empty() and newValue.back() == '|') newValue.pop_back(); if (!newValue.empty()) newValue.pop_back(); config.getLastNotEmpty(colName, lineIndex) = newValue; }; auto appliable = [colName, lineIndex, addition](const Config & config, const Action &) { if (!config.has(colName, lineIndex, 0)) return false; auto & current = config.getLastNotEmptyHypConst(colName, lineIndex); auto splited = util::split(current.get(), '|'); for (auto & part : splited) if (part == addition) return false; return true; }; return {Type::Write, apply, undo, appliable}; } Action Action::addToHypothesisRelative(const std::string & colName, Object object, int relativeIndex, const std::string & addition) { auto apply = [colName, object, relativeIndex, addition](Config & config, Action & a) { int lineIndex = 0; if (object == Object::Buffer) lineIndex = config.getWordIndex() + relativeIndex; else lineIndex = config.getStack(relativeIndex); return addToHypothesis(colName, lineIndex, addition).apply(config, a); }; auto undo = [colName, object, relativeIndex](Config & config, Action & a) { int lineIndex = 0; if (object == Object::Buffer) lineIndex = config.getWordIndex() + relativeIndex; else lineIndex = config.getStack(relativeIndex); return addToHypothesis(colName, lineIndex, "").undo(config, a); }; auto appliable = [colName, object, relativeIndex, addition](const Config & config, const Action & a) { int lineIndex = 0; if (object == Object::Buffer) lineIndex = config.getWordIndex() + relativeIndex; else if (config.hasStack(relativeIndex)) lineIndex = config.getStack(relativeIndex); else return false; return addToHypothesis(colName, lineIndex, addition).appliable(config, a); }; return {Type::Write, apply, undo, appliable}; } Action Action::addHypothesisRelative(const std::string & colName, Object object, int relativeIndex, const std::string & hypothesis) { auto apply = [colName, object, relativeIndex, hypothesis](Config & config, Action & a) { int lineIndex = 0; if (object == Object::Buffer) lineIndex = config.getWordIndex() + relativeIndex; else lineIndex = config.getStack(relativeIndex); return addHypothesis(colName, lineIndex, hypothesis).apply(config, a); }; auto undo = [colName, object, relativeIndex](Config & config, Action & a) { int lineIndex = 0; if (object == Object::Buffer) lineIndex = config.getWordIndex() + relativeIndex; else lineIndex = config.getStack(relativeIndex); return addHypothesis(colName, lineIndex, "").undo(config, a); }; auto appliable = [colName, object, relativeIndex](const Config & config, const Action & a) { int lineIndex = 0; if (object == Object::Buffer) lineIndex = config.getWordIndex() + relativeIndex; else if (config.hasStack(relativeIndex)) lineIndex = config.getStack(relativeIndex); else return false; return addHypothesis(colName, lineIndex, "").appliable(config, a); }; return {Type::Write, apply, undo, appliable}; } Action Action::pushWordIndexOnStack() { auto apply = [](Config & config, Action &) { config.addToStack(config.getWordIndex()); }; auto undo = [](Config & config, Action &) { config.popStack(); }; auto appliable = [](const Config & config, const Action &) { if (config.hasStack(0) and config.getStack(0) == config.getWordIndex()) return false; return (int)config.getWordIndex() != config.getLastPoppedStack(); }; return {Type::Push, apply, undo, appliable}; } Action Action::popStack() { auto apply = [](Config & config, Action & a) { auto toSave = config.getStack(0); a.data.push_back(std::to_string(toSave)); config.popStack(); }; auto undo = [](Config & config, Action & a) { config.addToStack(std::stoi(a.data.back())); }; auto appliable = [](const Config & config, const Action &) { return config.hasStack(0); }; return {Type::Pop, apply, undo, appliable}; } Action Action::endWord() { auto apply = [](Config & config, Action & a) { config.setCurrentWordId(config.getCurrentWordId()+1); addHypothesisRelative(Config::idColName, Object::Buffer, 0, std::to_string(config.getCurrentWordId())).apply(config, a); if (!config.rawInputOnlySeparatorsLeft() and !config.has(0,config.getWordIndex()+1,0)) config.addLines(1); }; auto undo = [](Config & config, Action &) { config.setCurrentWordId(config.getCurrentWordId()-1); config.getLastNotEmpty(Config::idColName, config.getWordIndex()) = ""; }; auto appliable = [](const Config & config, const Action &) { return !util::isEmpty(config.getAsFeature("FORM", config.getWordIndex())); }; return {Type::Write, apply, undo, appliable}; } Action Action::emptyStack() { auto apply = [](Config & config, Action & a) { while (config.hasStack(0)) { a.data.push_back(std::to_string(config.getStack(0))); config.popStack(); } }; auto undo = [](Config & config, Action & a) { while (a.data.size()) { config.addToStack(std::stoi(a.data.back())); a.data.pop_back(); } }; auto appliable = [](const Config & config, const Action &) { return true; }; return {Type::Pop, apply, undo, appliable}; } Action Action::ignoreCurrentCharacter() { auto apply = [](Config & config, Action & a) { config.moveCharacterIndex(1); }; auto undo = [](Config & config, Action & a) { config.moveCharacterIndex(-1); }; auto appliable = [](const Config & config, const Action &) { return config.hasCharacter(config.getCharacterIndex()) and util::isSeparator(config.getLetter(config.getCharacterIndex())) and config.canMoveCharacterIndex(1); }; return {Type::MoveChar, apply, undo, appliable}; } Action Action::addCurCharToCurWord() { auto apply = [](Config & config, Action & a) { auto & curWord = config.getLastNotEmptyHyp("FORM", config.getWordIndex()); curWord = fmt::format("{}{}", curWord, config.getLetter(config.getCharacterIndex())); }; auto undo = [](Config & config, Action & a) { auto & curWord = config.getLastNotEmptyHyp("FORM", config.getWordIndex()); std::string newWord = curWord; unsigned int nbToPop = fmt::format("{}", config.getLetter(config.getCharacterIndex())).size(); for (unsigned int i = 0; i < nbToPop; i++) newWord.pop_back(); curWord = newWord; }; auto appliable = [](const Config & config, const Action &) { return config.hasCharacter(config.getCharacterIndex()) and !util::isIllegal(config.getLetter(config.getCharacterIndex())); }; return {Type::Write, apply, undo, appliable}; } Action Action::setRoot() { auto apply = [](Config & config, Action & a) { int rootIndex = -1; int firstSentenceIndex = -1; for (int i = config.getStack(0); true; --i) { if (!config.has(0, i, 0)) { if (i < 0) break; util::myThrow("The current sentence is too long to be completly held by the data strucure. Consider increasing SubConfig::SpanSize"); } if (!config.isToken(i)) continue; if (config.getLastNotEmptyHypConst(Config::EOSColName, i) == Config::EOSSymbol1) break; firstSentenceIndex = i; if (util::isEmpty(config.getLastNotEmptyHypConst(Config::headColName, i))) { rootIndex = i; a.data.push_back(std::to_string(i)); } } for (int i = config.getStack(0); true; --i) { if (!config.has(0, i, 0)) { if (i < 0) break; util::myThrow("The current sentence is too long to be completly held by the data strucure. Consider increasing SubConfig::SpanSize"); } if (!config.isToken(i)) continue; if (config.getLastNotEmptyHypConst(Config::EOSColName, i) == Config::EOSSymbol1) break; if (util::isEmpty(config.getLastNotEmptyHypConst(Config::headColName, i))) { if (i == rootIndex) { config.getFirstEmpty(Config::headColName, i) = "0"; config.getFirstEmpty(Config::deprelColName, i) = "root"; } else { config.getFirstEmpty(Config::headColName, i) = std::to_string(rootIndex); } } else { if (std::stoi(config.getLastNotEmptyHypConst(Config::headColName, i)) < firstSentenceIndex) config.getFirstEmpty(Config::headColName, i) = std::to_string(rootIndex); } } }; auto undo = [](Config & config, Action & a) { while (a.data.size()) { config.getLastNotEmptyHyp(Config::headColName, std::stoi(a.data.back())) = ""; a.data.pop_back(); } }; auto appliable = [](const Config & config, const Action &) { return config.hasStack(0); }; return {Type::Write, apply, undo, appliable}; } Action Action::updateIds() { auto apply = [](Config & config, Action & a) { int firstIndexOfSentence = -1; for (int i = config.getStack(0); true; --i) { if (!config.has(0, i, 0)) { if (i < 0) break; util::myThrow("The current sentence is too long to be completly held by the data strucure. Consider increasing SubConfig::SpanSize"); } if (config.isComment(i) || config.isEmptyNode(i)) continue; if (config.getLastNotEmptyHypConst(Config::EOSColName, i) == Config::EOSSymbol1) break; firstIndexOfSentence = i; } if (firstIndexOfSentence < 0) util::myThrow("could not find any token in current sentence"); for (unsigned int i = firstIndexOfSentence, currentId = 1; i <= config.getStack(0); ++i) { if (config.isComment(i) || config.isEmptyNode(i)) continue; if (config.getLastNotEmptyHypConst(Config::EOSColName, i) == Config::EOSSymbol1) break; if (config.isMultiword(i)) config.getFirstEmpty(Config::idColName, i) = fmt::format("{}-{}", currentId, currentId+config.getMultiwordSize(i)); else config.getFirstEmpty(Config::idColName, i) = fmt::format("{}", currentId++); } }; auto undo = [](Config & config, Action & a) { // TODO : undo this }; auto appliable = [](const Config &, const Action &) { return true; }; return {Type::Write, apply, undo, appliable}; } Action Action::attach(Object governorObject, int governorIndex, Object dependentObject, int dependentIndex) { auto apply = [governorObject, governorIndex, dependentObject, dependentIndex](Config & config, Action & a) { int lineIndex = 0; if (governorObject == Object::Buffer) lineIndex = config.getWordIndex() + governorIndex; else lineIndex = config.getStack(governorIndex); addHypothesisRelative(Config::headColName, dependentObject, dependentIndex, std::to_string(lineIndex)).apply(config, a); }; auto undo = [governorObject, governorIndex, dependentObject, dependentIndex](Config & config, Action & a) { addHypothesisRelative(Config::headColName, dependentObject, dependentIndex, "").undo(config, a); }; auto appliable = [governorObject, governorIndex, dependentObject, dependentIndex](const Config & config, const Action & action) { int govLineIndex = 0; if (governorObject == Object::Buffer) { govLineIndex = config.getWordIndex() + governorIndex; if (!config.has(0, govLineIndex, 0)) return false; } else { if (!config.hasStack(governorIndex)) return false; govLineIndex = config.getStack(governorIndex); } int depLineIndex = 0; if (dependentObject == Object::Buffer) { depLineIndex = config.getWordIndex() + dependentIndex; if (!config.has(0, depLineIndex, 0)) return false; } else { if (!config.hasStack(dependentIndex)) return false; depLineIndex = config.getStack(dependentIndex); } // Check for cycles while (govLineIndex != depLineIndex) { try { govLineIndex = std::stoi(config.getLastNotEmptyHypConst(Config::headColName, govLineIndex)); } catch(std::exception &) {return true;} } return false; }; return {Type::Write, apply, undo, appliable}; } Action::Object Action::str2object(const std::string & s) { if (s == "b") return Object::Buffer; if (s == "s") return Object::Stack; util::myThrow(fmt::format("Invalid object '{}'", s)); return Object::Buffer; }