Resolve "Toggle Free Look with Hotkey"
[ryzomcore.git] / ryzom / server / src / ai_service / script_compiler.cpp
blobbafdefd9243636c1e3eda8c1d7027737db165b14
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 #include "stdpch.h"
19 #include "script_vm.h"
20 #include "script_compiler.h"
22 // Compiler special keywords (tokens and rules)
23 static std::string const s_kw_NUMBER = "NUMBER";
24 static std::string const s_kw_CHAIN = "CHAIN";
25 static std::string const s_kw_NAME = "NAME";
26 static std::string const s_kw_STRNAME = "STRNAME";
27 static std::string const s_kw_POINT = "POINT";
28 static std::string const s_kw_readConstVar = "readConstVar";
29 static std::string const s_kw_lineOrClose = "lineOrClose";
30 static std::string const s_kw_params = "params";
31 static std::string const s_kw_tuple = "tuple";
32 static std::string const s_kw_expeclose = "expeclose";
33 static std::string const s_kw_exp = "exp";
34 static std::string const s_kw_somme = "somme";
35 static std::string const s_kw_produit = "produit";
36 static std::string const s_kw_facteur = "facteur";
37 static std::string const s_kw_case = "case";
39 using namespace std;
40 using namespace NLMISC;
42 //////////////////////////////////////////////////////////////////////////
43 // A Small Custom Compiler For AI.
44 // (Token and Grammar are Upgradable, Error returns have to be upgraded).
45 // Ask to Stephane Le Dorze for Explanations.
46 //////////////////////////////////////////////////////////////////////////
48 namespace AICOMP
52 /****************************************************************************/
53 /* Script related classes */
54 /****************************************************************************/
56 //////////////////////////////////////////////////////////////////////////////
57 // CScriptNativeFuncParams //
58 //////////////////////////////////////////////////////////////////////////////
60 CScriptNativeFuncParams::CScriptNativeFuncParams(const std::string &str, FScrptNativeFunc func)
61 : _func(func)
62 , _va(false)
64 const size_t lastPartIndex2=str.find_last_of("_", string::npos);
65 nlassert(lastPartIndex2!=0 && lastPartIndex2!=string::npos);
66 const size_t lastPartIndex=str.find_last_of("_", lastPartIndex2-1);
67 nlassert(lastPartIndex!=0 && lastPartIndex!=string::npos);
69 _nbInParams=lastPartIndex2-lastPartIndex-1;
70 _nbOutParams=str.size()-lastPartIndex2-1;
71 if (_nbInParams==1 && str[lastPartIndex+1] == 'v')
73 _nbInParams = ~0;
74 _va = true;
76 if (_nbOutParams==1 && str[lastPartIndex2+1] == 'v')
78 _nbOutParams = ~0;
79 _va = true;
83 //////////////////////////////////////////////////////////////////////////////
84 // CJumpRememberer //
85 //////////////////////////////////////////////////////////////////////////////
87 class CJumpRememberer
89 public:
90 CJumpRememberer(size_t codeBlockIndex);
91 size_t _where;
92 size_t _codeBlockIndex;
95 //////////////////////////////////////////////////////////////////////////////
96 // CJumpTable //
97 //////////////////////////////////////////////////////////////////////////////
99 class CJumpTable
101 public:
102 CJumpTable(const CSmartPtr<AIVM::CByteCode> &byteCode);
103 virtual ~CJumpTable();
104 void add(CJumpRememberer jump);
105 void newCodeBlock();
106 private:
107 vector<size_t> _codeOffsets;
108 vector<CJumpRememberer> _jumps;
109 CSmartPtr<AIVM::CByteCode> _byteCode;
112 //////////////////////////////////////////////////////////////////////////////
113 // CCaseTracer //
114 //////////////////////////////////////////////////////////////////////////////
116 class CCaseTracer : public CRefCount
118 public:
119 CCaseTracer(const CSmartPtr<CSubRuleTracer> &tracer, const string &sourceName);
121 CSmartPtr<CSubRuleTracer> _tracer;
122 CSmartPtr<const AIVM::CByteCode> _code;
123 size_t _sortValue;
126 /****************************************************************************/
127 /* TOKEN SPECIALIZATION */
128 /****************************************************************************/
130 /* used:
131 [] -> or for character.
132 - -> from before to after (char) (inside []).
133 "c" -> character c (or \c)
135 {xx} -> the token 'xx'.
136 () -> enclosing
138 | -> or
140 + -> card suffixe 1..n
141 * -> card suffixe 0..n
142 ? -> card suffixe 0..1
143 {m,n} -> card suffixe m..n
146 //////////////////////////////////////////////////////////////////////////////
147 // CBracketToken //
148 //////////////////////////////////////////////////////////////////////////////
150 // [a-zA-Z0-9] (readOK)
151 class CBracketToken : public CBasicToken
153 public:
154 CBasicToken *createNew() const;
155 size_t init(TTokenList &tokenList, const string &str, size_t firstIndex, size_t lastIndex);
156 CTokenTestResult buildNode(const std::string &code, size_t &index) const;
157 void dump(size_t indent) const;
159 private:
160 string _Body;
163 //////////////////////////////////////////////////////////////////////////////
164 // CCharToken //
165 //////////////////////////////////////////////////////////////////////////////
167 // "c"
168 class CCharToken : public CBasicToken
170 public:
171 CBasicToken *createNew() const;
172 size_t init(TTokenList &tokenList, const string &str, size_t firstIndex, size_t lastIndex);
173 CTokenTestResult buildNode(const std::string &code, size_t &index) const;
174 void dump(size_t indent) const;
176 private:
177 char _c;
180 //////////////////////////////////////////////////////////////////////////////
181 // CTokenToken //
182 //////////////////////////////////////////////////////////////////////////////
184 // {mytoken}
185 class CTokenToken : public CBasicToken
187 public:
188 CBasicToken *createNew() const;
189 size_t init(TTokenList &tokenList, const string &str, size_t firstIndex, size_t lastIndex);
190 CTokenTestResult buildNode(const std::string &code, size_t &index) const;
191 void dump(size_t indent) const;
193 private:
194 string _tokenName;
197 //////////////////////////////////////////////////////////////////////////////
198 // CParenthesisToken //
199 //////////////////////////////////////////////////////////////////////////////
201 // (..)
202 class CParenthesisToken : public CBasicToken
204 public:
205 CBasicToken *createNew() const;
206 size_t init(TTokenList &tokenList, const string &str, size_t firstIndex, size_t lastIndex);
207 CTokenTestResult buildNode(const std::string &code, size_t &index) const;
208 void dump(size_t indent) const;
210 private:
211 TTokenList _tokenList;
214 //////////////////////////////////////////////////////////////////////////////
215 // COrToken //
216 //////////////////////////////////////////////////////////////////////////////
218 // | (readOK)
219 class COrToken : public CBasicToken
221 public:
222 CBasicToken *createNew() const;
223 size_t init(TTokenList &tokenList, const string &str, size_t firstIndex, size_t lastIndex);
224 CTokenTestResult buildNode(const std::string &code, size_t &index) const;
225 void dump(size_t indent) const;
227 private:
228 CSmartPtr<CBasicToken> firstToken;
229 CSmartPtr<CBasicToken> secondToken;
232 //////////////////////////////////////////////////////////////////////////////
233 // CCardToken //
234 //////////////////////////////////////////////////////////////////////////////
236 // * + ? (readOK)
237 class CCardToken : public CBasicToken
239 public:
240 enum TCardType
242 CARD_ZERO_ONE=0,
243 CARD_ZERO_MANY,
244 CARD_ONE_MANY,
247 CBasicToken *createNew() const;
248 size_t init(TTokenList &tokenList, const string &str, size_t firstIndex, size_t lastIndex);
249 CTokenTestResult buildNode(const std::string &code, size_t &index) const;
250 void dump(size_t indent) const;
252 private:
253 CSmartPtr<CBasicToken> _childToken;
254 TCardType _card;
257 /****************************************************************************/
258 /** Script related classes implementation ***********************************/
259 /****************************************************************************/
261 /****************************************************************************/
262 /* Tokens */
263 /****************************************************************************/
265 //////////////////////////////////////////////////////////////////////////////
266 // CBracketToken implementation //
267 //////////////////////////////////////////////////////////////////////////////
269 CBasicToken *CBracketToken::createNew() const
271 return new CBracketToken();
274 size_t CBracketToken::init(TTokenList &tokenList, const string &str, size_t firstIndex, size_t lastIndex)
276 const size_t index = str.find_first_of("]", firstIndex);
277 nlassert(index!=string::npos);
278 firstIndex++; // pass '['
279 _Body = str.substr(firstIndex, index-firstIndex);
280 tokenList.push_back(this);
281 return index+1;
284 CTokenTestResult CBracketToken::buildNode(const std::string &code, size_t &index) const
286 if (index>=code.size())
287 return CTokenTestResult(NULL, CTokenTestResult::BRULE_INVALID);
289 const char c = code.at(index);
290 bool success = false;
291 bool first = true;
292 FOREACHC(p, string, _Body)
294 const char cp = *p;
295 if (first)
297 if (cp==c)
298 goto found;
299 first=false;
300 continue;
303 if (cp=='-')
305 if ( c>=*(p-1)
306 && c<=*(p+1))
307 goto found;
308 p++; // to pass the last letter of the range
309 continue;
312 if (cp==c)
313 goto found;
316 return CTokenTestResult(NULL, CTokenTestResult::BRULE_INVALID);
317 found:
318 index++;
319 string name;
320 name+=c;
321 CSmartPtr<CCodeNode> codeNode=new CCodeNode("Bracket",name);
322 return CTokenTestResult(codeNode);
325 void CBracketToken::dump(size_t indent) const
327 string str;
328 str.resize(indent,' ');
329 str+="["+_Body+"]";
330 nldebug(str.c_str());
333 //////////////////////////////////////////////////////////////////////////////
334 // CCharToken implementation //
335 //////////////////////////////////////////////////////////////////////////////
337 CBasicToken *CCharToken::createNew() const
339 return new CCharToken();
342 size_t CCharToken::init(TTokenList &tokenList, const string &str, size_t firstIndex, size_t lastIndex)
344 if (str.at(firstIndex)=='\\')
346 _c=str.at(firstIndex+1);
347 tokenList.push_back(this);
348 return firstIndex+2;
350 else
352 nlassert(str.at(firstIndex)=='"');
353 nlassert(str.at(firstIndex+2)=='"');
354 _c=str.at(firstIndex+1);
355 tokenList.push_back(this);
356 return firstIndex+3;
360 CTokenTestResult CCharToken::buildNode(const std::string &code, size_t &index) const
362 if ( index<code.size()
363 && code.at(index)==_c)
365 index++;
366 string name;
367 name+=_c;
369 CSmartPtr<CCodeNode> codeNode=new CCodeNode("Char",name);
370 return CTokenTestResult(codeNode);
372 return CTokenTestResult(NULL, CTokenTestResult::BRULE_INVALID);
375 void CCharToken::dump(size_t indent) const
377 string str;
378 str.resize(indent,' ');
379 str+="'";
380 str+=_c;
381 str+="'";
382 nldebug(str.c_str());
385 //////////////////////////////////////////////////////////////////////////////
386 // CTokenToken implementation //
387 //////////////////////////////////////////////////////////////////////////////
389 CBasicToken *CTokenToken::createNew() const
391 return new CTokenToken();
394 size_t CTokenToken::init(TTokenList &tokenList, const string &str, size_t firstIndex, size_t lastIndex)
396 size_t index=str.find_first_of("}", firstIndex);
397 nlassert(index!=string::npos);
398 firstIndex++;
399 _tokenName=str.substr(firstIndex, index-firstIndex);
400 tokenList.push_back(this);
401 return index+1;
404 CTokenTestResult CTokenToken::buildNode(const std::string &code, size_t &index) const
406 CToken *const token=CCompiler::getInstance().getToken(_tokenName);
407 nlassert(token!=NULL);
408 const size_t lastIndex=index;
409 CTokenTestResult res=token->buildTree(code,index);
410 if (!res.isValid())
412 nlwarning("token %s not succeed index %d", _tokenName.c_str(), index);
413 return res;
416 CSmartPtr<CCodeNode> codeNode=new CCodeTokenNode("token", _tokenName, res.getCode());
417 return CTokenTestResult(codeNode);
420 void CTokenToken::dump(size_t indent) const
422 string str;
423 str.resize(indent,' ');
424 str+="{"+_tokenName+"}";
425 nldebug(str.c_str());
428 //////////////////////////////////////////////////////////////////////////////
429 // CParenthesisToken implementation //
430 //////////////////////////////////////////////////////////////////////////////
432 CBasicToken *CParenthesisToken::createNew() const
434 return new CParenthesisToken();
437 size_t CParenthesisToken::init(TTokenList &tokenList, const string &str, size_t firstIndex, size_t lastIndex)
439 const size_t index=initTokens(_tokenList, str, firstIndex+1, lastIndex);
440 nlassert(str.at(index)==')');
441 tokenList.push_back(this);
442 return index+1;
445 CTokenTestResult CParenthesisToken::buildNode(const std::string &code, size_t &index) const
447 CSmartPtr<CCodeNode> codeNode;
448 CSmartPtr<CCodeNode> nextCodeNode;
449 size_t localIndex=index;
451 FOREACHC(tokenIt, TTokenList, _tokenList)
453 CTokenTestResult res=(*tokenIt)->buildNode(code, localIndex);
454 if (!res.isValid())
455 return CTokenTestResult(NULL, CTokenTestResult::BRULE_INVALID);
457 CSmartPtr<CCodeNode> localCodeNode=res.getCode();
458 if (!localCodeNode)
459 continue;
461 // Chain result node.
462 if (!codeNode)
463 codeNode=localCodeNode;
464 else
465 nextCodeNode->_NextNode=localCodeNode;
466 nextCodeNode=localCodeNode;
467 while (nextCodeNode->_NextNode)
468 nextCodeNode=nextCodeNode->_NextNode;
470 if (codeNode)
471 index=localIndex;
472 return codeNode;
475 void CParenthesisToken::dump(size_t indent) const
477 string str;
478 str.resize(indent,' ');
480 nldebug((str+"(").c_str());
481 FOREACHC(tokenIt,TTokenList,_tokenList)
482 (*tokenIt)->dump(indent+1);
483 nldebug((str+")").c_str());
486 //////////////////////////////////////////////////////////////////////////////
487 // COrToken implementation //
488 //////////////////////////////////////////////////////////////////////////////
490 CBasicToken *COrToken::createNew() const
492 return new COrToken();
495 size_t COrToken::init(TTokenList &tokenList, const string &str, size_t firstIndex, size_t lastIndex)
497 nlassert(tokenList.size()>0);
498 const size_t orIndex=tokenList.size()-1;
499 const size_t finalIndex=initTokens(tokenList, str, firstIndex+1, lastIndex);
500 nlassert(tokenList.size()>1);
502 // insert the or operation.
503 firstToken=tokenList[orIndex];
504 secondToken=tokenList[orIndex+1];
505 tokenList[orIndex]=this;
506 tokenList.erase(tokenList.begin()+orIndex+1);
508 return finalIndex;
511 CTokenTestResult COrToken::buildNode(const std::string &code, size_t &index) const
513 size_t firstIndex=index;
514 CTokenTestResult firstRes=firstToken->buildNode (code, firstIndex);
515 if (!firstRes.isValid())
517 size_t secondIndex=index;
518 CTokenTestResult secondRes=secondToken->buildNode (code, secondIndex);
519 index=secondIndex;
520 return secondRes;
523 index=firstIndex;
524 return firstRes;
527 void COrToken::dump(size_t indent) const
529 firstToken->dump(indent+1);
530 string str;
531 str.resize(indent,' ');
532 str+="|";
533 nldebug(str.c_str());
534 secondToken->dump(indent+1);
537 //////////////////////////////////////////////////////////////////////////////
538 // CCardToken implementation //
539 //////////////////////////////////////////////////////////////////////////////
541 CBasicToken *CCardToken::createNew() const
543 return new CCardToken();
546 size_t CCardToken::init(TTokenList &tokenList, const string &str, size_t firstIndex, size_t lastIndex)
548 nlassert(tokenList.size()>0);
550 switch(str.at(firstIndex))
552 case '?':
553 _card = CARD_ZERO_ONE;
554 break;
555 case '*':
556 _card = CARD_ZERO_MANY;
557 break;
558 case '+':
559 _card = CARD_ONE_MANY;
560 break;
561 default:
562 break;
565 _childToken = tokenList.back();
566 tokenList.back()=this;
568 return firstIndex+1;
571 CTokenTestResult CCardToken::buildNode(const std::string &code, size_t &index) const
573 CSmartPtr<CCodeNode> firstCodeNode = NULL;
574 CSmartPtr<CCodeNode> nextCodeNode = NULL;
576 size_t localIndex = index;
577 size_t nbNodes = 0;
579 CTokenTestResult res;
581 while ((res=_childToken->buildNode(code, localIndex)).isValid())
583 CSmartPtr<CCodeNode> newCodeNode=res.getCode();
584 if (!newCodeNode)
585 continue;
587 if (_card==CARD_ZERO_ONE)
589 index=localIndex;
590 return res;
593 if (!firstCodeNode)
594 firstCodeNode = newCodeNode;
595 else
596 nextCodeNode->_NextNode=newCodeNode;
597 nextCodeNode=newCodeNode;
598 while (nextCodeNode->_NextNode)
599 nextCodeNode = nextCodeNode->_NextNode;
600 nbNodes++;
602 if (firstCodeNode)
603 index = localIndex;
604 return CTokenTestResult(firstCodeNode, (nbNodes==0 && _card==CARD_ONE_MANY)?CTokenTestResult::BRULE_INVALID:CTokenTestResult::BRULE_VALID);
607 void CCardToken::dump(size_t indent) const
609 string str;
610 str.resize(indent,' ');
611 switch(_card)
613 case CARD_ZERO_ONE:
614 str += "?(0..1)";
615 break;
616 case CARD_ZERO_MANY:
617 str += "*(0..n)";
618 break;
619 case CARD_ONE_MANY:
620 str += "+(1..n)";
621 break;
622 default:
623 break;
625 nldebug(str.c_str());
626 _childToken->dump(indent+1);
629 /****************************************************************************/
630 /* Nodes */
631 /****************************************************************************/
633 //////////////////////////////////////////////////////////////////////////////
634 // CCodeNode implementation //
635 //////////////////////////////////////////////////////////////////////////////
637 CCodeNode::CCodeNode(const string &type, const string &name, CSmartPtr<CCodeNode> firstChildNode)
638 : _Type(type)
639 , _Name(name)
640 , _FirstChildNode(firstChildNode)
644 void CCodeNode::dump(size_t indent)
646 string str;
647 str.resize(indent,' ');
648 str+=_Type+":"+_Name;
649 // nlwarning(str.c_str());
650 if (_NextNode)
651 _NextNode->dump(indent);
654 string CCodeNode::getFullName() const
656 if (_NextNode)
657 return _Name+_NextNode->getFullName();
658 return _Name;
661 //////////////////////////////////////////////////////////////////////////////
662 // CCodeTokenNode implementation //
663 //////////////////////////////////////////////////////////////////////////////
665 CCodeTokenNode::CCodeTokenNode(const string &type, const string &name, CSmartPtr<CCodeNode> firstChildNode)
666 : CCodeNode(type, name, firstChildNode)
670 void CCodeTokenNode::dump(size_t indent)
672 string str;
673 str.resize(indent,' ');
674 str+=_Type+":"+_Name;
676 if (_FirstChildNode)
677 str+=":"+_FirstChildNode->getFullName();
679 nldebug(str.c_str());
680 if (_FirstChildNode)
681 _FirstChildNode->dump(indent+1);
683 if (_NextNode)
684 _NextNode->dump(indent);
687 string CCodeTokenNode::getFullName() const
689 string returnName;
690 if (_FirstChildNode)
691 returnName+=_FirstChildNode->getFullName();
692 if (_NextNode)
693 returnName+=_NextNode->getFullName();
694 return returnName;
697 /****************************************************************************/
698 /* */
699 /****************************************************************************/
702 * Lexx
703 x the character "x"
704 "x" an "x", even if x is an operator.
705 \x an "x", even if x is an operator.
706 [xy] the character x or y.
707 [x-z] the characters x, y or z.
708 [^x] any character but x.
709 . any character but newline.
710 ^x an x at the beginning of a line.
711 <y>x an x when Lex is in start condition y.
712 x$ an x at the end of a line.
713 x? an optional x.
714 x* 0,1,2, ... instances of x.
715 x+ 1,2,3, ... instances of x.
716 x|y an x or a y.
717 (x) an x.
718 x/y an x but only if followed by y.
719 {xx} the translation of xx from the
720 definitions section.
721 x{m,n} m through n occurrences of x
724 /* used:
725 - -> from before to after (char)
726 [] -> or for character.
727 \c -> character c
728 "c" -> character c
729 {token} -> match token ?
730 () -> enclosing
731 | -> or
732 + -> card suffixe 1..n
733 * -> card suffixe 0..n
734 ? -> card suffixe 0..1
735 // {m,n} -> card suffixe m..n
739 //////////////////////////////////////////////////////////////////////////////
740 // CRule implementation //
741 //////////////////////////////////////////////////////////////////////////////
743 CRule::CRule(const std::string &name, const string &decl)
744 : _Name(name)
746 setDesc(decl);
749 void CRule::setDesc(const string &decl)
751 const AIVM::CStringSeparator strSep(decl, "|");
752 while (strSep.hasNext())
753 _subRules.push_back(new CSubRule(this,strSep.get()));
756 //////////////////////////////////////////////////////////////////////////////
757 // CSubRule implementation //
758 //////////////////////////////////////////////////////////////////////////////
760 CSubRule::CSubRule(CRule *parent, const string &decl)
761 :_Parent(parent)
763 const AIVM::CStringSeparator strSepTokenAction(decl, ",");
765 // Tokens.
766 if (strSepTokenAction.hasNext())
768 const string Tokens=strSepTokenAction.get();
770 // Tokens.
771 const AIVM::CStringSeparator strSep(Tokens, " ");
772 while (strSep.hasNext())
773 _tokens.push_back(strSep.get());
776 // Action.
777 while (strSepTokenAction.hasNext())
778 _ExecOpCodes.push_back(strSepTokenAction.get());
780 nlassert(_tokens.size()>0);
783 //////////////////////////////////////////////////////////////////////////////
784 // CBasicToken implementation //
785 //////////////////////////////////////////////////////////////////////////////
787 size_t CBasicToken::initTokens(TTokenList &tokenList, const string &str, size_t firstIndex, size_t lastIndex)
789 size_t index=firstIndex;
790 while (index<lastIndex)
792 CSmartPtr<CBasicToken> token=getNewToken(str.at(index));
793 if (!token)
794 break;
795 index=token->init(tokenList, str, index, lastIndex);
797 return index;
800 CSmartPtr<CBasicToken> CBasicToken::getNewToken(char c)
802 if (_BasicTokens.empty())
804 insertBasicToken('[', new CBracketToken());
806 insertBasicToken('"', new CCharToken());
807 insertBasicToken('\\', new CCharToken());
809 insertBasicToken('{', new CTokenToken());
810 insertBasicToken('(', new CParenthesisToken());
811 insertBasicToken('|', new COrToken());
813 insertBasicToken('*', new CCardToken());
814 insertBasicToken('?', new CCardToken());
815 insertBasicToken('+', new CCardToken());
817 TBasicTokenList::iterator it=_BasicTokens.find(c);
818 if (it==_BasicTokens.end())
819 return NULL;
820 return it->second->createNew();
823 void CBasicToken::insertBasicToken(char id, CSmartPtr<CBasicToken> token)
825 _BasicTokens.insert(make_pair(id,token));
829 //////////////////////////////////////////////////////////////////////////////
830 // CToken implementation //
831 //////////////////////////////////////////////////////////////////////////////
833 CToken::CToken(const string &tokenName, const string &tokenDesc)
834 :_tokenName(tokenName), _tokenDesc(tokenDesc)
836 const size_t index = CBasicToken::initTokens(_Tokens, _tokenDesc, 0, _tokenDesc.size());
837 nlassert(index==tokenDesc.size());
840 void CToken::dump() const
842 const string str = "Token:"+_tokenName+" : "+_tokenDesc;
843 nldebug(str.c_str());
844 FOREACHC(tokenIt,TTokenContainer,_Tokens)
845 (*tokenIt)->dump(0);
848 CTokenTestResult CToken::buildTree(const std::string &code, size_t &index)
850 CSmartPtr<CCodeNode> masterNode;
851 CSmartPtr<CCodeNode> currentNode;
852 size_t localIndex = index;
854 FOREACHC(tokenIt,std::vector<CSmartPtr<CBasicToken> >,_Tokens)
856 CTokenTestResult res = (*tokenIt)->buildNode(code,localIndex);
858 if (!res.isValid())
859 return CTokenTestResult(NULL, CTokenTestResult::BRULE_INVALID);
861 CSmartPtr<CCodeNode> const newCodeNode = res.getCode();
862 if (!newCodeNode)
863 continue;
865 // Chain result node.
866 if (!masterNode)
867 masterNode=newCodeNode;
868 else
869 currentNode->_NextNode=newCodeNode;
870 currentNode=newCodeNode;
871 while (currentNode->_NextNode)
872 currentNode=currentNode->_NextNode;
875 if (masterNode)
876 index=localIndex;
877 return masterNode;
880 const std::string &CToken::getName() const
882 return _tokenName;
885 //////////////////////////////////////////////////////////////////////////////
886 // CCompiler implementation //
887 //////////////////////////////////////////////////////////////////////////////
889 CCompiler::CCompiler()
891 string cfgFile = NLMISC::CPath::lookup ("ais_script_compiler.cfg", false);
892 if (!cfgFile.empty ())
894 NLMISC::CIFile file(cfgFile);
895 const int bufferSize = 256;
896 char buffer[bufferSize];
898 while (!file.eof())
900 file.getline(buffer, bufferSize);
901 if (buffer[0]=='#' || buffer[0]=='\0') // Skip lines beginning with a # and empty lines
902 continue;
903 string line = buffer;
904 const string sep1 = ": ";
905 const string sep2 = "=";
906 string part1, part2, part3;
908 string::size_type pos1 = line.find(sep1);
909 if (pos1!=string::npos)
911 pos1+=sep1.size();
912 string::size_type pos2 = line.find(sep2, pos1);
913 if (pos2!=string::npos)
915 pos2+=sep2.size();
916 if (pos1!=string::npos && pos2!=string::npos)
918 part1 = line.substr(0, pos1-sep1.size()); // begin to sep1
919 part2 = line.substr(pos1, pos2-pos1-sep2.size()); // sep1 to sep2
920 part3 = line.substr(pos2); // sep2 to end
925 if (part1.size()!=0 && part2.size()!=0 && part3.size()!=0)
927 if (part1=="token")
929 addToken(part2, part3);
931 else if (part1=="rule")
933 addRule(part2, part3);
936 else
938 nlwarning("Invalid script line: \"%s\"", line.c_str());
942 // Basic Opcodes ---------------------------------------------------------
943 #define REGISTER_OPCODE(__opcode) addOpcode(#__opcode,::AIVM::CScriptVM::__opcode)
945 REGISTER_OPCODE(AND);
946 REGISTER_OPCODE(OR);
947 REGISTER_OPCODE(NOT);
949 REGISTER_OPCODE(EQ);
950 REGISTER_OPCODE(NEQ);
951 REGISTER_OPCODE(INF);
952 REGISTER_OPCODE(INFEQ);
953 REGISTER_OPCODE(SUP);
954 REGISTER_OPCODE(SUPEQ);
956 REGISTER_OPCODE(ADD);
957 REGISTER_OPCODE(SUB);
958 REGISTER_OPCODE(MUL);
959 REGISTER_OPCODE(DIV);
961 REGISTER_OPCODE(PUSH_ON_STACK);
962 REGISTER_OPCODE(POP);
964 REGISTER_OPCODE(SET_VAR_VAL);
965 REGISTER_OPCODE(SET_STR_VAR_VAL);
966 REGISTER_OPCODE(SET_CTX_VAR_VAL);
968 REGISTER_OPCODE(PUSH_VAR_VAL);
969 REGISTER_OPCODE(PUSH_STR_VAR_VAL);
970 REGISTER_OPCODE(PUSH_CTX_VAR_VAL);
972 // REGISTER_OPCODE(SET_OTHER_VAR_VAL);
973 // REGISTER_OPCODE(SET_OTHER_STR_VAR_VAL);
974 // REGISTER_OPCODE(SET_OTHER_CTX_VAR_VAL);
976 // REGISTER_OPCODE(PUSH_OTHER_VAR_VAL);
977 // REGISTER_OPCODE(PUSH_OTHER_STR_VAR_VAL);
978 // REGISTER_OPCODE(PUSH_OTHER_CTX_VAR_VAL);
980 REGISTER_OPCODE(SET_CONTEXT_VAR_VAL);
981 REGISTER_OPCODE(SET_CONTEXT_STR_VAR_VAL);
982 REGISTER_OPCODE(SET_CONTEXT_CTX_VAR_VAL);
984 REGISTER_OPCODE(PUSH_CONTEXT_VAR_VAL);
985 REGISTER_OPCODE(PUSH_CONTEXT_STR_VAR_VAL);
986 REGISTER_OPCODE(PUSH_CONTEXT_CTX_VAR_VAL);
988 REGISTER_OPCODE(JUMP);
989 REGISTER_OPCODE(JE);
990 REGISTER_OPCODE(JNE);
992 REGISTER_OPCODE(PUSH_PRINT_STRING);
993 REGISTER_OPCODE(PUSH_PRINT_VAR);
994 REGISTER_OPCODE(PUSH_PRINT_STR_VAR);
995 REGISTER_OPCODE(PRINT_STRING);
997 REGISTER_OPCODE(LOG_STRING);
999 REGISTER_OPCODE(FUNCTION);
1000 REGISTER_OPCODE(CALL);
1002 REGISTER_OPCODE(PUSH_THIS);
1003 REGISTER_OPCODE(PUSH_GROUP);
1005 REGISTER_OPCODE(PUSH_STRING);
1006 REGISTER_OPCODE(ASSIGN_FUNC_FROM);
1008 REGISTER_OPCODE(NATIVE_CALL);
1009 REGISTER_OPCODE(RAND);
1010 REGISTER_OPCODE(RANDEND);
1012 REGISTER_OPCODE(RET);
1014 REGISTER_OPCODE(EOP);
1016 REGISTER_OPCODE(ONCHILDREN);
1017 REGISTER_OPCODE(SWITCH);
1019 REGISTER_OPCODE(INCR);
1020 REGISTER_OPCODE(DECR);
1022 REGISTER_OPCODE(CONCAT);
1023 REGISTER_OPCODE(FTOS);
1026 // Natives Funcs ---------------------------------------------------------
1028 registerNativeFunc();
1031 CSmartPtr<CSubRuleTracer> CCompiler::buildCodeTree (const string &code) const
1033 size_t tokenIndex=0;
1034 size_t index=0;
1036 CSmartPtr<CSubRuleTracer> lastInsertedTracer;
1037 CSmartPtr<CSubRuleTracer> firstInsertedTracer;
1039 // For each token of the code.
1040 while (index<code.size())
1042 std::string tokenName;
1043 std::string textValue;
1044 const size_t lastIndex=index;
1045 if (!getNextToken(code, index, tokenName, textValue))
1046 break;
1048 tokenIndex++;
1050 #ifdef DISPLAY_INFOS
1052 string str(">> TOKEN: ");
1053 str+=tokenName;
1054 str+=" "+toString(tokenIndex);
1055 nldebug(str.c_str());
1057 #endif
1058 CSmartPtr<CSubRuleTracer> tracer=new CSubRuleTracer(lastIndex, index-1, tokenName, textValue);
1060 if (lastInsertedTracer.isNull())
1062 lastInsertedTracer=tracer;
1063 firstInsertedTracer=tracer;
1065 else
1067 tracer->checkRules(index-1);
1068 lastInsertedTracer=tracer;
1071 return firstInsertedTracer;
1075 CSmartPtr<const AIVM::CByteCode> CCompiler::compileCode (const std::vector<std::string> &sourceCodeLines, const string &fullName) const
1077 CSubRuleTracer::_PreviousTracers.clear();
1078 CSubRuleTracer::_NextTracers.clear();
1080 typedef const std::vector<std::string> TList; // because there a problem with const in the macro.
1081 string code="{ \n";
1082 // Concatenates lines, avoid parts after // ..
1083 FOREACHC(itArg, TList, sourceCodeLines)
1085 const string &str=*itArg;
1086 size_t index = str.find("//",0);
1087 if (index == string::npos)
1088 code += str;
1089 else {
1090 // We have a potential comment. Now check if it is quoted or not
1091 bool inQuote = false;
1092 uint i = 0;
1093 for (;;)
1095 if ('"' == str[i])
1096 inQuote = !inQuote;
1098 if ( !inQuote && ('/' == str[i]) )
1100 ++i;
1101 if ('/' == str[i])
1102 break;
1104 code += '/';
1106 code += str[i];
1107 ++i;
1108 if (str.size() == i)
1109 break;
1113 code+="\n "; // additional ..
1115 code+="}";
1116 return compileCode(code, fullName);
1119 CSmartPtr<const AIVM::CByteCode> CCompiler::compileCode (const string &sourceCode, const string &fullName) const
1121 bool debug = NLNET::IService::getInstance()->haveArg('d');
1122 CSmartPtr<const AIVM::CByteCode> byteCode = compileCodeYacc (sourceCode, fullName, debug, false);
1124 if (debug)
1126 // Generate the old byte code
1127 CSmartPtr<const AIVM::CByteCode> oldbyteCode = compileCodeOld (sourceCode, fullName, debug);
1130 return byteCode;
1133 void CCompiler::dumpByteCode (const string &sourceCode, const string &fullName, CSmartPtr<const AIVM::CByteCode> &byteCode,
1134 const string &directory) const
1136 // Build a valid filename
1137 string tmp = fullName;
1138 string::size_type pos;
1139 while ((pos=tmp.find (':')) != string::npos)
1140 tmp[pos] = '-';
1142 // Create the bytecode directory
1143 CFile::createDirectory (directory.c_str ());
1145 // Save the bytecode
1146 nlinfo ("saving bytecode for %s", tmp.c_str ());
1147 FILE *file = fopen ((directory+"/"+tmp+".bin").c_str(), "wb");
1148 if (file)
1150 fwrite (&byteCode->_opcodes[0], sizeof(size_t), byteCode->_opcodes.size (), file);
1151 fclose (file);
1153 else
1154 nlwarning ("can't open %s for writing", tmp.c_str ());
1156 // Create the source directory
1157 CFile::createDirectory ("iasources");
1159 // Save the source code
1160 file = fopen (("iasources/"+tmp+".src").c_str(), "w");
1161 if (file)
1163 fwrite (sourceCode.c_str (), sourceCode.size(), 1, file);
1164 fclose (file);
1166 else
1167 nlstopex(("can't open %s for writing", tmp.c_str ()));
1170 CSmartPtr<const AIVM::CByteCode> CCompiler::compileCodeOld (const string &sourceCode, const string &fullName, bool debug) const
1172 CSmartPtr<AIVM::CByteCode> byteCode = new AIVM::CByteCode(fullName);
1174 nldebug("script compilation of %s", fullName.c_str());
1175 string code=sourceCode;
1178 nldebug(">parsing source code ..");
1179 CSmartPtr<CSubRuleTracer> tracer=buildCodeTree (code);
1181 if (tracer!=NULL)
1183 nldebug(">generating code tree ..");
1184 tracer=tracer->codifyTree (); // removes ambiguities, at this point a good or bad code is compiled.
1186 nldebug(">generating byte code ..");
1187 tracer->getHigherParent()->generateCode(byteCode);
1189 else
1191 nldebug(">empty source code ..");
1194 nldebug("compilation success. (code size %d)",byteCode->_opcodes.size()*4);
1196 CSmartPtr<const AIVM::CByteCode> tmp=&(*byteCode);
1198 if (debug)
1199 dumpByteCode (sourceCode, fullName, tmp, "iaoldbytecode");
1201 return tmp;
1203 catch (const Exception &e)
1205 nlwarning("compilation failed for %s", fullName.c_str());
1206 nlwarning(e.what());
1209 return NULL;
1212 CSmartPtr<const AIVM::CByteCode> CCompiler::compileCodeYacc (const string &sourceCode, const string &fullName, bool debug, bool win32report) const
1214 CSmartPtr<AIVM::CByteCode> byteCode = new AIVM::CByteCode(fullName);
1216 if (aiCompile (byteCode->_opcodes, sourceCode.c_str (), fullName.c_str (), win32report))
1218 CSmartPtr<const AIVM::CByteCode> tmp=&(*byteCode);
1220 if (debug)
1221 dumpByteCode (sourceCode, fullName, tmp, "ianewbytecode");
1223 return tmp;
1225 else if (debug)
1226 nlstop;
1228 return NULL;
1231 void CCompiler::addToken(const string &tokenName, const string &tokenDesc)
1233 CToken *token = new CToken(tokenName, tokenDesc);
1234 _Tokens.push_back(token);
1237 CToken *CCompiler::getToken(const string &tokenName)
1239 FOREACH (tokenIt, TTokenList, _Tokens)
1241 if ((*tokenIt)->getName()==tokenName)
1242 return *tokenIt;
1244 nlassert(false);
1245 return NULL;
1248 /// Helper function
1249 static void displayErrorLinesForIndex(const string &text, size_t &index)
1251 AIVM::CStringSeparator sep(text,"\n\r");
1252 size_t totalIndex = 0;
1253 string tmp;
1254 size_t lineIndex = 0;
1256 while (sep.hasNext())
1258 tmp = sep.get();
1259 const size_t stringSize = tmp.size()+1; // +1 for the \n text.
1260 if (totalIndex+stringSize>index)
1261 break;
1262 totalIndex += stringSize;
1263 lineIndex++;
1267 string lineoStr("at line ");
1268 lineoStr += toString(lineIndex);
1269 nlwarning(lineoStr.c_str());
1272 nlwarning(tmp.c_str());
1274 string indexerStr;
1275 indexerStr.resize(index-totalIndex,' ');
1276 indexerStr += "^";
1277 nlwarning(indexerStr.c_str());
1282 bool CCompiler::getNextToken(const string &text, size_t &index, string &tokenName, string &textValue)
1284 char c=text.at(index);
1285 while (c==' '||c=='\n'||c=='\r'||c=='\t') // to avoid blanks, returns and Tabs.
1287 index++;
1288 if (index==text.size())
1289 return false;
1290 c = text.at(index);
1292 const size_t firstIndex=index;
1294 static const string unknownS("unknown");
1295 FOREACH(tokenIt, TTokenList, _Tokens)
1297 CTokenTestResult res=(*tokenIt)->buildTree(text, index);
1298 if (res.isValid()) // we found it !!
1300 tokenName = (*tokenIt)->getName();
1301 textValue = text.substr(firstIndex, index-firstIndex);
1302 return true;
1306 displayErrorLinesForIndex(text, index);
1307 throw (EScriptError("(Unrecognized pattern)", index));
1308 nlassert(false);
1309 return false;
1312 void CCompiler::addOpcode(const std::string &str,AIVM::CScriptVM::EOpcode const& op)
1314 nlassert(_Opcodes.find(str)==_Opcodes.end());
1315 _Opcodes.insert(make_pair(str, op));
1318 const string &CCompiler::getOpcodeName(AIVM::CScriptVM::EOpcode const& op)
1320 static string unk("---");
1321 FOREACHC(opIt, TOpcodeMap, _Opcodes)
1323 if (opIt->second==op)
1324 return opIt->first;
1326 return unk;
1329 void CCompiler::addNativeFunc(std::string const& signature, FScrptNativeFunc const& func)
1331 TStringId strId = CStringMapper::map(signature);
1332 nlassert(_NativeFunctions.find(strId)==_NativeFunctions.end());
1334 _NativeFunctions.insert(make_pair(strId, new CScriptNativeFuncParams(signature, func)));
1337 void CCompiler::addDeprecatedNativeFunc(std::string const& signature)
1339 TStringId strId = CStringMapper::map(signature);
1340 nlassert(_NativeFunctions.find(strId)==_NativeFunctions.end());
1342 _NativeFunctions.insert(make_pair(strId, new CScriptNativeFuncParams(signature, NULL)));
1345 CScriptNativeFuncParams* CCompiler::getNativeFunc(const std::string &funcName, const std::string &inparams, const std::string &outparams)
1347 std::string signature;
1348 TStringId strId;
1349 TNativeFuncMap::iterator it;
1350 // Normal params
1351 signature = funcName + "_" + inparams + "_" + outparams;
1352 strId = CStringMapper::map(signature);
1353 it = _NativeFunctions.find(strId);
1354 if (it!=_NativeFunctions.end())
1355 return it->second;
1356 // Variable arguments (va) as input
1357 signature = funcName + "_v_" + outparams;
1358 strId = CStringMapper::map(signature);
1359 it = _NativeFunctions.find(strId);
1360 if (it!=_NativeFunctions.end())
1361 return it->second;
1362 // VA as output
1363 signature = funcName + "_" + inparams + "_v";
1364 strId = CStringMapper::map(signature);
1365 it = _NativeFunctions.find(strId);
1366 if (it!=_NativeFunctions.end())
1367 return it->second;
1368 // VA as input and output
1369 signature = funcName + "_v_v";
1370 strId = CStringMapper::map(signature);
1371 it = _NativeFunctions.find(strId);
1372 if (it!=_NativeFunctions.end())
1373 return it->second;
1374 return NULL;
1377 AIVM::CScriptVM::EOpcode CCompiler::getOpcodeAndValue(const std::string &str, std::string &value)
1379 AIVM::CScriptVM::EOpcode opcode=AIVM::CScriptVM::INVALID_OPCODE;
1380 AIVM::CStringSeparator sep(str," \t");
1381 if (sep.hasNext())
1383 TOpcodeMap::iterator it=_Opcodes.find(sep.get());
1384 if (it!=_Opcodes.end())
1385 opcode=it->second;
1387 if (sep.hasNext())
1388 value=sep.get();
1389 return opcode;
1392 void CCompiler::addRule (const string &ruleName, const string &ruleDesc)
1394 CRule *rule=getRule (ruleName);
1395 if (rule)
1397 rule->setDesc(ruleDesc);
1398 return;
1400 rule=new CRule(ruleName, ruleDesc);
1401 _Rules.push_back(rule);
1404 CSmartPtr<CRule> CCompiler::getRule (const string &ruleName)
1406 FOREACH(ruleIt, TRuleList, _Rules)
1408 if ((*ruleIt)->_Name==ruleName)
1409 return *ruleIt;
1411 return NULL;
1414 CCompiler::TOpcodeMap CCompiler::_Opcodes;
1415 CCompiler::TRuleList CCompiler::_Rules;
1416 CCompiler::TTokenList CCompiler::_Tokens;
1417 CCompiler::TNativeFuncMap CCompiler::_NativeFunctions;
1418 CCompiler* CCompiler::_Instance = NULL;
1419 CBasicToken::TBasicTokenList CBasicToken::_BasicTokens;
1421 //////////////////////////////////////////////////////////////////////////////
1422 // CJumpRememberer implementation //
1423 //////////////////////////////////////////////////////////////////////////////
1425 CJumpRememberer::CJumpRememberer(size_t codeBlockIndex)
1426 : _where(~0)
1427 , _codeBlockIndex(codeBlockIndex)
1431 //////////////////////////////////////////////////////////////////////////////
1432 // CJumpTable implementation //
1433 //////////////////////////////////////////////////////////////////////////////
1435 CJumpTable::CJumpTable(const CSmartPtr<AIVM::CByteCode> &byteCode)
1436 : _byteCode(byteCode)
1438 _codeOffsets.push_back(0);
1441 CJumpTable::~CJumpTable()
1443 newCodeBlock();
1445 FOREACHC(jumpIt, vector<CJumpRememberer>, _jumps)
1447 nlassert(jumpIt->_where<_byteCode->_opcodes.size());
1448 nlassert(jumpIt->_codeBlockIndex<_codeOffsets.size());
1449 _byteCode->_opcodes[jumpIt->_where]=_codeOffsets[jumpIt->_codeBlockIndex]-jumpIt->_where;
1453 void CJumpTable::add(CJumpRememberer jump)
1455 jump._where=_byteCode->_opcodes.size();
1456 _jumps.push_back(jump);
1459 void CJumpTable::newCodeBlock()
1461 _codeOffsets.push_back(_byteCode->_opcodes.size());
1464 //////////////////////////////////////////////////////////////////////////////
1465 // CCaseTracer implementation //
1466 //////////////////////////////////////////////////////////////////////////////
1468 CCaseTracer::CCaseTracer(const CSmartPtr<CSubRuleTracer> &tracer, const string &sourceName)
1469 : _tracer(tracer)
1471 const CSubRuleTracer *chldTracer=tracer->getChildForName(s_kw_readConstVar);
1472 const CSubRuleTracer *valChldTracer=NULL;
1474 breakable
1476 if (valChldTracer=chldTracer->getChildForName(s_kw_CHAIN))
1478 const string &strRef=valChldTracer->_TextValue;
1479 TStringId strId;
1480 if ( strRef.at(0)=='"'
1481 && strRef.at(0)==strRef.at(strRef.size()-1))
1482 strId=CStringMapper::map(strRef.substr(1,strRef.size()-2));
1483 else
1484 strId=CStringMapper::map(strRef);
1485 _sortValue=*((size_t*)&strId);
1486 break;
1488 if (valChldTracer=chldTracer->getChildForName(s_kw_NUMBER))
1490 const string &strRef=valChldTracer->_TextValue;
1491 float f;
1492 NLMISC::fromString(strRef, f);
1493 _sortValue=*((size_t*)&f);
1494 break;
1496 if (!valChldTracer)
1497 throw Exception("Invalid case parameter");
1500 chldTracer=tracer->getChildForName(s_kw_lineOrClose);
1502 CSmartPtr<AIVM::CByteCode> bc=new AIVM::CByteCode(sourceName);
1503 chldTracer->generateCode(bc);
1504 _code=&(*bc);
1507 typedef std::map<size_t, CSmartPtr<CCaseTracer> > TCaseTracerList;
1509 //////////////////////////////////////////////////////////////////////////////
1510 // CSubRuleTracer implementation //
1511 //////////////////////////////////////////////////////////////////////////////
1513 CSubRuleTracer::CSubRuleTracer(size_t tokenStartIndex, size_t currentTokenIndex, const string &name, const string &textValue)
1514 : _index(0)
1515 , _tokenStartIndex(tokenStartIndex)
1516 , _tokenIndex(currentTokenIndex)
1517 , _Name(name)
1518 , _TextValue(textValue)
1519 , _Valid(false)
1521 updatePreviousNext();
1524 CSubRuleTracer::CSubRuleTracer(NLMISC::CSmartPtr<CSubRule> subRule, size_t tokenStartIndex, size_t currentTokenIndex, const std::string &name, const std::string &textValue)
1525 : _index(0)
1526 , _tokenStartIndex(tokenStartIndex)
1527 , _tokenIndex(currentTokenIndex)
1528 , _Name(name)
1529 , _TextValue(textValue)
1530 , _Valid(false)
1531 , _subRule(subRule)
1533 updatePreviousNext();
1536 CSubRuleTracer::CSubRuleTracer(const CSubRuleTracer &otherSRT)
1537 : _index(otherSRT._index)
1538 , _tokenStartIndex(otherSRT._tokenStartIndex)
1539 , _tokenIndex(otherSRT._tokenIndex)
1540 , _Name(otherSRT._Name)
1541 , _TextValue(otherSRT._TextValue)
1542 , _Valid(otherSRT._Valid)
1543 , _subRule(otherSRT._subRule)
1545 updatePreviousNext();
1548 CSubRuleTracer::~CSubRuleTracer()
1552 void CSubRuleTracer::updatePreviousNext()
1554 // Next registration.
1556 CSubRuleTracer::TOrderedTracers::iterator it=CSubRuleTracer::_NextTracers.find(_tokenStartIndex);
1557 if (it==CSubRuleTracer::_NextTracers.end())
1559 CSubRuleTracer::_NextTracers.insert(make_pair(_tokenStartIndex, CSubRuleTracer::TTracersSet()) );
1560 it=CSubRuleTracer::_NextTracers.find(_tokenStartIndex);
1561 nlassert(it!=CSubRuleTracer::_NextTracers.end());
1563 CSubRuleTracer::TTracersSet &set=it->second;
1564 set.insert(this);
1567 // Previous registration.
1569 CSubRuleTracer::TOrderedTracers::iterator it=CSubRuleTracer::_PreviousTracers.find(_tokenIndex);
1570 if (it==CSubRuleTracer::_PreviousTracers.end())
1572 CSubRuleTracer::_PreviousTracers.insert(make_pair(_tokenIndex, CSubRuleTracer::TTracersSet()) );
1573 it=CSubRuleTracer::_PreviousTracers.find(_tokenIndex);
1574 nlassert(it!=CSubRuleTracer::_PreviousTracers.end());
1576 CSubRuleTracer::TTracersSet &set=it->second;
1577 set.insert(this);
1581 CSubRuleTracer::TOrderedTracers CSubRuleTracer::_PreviousTracers;
1582 CSubRuleTracer::TOrderedTracers CSubRuleTracer::_NextTracers;
1584 // Removers ------------------------------------------------------------------
1586 void CSubRuleTracer::removeParent(CSubRuleTracer *tracer)
1588 FOREACH(itParent, TSubRuleTracerList, _parentTracers)
1590 if ((*itParent)==tracer)
1592 _parentTracers.erase(itParent);
1593 return;
1596 nlassert(false);
1598 void CSubRuleTracer::removeChild(CSubRuleTracer *tracer)
1600 FOREACH(itChild, TSubRuleTracerList, _childTracers)
1602 if ((*itChild)==tracer)
1604 _childTracers.erase(itChild);
1605 return;
1608 nlassert(false);
1611 // ---------------------------------------------------------------------------
1613 void CSubRuleTracer::detachFromEveryBody()
1615 FOREACH(it, TSubRuleTracerList, _parentTracers)
1616 (*it)->removeChild(this);
1617 FOREACH(it, TSubRuleTracerList, _childTracers)
1618 (*it)->removeParent(this);
1621 void CSubRuleTracer::iterateToMarkValidTracer()
1623 _Valid=true;
1624 FOREACH(childIt, TSubRuleTracerList, _childTracers)
1625 (*childIt)->iterateToMarkValidTracer();
1628 CSmartPtr<CSubRuleTracer> CSubRuleTracer::getValidTracer() const
1630 CSmartPtr<CSubRuleTracer> sRT=new CSubRuleTracer(*this);
1632 sRT->_childTracers.reserve(_childTracers.size());
1633 FOREACHC(childIt, TSubRuleTracerList, _childTracers)
1635 CSmartPtr<CSubRuleTracer> child=(*childIt)->getValidTracer();
1636 child->_parentTracers.push_back(sRT);
1637 sRT->_childTracers.push_back(child);
1640 return sRT;
1643 void CSubRuleTracer::flushErrors ()
1645 FOREACH(itChild, TSubRuleTracerList, _childTracers)
1647 nlassert((*itChild)->_parentTracers.size()==1); // if there is a problem, we can see it here .. :)
1648 (*itChild)->flushErrors();
1652 void CSubRuleTracer::removeInvalidTracers()
1654 TSubRuleTracerList parentList=_parentTracers; // copy to avoid problems.
1656 if ( !_Valid
1657 && _childTracers.size()>0) // if not valid and not a base tracer.
1659 detachFromEveryBody();
1662 FOREACH(parentIt, TSubRuleTracerList, parentList)
1663 (*parentIt)->removeInvalidTracers();
1667 CSmartPtr<CSubRuleTracer> CSubRuleTracer::codifyTree()
1669 if (getHigherParent()==this) // an error occurred.
1671 bool errorAppened=false;
1672 // Check.
1674 CSubRuleTracer *tracer=this;
1675 while (tracer!=NULL)
1677 if (tracer->_parentTracers.size()==0) // if there is a problem, we can see it here .. :)
1679 errorAppened=true;
1680 nlwarning("an grammar error appeared that breaks this enclosing: ");
1681 tracer->dump(10);
1683 tracer=tracer->getNextLower();
1686 // Flush errors.
1687 getHigherParent()->flushErrors();
1688 if (errorAppened)
1689 throw Exception("Script Grammar Error");
1690 return getHigherParent();
1692 else
1694 CSmartPtr<CSubRuleTracer> returnTracer=getHigherParent()->getValidTracer();
1696 // Flush errors.
1697 returnTracer->flushErrors();
1698 return returnTracer;
1703 CSubRuleTracer *CSubRuleTracer::getNextLower() const
1705 CSubRuleTracer::TOrderedTracers::iterator it=CSubRuleTracer::_NextTracers.find(_tokenIndex+1);
1706 if (it!=CSubRuleTracer::_NextTracers.end())
1708 CSubRuleTracer::TTracersSet &set=it->second;
1709 FOREACH(setIt, CSubRuleTracer::TTracersSet, set)
1711 if ((*setIt)->_childTracers.size()==0)
1712 return (*setIt);
1715 return NULL;
1719 CSubRuleTracer *CSubRuleTracer::getHigherParent()
1721 if (_parentTracers.size()>0)
1722 return _parentTracers.back()->getHigherParent();
1723 return this;
1726 // Rule finding can be optimized here with an multimap tree.
1727 void CSubRuleTracer::checkRules(size_t currentToken)
1729 FOREACH(ruleIt, CCompiler::TRuleList, CCompiler::_Rules )
1731 FOREACH(subRuleIt, TSubRuleList, (*ruleIt)->_subRules)
1733 TSubRuleTracerList childTracers;
1734 checkRule(*subRuleIt,0, currentToken, childTracers);
1739 void CSubRuleTracer::checkRule(CSubRule *rule, size_t index, size_t currentToken, TSubRuleTracerList &childTracers)
1741 bool equal=false;
1742 string token=rule->_tokens[rule->_tokens.size()-1-index];
1743 if (token.at(0)=='+')
1744 equal=(_Name==token.substr(1,token.size()-1));
1745 else
1746 equal=(_Name==token);
1748 if (!equal) // if not equal, check if its equal to the previous one. (and if this one was 'multi')
1750 if (index==0) // failed.
1751 return;
1753 token=rule->_tokens[rule->_tokens.size()-index];
1754 if (token.at(0)!='+')
1755 return;
1757 equal=(_Name==token.substr(1,token.size()-1));
1758 if (!equal) // failed.
1759 return;
1760 index--;
1763 childTracers.push_back(this);
1765 if (index==rule->_tokens.size()-1) // do we match a rule here ?
1767 CSubRuleTracer *lastTracer=childTracers.back();
1769 CSmartPtr<CSubRuleTracer> newTracer=new CSubRuleTracer(rule, lastTracer->_tokenStartIndex, currentToken, rule->_Parent->_Name, "");
1771 // we have to copy this vector inverted ..
1772 newTracer->_childTracers.resize(childTracers.size());
1773 std::copy(childTracers.begin(), childTracers.end(), newTracer->_childTracers.rbegin());
1775 FOREACH(childIt, TSubRuleTracerList, childTracers)
1776 (*childIt)->_parentTracers.push_back(newTracer);
1778 // Recursively trying to complete others higher rules starting with this new Tracer. :)
1779 newTracer->checkRules(currentToken);
1781 else
1783 // _previousTracers cannot be affected by checkRule calls.
1785 if ((_tokenStartIndex-1)>=0)
1787 CSubRuleTracer::TOrderedTracers::iterator it=CSubRuleTracer::_PreviousTracers.find(_tokenStartIndex-1);
1788 if (it!=CSubRuleTracer::_PreviousTracers.end())
1790 CSubRuleTracer::TTracersSet &set=it->second;
1791 FOREACH(setIt, CSubRuleTracer::TTracersSet, set)
1792 (*setIt)->checkRule(rule, index+1, currentToken, childTracers);
1796 nlassert(childTracers.back()==(CSubRuleTracer*)this);
1797 childTracers.pop_back();
1800 void CSubRuleTracer::dump(size_t indent) const
1802 string str;
1803 str.resize(indent,' ');
1804 str+=_Name;
1805 str+="("+toString(_tokenStartIndex);
1806 str+=","+toString(_tokenIndex);
1807 str+="): "+_TextValue;
1809 nldebug(str.c_str());
1810 FOREACHC(itTracer, TSubRuleTracerList, _childTracers)
1811 (*itTracer)->dump(indent+1);
1814 const CSubRuleTracer *CSubRuleTracer::getChildForName(const string &name) const
1816 FOREACHC(childIt, TSubRuleTracerList, _childTracers)
1818 if ((*childIt)->_Name==name)
1819 return *childIt;
1821 return NULL;
1824 size_t CSubRuleTracer::getNbChildNamed(const string &name) const
1826 size_t nb=0;
1827 if (_Name==name)
1828 nb=1;
1830 FOREACHC(childIt, TSubRuleTracerList, _childTracers)
1831 nb+=(*childIt)->getNbChildNamed(name);
1832 return nb;
1835 void CSubRuleTracer::getSignature(string &signature, bool inOtherWiseOut) const
1837 if (inOtherWiseOut)
1839 if ( _Name==s_kw_exp
1840 || _Name==s_kw_somme
1841 || _Name==s_kw_produit
1842 || _Name==s_kw_facteur
1843 || _Name==s_kw_NAME
1844 || _Name==s_kw_NUMBER)
1846 signature+="f";
1847 return;
1849 if ( _Name==s_kw_CHAIN
1850 || _Name==s_kw_STRNAME)
1852 signature+="s";
1853 return;
1855 if ( _Name==s_kw_POINT)
1857 signature.resize(signature.size()-1);
1858 return;
1861 else
1863 if (_Name==s_kw_NAME)
1865 signature+="f";
1866 return;
1868 if (_Name==s_kw_STRNAME)
1870 signature+="s";
1871 return;
1873 if ( _Name==s_kw_POINT)
1875 signature.resize(signature.size()-1);
1876 return;
1878 if ( _Name==s_kw_exp
1879 || _Name==s_kw_somme
1880 || _Name==s_kw_produit
1881 || _Name==s_kw_facteur
1882 || _Name==s_kw_CHAIN
1883 || _Name==s_kw_NUMBER)
1885 signature+="!";
1886 return;
1889 FOREACHC(childIt, TSubRuleTracerList, _childTracers)
1890 (*childIt)->getSignature(signature,inOtherWiseOut);
1893 void CSubRuleTracer::generateCode(CSmartPtr<AIVM::CByteCode> &cByteCode) const
1895 using namespace AIVM;
1897 nlassert(!cByteCode.isNull());
1898 typedef vector<CSmartPtr<CByteCode> > TCodePieceList;
1899 size_t codeBlockIndex=0;
1900 TCodePieceList codePieces;
1901 vector<size_t> &byteCode=cByteCode->_opcodes;
1902 CJumpTable jumpTable(cByteCode);
1904 size_t randCountMarkIndex=~0;
1907 codePieces.resize(_childTracers.size());
1909 TCodePieceList::iterator itPiece=codePieces.begin();
1910 FOREACHC(childIt, TSubRuleTracerList, _childTracers)
1912 *itPiece=new CByteCode(cByteCode->_sourceName);
1913 (*childIt)->generateCode(*itPiece);
1914 ++itPiece;
1919 if (!_subRule.isNull())
1922 FOREACHC(instrIt, vector<string>, _subRule->_ExecOpCodes)
1924 const string str=*instrIt;
1925 string param;
1926 const CScriptVM::EOpcode op=CCompiler::getOpcodeAndValue(str, param);
1928 if (op!=CScriptVM::INVALID_OPCODE) // it could something else than an instruction.
1930 switch(op)
1932 case CScriptVM::JE:
1933 case CScriptVM::JNE:
1934 case CScriptVM::JUMP:
1935 byteCode.push_back(op); // + Jump offset.
1937 uint32 index;
1938 NLMISC::fromString(param, index);
1939 jumpTable.add(CJumpRememberer(index));
1940 byteCode.push_back(0); // Invalid
1941 break;
1942 default:
1943 byteCode.push_back(op);
1944 break;
1946 jumpTable.newCodeBlock();
1947 continue;
1950 breakable
1952 if (str.find("Atof")!=string::npos)
1954 uint32 index;
1955 NLMISC::fromString(param, index);
1956 --index;
1957 string &strRef=_childTracers[index]->_TextValue;
1958 float f;
1959 NLMISC::fromString(strRef, f);
1960 byteCode.push_back(*((size_t*)&f));
1961 jumpTable.newCodeBlock();
1962 break;
1965 if (str.find("String")!=string::npos)
1967 uint32 index;
1968 NLMISC::fromString(param, index);
1969 --index;
1970 string &strRef=_childTracers[index]->_TextValue;
1971 TStringId strId;
1972 if ( strRef.at(0)=='"'
1973 && strRef.at(0)==strRef.at(strRef.size()-1))
1974 strId=CStringMapper::map(strRef.substr(1,strRef.size()-2));
1975 else
1976 strId=CStringMapper::map(strRef);
1977 byteCode.push_back(*((size_t*)&strId));
1978 jumpTable.newCodeBlock();
1979 break;
1982 if (str.find("CodeAllExceptFirstAndLast")!=string::npos)
1984 size_t index=0;
1986 FOREACHC(CPIt, TCodePieceList, codePieces)
1988 index++; // Not the first, not the last.
1989 if ( index==1
1990 || index==codePieces.size())
1991 continue;
1993 if (byteCode.size()==0)
1994 byteCode=(*CPIt)->_opcodes;
1995 else
1997 FOREACHC(codePieceIt, vector<size_t>, (*CPIt)->_opcodes)
1998 byteCode.push_back(*codePieceIt);
2002 jumpTable.newCodeBlock();
2003 break;
2006 if (str.find("AllCode")!=string::npos)
2009 FOREACHC(CPIt, TCodePieceList, codePieces)
2011 if (byteCode.size()==0)
2012 byteCode=(*CPIt)->_opcodes;
2013 else
2015 FOREACHC(codePieceIt, vector<size_t>, (*CPIt)->_opcodes)
2016 byteCode.push_back(*codePieceIt);
2020 jumpTable.newCodeBlock();
2021 break;
2024 if (str.find("Code")!=string::npos)
2026 uint32 index;
2027 NLMISC::fromString(param, index);
2028 --index;
2029 if (byteCode.size()==0)
2030 byteCode=codePieces[index]->_opcodes;
2031 else
2033 FOREACHC(codePieceIt, vector<size_t>, codePieces[index]->_opcodes)
2034 byteCode.push_back(*codePieceIt);
2036 jumpTable.newCodeBlock();
2037 break;
2040 if (str.find("NativeCall")!=string::npos)
2042 string funcName;
2043 string inParamsSig;
2044 string outParamsSig;
2045 // Extract signature
2047 const CSubRuleTracer*paramTracer=getChildForName(s_kw_NAME);
2048 funcName=paramTracer->_TextValue;
2050 paramTracer=getChildForName(s_kw_params);
2051 if (!paramTracer)
2052 throw Exception("right params not found for the native call "+paramTracer->_TextValue);
2053 paramTracer->getSignature(inParamsSig, true);
2055 paramTracer=getChildForName(s_kw_tuple);
2056 if (!paramTracer)
2057 throw Exception("left params(tuple) not found for the native call "+paramTracer->_TextValue);
2058 paramTracer->getSignature(outParamsSig, false);
2060 // Get a function description
2061 CScriptNativeFuncParams *funcParam=CCompiler::getNativeFunc(funcName, inParamsSig, outParamsSig);
2062 if (!funcParam)
2064 string signature = funcName + "_" + inParamsSig + "_" + outParamsSig;
2065 throw Exception("Critical: unknown function name or bad parameters "+signature);
2068 size_t mode = 0;
2069 if (funcParam->_va)
2070 mode |= 1; // :KLUDGE: Hardcoded 1 :TODO: replace with a named constant
2072 byteCode.push_back(CScriptVM::NATIVE_CALL);
2073 // byteCode.push_back(*((size_t*)&funcParam));
2074 TStringId strId;
2075 strId = CStringMapper::map(funcName);
2076 byteCode.push_back(*((size_t*)&strId));
2077 byteCode.push_back(mode);
2078 strId = CStringMapper::map(inParamsSig);
2079 byteCode.push_back(*((size_t*)&strId));
2080 strId = CStringMapper::map(outParamsSig);
2081 byteCode.push_back(*((size_t*)&strId));
2083 jumpTable.newCodeBlock();
2084 break;
2087 if (str.find("RandomSeq")!=string::npos)
2089 const CSubRuleTracer *randomTracers=getChildForName(s_kw_expeclose);
2090 const size_t nbTracers=randomTracers->_childTracers.size()-2; // LA and RA are omitted.
2092 byteCode.push_back(CScriptVM::RAND);
2093 byteCode.push_back(nbTracers);
2095 byteCode.push_back(CScriptVM::JUMP);
2096 jumpTable.add(CJumpRememberer(nbTracers+1)); // Final Jump Position.
2097 byteCode.push_back(0); // Invalid
2099 size_t tracerInd=nbTracers;
2100 while (tracerInd>0)
2102 byteCode.push_back(CScriptVM::JUMP);
2103 jumpTable.add(CJumpRememberer(tracerInd));
2104 byteCode.push_back(0); // Invalid
2105 tracerInd--;
2107 byteCode.push_back(CScriptVM::RANDEND);
2108 jumpTable.newCodeBlock();
2111 codePieces.resize(0);
2112 codePieces.resize(randomTracers->_childTracers.size());
2114 TCodePieceList::iterator itPiece=codePieces.begin();
2115 FOREACHC(childIt, TSubRuleTracerList, randomTracers->_childTracers)
2117 *itPiece=new AIVM::CByteCode(cByteCode->_sourceName);
2118 (*childIt)->generateCode(*itPiece);
2119 ++itPiece;
2123 tracerInd=nbTracers;
2124 while (tracerInd>0)
2126 tracerInd--;
2127 if (byteCode.size()==0)
2128 byteCode=codePieces[tracerInd+1]->_opcodes;
2129 else
2131 FOREACHC(codePieceIt, vector<size_t>, codePieces[tracerInd+1]->_opcodes)
2132 byteCode.push_back(*codePieceIt);
2134 byteCode.push_back(CScriptVM::RET);
2135 jumpTable.newCodeBlock();
2137 break;
2140 if (str.find("SwitchSeq")!=string::npos)
2142 // first, build a list of case statements.
2143 TCaseTracerList caseTracers;
2144 FOREACHC(chldIt, TSubRuleTracerList, _childTracers)
2146 if ((*chldIt)->_Name==s_kw_case)
2148 CSmartPtr<CCaseTracer> ptr=new CCaseTracer(*chldIt, cByteCode->_sourceName);
2149 caseTracers.insert(make_pair(ptr->_sortValue, ptr));
2153 byteCode.push_back(CScriptVM::SWITCH);
2154 byteCode.push_back(caseTracers.size());
2156 jumpTable.add(CJumpRememberer(caseTracers.size()+2)); // Final Jump Position.
2157 byteCode.push_back(0); // Invalid
2160 size_t index=2;
2161 FOREACHC(caseIt, TCaseTracerList, caseTracers)
2163 byteCode.push_back(caseIt->first);
2164 jumpTable.add(CJumpRememberer(index)); // Final Jump Position.
2165 byteCode.push_back(0); // Invalid
2166 index++;
2169 jumpTable.newCodeBlock();
2171 FOREACHC(caseIt, TCaseTracerList, caseTracers)
2173 FOREACHC(codePieceIt, vector<size_t>, caseIt->second->_code->_opcodes)
2174 byteCode.push_back(*codePieceIt);
2175 byteCode.push_back(CScriptVM::RET);
2176 jumpTable.newCodeBlock();
2178 break;
2180 throw Exception("Unrecognized keyword "+str);
2187 if (outputFilename != "") output the byte code in a file
2189 bool compileExternalScript (const char *filename, const char *outputFilename)
2191 // Read the content
2192 bool result = false;
2193 FILE *file = fopen (filename, "r");
2194 if (file)
2196 string content;
2197 char buffer[512];
2198 int read;
2199 while ((read = (int)fread (buffer, 1, sizeof(buffer)-1, file)) == sizeof(buffer)-1)
2201 buffer[read] = 0;
2202 content += buffer;
2204 buffer[read] = 0;
2205 content += buffer;
2206 CSmartPtr<const AIVM::CByteCode> byteCode = CCompiler::getInstance ().compileCodeYacc (content, filename, NLNET::IService::getInstance()->haveArg('d'), true);
2207 fclose (file);
2208 if (byteCode)
2210 // Save the byte code ?
2211 if (strcmp (outputFilename, "") != 0)
2213 FILE *output = fopen (filename, "wb");
2214 if (output)
2216 size_t size = byteCode->_opcodes.size()*sizeof(size_t);
2217 if (fwrite (&byteCode->_opcodes[0], 1, size, output) == size)
2218 result = true;
2219 else
2220 nlwarning ("Error while writing %s", outputFilename);
2221 fclose (output);
2223 else
2224 nlwarning ("Can't open the file %s for writing", outputFilename);
2226 else
2227 result = true;
2230 else
2231 nlwarning ("Can't open the file %s for reading", filename);
2232 return result;
2235 //////////////////////////////////////////////////////////////////////////////
2239 }; // namespace
2241 NLMISC_COMMAND(listNativeFunctions, "list native functions of that AIS", "")
2243 CLogStringWriter stringWriter(&log);
2244 AICOMP::CCompiler::TNativeFuncMap const& funcs = AICOMP::CCompiler::getInstance().getFunctionList();
2245 std::deque<std::string> names;
2246 FOREACHC(itFunc, AICOMP::CCompiler::TNativeFuncMap, funcs)
2247 names.push_back(CStringMapper::unmap(itFunc->first));
2248 std::sort(names.begin(), names.end());
2249 FOREACHC(itName, std::deque<std::string>, names)
2250 log.displayNL("%s", itName->c_str());
2251 return true;