1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2013 Laszlo KIS-ADAM (dfighter) <dfighter1985@gmail.com>
6 // Copyright (C) 2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
8 // This program is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU Affero General Public License as
10 // published by the Free Software Foundation, either version 3 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU Affero General Public License for more details.
18 // You should have received a copy of the GNU Affero General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "nel/misc/algo.h"
25 #include "nel/gui/db_manager.h"
26 #include "nel/gui/interface_expr.h"
27 #include "nel/gui/interface_expr_node.h"
30 using namespace NLMISC
;
39 void ifexprufct_forcelink();
41 // Needed because otherwise GCC and co. omit the code in interface_expr_user_fct.cpp code
42 // causing the GUI not to work.
43 // It all happens because no function is called *directly* from that module.
48 ifexprufct_forcelink();
52 LinkTrickster linkTrickster
;
54 // Yoyo: Act like a singleton, else registerUserFct may crash.
55 CInterfaceExpr::TUserFctMap
*CInterfaceExpr::_UserFct
= NULL
;
57 static const std::string ExprLuaId
="lua:";
59 //==================================================================
61 void CInterfaceExpr::release()
67 //==================================================================
68 void formatLuaCall(const std::string
&expr
, std::string
&tempStr
)
70 /* Call the LUA interface exp fct, with the script as line, and resolve string definition conflicts:
72 lua:getSkillFromName('SM')
74 lua('getSkillFromName(\"SM\")')
76 tempStr
= expr
.substr(ExprLuaId
.size()); // eg: tempStr= getSkillFromName('SM')
77 while(strFindReplace(tempStr
, "'", "\\\"")); // eg: tempStr= getSkillFromName(\"SM\")
78 tempStr
= string("lua('") + tempStr
+ "')"; // eg: tempStr= lua('getSkillFromName(\"SM\")')
81 //==================================================================
82 bool CInterfaceExpr::eval(const std::string
&expr
, CInterfaceExprValue
&result
, std::vector
<ICDBNode
*> *nodes
, bool noFctCalls
/* = false */)
84 // Yoyo: Special InterfaceExpr Form to execute lua code?
85 if(expr
.compare(0, ExprLuaId
.size(), ExprLuaId
) ==0 )
88 formatLuaCall(expr
, tempStr
);
89 return evalExpr(tempStr
.c_str(), result
, nodes
, noFctCalls
) != NULL
;
93 return evalExpr(expr
.c_str(), result
, nodes
, noFctCalls
) != NULL
;
97 //==================================================================
98 CInterfaceExprNode
*CInterfaceExpr::buildExprTree(const std::string
&expr
)
100 CInterfaceExprNode
*node
;
102 // Yoyo: Special InterfaceExpr Form to execute lua code?
103 if(expr
.compare(0, ExprLuaId
.size(), ExprLuaId
) ==0 )
106 formatLuaCall(expr
, tempStr
);
107 if (!buildExprTree(tempStr
.c_str(), node
)) return NULL
;
111 if (!buildExprTree(expr
.c_str(), node
)) return NULL
;
118 //==================================================================
119 void CInterfaceExpr::registerUserFct(const char *name
,TUserFct fct
)
121 if(!_UserFct
) _UserFct
= new TUserFctMap
;
123 nlassert(fct
!= NULL
);
124 (*_UserFct
)[std::string(name
)] = fct
;
127 //==================================================================
128 /** tool fct : skip space, tab and carret-returns
130 static const char *skipBlank(const char *start
)
133 while (*start
== ' ' || *start
== '\t' || *start
== '\r' || *start
== '\n') ++start
;
137 //==================================================================
138 const char *CInterfaceExpr::evalExpr(const char *expr
, CInterfaceExprValue
&result
, std::vector
<ICDBNode
*> *nodes
, bool noFctCalls
)
140 nlassert(expr
!= NULL
);
141 expr
= skipBlank(expr
);
142 if (isalpha(*expr
)) // alpha character means this is a function name
144 return evalFct(expr
, result
, nodes
, noFctCalls
);
146 else if (*expr
== '@') // is it a database entry ?
149 expr
= skipBlank(expr
);
150 return evalDBEntry(expr
, result
, nodes
);
153 // try to parse a literal value
154 const char *newExpr
= result
.initFromString(expr
);
157 nlwarning("<CInterfaceExpr::evalExpr> : syntax error : %s", expr
);
163 //==================================================================
164 const char *CInterfaceExpr::buildExprTree(const char *expr
, CInterfaceExprNode
*&result
)
166 nlassert(expr
!= NULL
);
167 expr
= skipBlank(expr
);
168 if (isalpha(*expr
)) // alpha character means this is a function name
170 return buildFctNode(expr
, result
);
172 else if (*expr
== '@') // is it a database entry ?
175 expr
= skipBlank(expr
);
176 return buildDBEntryNode(expr
, result
);
180 CInterfaceExprValue value
;
181 // try to parse a literal value
182 const char *newExpr
= value
.initFromString(expr
);
185 nlwarning("<CInterfaceExpr::buildExprTree> : syntax error : %s", expr
);
188 CInterfaceExprNodeValue
*node
= new CInterfaceExprNodeValue
;
197 //==================================================================
198 const char *CInterfaceExpr::evalFct(const char *expr
, CInterfaceExprValue
&result
, std::vector
<ICDBNode
*> *nodes
, bool noFctCalls
)
200 if(!_UserFct
) _UserFct
= new TUserFctMap
;
202 const char *start
= expr
;
203 while (isalnum(*expr
)) ++ expr
;
204 std::string
fctName(start
, expr
- start
);
205 // find entry in the map
206 TUserFctMap::iterator fctIt
= _UserFct
->find(fctName
);
207 if (fctIt
== _UserFct
->end())
209 nlwarning("<CInterfaceExpr::evalFct> : Unknown function %s", fctName
.c_str());
212 nlassert(fctIt
->second
!= NULL
);
213 // eval list of arguments
215 expr
= skipBlank(expr
);
218 nlwarning("<CInterfaceExpr::evalFct> : '(' expected for function %s", fctName
.c_str());
222 expr
= skipBlank(expr
);
227 expr
= skipBlank(expr
);
229 argList
.push_back(CInterfaceExprValue());
230 expr
= evalExpr(expr
, argList
.back(), nodes
, noFctCalls
);
231 if (expr
== NULL
) return NULL
;
232 expr
= skipBlank(expr
);
233 if (*expr
== ')') break;
234 // if it isn't the end of the expression, then we should find a ',' before next argument
237 nlwarning("<CInterfaceExpr::evalFct> : ',' expected in function %s", fctName
.c_str());
245 if (!noFctCalls
) // should we make terminal function calls ?
247 if (fctIt
->second(argList
, result
)) return expr
;
256 //==================================================================
257 const char *CInterfaceExpr::buildFctNode(const char *expr
, CInterfaceExprNode
*&result
)
259 if(!_UserFct
) _UserFct
= new TUserFctMap
;
261 const char *start
= expr
;
262 while (isalnum(*expr
)) ++ expr
;
263 std::string
fctName(start
, expr
- start
);
264 // find entry in the map
265 TUserFctMap::iterator fctIt
= _UserFct
->find(fctName
);
266 if (fctIt
== _UserFct
->end())
268 nlwarning("<CInterfaceExpr::buildFctNode> : Unknown function %s", fctName
.c_str());
271 nlassert(fctIt
->second
!= NULL
);
272 // List of parameters
273 expr
= skipBlank(expr
);
276 nlwarning("<CInterfaceExpr::buildFctNode> : '(' expected for function %s", fctName
.c_str());
280 expr
= skipBlank(expr
);
281 std::vector
<CInterfaceExprNode
*> Params
;
286 expr
= skipBlank(expr
);
288 CInterfaceExprNode
*node
= NULL
;
289 expr
= buildExprTree(expr
, node
);
292 for(uint k
= 0; k
< Params
.size(); ++k
)
298 Params
.push_back(node
);
299 expr
= skipBlank(expr
);
300 if (*expr
== ')') break;
301 // if it isn't the end of the expression, then we should find a ',' before next argument
304 for(uint k
= 0; k
< Params
.size(); ++k
)
308 nlwarning("CInterfaceExpr::evalFct : ',' expected in function %s", fctName
.c_str());
315 CInterfaceExprNodeValueFnCall
*node
= new CInterfaceExprNodeValueFnCall
;
316 node
->Params
.swap(Params
);
317 node
->Func
= fctIt
->second
;
322 //==================================================================
323 const char *CInterfaceExpr::evalDBEntry(const char *expr
, CInterfaceExprValue
&result
, std::vector
<ICDBNode
*> *nodes
)
326 expr
= unpackDBentry(expr
, nodes
, dbEntry
);
327 if (!expr
) return NULL
;
329 //nlassert(NLGUI::CDBManager::getInstance()->getDbProp(dbEntry, false) || CInterfaceManager::getInstance()->getDbBranch(dbEntry));
331 CCDBNodeLeaf
*nl
= NLGUI::CDBManager::getInstance()->getDbProp(dbEntry
);
336 // insert node if not already present
337 if (std::find(nodes
->begin(), nodes
->end(), nl
) == nodes
->end())
339 nodes
->push_back(nl
);
342 result
.setInteger(nl
->getValue64());
347 CCDBNodeBranch
*nb
= NLGUI::CDBManager::getInstance()->getDbBranch(dbEntry
);
350 if (std::find(nodes
->begin(), nodes
->end(), nb
) == nodes
->end())
352 nodes
->push_back(nb
);
355 if (!nb
) return NULL
;
356 result
.setInteger(0);
362 //==================================================================
363 const char *CInterfaceExpr::buildDBEntryNode(const char *expr
, CInterfaceExprNode
*&result
)
367 const char *startChar
= expr
;
368 expr
= unpackDBentry(expr
, NULL
, dbEntry
, &indirection
);
369 if (!expr
) return NULL
;
372 // special node with no optimisation
373 CInterfaceExprNodeDependantDBRead
*node
= new CInterfaceExprNodeDependantDBRead
;
374 node
->Expr
.resize(expr
- startChar
+ 1);
375 std::copy(startChar
, expr
, node
->Expr
.begin() + 1);
383 //nlassert(NLGUI::CDBManager::getInstance()->getDbProp(dbEntry, false) || CInterfaceManager::getInstance()->getDbBranch(dbEntry));
384 CCDBNodeLeaf
*nl
= NLGUI::CDBManager::getInstance()->getDbProp(dbEntry
);
387 CInterfaceExprNodeDBLeaf
*node
= new CInterfaceExprNodeDBLeaf
;
394 CCDBNodeBranch
*nb
= NLGUI::CDBManager::getInstance()->getDbBranch(dbEntry
);
397 CInterfaceExprNodeDBBranch
*node
= new CInterfaceExprNodeDBBranch
;
407 //==================================================================
408 const char *CInterfaceExpr::unpackDBentry(const char *expr
, std::vector
<ICDBNode
*> *nodes
, std::string
&dest
, bool *hasIndirections
/* = NULL*/)
410 std::string entryName
;
411 bool indirection
= false;
418 std::string subEntry
;
419 expr
= unpackDBentry(expr
, nodes
, subEntry
);
420 if (!expr
) return NULL
;
421 // Read DB Index Offset.
422 sint32 indirectionOffset
= 0;
423 if (*expr
== '-' || *expr
=='+' )
425 bool negative
= *expr
== '-';
426 std::string offsetString
;
428 while(*expr
!=0 && isdigit(*expr
))
430 offsetString
.push_back(*expr
);
434 fromString(offsetString
, indirectionOffset
);
436 indirectionOffset
= -indirectionOffset
;
438 // Test end of indirection
441 nlwarning("CInterfaceExpr::unpackDBentry: ']' expected");
445 // get the db value at sub entry
447 //nlassert(NLGUI::CDBManager::getInstance()->getDbProp(subEntry, false) || CInterfaceManager::getInstance()->getDbBranch(subEntry));
448 CCDBNodeLeaf
*nl
= NLGUI::CDBManager::getInstance()->getDbProp(subEntry
);
451 if (std::find(nodes
->begin(), nodes
->end(), nl
) == nodes
->end())
453 nodes
->push_back(nl
);
456 // compute indirection, (clamp).
457 sint32 indirectionValue
= nl
->getValue32() + indirectionOffset
;
458 indirectionValue
= std::max((sint32
)0, indirectionValue
);
460 // Append to entry name.
461 entryName
+= NLMISC::toString(indirectionValue
);
463 else if (isalnum(*expr
) || *expr
== '_' || *expr
== ':')
475 *hasIndirections
= indirection
;
482 //==================================================================
483 bool CInterfaceExpr::evalAsInt(const std::string
&expr
, sint64
&dest
)
485 CInterfaceExprValue result
;
486 if (!eval(expr
, result
)) return false;
487 if (!result
.toInteger())
489 nlwarning("<CInterfaceExpr::evalAsInt> Can't convert value to an integer, expr = %s", expr
.c_str());
492 dest
= result
.getInteger();
496 //==================================================================
497 bool CInterfaceExpr::evalAsDouble(const std::string
&expr
, double &dest
)
499 CInterfaceExprValue result
;
500 if (!eval(expr
, result
)) return false;
501 if (!result
.toDouble())
503 nlwarning("<CInterfaceExpr::evalAsDouble> Can't convert value to a double, expr = %s", expr
.c_str());
506 dest
= result
.getDouble();
510 //==================================================================
511 bool CInterfaceExpr::evalAsBool(const std::string
&expr
, bool &dest
)
513 CInterfaceExprValue result
;
514 if (!eval(expr
, result
)) return false;
515 if (!result
.toBool())
517 nlwarning("<CInterfaceExpr::evalAsBool> Can't convert value to a boolean, expr = %s", expr
.c_str());
520 dest
= result
.getBool();
524 //==================================================================
525 bool CInterfaceExpr::evalAsString(const std::string
&expr
, std::string
&dest
)
527 CInterfaceExprValue result
;
528 if (!eval(expr
, result
)) return false;
529 if (!result
.toString())
531 nlwarning("<CInterfaceExpr::evalAsString> Can't convert value to a string, expr = %s", expr
.c_str());
534 dest
= result
.getString();
538 //==================================================================
539 //==================================================================
540 //==================================================================
541 //==================================================================
544 //==================================================================
545 bool CInterfaceExprValue::toBool()
549 case Boolean
: return true;
550 case Integer
: setBool(_IntegerValue
!= 0); return true;
551 case Double
: setBool(_DoubleValue
!= 0); return true;
552 case String
: return evalBoolean(_StringValue
.c_str()) != NULL
;
559 //==================================================================
560 bool CInterfaceExprValue::toInteger()
564 case Boolean
: setInteger(_BoolValue
? 1 : 0); return true;
565 case Integer
: return true;
566 case Double
: setInteger((sint64
) _DoubleValue
); return true;
568 if (evalNumber(_StringValue
.c_str())) return toInteger();
570 case RGBA
: setInteger((sint64
) _RGBAValue
); return true;
576 //==================================================================
577 bool CInterfaceExprValue::toDouble()
581 case Boolean
: setDouble(_BoolValue
? 1 : 0); return true;
582 case Integer
: setDouble((double) _IntegerValue
); return true;
583 case Double
: return true;
585 if (evalNumber(_StringValue
.c_str())) return toBool();
587 case RGBA
: setDouble((double) _RGBAValue
); return true;
593 //==================================================================
594 bool CInterfaceExprValue::toString()
598 case Boolean
: setString(_BoolValue
? "true" : "false"); return true;
599 case Integer
: setString(NLMISC::toString(_IntegerValue
)); return true;
600 case Double
: setString(NLMISC::toString("%.2f", _DoubleValue
)); return true;
601 case String
: return true;
605 r
= (_RGBAValue
&0xff);
606 g
= ((_RGBAValue
>>8)&0xff);
607 b
= ((_RGBAValue
>>16)&0xff);
608 a
= ((_RGBAValue
>>24)&0xff);
609 setString(NLMISC::toString("%d %d %d %d", r
, g
, b
, a
));
617 //==================================================================
618 bool CInterfaceExprValue::toRGBA()
626 setRGBA(NLMISC::CRGBA((uint8
)(_IntegerValue
&0xff), (uint8
)((_IntegerValue
>>8)&0xff),
627 (uint8
)((_IntegerValue
>>16)&0xff), (uint8
)((_IntegerValue
>>24)&0xff)));
631 setRGBA( NLMISC::CRGBA::stringToRGBA(_StringValue
.c_str()));
640 //==================================================================
641 bool CInterfaceExprValue::isNumerical() const
643 return _Type
== Boolean
|| _Type
== Integer
|| _Type
== Double
;
646 //==================================================================
647 const char *CInterfaceExprValue::initFromString(const char *expr
)
650 expr
= skipBlank(expr
);
651 if (isdigit(*expr
) || *expr
== '.' || *expr
== '-') return evalNumber(expr
);
658 return evalBoolean(expr
);
660 return evalString(expr
);
666 //==================================================================
667 const char *CInterfaceExprValue::evalBoolean(const char *expr
)
670 expr
= skipBlank(expr
);
671 if (toupper(expr
[0]) == 'T' &&
672 toupper(expr
[1]) == 'R' &&
673 toupper(expr
[2]) == 'U' &&
674 toupper(expr
[3]) == 'E')
680 if (toupper(expr
[0]) == 'F' &&
681 toupper(expr
[1]) == 'A' &&
682 toupper(expr
[2]) == 'L' &&
683 toupper(expr
[3]) == 'S' &&
684 toupper(expr
[4]) == 'E')
692 //==================================================================
693 const char *CInterfaceExprValue::evalNumber(const char *expr
)
696 bool hasPoint
= false;
698 expr
= skipBlank(expr
);
704 expr
= skipBlank(expr
);
711 const char *start
= expr
;
712 while (*expr
== '.' || isdigit(*expr
))
714 if (*expr
== '.') hasPoint
= true;
717 if (start
== expr
) return NULL
;
721 // this is an integer
722 for (const char *nbPtr
= start
; nbPtr
< expr
; ++ nbPtr
)
725 value
+= (sint64
) (*nbPtr
- '0');
727 setInteger(negative
? - value
: value
);
730 else // floating point value : use scanf
732 // well, for now, we only parse a float
734 std::string
floatValue(start
, expr
- start
);
735 if (fromString(floatValue
, value
))
737 setDouble(negative
? - value
: value
);
747 //==================================================================
748 const char *CInterfaceExprValue::evalString(const char *expr
)
750 expr
= skipBlank(expr
);
751 if (*expr
!= '\'') return NULL
;
758 nlwarning("CInterfaceExprValue::evalString : end of buffer encountered in a string");
767 if (*expr
== '\\') // special char
772 case 't': str
+= '\t'; break;
773 case 'r': str
+= '\r'; break;
774 case 'n': str
+= '\n'; break;
775 case '\'': str
+= '\''; break;
776 case '"': str
+= '"'; break;
777 case '\\': str
+= '\\'; break;
780 // string continue on next line, so do nothing
784 nlwarning("CInterfaceExprValue::evalString : unknown escape sequence : \\%c", *expr
);
785 if (*expr
) str
+= *expr
;
789 else if (*expr
== '\n' || *expr
== '\r')
791 nlwarning("CInterfaceExprValue::evalString : line break encountered in a string");
804 //==================================================================
805 bool CInterfaceExprValue::toType(TType type
)
809 case Boolean
: return toBool();
810 case Integer
: return toInteger();
811 case Double
: return toDouble();
812 case String
: return toString();
813 case RGBA
: return toRGBA();
814 default: return false;
819 //==================================================================
820 void CInterfaceExprValue::clean()
824 case String
: _StringValue
.clear(); break;
825 case UserType
: delete _UserTypeValue
; break;
830 //==================================================================
831 void CInterfaceExprValue::setUserType(CInterfaceExprUserType
*value
)
833 if (_Type
== UserType
&& value
== _UserTypeValue
) return;
836 _UserTypeValue
= value
;
839 //==================================================================
840 bool CInterfaceExprValue::getBool() const
842 if (_Type
!= Boolean
)
844 nlwarning("<CInterfaceExprValue::getBool> bad type!");
850 //==================================================================
851 sint64
CInterfaceExprValue::getInteger() const
853 if (_Type
!= Integer
)
855 nlwarning("<CInterfaceExprValue::getInteger> bad type!");
858 return _IntegerValue
;
861 //==================================================================
862 double CInterfaceExprValue::getDouble() const
866 nlwarning("<CInterfaceExprValue::getDouble> bad type!");
872 //==================================================================
873 const std::string
&CInterfaceExprValue::getString() const
877 nlwarning("<CInterfaceExprValue::getString> bad type!");
878 static const std::string empty
;
884 //==================================================================
885 NLMISC::CRGBA
CInterfaceExprValue::getRGBA() const
889 nlwarning("<CInterfaceExprValue::getRGBA> bad type!");
893 col
.R
= (uint8
)(_RGBAValue
&0xff);
894 col
.G
= (uint8
)((_RGBAValue
>>8)&0xff);
895 col
.B
= (uint8
)((_RGBAValue
>>16)&0xff);
896 col
.A
= (uint8
)((_RGBAValue
>>24)&0xff);
900 //==================================================================
901 CInterfaceExprUserType
*CInterfaceExprValue::getUserType() const
903 if (_Type
!= UserType
)
905 nlwarning("<CInterfaceExprValue::getUserType> bad type!");
908 return _UserTypeValue
;
911 //==================================================================
912 CInterfaceExprValue::CInterfaceExprValue(const CInterfaceExprValue
&other
) : _Type(NoType
)
917 //==================================================================
918 CInterfaceExprValue
&CInterfaceExprValue::operator = (const CInterfaceExprValue
&other
)
925 case Boolean
: _BoolValue
= other
._BoolValue
; break;
926 case Integer
: _IntegerValue
= other
._IntegerValue
; break;
927 case Double
: _DoubleValue
= other
._DoubleValue
; break;
928 case String
: _StringValue
= other
._StringValue
; break;
929 case RGBA
: _RGBAValue
= other
._RGBAValue
; break;
931 if (other
._UserTypeValue
!= NULL
)
933 _UserTypeValue
= other
._UserTypeValue
->clone();
937 _UserTypeValue
= NULL
;
942 nlwarning("<CInterfaceExprValue::operator=> bad source type") ;