1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
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.
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/>.
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";
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 //////////////////////////////////////////////////////////////////////////
52 /****************************************************************************/
53 /* Script related classes */
54 /****************************************************************************/
56 //////////////////////////////////////////////////////////////////////////////
57 // CScriptNativeFuncParams //
58 //////////////////////////////////////////////////////////////////////////////
60 CScriptNativeFuncParams::CScriptNativeFuncParams(const std::string
&str
, FScrptNativeFunc func
)
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')
76 if (_nbOutParams
==1 && str
[lastPartIndex2
+1] == 'v')
83 //////////////////////////////////////////////////////////////////////////////
85 //////////////////////////////////////////////////////////////////////////////
90 CJumpRememberer(size_t codeBlockIndex
);
92 size_t _codeBlockIndex
;
95 //////////////////////////////////////////////////////////////////////////////
97 //////////////////////////////////////////////////////////////////////////////
102 CJumpTable(const CSmartPtr
<AIVM::CByteCode
> &byteCode
);
103 virtual ~CJumpTable();
104 void add(CJumpRememberer jump
);
107 vector
<size_t> _codeOffsets
;
108 vector
<CJumpRememberer
> _jumps
;
109 CSmartPtr
<AIVM::CByteCode
> _byteCode
;
112 //////////////////////////////////////////////////////////////////////////////
114 //////////////////////////////////////////////////////////////////////////////
116 class CCaseTracer
: public CRefCount
119 CCaseTracer(const CSmartPtr
<CSubRuleTracer
> &tracer
, const string
&sourceName
);
121 CSmartPtr
<CSubRuleTracer
> _tracer
;
122 CSmartPtr
<const AIVM::CByteCode
> _code
;
126 /****************************************************************************/
127 /* TOKEN SPECIALIZATION */
128 /****************************************************************************/
131 [] -> or for character.
132 - -> from before to after (char) (inside []).
133 "c" -> character c (or \c)
135 {xx} -> the token 'xx'.
140 + -> card suffixe 1..n
141 * -> card suffixe 0..n
142 ? -> card suffixe 0..1
143 {m,n} -> card suffixe m..n
146 //////////////////////////////////////////////////////////////////////////////
148 //////////////////////////////////////////////////////////////////////////////
150 // [a-zA-Z0-9] (readOK)
151 class CBracketToken
: public CBasicToken
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;
163 //////////////////////////////////////////////////////////////////////////////
165 //////////////////////////////////////////////////////////////////////////////
168 class CCharToken
: public CBasicToken
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;
180 //////////////////////////////////////////////////////////////////////////////
182 //////////////////////////////////////////////////////////////////////////////
185 class CTokenToken
: public CBasicToken
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;
197 //////////////////////////////////////////////////////////////////////////////
198 // CParenthesisToken //
199 //////////////////////////////////////////////////////////////////////////////
202 class CParenthesisToken
: public CBasicToken
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;
211 TTokenList _tokenList
;
214 //////////////////////////////////////////////////////////////////////////////
216 //////////////////////////////////////////////////////////////////////////////
219 class COrToken
: public CBasicToken
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;
228 CSmartPtr
<CBasicToken
> firstToken
;
229 CSmartPtr
<CBasicToken
> secondToken
;
232 //////////////////////////////////////////////////////////////////////////////
234 //////////////////////////////////////////////////////////////////////////////
237 class CCardToken
: public CBasicToken
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;
253 CSmartPtr
<CBasicToken
> _childToken
;
257 /****************************************************************************/
258 /** Script related classes implementation ***********************************/
259 /****************************************************************************/
261 /****************************************************************************/
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);
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;
292 FOREACHC(p
, string
, _Body
)
308 p
++; // to pass the last letter of the range
316 return CTokenTestResult(NULL
, CTokenTestResult::BRULE_INVALID
);
321 CSmartPtr
<CCodeNode
> codeNode
=new CCodeNode("Bracket",name
);
322 return CTokenTestResult(codeNode
);
325 void CBracketToken::dump(size_t indent
) const
328 str
.resize(indent
,' ');
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);
352 nlassert(str
.at(firstIndex
)=='"');
353 nlassert(str
.at(firstIndex
+2)=='"');
354 _c
=str
.at(firstIndex
+1);
355 tokenList
.push_back(this);
360 CTokenTestResult
CCharToken::buildNode(const std::string
&code
, size_t &index
) const
362 if ( index
<code
.size()
363 && code
.at(index
)==_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
378 str
.resize(indent
,' ');
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
);
399 _tokenName
=str
.substr(firstIndex
, index
-firstIndex
);
400 tokenList
.push_back(this);
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
);
412 nlwarning("token %s not succeed index %d", _tokenName
.c_str(), index
);
416 CSmartPtr
<CCodeNode
> codeNode
=new CCodeTokenNode("token", _tokenName
, res
.getCode());
417 return CTokenTestResult(codeNode
);
420 void CTokenToken::dump(size_t indent
) const
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);
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
);
455 return CTokenTestResult(NULL
, CTokenTestResult::BRULE_INVALID
);
457 CSmartPtr
<CCodeNode
> localCodeNode
=res
.getCode();
461 // Chain result node.
463 codeNode
=localCodeNode
;
465 nextCodeNode
->_NextNode
=localCodeNode
;
466 nextCodeNode
=localCodeNode
;
467 while (nextCodeNode
->_NextNode
)
468 nextCodeNode
=nextCodeNode
->_NextNode
;
475 void CParenthesisToken::dump(size_t indent
) const
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);
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
);
527 void COrToken::dump(size_t indent
) const
529 firstToken
->dump(indent
+1);
531 str
.resize(indent
,' ');
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
))
553 _card
= CARD_ZERO_ONE
;
556 _card
= CARD_ZERO_MANY
;
559 _card
= CARD_ONE_MANY
;
565 _childToken
= tokenList
.back();
566 tokenList
.back()=this;
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
;
579 CTokenTestResult res
;
581 while ((res
=_childToken
->buildNode(code
, localIndex
)).isValid())
583 CSmartPtr
<CCodeNode
> newCodeNode
=res
.getCode();
587 if (_card
==CARD_ZERO_ONE
)
594 firstCodeNode
= newCodeNode
;
596 nextCodeNode
->_NextNode
=newCodeNode
;
597 nextCodeNode
=newCodeNode
;
598 while (nextCodeNode
->_NextNode
)
599 nextCodeNode
= nextCodeNode
->_NextNode
;
604 return CTokenTestResult(firstCodeNode
, (nbNodes
==0 && _card
==CARD_ONE_MANY
)?CTokenTestResult::BRULE_INVALID
:CTokenTestResult::BRULE_VALID
);
607 void CCardToken::dump(size_t indent
) const
610 str
.resize(indent
,' ');
625 nldebug(str
.c_str());
626 _childToken
->dump(indent
+1);
629 /****************************************************************************/
631 /****************************************************************************/
633 //////////////////////////////////////////////////////////////////////////////
634 // CCodeNode implementation //
635 //////////////////////////////////////////////////////////////////////////////
637 CCodeNode::CCodeNode(const string
&type
, const string
&name
, CSmartPtr
<CCodeNode
> firstChildNode
)
640 , _FirstChildNode(firstChildNode
)
644 void CCodeNode::dump(size_t indent
)
647 str
.resize(indent
,' ');
648 str
+=_Type
+":"+_Name
;
649 // nlwarning(str.c_str());
651 _NextNode
->dump(indent
);
654 string
CCodeNode::getFullName() const
657 return _Name
+_NextNode
->getFullName();
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
)
673 str
.resize(indent
,' ');
674 str
+=_Type
+":"+_Name
;
677 str
+=":"+_FirstChildNode
->getFullName();
679 nldebug(str
.c_str());
681 _FirstChildNode
->dump(indent
+1);
684 _NextNode
->dump(indent
);
687 string
CCodeTokenNode::getFullName() const
691 returnName
+=_FirstChildNode
->getFullName();
693 returnName
+=_NextNode
->getFullName();
697 /****************************************************************************/
699 /****************************************************************************/
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.
714 x* 0,1,2, ... instances of x.
715 x+ 1,2,3, ... instances of x.
718 x/y an x but only if followed by y.
719 {xx} the translation of xx from the
721 x{m,n} m through n occurrences of x
725 - -> from before to after (char)
726 [] -> or for character.
729 {token} -> match token ?
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
)
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
)
763 const AIVM::CStringSeparator
strSepTokenAction(decl
, ",");
766 if (strSepTokenAction
.hasNext())
768 const string Tokens
=strSepTokenAction
.get();
771 const AIVM::CStringSeparator
strSep(Tokens
, " ");
772 while (strSep
.hasNext())
773 _tokens
.push_back(strSep
.get());
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
));
795 index
=token
->init(tokenList
, str
, index
, lastIndex
);
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())
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
)
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
);
859 return CTokenTestResult(NULL
, CTokenTestResult::BRULE_INVALID
);
861 CSmartPtr
<CCodeNode
> const newCodeNode
= res
.getCode();
865 // Chain result node.
867 masterNode
=newCodeNode
;
869 currentNode
->_NextNode
=newCodeNode
;
870 currentNode
=newCodeNode
;
871 while (currentNode
->_NextNode
)
872 currentNode
=currentNode
->_NextNode
;
880 const std::string
&CToken::getName() const
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
];
900 file
.getline(buffer
, bufferSize
);
901 if (buffer
[0]=='#' || buffer
[0]=='\0') // Skip lines beginning with a # and empty lines
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
)
912 string::size_type pos2
= line
.find(sep2
, pos1
);
913 if (pos2
!=string::npos
)
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)
929 addToken(part2
, part3
);
931 else if (part1
=="rule")
933 addRule(part2
, part3
);
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
);
947 REGISTER_OPCODE(NOT
);
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
);
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;
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
))
1050 #ifdef DISPLAY_INFOS
1052 string
str(">> TOKEN: ");
1054 str
+=" "+toString(tokenIndex
);
1055 nldebug(str
.c_str());
1058 CSmartPtr
<CSubRuleTracer
> tracer
=new CSubRuleTracer(lastIndex
, index
-1, tokenName
, textValue
);
1060 if (lastInsertedTracer
.isNull())
1062 lastInsertedTracer
=tracer
;
1063 firstInsertedTracer
=tracer
;
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.
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
)
1090 // We have a potential comment. Now check if it is quoted or not
1091 bool inQuote
= false;
1098 if ( !inQuote
&& ('/' == str
[i
]) )
1108 if (str
.size() == i
)
1113 code
+="\n "; // additional ..
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);
1126 // Generate the old byte code
1127 CSmartPtr
<const AIVM::CByteCode
> oldbyteCode
= compileCodeOld (sourceCode
, fullName
, debug
);
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
)
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");
1150 fwrite (&byteCode
->_opcodes
[0], sizeof(size_t), byteCode
->_opcodes
.size (), file
);
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");
1163 fwrite (sourceCode
.c_str (), sourceCode
.size(), 1, file
);
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
);
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
);
1191 nldebug(">empty source code ..");
1194 nldebug("compilation success. (code size %d)",byteCode
->_opcodes
.size()*4);
1196 CSmartPtr
<const AIVM::CByteCode
> tmp
=&(*byteCode
);
1199 dumpByteCode (sourceCode
, fullName
, tmp
, "iaoldbytecode");
1203 catch (const Exception
&e
)
1205 nlwarning("compilation failed for %s", fullName
.c_str());
1206 nlwarning(e
.what());
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
);
1221 dumpByteCode (sourceCode
, fullName
, tmp
, "ianewbytecode");
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
)
1249 static void displayErrorLinesForIndex(const string
&text
, size_t &index
)
1251 AIVM::CStringSeparator
sep(text
,"\n\r");
1252 size_t totalIndex
= 0;
1254 size_t lineIndex
= 0;
1256 while (sep
.hasNext())
1259 const size_t stringSize
= tmp
.size()+1; // +1 for the \n text.
1260 if (totalIndex
+stringSize
>index
)
1262 totalIndex
+= stringSize
;
1267 string
lineoStr("at line ");
1268 lineoStr
+= toString(lineIndex
);
1269 nlwarning(lineoStr
.c_str());
1272 nlwarning(tmp
.c_str());
1275 indexerStr
.resize(index
-totalIndex
,' ');
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.
1288 if (index
==text
.size())
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
);
1306 displayErrorLinesForIndex(text
, index
);
1307 throw (EScriptError("(Unrecognized pattern)", index
));
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
)
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
;
1349 TNativeFuncMap::iterator it
;
1351 signature
= funcName
+ "_" + inparams
+ "_" + outparams
;
1352 strId
= CStringMapper::map(signature
);
1353 it
= _NativeFunctions
.find(strId
);
1354 if (it
!=_NativeFunctions
.end())
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())
1363 signature
= funcName
+ "_" + inparams
+ "_v";
1364 strId
= CStringMapper::map(signature
);
1365 it
= _NativeFunctions
.find(strId
);
1366 if (it
!=_NativeFunctions
.end())
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())
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");
1383 TOpcodeMap::iterator it
=_Opcodes
.find(sep
.get());
1384 if (it
!=_Opcodes
.end())
1392 void CCompiler::addRule (const string
&ruleName
, const string
&ruleDesc
)
1394 CRule
*rule
=getRule (ruleName
);
1397 rule
->setDesc(ruleDesc
);
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
)
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
)
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()
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
)
1471 const CSubRuleTracer
*chldTracer
=tracer
->getChildForName(s_kw_readConstVar
);
1472 const CSubRuleTracer
*valChldTracer
=NULL
;
1476 if (valChldTracer
=chldTracer
->getChildForName(s_kw_CHAIN
))
1478 const string
&strRef
=valChldTracer
->_TextValue
;
1480 if ( strRef
.at(0)=='"'
1481 && strRef
.at(0)==strRef
.at(strRef
.size()-1))
1482 strId
=CStringMapper::map(strRef
.substr(1,strRef
.size()-2));
1484 strId
=CStringMapper::map(strRef
);
1485 _sortValue
=*((size_t*)&strId
);
1488 if (valChldTracer
=chldTracer
->getChildForName(s_kw_NUMBER
))
1490 const string
&strRef
=valChldTracer
->_TextValue
;
1492 NLMISC::fromString(strRef
, f
);
1493 _sortValue
=*((size_t*)&f
);
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
);
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
)
1515 , _tokenStartIndex(tokenStartIndex
)
1516 , _tokenIndex(currentTokenIndex
)
1518 , _TextValue(textValue
)
1521 updatePreviousNext();
1524 CSubRuleTracer::CSubRuleTracer(NLMISC::CSmartPtr
<CSubRule
> subRule
, size_t tokenStartIndex
, size_t currentTokenIndex
, const std::string
&name
, const std::string
&textValue
)
1526 , _tokenStartIndex(tokenStartIndex
)
1527 , _tokenIndex(currentTokenIndex
)
1529 , _TextValue(textValue
)
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
;
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
;
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
);
1598 void CSubRuleTracer::removeChild(CSubRuleTracer
*tracer
)
1600 FOREACH(itChild
, TSubRuleTracerList
, _childTracers
)
1602 if ((*itChild
)==tracer
)
1604 _childTracers
.erase(itChild
);
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()
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
);
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.
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;
1674 CSubRuleTracer
*tracer
=this;
1675 while (tracer
!=NULL
)
1677 if (tracer
->_parentTracers
.size()==0) // if there is a problem, we can see it here .. :)
1680 nlwarning("an grammar error appeared that breaks this enclosing: ");
1683 tracer
=tracer
->getNextLower();
1687 getHigherParent()->flushErrors();
1689 throw Exception("Script Grammar Error");
1690 return getHigherParent();
1694 CSmartPtr
<CSubRuleTracer
> returnTracer
=getHigherParent()->getValidTracer();
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)
1719 CSubRuleTracer
*CSubRuleTracer::getHigherParent()
1721 if (_parentTracers
.size()>0)
1722 return _parentTracers
.back()->getHigherParent();
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
)
1742 string token
=rule
->_tokens
[rule
->_tokens
.size()-1-index
];
1743 if (token
.at(0)=='+')
1744 equal
=(_Name
==token
.substr(1,token
.size()-1));
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.
1753 token
=rule
->_tokens
[rule
->_tokens
.size()-index
];
1754 if (token
.at(0)!='+')
1757 equal
=(_Name
==token
.substr(1,token
.size()-1));
1758 if (!equal
) // failed.
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
);
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
1803 str
.resize(indent
,' ');
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
)
1824 size_t CSubRuleTracer::getNbChildNamed(const string
&name
) const
1830 FOREACHC(childIt
, TSubRuleTracerList
, _childTracers
)
1831 nb
+=(*childIt
)->getNbChildNamed(name
);
1835 void CSubRuleTracer::getSignature(string
&signature
, bool inOtherWiseOut
) const
1839 if ( _Name
==s_kw_exp
1840 || _Name
==s_kw_somme
1841 || _Name
==s_kw_produit
1842 || _Name
==s_kw_facteur
1844 || _Name
==s_kw_NUMBER
)
1849 if ( _Name
==s_kw_CHAIN
1850 || _Name
==s_kw_STRNAME
)
1855 if ( _Name
==s_kw_POINT
)
1857 signature
.resize(signature
.size()-1);
1863 if (_Name
==s_kw_NAME
)
1868 if (_Name
==s_kw_STRNAME
)
1873 if ( _Name
==s_kw_POINT
)
1875 signature
.resize(signature
.size()-1);
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
)
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
);
1919 if (!_subRule
.isNull())
1922 FOREACHC(instrIt
, vector
<string
>, _subRule
->_ExecOpCodes
)
1924 const string str
=*instrIt
;
1926 const CScriptVM::EOpcode op
=CCompiler::getOpcodeAndValue(str
, param
);
1928 if (op
!=CScriptVM::INVALID_OPCODE
) // it could something else than an instruction.
1933 case CScriptVM::JNE
:
1934 case CScriptVM::JUMP
:
1935 byteCode
.push_back(op
); // + Jump offset.
1938 NLMISC::fromString(param
, index
);
1939 jumpTable
.add(CJumpRememberer(index
));
1940 byteCode
.push_back(0); // Invalid
1943 byteCode
.push_back(op
);
1946 jumpTable
.newCodeBlock();
1952 if (str
.find("Atof")!=string::npos
)
1955 NLMISC::fromString(param
, index
);
1957 string
&strRef
=_childTracers
[index
]->_TextValue
;
1959 NLMISC::fromString(strRef
, f
);
1960 byteCode
.push_back(*((size_t*)&f
));
1961 jumpTable
.newCodeBlock();
1965 if (str
.find("String")!=string::npos
)
1968 NLMISC::fromString(param
, index
);
1970 string
&strRef
=_childTracers
[index
]->_TextValue
;
1972 if ( strRef
.at(0)=='"'
1973 && strRef
.at(0)==strRef
.at(strRef
.size()-1))
1974 strId
=CStringMapper::map(strRef
.substr(1,strRef
.size()-2));
1976 strId
=CStringMapper::map(strRef
);
1977 byteCode
.push_back(*((size_t*)&strId
));
1978 jumpTable
.newCodeBlock();
1982 if (str
.find("CodeAllExceptFirstAndLast")!=string::npos
)
1986 FOREACHC(CPIt
, TCodePieceList
, codePieces
)
1988 index
++; // Not the first, not the last.
1990 || index
==codePieces
.size())
1993 if (byteCode
.size()==0)
1994 byteCode
=(*CPIt
)->_opcodes
;
1997 FOREACHC(codePieceIt
, vector
<size_t>, (*CPIt
)->_opcodes
)
1998 byteCode
.push_back(*codePieceIt
);
2002 jumpTable
.newCodeBlock();
2006 if (str
.find("AllCode")!=string::npos
)
2009 FOREACHC(CPIt
, TCodePieceList
, codePieces
)
2011 if (byteCode
.size()==0)
2012 byteCode
=(*CPIt
)->_opcodes
;
2015 FOREACHC(codePieceIt
, vector
<size_t>, (*CPIt
)->_opcodes
)
2016 byteCode
.push_back(*codePieceIt
);
2020 jumpTable
.newCodeBlock();
2024 if (str
.find("Code")!=string::npos
)
2027 NLMISC::fromString(param
, index
);
2029 if (byteCode
.size()==0)
2030 byteCode
=codePieces
[index
]->_opcodes
;
2033 FOREACHC(codePieceIt
, vector
<size_t>, codePieces
[index
]->_opcodes
)
2034 byteCode
.push_back(*codePieceIt
);
2036 jumpTable
.newCodeBlock();
2040 if (str
.find("NativeCall")!=string::npos
)
2044 string outParamsSig
;
2045 // Extract signature
2047 const CSubRuleTracer
*paramTracer
=getChildForName(s_kw_NAME
);
2048 funcName
=paramTracer
->_TextValue
;
2050 paramTracer
=getChildForName(s_kw_params
);
2052 throw Exception("right params not found for the native call "+paramTracer
->_TextValue
);
2053 paramTracer
->getSignature(inParamsSig
, true);
2055 paramTracer
=getChildForName(s_kw_tuple
);
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
);
2064 string signature
= funcName
+ "_" + inParamsSig
+ "_" + outParamsSig
;
2065 throw Exception("Critical: unknown function name or bad parameters "+signature
);
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));
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();
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
;
2102 byteCode
.push_back(CScriptVM::JUMP
);
2103 jumpTable
.add(CJumpRememberer(tracerInd
));
2104 byteCode
.push_back(0); // Invalid
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
);
2123 tracerInd
=nbTracers
;
2127 if (byteCode
.size()==0)
2128 byteCode
=codePieces
[tracerInd
+1]->_opcodes
;
2131 FOREACHC(codePieceIt
, vector
<size_t>, codePieces
[tracerInd
+1]->_opcodes
)
2132 byteCode
.push_back(*codePieceIt
);
2134 byteCode
.push_back(CScriptVM::RET
);
2135 jumpTable
.newCodeBlock();
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
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
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();
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
)
2192 bool result
= false;
2193 FILE *file
= fopen (filename
, "r");
2199 while ((read
= (int)fread (buffer
, 1, sizeof(buffer
)-1, file
)) == sizeof(buffer
)-1)
2206 CSmartPtr
<const AIVM::CByteCode
> byteCode
= CCompiler::getInstance ().compileCodeYacc (content
, filename
, NLNET::IService::getInstance()->haveArg('d'), true);
2210 // Save the byte code ?
2211 if (strcmp (outputFilename
, "") != 0)
2213 FILE *output
= fopen (filename
, "wb");
2216 size_t size
= byteCode
->_opcodes
.size()*sizeof(size_t);
2217 if (fwrite (&byteCode
->_opcodes
[0], 1, size
, output
) == size
)
2220 nlwarning ("Error while writing %s", outputFilename
);
2224 nlwarning ("Can't open the file %s for writing", outputFilename
);
2231 nlwarning ("Can't open the file %s for reading", filename
);
2235 //////////////////////////////////////////////////////////////////////////////
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());