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