diff --git a/src/backend/legacy-parser.h b/src/backend/legacy-parser.h
index e1dbbb2b..78efd0b6 100644
--- a/src/backend/legacy-parser.h
+++ b/src/backend/legacy-parser.h
@@ -1,25 +1,25 @@
/* BOSS
-
- A "one-click" program for users that quickly optimises and avoids
- detrimental conflicts in their TES IV: Oblivion, Nehrim - At Fate's Edge,
+
+ A "one-click" program for users that quickly optimises and avoids
+ detrimental conflicts in their TES IV: Oblivion, Nehrim - At Fate's Edge,
TES V: Skyrim, Fallout 3 and Fallout: New Vegas mod load orders.
Copyright (C) 2009-2012 BOSS Development Team.
This file is part of BOSS.
- BOSS is free software: you can redistribute
- it and/or modify it under the terms of the GNU General Public License
- as published by the Free Software Foundation, either version 3 of
+ BOSS is free software: you can redistribute
+ it and/or modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
- BOSS is distributed in the hope that it will
+ BOSS is distributed in the hope that it will
be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with BOSS. If not, see
+ along with BOSS. If not, see
.
$Revision: 2188 $, $Date: 2011-01-20 10:05:16 +0000 (Thu, 20 Jan 2011) $
@@ -29,7 +29,7 @@
#define __BOSS_GRAMMAR_H__
#ifndef BOOST_SPIRIT_UNICODE
-#define BOOST_SPIRIT_UNICODE
+#define BOOST_SPIRIT_UNICODE
#endif
#include "metadata.h"
@@ -52,49 +52,20 @@
#include
namespace boss {
- namespace fs = boost::filesystem;
namespace qi = boost::spirit::qi;
namespace unicode = boost::spirit::unicode;
namespace phoenix = boost::phoenix;
- using namespace qi::labels;
- using boost::algorithm::to_lower_copy;
-
- using qi::skip;
- using qi::eol;
- using qi::eoi;
- using qi::lexeme;
- using qi::on_error;
- using qi::fail;
- using qi::lit;
- using qi::omit;
- using qi::eps;
- using qi::hex;
- using qi::bool_;
- using qi::uint_;
-
- using unicode::char_;
- using unicode::no_case;
- using unicode::space;
- using unicode::xdigit;
-
- using namespace std;
-
- using qi::grammar;
- using boost::spirit::info;
-
- //typedef boost::u8_to_u32_iterator iterator_type;
- //typedef iterator_type grammarIter;
- typedef string::const_iterator grammarIter;
///////////////////////////////
// Keyword structures
///////////////////////////////
-
+
struct masterlistMsgKey_ : qi::symbols {
- masterlistMsgKey_::masterlistMsgKey_() {
+ masterlistMsgKey_() {
add //New Message keywords.
("say",g_message_say)
+ ("tag",g_message_tag)
("req",g_message_say)
("inc", g_message_say)
("dirty",g_message_warn)
@@ -105,46 +76,45 @@ namespace boss {
};
-
///////////////////////////////
//Skipper Grammar
///////////////////////////////
-
- //Skipper for userlist, modlist and ini parsers.
-
- class Skipper : public grammar {
- public:
- Skipper::Skipper() : Skipper::base_type(start, "skipper grammar") {
- start =
+ //Skipper for userlist, modlist and ini parsers.
+ template
+ class Skipper : public qi::grammar {
+ public:
+ Skipper() : Skipper::base_type(start, "skipper grammar") {
+
+ start =
spc
| UTF8
| CComment
| CPlusPlusComment
| iniComment
| eof;
-
- spc = space - eol;
- UTF8 = char_("\xef") >> char_("\xbb") >> char_("\xbf"); //UTF8 BOM
+ spc = unicode::space - qi::eol;
- CComment = "/*" >> *(char_ - "*/") >> "*/";
+ UTF8 = unicode::char_("\xef") >> unicode::char_("\xbb") >> unicode::char_("\xbf"); //UTF8 BOM
- iniComment = lit("#") >> *(char_ - eol);
+ CComment = "/*" >> *(unicode::char_ - "*/") >> "*/";
- CPlusPlusComment = !(lit("http:") | lit("https:") | lit("file:")) >> "//" >> *(char_ - eol);
+ iniComment = qi::lit("#") >> *(unicode::char_ - qi::eol);
- eof = *(spc | CComment | CPlusPlusComment | eol) >> eoi;
+ CPlusPlusComment = !(qi::lit("http:") | qi::lit("https:") | qi::lit("file:")) >> "//" >> *(unicode::char_ - qi::eol);
+
+ eof = *(spc | CComment | CPlusPlusComment | qi::eol) >> qi::eoi;
}
-
- void Skipper::SkipIniComments(const bool b) {
+
+ void SkipIniComments(const bool b) {
if (b)
- iniComment = (lit("#") | lit(";")) >> *(char_ - eol);
+ iniComment = (qi::lit("#") | qi::lit(";")) >> *(unicode::char_ - qi::eol);
else
iniComment = CComment;
}
private:
- qi::rule start, spc, eof, CComment, CPlusPlusComment, lineComment, iniComment, UTF8;
+ qi::rule start, spc, eof, CComment, CPlusPlusComment, lineComment, iniComment, UTF8;
};
///////////////////////////////
@@ -152,35 +122,27 @@ namespace boss {
///////////////////////////////
//Modlist/Masterlist grammar.
-
- class modlist_grammar : public grammar(), Skipper> {
+ template
+ class modlist_grammar : public qi::grammar(), Skipper > {
public:
- modlist_grammar::modlist_grammar()
+ modlist_grammar()
: modlist_grammar::base_type(modList, "modlist grammar") {
masterlistMsgKey_ masterlistMsgKey;
- const list noMessages; //An empty set of messages.
+ const std::list noMessages; //An empty set of messages.
- modList =
- *eol
+ modList %=
+ *qi::eol
>
(
- listVar
+ listVar
| globalMessage
| groupLine
- | listPlugin [phoenix::bind(&modlist_grammar::StoreItem, this, _val, _1)]
- ) % +eol;
-
- groupLine =
- conditionals
- > (
- no_case[lit("begingroup:")]
- | no_case[lit("endgroup")]
- )
- > charString;
+ | listPlugin
+ ) % +qi::eol;
listVar =
conditionals
- >> no_case[lit("set")]
+ >> unicode::no_case[qi::lit("set")]
>> (
':'
> charString
@@ -188,95 +150,91 @@ namespace boss {
globalMessage =
conditionals
- >> no_case[lit("global")]
+ >> unicode::no_case[qi::lit("global")]
>> (
messageKeyword
>> ':'
>> charString
);
- listPlugin =
- conditionals
- > no_case[lit("mod:") | lit("regex:")]
- > itemName
- > (
- itemMessages
- | bashTagMessage
- ) % +eol;
+ groupLine =
+ conditionals
+ > (
+ unicode::no_case[qi::lit("begingroup:")]
+ | unicode::no_case[qi::lit("endgroup")]
+ )
+ > charString;
- itemName =
- charString [_val = boost::algorithm::trim_copy(_1)];
-
- bashTagMessage =
- (conditionals
- >> no_case[lit("tag")]
- >> ':'
- >> charString) [phoenix::bind(&modlist_grammar::ExtractTags, this, _val, _1, _2)]
+ listPlugin =
+ conditionals
+ > unicode::no_case[qi::lit("mod:") | qi::lit("regex:")]
+ > (charString
+ > pluginMessages) [phoenix::bind(&modlist_grammar::MakePlugin, this, qi::_val, qi::_1, qi::_2)]
;
- itemMessages %=
+ pluginMessages %=
(
- +eol
- >> itemMessage % +eol
- ) | eps [_1 = noMessages];
+ +qi::eol
+ >> pluginMessage % +qi::eol
+ ) | qi::eps [qi::_1 = noMessages];
- itemMessage %=
- conditionals
- >> messageKeyword
+ pluginMessage =
+ (conditionals
+ >> messageKeyword
>> ':'
- >> charString //The double >> matters. A single > doesn't work.
+ >> charString) [phoenix::bind(&modlist_grammar::MakeMessage, this, qi::_val, qi::_1, qi::_2, qi::_3)] //The double >> matters. A single > doesn't work.
;
- charString %= lexeme[+(char_ - eol)]; //String, with no skipper.
+ charString %= qi::lexeme[+(unicode::char_ - qi::eol)]; //String, with no skipper.
- messageKeyword %= no_case[masterlistMsgKey];
+ messageKeyword %= unicode::no_case[masterlistMsgKey];
- conditionals =
+ conditionals =
(
- conditional [_val = _1]
- > *((andOr > conditional) [_val += _1 + _2])
+ conditional [qi::_val = qi::_1]
+ > *((andOr > conditional) [qi::_val += qi::_1 + qi::_2])
)
- | no_case[unicode::string("else")][_val = _1]
- | eps [_val = ""];
+ | unicode::no_case[unicode::string("else")][qi::_val = qi::_1]
+ | qi::eps [qi::_val = ""];
- andOr %=
- unicode::string("&&")
+ andOr %=
+ unicode::string("&&")
| unicode::string("||");
- conditional %=
+ conditional %=
(
- no_case[unicode::string("ifnot")
+ unicode::no_case[unicode::string("ifnot")
| unicode::string("if")]
- )
+ )
> functCondition;
-
+
functCondition %=
(
- no_case[unicode::string("var")] > char_('(') > variable > char_(')') //Variable condition.
+ unicode::no_case[unicode::string("var")] > unicode::char_('(') > variable > unicode::char_(')') //Variable condition.
) | (
- no_case[unicode::string("file")] > char_('(') > file > char_(')') //File condition.
+ unicode::no_case[unicode::string("file")] > unicode::char_('(') > file > unicode::char_(')') //File condition.
) | (
- no_case[unicode::string("checksum")] > char_('(') > file > char_(',') > checksum > char_(')') //Checksum condition.
+ unicode::no_case[unicode::string("checksum")] > unicode::char_('(') > file > unicode::char_(',') > checksum > unicode::char_(')') //Checksum condition.
) | (
- no_case[unicode::string("version")] > char_('(') > file > char_(',') > version > char_(',') > comparator > char_(')') //Version condition.
+ unicode::no_case[unicode::string("version")] > unicode::char_('(') > file > unicode::char_(',') > version > unicode::char_(',') > comparator > unicode::char_(')') //Version condition.
) | (
- no_case[unicode::string("regex")] > char_('(') > regex > char_(')') //Regex condition.
+ unicode::no_case[unicode::string("regex")] > unicode::char_('(') > regex > unicode::char_(')') //Regex condition.
) | (
- no_case[unicode::string("active")] > char_('(') > file > char_(')') //Active condition.
+ unicode::no_case[unicode::string("active")] > unicode::char_('(') > file > unicode::char_(')') //Active condition.
) | (
- no_case[unicode::string("lang")] > char_('(') > language > char_(')') //Language condition.
+ unicode::no_case[unicode::string("lang")] > unicode::char_('(') > language > unicode::char_(')') //Language condition.
)
;
- variable %= +(char_ - (')' | eol));
+ variable %= +(unicode::char_ - (')' | qi::eol));
- file %= lexeme[char_('"') > +(char_ - ('"' | eol)) > char_('"')];
+ file %= qi::lexeme[unicode::char_('"') > +(unicode::char_ - ('"' | qi::eol)) > unicode::char_('"')];
- checksum %= +xdigit;
+ checksum %= +unicode::xdigit;
version %= file;
- comparator %= char_('=') | char_('>') | char_('<');
+ comparator %= unicode::char_('=') | unicode::char_('>') | unicode::char_('<');
regex %= file;
@@ -285,11 +243,9 @@ namespace boss {
modList.name("modList");
listVar.name("listVar");
- listItem.name("listItem");
- ItemType.name("ItemType");
- itemName.name("itemName");
- itemMessages.name("itemMessages");
- itemMessage.name("itemMessage");
+ listPlugin.name("listPlugin");
+ pluginMessages.name("pluginMessages");
+ pluginMessage.name("pluginMessage");
charString.name("charString");
messageKeyword.name("messageKeyword");
conditionals.name("conditional");
@@ -304,52 +260,114 @@ namespace boss {
comparator.name("comparator");
regex.name("regex file");
file.name("language");
-
- on_error(modList, phoenix::bind(&modlist_grammar::SyntaxError, this, _1, _2, _3, _4));
- on_error(listVar, phoenix::bind(&modlist_grammar::SyntaxError, this, _1, _2, _3, _4));
- on_error(listItem, phoenix::bind(&modlist_grammar::SyntaxError, this, _1, _2, _3, _4));
- on_error(ItemType, phoenix::bind(&modlist_grammar::SyntaxError, this, _1, _2, _3, _4));
- on_error(itemName, phoenix::bind(&modlist_grammar::SyntaxError, this, _1, _2, _3, _4));
- on_error(itemMessages, phoenix::bind(&modlist_grammar::SyntaxError, this, _1, _2, _3, _4));
- on_error(itemMessage, phoenix::bind(&modlist_grammar::SyntaxError, this, _1, _2, _3, _4));
- on_error(charString, phoenix::bind(&modlist_grammar::SyntaxError, this, _1, _2, _3, _4));
- on_error(messageKeyword, phoenix::bind(&modlist_grammar::SyntaxError, this, _1, _2, _3, _4));
- on_error(conditionals, phoenix::bind(&modlist_grammar::SyntaxError, this, _1, _2, _3, _4));
- on_error(andOr, phoenix::bind(&modlist_grammar::SyntaxError, this, _1, _2, _3, _4));
- on_error(conditional, phoenix::bind(&modlist_grammar::SyntaxError, this, _1, _2, _3, _4));
- on_error(functCondition, phoenix::bind(&modlist_grammar::SyntaxError, this, _1, _2, _3, _4));
- on_error(shortCondition, phoenix::bind(&modlist_grammar::SyntaxError, this, _1, _2, _3, _4));
- on_error(variable, phoenix::bind(&modlist_grammar::SyntaxError, this, _1, _2, _3, _4));
- on_error(file, phoenix::bind(&modlist_grammar::SyntaxError, this, _1, _2, _3, _4));
- on_error(checksum, phoenix::bind(&modlist_grammar::SyntaxError, this, _1, _2, _3, _4));
- on_error(version, phoenix::bind(&modlist_grammar::SyntaxError, this, _1, _2, _3, _4));
- on_error(comparator, phoenix::bind(&modlist_grammar::SyntaxError, this, _1, _2, _3, _4));
- on_error(regex, phoenix::bind(&modlist_grammar::SyntaxError, this, _1, _2, _3, _4));
- on_error(language, phoenix::bind(&modlist_grammar::SyntaxError, this, _1, _2, _3, _4));
+
+ qi::on_error(modList, phoenix::bind(&modlist_grammar::SyntaxError, this, qi::_1, qi::_2, qi::_3, qi::_4));
+ qi::on_error(listVar, phoenix::bind(&modlist_grammar::SyntaxError, this, qi::_1, qi::_2, qi::_3, qi::_4));
+ qi::on_error(listPlugin, phoenix::bind(&modlist_grammar::SyntaxError, this, qi::_1, qi::_2, qi::_3, qi::_4));
+ qi::on_error(pluginMessages, phoenix::bind(&modlist_grammar::SyntaxError, this, qi::_1, qi::_2, qi::_3, qi::_4));
+ qi::on_error(pluginMessage, phoenix::bind(&modlist_grammar::SyntaxError, this, qi::_1, qi::_2, qi::_3, qi::_4));
+ qi::on_error(charString, phoenix::bind(&modlist_grammar::SyntaxError, this, qi::_1, qi::_2, qi::_3, qi::_4));
+ qi::on_error(messageKeyword, phoenix::bind(&modlist_grammar::SyntaxError, this, qi::_1, qi::_2, qi::_3, qi::_4));
+ qi::on_error(conditionals, phoenix::bind(&modlist_grammar::SyntaxError, this, qi::_1, qi::_2, qi::_3, qi::_4));
+ qi::on_error(andOr, phoenix::bind(&modlist_grammar::SyntaxError, this, qi::_1, qi::_2, qi::_3, qi::_4));
+ qi::on_error(conditional, phoenix::bind(&modlist_grammar::SyntaxError, this, qi::_1, qi::_2, qi::_3, qi::_4));
+ qi::on_error(functCondition, phoenix::bind(&modlist_grammar::SyntaxError, this, qi::_1, qi::_2, qi::_3, qi::_4));
+ qi::on_error(shortCondition, phoenix::bind(&modlist_grammar::SyntaxError, this, qi::_1, qi::_2, qi::_3, qi::_4));
+ qi::on_error(variable, phoenix::bind(&modlist_grammar::SyntaxError, this, qi::_1, qi::_2, qi::_3, qi::_4));
+ qi::on_error(file, phoenix::bind(&modlist_grammar::SyntaxError, this, qi::_1, qi::_2, qi::_3, qi::_4));
+ qi::on_error(checksum, phoenix::bind(&modlist_grammar::SyntaxError, this, qi::_1, qi::_2, qi::_3, qi::_4));
+ qi::on_error(version, phoenix::bind(&modlist_grammar::SyntaxError, this, qi::_1, qi::_2, qi::_3, qi::_4));
+ qi::on_error(comparator, phoenix::bind(&modlist_grammar::SyntaxError, this, qi::_1, qi::_2, qi::_3, qi::_4));
+ qi::on_error(regex, phoenix::bind(&modlist_grammar::SyntaxError, this, qi::_1, qi::_2, qi::_3, qi::_4));
+ qi::on_error(language, phoenix::bind(&modlist_grammar::SyntaxError, this, qi::_1, qi::_2, qi::_3, qi::_4));
}
private:
- qi::rule(), Skipper> modList;
- qi::rule listItem;
- qi::rule ItemType;
- qi::rule itemName;
- qi::rule(), Skipper> itemMessages;
- qi::rule itemMessage, globalMessage;
- qi::rule listVar;
- qi::rule charString, andOr, conditional, conditionals, functCondition, shortCondition, variable, file, checksum, version, comparator, regex, language;
- qi::rule messageKeyword;
-
+ qi::rule(), Skipper > modList;
+ qi::rule > listVar, globalMessage, groupLine;
+ qi::rule > listPlugin;
+ qi::rule(), Skipper > pluginMessages;
+ qi::rule > pluginMessage;
+ qi::rule > messageKeyword;
+ qi::rule > charString, andOr, conditional, conditionals, functCondition, shortCondition, variable, file, checksum, version, comparator, regex, language;
+
+ void MakePlugin(Plugin& plugin, std::string& name, std::list& messages) {
+ plugin.Name(boost::algorithm::trim_copy(name));
+
+ //Need to remove any Bash Tag suggestion messages and apply them properly.
+ std::set tags;
+ std::list::iterator it = messages.begin();
+ while (it != messages.end()) {
+ if (it->Type() == g_message_tag) {
+ std::string message = it->ChooseContent(g_lang_any).Str();
+
+ std::string addedList, removedList;
+ size_t pos1, pos2 = std::string::npos;
+
+ pos1 = message.find("{{BASH:");
+ if (pos1 != std::string::npos)
+ pos2 = message.find("}}", pos1);
+ if (pos2 != std::string::npos)
+ addedList = message.substr(pos1 + 7, pos2 - pos1 - 7);
+
+ pos1 = message.find("[");
+ pos2 = std::string::npos;
+ if (pos1 != std::string::npos)
+ pos2 = message.find("]", pos1);
+ if (pos2 != std::string::npos)
+ removedList = message.substr(pos1+1,pos2-pos1-1);
+
+ if (!addedList.empty()) {
+ //Now we move through the list of added Tags.
+ //Search the set of already added Tags for each one and only add if it's not found.
+ std::string name;
+ pos1 = 0;
+ pos2 = addedList.find(",", pos1);
+ while (pos2 != std::string::npos) {
+ name = boost::algorithm::trim_copy(addedList.substr(pos1,pos2-pos1));
+ if (tags.find(Tag(name)) == tags.end())
+ tags.insert(Tag(name));
+ pos1 = pos2+1;
+ pos2 = addedList.find(",", pos1);
+ }
+ name = boost::algorithm::trim_copy(addedList.substr(pos1));
+ if (tags.find(Tag(name)) == tags.end())
+ tags.insert(Tag(name));
+ }
+
+ if (!removedList.empty()) {
+ std::string name;
+ pos1 = 0;
+ pos2 = removedList.find(",", pos1);
+ while (pos2 != std::string::npos) {
+ name = boost::algorithm::trim_copy(removedList.substr(pos1,pos2-pos1));
+ if (tags.find(Tag(name, false)) == tags.end())
+ tags.insert(Tag(name, false));
+ pos1 = pos2+1;
+ pos2 = removedList.find(",", pos1);
+ }
+ name = boost::algorithm::trim_copy(removedList.substr(pos1));
+ if (tags.find(Tag(name, false)) == tags.end())
+ tags.insert(Tag(name, false));
+ }
+
+ it = messages.erase(it);
+ } else
+ ++it;
+ }
+
+ plugin.Messages(messages);
+ }
+
+ void MakeMessage(Message& message, std::string& condition, unsigned int type, std::string& content) {
+ message = Message(type, content, condition);
+ }
+
//Parser error reporter.
- void modlist_grammar::SyntaxError(grammarIter const& /*first*/, grammarIter const& last, grammarIter const& errorpos, boost::spirit::info const& what) {
+ void SyntaxError(Iter const& /*first*/, Iter const& last, Iter const& errorpos, boost::spirit::info const& what) {
std::string context(errorpos, min(errorpos +50, last));
boost::trim(context);
-
- throw boss::error(boss::error::condition_eval_fail, "Error parsing condition at \"" + context + "\", expected \"" + what.tag + "\"");
- }
- //Stores the given item and records any changes to open groups.
- void modlist_grammar::StoreItem(vector- & list, Item currentItem) {
- if (!currentItem.Name().empty()) //A blank line can be confused with a mod entry.
- list.push_back(currentItem);
+ throw boss::error(boss::error::condition_eval_fail, "Error parsing condition at \"" + context + "\", expected \"" + what.tag + "\"");
}
};
}