1 /*
2 -----------------------------------------------------------------------------
3 This source file is part of OGRE
4 (Object-oriented Graphics Rendering Engine)
5 For the latest info, see http://www.ogre3d.org/
6 
7 Copyright (c) 2000-2013 Torus Knot Software Ltd
8 
9 Permission is hereby granted, free of charge, to any person obtaining a copy
10 of this software and associated documentation files (the "Software"), to deal
11 in the Software without restriction, including without limitation the rights
12 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 copies of the Software, and to permit persons to whom the Software is
14 furnished to do so, subject to the following conditions:
15 
16 The above copyright notice and this permission notice shall be included in
17 all copies or substantial portions of the Software.
18 
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 THE SOFTWARE.
26 -----------------------------------------------------------------------------
27 */
28 #include <assert.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include "Compiler2Pass.h"
33 
Compiler2Pass()34 Compiler2Pass::Compiler2Pass()
35 {
36 	// reserve some memory space in the containers being used
37 	mTokenInstructions.reserve(100);
38 	mConstants.reserve(80);
39 	// default contexts allows all contexts
40 	// subclass should change it to fit the language being compiled
41 	mActiveContexts = 0xffffffff;
42 
43 }
44 
45 
46 
InitSymbolTypeLib()47 void Compiler2Pass::InitSymbolTypeLib()
48 {
49 	uint token_ID;
50 	// find a default text for all Symbol Types in library
51 
52 	// scan through all the rules and initialize TypeLib with index to text and index to rules for non-terminal tokens
53 	for(int i = 0; i < mRulePathLibCnt; i++) {
54 		token_ID = mRootRulePath[i].mTokenID;
55 		// make sure SymbolTypeLib holds valid token
56 		assert(mSymbolTypeLib[token_ID].mID == token_ID);
57 		switch(mRootRulePath[i].mOperation) {
58 			case otRULE:
59 				// if operation is a rule then update typelib
60 				mSymbolTypeLib[token_ID].mRuleID = i;
61 
62 			case otAND:
63 			case otOR:
64 			case otOPTIONAL:
65 				// update text index in typelib
66 				if (mRootRulePath[i].mSymbol != NULL) mSymbolTypeLib[token_ID].mDefTextID = i;
67 				break;
68             case otREPEAT:
69             case otEND:
70                 break;
71 		}
72 	}
73 
74 }
75 
76 
compile(const char * source)77 bool Compiler2Pass::compile(const char* source)
78 {
79 	bool Passed = false;
80 
81 	mSource = source;
82 	// start compiling if there is a rule base to work with
83 	if(mRootRulePath != NULL) {
84 		 Passed = doPass1();
85 
86 		if(Passed) {
87 			Passed = doPass2();
88 		}
89 	}
90 	return Passed;
91 }
92 
93 
doPass1()94 bool Compiler2Pass::doPass1()
95 {
96 	// scan through Source string and build a token list using TokenInstructions
97 	// this is a simple brute force lexical scanner/analyzer that also parses the formed
98 	// token for proper semantics and context in one pass
99 
100 	mCurrentLine = 1;
101 	mCharPos = 0;
102 	// reset position in Constants container
103 	mConstants.clear();
104 	mEndOfSource = strlen(mSource);
105 
106 	// start with a clean slate
107 	mTokenInstructions.clear();
108 	// tokenize and check semantics until an error occurs or end of source is reached
109 	// assume RootRulePath has pointer to rules so start at index + 1 for first rule path
110 	// first rule token would be a rule definition so skip over it
111 	bool passed = processRulePath(0);
112 	// if a symbol in source still exists then the end of source was not reached and there was a problem some where
113 	if (positionToNextSymbol()) passed = false;
114 	return passed;
115 
116 }
117 
118 
processRulePath(uint rulepathIDX)119 bool Compiler2Pass::processRulePath( uint rulepathIDX)
120 {
121 	// rule path determines what tokens and therefore what symbols are acceptable from the source
122 	// it is assumed that the tokens with the longest similar symbols are arranged first so
123 	// if a match is found it is accepted and no further searching is done
124 
125 	// record position of last token in container
126 	// to be used as the rollback position if a valid token is not found
127 	uint TokenContainerOldSize = mTokenInstructions.size();
128 	int OldCharPos = mCharPos;
129 	int OldLinePos = mCurrentLine;
130 	uint OldConstantsSize = mConstants.size();
131 
132 	// keep track of what non-terminal token activated the rule
133 	uint ActiveNTTRule = mRootRulePath[rulepathIDX].mTokenID;
134 	// start rule path at next position for definition
135 	rulepathIDX++;
136 
137 	// assume the rule will pass
138 	bool Passed = true;
139 	bool EndFound = false;
140 
141 	// keep following rulepath until the end is reached
142 	while (EndFound == false) {
143 		switch (mRootRulePath[rulepathIDX].mOperation) {
144 
145 			case otAND:
146 				// only validate if the previous rule passed
147 				if(Passed) Passed = ValidateToken(rulepathIDX, ActiveNTTRule);
148 				break;
149 
150 			case otOR:
151 				// only validate if the previous rule failed
152 				if ( Passed == false ) {
153 					// clear previous tokens from entry and try again
154 					mTokenInstructions.resize(TokenContainerOldSize);
155 					Passed = ValidateToken(rulepathIDX, ActiveNTTRule);
156 				}
157 				else { // path passed up to this point therefore finished so pretend end marker found
158 					EndFound = true;
159 				}
160 				break;
161 
162 			case otOPTIONAL:
163 				// if previous passed then try this rule but it does not effect succes of rule since its optional
164 				if(Passed) ValidateToken(rulepathIDX, ActiveNTTRule);
165 				break;
166 
167 			case otREPEAT:
168 				// repeat until no tokens of this type found
169 				// at least one must be found
170 				if(Passed) {
171 					int TokensPassed = 0;
172 					// keep calling until failure
173 					while ((Passed = ValidateToken(rulepathIDX, ActiveNTTRule))) {
174 						// increment count for previous passed token
175 						TokensPassed++;
176 					}
177 					// defaults to Passed = fail
178 					// if at least one token found then return passed = true
179 					if (TokensPassed > 0) Passed = true;
180 				}
181 				break;
182 
183 			case otEND:
184 				// end of rule found so time to return
185 				EndFound = true;
186 				if(Passed == false) {
187 					// the rule did not validate so get rid of tokens decoded
188 					// roll back the token container end position to what it was when rule started
189 					// this will get rid of all tokens that had been pushed on the container while
190 					// trying to validating this rule
191 					mTokenInstructions.resize(TokenContainerOldSize);
192 					mConstants.resize(OldConstantsSize);
193 					mCharPos = OldCharPos;
194 					mCurrentLine = OldLinePos;
195 				}
196 				break;
197 
198 			default:
199 				// an exception should be raised since the code should never get here
200 				Passed = false;
201 				EndFound = true;
202 				break;
203 
204 		}
205 
206 
207 		// move on to the next rule in the path
208 		rulepathIDX++;
209 	}
210 
211 	return Passed;
212 
213 }
214 
215 
ValidateToken(const uint rulepathIDX,const uint activeRuleID)216 bool Compiler2Pass::ValidateToken(const uint rulepathIDX, const uint activeRuleID)
217 {
218 	int tokenlength = 0;
219 	// assume the test is going to fail
220 	bool Passed = false;
221 	uint TokenID = mRootRulePath[rulepathIDX].mTokenID;
222 	// only validate token if context is correct
223 	if (mSymbolTypeLib[TokenID].mContextKey & mActiveContexts) {
224 
225 		// if terminal token then compare text of symbol with what is in source
226 		if ( mSymbolTypeLib[TokenID].mRuleID == 0){
227 
228 			if (positionToNextSymbol()) {
229 				// if Token is supposed to be a number then check if its a numerical constant
230 				if (TokenID == mValueID) {
231 					float constantvalue;
232 					if((Passed = isFloatValue(constantvalue, tokenlength))) {
233 						mConstants.push_back(constantvalue);
234 					}
235 
236 				}
237 				// compare token symbol text with source text
238 				else Passed = isSymbol(mRootRulePath[rulepathIDX].mSymbol, tokenlength);
239 
240 				if(Passed) {
241 					TokenInst newtoken;
242 					// push token onto end of container
243 					newtoken.mID = TokenID;
244 					newtoken.mNTTRuleID = activeRuleID;
245 					newtoken.mLine = mCurrentLine;
246 					newtoken.mPos = mCharPos;
247 
248 					mTokenInstructions.push_back(newtoken);
249 					// update source position
250 					mCharPos += tokenlength;
251 
252 					// allow token instruction to change the ActiveContexts
253 					// use token contexts pattern to clear ActiveContexts pattern bits
254 					mActiveContexts &= ~mSymbolTypeLib[TokenID].mContextPatternClear;
255 					// use token contexts pattern to set ActiveContexts pattern bits
256 					mActiveContexts |= mSymbolTypeLib[TokenID].mContextPatternSet;
257 				}
258 			}
259 
260 		}
261 		// else a non terminal token was found
262 		else {
263 
264 			// execute rule for non-terminal
265 			// get rule_ID for index into  rulepath to be called
266 			Passed = processRulePath(mSymbolTypeLib[TokenID].mRuleID);
267 		}
268 	}
269 
270 
271 	return Passed;
272 
273 }
274 
275 
getTypeDefText(const uint sid)276 const char* Compiler2Pass::getTypeDefText(const uint sid)
277 {
278 	return mRootRulePath[mSymbolTypeLib[sid].mDefTextID].mSymbol;
279 }
280 
281 
isFloatValue(float & fvalue,int & charsize)282 bool Compiler2Pass::isFloatValue(float& fvalue, int& charsize)
283 {
284 	// check to see if it is a numeric float value
285 	bool valuefound = false;
286 
287 	const char* startptr = mSource + mCharPos;
288 	char* endptr = NULL;
289 
290 	fvalue = (float)strtod(startptr, &endptr);
291 	// if a valid float was found then endptr will have the pointer to the first invalid character
292 	if(endptr) {
293 		if(endptr>startptr) {
294 			// a valid value was found so process it
295 			charsize = endptr - startptr;
296 			valuefound = true;
297 		}
298 	}
299 
300 	return valuefound;
301 }
302 
303 
isSymbol(const char * symbol,int & symbolsize)304 bool Compiler2Pass::isSymbol(const char* symbol, int& symbolsize)
305 {
306 	// compare text at source+charpos with the symbol : limit testing to symbolsize
307 	bool symbolfound = false;
308 	symbolsize = strlen(symbol);
309 	if(strncmp(mSource + mCharPos, symbol, symbolsize)==0) {
310 		symbolfound = true;
311 	}
312 
313 	return symbolfound;
314 }
315 
316 
positionToNextSymbol()317 bool Compiler2Pass::positionToNextSymbol()
318 {
319 	bool validsymbolfound = false;
320 	bool endofsource = false;
321 	while(!validsymbolfound && !endofsource) {
322 		skipWhiteSpace();
323 		skipEOL();
324 		skipComments();
325 		// have we reached the end of the string?
326 		if (mCharPos == mEndOfSource) endofsource = true;
327 		else {
328 			// if ASCII > space then assume valid character is found
329 			if (mSource[mCharPos] > ' ') validsymbolfound = true;
330 		}
331 	}// end of while
332 
333 	return validsymbolfound;
334 }
335 
336 
337 
skipComments()338 void Compiler2Pass::skipComments()
339 {
340   // if current char and next are // then search for EOL
341 	if(mCharPos < mEndOfSource) {
342 		if( ((mSource[mCharPos] == '/') && (mSource[mCharPos + 1] == '/')) ||
343 			(mSource[mCharPos] == ';') ||
344 			(mSource[mCharPos] == '#') ) findEOL();
345 	}
346 }
347 
348 
findEOL()349 void Compiler2Pass::findEOL()
350 {
351 	// find eol charter and move to this position
352 	const char* newpos = strchr(&mSource[mCharPos], '\n');
353 	if(newpos) {
354 		mCharPos += newpos - &mSource[mCharPos];
355 	}
356 	// couldn't find end of line so skip to the end
357 	else mCharPos = mEndOfSource - 1;
358 
359 }
360 
361 
skipEOL()362 void Compiler2Pass::skipEOL()
363 {
364 	if ((mSource[mCharPos] == '\n') || (mSource[mCharPos] == '\r')) {
365 		mCurrentLine++;
366 		mCharPos++;
367 		if ((mSource[mCharPos] == '\n') || (mSource[mCharPos] == '\r')) {
368 			mCharPos++;
369 		}
370 	}
371 }
372 
373 
skipWhiteSpace()374 void Compiler2Pass::skipWhiteSpace()
375 {
376 	// FIX - this method kinda slow
377 	while((mSource[mCharPos] == ' ') || (mSource[mCharPos] == '\t')) mCharPos++; // find first non white space character
378 }
379 
380