1 /* ConditionSet.h
2 Copyright (c) 2014 by Michael Zahniser
3 
4 Endless Sky is free software: you can redistribute it and/or modify it under the
5 terms of the GNU General Public License as published by the Free Software
6 Foundation, either version 3 of the License, or (at your option) any later version.
7 
8 Endless Sky is distributed in the hope that it will be useful, but WITHOUT ANY
9 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
10 PARTICULAR PURPOSE.  See the GNU General Public License for more details.
11 */
12 
13 #ifndef CONDITION_SET_H_
14 #define CONDITION_SET_H_
15 
16 #include <map>
17 #include <string>
18 #include <vector>
19 
20 class DataNode;
21 class DataWriter;
22 
23 
24 
25 // A condition set is a collection of operations on the player's set of named
26 // "conditions". This includes "test" operations that just check the values of
27 // those conditions, and other operations that can be "applied" to change the
28 // values.
29 class ConditionSet {
30 public:
31 	using Conditions = std::map<std::string, int64_t>;
32 	ConditionSet() noexcept = default;
33 	// Construct and Load() at the same time.
34 	ConditionSet(const DataNode &node);
35 
36 	// Load a set of conditions from the children of this node. Prints a
37 	// warning if an and/or node contains assignment expressions.
38 	void Load(const DataNode &node);
39 	// Save a set of conditions.
40 	void Save(DataWriter &out) const;
41 
42 	// Check if there are any entries in this set.
43 	bool IsEmpty() const;
44 
45 	// Read a single condition from a data node.
46 	void Add(const DataNode &node);
47 	bool Add(const std::string &firstToken, const std::string &secondToken);
48 	// Add simple conditions having only a single operator.
49 	bool Add(const std::string &name, const std::string &op, const std::string &value);
50 	// Add complex conditions having multiple operators, including parentheses.
51 	bool Add(const std::vector<std::string> &lhs, const std::string &op, const std::vector<std::string> &rhs);
52 
53 	// Check if the given condition values satisfy this set of expressions. First applies
54 	// all assignment expressions to create any temporary conditions, then evaluates.
55 	bool Test(const Conditions &conditions) const;
56 	// Modify the given set of conditions with this ConditionSet.
57 	// (Order of operations is like the order of specification: all sibling
58 	// expressions are applied, then any and/or nodes are applied.)
59 	void Apply(Conditions &conditions) const;
60 
61 
62 private:
63 	// Compare this set's expressions and the union of created and supplied conditions.
64 	bool TestSet(const Conditions &conditions, const Conditions &created) const;
65 	// Evaluate this set's assignment expressions and store the result in "created" (for use by TestSet).
66 	void TestApply(const Conditions &conditions, Conditions &created) const;
67 
68 
69 private:
70 	// This class represents a single expression involving a condition,
71 	// either testing what value it has, or modifying it in some way.
72 	class Expression {
73 	public:
74 		Expression(const std::vector<std::string> &left, const std::string &op, const std::vector<std::string> &right);
75 		Expression(const std::string &left, const std::string &op, const std::string &right);
76 
77 		void Save(DataWriter &out) const;
78 		// Convert this expression into a string, for traces.
79 		std::string ToString() const;
80 
81 		// Determine if this Expression instantiated properly.
82 		bool IsEmpty() const;
83 
84 		// Returns the left side of this Expression.
85 		std::string Name() const;
86 		// True if this Expression performs a comparison and false if it performs an assignment.
87 		bool IsTestable() const;
88 
89 		// Functions to use this expression:
90 		bool Test(const Conditions &conditions, const Conditions &created) const;
91 		void Apply(Conditions &conditions, Conditions &created) const;
92 		void TestApply(const Conditions &conditions, Conditions &created) const;
93 
94 
95 	private:
96 		// A SubExpression results from applying operator-precedence parsing to one side of
97 		// an Expression. The operators and tokens needed to recreate the given side are
98 		// stored, and can be interleaved to restore the original string. Based on them, a
99 		// sequence of "Operations" is created for runtime evaluation.
100 		class SubExpression {
101 		public:
102 			SubExpression(const std::vector<std::string> &side);
103 			SubExpression(const std::string &side);
104 
105 			// Interleave tokens and operators to reproduce the initial string.
106 			const std::string ToString() const;
107 			// Interleave tokens and operators, but do not combine.
108 			const std::vector<std::string> ToStrings() const;
109 
110 			bool IsEmpty() const;
111 
112 			// Substitute numbers for any string values and then compute the result.
113 			int64_t Evaluate(const Conditions &conditions, const Conditions &created) const;
114 
115 
116 		private:
117 			void ParseSide(const std::vector<std::string> &side);
118 			void GenerateSequence();
119 			bool AddOperation(std::vector<int> &data, size_t &index, const size_t &opIndex);
120 
121 
122 		private:
123 			// An Operation has a pointer to its binary function, and the data indices for
124 			// its operands. The result is always placed on the back of the data vector.
125 			class Operation {
126 			public:
127 				explicit Operation(const std::string &op, size_t &a, size_t &b);
128 
129 				int64_t (*fun)(int64_t, int64_t);
130 				size_t a;
131 				size_t b;
132 			};
133 
134 
135 		private:
136 			// Iteration of the sequence vector yields the result.
137 			std::vector<Operation> sequence;
138 			// The tokens vector converts into a data vector of numeric values during evaluation.
139 			std::vector<std::string> tokens;
140 			std::vector<std::string> operators;
141 			// The number of true (non-parentheses) operators.
142 			int operatorCount = 0;
143 		};
144 
145 
146 	private:
147 		// String representation of the Expression's binary function.
148 		std::string op;
149 		// Pointer to a binary function that defines the assignment or
150 		// comparison operation to be performed between SubExpressions.
151 		int64_t (*fun)(int64_t, int64_t);
152 
153 		// SubExpressions contain one or more tokens and any number of simple operators.
154 		SubExpression left;
155 		SubExpression right;
156 	};
157 
158 
159 private:
160 	// Sets of condition tests can contain nested sets of tests. Each set is
161 	// either an "and" grouping (meaning every condition must be true to satisfy
162 	// it) or an "or" grouping where only one condition needs to be true.
163 	bool isOr = false;
164 	// If this set contains assignment expressions. If true, the Test()
165 	// method must first apply them before testing any conditions.
166 	bool hasAssign = false;
167 	// Conditions that this set tests or applies.
168 	std::vector<Expression> expressions;
169 	// Nested sets of conditions to be tested.
170 	std::vector<ConditionSet> children;
171 };
172 
173 
174 
175 #endif
176