/
DeclarationBase.h
210 lines (169 loc) · 5.79 KB
/
DeclarationBase.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
#pragma once
#include "ideclmanager.h"
#include "parser/DefTokeniser.h"
namespace decl
{
/**
* Base declaration implementation shared by all decls supported by DarkRadiant.
*
* To avoid subclasses having to inherit the IDeclaration interface themselves
* (and to circumvent the diamond pattern and virtual inheritance), subclasses
* should pass the desired interface as template argument:
*
* class SoundShader : public DeclarationBase<ISoundShader>
* {}
*
* Declaration types that can be modified through its public API should inherit
* and implement the EditableDeclaration<> template.
*/
template<typename DeclarationInterface>
class DeclarationBase :
public DeclarationInterface
{
private:
static_assert(std::is_base_of_v<IDeclaration, DeclarationInterface>,
"DeclarationInterface type must inherit from IDeclaration");
std::string _name;
std::string _originalName; // the original name as parsed from the file
decl::Type _type;
std::size_t _parseStamp;
// The raw unparsed definition block
DeclarationBlockSyntax _declBlock;
bool _parsed;
std::string _parseErrors;
sigc::signal<void> _changedSignal;
protected:
DeclarationBase(decl::Type type, const std::string& name) :
_name(name),
_originalName(name),
_type(type),
_parseStamp(0),
_parsed(false)
{}
DeclarationBase(const DeclarationBase<DeclarationInterface>& other) = default;
public:
const std::string& getDeclName() const final
{
return _name;
}
void setDeclName(const std::string& newName) override
{
_name = newName;
_declBlock.name = newName;
}
const std::string& getOriginalDeclName() const final
{
return _originalName;
}
void setOriginalDeclName(const std::string& newName) override
{
_originalName = newName;
}
decl::Type getDeclType() const final
{
return _type;
}
const DeclarationBlockSyntax& getBlockSyntax() override
{
return _declBlock;
}
void setBlockSyntax(const DeclarationBlockSyntax& block) final
{
_declBlock = block;
// Reset the parsed flag and notify the subclasses
_parsed = false;
onSyntaxBlockAssigned(_declBlock);
_changedSignal.emit();
}
std::string getModName() const final
{
return _declBlock.getModName();
}
std::string getDeclFilePath() const final
{
return _declBlock.fileInfo.fullPath();
}
void setFileInfo(const vfs::FileInfo& fileInfo) override
{
_declBlock.fileInfo = fileInfo;
}
std::size_t getParseStamp() const final
{
return _parseStamp;
}
void setParseStamp(std::size_t parseStamp) final
{
_parseStamp = parseStamp;
}
sigc::signal<void>& signal_DeclarationChanged() final
{
return _changedSignal;
}
const std::string& getParseErrors()
{
ensureParsed();
return _parseErrors;
}
protected:
// Defines the whitespace characters used by the DefTokeniser to separate tokens
virtual const char* getWhitespaceDelimiters() const
{
return parser::WHITESPACE;
}
// Defines the characters separating tokens and are considered tokens themselves
virtual const char* getKeptDelimiters() const
{
return "{}()";
}
// Subclasses should call this to ensure the attached syntax block has been processed.
// In case the block needs parsing, the parseFromTokens() method will be invoked,
// followed by an onParseFinished() call (the latter of which is invoked regardless
// of any parse exceptions that might have been occurring).
void ensureParsed()
{
if (_parsed) return;
// Set the flag to true before parsing, to avoid infinite loops
_parsed = true;
_parseErrors.clear();
onBeginParsing();
try
{
// Set up a tokeniser to let the subclass implementation parse the contents
parser::BasicDefTokeniser<std::string> tokeniser(getBlockSyntax().contents,
getWhitespaceDelimiters(), getKeptDelimiters());
parseFromTokens(tokeniser);
}
catch (const parser::ParseException& ex)
{
_parseErrors = ex.what();
rError() << "[DeclParser]: Error parsing " << getTypeName(getDeclType()) << " " << getDeclName()
<< ": " << ex.what() << std::endl;
}
onParsingFinished();
}
// Optional callback to be overridden by subclasses.
// Will always be called before parseFromTokens().
virtual void onBeginParsing()
{}
// To be implemented by subclasses, this method will parse the contents of the decl block
// and extract the relevant information. After parseFromTokens() a call to onParseFinished()
// will follow, unconditionally.
virtual void parseFromTokens(parser::DefTokeniser& tokeniser) = 0;
// Optional callback to be overridden by subclasses.
// Will always be called after parseFromTokens() has completed, even in case of parse errors.
virtual void onParsingFinished()
{}
// Invoked after a new syntax block has been assigned through setBlockSyntax()
// Allows subclasses to either reparse immediately or schedule a later parse
virtual void onSyntaxBlockAssigned(const DeclarationBlockSyntax& block)
{}
// Updates the contents member of the attached syntax block without
// firing any update signals. This is meant to be used internally by EditableDeclaration.
// Assigning the block contents will not change the "parsed" status, the method
// parseFromTokens() will not forced to be called afterwards.
void assignSyntaxBlockContents(const std::string& newSyntax)
{
_declBlock.contents = newSyntax;
}
};
}