From 3a3f93412a89d3594a89e70811b8809a3fb661f3 Mon Sep 17 00:00:00 2001 From: AbdielKavash <19243993+AbdielKavash@users.noreply.github.com> Date: Tue, 12 Mar 2019 21:26:23 -0600 Subject: [PATCH] First somewhat working version! --- highlighting.xml | 64 +++++ makefile | 39 +-- src/Compiler.cpp | 562 +++++++---------------------------------- src/Compiler.h | 39 +-- src/Context.cpp | 90 ++++--- src/Context.h | 13 +- src/Lexer.l | 121 ++++----- src/Parser.y | 457 +++++++++++++-------------------- src/ParserWrapper.cpp | 10 +- src/RuleNative.cpp | 301 ++++++++++++++++++++++ src/RuleNative.h | 46 ++++ src/RuleOperations.cpp | 14 +- src/RuleOperations.h | 4 +- src/Types.cpp | 244 ++++++++++-------- src/Types.h | 302 ++++++++++++---------- src/ifpp.cpp | 140 ++++++---- 16 files changed, 1226 insertions(+), 1220 deletions(-) create mode 100644 highlighting.xml create mode 100644 src/RuleNative.cpp create mode 100644 src/RuleNative.h diff --git a/highlighting.xml b/highlighting.xml new file mode 100644 index 0000000..dcd6adc --- /dev/null +++ b/highlighting.xml @@ -0,0 +1,64 @@ + + + + + + + + 00# 00// 01 02 03/* 04*/ + + x + a b c d e f A B C D E F + + + + + < <= = => > .. : + + { + + } + + + + + + + ItemLevel DropLevel Quality Sockets LinkedSockets Height Width StackSize GemLevel MapTier Rarity Class BaseType Prophecy HasExplicitMod HasEnchantment Identified Corrupted ElderItem ShaperItem ShapedMap FracturedItem SynthesisedItem AnyEnchantment SocketGroup SetFontSize SetTextSize SetBorderColor SetTextColor SetFontColor SetBackgroundColor PlayAlertSound PlayAlertSoundPositional CustomAlertSound MinimapIcon PlayEffect DisableDropSound + true false Normal Magic Rare Unique ShAlchemy ShBlessed ShChaos ShDivine ShExalted ShFusing ShGeneral ShMirror ShRegal ShVaal Blue Brown Green Red White Yellow Circle Diamond Hexagon Square Star Triangle Temp + Define Version Rule Modifier Default DefaultIgnore Hide Hidden UseStyle Number Color File List Style + Override + $ + + + + 00" 01 02" 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/makefile b/makefile index 6c0cb6e..3c93bb2 100644 --- a/makefile +++ b/makefile @@ -1,14 +1,8 @@ GenDir = gen SrcDir = src -TestDir = tests GenClass = Lexer Parser -SrcClass = Types Logger Context ParserWrapper -#ifppDriver ifppRuleOperations ifppCompiler - -TestParse = basicSyntax -TestCompile = overrideWithoutChange overrideSameAction overrideNumeric nameLists incremental -AllTests = $(TestParse) $(TestCompile) +SrcClass = Types Logger Context RuleNative Compiler GenObj = $(addprefix $(GenDir)/,$(addsuffix .o,$(GenClass))) SrcObj = $(addprefix $(GenDir)/,$(addsuffix .o,$(SrcClass))) @@ -16,8 +10,8 @@ AllObjs = $(GenObj) $(SrcObj) Flex = flex -i Bison = bison -Gcc = g++ -Wall -Wextra -pedantic -I src -I gen -GccStrict = g++ -Wall -Wextra -pedantic -Weffc++ -Werror -I src -I gen +Gcc = g++ -Wall -Wextra -pedantic -Wno-unused-function -Wfatal-errors -I src -I gen +GccStrict = g++ -Wall -Wextra -pedantic -Weffc++ -Werror -Wfatal-errors -I src -I gen .PHONY: lex parser analyze tests doc clean @@ -26,20 +20,16 @@ ifpp: $(AllObjs) src/ifpp.cpp lex: $(GenDir)/Lexer.cpp $(GenDir)/Lexer.h -parser: $(GenDir)/Parser.cpp $(GenDir)/Parser.h +parser: $(GenDir)/Parser.cpp $(GenDir)/Parser.h $(GenDir)/location.hh $(GenDir)/position.hh analyze: src/Parser.y $(Bison) -Wall --report=all --report-file=BisonReport.txt $< -tests: $(AllTests) - doc: doc/ifpp-manual.html clean: -rm gen/* - -rm tests/*.parsed.ifpp - -rm tests/*.partial.ifpp - -rm tests/*.filter + -rm doc/* -rm ifpp.exe @@ -47,28 +37,15 @@ clean: $(GenDir)/Lexer.cpp $(GenDir)/Lexer.h: $(SrcDir)/Lexer.l $(SrcDir)/Types.h $(GenDir)/Parser.h $(Flex) --outfile="gen/Lexer.cpp" --header-file="gen/Lexer.h" $(SrcDir)/Lexer.l -$(GenDir)/Parser.cpp $(GenDir)/Parser.h $(GenDir)/location.hh: $(SrcDir)/Parser.y $(SrcDir)/Types.h $(SrcDir)/Context.h +$(GenDir)/Parser.cpp $(GenDir)/Parser.h $(GenDir)/location.hh $(GenDir)/position.hh: $(SrcDir)/Parser.y $(SrcDir)/Types.h $(SrcDir)/Context.h $(Bison) --output="gen/Parser.cpp" --defines="gen/Parser.h" $(SrcDir)/Parser.y -$(GenObj): $(GenDir)/%.o: $(GenDir)/%.cpp $(SrcDir)/Types.h $(SrcDir)/Logger.h +$(GenObj): $(GenDir)/%.o: $(GenDir)/%.cpp $(GenDir)/%.h $(Gcc) -c -o $@ $< -$(SrcObj): $(GenDir)/%.o: $(SrcDir)/%.cpp $(SrcDir)/%.h $(SrcDir)/Types.h +$(SrcObj): $(GenDir)/%.o: $(SrcDir)/%.cpp $(SrcDir)/%.h $(GccStrict) -c -o $@ $< -$(GenDir)/Context.o: $(SrcDir)/Logger.h - - - -$(TestParse): %: $(TestDir)/%.parsed.ifpp - -$(TestCompile): %: $(TestDir)/%.filter - -$(TestDir)/%.parsed.ifpp: $(TestDir)/%.ifpp ifpp - ./ifpp -c -Dpartial -DparseOnly $< - -$(TestDir)/%.filter: $(TestDir)/%.ifpp ifpp - ./ifpp -c -Dpartial $< doc/ifpp-manual.html: src/ifpp-manual.texinfo makeinfo --html --no-split --css-include="src/ifpp-manual.css" -o "doc/ifpp-manual.html" "src/ifpp-manual.texinfo" diff --git a/src/Compiler.cpp b/src/Compiler.cpp index dd4e20c..38d56e3 100644 --- a/src/Compiler.cpp +++ b/src/Compiler.cpp @@ -1,416 +1,118 @@ #include "Compiler.h" -#include "RuleOperations.h" -#include -#include -#include -#include namespace ifpp { - -std::ostream & RuleNative::printSelf(std::ostream & os, PrintStyle ps) const { - switch (ps) { - case PRINT_NATIVE: { - if (useless) { - throw InternalError("Writing a useless rule to native filter!", __FILE__, __LINE__); - } - - auto it = actions.find("Hidden"); - if (it != actions.end() && static_cast(it->second)->par1) { - os << "Show" << std::endl; - } else { - os << "Hide" << std::endl; - } - break; - } - case PRINT_IFPP: - os << '[' << guid << "] Rule "; - if (useless) os << "USELESS "; - print(os, ps, modifiers) << '{' << std::endl; - break; - - default: - throw UnhandledCase("Print style", __FILE__, __LINE__); - } - - ++IFPP_TABS; - for (const auto & c : conditions) { - print(os, ps, c.second); - } - for (const auto & a : actions) { - print(os, ps, a.second); - } - --IFPP_TABS; - - if (ps == PRINT_IFPP) { - os << '}' << std::endl; - } - return os; -} - -bool RuleNative::hasMod(ModifierList ml) const { - return modifiers & ml; -} - -RuleNative::~RuleNative() { - for (auto & c : conditions) delete c.second; - for (auto & a : actions) delete a.second; -} - -/* -Always adds a clone of c, if necessary. -*/ -void RuleNative::addCondition(const Condition * c) { - auto cOld = conditions.find(c->what); - if (cOld == conditions.end()) { - // We do not have this type of condition yet. - // Check if the condition matches anything. - // We still add it though. - switch (c->conType) { - case CON_INTERVAL: { - auto ci = static_cast(c); - if (ci->from > ci->to) useless = true; - break; - } - case CON_SOCKETGROUP: { - auto csg = static_cast(c); - if (csg->socketGroup.r + csg->socketGroup.g + csg->socketGroup.b + csg->socketGroup.w > getLimit("LinkedSockets", MAX)) { - useless = true; - } - break; - } - case CON_BOOL: // Always matches something. + +static RuleNative * ModifyRule(RuleNative * ruleOld, const Modifier * mod) { + RuleNative * ruleNew = ruleOld->clone(); + for (const auto c : mod->commands) { + switch (c->comType) { + case COM_CONDITION: + ruleNew->addCondition(static_cast(c)); break; - case CON_NAMELIST: // We can not determine what this matches. + + case COM_ACTION: + ruleNew->addAction(static_cast(c)); break; + + case COM_RULE: + throw InternalError("Rules inside modifiers are not implemented yet.", __FILE__, __LINE__); + + case COM_MODIFIER: + throw InternalError("Nested modifiers are not implemented yet.", __FILE__, __LINE__); + + case COM_DEFAULT: + case COM_IGNORE: + throw InternalError("Deafult styles inside modifiers are not implemented yet.", __FILE__, __LINE__); + default: - throw UnhandledCase("Condition type", __FILE__, __LINE__); + throw UnhandledCase("Command type", __FILE__, __LINE__); } - // Add the condition and take ownership. - conditions.insert(std::make_pair(c->what, static_cast(c->clone()))); - return; - } - - // A condition of this type already exists. Intersect with it. - switch (c->conType) { - case CON_INTERVAL: { - // Refine the interval in existing condition. - auto c1 = static_cast(cOld->second); - auto c2 = static_cast(c); - - if (c1->from < c2->from) c1->from = c2->from; - if (c1->to > c2->to) c1->to = c2->to; - if (c1->from > c1->to) useless = true; - break; - } - case CON_BOOL: { - // A condition can not be true and false at the same time. - auto c1 = static_cast(cOld->second); - auto c2 = static_cast(c); - - if (c1->value != c2->value) useless = true; - break; - } - case CON_SOCKETGROUP: { - auto c1 = static_cast(c); - int r = c1->socketGroup.r; - int g = c1->socketGroup.g; - int b = c1->socketGroup.b; - int w = c1->socketGroup.w; - - bool add = true; - auto range = conditions.equal_range(c->what); - for (auto it = range.first; it != range.second; ) { - // Check if we need more than 6 different sockets. - auto c2 = static_cast(it->second); - if (c2->socketGroup.r > r) r = c2->socketGroup.r; - if (c2->socketGroup.g > g) g = c2->socketGroup.g; - if (c2->socketGroup.b > b) b = c2->socketGroup.b; - if (c2->socketGroup.w > w) w = c2->socketGroup.w; - - // If some existing condition is stricter than the new condition, we do not need to do anything. - if (ConditionSubset(c2, c1)) { - add = false; - break; - } - // If the new condition is stricter than some existing condition, we can remove the existing one. - if (ConditionSubset(c1, c2)) { - delete it->second; - it = conditions.erase(it); - } else { - ++it; - } - } - - // Check if we need more than 6 different sockets. - if (r + g + b + w > getLimit("SocketGroup", MAX)) useless = true; - - // Add the new condition. - if (add) { - conditions.insert(std::make_pair(c->what, static_cast(c->clone()))); - } - break; - } - case CON_NAMELIST: { - // There can be more than one of these conditions, in case of intersections. - auto c1 = static_cast(c); - - bool add = true; - auto range = conditions.equal_range(c->what); - for (auto it = range.first; it != range.second; ) { - auto c2 = static_cast(it->second); - - // If some existing condition is stricter than the new condition, we do not need to do anything. - if (ConditionSubset(c2, c1)) { - add = false; - break; - } - // If the new condition is stricter than some existing condition, we can remove the existing one. - if (ConditionSubset(c1, c2)) { - delete it->second; - it = conditions.erase(it); - } else { - ++it; - } - } - - // Add the new condition. - if (add) { - conditions.insert(std::make_pair(c->what, static_cast(c->clone()))); - } - break; - } - default: - throw UnhandledCase("Condition type", __FILE__, __LINE__); + if (ruleNew->useless) break; } + return ruleNew; } - + /* -Adds a clone of the action. -Does NOT take modifiers into account. This should be handled outside. - -TODO: possibly only change the parameters of an action when replacing it? +Modifies the entire input filter by adding the given modifier to all the rules. */ -void RuleNative::addAction(const Action * a) { - // Possibly override other actions of the same type. - auto it = actions.find(a->what); - if (it != actions.end()) { - delete it->second; - it->second = static_cast(a->clone()); - } else { - actions.insert(std::make_pair(a->what, static_cast(a->clone()))); - } -} - -RuleNative * RuleNative::clone() const { - // We do not need to do checking, just copy the conditions and actions. - // Beware, this might break if addCondition or addAction start to do other things. - RuleNative * r = new RuleNative(modifiers); - r->useless = useless; - - for (const auto & c : conditions) { - r->conditions.insert(std::make_pair(c.first, static_cast(c.second->clone()))); - } - for (const auto & a : actions) { - r->actions.insert(std::make_pair(a.first, static_cast(a.second->clone()))); +static void AddModifier(FilterNative & filter, const Modifier * mod) { + FilterNative tempFilter; + tempFilter.swap(filter); + + // Modify each rule by the modifier. + for (auto ruleOld : tempFilter) { + RuleNative * ruleNew = ModifyRule(ruleOld, mod); + + if (!ruleNew->useless) filter.push_back(ruleNew); + else delete ruleNew; + + if (!ruleOld->useless) filter.push_back(ruleOld); + else delete ruleOld; } - - return r; } -std::ostream & print(std::ostream & os, PrintStyle ps, const FilterNative & f) { - for (size_t i = 0; i < f.size(); ++i) { - if (i) os << std::endl; - print(os, ps, f[i]); - } - return os; +/* +Appends all rules from the second filter to the first. Clears the second filter. +Currently there is no optimization being done between different sections - this could be added? +*/ +void AppendFilter(FilterNative & first, FilterNative & second) { + first.reserve(first.size() + second.size()); + first.insert(first.end(), second.begin(), second.end()); } /* -Adds an IFPP rule (with potential sub-rules) to a native filter. -This rule will be *intersected* with all rules already in the filter, -so we do not want to do this with top-level rules! +Compiles a single top-level IFPP rule and appends the native rules to a filter. */ -static void AddRuleRecursive(FilterNative & filter, RuleNative * base, RuleIFPP * rule) { - for (const auto & c : rule->commands) { +void CompileRule(const RuleIFPP * rule, FilterNative & outFilter, const RuleNative * baseRule) { + FilterNative tempFilter; + RuleNative * base = baseRule->clone(); + + for (const auto c : rule->commands) { switch (c->comType) { case COM_CONDITION: - base.addCondition(static_cast(c)); + base->addCondition(static_cast(c)); break; + case COM_ACTION: - base.addAction(static_cast(c)); + base->addAction(static_cast(c)); break; - case COM_RULE: { - RuleNative b = base->clone(); - AddRuleRecursive(filter, b, static_cast(c)); - delete b; + + case COM_RULE: + CompileRule(static_cast(c), tempFilter, base); + break; + + case COM_MODIFIER: { + AddModifier(tempFilter, static_cast(c)); + RuleNative * ruleNew = ModifyRule(base, static_cast(c)); + if (!ruleNew->useless) tempFilter.push_back(ruleNew); + else delete ruleNew; break; + } + case COM_DEFAULT: // TODO: This should only do stuff for non-intersecting conditions. - for (const auto & a : static_cast(c)->style) { - base.addAction(a); + for (const auto a : static_cast(c)->style) { + base->addAction(a); } break; + + case COM_IGNORE: + // Somewhat hacky but w/e. + base->useless = true; + break; + default: throw UnhandledCase("Command type", __FILE__, __LINE__); } } - // TODO: WE ARE HERE. ADD THE RULE AND INTERSECT WITH ALL ACTIONS. -} - -static void AddRule(FilterNative & filter, RuleIFPP * rule) { - FilterNative temp; - RuleNative r = new RuleNative(rule->modifiers); - AddRuleRecursive(temp, r, rule); - delete r; - filter.insert(filter.end(), temp.begin(), temp.end(); -} - - - - - -/* -Processes an incremental rule. -The rule first is modified by the rule second: - -Conditions of the same type are overwritten. -Conditions which are only in first are retained. -Conditions only in second are added. - -Actions - same. - -All conditions and actions in the second rule are deleted! -*/ -static void IncrementRule(RuleNative * first, RuleNative * second) { - for (const auto & c : second->conditions) { - // Delete all matching conditions from first. - // Caution, need to call destructors properly. - const auto & r = first->conditions.equal_range(c.first); - for (auto it = r.first; it != r.second; ++it) { - delete it->second; - } - first->conditions.erase(r.first, r.second); - } - // We deleted some conditions, the rule is possibly not useless anymore. - first->useless = false; - - // Add all conditions and actions. - for (const auto & c : second->conditions) { - // Any conditions of the same type have been deleted. - first->addCondition(c.second); - delete c.second; - } - for (const auto & a : second->actions) { - // This overrides any actions of the same type. - first->addAction(a.second); - delete a.second; - } - - // Clean up the second rule. - second->conditions.clear(); - second->actions.clear(); -} - -/* -Processes a rule from input to get it ready for compilation. -This: -- TODO: splits incremental rules. -- Organizes conditions and actions into maps. -- Resolves duplicate conditions and actions. --- Duplicate conditions are intersected. --- Only the latter of duplicate actions is kept. -*/ -void preprocessRule(const Rule * rule, std::vector & rules) { - // Preprocess the rule into native rules. - - // Compatibility with native rules. Do not process incremental rules at all. - if (rule->hasMod(MOD_SHOW) || rule->hasMod(MOD_HIDE)) { - RuleNative * rn = new RuleNative(MOD_APPEND & MOD_FINAL); - - for (const auto & com : rule->commands) { - switch (com->comType) { - case COM_CONDITION: - // This only removes conditions that are a subset/superset of one another. - // We don't do intersections here. - rn->addCondition(static_cast(com)); - break; - - case COM_ACTION: - rn->addAction(static_cast(com)); - break; - - default: - throw InternalError("Unknown command type!", __FILE__, __LINE__); - } - } - - // Add action Hidden to native Hide rules. - // This action should not be Override (?) - if (rule->hasMod(MOD_HIDE)) rn->addAction(new ActionBool(0, "Hidden", true)); - - if (!rn->useless) { - rules.push_back(rn); - } else { - delete rn; - } - } - else { - // IFPP rule, possibly incremental. - RuleNative * fullRule = new RuleNative(rule->modifiers); - RuleNative * currentRule = new RuleNative(rule->modifiers); - - // Whether the last thing we have read was an action. - bool lastAction = false; - - for (const auto & com : rule->commands) { - switch (com->comType) { - case COM_CONDITION: - if (lastAction) { - // We have a piece of an incremental rule. - IncrementRule(fullRule, currentRule); - if (!fullRule->useless) { - rules.push_back(fullRule->clone()); - } - } - currentRule->addCondition(static_cast(com)); - lastAction = false; - break; - - case COM_ACTION: - currentRule->addAction(static_cast(com)); - lastAction = true; - break; - - default: - throw InternalError("Unknown command type!", __FILE__, __LINE__); - } - } - - // Add the last incremental section. - IncrementRule(fullRule, currentRule); - if (!fullRule->useless) { - rules.push_back(fullRule); // We do not need one more copy. - } else { - delete fullRule; - } - delete currentRule; + if (!base->useless) { + tempFilter.push_back(base); + } else { + delete base; } -} - -/* -Appends all rules from the second filter to the first. Clears the second filter. -Currently there is no optimization being done between different sections - this could be added? -*/ -void AppendFilter(FilterNative & first, FilterNative & second) { - // first.reserve(first.size() + second.size()); // This is probably smart enough to not be needed? - first.insert(first.end(), second.begin(), second.end()); -} - -/* -Compiles a single IFPP rule and appends the native rules to a filter. -*/ -void Compiler::CompileRule(const RuleIFPP * rule, FilterNative & outFilter) { + + AppendFilter(outFilter, tempFilter); } /* @@ -418,8 +120,7 @@ Compiles an IFPP filter into a native filter. */ void Compiler::Compile(const FilterIFPP & inFilter, FilterNative & outFilter) { outFilter.clear(); - - FilterNative partFilter; + RuleNative * blank = new RuleNative(); for (auto statement : inFilter) { switch (statement->stmType) { @@ -431,107 +132,16 @@ void Compiler::Compile(const FilterIFPP & inFilter, FilterNative & outFilter) { // Version is handled in the parser. // TODO break; - case STM_RULE: { - CompileRule(static_cast(statement), outFilter); + case STM_RULE: + CompileRule(static_cast(statement), outFilter, blank); break; default: throw UnhandledCase("Statement type", __FILE__, __LINE__); - - /* - rules.clear(); - preprocessRule(, rules); - - if (rules.size() == 0) { - if (writePartial) { - partialStream << "######################" << std::endl; - partialStream << "Rule [" << section->guid << "] skipped, matches nothing" << std::endl; - print(partialStream, PRINT_IFPP, section); - partialStream << "###########" << std::endl << std::endl; - } - } - else { - if (writePartial) { - partialStream << "######################" << std::endl; - partialStream << "Adding rule [" << section->guid << ']' << std::endl; - print(partialStream, PRINT_IFPP, section); - - if (rules.size() > 1) { - partialStream << "###########" << std::endl; - partialStream << "# Incremental rule, preprocessed into:" << std::endl; - partialStream << "###########" << std::endl << std::endl; - print(partialStream, PRINT_IFPP, rules); - } - partialStream << "###########" << std::endl << std::endl; - } - - static FilterNative temp; - temp.clear(); - temp.swap(partFilter); - - // TODO: comment this better. - for (auto & ruleOld : temp) { - for (auto & ruleNew : rules) { - if (ruleNew) { - RuleNative * top = RuleIntersection(ruleOld, ruleNew); - - if (!top) { - // There is no intersection. The rules don't interact at all. - // Save us some time by not having to compute differences. - continue; - } - - RuleNative * mid = RuleDifference(ruleOld, ruleNew); - RuleNative * bot = RuleDifference(ruleNew, ruleOld); - - if (!ruleOld->hasMod(MOD_FINAL) && top != ruleOld) { - // Add the rule for the intersection, since it is Overriding some parts of old. - partFilter.push_back(top); - - // Modify the old rule. - if (mid != ruleOld) { - delete ruleOld; - ruleOld = mid; - } - } - - if (bot != ruleNew) { - // Modify the new rule. - // Do this even if it is Final - Final only applies to following rules! - delete ruleNew; - ruleNew = bot; - } - - if (!ruleOld) { - // We have added all possible combinations with the new rule. We can stop here. - break; - } - } - } - - if (ruleOld) partFilter.push_back(ruleOld); - } - - for (auto & rule : rules) { - if (rule && !rule->hasMod(MOD_ADDONLY)) partFilter.push_back(rule); - } - - if (writePartial) { - print(partialStream, PRINT_IFPP, partFilter) << std::endl; - } - } - break; - - } - - default: - throw InternalError("Unknown section type!", __FILE__, __LINE__); - */ } } - - // Attach the rest of the filter since the last Flush. - AppendFilter(outFilter, partFilter); + + delete blank; } } \ No newline at end of file diff --git a/src/Compiler.h b/src/Compiler.h index bd363c6..5c4c479 100644 --- a/src/Compiler.h +++ b/src/Compiler.h @@ -1,47 +1,16 @@ #ifndef IFPP_COMPILER_H #define IFPP_COMPILER_H -#include "ifppTypes.h" -#include "ifppLogger.h" +#include "Types.h" +#include "RuleNative.h" +#include "Logger.h" namespace ifpp { -// Native rule, but keeps modifiers. -// Only one action per type allowed. -// No nested rules. -struct RuleNative { - RuleNative(ModifierList m) : modifiers(m), conditions(), actions(), useless(false), guid(IFPP_GUID()) {} - std::ostream & printSelf(std::ostream & os, PrintStyle ps) const; - bool hasMod(ModifierList ml) const; - ~RuleNative(); - - void addCondition(const Condition * c); - void addAction(const Action * a); - RuleNative * clone() const; - - ModifierList modifiers; - - // There can be more than one of certain conditions (lists, socketGroups). - // We assume that there is at most one of others (interval, bool). - std::multimap conditions; - - // Only the last action of each type is preserved. - std::map actions; - - // True if the rule does not match anything. - // In this case we do not guarantee that the list of conditions will be anything sensible. - bool useless; - - int guid; -}; - -typedef std::vector FilterNative; -std::ostream & print(std::ostream & os, PrintStyle ps, const FilterNative & f); - class Compiler { public: Compiler(Logger & l, bool wp, std::ostream & ps) : log(l), writePartial(wp), partialStream(ps) {}; - void Compile(const Filter & inFilter, FilterNative & outFilter); + void Compile(const FilterIFPP & inFilter, FilterNative & outFilter); private: Logger & log; diff --git a/src/Context.cpp b/src/Context.cpp index b134627..ba62e8e 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -1,17 +1,25 @@ -extern const int IFPP_VERSION_MAJOR; -extern const int IFPP_VERSION_MINOR; -extern const int IFPP_VERSION_PATCH; - #include "Context.h" +#include "location.hh" +// Autogenerated files are not super strict. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Weffc++" +#include "Parser.h" +#include "Lexer.h" +#pragma GCC diagnostic pop + +#include +#include #include -#include "location.hh" +extern const int IFPP_VERSION_MAJOR; +extern const int IFPP_VERSION_MINOR; +extern const int IFPP_VERSION_PATCH; namespace ifpp { void Context::reset() { - for (const auto & stm : filter) delete stm; + for (const auto stm : filter) delete stm; filter.clear(); countIns = 0; @@ -24,66 +32,88 @@ void Context::reset() { varList.clear(); for (const auto & s : varStyle) { - for (const auto & a : s.second) { + for (const auto a : s.second) { delete a; } } varStyle.clear(); } +void Context::parse() { + if (!(yyin = fopen(file.c_str(), "r"))) { + std::stringstream ss; + ss << "Unable to open input file \"" << file << "\"!" << std::endl; + ss << "Reason: " << strerror(errno); + throw std::runtime_error(ss.str()); + } + + yy::Parser parser(*this); + + yyset_debug(false); + parser.set_debug_level(false); + + log.message() << "Parser initialized." << std::endl; + log.message() << "Parsing file \"" << file << "\"..." << std::endl; + + int result = parser.parse(); + fclose(yyin); + + if (result != 0) throw InternalError("Parser finished with an error!", __FILE__, __LINE__); +} + bool Context::versionCheck(int vMajor, int vMinor, int vPatch) const { return vMajor == IFPP_VERSION_MAJOR && vMinor == IFPP_VERSION_MINOR && vPatch == IFPP_VERSION_PATCH; } -void Context::defineVariable(const std::string & name, VariableType type, int value) { - if (type != VAR_NUMBER) throw InternalError("Defining a variable with the wrong type! Expected VAR_NUMBER.", __FILE__, __LINE__); +void Context::defineVariable(const std::string & name, ExprType type, int value) { + if (type != EXPR_NUMBER) throw InternalError("Defining a variable with the wrong type! Expected EXPR_NUMBER.", __FILE__, __LINE__); varNumber[name] = value; } -void Context::defineVariable(const std::string & name, VariableType type, const Color & value) { - if (type != VAR_COLOR) throw InternalError("Defining a variable with the wrong type! Expected VAR_COLOR.", __FILE__, __LINE__); +void Context::defineVariable(const std::string & name, ExprType type, const Color & value) { + if (type != EXPR_COLOR) throw InternalError("Defining a variable with the wrong type! Expected EXPR_COLOR.", __FILE__, __LINE__); varColor[name] = value; } -void Context::defineVariable(const std::string & name, VariableType type, const std::string & value) { - if (type != VAR_FILE) throw InternalError("Defining a variable with the wrong type! Expected VAR_FILE.", __FILE__, __LINE__); +void Context::defineVariable(const std::string & name, ExprType type, const std::string & value) { + if (type != EXPR_FILE) throw InternalError("Defining a variable with the wrong type! Expected EXPR_FILE.", __FILE__, __LINE__); varFile[name] = value; } -void Context::defineVariable(const std::string & name, VariableType type, const NameList & value) { - if (type != VAR_LIST) throw InternalError("Defining a variable with the wrong type! Expected VAR_LIST.", __FILE__, __LINE__); +void Context::defineVariable(const std::string & name, ExprType type, const NameList & value) { + if (type != EXPR_LIST) throw InternalError("Defining a variable with the wrong type! Expected EXPR_LIST.", __FILE__, __LINE__); varList[name] = value; } -void Context::defineVariable(const std::string & name, VariableType type, const Style & value) { - if (type != VAR_STYLE) throw InternalError("Defining a variable with the wrong type! Expected VAR_STYLE.", __FILE__, __LINE__); +void Context::defineVariable(const std::string & name, ExprType type, const Style & value) { + if (type != EXPR_STYLE) throw InternalError("Defining a variable with the wrong type! Expected EXPR_STYLE.", __FILE__, __LINE__); varStyle[name] = value; } void Context::undefineVariable(const std::string & name) { switch (getVarType(name)) { - case VAR_NUMBER: varNumber.erase(name); break; - case VAR_COLOR: varColor.erase(name); break; - case VAR_FILE: varFile.erase(name); break; - case VAR_LIST: varList.erase(name); break; - case VAR_STYLE: + case EXPR_NUMBER: varNumber.erase(name); break; + case EXPR_COLOR: varColor.erase(name); break; + case EXPR_FILE: varFile.erase(name); break; + case EXPR_LIST: varList.erase(name); break; + case EXPR_STYLE: for (const auto & a : varStyle[name]) delete a; varStyle.erase(name); break; - case VAR_UNDEFINED: break; + case EXPR_UNDEFINED: break; default: throw UnhandledCase("Variable type", __FILE__, __LINE__); } } -VariableType Context::getVarType(const std::string & name) const { - if (varNumber.count(name) > 0) return VAR_NUMBER; - if (varColor.count(name) > 0) return VAR_COLOR; - if (varFile.count(name) > 0) return VAR_FILE; - if (varList.count(name) > 0) return VAR_LIST; - if (varStyle.count(name) > 0) return VAR_STYLE; - return VAR_UNDEFINED; +ExprType Context::getVarType(const std::string & name) const { + if (varNumber.count(name) > 0) return EXPR_NUMBER; + if (varColor.count(name) > 0) return EXPR_COLOR; + if (varFile.count(name) > 0) return EXPR_FILE; + if (varList.count(name) > 0) return EXPR_LIST; + if (varStyle.count(name) > 0) return EXPR_STYLE; + return EXPR_UNDEFINED; } // std::map::at throws std::out_of_range if value was not found. diff --git a/src/Context.h b/src/Context.h index 013c95b..5458743 100644 --- a/src/Context.h +++ b/src/Context.h @@ -17,18 +17,19 @@ class Context { varNumber(), varColor(), varFile(), varList(), varStyle() {}; void reset(); + void parse(); bool versionCheck(int vMajor, int vMinor, int vPatch) const; - void defineVariable(const std::string & name, ifpp::VariableType type, int value); - void defineVariable(const std::string & name, ifpp::VariableType type, const ifpp::Color & value); - void defineVariable(const std::string & name, ifpp::VariableType type, const std::string & value); - void defineVariable(const std::string & name, ifpp::VariableType type, const ifpp::NameList & value); - void defineVariable(const std::string & name, ifpp::VariableType type, const ifpp::Style & value); + void defineVariable(const std::string & name, ifpp::ExprType type, int value); + void defineVariable(const std::string & name, ifpp::ExprType type, const ifpp::Color & value); + void defineVariable(const std::string & name, ifpp::ExprType type, const std::string & value); + void defineVariable(const std::string & name, ifpp::ExprType type, const ifpp::NameList & value); + void defineVariable(const std::string & name, ifpp::ExprType type, const ifpp::Style & value); void undefineVariable(const std::string & name); - ifpp::VariableType getVarType(const std::string & name) const; + ifpp::ExprType getVarType(const std::string & name) const; int getVarValueNumber(const std::string & name) const; const ifpp::Color & getVarValueColor(const std::string & name) const; diff --git a/src/Lexer.l b/src/Lexer.l index a45a66e..598e83c 100644 --- a/src/Lexer.l +++ b/src/Lexer.l @@ -26,7 +26,7 @@ static yy::location loc; %} -%option noyywrap nounput batch noinput debug +%option noyywrap nounput batch noinput debug stack %x comment %x nameList @@ -35,6 +35,7 @@ blank [ \t\r] idPrefix "$" id [[:alpha:][:digit:]_] +dotdot ".." hex [0-9a-fA-F] socket [rgbwRGBW] @@ -56,17 +57,20 @@ garbage [^ \t\r\n.><={}/#] return yy::Parser::make_NEWLINE(l); } -"/*" BEGIN(comment); -[^*\n]* -"*"+[^*/\n]* -\n loc.lines(); -"*"+"/" BEGIN(INITIAL); +"/*" yy_push_state(comment); + +{ + [^*\n]* + "*"+[^*/\n]* + \n loc.lines(); + "*"+"/" yy_pop_state(); +} "{" return yy::Parser::make_CHR_LEFTBRACKET(loc); "}" return yy::Parser::make_CHR_RIGHTBRACKET(loc); -"." return yy::Parser::make_CHR_PERIOD(loc); ".." return yy::Parser::make_CHR_DOTDOT(loc); +"." return yy::Parser::make_CHR_PERIOD(loc); ":" return yy::Parser::make_CHR_COLON(loc); "<" return yy::Parser::make_OP_LT(ifpp::OP_LT, loc); @@ -75,38 +79,43 @@ garbage [^ \t\r\n.><={}/#] ">=" return yy::Parser::make_OP_GE(ifpp::OP_GE, loc); ">" return yy::Parser::make_OP_GT(ifpp::OP_GT, loc); +Define return yy::Parser::make_KW_DEFINE(loc); +Version return yy::Parser::make_KW_VERSION(loc); + Rule return yy::Parser::make_KW_RULE(loc); +Modifier return yy::Parser::make_KW_MODIFIER(loc); Default return yy::Parser::make_KW_DEFAULT(loc); +DefaultIgnore return yy::Parser::make_KW_IGNORE(loc); -Override return yy::Parser::make_KW_OVERRIDE(ifpp::MOD_OVERRIDE, loc); - -Define return yy::Parser::make_KW_DEFINE(loc); -Redefine return yy::Parser::make_KW_REDEFINE(loc); - -Version return yy::Parser::make_KW_VERSION(loc); +Override return yy::Parser::make_TAG_OVERRIDE(loc); -ItemLevel return yy::Parser::make_CON_ITEMLEVEL("ItemLevel", loc); -DropLevel return yy::Parser::make_CON_DROPLEVEL("DropLevel", loc); -Quality return yy::Parser::make_CON_QUALITY("Quality", loc); -Sockets return yy::Parser::make_CON_SOCKETS("Sockets", loc); -LinkedSockets return yy::Parser::make_CON_LINKEDSOCKETS("LinkedSockets", loc); -Height return yy::Parser::make_CON_HEIGHT("Height", loc); -Width return yy::Parser::make_CON_WIDTH("Width", loc); -StackSize return yy::Parser::make_CON_STACKSIZE("StackSize", loc); -GemLevel return yy::Parser::make_CON_GEMLEVEL("GemLevel", loc); -MapTier return yy::Parser::make_CON_MAPTIER("MapTier", loc); +ItemLevel return yy::Parser::make_CON_NUMERIC("ItemLevel", loc); +DropLevel return yy::Parser::make_CON_NUMERIC("DropLevel", loc); +Quality return yy::Parser::make_CON_NUMERIC("Quality", loc); +Sockets return yy::Parser::make_CON_NUMERIC("Sockets", loc); +LinkedSockets return yy::Parser::make_CON_NUMERIC("LinkedSockets", loc); +Height return yy::Parser::make_CON_NUMERIC("Height", loc); +Width return yy::Parser::make_CON_NUMERIC("Width", loc); +StackSize return yy::Parser::make_CON_NUMERIC("StackSize", loc); +GemLevel return yy::Parser::make_CON_NUMERIC("GemLevel", loc); +MapTier return yy::Parser::make_CON_NUMERIC("MapTier", loc); Rarity return yy::Parser::make_CON_RARITY("Rarity", loc); -Identified return yy::Parser::make_CON_IDENTIFIED("Identified", loc); -Corrupted return yy::Parser::make_CON_CORRUPTED("Corrupted", loc); -ElderItem return yy::Parser::make_CON_ELDERITEM("ElderItem", loc); -ShaperItem return yy::Parser::make_CON_SHAPERITEM("ShaperItem", loc); -ShapedMap return yy::Parser::make_CON_SHAPEDMAP("ShapedMap", loc); - -Class BEGIN(nameList); return yy::Parser::make_CON_CLASS("Class", loc); -BaseType BEGIN(nameList); return yy::Parser::make_CON_BASETYPE("BaseType", loc); -HasExplicitMod BEGIN(nameList); return yy::Parser::make_CON_EXPLICIT("HasExplicitMod", loc); +Class BEGIN(nameList); return yy::Parser::make_CON_LIST("Class", loc); +BaseType BEGIN(nameList); return yy::Parser::make_CON_LIST("BaseType", loc); +Prophecy BEGIN(nameList); return yy::Parser::make_CON_LIST("Prophecy", loc); +HasExplicitMod BEGIN(nameList); return yy::Parser::make_CON_LIST("HasExplicitMod", loc); +HasEnchantment BEGIN(nameList); return yy::Parser::make_CON_LIST("HasEnchantment", loc); + +Identified return yy::Parser::make_CON_BOOL("Identified", loc); +Corrupted return yy::Parser::make_CON_BOOL("Corrupted", loc); +ElderItem return yy::Parser::make_CON_BOOL("ElderItem", loc); +ShaperItem return yy::Parser::make_CON_BOOL("ShaperItem", loc); +ShapedMap return yy::Parser::make_CON_BOOL("ShapedMap", loc); +FracturedItem return yy::Parser::make_CON_BOOL("FracturedItem", loc); +SynthesisedItem return yy::Parser::make_CON_BOOL("SynthesisedItem", loc); +AnyEnchantment return yy::Parser::make_CON_BOOL("AnyEnchantment", loc); SocketGroup return yy::Parser::make_CON_SOCKETGROUP("SocketGroup", loc); @@ -126,15 +135,16 @@ MinimapIcon return yy::Parser::make_AC_MINIMAPICON("MinimapIcon", loc); PlayEffect return yy::Parser::make_AC_PLAYEFFECT("PlayEffect", loc); DisableDropSound return yy::Parser::make_AC_DISABLESOUND("DisableDropSound", loc); +Hide return yy::Parser::make_AC_HIDDEN("Hidden", loc); Hidden return yy::Parser::make_AC_HIDDEN("Hidden", loc); UseStyle return yy::Parser::make_AC_USESTYLE("UseStyle", loc); -Number return yy::Parser::make_TYPE_NUMBER(ifpp::VAR_NUMBER, loc); -Color return yy::Parser::make_TYPE_COLOR(ifpp::VAR_COLOR, loc); -File return yy::Parser::make_TYPE_FILE(ifpp::VAR_FILE, loc); -List BEGIN(nameList); return yy::Parser::make_TYPE_LIST(ifpp::VAR_LIST, loc); -Style return yy::Parser::make_TYPE_STYLE(ifpp::VAR_STYLE, loc); +Number return yy::Parser::make_TYPE_NUMBER(ifpp::EXPR_NUMBER, loc); +Color return yy::Parser::make_TYPE_COLOR(ifpp::EXPR_COLOR, loc); +File return yy::Parser::make_TYPE_FILE(ifpp::EXPR_FILE, loc); +List BEGIN(nameList); return yy::Parser::make_TYPE_LIST(ifpp::EXPR_LIST, loc); +Style return yy::Parser::make_TYPE_STYLE(ifpp::EXPR_STYLE, loc); true return yy::Parser::make_CONST_BOOL(true, loc); false return yy::Parser::make_CONST_BOOL(false, loc); @@ -144,28 +154,21 @@ Magic return yy::Parser::make_CONST_RARITY(ifpp::Magic, loc); Rare return yy::Parser::make_CONST_RARITY(ifpp::Rare, loc); Unique return yy::Parser::make_CONST_RARITY(ifpp::Unique, loc); -ItemFilterAlert10 | -ItemFilterAlert11 | -ItemFilterAlert12 | -ItemFilterAlert13 | -ItemFilterAlert14 | -ItemFilterAlert15 | -ItemFilterAlert16 | -ShAlchemy | -ShBlessed | -ShChaos | -ShDivine | -ShExalted | -ShFusing | -ShGeneral | -ShMirror | -ShRegal | -ShVaal return yy::Parser::make_CONST_SOUND(yytext, loc); +ShAlchemy return yy::Parser::make_CONST_SOUND("ShAlchemy", loc); +ShBlessed return yy::Parser::make_CONST_SOUND("ShBlessed", loc); +ShChaos return yy::Parser::make_CONST_SOUND("ShChaos", loc); +ShDivine return yy::Parser::make_CONST_SOUND("ShDivine", loc); +ShExalted return yy::Parser::make_CONST_SOUND("ShExalted", loc); +ShFusing return yy::Parser::make_CONST_SOUND("ShFusing", loc); +ShGeneral return yy::Parser::make_CONST_SOUND("ShGeneral", loc); +ShMirror return yy::Parser::make_CONST_SOUND("ShMirror", loc); +ShRegal return yy::Parser::make_CONST_SOUND("ShRegal", loc); +ShVaal return yy::Parser::make_CONST_SOUND("ShVaal", loc); -Red return yy::Parser::make_CONST_COLOR("Red", loc); -Green return yy::Parser::make_CONST_COLOR("Green", loc); Blue return yy::Parser::make_CONST_COLOR("Blue", loc); Brown return yy::Parser::make_CONST_COLOR("Brown", loc); +Green return yy::Parser::make_CONST_COLOR("Green", loc); +Red return yy::Parser::make_CONST_COLOR("Red", loc); White return yy::Parser::make_CONST_COLOR("White", loc); Yellow return yy::Parser::make_CONST_COLOR("Yellow", loc); @@ -193,7 +196,7 @@ x{hex}+ return yy::Parser::make_HEX(yytext + 1, loc); {socket}+ return yy::Parser::make_SOCKETGROUP(std::string(yytext), loc); \"[^"\r\n]*\" return yy::Parser::make_FILENAME(yytext, loc); -{blank}+ +{blank}+ \n loc.lines(); BEGIN(INITIAL); return yy::Parser::make_NEWLINE(loc); {idPrefix}{id}+ return yy::Parser::make_VARIABLE(yytext, loc); @@ -210,9 +213,9 @@ x{hex}+ return yy::Parser::make_HEX(yytext + 1, loc); static bool first = true; if (first) { first = false; - return yy::Parser::make_NEWLINE(loc); + return yy::Parser::make_NEWLINE(loc); } - return yy::Parser::make_END(loc); + return yy::Parser::make_END(loc); } %% diff --git a/src/Parser.y b/src/Parser.y index bce4ea9..c71fb87 100644 --- a/src/Parser.y +++ b/src/Parser.y @@ -67,10 +67,10 @@ return true; } - static bool checkVarUse(ifpp::Context & ctx, const yy::location & l, const std::string & name, ifpp::VariableType type) { - ifpp::VariableType oldType = ctx.getVarType(name); - if (oldType == ifpp::VAR_UNDEFINED) { - if (type == ifpp::VAR_LIST) { + static bool checkVarUse(ifpp::Context & ctx, const yy::location & l, const std::string & name, ifpp::ExprType type) { + ifpp::ExprType oldType = ctx.getVarType(name); + if (oldType == ifpp::EXPR_UNDEFINED) { + if (type == ifpp::EXPR_LIST) { ctx.errorAt(l) << "Variable " << name << " has not been defined. " << "It will be ignored in this list." << std::endl; } else { @@ -79,7 +79,7 @@ } return false; } else if (oldType != type) { - if (type == ifpp::VAR_LIST) { + if (type == ifpp::EXPR_LIST) { ctx.errorAt(l) << "Variable " << name << " is of type " << oldType << ", but this command requires " << type << ". " << "The variable will be ignored in this list." << std::endl; } else { @@ -93,11 +93,11 @@ template static ifpp::DefinitionBase * magicDefinition(ifpp::Context & ctx, const yy::location & l, - const std::string & name, ifpp::VariableType type, const T & value, bool redefine = false) { + const std::string & name, ifpp::ExprType type, const T & value, bool redefine = false) { - ifpp::VariableType oldType = ctx.getVarType(name); + ifpp::ExprType oldType = ctx.getVarType(name); if (redefine) { - if (oldType == ifpp::VAR_UNDEFINED) { + if (oldType == ifpp::EXPR_UNDEFINED) { ctx.warningAt(l) << "Variable " << name << " has not been defined yet. " << "It will be defined now. Use the Define command instead to avoid this warning." << std::endl; } else if (oldType != type) { @@ -109,7 +109,7 @@ if (oldType == type) { ctx.warningAt(l) << "Variable " << name << " has already been defined. " << "It will be replaced by the new value. Use the Redefine command instead to avoid this warning." << std::endl; - } else if (oldType != ifpp::VAR_UNDEFINED) { + } else if (oldType != ifpp::EXPR_UNDEFINED) { ctx.errorAt(l) << "Variable " << name << " has already been defined as type " << oldType << "! " << "The variable will be replaced by the new value of type " << type << "." << std::endl; ctx.undefineVariable(name); @@ -121,14 +121,14 @@ } static ifpp::ConditionInterval * magicInterval(ifpp::Context & ctx, const yy::location & l, - const std::string & what, int from, int to) { + const std::string & what, int from, int to, ifpp::TagList tags) { clampInterval(ctx, l, what, from, to, ifpp::getLimit(what, ifpp::MIN), ifpp::getLimit(what, ifpp::MAX)); - return new ifpp::ConditionInterval(what, from, to); + return new ifpp::ConditionInterval(what, from, to, tags); } static ifpp::ConditionInterval * magicInterval(ifpp::Context & ctx, const yy::location & l, - const std::string & what, ifpp::Operator op, int value) { + const std::string & what, ifpp::Operator op, int value, ifpp::TagList tags) { int from = INT_MIN, to = INT_MAX; switch(op) { @@ -139,7 +139,7 @@ case ifpp::OP_GT: from = value + 1; break; default: throw ifpp::UnhandledCase("Operator", __FILE__, __LINE__); } - return magicInterval(ctx, l, what, from, to); + return magicInterval(ctx, l, what, from, to, tags); } template static void listNew(std::vector & l) { @@ -185,16 +185,18 @@ CHR_DOTDOT ".." CHR_COLON ":" + KW_VERSION "Version" + KW_DEFINE "Define" + KW_RULE "Rule" + KW_MODIFIER "Modifier" KW_DEFAULT "Default" + KW_IGNORE "DefaultIgnore" - KW_DEFINE "Define" - KW_REDEFINE "Redefine" - - KW_VERSION "Version" + TAG_OVERRIDE "Override" ; -%token +%token TYPE_NUMBER "Number" TYPE_COLOR "Color" TYPE_FILE "File" @@ -212,36 +214,15 @@ %token SOCKETGROUP "socket group" -%token KW_OVERRIDE "Override" - %token CONST_BOOL "boolean value" %token - CON_ITEMLEVEL "ItemLevel" - CON_DROPLEVEL "DropLevel" - CON_QUALITY "Quality" - CON_SOCKETS "Sockets" - CON_LINKEDSOCKETS "LinkedSockets" - CON_HEIGHT "Height" - CON_WIDTH "Width" - CON_STACKSIZE "StackSize" - CON_GEMLEVEL "GemLevel" - CON_MAPTIER "MapTier" - - CON_RARITY "Rarity" - - CON_CLASS "Class" - CON_BASETYPE "BaseType" - CON_EXPLICIT "HasExplicitMod" - - CON_IDENTIFIED "Identified" - CON_CORRUPTED "Corrupted" - CON_ELDERITEM "ElderItem" - CON_SHAPERITEM "ShaperItem" - CON_SHAPEDMAP "ShapedMap" - - CON_SOCKETGROUP "SocketGroup" - + CON_NUMERIC "Condition (Numeric)" + CON_RARITY "Condition (Rarity)" + CON_LIST "Condition (List)" + CON_BOOL "Condition (Boolean)" + CON_SOCKETGROUP "Condition (Socket group)" + AC_FONTSIZE "SetFontSize" AC_BORDERCOLOR "SetBorderColor" @@ -280,44 +261,32 @@ %token END 0 "end of file" + %type instruction %type definition +%type rule -%type - ruleBody - conditions - actions - actionsNotEmpty - rules - rulesNotEmpty +%type commands +%type + style + actionStyle ; %type condition %type action +%type modifier %type defaultStyle -%type rule -%type - style - actionStyle - exprStyle - varStyle +%type + tags + tag ; -%type operator +%type exprList -%type - modifiers - modifier -; +%type exprColor %type - conditionNumeric - conditionRarity - conditionBool - conditionNameList - conditionSocketGroup - actionNumber actionColor actionBool @@ -326,30 +295,18 @@ soundId exprFile - varFile ; +%type operator + %type - soundVolume exprNumber - exprReallyNumber - varNumber + soundVolume ; -%type - exprColor - varColor -; +%type exprBool -%type - exprList - varList -; -%type - defineOrRedefine - exprBool -; %printer { ifpp::print(yyoutput, ifpp::PRINT_IFPP, $$); } @@ -368,8 +325,12 @@ %printer { yyoutput << $$; } <*> + + %% + + %start statements; statements: @@ -391,145 +352,98 @@ KW_VERSION NUMBER[vMajor] CHR_PERIOD NUMBER[vMinor] CHR_PERIOD NUMBER[vBugfix] N } definition: - defineOrRedefine VARIABLE TYPE_NUMBER exprNumber NEWLINE - { $$ = magicDefinition(ctx, @$, $2, $3, $4, $1); } -| defineOrRedefine VARIABLE TYPE_COLOR exprColor NEWLINE - { $$ = magicDefinition(ctx, @$, $2, $3, $4, $1); } -| defineOrRedefine VARIABLE TYPE_FILE exprFile NEWLINE - { $$ = magicDefinition(ctx, @$, $2, $3, $4, $1); } -| defineOrRedefine VARIABLE TYPE_LIST exprList NEWLINE - { $$ = magicDefinition(ctx, @$, $2, $3, $4, $1); } -| defineOrRedefine VARIABLE TYPE_STYLE newlines CHR_LEFTBRACKET NEWLINE style CHR_RIGHTBRACKET NEWLINE - { $$ = magicDefinition(ctx, @$, $2, $3, $style, $1); } - -defineOrRedefine: - KW_DEFINE { $$ = false; } -| KW_REDEFINE { $$ = true; } + KW_DEFINE VARIABLE TYPE_NUMBER exprNumber NEWLINE + { $$ = magicDefinition(ctx, @$, $2, $3, $4, false); } +| KW_DEFINE VARIABLE TYPE_COLOR exprColor NEWLINE + { $$ = magicDefinition(ctx, @$, $2, $3, $4, false); } +| KW_DEFINE VARIABLE TYPE_FILE exprFile NEWLINE + { $$ = magicDefinition(ctx, @$, $2, $3, $4, false); } +| KW_DEFINE VARIABLE TYPE_LIST exprList NEWLINE + { $$ = magicDefinition(ctx, @$, $2, $3, $4, false); } +| KW_DEFINE VARIABLE TYPE_STYLE newlines CHR_LEFTBRACKET NEWLINE style CHR_RIGHTBRACKET NEWLINE + { $$ = magicDefinition(ctx, @$, $2, $3, $style, false); } rule: -KW_RULE modifiers newlines CHR_LEFTBRACKET NEWLINE ruleBody CHR_RIGHTBRACKET NEWLINE { - $$ = new ifpp::RuleIFPP($modifiers, $ruleBody); +KW_RULE tags newlines CHR_LEFTBRACKET NEWLINE commands CHR_RIGHTBRACKET NEWLINE { + $$ = new ifpp::RuleIFPP($commands, $tags); } -ruleBody: -conditions actions rules defaultStyle { - $$.insert($$.end(), $1.begin(), $1.end()); - $$.insert($$.end(), $2.begin(), $2.end()); - $$.insert($$.end(), $3.begin(), $3.end()); - if ($4) $$.push_back($4); +modifier: +KW_MODIFIER tags newlines CHR_LEFTBRACKET NEWLINE commands CHR_RIGHTBRACKET NEWLINE { + $$ = new ifpp::Modifier($commands, $tags); } -conditions: -%empty {} -| conditions condition { listAppend($$, $1, $2); } -| conditions NEWLINE { listCopy($$, $1); } -| conditions error NEWLINE { listCopy($$, $1); } - -actions: -%empty { /* To fix ambiguity when a rule only contains newlines. */ } -| actionsNotEmpty { listCopy($$, $1); } - -actionsNotEmpty: -action { listAppend($$, $$, $1); } -| actionStyle { listMerge($$, $$, $1); } -| actionsNotEmpty action { listAppend($$, $1, $2); } -| actionsNotEmpty actionStyle { listMerge($$, $1, $2); } -| actionsNotEmpty NEWLINE { listCopy($$, $1); } -| actionsNotEmpty error NEWLINE { listCopy($$, $1); } - -rules: -%empty { /* To fix ambiguity when a rule only contains newlines. */ } -| rulesNotEmpty { $$ = $1; } - -rulesNotEmpty: -rule { listAppend($$, $$, $1); } -| rulesNotEmpty rule { listAppend($$, $1, $2); } -| rulesNotEmpty NEWLINE { listCopy($$, $1); } -| rulesNotEmpty error NEWLINE { listCopy($$, $1); } - defaultStyle: -%empty { $$ = NULL; } -| KW_DEFAULT newlines CHR_LEFTBRACKET NEWLINE style CHR_RIGHTBRACKET newlines { $$ = new ifpp::DefaultStyle($style); } +KW_DEFAULT tags newlines CHR_LEFTBRACKET NEWLINE style CHR_RIGHTBRACKET NEWLINE { + $$ = new ifpp::DefaultStyle($style, $tags); +} -newlines: -%empty {} -| newlines NEWLINE {} + + +commands: +%empty { listNew($$); } +| commands condition { listAppend($$, $1, $2); } +| commands action { listAppend($$, $1, $2); } +| commands actionStyle { listMerge($$, $1, $2); } +| commands rule { listAppend($$, $1, $2); } +| commands modifier { listAppend($$, $1, $2); } +| commands defaultStyle { listAppend($$, $1, $2); } +| commands tags KW_IGNORE NEWLINE { listAppend($$, $1, new ifpp::Ignore($tags)); } +| commands NEWLINE { listCopy($$, $1); } +| commands error NEWLINE { listCopy($$, $1); } + +style: +%empty { listNew($$); } +| style action { listAppend($$, $1, $2); } +| style actionStyle { listMerge($$, $1, $2); } +| style NEWLINE { listCopy($$, $1); } +| style error NEWLINE { listCopy($$, $1); } condition: - conditionNumeric[what] operator[op] exprNumber[value] NEWLINE - { $$ = magicInterval(ctx, @$, $what, $op, $value); } -| conditionNumeric[what] exprNumber[value] NEWLINE - { $$ = magicInterval(ctx, @$, $what, $value, $value); } -| conditionNumeric[what] exprNumber[from] CHR_DOTDOT exprNumber[to] NEWLINE - { $$ = magicInterval(ctx, @$, $what, $from, $to); } -| conditionRarity[what] operator[op] CONST_RARITY[value] NEWLINE - { $$ = magicInterval(ctx, @$, $what, $op, $value); } -| conditionRarity[what] CONST_RARITY[value] NEWLINE - { $$ = magicInterval(ctx, @$, $what, $value, $value); } -| conditionRarity[what] CONST_RARITY[from] CHR_DOTDOT CONST_RARITY[to] NEWLINE - { $$ = magicInterval(ctx, @$, $what, $from, $to); } -| conditionBool[what] exprBool[value] NEWLINE - { $$ = new ifpp::ConditionBool($what, $value); } -| conditionNameList[what] exprList[list] NEWLINE - { $$ = new ifpp::ConditionNameList($what, $list); } -| conditionSocketGroup[what] SOCKETGROUP[value] NEWLINE - { $$ = new ifpp::ConditionSocketGroup($what, $value); } - -conditionNumeric: - CON_ITEMLEVEL { $$ = $1; } -| CON_DROPLEVEL { $$ = $1; } -| CON_QUALITY { $$ = $1; } -| CON_SOCKETS { $$ = $1; } -| CON_LINKEDSOCKETS { $$ = $1; } -| CON_HEIGHT { $$ = $1; } -| CON_WIDTH { $$ = $1; } -| CON_STACKSIZE { $$ = $1; } -| CON_GEMLEVEL { $$ = $1; } -| CON_MAPTIER { $$ = $1; } - -conditionRarity: -CON_RARITY { $$ = $1; } - -conditionBool: - CON_IDENTIFIED { $$ = $1; } -| CON_CORRUPTED { $$ = $1; } -| CON_ELDERITEM { $$ = $1; } -| CON_SHAPERITEM { $$ = $1; } -| CON_SHAPEDMAP { $$ = $1; } - -conditionNameList: - CON_CLASS { $$ = $1; } -| CON_BASETYPE { $$ = $1; } -| CON_EXPLICIT { $$ = $1; } - -conditionSocketGroup: -CON_SOCKETGROUP { $$ = $1; } + tags CON_NUMERIC[what] operator[op] exprNumber[value] NEWLINE + { $$ = magicInterval(ctx, @$, $what, $op, $value, $tags); } +| tags CON_NUMERIC[what] exprNumber[value] NEWLINE + { $$ = magicInterval(ctx, @$, $what, $value, $value, $tags); } +| tags CON_NUMERIC[what] exprNumber[from] CHR_DOTDOT exprNumber[to] NEWLINE + { $$ = magicInterval(ctx, @$, $what, $from, $to, $tags); } +| tags CON_RARITY[what] operator[op] CONST_RARITY[value] NEWLINE + { $$ = magicInterval(ctx, @$, $what, $op, $value, $tags); } +| tags CON_RARITY[what] CONST_RARITY[value] NEWLINE + { $$ = magicInterval(ctx, @$, $what, $value, $value, $tags); } +| tags CON_RARITY[what] CONST_RARITY[from] CHR_DOTDOT CONST_RARITY[to] NEWLINE + { $$ = magicInterval(ctx, @$, $what, $from, $to, $tags); } +| tags CON_LIST[what] exprList[list] NEWLINE + { $$ = new ifpp::ConditionNameList($what, $list, $tags); } +| tags CON_BOOL[what] exprBool[value] NEWLINE + { $$ = new ifpp::ConditionBool($what, $value, $tags); } +| tags CON_SOCKETGROUP[what] SOCKETGROUP[value] NEWLINE + { $$ = new ifpp::ConditionSocketGroup($what, $value, $tags); } action: - modifiers actionNumber[what] exprNumber[value] NEWLINE { + tags actionNumber[what] exprNumber[value] NEWLINE { clampValue(ctx, @$, $what, $value, ifpp::getLimit($what, ifpp::MIN), ifpp::getLimit($what, ifpp::MAX)); - $$ = new ifpp::ActionNumber($modifiers, $what, $value); + $$ = new ifpp::ActionNumber($what, $value, $tags); } -| modifiers actionColor[what] exprColor[value] NEWLINE - { $$ = new ifpp::ActionColor($modifiers, $what, $value); } -| modifiers actionBool[what] exprBool[value] NEWLINE - { $$ = new ifpp::ActionBool($modifiers, $what, $value); } -| modifiers actionSound[what] soundId soundVolume NEWLINE - { $$ = new ifpp::ActionSound($modifiers, $what, $soundId, $soundVolume); } -| modifiers AC_CUSTOMSOUND[what] exprFile[file] NEWLINE - { $$ = new ifpp::ActionFile($modifiers, $what, $file); } -| modifiers AC_MINIMAPICON[what] exprNumber[size] CONST_COLOR[color] CONST_SHAPE[shape] NEWLINE { +| tags actionColor[what] exprColor[value] NEWLINE + { $$ = new ifpp::ActionColor($what, $value, $tags); } +| tags actionBool[what] exprBool[value] NEWLINE + { $$ = new ifpp::ActionBool($what, $value, $tags); } +| tags actionSound[what] soundId soundVolume NEWLINE + { $$ = new ifpp::ActionSound($what, $soundId, $soundVolume, $tags); } +| tags AC_CUSTOMSOUND[what] exprFile[file] NEWLINE + { $$ = new ifpp::ActionFile($what, $file, $tags); } +| tags AC_MINIMAPICON[what] exprNumber[size] CONST_COLOR[color] CONST_SHAPE[shape] NEWLINE { clampValue(ctx, @$, $what, $size, ifpp::getLimit($what, ifpp::MIN), ifpp::getLimit($what, ifpp::MAX)); - $$ = new ifpp::ActionMapIcon($modifiers, $what, $size, $color, $shape); + $$ = new ifpp::ActionMapIcon($what, $size, $color, $shape, $tags); } -| modifiers AC_PLAYEFFECT[what] CONST_COLOR[color] NEWLINE - { $$ = new ifpp::ActionEffect($modifiers, $what, $color, ""); } -| modifiers AC_PLAYEFFECT[what] CONST_COLOR[color] CONST_TEMP[temp] NEWLINE - { $$ = new ifpp::ActionEffect($modifiers, $what, $color, $temp); } +| tags AC_PLAYEFFECT[what] CONST_COLOR[color] NEWLINE + { $$ = new ifpp::ActionEffect($what, $color, "", $tags); } +| tags AC_PLAYEFFECT[what] CONST_COLOR[color] CONST_TEMP NEWLINE + { $$ = new ifpp::ActionEffect($what, $color, "Temp", $tags); } actionNumber: AC_FONTSIZE { $$ = $1; } @@ -548,39 +462,18 @@ actionSound: | AC_SOUNDPOSITIONAL { $$ = $1; } actionStyle: -modifiers AC_USESTYLE exprStyle[style] NEWLINE { - listNew($$); - for (const auto & a : $style) { - ifpp::Action * aa = static_cast(a->clone()); - aa->modifiers |= $modifiers; - $$.push_back(aa); +tags AC_USESTYLE[what] VARIABLE[varName] NEWLINE { + if (checkVarUse(ctx, @varName, $varName, ifpp::EXPR_STYLE)) { + $$ = ctx.getVarValueStyle($varName); + } else { + YYERROR; } -} - - - -modifiers: -%empty { $$ = 0; } -| modifiers modifier { $$ |= $1; } - -modifier: -KW_OVERRIDE { $$ = $1; } - - - -style: -actions { - for (auto & a : $1) { - $$.push_back(static_cast(a)); + for (auto a : $$) { + a->tags |= $tags; } } -operator: - OP_LT { $$ = $1; } -| OP_LE { $$ = $1; } -| OP_EQ { $$ = $1; } -| OP_GE { $$ = $1; } -| OP_GT { $$ = $1; } + soundId: exprNumber { @@ -599,21 +492,27 @@ soundVolume: -exprNumber: -varNumber { $$ = $1; } -| exprReallyNumber { $$ = $1; } +tags: +%empty { $$ = 0; } +| tags tag { $$ = $1 | $2; } -exprReallyNumber: -NUMBER { $$ = $1; } +tag: +TAG_OVERRIDE { $$ = ifpp::TAG_OVERRIDE; } -exprBool: -%empty { $$ = true; } -| CONST_BOOL { $$ = $1; } + + +exprNumber: +NUMBER { $$ = $1; } +| VARIABLE { + if (checkVarUse(ctx, @1, $1, ifpp::EXPR_NUMBER)) { + $$ = ctx.getVarValueNumber($1); + } else { + YYERROR; + } +} exprColor: HEX { $$ = ifpp::Color($1); } -| varColor { $$ = $1; } -| varColor CHR_COLON exprNumber { $$ = $1; $$.a = $3; } | exprNumber exprNumber exprNumber { clampValue(ctx, @1, "Color value", $1, ifpp::getLimit("Color", ifpp::MIN), ifpp::getLimit("Color", ifpp::MAX)); clampValue(ctx, @2, "Color value", $2, ifpp::getLimit("Color", ifpp::MIN), ifpp::getLimit("Color", ifpp::MAX)); @@ -627,64 +526,58 @@ HEX { $$ = ifpp::Color($1); } clampValue(ctx, @4, "Color value", $4, ifpp::getLimit("Color", ifpp::MIN), ifpp::getLimit("Color", ifpp::MAX)); $$ = ifpp::Color($1, $2, $3, $4); } - -exprFile: -varFile { $$ = $1; } -| FILENAME { $$ = $1; } - -exprList: -%empty { listNew($$); } -| exprList NAME { listAppend($$, $1, $2); } -| exprList varList[var] { listMerge($$, $1, $2); } - -exprStyle: -varStyle { $$ = $1; } - - - -varNumber: -VARIABLE { - if (checkVarUse(ctx, @1, $1, ifpp::VAR_NUMBER)) { - $$ = ctx.getVarValueNumber($1); +| VARIABLE { + if (checkVarUse(ctx, @1, $1, ifpp::EXPR_COLOR)) { + $$ = ctx.getVarValueColor($1); } else { YYERROR; } } - -varColor: -VARIABLE { - if (checkVarUse(ctx, @1, $1, ifpp::VAR_COLOR)) { +| VARIABLE CHR_COLON exprNumber { + if (checkVarUse(ctx, @1, $1, ifpp::EXPR_COLOR)) { $$ = ctx.getVarValueColor($1); } else { YYERROR; } + $$.a = $3; } -varFile: -VARIABLE { - if (checkVarUse(ctx, @1, $1, ifpp::VAR_FILE)) { +exprBool: +%empty { $$ = true; } +| CONST_BOOL { $$ = $1; } + +exprFile: +FILENAME { $$ = $1; } +| VARIABLE { + if (checkVarUse(ctx, @1, $1, ifpp::EXPR_FILE)) { $$ = ctx.getVarValueFile($1); } else { YYERROR; } } -varList: -VARIABLE { - if (checkVarUse(ctx, @1, $1, ifpp::VAR_LIST)) { - $$ = ctx.getVarValueList($1); +exprList: +%empty { listNew($$); } +| exprList NAME { listAppend($$, $1, $2); } +| exprList VARIABLE { + if (checkVarUse(ctx, @2, $2, ifpp::EXPR_LIST)) { + listMerge($$, $1, ctx.getVarValueList($2)); } else { - listNew($$); + listCopy($$, $1); } } -varStyle: -VARIABLE { - if (checkVarUse(ctx, @1, $1, ifpp::VAR_STYLE)) { - $$ = ctx.getVarValueStyle($1); - } else { - YYERROR; - } -} + + +operator: + OP_LT { $$ = $1; } +| OP_LE { $$ = $1; } +| OP_EQ { $$ = $1; } +| OP_GE { $$ = $1; } +| OP_GT { $$ = $1; } + +newlines: +%empty {} +| newlines NEWLINE {} %% diff --git a/src/ParserWrapper.cpp b/src/ParserWrapper.cpp index cd18443..6ba7da6 100644 --- a/src/ParserWrapper.cpp +++ b/src/ParserWrapper.cpp @@ -1,13 +1,15 @@ #include "ParserWrapper.h" -#include -#include -#include - // Autogenerated files are not super strict. +#pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Weffc++" #include "Parser.h" #include "Lexer.h" +#pragma GCC diagnostic pop + +#include +#include +#include namespace ifpp { diff --git a/src/RuleNative.cpp b/src/RuleNative.cpp new file mode 100644 index 0000000..caa64ab --- /dev/null +++ b/src/RuleNative.cpp @@ -0,0 +1,301 @@ +#include "RuleNative.h" + +namespace ifpp { + +/*********** +* TESTING CONTAINMENT OF CONDITIONS +***********/ + +static bool MatchedBy(const std::string & s1, const std::string & s2) { + return s1.find(s2) != std::string::npos; +} + +static bool ConditionSubset(const ConditionInterval * first, const ConditionInterval * second) { + return second->from <= first->from && first->to <= second->to; +} + +static bool ConditionSubset(const ConditionBool * first, const ConditionBool * second) { + return first->value == second->value; +} + +static bool ConditionSubset(const ConditionNameList * first, const ConditionNameList * second) { + // True if every string in the first list is matched by some string in the second list. + for (const auto & s1 : first->nameList) { + bool found = false; + for (const auto & s2 : second->nameList) { + if (MatchedBy(s1, s2)) { + // Anything matching s1 can also be matched by s2. + found = true; + break; + } + } + if (!found) return false; // This string can not be matched by the second list. + } + return true; +} + +static bool ConditionSubset(const ConditionSocketGroup * first, const ConditionSocketGroup * second) { + // True if the first condition needs fewer or equal sockets of every color. + if (first->socketGroup.r > second->socketGroup.r) return false; + if (first->socketGroup.g > second->socketGroup.g) return false; + if (first->socketGroup.b > second->socketGroup.b) return false; + if (first->socketGroup.w > second->socketGroup.w) return false; + return true; +} + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-function" +static bool ConditionSubset(const Condition * first, const Condition * second) { + if (!first || !second) { + throw InternalError("Attempting to test containment of a NULL condition!", __FILE__, __LINE__); + } + if (first->what != second->what) { + throw InternalError("Attempting to test containment of conditions of different type!", __FILE__, __LINE__); + } + switch (first->conType) { + case CON_INTERVAL: + return ConditionSubset(static_cast(first), static_cast(second)); + case CON_BOOL: + return ConditionSubset(static_cast(first), static_cast(second)); + case CON_NAMELIST: + return ConditionSubset(static_cast(first), static_cast(second)); + case CON_SOCKETGROUP: + return ConditionSubset(static_cast(first), static_cast(second)); + default: + throw UnhandledCase("Condition type", __FILE__, __LINE__); + } +} +#pragma GCC diagnostic pop + + + +/*********** +* RULENATIVE +***********/ + +/* +Always adds a clone of c, if necessary. +*/ +void RuleNative::addCondition(const Condition * c) { + auto cOld = conditions.find(c->what); + if (cOld == conditions.end()) { + // We do not have this type of condition yet. + // Check if the condition matches anything. + // We still add it though. + switch (c->conType) { + case CON_INTERVAL: { + auto ci = static_cast(c); + if (ci->from > ci->to) useless = true; + break; + } + case CON_SOCKETGROUP: { + auto csg = static_cast(c); + if (csg->socketGroup.r + csg->socketGroup.g + csg->socketGroup.b + csg->socketGroup.w > getLimit("LinkedSockets", MAX)) { + useless = true; + } + break; + } + case CON_BOOL: // Always matches something. + break; + case CON_NAMELIST: // We can not determine what this matches. + break; + default: + throw UnhandledCase("Condition type", __FILE__, __LINE__); + } + // Add the condition and take ownership. + conditions.insert(std::make_pair(c->what, static_cast(c->clone()))); + return; + } + + // A condition of this type already exists. Intersect with it. + + // Override - replace the condition. + if (c->hasTag(TAG_OVERRIDE)) { + delete cOld->second; + cOld->second = static_cast(c->clone()); + return; + } + + switch (c->conType) { + case CON_INTERVAL: { + // Refine the interval in existing condition. + auto c1 = static_cast(cOld->second); + auto c2 = static_cast(c); + + if (c1->from < c2->from) c1->from = c2->from; + if (c1->to > c2->to) c1->to = c2->to; + if (c1->from > c1->to) useless = true; + break; + } + case CON_BOOL: { + // A condition can not be true and false at the same time. + auto c1 = static_cast(cOld->second); + auto c2 = static_cast(c); + + if (c1->value != c2->value) useless = true; + break; + } + case CON_SOCKETGROUP: { + auto c1 = static_cast(c); + int r = c1->socketGroup.r; + int g = c1->socketGroup.g; + int b = c1->socketGroup.b; + int w = c1->socketGroup.w; + + bool add = true; + auto range = conditions.equal_range(c->what); + for (auto it = range.first; it != range.second; ) { + // Check if we need more than 6 different sockets. + auto c2 = static_cast(it->second); + if (c2->socketGroup.r > r) r = c2->socketGroup.r; + if (c2->socketGroup.g > g) g = c2->socketGroup.g; + if (c2->socketGroup.b > b) b = c2->socketGroup.b; + if (c2->socketGroup.w > w) w = c2->socketGroup.w; + + // If some existing condition is stricter than the new condition, we do not need to do anything. + if (ConditionSubset(c2, c1)) { + add = false; + break; + } + // If the new condition is stricter than some existing condition, we can remove the existing one. + if (ConditionSubset(c1, c2)) { + delete it->second; + it = conditions.erase(it); + } else { + ++it; + } + } + + // Check if we need more than 6 different sockets. + if (r + g + b + w > getLimit("SocketGroup", MAX)) useless = true; + + // Add the new condition. + if (add) { + conditions.insert(std::make_pair(c->what, static_cast(c->clone()))); + } + break; + } + case CON_NAMELIST: { + // There can be more than one of these conditions, in case of intersections. + auto c1 = static_cast(c); + + bool add = true; + auto range = conditions.equal_range(c->what); + for (auto it = range.first; it != range.second; ) { + auto c2 = static_cast(it->second); + + // If some existing condition is stricter than the new condition, we do not need to do anything. + if (ConditionSubset(c2, c1)) { + add = false; + break; + } + // If the new condition is stricter than some existing condition, we can remove the existing one. + if (ConditionSubset(c1, c2)) { + delete it->second; + it = conditions.erase(it); + } else { + ++it; + } + } + + // Add the new condition. + if (add) { + conditions.insert(std::make_pair(c->what, static_cast(c->clone()))); + } + break; + } + default: + throw UnhandledCase("Condition type", __FILE__, __LINE__); + } +} + +/* +Adds a clone of the action. +If the action has the Override tag, replaces an existing action with the same name. +Otherwise the old action is preserved. +*/ +void RuleNative::addAction(const Action * a) { + // Possibly override other actions of the same type. + auto it = actions.find(a->what); + if (it != actions.end() && a->hasTag(TAG_OVERRIDE)) { + delete it->second; + it->second = static_cast(a->clone()); + } else { + actions.insert(std::make_pair(a->what, static_cast(a->clone()))); + } +} + +bool RuleNative::hasTag(TagList t) const { + return tags & t; +} + +std::ostream & RuleNative::printSelf(std::ostream & os, PrintStyle ps) const { + switch (ps) { + case PRINT_NATIVE: { + if (useless) { + throw InternalError("Writing a useless rule to native filter!", __FILE__, __LINE__); + } + + auto it = actions.find("Hidden"); + if (it != actions.end() && static_cast(it->second)->arg1) { + os << "Hide" << std::endl; + } else { + os << "Show" << std::endl; + } + break; + } + case PRINT_IFPP: + os << '[' << guid << "] Rule "; + if (useless) os << "USELESS "; + print(os, ps, tags) << '{' << std::endl; + break; + + default: + throw UnhandledCase("Print style", __FILE__, __LINE__); + } + + ++IFPP_TABS; + for (const auto & c : conditions) { + print(os, ps, c.second); + } + for (const auto & a : actions) { + print(os, ps, a.second); + } + --IFPP_TABS; + + if (ps == PRINT_IFPP) { + os << '}' << std::endl; + } + return os; +} + +RuleNative * RuleNative::clone() const { + // We do not need to do checking, just copy the conditions and actions. + // Beware, this might break if addCondition or addAction start to do other things. + RuleNative * r = new RuleNative(tags); + r->useless = useless; + + for (const auto & c : conditions) { + r->conditions.insert(std::make_pair(c.first, static_cast(c.second->clone()))); + } + for (const auto & a : actions) { + r->actions.insert(std::make_pair(a.first, static_cast(a.second->clone()))); + } + + return r; +} + +RuleNative::~RuleNative() { + for (auto & c : conditions) delete c.second; + for (auto & a : actions) delete a.second; +} + +std::ostream & print(std::ostream & os, PrintStyle ps, const FilterNative & f) { + for (size_t i = 0; i < f.size(); ++i) { + if (i) os << std::endl; + print(os, ps, f[i]); + } + return os; +} + +} \ No newline at end of file diff --git a/src/RuleNative.h b/src/RuleNative.h new file mode 100644 index 0000000..102d58d --- /dev/null +++ b/src/RuleNative.h @@ -0,0 +1,46 @@ +#ifndef IFPP_RULENATIVE_H +#define IFPP_RULENATIVE_H + +#include "Types.h" +#include +#include +#include + +namespace ifpp { + +// Native rule, but keeps tags. +// Only one action per type allowed. +// No nested rules. +struct RuleNative { + RuleNative(TagList t = 0) : tags(t), conditions(), actions(), useless(false), guid(IFPP_GUID()) {} + + void addCondition(const Condition * c); + void addAction(const Action * a); + + bool hasTag(TagList t) const; + std::ostream & printSelf(std::ostream & os, PrintStyle ps) const; + RuleNative * clone() const; + ~RuleNative(); + + TagList tags; + + // There can be more than one of certain conditions (lists, socketGroups). + // We assume that there is at most one of others (interval, bool). + std::multimap conditions; + + // Only the last action of each type is preserved. + std::map actions; + + // True if the rule does not match anything. + // In this case we do not guarantee that the list of conditions will be anything sensible. + bool useless; + + int guid; +}; + +typedef std::vector FilterNative; +std::ostream & print(std::ostream & os, PrintStyle ps, const FilterNative & f); + +} + +#endif \ No newline at end of file diff --git a/src/RuleOperations.cpp b/src/RuleOperations.cpp index 07f0e62..eca7c05 100644 --- a/src/RuleOperations.cpp +++ b/src/RuleOperations.cpp @@ -145,7 +145,7 @@ static bool contains(const std::vector & v, const T & t) { static const std::vector special = {"Class", "BaseType"}; RuleNative * RuleIntersection(const RuleNative * first, const RuleNative * second) { - if (first->hasMod(MOD_FINAL)) { + if (first->hasTag(MOD_FINAL)) { // First rule is Final and can not be overridden. return const_cast(first); } @@ -154,7 +154,7 @@ RuleNative * RuleIntersection(const RuleNative * first, const RuleNative * secon // Preserve modifiers - Final. // At this point Show or Hide do not exist, // they get deleted when we transform an IFPP rule into a native one and then rebuilt when we print it. - RuleNative * result = new RuleNative(second->hasMod(MOD_FINAL) ? MOD_FINAL : 0); + RuleNative * result = new RuleNative(second->hasTag(MOD_FINAL) ? MOD_FINAL : 0); // All other conditions are intersected by simply adding them to the rule. for (const auto & c : first->conditions) { @@ -219,7 +219,7 @@ RuleNative * RuleIntersection(const RuleNative * first, const RuleNative * secon // We want to know if it actually changes the rule it overrides. // It might not, because all of first's actions are Final, or all of second's actions are Append. bool changed = false; - if (second->hasMod(MOD_FINAL)) { + if (second->hasTag(MOD_FINAL)) { // The new rule adds a Final modifier to the previous rule. changed = true; } @@ -250,8 +250,8 @@ RuleNative * RuleIntersection(const RuleNative * first, const RuleNative * secon const Action * a1 = a.second.first; const Action * a2 = a.second.second; - if (second->hasMod(MOD_OVERRIDE)) { - if (a1 && a1->hasMod(MOD_FINAL)) { + if (second->hasTag(TAG_OVERRIDE)) { + if (a1 && a1->hasTag(MOD_FINAL)) { // First action is final, do not change. result->addAction(a1); } else if (a2) { @@ -266,10 +266,10 @@ RuleNative * RuleIntersection(const RuleNative * first, const RuleNative * secon // First action is not defined, second is. result->addAction(a2); changed = true; - } else if (a1 && a1->hasMod(MOD_FINAL)) { + } else if (a1 && a1->hasTag(MOD_FINAL)) { // First action is Final. result->addAction(a1); - } else if (a2 && a2->hasMod(MOD_OVERRIDE)) { + } else if (a2 && a2->hasTag(TAG_OVERRIDE)) { // Second action is Override. result->addAction(a2); changed = true; diff --git a/src/RuleOperations.h b/src/RuleOperations.h index 685c342..0dd99cc 100644 --- a/src/RuleOperations.h +++ b/src/RuleOperations.h @@ -1,8 +1,8 @@ #ifndef IFPP_RULE_OPERATIONS_H #define IFPP_RULE_OPERATIONS_H -#include "ifppTypes.h" -#include "ifppCompiler.h" +#include "Types.h" +#include "Compiler.h" namespace ifpp { diff --git a/src/Types.cpp b/src/Types.cpp index c31cc4e..c532231 100644 --- a/src/Types.cpp +++ b/src/Types.cpp @@ -64,8 +64,8 @@ std::ostream & operator<<(std::ostream & os, const Color & c) { } SocketGroup::SocketGroup(const std::string & sockets) : r(0), g(0), b(0), w(0) { - for (size_t i = 0; i < sockets.size(); ++i) { - switch (sockets[i]) { + for (char s : sockets) { + switch (s) { case 'r': case 'R': ++r; break; case 'g': case 'G': ++g; break; case 'b': case 'B': ++b; break; @@ -94,46 +94,28 @@ std::ostream & operator<<(std::ostream & os, const NameList & nl) { return os; } -std::string modString(ModifierList ml) { - if (ml == MOD_OVERRIDE) return "Override"; - if (ml == MOD_APPEND) return "Append"; - if (ml == MOD_FINAL) return "Final"; - if (ml == MOD_ADDONLY) return "Final"; - if (ml == MOD_SHOW) return "Show"; - if (ml == MOD_HIDE) return "Hide"; - throw InternalError("Attempting to convert unknown or compound modifier to string!", __FILE__, __LINE__); -} - -std::ostream & print(std::ostream & os, PrintStyle ps, ModifierList ml) { +std::ostream & print(std::ostream & os, PrintStyle ps, TagList t) { switch (ps) { - case PRINT_IFPP: - if (ml & MOD_OVERRIDE) os << "Override "; - if (ml & MOD_APPEND) os << "Append "; - if (ml & MOD_FINAL) os << "Final "; - if (ml & MOD_ADDONLY) os << "AddOnly "; - if (ml & MOD_SHOW) os << "Show "; - if (ml & MOD_HIDE) os << "Hide "; - return os; - case PRINT_NATIVE: throw InternalError("Attempting to write modifiers to native filter!", __FILE__, __LINE__); + case PRINT_IFPP: return os << std::string(IFPP_TABS, '\t') << (t & TAG_OVERRIDE ? "Override" : ""); + case PRINT_NATIVE: throw InternalError("Attempting to write IFPP-only modifier to native filter!", __FILE__, __LINE__); default: throw UnhandledCase("Print style", __FILE__, __LINE__); } } - /*********** * ENUMS ***********/ -std::ostream & operator<<(std::ostream & os, VariableType vt) { - switch (vt) { - case VAR_NUMBER: return os << "Number"; - case VAR_COLOR: return os << "Color"; - case VAR_FILE: return os << "File"; - case VAR_LIST: return os << "List"; - case VAR_STYLE: return os << "Style"; - case VAR_UNDEFINED: return os << "Undefined"; - default: throw UnhandledCase("Variable type", __FILE__, __LINE__); +std::ostream & operator<<(std::ostream & os, ExprType et) { + switch (et) { + case EXPR_NUMBER: return os << "Number"; + case EXPR_COLOR: return os << "Color"; + case EXPR_FILE: return os << "File"; + case EXPR_LIST: return os << "List"; + case EXPR_STYLE: return os << "Style"; + case EXPR_ERROR: return os << "Error"; + default: throw UnhandledCase("Expression type", __FILE__, __LINE__); } } @@ -148,9 +130,59 @@ std::ostream & operator<<(std::ostream & os, Operator o) { } } +/*********** +* STATEMENTS +***********/ + +std::ostream & Statement::printSelf(std::ostream & os, PrintStyle ps) const { + switch (ps) { + case PRINT_IFPP: return os << std::string(IFPP_TABS, '\t') << "[" << guid << "] "; + case PRINT_NATIVE: return os << std::string(IFPP_TABS, '\t'); + default: throw UnhandledCase("Print style", __FILE__, __LINE__); + } +} + +std::ostream & print(std::ostream & os, PrintStyle ps, const FilterIFPP & f) { + bool first = true; + for (const auto & stm : f) { + if (!first) os << std::endl; + first = false; + print(os, ps, stm); + } + return os; +} + +std::ostream & Instruction::printSelf(std::ostream & os, PrintStyle ps) const { + switch (ps) { + case PRINT_IFPP: return Statement::printSelf(os, ps) << what; + case PRINT_NATIVE: throw InternalError("Attempting to write IFPP-only instruction to native filter!", __FILE__, __LINE__); + default: throw UnhandledCase("Print style", __FILE__, __LINE__); + } +} + +std::ostream & InstructionVersion::printSelf(std::ostream & os, PrintStyle ps) const { + return Instruction::printSelf(os, ps) << ' ' << vMajor << '.' << vMinor << '.' << vPatch << std::endl; +} + +InstructionVersion * InstructionVersion::clone() const { + return new InstructionVersion(vMajor, vMinor, vPatch); +} + +std::ostream & DefinitionBase::printSelf(std::ostream & os, PrintStyle ps) const { + switch (ps) { + case PRINT_IFPP: return Statement::printSelf(os, ps) << "Define " << varName << ' ' << varType /*<< ' ' << value*/; + case PRINT_NATIVE: throw InternalError("Attempting to write a variable definition to native filter!", __FILE__, __LINE__); + default: throw UnhandledCase("Print style", __FILE__, __LINE__); + } +} + +/*********** +* COMMANDS +***********/ + std::ostream & Command::printSelf(std::ostream & os, PrintStyle ps) const { switch (ps) { - case PRINT_IFPP: return os << std::string(IFPP_TABS, '\t') << '[' << guid << "] "; + case PRINT_IFPP: return os << std::string(IFPP_TABS, '\t') << "[" << guid << "] "; case PRINT_NATIVE: return os << std::string(IFPP_TABS, '\t'); default: throw UnhandledCase("Print style", __FILE__, __LINE__); } @@ -206,7 +238,7 @@ std::ostream & ConditionInterval::printSelf(std::ostream & os, PrintStyle ps) co } ConditionInterval * ConditionInterval::clone() const { - return new ConditionInterval(what, from, to); + return new ConditionInterval(what, from, to, tags); } std::ostream & ConditionNameList::printSelf(std::ostream & os, PrintStyle ps) const { @@ -214,7 +246,7 @@ std::ostream & ConditionNameList::printSelf(std::ostream & os, PrintStyle ps) co } ConditionNameList * ConditionNameList::clone() const { - return new ConditionNameList(what, nameList); + return new ConditionNameList(what, nameList, tags); } std::ostream & ConditionSocketGroup::printSelf(std::ostream & os, PrintStyle ps) const { @@ -222,7 +254,7 @@ std::ostream & ConditionSocketGroup::printSelf(std::ostream & os, PrintStyle ps) } ConditionSocketGroup * ConditionSocketGroup::clone() const { - return new ConditionSocketGroup(what, socketGroup); + return new ConditionSocketGroup(what, socketGroup, tags); } std::ostream & ConditionBool::printSelf(std::ostream & os, PrintStyle ps) const { @@ -230,7 +262,7 @@ std::ostream & ConditionBool::printSelf(std::ostream & os, PrintStyle ps) const } ConditionBool * ConditionBool::clone() const { - return new ConditionBool(what, value); + return new ConditionBool(what, value, tags); } @@ -242,17 +274,13 @@ ConditionBool * ConditionBool::clone() const { std::ostream & Action::printSelf(std::ostream & os, PrintStyle ps) const { Command::printSelf(os, ps); switch (ps) { - case PRINT_IFPP: print(os, ps, modifiers); break; + case PRINT_IFPP: print(os, ps, tags); break; case PRINT_NATIVE: break; default: throw UnhandledCase("Print style", __FILE__, __LINE__); } return os << what; } -bool Action::hasMod(ModifierList ml) const { - return modifiers & ml; -} - std::ostream & operator<<(std::ostream & os, const Style & s) { for (const auto & a : s) { a->printSelf(os, PRINT_IFPP); @@ -260,116 +288,110 @@ std::ostream & operator<<(std::ostream & os, const Style & s) { return os; } -std::ostream & DefaultStyle::printSelf(std::ostream & os, PrintStyle ps) const { +std::ostream & Ignore::printSelf(std::ostream & os, PrintStyle ps) const { switch (ps) { case PRINT_IFPP: Command::printSelf(os, ps); - os << what << '{' << std::endl; - ++IFPP_TABS; - print(os, ps, style); - --IFPP_TABS; - os << std::string(IFPP_TABS, '\t') << '}' << std::endl; - return os; + return os << what << std::endl; case PRINT_NATIVE: - throw InternalError("Attempting to print a Default action to native filter!", __FILE__, __LINE__); + throw InternalError("Attempting to print a DefaultIgnore to native filter!", __FILE__, __LINE__); default: throw UnhandledCase("Print style", __FILE__, __LINE__); } } -DefaultStyle * DefaultStyle::clone() const { - Style s; - for (const auto & a : style) s.push_back(a->clone()); - return new DefaultStyle(s); +Ignore * Ignore::clone() const { + return new Ignore(tags); } /*********** -* STATEMENTS +* RULES ***********/ -std::ostream & Statement::printSelf(std::ostream & os, PrintStyle ps) const { - switch (ps) { - case PRINT_IFPP: return os << std::string(IFPP_TABS, '\t') << "[" << guid << "] "; - case PRINT_NATIVE: return os << std::string(IFPP_TABS, '\t'); - default: throw UnhandledCase("Print style", __FILE__, __LINE__); - } -} - -std::ostream & print(std::ostream & os, PrintStyle ps, const FilterIFPP & f) { - bool first = true; - for (const auto & stm : f) { - if (!first) os << std::endl; - first = false; - print(os, ps, stm); - } - return os; -} - -std::ostream & Instruction::printSelf(std::ostream & os, PrintStyle ps) const { +std::ostream & RuleIFPP::printSelf(std::ostream & os, PrintStyle ps) const { switch (ps) { - case PRINT_IFPP: return Statement::printSelf(os, ps) << what; - case PRINT_NATIVE: throw InternalError("Attempting to write IFPP-only instruction to native filter!", __FILE__, __LINE__); + case PRINT_IFPP: + Statement::printSelf(os, ps) << "Rule "; + print(os, ps, tags) << '{' << std::endl; + ++IFPP_TABS; + print(os, ps, commands); + --IFPP_TABS; + os << std::string(IFPP_TABS, '\t') << '}' << std::endl; + return os; + case PRINT_NATIVE: throw InternalError("Attempting to write an IFPP rule to native filter!", __FILE__, __LINE__); default: throw UnhandledCase("Print style", __FILE__, __LINE__); } } -std::ostream & InstructionFlush::printSelf(std::ostream & os, PrintStyle ps) const { - return Instruction::printSelf(os, ps) << std::endl; +RuleIFPP * RuleIFPP::clone() const { + RuleIFPP * r = new RuleIFPP(tags); + r->commands.reserve(commands.size()); + for (const auto c : commands) r->commands.push_back(c->clone()); + return r; } -InstructionFlush * InstructionFlush::clone() const { - return new InstructionFlush(); +RuleIFPP::~RuleIFPP() { + for (const auto c : commands) delete c; } -std::ostream & InstructionVersion::printSelf(std::ostream & os, PrintStyle ps) const { - return Instruction::printSelf(os, ps) << ' ' << vMajor << '.' << vMinor << '.' << vPatch << std::endl; +std::ostream & Modifier::printSelf(std::ostream & os, PrintStyle ps) const { + switch (ps) { + case PRINT_IFPP: + Command::printSelf(os, ps); + os << what << " {" << std::endl; + ++IFPP_TABS; + print(os, ps, commands); + --IFPP_TABS; + os << std::string(IFPP_TABS, '\t') << '}' << std::endl; + return os; + case PRINT_NATIVE: + throw InternalError("Attempting to print a Modifier to native filter!", __FILE__, __LINE__); + default: + throw UnhandledCase("Print style", __FILE__, __LINE__); + } } -InstructionVersion * InstructionVersion::clone() const { - return new InstructionVersion(vMajor, vMinor, vPatch); +Modifier * Modifier::clone() const { + Modifier * m = new Modifier(tags); + m->commands.reserve(commands.size()); + for (const auto c : commands) m->commands.push_back(c->clone()); + return m; } - -std::ostream & DefinitionBase::printSelf(std::ostream & os, PrintStyle ps) const { - switch (ps) { - case PRINT_IFPP: return Statement::printSelf(os, ps) << "Define " << varName << ' ' << varType; - case PRINT_NATIVE: throw InternalError("Attempting to write a variable definition to native filter!", __FILE__, __LINE__); - default: throw UnhandledCase("Print style", __FILE__, __LINE__); - } +Modifier::~Modifier() { + for (auto c : commands) delete c; } -std::ostream & RuleIFPP::printSelf(std::ostream & os, PrintStyle ps) const { - switch (ps) { +std::ostream & DefaultStyle::printSelf(std::ostream & os, PrintStyle ps) const { + switch (ps) { case PRINT_IFPP: - Statement::printSelf(os, ps) << "Rule "; - print(os, ps, modifiers) << '{' << std::endl; + Command::printSelf(os, ps); + os << what << " {" << std::endl; ++IFPP_TABS; - print(os, ps, commands); + print(os, ps, style); --IFPP_TABS; os << std::string(IFPP_TABS, '\t') << '}' << std::endl; return os; - case PRINT_NATIVE: throw InternalError("Attempting to write an IFPP rule to native filter!", __FILE__, __LINE__); - default: throw UnhandledCase("Print style", __FILE__, __LINE__); + case PRINT_NATIVE: + throw InternalError("Attempting to print a Default rule to native filter!", __FILE__, __LINE__); + default: + throw UnhandledCase("Print style", __FILE__, __LINE__); } } -RuleIFPP * RuleIFPP::clone() const { - RuleIFPP * r = new RuleIFPP(modifiers); - r->commands.reserve(commands.size()); - for (const auto & c : commands) { - r->commands.push_back(c->clone()); - } - return r; +DefaultStyle * DefaultStyle::clone() const { + DefaultStyle * ds = new DefaultStyle(tags); + ds->style.reserve(style.size()); + for (const auto a : style) ds->style.push_back(a->clone()); + return ds; } -bool RuleIFPP::hasMod(ModifierList ml) const { - return modifiers & ml; +DefaultStyle::~DefaultStyle() { + for (auto a : style) delete a; } -RuleIFPP::~RuleIFPP() { - for (const auto & c : commands) delete c; -} + int getLimit(const std::string & what, WhichLimit which) { static std::map > limits; @@ -395,7 +417,7 @@ int getLimit(const std::string & what, WhichLimit which) { limits["MinimapIcon"] = std::make_pair(0, 2); defaults["Color"] = 255; - defaults["Volume"] = 100; // ??? + defaults["Volume"] = 300; // LOUDER defaults["FontSize"] = 33; } try { diff --git a/src/Types.h b/src/Types.h index f021fd5..d30066b 100644 --- a/src/Types.h +++ b/src/Types.h @@ -73,15 +73,6 @@ std::ostream & operator<<(std::ostream & os, const SocketGroup & sg); typedef std::vector NameList; std::ostream & operator<<(std::ostream & os, const NameList & nl); -typedef unsigned int ModifierList; -const unsigned int MOD_OVERRIDE = 1 << 0; -const unsigned int MOD_APPEND = 1 << 1; -const unsigned int MOD_FINAL = 1 << 2; -const unsigned int MOD_ADDONLY = 1 << 3; -const unsigned int MOD_SHOW = 1 << 10; -const unsigned int MOD_HIDE = 1 << 11; -std::string modString(ModifierList ml); -std::ostream & print(std::ostream & os, PrintStyle ps, ModifierList ml); /*********** @@ -89,22 +80,116 @@ std::ostream & print(std::ostream & os, PrintStyle ps, ModifierList ml); ***********/ enum StatementType { STM_DEFINITION, STM_INSTRUCTION, STM_RULE }; -enum CommandType { COM_CONDITION, COM_ACTION, COM_RULE, COM_DEFAULT }; +enum CommandType { COM_CONDITION, COM_ACTION, COM_RULE, COM_MODIFIER, COM_DEFAULT, COM_IGNORE }; enum ConditionType { CON_INTERVAL, CON_RARITY, CON_BOOL, CON_NAMELIST, CON_SOCKETGROUP }; -//enum ActionType { AC_COLOR, AC_NUMBER, AC_SOUND, AC_FILENAME, AC_BOOLEAN, AC_STYLE }; - -enum VariableType { VAR_NUMBER, VAR_COLOR, VAR_FILE, VAR_LIST, VAR_STYLE, VAR_UNDEFINED }; -std::ostream & operator<<(std::ostream & os, VariableType vt); +enum ActionType { AC_NUMBER, AC_COLOR, AC_FILE, AC_SOUND, AC_BOOLEAN, AC_STYLE }; +enum ExprType { EXPR_NUMBER, EXPR_COLOR, EXPR_FILE, EXPR_LIST, EXPR_STYLE, EXPR_ERROR, EXPR_UNDEFINED }; enum Operator { OP_LT, OP_LE, OP_EQ, OP_GE, OP_GT }; std::ostream & operator<<(std::ostream & os, Operator o); -struct Command { - CommandType comType; +typedef unsigned int TagList; +const unsigned int TAG_OVERRIDE = 1 << 0; +/*const unsigned int MOD_APPEND = 1 << 1; +const unsigned int MOD_FINAL = 1 << 2; +const unsigned int MOD_ADDONLY = 1 << 3; +const unsigned int MOD_SHOW = 1 << 10; +const unsigned int MOD_HIDE = 1 << 11;*/ +std::ostream & print(std::ostream & os, PrintStyle ps, TagList t); + +/* +Statements - top level commands. +This can be: +- Instruction + - Version + - TODO: conditional compilation +- Definition +- Rule +- Modifier +*/ + +struct Statement { + int guid; + StatementType stmType; + + Statement(StatementType t) : guid(IFPP_GUID()), stmType(t) {} + virtual std::ostream & printSelf(std::ostream & os, PrintStyle ps) const; + virtual Statement * clone() const = 0; + virtual ~Statement() {} +}; +typedef std::vector FilterIFPP; +std::ostream & print(std::ostream & os, PrintStyle ps, const FilterIFPP & f); + +struct Instruction : public Statement { std::string what; - int guid; - Command(CommandType t, const std::string & w) : comType(t), what(w), guid(IFPP_GUID()) {} + Instruction(const std::string & w) : Statement(STM_INSTRUCTION), what(w) {} + virtual std::ostream & printSelf(std::ostream & os, PrintStyle ps) const override; +}; + +struct InstructionVersion : public Instruction { + int vMajor, vMinor, vPatch; // Minor and major are reserved by GCC? + + InstructionVersion(int M, int m, int p) : + Instruction("Version"), vMajor(M), vMinor(m), vPatch(p) {} + std::ostream & printSelf(std::ostream & os, PrintStyle ps) const override; + InstructionVersion * clone() const override; +}; + +/* +struct Definition : public Statement { + std::string varName; + ExprType varType; + Expression value; + + Definition(const std::string & vn, ExprType vt, const Expression * value); + std::ostream & printSelf(std::ostream & os, PrintStyle ps) const override; +}; +*/ + +struct DefinitionBase : public Statement { + std::string varName; + ExprType varType; + DefinitionBase(const std::string & vn, ExprType et) : + Statement(STM_DEFINITION), varName(vn), varType(et) {} + virtual std::ostream & printSelf(std::ostream & os, PrintStyle ps) const override; +}; + +template +struct Definition : public DefinitionBase { + T value; + + Definition(const std::string & vn, ExprType et, const T & v) : + DefinitionBase(vn, et), value(v) {} + std::ostream & printSelf(std::ostream & os, PrintStyle ps) const override { + return DefinitionBase::printSelf(os, ps) << ' ' << value << std::endl << std::endl; + } + Definition * clone() const override { + return new Definition(varName, varType, value); + } +}; + +// DefinitionStyle - see below after Actions + + +/* +Commands - parts of a rule. +This can be: +- Condition +- Action +- Rule +- Modifier +*/ + +struct Command { + int guid; + CommandType comType; + std::string what; + TagList tags; + + Command(CommandType ct, const std::string & w, TagList t = 0) : + guid(IFPP_GUID()), comType(ct), what(w), tags(t) {} + bool hasTag(TagList t) const { return tags & t; } virtual std::ostream & printSelf(std::ostream & os, PrintStyle ps) const; virtual Command * clone() const = 0; virtual ~Command() {} @@ -120,16 +205,16 @@ typedef std::vector CommandList; struct Condition : public Command { ConditionType conType; - Condition(ConditionType t, const std::string & w) : - Command(COM_CONDITION, w), conType(t) {} + Condition(ConditionType ct, const std::string & w, TagList t = 0) : + Command(COM_CONDITION, w, t), conType(ct) {} virtual std::ostream & printSelf(std::ostream & os, PrintStyle ps) const override; }; struct ConditionInterval : public Condition { int from, to; - ConditionInterval(const std::string & w, int f, int t) : - Condition(CON_INTERVAL, w), from(f), to(t) {} + ConditionInterval(const std::string & w, int f, int T, TagList t = 0) : + Condition(CON_INTERVAL, w, t), from(f), to(T) {} std::ostream & printSelf(std::ostream & os, PrintStyle ps) const override; ConditionInterval * clone() const override; }; @@ -137,8 +222,8 @@ struct ConditionInterval : public Condition { struct ConditionBool : public Condition { bool value; - ConditionBool(const std::string & w, bool v) : - Condition(CON_BOOL, w), value(v) {} + ConditionBool(const std::string & w, bool v, TagList t = 0) : + Condition(CON_BOOL, w, t), value(v) {} std::ostream & printSelf(std::ostream & os, PrintStyle ps) const override; ConditionBool * clone() const override; }; @@ -146,8 +231,8 @@ struct ConditionBool : public Condition { struct ConditionNameList : public Condition { NameList nameList; - ConditionNameList(const std::string & w, const NameList & nl) : - Condition(CON_NAMELIST, w), nameList(nl) {} + ConditionNameList(const std::string & w, const NameList & nl, TagList t = 0) : + Condition(CON_NAMELIST, w, t), nameList(nl) {} std::ostream & printSelf(std::ostream & os, PrintStyle ps) const override; ConditionNameList * clone() const override; }; @@ -155,8 +240,8 @@ struct ConditionNameList : public Condition { struct ConditionSocketGroup : public Condition { SocketGroup socketGroup; - ConditionSocketGroup(const std::string & w, const SocketGroup & sg) : - Condition(CON_SOCKETGROUP, w), socketGroup(sg) {} + ConditionSocketGroup(const std::string & w, const SocketGroup & sg, TagList t = 0) : + Condition(CON_SOCKETGROUP, w, t), socketGroup(sg) {} std::ostream & printSelf(std::ostream & os, PrintStyle ps) const override; ConditionSocketGroup * clone() const override; }; @@ -168,28 +253,42 @@ struct ConditionSocketGroup : public Condition { ***********/ struct Action : public Command { - ModifierList modifiers; - - Action(ModifierList m, const std::string & w) : - Command(COM_ACTION, w), modifiers(m) {} + Action(const std::string & w, TagList t = 0) : + Command(COM_ACTION, w, t) {} virtual std::ostream & printSelf(std::ostream & os, PrintStyle ps) const override; virtual Action * clone() const override = 0; - bool hasMod(ModifierList ml) const; }; typedef std::vector Style; std::ostream & operator<<(std::ostream & os, const Style & s); +template<> +struct Definition