1 /* "CodeWorker": a scripting language for parsing and generating text. 2 3 Copyright (C) 1996-1997, 1999-2004 C�dric Lemaire 4 5 This library is free software; you can redistribute it and/or 6 modify it under the terms of the GNU Lesser General Public 7 License as published by the Free Software Foundation; either 8 version 2.1 of the License, or (at your option) any later version. 9 10 This library is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 Lesser General Public License for more details. 14 15 You should have received a copy of the GNU Lesser General Public 16 License along with this library; if not, write to the Free Software 17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 19 To contact the author: codeworker@free.fr 20 */ 21 22 #ifdef WIN32 23 #pragma warning (disable : 4786) 24 #endif 25 26 #include "UtlException.h" 27 #include "UtlDate.h" 28 #include "ScpStream.h" 29 30 #include "DtaProject.h" 31 #include "CGRuntime.h" 32 #include "DtaBNFScript.h" 33 34 #include "DtaSharpTagsHandler.h" 35 36 namespace CodeWorker { 37 ~DtaSharpTagsHandlerImpl()38 DtaSharpTagsHandlerImpl::~DtaSharpTagsHandlerImpl() {} 39 40 41 class DtaClassicalSharpTagsHandler : public DtaSharpTagsHandlerImpl { 42 private: 43 std::string _sCommentEnd; 44 45 // some attributes concerning '##markup##' 46 bool _bKeyIsIdentifier; 47 48 public: DtaClassicalSharpTagsHandler(ScpStream * pInputStream)49 DtaClassicalSharpTagsHandler(ScpStream* pInputStream) : DtaSharpTagsHandlerImpl(pInputStream) { 50 if ((DtaProject::getInstance().getTextMode() == DtaProject::DOS_MODE) && (DtaProject::getInstance().getCommentEnd() == "\n")) _sCommentEnd = "\r\n"; 51 else _sCommentEnd = DtaProject::getInstance().getCommentEnd(); 52 } 53 findExpansionMarkup()54 virtual bool findExpansionMarkup() { 55 // complete reading of the markup announcement 56 std::string sMarker = DtaProject::getInstance().getCommentBegin() + "##mark"; 57 if (!_pInputStream->findString(sMarker)) return false; 58 if (!_pInputStream->isEqualTo("er##") && !_pInputStream->isEqualTo("up##")) throw UtlException(*_pInputStream, "unrecognized markup format: '##markup##' or '##marker##' expected"); 59 _pInputStream->skipEmpty(); 60 std::string sMarkerKey; 61 if (_pInputStream->readString(sMarkerKey)) _bKeyIsIdentifier = false; 62 else if (_pInputStream->readIdentifier(sMarkerKey)) _bKeyIsIdentifier = true; 63 else { 64 throw UtlException(*_pInputStream, "unrecognized markup format: string or identifier expected after '##markup##'"); 65 } 66 DtaProject::getInstance().setMarkupKey(sMarkerKey); 67 while (!_pInputStream->isEqualTo(DtaProject::getInstance().getCommentEnd())) { 68 int a = _pInputStream->readChar(); 69 if ((a < 0) || (a > (int) ' ')) throw UtlException(*_pInputStream, "end of comment expected after '##markup##\"" + DtaProject::getInstance().getMarkupKey() + "\"'"); 70 } 71 return true; 72 } 73 readMarkupScript(std::string & sScript)74 virtual bool readMarkupScript(std::string& sScript) { 75 std::string sScriptMarker = DtaProject::getInstance().getCommentBegin() + "##script##"; 76 if (!_pInputStream->isEqualTo(sScriptMarker)) return false; 77 // extracting the script block 78 // //##script## ... //##script## 79 while (!_pInputStream->isEqualTo(DtaProject::getInstance().getCommentEnd())) { 80 int a = _pInputStream->readChar(); 81 if ((a < 0) || (a > (int) ' ')) throw UtlException(*_pInputStream, "end of comment expected after '##script##'"); 82 } 83 int iStartScript = _pInputStream->getInputLocation(); 84 if (!_pInputStream->findString(sScriptMarker)) throw UtlException(*_pInputStream, "'##script##' expected to close the template-based script block of markup '" + DtaProject::getInstance().getMarkupKey() + "'"); 85 _pInputStream->setInputLocation(_pInputStream->getInputLocation() - sScriptMarker.size()); 86 _pInputStream->readLastChars(_pInputStream->getInputLocation() - iStartScript, sScript); 87 _pInputStream->setInputLocation(_pInputStream->getInputLocation() + sScriptMarker.size()); 88 while (!_pInputStream->isEqualTo(DtaProject::getInstance().getCommentEnd())) { 89 int a = _pInputStream->readChar(); 90 if ((a < 0) || (a > (int) ' ')) throw UtlException(*_pInputStream, "end of comment expected after '##script##'"); 91 } 92 std::string::size_type iIndex = 0; 93 register char a; 94 while ((a = sScript[iIndex]) && (a > '\0') && (a <= ' ')) iIndex++; 95 int iCBLength = DtaProject::getInstance().getCommentBegin().size(); 96 while (strncmp(sScript.c_str() + iIndex, DtaProject::getInstance().getCommentBegin().c_str(), iCBLength) == 0) { 97 std::string::size_type iNewIndex = sScript.find(DtaProject::getInstance().getCommentEnd(), iIndex); 98 if (iNewIndex == std::string::npos) break; 99 std::string sTemp; 100 if (iNewIndex > 0) sTemp = sScript.substr(0, iIndex); 101 std::string::size_type iNextIndex = iNewIndex + DtaProject::getInstance().getCommentEnd().size(); 102 sTemp += sScript.substr(iIndex + iCBLength, iNewIndex - (iIndex + iCBLength)); 103 sTemp += '\n'; 104 if (iNextIndex < sScript.size()) { 105 sScript = sTemp + sScript.substr(iNextIndex); 106 iIndex = sTemp.size(); 107 register char a; 108 while ((a = sScript[iIndex]) && (a > '\0') && (a <= ' ')) iIndex++; 109 } else { 110 sScript = sTemp; 111 break; 112 } 113 } 114 return true; 115 } 116 readMarkupData(std::string & sData)117 virtual bool readMarkupData(std::string& sData) { 118 std::string sDataMarker = DtaProject::getInstance().getCommentBegin() + "##data##"; 119 if (!_pInputStream->isEqualTo(sDataMarker)) return false; 120 // extracting the data block 121 // //##data## ... //##data## 122 while (!_pInputStream->isEqualTo(DtaProject::getInstance().getCommentEnd())) { 123 int a = _pInputStream->readChar(); 124 if ((a < 0) || (a > (int) ' ')) throw UtlException(*_pInputStream, "end of comment expected after '##data##'"); 125 } 126 int iStartData = _pInputStream->getInputLocation(); 127 if (!_pInputStream->findString(sDataMarker)) throw UtlException(*_pInputStream, "'##data##' expected to close the data block of markup '" + DtaProject::getInstance().getMarkupKey() + "'"); 128 _pInputStream->setInputLocation(_pInputStream->getInputLocation() - sDataMarker.size()); 129 _pInputStream->readLastChars(_pInputStream->getInputLocation() - iStartData, sData); 130 _pInputStream->setInputLocation(_pInputStream->getInputLocation() + sDataMarker.size()); 131 while (!_pInputStream->isEqualTo(DtaProject::getInstance().getCommentEnd())) { 132 int a = _pInputStream->readChar(); 133 if ((a < 0) || (a > (int) ' ')) throw UtlException(*_pInputStream, "end of comment expected after '##data##'"); 134 } 135 return true; 136 } 137 readMarkupBegin()138 virtual bool readMarkupBegin() { 139 if (DtaProject::getInstance().getCommentBegin().empty() || !_pInputStream->isEqualTo(DtaProject::getInstance().getCommentBegin() + "##begin##")) return false; 140 std::string sStringMarkerKey = "\"" + CGRuntime::composeCLikeString(DtaProject::getInstance().getMarkupKey()) + "\""; 141 if (!_pInputStream->isEqualTo(sStringMarkerKey)) throw UtlException(*_pInputStream, "'##begin##' tag is expected to concern marker called '" + sStringMarkerKey + "'"); 142 if (!_pInputStream->findString(DtaProject::getInstance().getCommentEnd())) throw UtlException(*_pInputStream, "'##begin##' tag is expected to end with an end of comment marker"); 143 return true; 144 } 145 locateMarkupEnd()146 virtual int locateMarkupEnd() { 147 std::string sMarkupBegin = DtaProject::getInstance().getCommentBegin() + "##end##"; 148 if (!_pInputStream->findString(sMarkupBegin)) throw UtlException(*_pInputStream, "'##begin##' tag is expected to have an associated '##end##' tag"); 149 int iEnd = _pInputStream->getInputLocation() - sMarkupBegin.size(); 150 std::string sStringMarkerKey = "\"" + CGRuntime::composeCLikeString(DtaProject::getInstance().getMarkupKey()) + "\""; 151 if (!_pInputStream->isEqualTo(sStringMarkerKey)) throw UtlException(*_pInputStream, "'##end##' tag is expected to concern marker called " + sStringMarkerKey); 152 if (!_pInputStream->findString(DtaProject::getInstance().getCommentEnd())) throw UtlException(*_pInputStream, "'##end##' tag is expected to end with an end of comment marker"); 153 return iEnd; 154 } 155 writeMarkupBegin(ScpStream & outputStream)156 virtual void writeMarkupBegin(ScpStream& outputStream) { 157 outputStream << DtaProject::getInstance().getCommentBegin() << "##begin##"; 158 if (_bKeyIsIdentifier) outputStream << DtaProject::getInstance().getMarkupKey(); 159 else outputStream.writeString(DtaProject::getInstance().getMarkupKey()); 160 outputStream << _sCommentEnd; 161 } 162 writeMarkupEnd(ScpStream & outputStream)163 virtual void writeMarkupEnd(ScpStream& outputStream) { 164 outputStream << DtaProject::getInstance().getCommentBegin() << "##end##"; 165 if (_bKeyIsIdentifier) outputStream << DtaProject::getInstance().getMarkupKey(); 166 else outputStream.writeString(DtaProject::getInstance().getMarkupKey()); 167 outputStream << _sCommentEnd; 168 } 169 170 findGenerationProtectBegin(int iEnd)171 virtual bool findGenerationProtectBegin(int iEnd) { 172 std::string sBeginning = DtaProject::getInstance().getCommentBegin() + "##protect"; 173 if (iEnd < 0) { 174 if (!_pInputStream->findString(sBeginning.c_str())) return false; 175 } else { 176 if (!_pInputStream->findString(sBeginning.c_str(), iEnd)) return false; 177 } 178 _pInputStream->skipEmpty(); 179 bool bNotProtectDefine = _pInputStream->isEqualTo('!') && _pInputStream->skipEmpty(); 180 if (_pInputStream->readIdentifier(_sProtectDefine)) { 181 if (bNotProtectDefine) _sProtectDefine = "!" + _sProtectDefine; 182 _pInputStream->skipEmpty(); 183 } else if (bNotProtectDefine) { 184 throw UtlException(*_pInputStream, "'!' should be followed by a property identifier"); 185 } else { 186 _sProtectDefine = ""; 187 } 188 if (!_pInputStream->isEqualTo("##")) throw UtlException(*_pInputStream, "wrong syntax for announcing protected area, '##' expected"); 189 if (!_pInputStream->readString(_sProtectKey)) throw UtlException(*_pInputStream, "wrong syntax for announcing protected area"); 190 if (!_pInputStream->findString(DtaProject::getInstance().getCommentEnd().c_str())) throw UtlException(*_pInputStream, "end of comment expected after announcing protected area \"" + _sProtectKey + "\""); 191 return true; 192 } 193 locateGenerationProtectEnd()194 virtual int locateGenerationProtectEnd() { 195 std::string sBeginning = DtaProject::getInstance().getCommentBegin() + "##protect"; 196 if (!_pInputStream->findString(sBeginning.c_str())) throw UtlException(*_pInputStream, "end of protected area \"" + _sProtectKey + "\" expected"); 197 int iEnd = _pInputStream->getInputLocation() - sBeginning.size(); 198 _pInputStream->skipEmpty(); 199 std::string sProtectDefine; 200 bool bNotProtectDefine = _pInputStream->isEqualTo('!') && _pInputStream->skipEmpty(); 201 if (_pInputStream->readIdentifier(sProtectDefine)) { 202 if (bNotProtectDefine) sProtectDefine = "!" + sProtectDefine; 203 _pInputStream->skipEmpty(); 204 } else if (bNotProtectDefine) { 205 throw UtlException(*_pInputStream, "'!' should be followed by a property identifier"); 206 } 207 if (sProtectDefine != _sProtectDefine) throw UtlException(*_pInputStream, "wrong syntax for finishing protected area \"" + _sProtectKey + "\", defines are different"); 208 if (!_pInputStream->isEqualTo("##")) throw UtlException(*_pInputStream, "wrong syntax for finishing protected area \"" + _sProtectKey + "\", '##' expected"); 209 std::string sProtectKey; 210 if (!_pInputStream->readString(sProtectKey)) throw UtlException(*_pInputStream, "wrong syntax for finishing protected area \"" + _sProtectKey + "\""); 211 if (sProtectKey != _sProtectKey) throw UtlException(*_pInputStream, "the key \"" + sProtectKey + "\" of the preserved area's end doesn't match with key \"" + _sProtectKey + "\", announced at the beginning"); 212 if (!_pInputStream->findString(DtaProject::getInstance().getCommentEnd().c_str())) throw UtlException(*_pInputStream, "end of comment expected after finishing protected area \"" + _sProtectKey + "\""); 213 return iEnd; 214 } 215 writeProtectBegin(ScpStream & outputStream,const std::string & sKey,const std::string & sDefine)216 virtual void writeProtectBegin(ScpStream& outputStream, const std::string& sKey, const std::string& sDefine) { 217 outputStream << DtaProject::getInstance().getCommentBegin() << "##protect"; 218 if (!sDefine.empty()) outputStream << " " << sDefine; 219 outputStream << "##"; 220 outputStream.writeString(sKey); 221 if ((DtaProject::getInstance().getTextMode() == DtaProject::DOS_MODE) && (DtaProject::getInstance().getCommentEnd() == "\n")) outputStream << "\r\n"; 222 else outputStream << DtaProject::getInstance().getCommentEnd(); 223 } 224 writeProtectEnd(ScpStream & outputStream,const std::string & sKey,const std::string & sDefine)225 virtual void writeProtectEnd(ScpStream& outputStream, const std::string& sKey, const std::string& sDefine) { 226 writeProtectBegin(outputStream, sKey, sDefine); 227 } 228 }; 229 230 231 class DtaCustomSharpTagsHandler : public DtaSharpTagsHandlerImpl { 232 private: 233 DtaBNFScript* _pReader; 234 DtaPatternScript* _pWriter; 235 DtaScriptVariable _context; 236 237 public: DtaCustomSharpTagsHandler(ScpStream * pInputStream,DtaBNFScript * pReader,DtaPatternScript * pWriter)238 inline DtaCustomSharpTagsHandler(ScpStream* pInputStream, DtaBNFScript* pReader, DtaPatternScript* pWriter) 239 : DtaSharpTagsHandlerImpl(pInputStream), 240 _pReader(pReader), _pWriter(pWriter), 241 _context(&(DtaProject::getInstance()), "handler_context") 242 { 243 } 244 245 private: executeReader()246 bool executeReader() { 247 CGRuntimeInputStream newInputStream(_pInputStream); 248 return (_pReader->DtaPatternScript::execute(_context) == NO_INTERRUPTION); 249 } 250 executeWriter(ScpStream & outputStream)251 void executeWriter(ScpStream& outputStream) { 252 CGRuntimeOutputStream newOutputStream(&outputStream); 253 _pWriter->execute(_context); 254 } 255 256 public: findExpansionMarkup()257 virtual bool findExpansionMarkup() { 258 _context.insertNode("execute")->setValue("find_expansion_markup"); 259 if (!executeReader()) return false; 260 DtaScriptVariable* pVariable = _context.getNode("markup_key"); 261 if (pVariable == NULL) throw UtlException("field 'this.markup_key' should have been populated by the 'find_expansion_markup' reader of the generation tags handler designated by key '" + DtaProject::getInstance().getCurrentGenerationTagsHandler() + "'"); 262 const char* tcMarkupKey = pVariable->getValue(); 263 if (tcMarkupKey != NULL) { 264 DtaProject::getInstance().setMarkupKey(tcMarkupKey); 265 } else { 266 DtaProject::getInstance().setMarkupKey(""); 267 } 268 return true; 269 } 270 readMarkupScript(std::string & sScript)271 virtual bool readMarkupScript(std::string& sScript) { 272 _context.insertNode("execute")->setValue("read_markup_script"); 273 if (!executeReader()) return false; 274 DtaScriptVariable* pVariable = _context.getNode("script"); 275 if (pVariable == NULL) throw UtlException("field 'this.script' should have been populated by the 'read_markup_script' reader of the generation tags handler designated by key '" + DtaProject::getInstance().getCurrentGenerationTagsHandler() + "'"); 276 const char* tcScript = pVariable->getValue(); 277 sScript = ((tcScript == NULL) ? "" : tcScript); 278 return true; 279 } 280 readMarkupData(std::string & sData)281 virtual bool readMarkupData(std::string& sData) { 282 _context.insertNode("execute")->setValue("read_markup_data"); 283 if (!executeReader()) return false; 284 DtaScriptVariable* pVariable = _context.getNode("data"); 285 if (pVariable == NULL) throw UtlException("field 'this.data' should have been populated by the 'read_markup_data' reader of the generation tags handler designated by key '" + DtaProject::getInstance().getCurrentGenerationTagsHandler() + "'"); 286 const char* tcData = pVariable->getValue(); 287 sData = ((tcData == NULL) ? "" : tcData); 288 return true; 289 } 290 readMarkupBegin()291 virtual bool readMarkupBegin() { 292 _context.insertNode("execute")->setValue("read_markup_begin"); 293 if (!executeReader()) return false; 294 return true; 295 } 296 locateMarkupEnd()297 virtual int locateMarkupEnd() { 298 _context.insertNode("execute")->setValue("locate_markup_end"); 299 if (!executeReader()) throw UtlException(*_pInputStream, "unable to locate the end of the markup '" + DtaProject::getInstance().getMarkupKey() + "'"); 300 DtaScriptVariable* pVariable = _context.getNode("beginning_position"); 301 if (pVariable == NULL) throw UtlException("field 'this.beginning_position' should have been populated by the 'locate_markup_end' reader of the generation tags handler designated by key '" + DtaProject::getInstance().getCurrentGenerationTagsHandler() + "'"); 302 return pVariable->getIntValue(); 303 } 304 writeMarkupBegin(ScpStream & outputStream)305 virtual void writeMarkupBegin(ScpStream& outputStream) { 306 _context.insertNode("execute")->setValue("write_markup_begin"); 307 executeWriter(outputStream); 308 } 309 writeMarkupEnd(ScpStream & outputStream)310 virtual void writeMarkupEnd(ScpStream& outputStream) { 311 _context.insertNode("execute")->setValue("write_markup_end"); 312 executeWriter(outputStream); 313 } 314 315 findGenerationProtectBegin(int iEnd)316 virtual bool findGenerationProtectBegin(int iEnd) { 317 _context.insertNode("execute")->setValue("find_generation_protect_begin"); 318 if (iEnd >= 0) _context.insertNode("end_position")->setValue(iEnd); 319 else _context.remove("end_position"); 320 if (!executeReader()) return false; 321 DtaScriptVariable* pVariable = _context.getNode("id"); 322 if (pVariable == NULL) throw UtlException("field 'this.id' should have been populated by the 'find_generation_protect_begin' reader of the generation tags handler designated by key '" + DtaProject::getInstance().getCurrentGenerationTagsHandler() + "'"); 323 const char* tcProtectKey = pVariable->getValue(); 324 _sProtectKey = ((tcProtectKey == NULL) ? "" : tcProtectKey); 325 pVariable = _context.getNode("property"); 326 if (pVariable != NULL) { 327 const char* tcProtectDefine = pVariable->getValue(); 328 _sProtectDefine = ((tcProtectDefine == NULL) ? "" : tcProtectDefine); 329 } else _sProtectDefine = ""; 330 return true; 331 } 332 locateGenerationProtectEnd()333 virtual int locateGenerationProtectEnd() { 334 _context.insertNode("execute")->setValue("locate_generation_protect_end"); 335 _context.insertNode("id")->setValue(getProtectKey().c_str()); 336 if (!executeReader()) throw UtlException(*_pInputStream, "unable to locate the end of the protected area '" + getProtectKey() + "'"); 337 DtaScriptVariable* pVariable = _context.getNode("beginning_position"); 338 if (pVariable == NULL) throw UtlException("field 'this.beginning_position' should have been populated by the 'locate_generation_protect_end' reader of the generation tags handler designated by key '" + DtaProject::getInstance().getCurrentGenerationTagsHandler() + "'"); 339 return pVariable->getIntValue(); 340 } 341 writeProtectBegin(ScpStream & outputStream,const std::string & sKey,const std::string & sDefine)342 virtual void writeProtectBegin(ScpStream& outputStream, const std::string& sKey, const std::string& sDefine) { 343 _context.insertNode("execute")->setValue("write_protect_begin"); 344 _context.insertNode("id")->setValue(sKey.c_str()); 345 if (!sDefine.empty()) _context.insertNode("property")->setValue(sDefine.c_str()); 346 else _context.remove("property"); 347 executeWriter(outputStream); 348 } 349 writeProtectEnd(ScpStream & outputStream,const std::string & sKey,const std::string & sDefine)350 virtual void writeProtectEnd(ScpStream& outputStream, const std::string& sKey, const std::string& sDefine) { 351 _context.insertNode("execute")->setValue("write_protect_end"); 352 _context.insertNode("id")->setValue(sKey.c_str()); 353 if (!sDefine.empty()) _context.insertNode("property")->setValue(sDefine.c_str()); 354 else _context.remove("property"); 355 executeWriter(outputStream); 356 } 357 }; 358 359 DtaSharpTagsHandler(ScpStream * pInputStream)360 DtaSharpTagsHandler::DtaSharpTagsHandler(ScpStream* pInputStream) { 361 std::string sKey = DtaProject::getInstance().getCurrentGenerationTagsHandler(); 362 if (sKey.empty()) { 363 _pInternal = new DtaClassicalSharpTagsHandler(pInputStream); 364 } else { 365 DtaBNFScript* pReader; 366 DtaPatternScript* pWriter; 367 if (!DtaProject::getInstance().getGenerationTagsHandler(sKey, pReader, pWriter)) throw UtlException(*pInputStream, "internal error: no generation tags handler registered to the key '" + sKey + "'"); 368 _pInternal = new DtaCustomSharpTagsHandler(pInputStream, pReader, pWriter); 369 } 370 } 371 ~DtaSharpTagsHandler()372 DtaSharpTagsHandler::~DtaSharpTagsHandler() { 373 delete _pInternal; 374 } 375 } 376