source: OGRE/trunk/ogrenew/OgreMain/src/OgreCompiler2Pass.cpp @ 692

Revision 692, 60.0 KB checked in by mattausch, 19 years ago (diff)

adding ogre 1.2 and dependencies

Line 
1/*
2-----------------------------------------------------------------------------
3This source file is part of OGRE
4    (Object-oriented Graphics Rendering Engine)
5For the latest info, see http://www.stevestreeting.com/ogre/
6
7Copyright (c) 2000-2005 The OGRE Team
8Also see acknowledgements in Readme.html
9
10This program is free software; you can redistribute it and/or modify it under
11the terms of the GNU General Public License as published by the Free Software
12Foundation; either version 2 of the License, or (at your option) any later
13version.
14
15This program is distributed in the hope that it will be useful, but WITHOUT
16ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
18
19You should have received a copy of the GNU General Public License along with
20this program; if not, write to the Free Software Foundation, Inc., 59 Temple
21Place - Suite 330, Boston, MA 02111-1307, USA, or go to
22http://www.gnu.org/copyleft/gpl.html.
23-----------------------------------------------------------------------------
24*/
25#include "OgreStableHeaders.h"
26#include "OgreCompiler2Pass.h"
27#include "OgreLogManager.h"
28#include "OgreException.h"
29#include "OgreStringConverter.h"
30
31namespace Ogre {
32    //-----------------------------------------------------------------------
33    // instantiate static members
34    Compiler2Pass::TokenState Compiler2Pass::mBNFTokenState;
35    Compiler2Pass::TokenStateContainer Compiler2Pass::mClientTokenStates;
36    //-----------------------------------------------------------------------
37    Compiler2Pass::Compiler2Pass()
38        : mActiveTokenState(&mBNFTokenState)
39        , mSource(0)
40        , mSourceName("system")
41    {
42            // reserve some memory space in the containers being used
43            mBNFTokenState.tokenQue.reserve(100);
44        mBNFTokenState.lexemeTokenDefinitions.reserve(50);
45
46        initBNFCompiler();
47    }
48    //-----------------------------------------------------------------------
49    void Compiler2Pass::initBNFCompiler(void)
50    {
51        if (mBNFTokenState.lexemeTokenDefinitions.empty())
52        {
53            addLexemeToken("UNKNOWN", BNF_UNKOWN);
54            addLexemeToken("syntax", BNF_SYNTAX);
55            addLexemeToken("rule", BNF_RULE);
56            addLexemeToken("identifier", BNF_IDENTIFIER);
57            addLexemeToken("identifier_right", BNF_IDENTIFIER_RIGHT);
58            addLexemeToken("identifier_characters", BNF_IDENTIFIER_CHARACTERS);
59            addLexemeToken("<", BNF_ID_BEGIN, false, true);
60            addLexemeToken(">", BNF_ID_END, false, true);
61            addLexemeToken("<#", BNF_CONSTANT_BEGIN, false, true);
62            addLexemeToken("::=", BNF_SET_RULE, false, true);
63            addLexemeToken("expression", BNF_EXPRESSION);
64            addLexemeToken("and_term", BNF_AND_TERM);
65            addLexemeToken("or_term", BNF_OR_TERM);
66            addLexemeToken("term", BNF_TERM);
67            addLexemeToken("term_id", BNF_TERM_ID);
68            addLexemeToken("constant", BNF_CONSTANT);
69            addLexemeToken("|", BNF_OR, false, true);
70            addLexemeToken("terminal_symbol", BNF_TERMINAL_SYMBOL);
71            addLexemeToken("terminal_start", BNF_TERMINAL_START);
72            addLexemeToken("repeat_expression", BNF_REPEAT_EXPRESSION);
73            addLexemeToken("not_expression", BNF_NOT_EXPRESSION);
74            addLexemeToken("{", BNF_REPEAT_BEGIN, false, true);
75            addLexemeToken("}", BNF_REPEAT_END, false, true);
76            addLexemeToken("set", BNF_SET);
77            addLexemeToken("(", BNF_SET_BEGIN, false, true);
78            addLexemeToken(")", BNF_SET_END, false, true);
79            addLexemeToken("set_end_exc", BNF_SET_END_EXC);
80            addLexemeToken("optional_expression", BNF_OPTIONAL_EXPRESSION);
81            addLexemeToken("[", BNF_OPTIONAL_BEGIN, false, true);
82            addLexemeToken("]", BNF_OPTIONAL_END, false, true);
83            addLexemeToken("not_test", BNF_NOT_TEST);
84            addLexemeToken("not_chk", BNF_NOT_CHK);
85            addLexemeToken("(?!", BNF_NOT_TEST_BEGIN, false, true);
86            addLexemeToken("'", BNF_SINGLEQUOTE, false, true);
87            addLexemeToken("-'", BNF_NO_TOKEN_START, false, true);
88            addLexemeToken("any_character", BNF_ANY_CHARACTER);
89            addLexemeToken("single_quote_exc", BNF_SINGLE_QUOTE_EXC);
90            addLexemeToken("white_space_chk", BNF_WHITE_SPACE_CHK);
91            addLexemeToken("special_characters1", BNF_SPECIAL_CHARACTERS1);
92            addLexemeToken("special_characters2", BNF_SPECIAL_CHARACTERS2);
93
94            addLexemeToken("letter", BNF_LETTER);
95            addLexemeToken("letter_digit", BNF_LETTER_DIGIT);
96            addLexemeToken("digit", BNF_DIGIT);
97            addLexemeToken("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", BNF_ALPHA_SET, false, true);
98            addLexemeToken("0123456789", BNF_NUMBER_SET, false, true);
99            addLexemeToken("`~!@#$%^&*(-_=+\\|[]{}:;\"<>,.?/", BNF_SPECIAL_CHARACTER_SET2, false, true);
100            addLexemeToken("$_", BNF_SPECIAL_CHARACTER_SET1, false, true);
101            addLexemeToken(" ", BNF_WHITE_SPACE, false, true);
102            addLexemeToken("?!", BNF_NOT_CHARS, false, true);
103        }
104
105        if (mBNFTokenState.rootRulePath.empty())
106        {
107            // first entry is set to unknown in order to trap rule id's not set for non-terminal tokens
108            mBNFTokenState.rootRulePath.resize(1);
109            //  used by bootstrap BNF text parser
110            //  <>      - non-terminal token
111            //  ()  - set of
112            // ::=      - rule definition
113            #define _rule_(id)      mBNFTokenState.rootRulePath.push_back(TokenRule(otRULE, id));
114            #define _is_(id)        mBNFTokenState.rootRulePath.push_back(TokenRule(otAND, id));
115            //     - blank space is an implied "AND" meaning the token is required
116            #define _and_(id)           mBNFTokenState.rootRulePath.push_back(TokenRule(otAND, id));
117            // |        - or
118            #define _or_(id)            mBNFTokenState.rootRulePath.push_back(TokenRule(otOR,   id));
119            // []       - optional
120            #define _optional_(id)      mBNFTokenState.rootRulePath.push_back(TokenRule(otOPTIONAL, id));
121            // {}       - repeat 0 or more times until fail or rule does not progress
122            #define _repeat_(id)        mBNFTokenState.rootRulePath.push_back(TokenRule(otREPEAT,       id));
123            #define _data_(id)      mBNFTokenState.rootRulePath.push_back(TokenRule(otDATA, id));
124            // (?! ) - lookahead negative (not test)
125            #define _not_(id)       mBNFTokenState.rootRulePath.push_back(TokenRule(otNOT_TEST, id));
126            #define _end_               mBNFTokenState.rootRulePath.push_back(TokenRule(otEND,0));
127
128            // <syntax>     ::=  { rule }
129            _rule_(BNF_SYNTAX) _repeat_(BNF_RULE) _end_
130
131            // <rule>       ::=  <identifier>  "::="  <expression>
132            _rule_(BNF_RULE)
133                _is_(BNF_IDENTIFIER)
134                _and_(BNF_SET_RULE)
135                _and_(BNF_EXPRESSION)
136            _end_
137
138            // <expression> ::=  <and_term> { <or_term> }
139            _rule_(BNF_EXPRESSION)
140                _is_(BNF_AND_TERM)
141                _repeat_(BNF_OR_TERM)
142            _end_
143            // <or_term>    ::=  "|" <and_term>
144            _rule_(BNF_OR_TERM)
145                _is_(BNF_OR)
146                _and_(BNF_AND_TERM)
147            _end_
148            // <and_term>   ::=  <term> { <term> }
149            _rule_(BNF_AND_TERM)
150                _is_(BNF_TERM)
151                _repeat_(BNF_TERM)
152            _end_
153            // <term>       ::=  <term_id> | <repeat_expression> | <optional_expression> | <not_expression>
154            _rule_(BNF_TERM)
155                _is_(BNF_TERM_ID)
156                _or_(BNF_REPEAT_EXPRESSION)
157                _or_(BNF_OPTIONAL_EXPRESSION)
158                _or_(BNF_NOT_EXPRESSION)
159            _end_
160
161            // <term_id>    ::= <constant> | <identifier_right> | <terminal_symbol> | <set>
162            _rule_(BNF_TERM_ID)
163                _is_(BNF_CONSTANT)
164                _or_(BNF_IDENTIFIER_RIGHT)
165                _or_(BNF_TERMINAL_SYMBOL)
166                _or_(BNF_SET)
167            _end_
168
169            // <repeat_expression> ::=  "{"  <term_id>  "}"
170            _rule_(BNF_REPEAT_EXPRESSION)
171                _is_(BNF_REPEAT_BEGIN)
172                _and_(BNF_TERM_ID)
173                _and_(BNF_REPEAT_END)
174            _end_
175
176            // <optional_expression> ::= "["  <term_id>  "]"
177            _rule_(BNF_OPTIONAL_EXPRESSION)
178                _is_(BNF_OPTIONAL_BEGIN)
179                _and_(BNF_TERM_ID)
180                _and_(BNF_OPTIONAL_END)
181            _end_
182
183            // <not_expression> ::= "(?!" <term_id> ")"
184            _rule_(BNF_NOT_EXPRESSION)
185                _is_(BNF_NOT_TEST_BEGIN)
186                _and_(BNF_TERM_ID)
187                _and_(BNF_SET_END)
188            _end_
189
190            // <identifier_right> ::= <indentifier> (?!"::=")
191            _rule_(BNF_IDENTIFIER_RIGHT)
192                _is_(BNF_IDENTIFIER)
193                _not_(BNF_SET_RULE)
194            _end_
195
196            // <identifier> ::=  "<" <letter> {<identifier_characters>} ">"
197            _rule_(BNF_IDENTIFIER)
198                _is_(BNF_ID_BEGIN)
199                _and_(BNF_LETTER)
200                _repeat_(BNF_IDENTIFIER_CHARACTERS)
201                _and_(BNF_ID_END)
202            _end_
203
204            // <identifier_characters> ::= <letter_digit> | <special_characters1>
205            _rule_(BNF_IDENTIFIER_CHARACTERS)
206                _is_(BNF_LETTER_DIGIT)
207                _or_(BNF_SPECIAL_CHARACTERS1)
208            _end_
209
210            // <terminal_symbol> ::= <terminal_start> @{ <any_character> } "'"
211            _rule_(BNF_TERMINAL_SYMBOL)
212                _is_(BNF_TERMINAL_START)
213                _and_(_no_space_skip_)
214                _repeat_(BNF_ANY_CHARACTER)
215                _and_(BNF_SINGLEQUOTE)
216            _end_
217
218            // <terminal_start> ::= "-'" | "'"
219            _rule_(BNF_TERMINAL_START)
220                _is_(BNF_NO_TOKEN_START)
221                _or_(BNF_SINGLEQUOTE)
222            _end_
223
224            // <constant> ::= "<#" <letter> {<identifier_characters>} ">"
225            _rule_(BNF_CONSTANT)
226                _is_(BNF_CONSTANT_BEGIN)
227                _and_(BNF_LETTER)
228                _repeat_(BNF_IDENTIFIER_CHARACTERS)
229                _and_(BNF_ID_END)
230            _end_
231
232            // <set> ::= "(" (?!<not_chk>) @{<any_character>} ")"
233            _rule_(BNF_SET)
234                _is_(BNF_SET_BEGIN)
235                _not_(BNF_NOT_CHK)
236                _and_(_no_space_skip_)
237                _repeat_(BNF_ANY_CHARACTER)
238                _and_(BNF_SET_END)
239            _end_
240
241            // <any_character> ::= <letter_digit> | <special_characters2>
242            _rule_(BNF_ANY_CHARACTER)
243                _is_(BNF_LETTER_DIGIT)
244                _or_(BNF_SPECIAL_CHARACTERS2)
245            _end_
246
247                        // <letter_digit> ::= <letter> | <digit>
248            _rule_(BNF_LETTER_DIGIT)
249                _is_(BNF_LETTER)
250                _or_(BNF_DIGIT)
251            _end_
252
253            // <letter> ::= (abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ)
254            _rule_(BNF_LETTER)
255                _is_(_character_)
256                _data_(BNF_ALPHA_SET)// "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"
257            _end_
258
259            // <digit> ::= (0123456789)
260            _rule_(BNF_DIGIT)
261                _is_(_character_)
262                _data_(BNF_NUMBER_SET)
263            _end_
264
265            // <special_characters1> ::= ($_)
266            _rule_(BNF_SPECIAL_CHARACTERS1)
267                _is_(_character_)
268                _data_(BNF_SPECIAL_CHARACTER_SET1)
269            _end_
270
271            // <special_characters2> ::= (`~!@#$%^&*(-_=+\|[]{}:;"<>,.?/) | <single_quote_exc>
272            //                           | <white_space_chk> | <set_end_exc>
273            _rule_(BNF_SPECIAL_CHARACTERS2)
274                _is_(_character_)
275                _data_(BNF_SPECIAL_CHARACTER_SET2)
276                _or_(BNF_WHITE_SPACE_CHK)
277                _or_(BNF_SINGLE_QUOTE_EXC)
278                _or_(BNF_SET_END_EXC)
279            _end_
280
281            // <single_quote_exc> ::= "'" (?!" ")
282            _rule_(BNF_SINGLE_QUOTE_EXC)
283                _is_(_character_)
284                _data_(BNF_SINGLEQUOTE)
285                _not_(BNF_WHITE_SPACE_CHK)
286            _end_
287
288            // <set_end_exc> ::= ")" (?!" ")
289            _rule_(BNF_SET_END_EXC)
290                _is_(_character_)
291                _data_(BNF_SET_END)
292                _not_(BNF_WHITE_SPACE_CHK)
293            _end_
294
295            // <white_space_chk> ::= ( )
296            _rule_(BNF_WHITE_SPACE_CHK)
297                _is_(_character_)
298                _data_(BNF_WHITE_SPACE)
299            _end_
300            // <not_chk> ::= (?!)
301            _rule_(BNF_NOT_CHK)
302                _is_(_character_)
303                _data_(BNF_NOT_CHARS)
304            _end_
305
306            // now that all the rules are added, update token definitions with rule links
307            verifyTokenRuleLinks("system");
308        }
309        // switch to client state
310        mActiveTokenState = mClientTokenState;
311    }
312
313    //-----------------------------------------------------------------------
314    void Compiler2Pass::verifyTokenRuleLinks(const String& grammerName)
315    {
316            size_t token_ID;
317
318            // scan through all the rules and initialize index to rules for non-terminal tokens
319        const size_t ruleCount = mActiveTokenState->rootRulePath.size();
320        for (size_t i = 0; i < ruleCount; ++i)
321        {
322                   // make sure token definition holds valid token
323                   if (mActiveTokenState->rootRulePath[i].operation == otRULE)
324                   {
325                   token_ID = mActiveTokenState->rootRulePath[i].tokenID;
326                   // system token id's can never have a rule assigned to them so no need to check if token is system token
327                   // but do make sure the id is within defined bounds
328               if (token_ID >= mActiveTokenState->lexemeTokenDefinitions.size())
329                    OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "For grammer: " + grammerName +
330                        ", a token ID was out of token definition range.",
331                        "Compiler2Pass::verifyTokenRuleLinks");
332
333               LexemeTokenDef& tokenDef = mActiveTokenState->lexemeTokenDefinitions[token_ID];
334                   if (tokenDef.ID != token_ID)
335                    OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "For grammer: " + grammerName +
336                        ", lexeme non-terminal token definition: " +
337                        tokenDef.lexeme + " is corrupted and does not match its assigned rule.",
338                        "Compiler2Pass::verifyTokenRuleLinks");
339                           // if operation is a rule then update token definition
340               tokenDef.ruleID = i;
341               tokenDef.isNonTerminal = true;
342                   }
343            } // end for
344
345            // test all non terminals for valid rule ID
346        const size_t definitionCount = mActiveTokenState->lexemeTokenDefinitions.size();
347        bool errorsFound = false;
348        // report all non-terminals that don't have a rule then throw an exception
349        for (token_ID = 0; token_ID < definitionCount; ++token_ID)
350        {
351            const LexemeTokenDef& tokenDef = mActiveTokenState->lexemeTokenDefinitions[token_ID];
352            if (tokenDef.isNonTerminal && (tokenDef.ruleID == 0))
353            {
354                errorsFound = true;
355                LogManager::getSingleton().logMessage(
356                "For grammer: " + grammerName +
357                ", lexeme non-terminal token definition: " + tokenDef.lexeme +
358                " found with no rule definition or corrupted."
359                );
360            }
361        }
362        if (errorsFound)
363        {
364            OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR,  "For grammer: " + grammerName +
365                ", lexeme non-terminal token definition(s) found with no rule definition or corrupted.",
366                "Compiler2Pass::verifyTokenRuleLinks");
367        }
368    }
369
370    //-----------------------------------------------------------------------
371    bool Compiler2Pass::compile(const String& source, const String& sourceName)
372    {
373        // make sure BNF compiler is setup to compile BNF grammer if required
374        initBNFCompiler();
375        // compile the client's BNF grammer
376        setClientBNFGrammer();
377
378            bool Passed = false;
379
380            mSource = &source;
381            mSourceName = sourceName;
382        mActiveTokenState = mClientTokenState;
383            // start compiling if there is a rule base to work with
384        if (mActiveTokenState->rootRulePath.size() > 1)
385            {
386                    Passed = doPass1();
387
388                    if (Passed)
389                    {
390                            Passed = doPass2();
391                    }
392            }
393
394            return Passed;
395    }
396
397    //-----------------------------------------------------------------------
398    bool Compiler2Pass::doPass1()
399    {
400            // scan through Source string and build a token list using TokenInstructions
401            // this is a simple brute force lexical scanner/analyzer that also parses the formed
402            // token for proper semantics and context in one pass
403
404            mCurrentLine = 1;
405            mCharPos = 0;
406            // reset position in Constants container
407            mConstants.clear();
408        mLabels.clear();
409        // there is no active label when first starting pass 1
410        mLabelIsActive = false;
411        mActiveLabelKey = 0;
412        mEndOfSource = mSource->length();
413
414            // start with a clean slate
415            mActiveTokenState->tokenQue.clear();
416            mPass2TokenQuePosition = 0;
417            mPreviousActionQuePosition = 0;
418            mNoTerminalToken = false;
419            mNoSpaceSkip = false;
420            // tokenize and check semantics untill an error occurs or end of source is reached
421            // assume RootRulePath has pointer to rules so start at index + 1 for first rule path
422            // first rule token would be a rule definition so skip over it
423            bool passed = false;
424
425            try
426            {
427            passed = processRulePath(1);
428            // if a lexeme in source still exists then the end of source was not reached and there was a problem some where
429            if (positionToNextLexeme()) passed = false;
430            if (passed)
431            {
432                // special condition at end of script.  The last action needs to be triggered if
433                // parsing reached the end of the source.
434                activatePreviousTokenAction();
435            }
436
437            }
438            catch (Exception& e)
439            {
440            LogManager::getSingleton().logMessage( "Exception caught: "
441            + e.getFullDescription()
442            + ", while trying to parse: "
443            + getClientGrammerName()
444            + ": "
445            + mSourceName
446            );
447            }
448            catch (...)
449            {
450            LogManager::getSingleton().logMessage( "Unkown exception while trying to parse: "
451            + getClientGrammerName()
452            + ": "
453            + mSourceName
454            );
455            }
456
457            return passed;
458
459    }
460
461    //-----------------------------------------------------------------------
462    bool Compiler2Pass::doPass2()
463    {
464        bool passed = true;
465        // step through tokens container and execute until end found or error occurs
466
467        return passed;
468    }
469
470    //-----------------------------------------------------------------------
471    const Compiler2Pass::TokenInst& Compiler2Pass::getNextToken(const size_t expectedTokenID)
472    {
473        //static TokenInst badToken;
474        // advance instruction que index by one then get the current token instruction
475        if (mPass2TokenQuePosition < mActiveTokenState->tokenQue.size() - 1)
476        {
477            ++mPass2TokenQuePosition;
478            const TokenInst& tokenInst = mActiveTokenState->tokenQue[mPass2TokenQuePosition];
479            if (expectedTokenID > 0 && (tokenInst.tokenID != expectedTokenID))
480            {
481                OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, getClientGrammerName() + ":" + mSourceName
482                    + ", expected token ID not found" ,
483                    "Compiler2Pass::getNextToken");
484            }
485
486            return tokenInst;
487        }
488        else
489            // no more tokens left for pass 2 processing
490            OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, getClientGrammerName() + ":" + mSourceName
491                + ", no more tokens available for pass 2 processing" ,
492                "Compiler2Pass::getNextToken");
493    }
494    //-----------------------------------------------------------------------
495    const Compiler2Pass::TokenInst& Compiler2Pass::getCurrentToken(void)
496    {
497        if (mPass2TokenQuePosition < mActiveTokenState->tokenQue.size() - 1)
498            return mActiveTokenState->tokenQue[mPass2TokenQuePosition];
499        else
500            OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, getClientGrammerName() + ":" + mSourceName
501                + "no token available, all pass 2 tokens processed" ,
502                "Compiler2Pass::getCurrentToken");
503    }
504    //-----------------------------------------------------------------------
505    bool Compiler2Pass::testNextTokenID(const size_t expectedTokenID)
506    {
507        bool passed = false;
508        const size_t nextTokenIndex = mPass2TokenQuePosition + 1;
509        if (nextTokenIndex < mActiveTokenState->tokenQue.size() - 1)
510            passed = mActiveTokenState->tokenQue[nextTokenIndex].tokenID == expectedTokenID;
511
512        return passed;
513    }
514    //-----------------------------------------------------------------------
515    void Compiler2Pass::replaceToken(void)
516    {
517        // move instruction que index back one position
518        if (mPass2TokenQuePosition > 0)
519            --mPass2TokenQuePosition;
520    }
521    //-----------------------------------------------------------------------
522    float Compiler2Pass::getNextTokenValue(void)
523    {
524        // get float value from current token instruction
525        if (getNextToken().tokenID == _value_)
526            return mConstants[mPass2TokenQuePosition];
527        else
528        {
529            const TokenInst& token = getCurrentToken();
530            // if token is not for a value then throw an exception
531            OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, "In " + mSourceName +
532                ", Line " + StringConverter::toString(token.line) +
533                ", token is not for a value.  Found: >>>" + mSource->substr(token.pos, 20) +
534                        "<<<",
535                "Compiler2Pass::getNextTokenValue");
536        }
537    }
538    //-----------------------------------------------------------------------
539    const String& Compiler2Pass::getNextTokenLabel(void)
540    {
541        // get label from current token instruction
542        if (getNextToken().tokenID == _character_)
543            return mLabels[mPass2TokenQuePosition];
544        else
545        {
546            const TokenInst& token = getCurrentToken();
547            // if token is not for a label then throw an exception
548            OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, "In " + mSourceName +
549                ", Line " + StringConverter::toString(token.line) +
550                ", token is not for a label.  Found: >>>" + mSource->substr(token.pos, 20) +
551                        "<<<",
552                "Compiler2Pass::getNextTokenLabel");
553        }
554    }
555    //-----------------------------------------------------------------------
556    size_t Compiler2Pass::getPass2TokenQueCount(void) const
557    {
558        // calculate number of tokens between current token instruction and next token with action
559        if(mActiveTokenState->tokenQue.size() > mPass2TokenQuePosition)
560            return mActiveTokenState->tokenQue.size() - 1 - mPass2TokenQuePosition;
561        else
562            return 0;
563    }
564    //-----------------------------------------------------------------------
565    size_t Compiler2Pass::getRemainingTokensForAction(void) const
566    {
567        size_t remaingingTokens = getPass2TokenQueCount();
568        // don't count token for next action
569        if (remaingingTokens > 0)
570            --remaingingTokens;
571        return remaingingTokens;
572    }
573
574    //-----------------------------------------------------------------------
575    void Compiler2Pass::setClientBNFGrammer(void)
576    {
577        // switch to internal BNF Containers
578        // clear client containers
579        mClientTokenState = &mClientTokenStates[getClientGrammerName()];
580        // attempt to compile the grammer into a rule base if no rules exist
581        if (mClientTokenState->rootRulePath.size() == 0)
582        {
583            mClientTokenState->tokenQue.reserve(100);
584            mClientTokenState->lexemeTokenDefinitions.reserve(100);
585            // first entry in rule path is set as a bad entry and no token should reference it
586            mClientTokenState->rootRulePath.resize(1);
587            // allow the client to setup token definitions prior to
588            // compiling the BNF grammer
589            // ensure token definitions are added to the client state
590            mActiveTokenState = mClientTokenState;
591            // get client to setup token definitions and actions it wants to know about
592            setupTokenDefinitions();
593            // make sure active token state is for BNF compiling
594            mActiveTokenState = &mBNFTokenState;
595            mSource = &getClientBNFGrammer();
596
597            if (doPass1())
598            {
599                buildClientBNFRulePaths();
600            }
601            else
602            {
603                OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "BNF Grammar compilation failed for " +
604                    getClientGrammerName(), "Compiler2Pass::setClientBNFGrammer");
605            }
606            // change token state to client data after compiling grammer
607            mActiveTokenState = mClientTokenState;
608            // verify the client rule paths and associated terminal and non-terminal lexemes
609            verifyTokenRuleLinks(getClientGrammerName());
610        }
611    }
612
613    //-----------------------------------------------------------------------
614    bool Compiler2Pass::processRulePath( size_t rulepathIDX)
615    {
616            // rule path determines what tokens and therefore what lexemes are acceptable from the source
617            // it is assumed that the tokens with the longest similar lexemes are arranged first so
618            // if a match is found it is accepted and no further searching is done
619
620        if (rulepathIDX >= mActiveTokenState->rootRulePath.size())
621        {
622            // This is very bad and no way to recover so raise exception
623            OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "rule ID exceeds rule base bounds.", "Compiler2Pass::processRulePath");
624        }
625            // record position of last token in container
626            // to be used as the rollback position if a valid token is not found
627        const size_t TokenContainerOldSize = mActiveTokenState->tokenQue.size();
628            const size_t OldCharPos = mCharPos;
629            const size_t OldLinePos = mCurrentLine;
630        const bool OldLabelIsActive = mLabelIsActive;
631        const size_t OldActiveLabelKey = mActiveLabelKey;
632        const String OldLabel = mLabels[OldActiveLabelKey];
633
634            // keep track of what non-terminal token activated the rule
635            size_t ActiveNTTRule = mActiveTokenState->rootRulePath[rulepathIDX].tokenID;
636            // start rule path at next position for definition
637            ++rulepathIDX;
638
639            // assume the rule will pass
640            bool passed = true;
641        bool tokenFound = false;
642            bool endFound = false;
643            bool parseErrorLogged = false;
644
645            // keep following rulepath until the end is reached
646        while (!endFound)
647            {
648                    switch (mActiveTokenState->rootRulePath[rulepathIDX].operation)
649                    {
650
651                    case otAND:
652                            // only validate if the previous rule passed
653                            if (passed)
654                                    passed = ValidateToken(rulepathIDX, ActiveNTTRule);
655                                    // log error message if a previouse token was found in this rule path and current token failed
656                                    if (tokenFound && !parseErrorLogged && !passed)
657                    {
658                        parseErrorLogged = true;
659                        LogManager::getSingleton().logMessage(
660                        "*** ERROR in : " + getClientGrammerName() +
661                        "\nSource: " + mSourceName +
662                        "\nUnkown token found, was expecting: " + getBNFGrammerTextFromRulePath(rulepathIDX)
663                        );
664                        LogManager::getSingleton().logMessage(
665                        " Found: >>>" + mSource->substr(mCharPos, 20) +
666                        "<<<, while in rule path: <" + mActiveTokenState->lexemeTokenDefinitions[ActiveNTTRule].lexeme
667                        + ">"
668                        );
669                    }
670
671                            break;
672
673                    case otOR:
674                            // only validate if the previous rule failed
675                            if ( passed == false )
676                            {
677                                    // clear previous tokens from entry and try again
678                                    mActiveTokenState->tokenQue.resize(TokenContainerOldSize);
679                                    passed = ValidateToken(rulepathIDX, ActiveNTTRule);
680                            }
681                            else
682                            {
683                                    // path passed up to this point therefore finished so pretend end marker found
684                                    endFound = true;
685                            }
686                            break;
687
688                    case otOPTIONAL:
689                            // if previous passed then try this rule but it does not effect succes of rule since its optional
690                            if(passed) ValidateToken(rulepathIDX, ActiveNTTRule);
691                            break;
692
693                    case otREPEAT:
694                            // repeat until called rule fails or cursor does not advance
695                            // repeat is 0 or more times
696                            if (passed)
697                            {
698                                    // keep calling until failure or no change in cursor position
699                    size_t prevPos = mCharPos;
700                                    while ( ValidateToken(rulepathIDX, ActiveNTTRule))
701                                    {
702                        if (mCharPos > prevPos)
703                        {
704                            prevPos = mCharPos;
705                        }
706                        else
707                        {
708                            // repeat failed to advance the cursor position so time to quit since the repeating rule
709                            // path isn't finding anything
710                            // this can happen if the rule being called only has _optional_ rules
711                            // this checking of the cursor positions prevents infinite loop from occuring
712                            break;
713                        }
714                                    }
715                            }
716                            break;
717
718            case otDATA:
719                // skip it, should have been handled by previous operation.
720                break;
721
722            case otNOT_TEST:
723                            // only validate if the previous rule passed
724                            if (passed)
725                {
726
727                    // perform look ahead and test if rule production fails
728                    const size_t la_TokenContainerOldSize = mActiveTokenState->tokenQue.size();
729                        const size_t la_OldCharPos = mCharPos;
730                        const size_t la_OldLinePos = mCurrentLine;
731                        const bool la_OldLabelIsActive = mLabelIsActive;
732                        const size_t la_OldActiveLabelKey = mActiveLabelKey;
733                        const String la_OldLabel = mLabels[la_OldActiveLabelKey];
734
735                    passed = !ValidateToken(rulepathIDX, ActiveNTTRule);
736
737                    // only wanted to take a peek as to what was ahead so now restore back to current position
738                                mActiveTokenState->tokenQue.resize(la_TokenContainerOldSize);
739                                mCharPos = la_OldCharPos;
740                                mCurrentLine = la_OldLinePos;
741                                // restor label state if it was active before not test
742                                if (la_OldLabelIsActive)
743                                {
744                        mActiveLabelKey = la_OldActiveLabelKey;
745                        mLabels[la_OldActiveLabelKey] = la_OldLabel;
746                        mLabelIsActive = la_OldLabelIsActive;
747                                }
748                    // only perform full rollback if tokens found
749                                if (!passed)
750                                {
751                                        // the rule did not validate so get rid of tokens decoded
752                                        // roll back the token container end position to what it was when rule started
753                                        // this will get rid of all tokens that had been pushed on the container while
754                                        // trying to validating this rule
755                                        mActiveTokenState->tokenQue.resize(TokenContainerOldSize);
756                                        //mConstants.resize(OldConstantsSize);
757                                        mCharPos = OldCharPos;
758                                        mCurrentLine = OldLinePos;
759                        // restor label state if it was active before not test
760                        if (OldLabelIsActive)
761                        {
762                            mActiveLabelKey = OldActiveLabelKey;
763                            mLabels[OldActiveLabelKey] = OldLabel;
764                            mLabelIsActive = OldLabelIsActive;
765                        }
766
767                        // terminate rule production processing
768                                    endFound = true;
769                                }
770                }
771                break;
772
773                    case otEND:
774                            // end of rule found so time to return
775                            endFound = true;
776                // only rollback if no tokens found
777                            if (!passed && !tokenFound)
778                            {
779                                    // the rule did not validate so get rid of tokens decoded
780                                    // roll back the token container end position to what it was when rule started
781                                    // this will get rid of all tokens that had been pushed on the container while
782                                    // trying to validating this rule
783                                    mActiveTokenState->tokenQue.resize(TokenContainerOldSize);
784                                    //mConstants.resize(OldConstantsSize);
785                                    mCharPos = OldCharPos;
786                                    mCurrentLine = OldLinePos;
787                            }
788                            else
789                            {
790                                // if the rule path was partially completed, one or more tokeks found, then put a
791                                // warning in the log
792                    if (!passed && tokenFound && !mLabelIsActive)
793                    {
794                        passed = true;
795                        // log last valid token found
796                        const TokenInst& tokenInst = mActiveTokenState->tokenQue[mActiveTokenState->tokenQue.size() - 1];
797                        LogManager::getSingleton().logMessage(
798                            "Last valid token found was at line: " + StringConverter::toString(tokenInst.line));
799                        LogManager::getSingleton().logMessage(
800                            "source hint: >>>" + mSource->substr(tokenInst.pos, 20) + "<<<");
801                    }
802                            }
803                            break;
804
805                    default:
806                            // an exception should be raised since the code should never get here
807                            passed = false;
808                            endFound = true;
809                            break;
810
811                    } // end switch
812            // prevent rollback from occuring if a token was found but later part of rule fails
813            // this allows pass2 to either fix the problem or report the error and continue on.
814            // Don't do this for _no_token_ since its a special system token and has nothing todo with
815            // a successfull parse of the source.  Can check this by looking at mNoTerminalToken state.
816            // if _no_token had just been validated then mNoTerminalToken will be true.
817            if (passed && !mNoTerminalToken)
818                tokenFound = true;
819                    // move on to the next rule in the path
820                    ++rulepathIDX;
821            } // end while
822
823            return passed;
824    }
825
826    //-----------------------------------------------------------------------
827    bool Compiler2Pass::isCharacterLabel(const size_t rulepathIDX)
828    {
829            // assume the test is going to fail
830            bool Passed = false;
831
832        // get token from next rule operation
833        // token string is list of valid single characters
834        // compare character at current cursor position in script to characters in list for a match
835        // if match found then add character to active label
836        // _character_ will not have  a token definition but the next rule operation should be
837        // DATA and have the token ID required to get the character set.
838        const TokenRule& rule = mActiveTokenState->rootRulePath[rulepathIDX + 1];
839        if (rule.operation == otDATA)
840        {
841            const size_t TokenID = rule.tokenID;
842            if (mActiveTokenState->lexemeTokenDefinitions[TokenID].lexeme.find((*mSource)[mCharPos]) != String::npos)
843            {
844                // is a new label starting?
845                // if mLabelIsActive is false then starting a new label so need a new mActiveLabelKey
846                if (!mLabelIsActive)
847                {
848                    // mActiveLabelKey will be the end of the instruction container ie the size of mTokenInstructions
849                    mActiveLabelKey = mActiveTokenState->tokenQue.size();
850                    mLabelIsActive = true;
851                    mNoSpaceSkip = true;
852                    // reset the contents of the label since it might have been used prior to a rollback
853                    mLabels[mActiveLabelKey] = "";
854                }
855                // add the single character to the end of the active label
856                mLabels[mActiveLabelKey] += (*mSource)[mCharPos];
857                Passed = true;
858            }
859        }
860
861        return Passed;
862    }
863    //-----------------------------------------------------------------------
864    bool Compiler2Pass::ValidateToken(const size_t rulepathIDX, const size_t activeRuleID)
865    {
866            size_t tokenlength = 0;
867            // assume the test is going to fail
868            bool Passed = false;
869            size_t tokenID = mActiveTokenState->rootRulePath[rulepathIDX].tokenID;
870            // if terminal token then compare text of lexeme with what is in source
871            if ( (tokenID >= SystemTokenBase) ||
872            !mActiveTokenState->lexemeTokenDefinitions[tokenID].isNonTerminal )
873            {
874            if (tokenID != _character_)
875            {
876                mLabelIsActive = false;
877                // allow spaces to be skipped for next lexeme processing
878                mNoSpaceSkip = false;
879            }
880
881                if (tokenID == _no_space_skip_)
882                {
883                // don't skip spaces to get to next lexeme
884                mNoSpaceSkip = true;
885                // move on to next rule
886                Passed = true;
887                }
888                else if (tokenID == _no_token_)
889                {
890                    // turn on no terminal token processing for next rule
891                mNoTerminalToken = true;
892                // move on to next rule
893                Passed = true;
894                }
895            // if label processing is active ie previous token was _character_
896            // and current token is supposed to be a _character_ then don't
897            // position to next lexeme in source
898                    else if (mNoSpaceSkip || positionToNextLexeme())
899                    {
900                            // if Token is supposed to be a number then check if its a numerical constant
901                            if (tokenID == _value_)
902                            {
903                            float constantvalue = 0.0f;
904                                    if (Passed = isFloatValue(constantvalue, tokenlength))
905                                    {
906                        // key is the next instruction index
907                        mConstants[mActiveTokenState->tokenQue.size()] = constantvalue;
908                                    }
909                            }
910                else // check if user label or valid keyword token
911                {
912                    if (tokenID == _character_)
913                    {
914                        if (Passed = isCharacterLabel(rulepathIDX))
915                            // only one character was processed
916                            tokenlength = 1;
917                    }
918                    else
919                    {
920
921                                    // compare token lexeme text with source text
922                        if (Passed = isLexemeMatch(mActiveTokenState->lexemeTokenDefinitions[tokenID].lexeme, mActiveTokenState->lexemeTokenDefinitions[tokenID].isCaseSensitive))
923                        {
924                            tokenlength = mActiveTokenState->lexemeTokenDefinitions[tokenID].lexeme.length();
925                            // check if terminal token should be ignored ie not put in instruction que
926                            if (mNoTerminalToken)
927                                tokenID = _no_token_;
928                        }
929                        // always clear no terminal token flag.  it only works for one pending terminal token.
930                        mNoTerminalToken = false;
931                    }
932                }
933
934                // turn off label processing if token ID was not for _character_
935                if (tokenID == _character_)
936                {
937                    // _character_ token being processed
938                    // turn off generation of a new token instruction if this is not
939                    // the first _character_ in a sequence of _character_ terminal tokens.
940                    // Only want one _character_ token which Identifies a label
941
942                    if (mActiveTokenState->tokenQue.size() > mActiveLabelKey)
943                    {
944                        // this token is not the first _character_ in the label sequence
945                        // so turn off the token by turning TokenID into _no_token_
946                        tokenID = _no_token_;
947                    }
948                }
949
950                // if valid terminal token was found then add it to the instruction container for pass 2 processing
951                            if (Passed)
952                            {
953                    if (tokenID != _no_token_)
954                    {
955                                        TokenInst newtoken;
956                                        // push token onto end of container
957                                        newtoken.tokenID = tokenID;
958                                        newtoken.NTTRuleID = activeRuleID;
959                                        newtoken.line = mCurrentLine;
960                                        newtoken.pos = mCharPos;
961                        newtoken.found = true;
962
963                                        mActiveTokenState->tokenQue.push_back(newtoken);
964                                        // token action processing
965                                        // if the token has an action then fire previous token action
966                                        checkTokenActionTrigger();
967                    }
968
969                    // update source position
970                                    mCharPos += tokenlength;
971                            } // end if
972                    } // end else if
973
974            }
975            // else a non terminal token was found
976            else
977            {
978                    // execute rule for non-terminal
979                    // get rule_ID for index into  rulepath to be called
980                    Passed = processRulePath(mActiveTokenState->lexemeTokenDefinitions[tokenID].ruleID);
981            }
982
983            return Passed;
984    }
985
986    //-----------------------------------------------------------------------
987    bool Compiler2Pass::isFloatValue(float& fvalue, size_t& charsize) const
988    {
989            // check to see if it is a numeric float value
990            bool valuefound = false;
991
992        const char* startptr = mSource->c_str() + mCharPos;
993            char* endptr = NULL;
994
995            fvalue = (float)strtod(startptr, &endptr);
996            // if a valid float was found then endptr will have the pointer to the first invalid character
997            if (endptr)
998            {
999                    if (endptr>startptr)
1000                    {
1001                            // a valid value was found so process it
1002                            charsize = endptr - startptr;
1003                            valuefound = true;
1004                    }
1005            }
1006
1007            return valuefound;
1008    }
1009
1010    //-----------------------------------------------------------------------
1011    bool Compiler2Pass::isLexemeMatch(const String& lexeme, const bool caseSensitive) const
1012    {
1013            // compare text at source+charpos with the lexeme : limit testing to lexeme size
1014            if (!caseSensitive)
1015            {
1016                String testItem = mSource->substr(mCharPos, lexeme.length());
1017                StringUtil::toLowerCase(testItem);
1018            return (testItem.compare(lexeme) == 0);
1019            }
1020            else
1021            {
1022            return (mSource->compare(mCharPos, lexeme.length(), lexeme) == 0);
1023            }
1024    }
1025
1026    //-----------------------------------------------------------------------
1027    bool Compiler2Pass::positionToNextLexeme()
1028    {
1029        bool validlexemefound = false;
1030            bool endofsource = mCharPos >= mEndOfSource;
1031
1032            while (!validlexemefound && !endofsource)
1033            {
1034                    skipWhiteSpace();
1035                    skipEOL();
1036                    skipComments();
1037                    // have we reached the end of the string?
1038                    if (mCharPos >= mEndOfSource)
1039                            endofsource = true;
1040                    else
1041                    {
1042                            // if ASCII > space then assume valid character is found
1043                            if ((*mSource)[mCharPos] > ' ') validlexemefound = true;
1044                    }
1045            }// end of while
1046
1047            return validlexemefound;
1048    }
1049
1050    //-----------------------------------------------------------------------
1051    void Compiler2Pass::skipComments()
1052    {
1053        if (mCharPos >= mEndOfSource)
1054            return;
1055        // if current char and next are // then search for EOL
1056        if (mSource->compare(mCharPos, 2, "//") == 0)
1057                         findEOL();
1058    }
1059
1060    //-----------------------------------------------------------------------
1061    void Compiler2Pass::findEOL()
1062    {
1063        if (mCharPos >= mEndOfSource)
1064            return;
1065            // find eol charter and move to this position
1066        mCharPos = mSource->find('\n', mCharPos);
1067    }
1068
1069    //-----------------------------------------------------------------------
1070    void Compiler2Pass::skipEOL()
1071    {
1072        if (mCharPos >= mEndOfSource)
1073            return;
1074
1075            if (((*mSource)[mCharPos] == '\n') || ((*mSource)[mCharPos] == '\r'))
1076            {
1077                    mCurrentLine++;
1078                    mCharPos++;
1079            if (mCharPos >= mEndOfSource)
1080                return;
1081                    if (((*mSource)[mCharPos] == '\n') || ((*mSource)[mCharPos] == '\r'))
1082                    {
1083                            mCharPos++;
1084                    }
1085            }
1086    }
1087
1088    //-----------------------------------------------------------------------
1089    void Compiler2Pass::skipWhiteSpace()
1090    {
1091        if (mCharPos >= mEndOfSource)
1092            return;
1093
1094        mCharPos = mSource->find_first_not_of(" \t", mCharPos);
1095    }
1096
1097    //-----------------------------------------------------------------------
1098    void Compiler2Pass::addLexemeToken(const String& lexeme, const size_t token, const bool hasAction, const bool caseSensitive)
1099    {
1100        if (token >= mActiveTokenState->lexemeTokenDefinitions.size())
1101            mActiveTokenState->lexemeTokenDefinitions.resize(token + 1);
1102        // since resizing guarentees the token definition will exist, just assign values to members
1103        LexemeTokenDef& tokenDef = mActiveTokenState->lexemeTokenDefinitions[token];
1104        if (tokenDef.ID != 0)
1105        {
1106            OGRE_EXCEPT(Exception::ERR_DUPLICATE_ITEM, "In " + getClientGrammerName() +
1107                ", lexeme >>>" +
1108                lexeme + "<<< already exists in lexeme token definitions",
1109                "Compiler2Pass::addLexemeToken");
1110        }
1111        tokenDef.ID = token;
1112        tokenDef.lexeme = lexeme;
1113        if (!caseSensitive)
1114            StringUtil::toLowerCase(tokenDef.lexeme);
1115        tokenDef.hasAction = hasAction;
1116        tokenDef.isCaseSensitive = caseSensitive;
1117
1118        mActiveTokenState->lexemeTokenMap[lexeme] = token;
1119    }
1120
1121    //-----------------------------------------------------------------------
1122    void Compiler2Pass::checkTokenActionTrigger(void)
1123    {
1124        size_t lastTokenQuePos = mActiveTokenState->tokenQue.size();
1125        // if there are no token instructions in the que then there is nothing todo
1126        if (lastTokenQuePos == 0)
1127            return;
1128
1129        --lastTokenQuePos;
1130        // if last token index is zero and previous action position are zero  or the two are the same then do nothing
1131        if (lastTokenQuePos == mPreviousActionQuePosition)
1132            return;
1133
1134        const size_t lastTokenID = mActiveTokenState->tokenQue.at(lastTokenQuePos).tokenID;
1135        // dont check actions for system token ID since they are not in lexemeTokenDefinitions
1136        if (lastTokenID >= SystemTokenBase)
1137            return;
1138
1139        // check action trigger if last token has an action
1140        if (mActiveTokenState->lexemeTokenDefinitions.at(lastTokenID).hasAction)
1141        {
1142            // only activate the action belonging to the token found previously
1143            activatePreviousTokenAction();
1144            // current token action now becomes the previous one
1145            mPreviousActionQuePosition = lastTokenQuePos;
1146        }
1147    }
1148
1149    //-----------------------------------------------------------------------
1150    String Compiler2Pass::getBNFGrammerTextFromRulePath(size_t ruleID)
1151    {
1152
1153        String grammerText;
1154
1155        // default to using Client rule path
1156        // check if index is inbounds
1157        if (ruleID >= mActiveTokenState->rootRulePath.size())
1158        {
1159            OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "rule ID exceeds client rule path bounds.", "Compiler2Pass::getBNFGrammerRulePathText");
1160        }
1161        // iterate through rule path and get terminal and non-terminal strings
1162        const TokenRuleContainer& rulePath = mActiveTokenState->rootRulePath;
1163
1164        while (rulePath[ruleID].operation != otEND)
1165        {
1166            // rule text processing - the op instructions, system tokens
1167            switch (rulePath[ruleID].operation)
1168            {
1169            // rule lexeme ::=
1170            case otRULE:
1171                grammerText += "\n" + getLexemeText(ruleID) + " ::=";
1172                break;
1173            // no special processing for AND op
1174            case otAND:
1175                grammerText += " " + getLexemeText(ruleID);
1176                break;
1177            // or | lexeme
1178            case otOR:
1179                grammerText += " | " + getLexemeText(ruleID);
1180                break;
1181            // optional [lexeme]
1182            case otOPTIONAL:
1183                grammerText += " [" + getLexemeText(ruleID) + "]";
1184                break;
1185            // repeat {lexeme}
1186            case otREPEAT:
1187                grammerText += " {" + getLexemeText(ruleID) + "}";
1188                break;
1189            // not test (?!lexeme)
1190            case otNOT_TEST:
1191                grammerText += " (?!" + getLexemeText(ruleID) + ")";
1192                break;
1193            }
1194            // lexeme/token text procesing
1195            ++ruleID;
1196        }
1197
1198        return grammerText;
1199    }
1200
1201    //-----------------------------------------------------------------------
1202
1203    //-----------------------------------------------------------------------
1204    //              Private Methods
1205    //-----------------------------------------------------------------------
1206    //-----------------------------------------------------------------------
1207    String Compiler2Pass::getLexemeText(size_t& ruleID)
1208    {
1209        if (ruleID >= mActiveTokenState->rootRulePath.size())
1210        {
1211            OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR,
1212             "rule ID exceeds client rule path bounds.", "Compiler2Pass::getLexemeText"
1213            );
1214        }
1215
1216        String lexeme;
1217
1218        const TokenRuleContainer& rulePath = mActiveTokenState->rootRulePath;
1219        const size_t tokenID = rulePath[ruleID].tokenID;
1220
1221        if ( tokenID < SystemTokenBase)
1222        {
1223            // non-terminal tokens
1224            if (mActiveTokenState->lexemeTokenDefinitions[tokenID].isNonTerminal)
1225            {
1226                lexeme = "<" + mActiveTokenState->lexemeTokenDefinitions[tokenID].lexeme + ">";
1227            }
1228            else // terminal tokens
1229            {
1230                lexeme = "'" + mActiveTokenState->lexemeTokenDefinitions[tokenID].lexeme + "'";
1231            }
1232        }
1233        else // system token processing
1234        {
1235            switch (rulePath[ruleID].tokenID)
1236            {
1237            case _character_:
1238                // need to get next rule instruction for data
1239                ++ruleID;
1240                // data for _character_ is always a set so put () around text string
1241                lexeme = "{" + mActiveTokenState->lexemeTokenDefinitions[rulePath[ruleID].tokenID].lexeme + ")";
1242                break;
1243            case _value_:
1244                // <#> - need name of label?
1245                lexeme = "<#>";
1246                break;
1247            }
1248        }
1249
1250        return lexeme;
1251    }
1252    //-----------------------------------------------------------------------
1253    void Compiler2Pass::activatePreviousTokenAction(void)
1254    {
1255        const size_t previousTokenID = mActiveTokenState->tokenQue.at(mPreviousActionQuePosition).tokenID;
1256        const LexemeTokenDef& tokenDef = mActiveTokenState->lexemeTokenDefinitions.at(previousTokenID);
1257        if (tokenDef.hasAction)
1258        {
1259            // set the current pass 2 token que position to previous action que position
1260            // assume that pass 2 processing will use tokens downstream
1261            mPass2TokenQuePosition = mPreviousActionQuePosition;
1262            executeTokenAction(previousTokenID);
1263        }
1264    }
1265    //-----------------------------------------------------------------------
1266    void Compiler2Pass::buildClientBNFRulePaths(void)
1267    {
1268        bool isFirstToken = true;
1269        OperationType pendingRuleOp = otAND;
1270
1271        // convert tokens in BNF token que to rule paths
1272        while (getPass2TokenQueCount() > 0)
1273        {
1274            // get a pass 2 token
1275            // if this is the first time getting a token then get the current token
1276            const TokenInst& currentToken  = isFirstToken ? getCurrentToken() : getNextToken();
1277            isFirstToken = false;
1278            // only process the token if its valid
1279            if (currentToken.found)
1280            {
1281                // a valid token has been found, convert to a rule
1282                switch (currentToken.tokenID)
1283                {
1284                case BNF_ID_BEGIN: // <
1285                    extractNonTerminal(pendingRuleOp);
1286                    pendingRuleOp = otAND;
1287                    break;
1288
1289
1290                case BNF_CONSTANT_BEGIN: // <#
1291                    extractNumericConstant(pendingRuleOp);
1292                    pendingRuleOp = otAND;
1293                    break;
1294
1295                case BNF_OR: // |
1296                    pendingRuleOp = otOR;
1297                    break;
1298
1299                case BNF_REPEAT_BEGIN: // {
1300                    pendingRuleOp = otREPEAT;
1301                    break;
1302
1303                case BNF_NO_TOKEN_START: // '
1304                    extractTerminal(pendingRuleOp, true);
1305                    pendingRuleOp = otAND;
1306                    break;
1307
1308                case BNF_SINGLEQUOTE: // '
1309                    extractTerminal(pendingRuleOp);
1310                    pendingRuleOp = otAND;
1311                    break;
1312
1313                case BNF_OPTIONAL_BEGIN: // [
1314                    pendingRuleOp = otOPTIONAL;
1315                    break;
1316
1317                case BNF_NOT_TEST_BEGIN: // (?!
1318                    pendingRuleOp = otNOT_TEST;
1319                    break;
1320
1321                case BNF_SET_BEGIN: // (
1322                    extractSet(pendingRuleOp);
1323                    pendingRuleOp = otAND;
1324                    break;
1325
1326                default:
1327                    // trap closings ie ] } )
1328                    break;
1329                } // end switch
1330            } // end if
1331        } // end while
1332    }
1333
1334    //-----------------------------------------------------------------------
1335    void Compiler2Pass::modifyLastRule(const OperationType pendingRuleOp, const size_t tokenID)
1336    {
1337        // add operation using this token ID to the current rule expression
1338        size_t lastIndex = mClientTokenState->rootRulePath.size();
1339        if (lastIndex == 0)
1340        {
1341            // throw exception since there should have been at least one rule existing
1342            OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "BNF Grammar build rules failed: no previous rule op defined", "Compiler2Pass::modifyLastRule");
1343        }
1344        --lastIndex;
1345        mClientTokenState->rootRulePath[lastIndex].operation = pendingRuleOp;
1346        mClientTokenState->rootRulePath[lastIndex].tokenID = tokenID;
1347        // add new end op token rule
1348        mClientTokenState->rootRulePath.push_back(TokenRule(otEND, 0));
1349    }
1350
1351    //-----------------------------------------------------------------------
1352    size_t Compiler2Pass::getClientLexemeTokenID(const String& lexeme, const bool isCaseSensitive)
1353    {
1354        size_t tokenID = mClientTokenState->lexemeTokenMap[lexeme];
1355
1356        if (tokenID == 0)
1357        {
1358            // lexeme not found so a new entry is made by the system
1359            // note that all lexemes added by the system will not/can not have an action
1360            tokenID = mClientTokenState->lexemeTokenDefinitions.size();
1361            // add identifier to client lexeme tokens
1362            mActiveTokenState = mClientTokenState;
1363            addLexemeToken(lexeme, tokenID, false, isCaseSensitive);
1364            mActiveTokenState = &mBNFTokenState;
1365        }
1366
1367        return tokenID;
1368    }
1369    //-----------------------------------------------------------------------
1370    void Compiler2Pass::extractNonTerminal(const OperationType pendingRuleOp)
1371    {
1372        // begining of identifier
1373        // next token should be for a label
1374        const String& identifierLabel = getNextTokenLabel();
1375        // next token should be id end
1376        getNextToken(BNF_ID_END);
1377        // add identifier to lexeme token definitions but keep case sensitivity
1378        const size_t tokenID = getClientLexemeTokenID(identifierLabel, true);
1379        LexemeTokenDef& tokenDef = mClientTokenState->lexemeTokenDefinitions[tokenID];
1380
1381        // peek at the next token isntruction to see if this
1382        // identifier is for a new rule or is part of the current rule
1383        if (testNextTokenID(BNF_SET_RULE))
1384        {
1385            // consume set rule
1386            getNextToken(BNF_SET_RULE);
1387            // check to make sure this is the first time this rule is being setup by
1388            // verifying rule id is 0
1389            if (tokenDef.ruleID != 0)
1390            {
1391                // this is not the first time for this identifier to be set up as a rule
1392                // since duplicate rules can not exist, throw an exception
1393                OGRE_EXCEPT(Exception::ERR_DUPLICATE_ITEM, "while parsing BNF grammer for: " +
1394                    getClientGrammerName() +
1395                    ", an attempt was made to assign a rule to identifier: " +
1396                    tokenDef.lexeme + ", that already had a rule assigned",
1397                    "Compiler2Pass::extractNonTerminal");
1398            }
1399            // add new rule to end of rule path
1400            mClientTokenState->rootRulePath.push_back(TokenRule(otRULE, tokenID));
1401            tokenDef.ruleID = mClientTokenState->rootRulePath.size() - 1;
1402            // add new end op token rule
1403            mClientTokenState->rootRulePath.push_back(TokenRule(otEND, 0));
1404        }
1405        else // just a reference to a non-terminal
1406        {
1407            modifyLastRule(pendingRuleOp, tokenID);
1408        }
1409
1410        tokenDef.isNonTerminal = true;
1411    }
1412    //-----------------------------------------------------------------------
1413    void Compiler2Pass::extractTerminal(const OperationType pendingRuleOp, const bool notoken)
1414    {
1415        // begining of label
1416        // next token should be for a label
1417        const String& terminalLabel = getNextTokenLabel();
1418        // next token should be single quote end
1419        getNextToken(BNF_SINGLEQUOTE);
1420        // add terminal to lexeme token definitions
1421        // note that if label not in the map it is automatically added
1422        const size_t tokenID = getClientLexemeTokenID(terminalLabel);
1423        if (notoken)
1424            modifyLastRule(otAND, _no_token_);
1425        modifyLastRule(pendingRuleOp, tokenID);
1426    }
1427    //-----------------------------------------------------------------------
1428    void Compiler2Pass::extractSet(const OperationType pendingRuleOp)
1429    {
1430        const String& setLabel = getNextTokenLabel();
1431        // next token should be )
1432        getNextToken(BNF_SET_END);
1433        // add set to lexeme token definitions but keep case sensitivity
1434        const size_t tokenID = getClientLexemeTokenID(setLabel, true);
1435        // add operation using this token ID to the current rule expression
1436        modifyLastRule(pendingRuleOp, _character_);
1437        // add the data required by the character lookup operation
1438        modifyLastRule(otDATA, tokenID);
1439    }
1440    //-----------------------------------------------------------------------
1441    void Compiler2Pass::extractNumericConstant(const OperationType pendingRuleOp)
1442    {
1443        // consume label for constant, don't need it for anything
1444        getNextTokenLabel();
1445
1446        getNextToken(BNF_ID_END); // >
1447        // add operation using this token ID to the current rule expression
1448        modifyLastRule(pendingRuleOp, _value_);
1449    }
1450
1451
1452}
Note: See TracBrowser for help on using the repository browser.