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;
}
};
}
|