2017-03-05 15:47:28 +01:00
/*************************************************************************/
/* visual_script_expression.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
2017-04-08 00:11:42 +02:00
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
2017-03-05 15:47:28 +01:00
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
2016-09-04 15:34:40 +02:00
# include "visual_script_expression.h"
2017-03-05 16:44:50 +01:00
bool VisualScriptExpression : : _set ( const StringName & p_name , const Variant & p_value ) {
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
if ( String ( p_name ) = = " expression " ) {
expression = p_value ;
expression_dirty = true ;
2016-09-04 15:34:40 +02:00
ports_changed_notify ( ) ;
return true ;
}
2017-03-05 16:44:50 +01:00
if ( String ( p_name ) = = " out_type " ) {
output_type = Variant : : Type ( int ( p_value ) ) ;
expression_dirty = true ;
2016-09-04 15:34:40 +02:00
ports_changed_notify ( ) ;
return true ;
}
2017-03-05 16:44:50 +01:00
if ( String ( p_name ) = = " sequenced " ) {
sequenced = p_value ;
2016-09-04 15:34:40 +02:00
ports_changed_notify ( ) ;
return true ;
}
2017-03-05 16:44:50 +01:00
if ( String ( p_name ) = = " input_count " ) {
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
int from = inputs . size ( ) ;
2016-09-04 15:34:40 +02:00
inputs . resize ( int ( p_value ) ) ;
2017-03-05 16:44:50 +01:00
for ( int i = from ; i < inputs . size ( ) ; i + + ) {
inputs [ i ] . name = String : : chr ( ' a ' + i ) ;
if ( from = = 0 ) {
inputs [ i ] . type = output_type ;
2016-09-04 15:34:40 +02:00
} else {
2017-03-05 16:44:50 +01:00
inputs [ i ] . type = inputs [ from - 1 ] . type ;
2016-09-04 15:34:40 +02:00
}
}
2017-03-05 16:44:50 +01:00
expression_dirty = true ;
2016-09-04 15:34:40 +02:00
ports_changed_notify ( ) ;
_change_notify ( ) ;
return true ;
}
2017-07-01 02:30:17 +02:00
if ( String ( p_name ) . begins_with ( " input_ " ) ) {
2016-09-04 15:34:40 +02:00
2017-07-01 02:30:17 +02:00
int idx = String ( p_name ) . get_slicec ( ' _ ' , 1 ) . get_slicec ( ' / ' , 0 ) . to_int ( ) ;
2017-03-05 16:44:50 +01:00
ERR_FAIL_INDEX_V ( idx , inputs . size ( ) , false ) ;
2016-09-04 15:34:40 +02:00
2017-07-01 02:30:17 +02:00
String what = String ( p_name ) . get_slice ( " / " , 1 ) ;
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
if ( what = = " type " ) {
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
inputs [ idx ] . type = Variant : : Type ( int ( p_value ) ) ;
} else if ( what = = " name " ) {
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
inputs [ idx ] . name = p_value ;
2016-09-04 15:34:40 +02:00
} else {
return false ;
}
2017-03-05 16:44:50 +01:00
expression_dirty = true ;
2016-09-04 15:34:40 +02:00
ports_changed_notify ( ) ;
return true ;
}
return false ;
}
2017-03-05 16:44:50 +01:00
bool VisualScriptExpression : : _get ( const StringName & p_name , Variant & r_ret ) const {
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
if ( String ( p_name ) = = " expression " ) {
r_ret = expression ;
2016-09-04 15:34:40 +02:00
return true ;
}
2017-03-05 16:44:50 +01:00
if ( String ( p_name ) = = " out_type " ) {
r_ret = output_type ;
2016-09-04 15:34:40 +02:00
return true ;
}
2017-03-05 16:44:50 +01:00
if ( String ( p_name ) = = " sequenced " ) {
r_ret = sequenced ;
2016-09-04 15:34:40 +02:00
return true ;
}
2017-03-05 16:44:50 +01:00
if ( String ( p_name ) = = " input_count " ) {
r_ret = inputs . size ( ) ;
2016-09-04 15:34:40 +02:00
return true ;
}
2017-07-01 02:30:17 +02:00
if ( String ( p_name ) . begins_with ( " input_ " ) ) {
2016-09-04 15:34:40 +02:00
2017-07-01 02:30:17 +02:00
int idx = String ( p_name ) . get_slicec ( ' _ ' , 1 ) . get_slicec ( ' / ' , 0 ) . to_int ( ) ;
2017-03-05 16:44:50 +01:00
ERR_FAIL_INDEX_V ( idx , inputs . size ( ) , false ) ;
2016-09-04 15:34:40 +02:00
2017-07-01 02:30:17 +02:00
String what = String ( p_name ) . get_slice ( " / " , 1 ) ;
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
if ( what = = " type " ) {
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
r_ret = inputs [ idx ] . type ;
} else if ( what = = " name " ) {
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
r_ret = inputs [ idx ] . name ;
2016-09-04 15:34:40 +02:00
} else {
return false ;
}
return true ;
}
return false ;
}
2017-03-05 16:44:50 +01:00
void VisualScriptExpression : : _get_property_list ( List < PropertyInfo > * p_list ) const {
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
String argt = " Any " ;
for ( int i = 1 ; i < Variant : : VARIANT_MAX ; i + + ) {
argt + = " , " + Variant : : get_type_name ( Variant : : Type ( i ) ) ;
2016-09-04 15:34:40 +02:00
}
2017-03-05 16:44:50 +01:00
p_list - > push_back ( PropertyInfo ( Variant : : STRING , " expression " , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_NOEDITOR ) ) ;
p_list - > push_back ( PropertyInfo ( Variant : : INT , " out_type " , PROPERTY_HINT_ENUM , argt ) ) ;
p_list - > push_back ( PropertyInfo ( Variant : : INT , " input_count " , PROPERTY_HINT_RANGE , " 0,64,1 " ) ) ;
p_list - > push_back ( PropertyInfo ( Variant : : BOOL , " sequenced " ) ) ;
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < inputs . size ( ) ; i + + ) {
2016-09-04 15:34:40 +02:00
2017-07-01 02:30:17 +02:00
p_list - > push_back ( PropertyInfo ( Variant : : INT , " input_ " + itos ( i ) + " /type " , PROPERTY_HINT_ENUM , argt ) ) ;
p_list - > push_back ( PropertyInfo ( Variant : : STRING , " input_ " + itos ( i ) + " /name " ) ) ;
2016-09-04 15:34:40 +02:00
}
}
int VisualScriptExpression : : get_output_sequence_port_count ( ) const {
2017-03-05 16:44:50 +01:00
return sequenced ? 1 : 0 ;
2016-09-04 15:34:40 +02:00
}
2017-03-05 16:44:50 +01:00
bool VisualScriptExpression : : has_input_sequence_port ( ) const {
2016-09-04 15:34:40 +02:00
return sequenced ;
}
2017-03-05 16:44:50 +01:00
String VisualScriptExpression : : get_output_sequence_port_text ( int p_port ) const {
2016-09-04 15:34:40 +02:00
return String ( ) ;
}
2017-03-05 16:44:50 +01:00
int VisualScriptExpression : : get_input_value_port_count ( ) const {
2016-09-04 15:34:40 +02:00
return inputs . size ( ) ;
}
2017-03-05 16:44:50 +01:00
int VisualScriptExpression : : get_output_value_port_count ( ) const {
2016-09-04 15:34:40 +02:00
return 1 ;
}
2017-03-05 16:44:50 +01:00
PropertyInfo VisualScriptExpression : : get_input_value_port_info ( int p_idx ) const {
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
return PropertyInfo ( inputs [ p_idx ] . type , inputs [ p_idx ] . name ) ;
2016-09-04 15:34:40 +02:00
}
2017-03-05 16:44:50 +01:00
PropertyInfo VisualScriptExpression : : get_output_value_port_info ( int p_idx ) const {
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
return PropertyInfo ( output_type , " result " ) ;
2016-09-04 15:34:40 +02:00
}
2017-03-05 16:44:50 +01:00
String VisualScriptExpression : : get_caption ( ) const {
2016-09-04 15:34:40 +02:00
return " Expression " ;
}
2017-03-05 16:44:50 +01:00
String VisualScriptExpression : : get_text ( ) const {
2016-09-04 15:34:40 +02:00
return expression ;
}
2017-03-05 16:44:50 +01:00
Error VisualScriptExpression : : _get_token ( Token & r_token ) {
2016-09-04 15:34:40 +02:00
while ( true ) {
2017-03-05 16:44:50 +01:00
# define GET_CHAR() (str_ofs >= expression.length() ? 0 : expression[str_ofs++])
2016-09-04 15:34:40 +02:00
CharType cchar = GET_CHAR ( ) ;
2017-03-05 16:44:50 +01:00
if ( cchar = = 0 ) {
r_token . type = TK_EOF ;
2016-09-04 15:34:40 +02:00
return OK ;
}
2017-03-05 16:44:50 +01:00
switch ( cchar ) {
2016-09-04 15:34:40 +02:00
case 0 : {
2017-03-05 16:44:50 +01:00
r_token . type = TK_EOF ;
2016-09-04 15:34:40 +02:00
return OK ;
} break ;
case ' { ' : {
2017-03-05 16:44:50 +01:00
r_token . type = TK_CURLY_BRACKET_OPEN ;
2016-09-04 15:34:40 +02:00
return OK ;
} ;
case ' } ' : {
2017-03-05 16:44:50 +01:00
r_token . type = TK_CURLY_BRACKET_CLOSE ;
2016-09-04 15:34:40 +02:00
return OK ;
} ;
case ' [ ' : {
2017-03-05 16:44:50 +01:00
r_token . type = TK_BRACKET_OPEN ;
2016-09-04 15:34:40 +02:00
return OK ;
} ;
case ' ] ' : {
2017-03-05 16:44:50 +01:00
r_token . type = TK_BRACKET_CLOSE ;
2016-09-04 15:34:40 +02:00
return OK ;
} ;
case ' ( ' : {
2017-03-05 16:44:50 +01:00
r_token . type = TK_PARENTHESIS_OPEN ;
2016-09-04 15:34:40 +02:00
return OK ;
} ;
case ' ) ' : {
2017-03-05 16:44:50 +01:00
r_token . type = TK_PARENTHESIS_CLOSE ;
2016-09-04 15:34:40 +02:00
return OK ;
} ;
case ' , ' : {
2017-03-05 16:44:50 +01:00
r_token . type = TK_COMMA ;
2016-09-04 15:34:40 +02:00
return OK ;
} ;
case ' : ' : {
2017-03-05 16:44:50 +01:00
r_token . type = TK_COLON ;
2016-09-04 15:34:40 +02:00
return OK ;
} ;
case ' . ' : {
2017-03-05 16:44:50 +01:00
r_token . type = TK_PERIOD ;
2016-09-04 15:34:40 +02:00
return OK ;
} ;
case ' = ' : {
2017-03-05 16:44:50 +01:00
cchar = GET_CHAR ( ) ;
if ( cchar = = ' = ' ) {
r_token . type = TK_OP_EQUAL ;
2016-09-04 15:34:40 +02:00
} else {
_set_error ( " Expected '=' " ) ;
2017-03-05 16:44:50 +01:00
r_token . type = TK_ERROR ;
2016-09-04 15:34:40 +02:00
return ERR_PARSE_ERROR ;
}
return OK ;
} ;
case ' ! ' : {
2017-03-05 16:44:50 +01:00
if ( expression [ str_ofs ] = = ' = ' ) {
r_token . type = TK_OP_NOT_EQUAL ;
2016-09-04 15:34:40 +02:00
str_ofs + + ;
} else {
2017-03-05 16:44:50 +01:00
r_token . type = TK_OP_NOT ;
2016-09-04 15:34:40 +02:00
}
return OK ;
} ;
case ' > ' : {
2017-03-05 16:44:50 +01:00
if ( expression [ str_ofs ] = = ' = ' ) {
r_token . type = TK_OP_GREATER_EQUAL ;
2016-09-04 15:34:40 +02:00
str_ofs + + ;
2017-03-05 16:44:50 +01:00
} else if ( expression [ str_ofs ] = = ' > ' ) {
r_token . type = TK_OP_SHIFT_RIGHT ;
2016-09-04 15:34:40 +02:00
str_ofs + + ;
} else {
2017-03-05 16:44:50 +01:00
r_token . type = TK_OP_GREATER ;
2016-09-04 15:34:40 +02:00
}
return OK ;
} ;
case ' < ' : {
2017-03-05 16:44:50 +01:00
if ( expression [ str_ofs ] = = ' = ' ) {
r_token . type = TK_OP_LESS_EQUAL ;
2016-09-04 15:34:40 +02:00
str_ofs + + ;
2017-03-05 16:44:50 +01:00
} else if ( expression [ str_ofs ] = = ' < ' ) {
r_token . type = TK_OP_SHIFT_LEFT ;
2016-09-04 15:34:40 +02:00
str_ofs + + ;
} else {
2017-03-05 16:44:50 +01:00
r_token . type = TK_OP_LESS ;
2016-09-04 15:34:40 +02:00
}
return OK ;
} ;
case ' + ' : {
2017-03-05 16:44:50 +01:00
r_token . type = TK_OP_ADD ;
2016-09-04 15:34:40 +02:00
return OK ;
} ;
case ' - ' : {
2017-03-05 16:44:50 +01:00
r_token . type = TK_OP_SUB ;
2016-09-04 15:34:40 +02:00
return OK ;
} ;
case ' / ' : {
2017-03-05 16:44:50 +01:00
r_token . type = TK_OP_DIV ;
2016-09-04 15:34:40 +02:00
return OK ;
} ;
case ' * ' : {
2017-03-05 16:44:50 +01:00
r_token . type = TK_OP_MUL ;
2016-09-04 15:34:40 +02:00
return OK ;
} ;
case ' % ' : {
2017-03-05 16:44:50 +01:00
r_token . type = TK_OP_MOD ;
2016-09-04 15:34:40 +02:00
return OK ;
} ;
case ' & ' : {
2017-03-05 16:44:50 +01:00
if ( expression [ str_ofs ] = = ' & ' ) {
r_token . type = TK_OP_AND ;
2016-09-04 15:34:40 +02:00
str_ofs + + ;
} else {
2017-03-05 16:44:50 +01:00
r_token . type = TK_OP_BIT_AND ;
2016-09-04 15:34:40 +02:00
}
return OK ;
} ;
case ' | ' : {
2017-03-05 16:44:50 +01:00
if ( expression [ str_ofs ] = = ' | ' ) {
r_token . type = TK_OP_OR ;
2016-09-04 15:34:40 +02:00
str_ofs + + ;
} else {
2017-03-05 16:44:50 +01:00
r_token . type = TK_OP_BIT_OR ;
2016-09-04 15:34:40 +02:00
}
return OK ;
} ;
case ' ^ ' : {
2017-03-05 16:44:50 +01:00
r_token . type = TK_OP_BIT_XOR ;
2016-09-04 15:34:40 +02:00
return OK ;
} ;
case ' ~ ' : {
2017-03-05 16:44:50 +01:00
r_token . type = TK_OP_BIT_INVERT ;
2016-09-04 15:34:40 +02:00
return OK ;
} ;
case ' " ' : {
String str ;
2017-03-05 16:44:50 +01:00
while ( true ) {
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
CharType ch = GET_CHAR ( ) ;
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
if ( ch = = 0 ) {
2016-09-04 15:34:40 +02:00
_set_error ( " Unterminated String " ) ;
2017-03-05 16:44:50 +01:00
r_token . type = TK_ERROR ;
2016-09-04 15:34:40 +02:00
return ERR_PARSE_ERROR ;
2017-03-05 16:44:50 +01:00
} else if ( ch = = ' " ' ) {
2016-09-04 15:34:40 +02:00
break ;
2017-03-05 16:44:50 +01:00
} else if ( ch = = ' \\ ' ) {
2016-09-04 15:34:40 +02:00
//escaped characters...
CharType next = GET_CHAR ( ) ;
2017-03-05 16:44:50 +01:00
if ( next = = 0 ) {
2016-09-04 15:34:40 +02:00
_set_error ( " Unterminated String " ) ;
2017-03-05 16:44:50 +01:00
r_token . type = TK_ERROR ;
return ERR_PARSE_ERROR ;
2016-09-04 15:34:40 +02:00
}
2017-03-05 16:44:50 +01:00
CharType res = 0 ;
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
switch ( next ) {
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
case ' b ' : res = 8 ; break ;
case ' t ' : res = 9 ; break ;
case ' n ' : res = 10 ; break ;
case ' f ' : res = 12 ; break ;
case ' r ' : res = 13 ; break ;
2016-09-04 15:34:40 +02:00
case ' u ' : {
//hexnumbarh - oct is deprecated
2017-03-05 16:44:50 +01:00
for ( int j = 0 ; j < 4 ; j + + ) {
2016-09-04 15:34:40 +02:00
CharType c = GET_CHAR ( ) ;
2017-03-05 16:44:50 +01:00
if ( c = = 0 ) {
2016-09-04 15:34:40 +02:00
_set_error ( " Unterminated String " ) ;
2017-03-05 16:44:50 +01:00
r_token . type = TK_ERROR ;
2016-09-04 15:34:40 +02:00
return ERR_PARSE_ERROR ;
}
2017-03-05 16:44:50 +01:00
if ( ! ( ( c > = ' 0 ' & & c < = ' 9 ' ) | | ( c > = ' a ' & & c < = ' f ' ) | | ( c > = ' A ' & & c < = ' F ' ) ) ) {
2016-09-04 15:34:40 +02:00
_set_error ( " Malformed hex constant in string " ) ;
2017-03-05 16:44:50 +01:00
r_token . type = TK_ERROR ;
2016-09-04 15:34:40 +02:00
return ERR_PARSE_ERROR ;
}
CharType v ;
2017-03-05 16:44:50 +01:00
if ( c > = ' 0 ' & & c < = ' 9 ' ) {
v = c - ' 0 ' ;
} else if ( c > = ' a ' & & c < = ' f ' ) {
v = c - ' a ' ;
v + = 10 ;
} else if ( c > = ' A ' & & c < = ' F ' ) {
v = c - ' A ' ;
v + = 10 ;
2016-09-04 15:34:40 +02:00
} else {
ERR_PRINT ( " BUG " ) ;
2017-03-05 16:44:50 +01:00
v = 0 ;
2016-09-04 15:34:40 +02:00
}
2017-03-05 16:44:50 +01:00
res < < = 4 ;
res | = v ;
2016-09-04 15:34:40 +02:00
}
} break ;
//case '\"': res='\"'; break;
//case '\\': res='\\'; break;
//case '/': res='/'; break;
default : {
res = next ;
//r_err_str="Invalid escape sequence";
//return ERR_PARSE_ERROR;
} break ;
}
2017-03-05 16:44:50 +01:00
str + = res ;
2016-09-04 15:34:40 +02:00
} else {
2017-03-05 16:44:50 +01:00
str + = ch ;
2016-09-04 15:34:40 +02:00
}
}
2017-03-05 16:44:50 +01:00
r_token . type = TK_CONSTANT ;
r_token . value = str ;
2016-09-04 15:34:40 +02:00
return OK ;
} break ;
default : {
2017-03-05 16:44:50 +01:00
if ( cchar < = 32 ) {
2016-09-04 15:34:40 +02:00
break ;
}
2017-03-05 16:44:50 +01:00
if ( cchar = = ' - ' | | ( cchar > = ' 0 ' & & cchar < = ' 9 ' ) ) {
2016-09-04 15:34:40 +02:00
//a number
String num ;
# define READING_SIGN 0
# define READING_INT 1
# define READING_DEC 2
# define READING_EXP 3
# define READING_DONE 4
2017-03-05 16:44:50 +01:00
int reading = READING_INT ;
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
if ( cchar = = ' - ' ) {
num + = ' - ' ;
cchar = GET_CHAR ( ) ;
2016-09-04 15:34:40 +02:00
}
CharType c = cchar ;
2017-03-05 16:44:50 +01:00
bool exp_sign = false ;
bool exp_beg = false ;
bool is_float = false ;
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
while ( true ) {
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
switch ( reading ) {
2016-09-04 15:34:40 +02:00
case READING_INT : {
2017-03-05 16:44:50 +01:00
if ( c > = ' 0 ' & & c < = ' 9 ' ) {
2016-09-04 15:34:40 +02:00
//pass
2017-03-05 16:44:50 +01:00
} else if ( c = = ' . ' ) {
reading = READING_DEC ;
is_float = true ;
} else if ( c = = ' e ' ) {
reading = READING_EXP ;
2016-09-04 15:34:40 +02:00
} else {
2017-03-05 16:44:50 +01:00
reading = READING_DONE ;
2016-09-04 15:34:40 +02:00
}
2017-03-05 16:44:50 +01:00
} break ;
2016-09-04 15:34:40 +02:00
case READING_DEC : {
2017-03-05 16:44:50 +01:00
if ( c > = ' 0 ' & & c < = ' 9 ' ) {
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
} else if ( c = = ' e ' ) {
reading = READING_EXP ;
2016-09-04 15:34:40 +02:00
} else {
2017-03-05 16:44:50 +01:00
reading = READING_DONE ;
2016-09-04 15:34:40 +02:00
}
2017-03-05 16:44:50 +01:00
} break ;
2016-09-04 15:34:40 +02:00
case READING_EXP : {
2017-03-05 16:44:50 +01:00
if ( c > = ' 0 ' & & c < = ' 9 ' ) {
exp_beg = true ;
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
} else if ( ( c = = ' - ' | | c = = ' + ' ) & & ! exp_sign & & ! exp_beg ) {
if ( c = = ' - ' )
is_float = true ;
exp_sign = true ;
2016-09-04 15:34:40 +02:00
} else {
2017-03-05 16:44:50 +01:00
reading = READING_DONE ;
2016-09-04 15:34:40 +02:00
}
2017-03-05 16:44:50 +01:00
} break ;
2016-09-04 15:34:40 +02:00
}
2017-03-05 16:44:50 +01:00
if ( reading = = READING_DONE )
2016-09-04 15:34:40 +02:00
break ;
2017-03-05 16:44:50 +01:00
num + = String : : chr ( c ) ;
2016-09-04 15:34:40 +02:00
c = GET_CHAR ( ) ;
}
str_ofs - - ;
2017-03-05 16:44:50 +01:00
r_token . type = TK_CONSTANT ;
2016-09-04 15:34:40 +02:00
if ( is_float )
2017-03-05 16:44:50 +01:00
r_token . value = num . to_double ( ) ;
2016-09-04 15:34:40 +02:00
else
2017-03-05 16:44:50 +01:00
r_token . value = num . to_int ( ) ;
2016-09-04 15:34:40 +02:00
return OK ;
2017-03-05 16:44:50 +01:00
} else if ( ( cchar > = ' A ' & & cchar < = ' Z ' ) | | ( cchar > = ' a ' & & cchar < = ' z ' ) | | cchar = = ' _ ' ) {
2016-09-04 15:34:40 +02:00
String id ;
2017-03-05 16:44:50 +01:00
bool first = true ;
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
while ( ( cchar > = ' A ' & & cchar < = ' Z ' ) | | ( cchar > = ' a ' & & cchar < = ' z ' ) | | cchar = = ' _ ' | | ( ! first & & cchar > = ' 0 ' & & cchar < = ' 9 ' ) ) {
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
id + = String : : chr ( cchar ) ;
cchar = GET_CHAR ( ) ;
first = false ;
2016-09-04 15:34:40 +02:00
}
str_ofs - - ; //go back one
2017-03-05 16:44:50 +01:00
if ( id = = " in " ) {
r_token . type = TK_OP_IN ;
} else if ( id = = " null " ) {
r_token . type = TK_CONSTANT ;
r_token . value = Variant ( ) ;
} else if ( id = = " true " ) {
r_token . type = TK_CONSTANT ;
r_token . value = true ;
} else if ( id = = " false " ) {
r_token . type = TK_CONSTANT ;
r_token . value = false ;
} else if ( id = = " PI " ) {
r_token . type = TK_CONSTANT ;
r_token . value = Math_PI ;
2017-02-06 23:44:22 +01:00
} else if ( id = = " INF " ) {
r_token . type = TK_CONSTANT ;
r_token . value = Math_INF ;
} else if ( id = = " NAN " ) {
r_token . type = TK_CONSTANT ;
r_token . value = Math_NAN ;
2017-03-05 16:44:50 +01:00
} else if ( id = = " not " ) {
r_token . type = TK_OP_NOT ;
} else if ( id = = " or " ) {
r_token . type = TK_OP_OR ;
} else if ( id = = " and " ) {
r_token . type = TK_OP_AND ;
} else if ( id = = " self " ) {
r_token . type = TK_SELF ;
2016-09-04 15:34:40 +02:00
} else {
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < Variant : : VARIANT_MAX ; i + + ) {
if ( id = = Variant : : get_type_name ( Variant : : Type ( i ) ) ) {
r_token . type = TK_BASIC_TYPE ;
r_token . value = i ;
2016-09-04 15:34:40 +02:00
return OK ;
break ;
}
}
2016-09-07 00:12:28 +02:00
VisualScriptBuiltinFunc : : BuiltinFunc bifunc = VisualScriptBuiltinFunc : : find_function ( id ) ;
2017-03-05 16:44:50 +01:00
if ( bifunc ! = VisualScriptBuiltinFunc : : FUNC_MAX ) {
r_token . type = TK_BUILTIN_FUNC ;
r_token . value = bifunc ;
2016-09-07 00:12:28 +02:00
return OK ;
}
2017-03-05 16:44:50 +01:00
r_token . type = TK_IDENTIFIER ;
r_token . value = id ;
2016-09-04 15:34:40 +02:00
}
return OK ;
} else {
_set_error ( " Unexpected character. " ) ;
2017-03-05 16:44:50 +01:00
r_token . type = TK_ERROR ;
2016-09-04 15:34:40 +02:00
return ERR_PARSE_ERROR ;
}
}
}
}
2017-03-05 16:44:50 +01:00
r_token . type = TK_ERROR ;
2016-09-04 15:34:40 +02:00
return ERR_PARSE_ERROR ;
}
2017-03-05 16:44:50 +01:00
const char * VisualScriptExpression : : token_name [ TK_MAX ] = {
" CURLY BRACKET OPEN " ,
" CURLY BRACKET CLOSE " ,
" BRACKET OPEN " ,
" BRACKET CLOSE " ,
" PARENTHESIS OPEN " ,
" PARENTHESIS CLOSE " ,
" IDENTIFIER " ,
" BUILTIN FUNC " ,
" SELF " ,
" CONSTANT " ,
" BASIC TYPE " ,
" COLON " ,
" COMMA " ,
" PERIOD " ,
" OP IN " ,
" OP EQUAL " ,
" OP NOT EQUAL " ,
" OP LESS " ,
" OP LESS EQUAL " ,
" OP GREATER " ,
" OP GREATER EQUAL " ,
" OP AND " ,
" OP OR " ,
" OP NOT " ,
" OP ADD " ,
" OP SUB " ,
" OP MUL " ,
" OP DIV " ,
" OP MOD " ,
" OP SHIFT LEFT " ,
" OP SHIFT RIGHT " ,
" OP BIT AND " ,
" OP BIT OR " ,
" OP BIT XOR " ,
" OP BIT INVERT " ,
" EOF " ,
" ERROR "
2016-09-04 15:34:40 +02:00
} ;
2017-03-05 16:44:50 +01:00
VisualScriptExpression : : ENode * VisualScriptExpression : : _parse_expression ( ) {
2016-09-04 15:34:40 +02:00
Vector < Expression > expression ;
2017-03-05 16:44:50 +01:00
while ( true ) {
2016-09-04 15:34:40 +02:00
//keep appending stuff to expression
2017-03-05 16:44:50 +01:00
ENode * expr = NULL ;
2016-09-04 15:34:40 +02:00
Token tk ;
_get_token ( tk ) ;
if ( error_set )
return NULL ;
2017-03-05 16:44:50 +01:00
switch ( tk . type ) {
2016-09-04 15:34:40 +02:00
case TK_CURLY_BRACKET_OPEN : {
//a dictionary
DictionaryNode * dn = alloc_node < DictionaryNode > ( ) ;
2017-03-05 16:44:50 +01:00
while ( true ) {
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
int cofs = str_ofs ;
2016-09-04 15:34:40 +02:00
_get_token ( tk ) ;
2017-03-05 16:44:50 +01:00
if ( tk . type = = TK_CURLY_BRACKET_CLOSE ) {
2016-09-04 15:34:40 +02:00
break ;
}
2017-03-05 16:44:50 +01:00
str_ofs = cofs ; //revert
2016-09-04 15:34:40 +02:00
//parse an expression
2017-03-05 16:44:50 +01:00
ENode * expr = _parse_expression ( ) ;
2016-09-04 15:34:40 +02:00
if ( ! expr )
return NULL ;
dn - > dict . push_back ( expr ) ;
_get_token ( tk ) ;
2017-03-05 16:44:50 +01:00
if ( tk . type ! = TK_COLON ) {
2016-09-04 15:34:40 +02:00
_set_error ( " Expected ':' " ) ;
return NULL ;
}
2017-03-05 16:44:50 +01:00
expr = _parse_expression ( ) ;
2016-09-04 15:34:40 +02:00
if ( ! expr )
return NULL ;
dn - > dict . push_back ( expr ) ;
2017-03-05 16:44:50 +01:00
cofs = str_ofs ;
2016-09-04 15:34:40 +02:00
_get_token ( tk ) ;
2017-03-05 16:44:50 +01:00
if ( tk . type = = TK_COMMA ) {
2016-09-04 15:34:40 +02:00
//all good
2017-03-05 16:44:50 +01:00
} else if ( tk . type = = TK_CURLY_BRACKET_CLOSE ) {
str_ofs = cofs ;
2016-09-04 15:34:40 +02:00
} else {
_set_error ( " Expected ',' or '}' " ) ;
}
}
2017-03-05 16:44:50 +01:00
expr = dn ;
2016-09-04 15:34:40 +02:00
} break ;
case TK_BRACKET_OPEN : {
//an array
ArrayNode * an = alloc_node < ArrayNode > ( ) ;
2017-03-05 16:44:50 +01:00
while ( true ) {
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
int cofs = str_ofs ;
2016-09-04 15:34:40 +02:00
_get_token ( tk ) ;
2017-03-05 16:44:50 +01:00
if ( tk . type = = TK_BRACKET_CLOSE ) {
2016-09-04 15:34:40 +02:00
break ;
}
2017-03-05 16:44:50 +01:00
str_ofs = cofs ; //revert
2016-09-04 15:34:40 +02:00
//parse an expression
2017-03-05 16:44:50 +01:00
ENode * expr = _parse_expression ( ) ;
2016-09-04 15:34:40 +02:00
if ( ! expr )
return NULL ;
an - > array . push_back ( expr ) ;
2017-03-05 16:44:50 +01:00
cofs = str_ofs ;
2016-09-04 15:34:40 +02:00
_get_token ( tk ) ;
2017-03-05 16:44:50 +01:00
if ( tk . type = = TK_COMMA ) {
2016-09-04 15:34:40 +02:00
//all good
2017-03-05 16:44:50 +01:00
} else if ( tk . type = = TK_BRACKET_CLOSE ) {
str_ofs = cofs ;
2016-09-04 15:34:40 +02:00
} else {
_set_error ( " Expected ',' or ']' " ) ;
}
}
2017-03-05 16:44:50 +01:00
expr = an ;
2016-09-04 15:34:40 +02:00
} break ;
case TK_PARENTHESIS_OPEN : {
//a suexpression
2017-03-05 16:44:50 +01:00
ENode * e = _parse_expression ( ) ;
2016-09-04 15:34:40 +02:00
if ( error_set )
return NULL ;
_get_token ( tk ) ;
2017-03-05 16:44:50 +01:00
if ( tk . type ! = TK_PARENTHESIS_CLOSE ) {
2016-09-04 15:34:40 +02:00
_set_error ( " Expected ')' " ) ;
return NULL ;
}
2017-03-05 16:44:50 +01:00
expr = e ;
2016-09-04 15:34:40 +02:00
} break ;
case TK_IDENTIFIER : {
String what = tk . value ;
2017-03-05 16:44:50 +01:00
int index = - 1 ;
for ( int i = 0 ; i < inputs . size ( ) ; i + + ) {
if ( what = = inputs [ i ] . name ) {
index = i ;
2016-09-04 15:34:40 +02:00
break ;
}
}
2017-03-05 16:44:50 +01:00
if ( index ! = - 1 ) {
2016-09-04 15:34:40 +02:00
InputNode * input = alloc_node < InputNode > ( ) ;
2017-03-05 16:44:50 +01:00
input - > index = index ;
expr = input ;
2016-09-04 15:34:40 +02:00
} else {
2017-03-05 16:44:50 +01:00
_set_error ( " Invalid input identifier ' " + what + " '. For script variables, use self (locals are for inputs). " + what ) ;
2016-09-04 15:34:40 +02:00
return NULL ;
}
} break ;
case TK_SELF : {
SelfNode * self = alloc_node < SelfNode > ( ) ;
2017-03-05 16:44:50 +01:00
expr = self ;
2016-09-04 15:34:40 +02:00
} break ;
case TK_CONSTANT : {
ConstantNode * constant = alloc_node < ConstantNode > ( ) ;
2017-03-05 16:44:50 +01:00
constant - > value = tk . value ;
expr = constant ;
2016-09-04 15:34:40 +02:00
} break ;
case TK_BASIC_TYPE : {
//constructor..
Variant : : Type bt = Variant : : Type ( int ( tk . value ) ) ;
_get_token ( tk ) ;
2017-03-05 16:44:50 +01:00
if ( tk . type ! = TK_PARENTHESIS_OPEN ) {
2016-09-04 15:34:40 +02:00
_set_error ( " Expected '(' " ) ;
return NULL ;
}
ConstructorNode * constructor = alloc_node < ConstructorNode > ( ) ;
2017-03-05 16:44:50 +01:00
constructor - > data_type = bt ;
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
while ( true ) {
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
int cofs = str_ofs ;
2016-09-04 15:34:40 +02:00
_get_token ( tk ) ;
2017-03-05 16:44:50 +01:00
if ( tk . type = = TK_PARENTHESIS_CLOSE ) {
2016-09-04 15:34:40 +02:00
break ;
}
2017-03-05 16:44:50 +01:00
str_ofs = cofs ; //revert
2016-09-04 15:34:40 +02:00
//parse an expression
2017-03-05 16:44:50 +01:00
ENode * expr = _parse_expression ( ) ;
2016-09-04 15:34:40 +02:00
if ( ! expr )
return NULL ;
constructor - > arguments . push_back ( expr ) ;
2017-03-05 16:44:50 +01:00
cofs = str_ofs ;
2016-09-04 15:34:40 +02:00
_get_token ( tk ) ;
2017-03-05 16:44:50 +01:00
if ( tk . type = = TK_COMMA ) {
2016-09-04 15:34:40 +02:00
//all good
2017-03-05 16:44:50 +01:00
} else if ( tk . type = = TK_PARENTHESIS_CLOSE ) {
str_ofs = cofs ;
2016-09-04 15:34:40 +02:00
} else {
_set_error ( " Expected ',' or ')' " ) ;
}
}
2017-03-05 16:44:50 +01:00
expr = constructor ;
2016-09-04 15:34:40 +02:00
2016-09-07 00:12:28 +02:00
} break ;
case TK_BUILTIN_FUNC : {
//builtin function
Variant : : Type bt = Variant : : Type ( int ( tk . value ) ) ;
_get_token ( tk ) ;
2017-03-05 16:44:50 +01:00
if ( tk . type ! = TK_PARENTHESIS_OPEN ) {
2016-09-07 00:12:28 +02:00
_set_error ( " Expected '(' " ) ;
return NULL ;
}
BuiltinFuncNode * bifunc = alloc_node < BuiltinFuncNode > ( ) ;
2017-03-05 16:44:50 +01:00
bifunc - > func = VisualScriptBuiltinFunc : : BuiltinFunc ( int ( tk . value ) ) ;
2016-09-07 00:12:28 +02:00
2017-03-05 16:44:50 +01:00
while ( true ) {
2016-09-07 00:12:28 +02:00
2017-03-05 16:44:50 +01:00
int cofs = str_ofs ;
2016-09-07 00:12:28 +02:00
_get_token ( tk ) ;
2017-03-05 16:44:50 +01:00
if ( tk . type = = TK_PARENTHESIS_CLOSE ) {
2016-09-07 00:12:28 +02:00
break ;
}
2017-03-05 16:44:50 +01:00
str_ofs = cofs ; //revert
2016-09-07 00:12:28 +02:00
//parse an expression
2017-03-05 16:44:50 +01:00
ENode * expr = _parse_expression ( ) ;
2016-09-07 00:12:28 +02:00
if ( ! expr )
return NULL ;
bifunc - > arguments . push_back ( expr ) ;
2017-03-05 16:44:50 +01:00
cofs = str_ofs ;
2016-09-07 00:12:28 +02:00
_get_token ( tk ) ;
2017-03-05 16:44:50 +01:00
if ( tk . type = = TK_COMMA ) {
2016-09-07 00:12:28 +02:00
//all good
2017-03-05 16:44:50 +01:00
} else if ( tk . type = = TK_PARENTHESIS_CLOSE ) {
str_ofs = cofs ;
2016-09-07 00:12:28 +02:00
} else {
_set_error ( " Expected ',' or ')' " ) ;
}
}
int expected_args = VisualScriptBuiltinFunc : : get_func_argument_count ( bifunc - > func ) ;
if ( bifunc - > arguments . size ( ) ! = expected_args ) {
2017-03-05 16:44:50 +01:00
_set_error ( " Builtin func ' " + VisualScriptBuiltinFunc : : get_func_name ( bifunc - > func ) + " ' expects " + itos ( expected_args ) + " arguments. " ) ;
2016-09-07 00:12:28 +02:00
}
2017-03-05 16:44:50 +01:00
expr = bifunc ;
2016-09-07 00:12:28 +02:00
2016-09-04 15:34:40 +02:00
} break ;
case TK_OP_SUB : {
Expression e ;
2017-03-05 16:44:50 +01:00
e . is_op = true ;
e . op = Variant : : OP_NEGATE ;
2016-09-04 15:34:40 +02:00
expression . push_back ( e ) ;
continue ;
} break ;
case TK_OP_NOT : {
Expression e ;
2017-03-05 16:44:50 +01:00
e . is_op = true ;
e . op = Variant : : OP_NOT ;
2016-09-04 15:34:40 +02:00
expression . push_back ( e ) ;
continue ;
} break ;
default : {
_set_error ( " Expected expression. " ) ;
return NULL ;
} break ;
}
//before going to operators, must check indexing!
2017-03-05 16:44:50 +01:00
while ( true ) {
int cofs2 = str_ofs ;
2016-09-04 15:34:40 +02:00
_get_token ( tk ) ;
if ( error_set )
return NULL ;
2017-03-05 16:44:50 +01:00
bool done = false ;
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
switch ( tk . type ) {
2016-09-04 15:34:40 +02:00
case TK_BRACKET_OPEN : {
//value indexing
IndexNode * index = alloc_node < IndexNode > ( ) ;
2017-03-05 16:44:50 +01:00
index - > base = expr ;
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
ENode * what = _parse_expression ( ) ;
2016-09-04 15:34:40 +02:00
if ( ! what )
return NULL ;
2017-03-05 16:44:50 +01:00
index - > index = what ;
2016-09-04 15:34:40 +02:00
_get_token ( tk ) ;
2017-03-05 16:44:50 +01:00
if ( tk . type ! = TK_BRACKET_CLOSE ) {
2016-09-04 15:34:40 +02:00
_set_error ( " Expected ']' at end of index. " ) ;
return NULL ;
}
2017-03-05 16:44:50 +01:00
expr = index ;
2016-09-04 15:34:40 +02:00
} break ;
case TK_PERIOD : {
//named indexing or function call
_get_token ( tk ) ;
2017-03-05 16:44:50 +01:00
if ( tk . type ! = TK_IDENTIFIER ) {
2016-09-04 15:34:40 +02:00
_set_error ( " Expected identifier after '.' " ) ;
return NULL ;
}
2017-03-05 16:44:50 +01:00
StringName identifier = tk . value ;
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
int cofs = str_ofs ;
2016-09-04 15:34:40 +02:00
_get_token ( tk ) ;
2017-03-05 16:44:50 +01:00
if ( tk . type = = TK_PARENTHESIS_OPEN ) {
2016-09-04 15:34:40 +02:00
//function call
CallNode * func_call = alloc_node < CallNode > ( ) ;
2017-03-05 16:44:50 +01:00
func_call - > method = identifier ;
func_call - > base = expr ;
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
while ( true ) {
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
int cofs = str_ofs ;
2016-09-04 15:34:40 +02:00
_get_token ( tk ) ;
2017-03-05 16:44:50 +01:00
if ( tk . type = = TK_PARENTHESIS_CLOSE ) {
2016-09-04 15:34:40 +02:00
break ;
}
2017-03-05 16:44:50 +01:00
str_ofs = cofs ; //revert
2016-09-04 15:34:40 +02:00
//parse an expression
2017-03-05 16:44:50 +01:00
ENode * expr = _parse_expression ( ) ;
2016-09-04 15:34:40 +02:00
if ( ! expr )
return NULL ;
func_call - > arguments . push_back ( expr ) ;
2017-03-05 16:44:50 +01:00
cofs = str_ofs ;
2016-09-04 15:34:40 +02:00
_get_token ( tk ) ;
2017-03-05 16:44:50 +01:00
if ( tk . type = = TK_COMMA ) {
2016-09-04 15:34:40 +02:00
//all good
2017-03-05 16:44:50 +01:00
} else if ( tk . type = = TK_PARENTHESIS_CLOSE ) {
str_ofs = cofs ;
2016-09-04 15:34:40 +02:00
} else {
_set_error ( " Expected ',' or ')' " ) ;
}
}
2017-03-05 16:44:50 +01:00
expr = func_call ;
2016-09-04 15:34:40 +02:00
} else {
//named indexing
2017-03-05 16:44:50 +01:00
str_ofs = cofs ;
2016-09-04 15:34:40 +02:00
NamedIndexNode * index = alloc_node < NamedIndexNode > ( ) ;
2017-03-05 16:44:50 +01:00
index - > base = expr ;
index - > name = identifier ;
expr = index ;
2016-09-04 15:34:40 +02:00
}
} break ;
default : {
2017-03-05 16:44:50 +01:00
str_ofs = cofs2 ;
done = true ;
2016-09-04 15:34:40 +02:00
} break ;
}
if ( done )
break ;
}
//push expression
{
Expression e ;
2017-03-05 16:44:50 +01:00
e . is_op = false ;
e . node = expr ;
2016-09-04 15:34:40 +02:00
expression . push_back ( e ) ;
}
//ok finally look for an operator
2017-03-05 16:44:50 +01:00
int cofs = str_ofs ;
2016-09-04 15:34:40 +02:00
_get_token ( tk ) ;
if ( error_set )
return NULL ;
Variant : : Operator op = Variant : : OP_MAX ;
2017-03-05 16:44:50 +01:00
switch ( tk . type ) {
case TK_OP_IN : op = Variant : : OP_IN ; break ;
case TK_OP_EQUAL : op = Variant : : OP_EQUAL ; break ;
case TK_OP_NOT_EQUAL : op = Variant : : OP_NOT_EQUAL ; break ;
case TK_OP_LESS : op = Variant : : OP_LESS ; break ;
case TK_OP_LESS_EQUAL : op = Variant : : OP_LESS_EQUAL ; break ;
case TK_OP_GREATER : op = Variant : : OP_GREATER ; break ;
case TK_OP_GREATER_EQUAL : op = Variant : : OP_GREATER_EQUAL ; break ;
case TK_OP_AND : op = Variant : : OP_AND ; break ;
case TK_OP_OR : op = Variant : : OP_OR ; break ;
case TK_OP_NOT : op = Variant : : OP_NOT ; break ;
case TK_OP_ADD : op = Variant : : OP_ADD ; break ;
case TK_OP_SUB : op = Variant : : OP_SUBSTRACT ; break ;
case TK_OP_MUL : op = Variant : : OP_MULTIPLY ; break ;
case TK_OP_DIV : op = Variant : : OP_DIVIDE ; break ;
case TK_OP_MOD : op = Variant : : OP_MODULE ; break ;
case TK_OP_SHIFT_LEFT : op = Variant : : OP_SHIFT_LEFT ; break ;
case TK_OP_SHIFT_RIGHT : op = Variant : : OP_SHIFT_RIGHT ; break ;
case TK_OP_BIT_AND : op = Variant : : OP_BIT_AND ; break ;
case TK_OP_BIT_OR : op = Variant : : OP_BIT_OR ; break ;
case TK_OP_BIT_XOR : op = Variant : : OP_BIT_XOR ; break ;
case TK_OP_BIT_INVERT : op = Variant : : OP_BIT_NEGATE ; break ;
2016-09-04 15:34:40 +02:00
default : { } ;
}
2017-03-05 16:44:50 +01:00
if ( op = = Variant : : OP_MAX ) { //stop appending stuff
str_ofs = cofs ;
2016-09-04 15:34:40 +02:00
break ;
}
//push operator and go on
{
Expression e ;
2017-03-05 16:44:50 +01:00
e . is_op = true ;
e . op = op ;
2016-09-04 15:34:40 +02:00
expression . push_back ( e ) ;
}
}
/* Reduce the set set of expressions and place them in an operator tree, respecting precedence */
2017-03-05 16:44:50 +01:00
while ( expression . size ( ) > 1 ) {
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
int next_op = - 1 ;
int min_priority = 0xFFFFF ;
bool is_unary = false ;
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < expression . size ( ) ; i + + ) {
2016-09-04 15:34:40 +02:00
if ( ! expression [ i ] . is_op ) {
continue ;
}
int priority ;
2017-03-05 16:44:50 +01:00
bool unary = false ;
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
switch ( expression [ i ] . op ) {
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
case Variant : : OP_BIT_NEGATE :
priority = 0 ;
unary = true ;
break ;
case Variant : : OP_NEGATE :
priority = 1 ;
unary = true ;
break ;
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
case Variant : : OP_MULTIPLY : priority = 2 ; break ;
case Variant : : OP_DIVIDE : priority = 2 ; break ;
case Variant : : OP_MODULE : priority = 2 ; break ;
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
case Variant : : OP_ADD : priority = 3 ; break ;
case Variant : : OP_SUBSTRACT : priority = 3 ; break ;
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
case Variant : : OP_SHIFT_LEFT : priority = 4 ; break ;
case Variant : : OP_SHIFT_RIGHT : priority = 4 ; break ;
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
case Variant : : OP_BIT_AND : priority = 5 ; break ;
case Variant : : OP_BIT_XOR : priority = 6 ; break ;
case Variant : : OP_BIT_OR : priority = 7 ; break ;
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
case Variant : : OP_LESS : priority = 8 ; break ;
case Variant : : OP_LESS_EQUAL : priority = 8 ; break ;
case Variant : : OP_GREATER : priority = 8 ; break ;
case Variant : : OP_GREATER_EQUAL : priority = 8 ; break ;
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
case Variant : : OP_EQUAL : priority = 8 ; break ;
case Variant : : OP_NOT_EQUAL : priority = 8 ; break ;
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
case Variant : : OP_IN : priority = 10 ; break ;
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
case Variant : : OP_NOT :
priority = 11 ;
unary = true ;
break ;
case Variant : : OP_AND : priority = 12 ; break ;
case Variant : : OP_OR : priority = 13 ; break ;
2016-09-04 15:34:40 +02:00
default : {
2017-03-05 16:44:50 +01:00
_set_error ( " Parser bug, invalid operator in expression: " + itos ( expression [ i ] . op ) ) ;
2016-09-04 15:34:40 +02:00
return NULL ;
}
}
2017-03-05 16:44:50 +01:00
if ( priority < min_priority ) {
2016-09-04 15:34:40 +02:00
// < is used for left to right (default)
// <= is used for right to left
2017-03-05 16:44:50 +01:00
next_op = i ;
min_priority = priority ;
is_unary = unary ;
2016-09-04 15:34:40 +02:00
}
}
2017-03-05 16:44:50 +01:00
if ( next_op = = - 1 ) {
2016-09-04 15:34:40 +02:00
_set_error ( " Yet another parser bug.... " ) ;
2017-03-05 16:44:50 +01:00
ERR_FAIL_COND_V ( next_op = = - 1 , NULL ) ;
2016-09-04 15:34:40 +02:00
}
// OK! create operator..
if ( is_unary ) {
2017-03-05 16:44:50 +01:00
int expr_pos = next_op ;
while ( expression [ expr_pos ] . is_op ) {
2016-09-04 15:34:40 +02:00
expr_pos + + ;
2017-03-05 16:44:50 +01:00
if ( expr_pos = = expression . size ( ) ) {
2016-09-04 15:34:40 +02:00
//can happen..
_set_error ( " Unexpected end of expression.. " ) ;
return NULL ;
}
}
//consecutively do unary opeators
2017-03-05 16:44:50 +01:00
for ( int i = expr_pos - 1 ; i > = next_op ; i - - ) {
2016-09-04 15:34:40 +02:00
OperatorNode * op = alloc_node < OperatorNode > ( ) ;
2017-03-05 16:44:50 +01:00
op - > op = expression [ i ] . op ;
op - > nodes [ 0 ] = expression [ i + 1 ] . node ;
op - > nodes [ 1 ] = NULL ;
expression [ i ] . is_op = false ;
expression [ i ] . node = op ;
expression . remove ( i + 1 ) ;
2016-09-04 15:34:40 +02:00
}
} else {
2017-03-05 16:44:50 +01:00
if ( next_op < 1 | | next_op > = ( expression . size ( ) - 1 ) ) {
2016-09-04 15:34:40 +02:00
_set_error ( " Parser bug.. " ) ;
ERR_FAIL_V ( NULL ) ;
}
OperatorNode * op = alloc_node < OperatorNode > ( ) ;
2017-03-05 16:44:50 +01:00
op - > op = expression [ next_op ] . op ;
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
if ( expression [ next_op - 1 ] . is_op ) {
2016-09-04 15:34:40 +02:00
_set_error ( " Parser bug.. " ) ;
ERR_FAIL_V ( NULL ) ;
}
2017-03-05 16:44:50 +01:00
if ( expression [ next_op + 1 ] . is_op ) {
2016-09-04 15:34:40 +02:00
// this is not invalid and can really appear
// but it becomes invalid anyway because no binary op
// can be followed by an unary op in a valid combination,
2017-03-24 21:45:31 +01:00
// due to how precedence works, unaries will always disappear first
2016-09-04 15:34:40 +02:00
_set_error ( " Unexpected two consecutive operators. " ) ;
return NULL ;
}
2017-03-05 16:44:50 +01:00
op - > nodes [ 0 ] = expression [ next_op - 1 ] . node ; //expression goes as left
op - > nodes [ 1 ] = expression [ next_op + 1 ] . node ; //next expression goes as right
2016-09-04 15:34:40 +02:00
//replace all 3 nodes by this operator and make it an expression
2017-03-05 16:44:50 +01:00
expression [ next_op - 1 ] . node = op ;
2016-09-04 15:34:40 +02:00
expression . remove ( next_op ) ;
expression . remove ( next_op ) ;
}
}
return expression [ 0 ] . node ;
}
bool VisualScriptExpression : : _compile_expression ( ) {
if ( ! expression_dirty )
return error_set ;
if ( nodes ) {
memdelete ( nodes ) ;
2017-03-05 16:44:50 +01:00
nodes = NULL ;
root = NULL ;
2016-09-04 15:34:40 +02:00
}
2017-03-05 16:44:50 +01:00
error_str = String ( ) ;
error_set = false ;
str_ofs = 0 ;
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
root = _parse_expression ( ) ;
2016-09-04 15:34:40 +02:00
if ( error_set ) {
2017-03-05 16:44:50 +01:00
root = NULL ;
2016-09-04 15:34:40 +02:00
if ( nodes ) {
memdelete ( nodes ) ;
}
2017-03-05 16:44:50 +01:00
nodes = NULL ;
2016-09-04 15:34:40 +02:00
return true ;
}
2017-03-05 16:44:50 +01:00
expression_dirty = false ;
2016-09-04 15:34:40 +02:00
return false ;
}
class VisualScriptNodeInstanceExpression : public VisualScriptNodeInstance {
public :
2017-03-05 16:44:50 +01:00
VisualScriptInstance * instance ;
2016-09-04 15:34:40 +02:00
VisualScriptExpression * expression ;
//virtual int get_working_memory_size() const { return 0; }
//execute by parsing the tree directly
2017-03-05 16:44:50 +01:00
virtual bool _execute ( const Variant * * p_inputs , VisualScriptExpression : : ENode * p_node , Variant & r_ret , String & r_error_str , Variant : : CallError & ce ) {
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
switch ( p_node - > type ) {
case VisualScriptExpression : : ENode : : TYPE_INPUT : {
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
const VisualScriptExpression : : InputNode * in = static_cast < const VisualScriptExpression : : InputNode * > ( p_node ) ;
r_ret = * p_inputs [ in - > index ] ;
2016-09-04 15:34:40 +02:00
} break ;
2017-03-05 16:44:50 +01:00
case VisualScriptExpression : : ENode : : TYPE_CONSTANT : {
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
const VisualScriptExpression : : ConstantNode * c = static_cast < const VisualScriptExpression : : ConstantNode * > ( p_node ) ;
r_ret = c - > value ;
2016-09-04 15:34:40 +02:00
} break ;
2017-03-05 16:44:50 +01:00
case VisualScriptExpression : : ENode : : TYPE_SELF : {
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
r_ret = instance - > get_owner_ptr ( ) ;
2016-09-04 15:34:40 +02:00
} break ;
2017-03-05 16:44:50 +01:00
case VisualScriptExpression : : ENode : : TYPE_OPERATOR : {
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
const VisualScriptExpression : : OperatorNode * op = static_cast < const VisualScriptExpression : : OperatorNode * > ( p_node ) ;
2016-09-04 15:34:40 +02:00
Variant a ;
2017-03-05 16:44:50 +01:00
bool ret = _execute ( p_inputs , op - > nodes [ 0 ] , a , r_error_str , ce ) ;
2016-09-04 15:34:40 +02:00
if ( ret )
return true ;
Variant b ;
if ( op - > nodes [ 1 ] ) {
2017-03-05 16:44:50 +01:00
ret = _execute ( p_inputs , op - > nodes [ 1 ] , b , r_error_str , ce ) ;
2016-09-04 15:34:40 +02:00
if ( ret )
return true ;
}
2017-03-05 16:44:50 +01:00
bool valid = true ;
Variant : : evaluate ( op - > op , a , b , r_ret , valid ) ;
2016-09-04 15:34:40 +02:00
if ( ! valid ) {
2017-03-05 16:44:50 +01:00
r_error_str = " Invalid operands to operator " + Variant : : get_operator_name ( op - > op ) + " : " + Variant : : get_type_name ( a . get_type ( ) ) + " and " + Variant : : get_type_name ( b . get_type ( ) ) + " . " ;
2016-09-04 15:34:40 +02:00
return true ;
}
} break ;
2017-03-05 16:44:50 +01:00
case VisualScriptExpression : : ENode : : TYPE_INDEX : {
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
const VisualScriptExpression : : IndexNode * index = static_cast < const VisualScriptExpression : : IndexNode * > ( p_node ) ;
2016-09-04 15:34:40 +02:00
Variant base ;
2017-03-05 16:44:50 +01:00
bool ret = _execute ( p_inputs , index - > base , base , r_error_str , ce ) ;
2016-09-04 15:34:40 +02:00
if ( ret )
return true ;
Variant idx ;
2017-03-05 16:44:50 +01:00
ret = _execute ( p_inputs , index - > index , idx , r_error_str , ce ) ;
2016-09-04 15:34:40 +02:00
if ( ret )
return true ;
bool valid ;
2017-03-05 16:44:50 +01:00
r_ret = base . get ( idx , & valid ) ;
2016-09-04 15:34:40 +02:00
if ( ! valid ) {
2017-03-05 16:44:50 +01:00
r_error_str = " Invalid index of type " + Variant : : get_type_name ( idx . get_type ( ) ) + " for base of type " + Variant : : get_type_name ( base . get_type ( ) ) + " . " ;
2016-09-04 15:34:40 +02:00
return true ;
}
} break ;
2017-03-05 16:44:50 +01:00
case VisualScriptExpression : : ENode : : TYPE_NAMED_INDEX : {
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
const VisualScriptExpression : : NamedIndexNode * index = static_cast < const VisualScriptExpression : : NamedIndexNode * > ( p_node ) ;
2016-09-04 15:34:40 +02:00
Variant base ;
2017-03-05 16:44:50 +01:00
bool ret = _execute ( p_inputs , index - > base , base , r_error_str , ce ) ;
2016-09-04 15:34:40 +02:00
if ( ret )
return true ;
bool valid ;
2017-03-05 16:44:50 +01:00
r_ret = base . get_named ( index - > name , & valid ) ;
2016-09-04 15:34:40 +02:00
if ( ! valid ) {
2017-03-05 16:44:50 +01:00
r_error_str = " Invalid index ' " + String ( index - > name ) + " ' for base of type " + Variant : : get_type_name ( base . get_type ( ) ) + " . " ;
2016-09-04 15:34:40 +02:00
return true ;
}
} break ;
2017-03-05 16:44:50 +01:00
case VisualScriptExpression : : ENode : : TYPE_ARRAY : {
const VisualScriptExpression : : ArrayNode * array = static_cast < const VisualScriptExpression : : ArrayNode * > ( p_node ) ;
2016-09-04 15:34:40 +02:00
Array arr ;
arr . resize ( array - > array . size ( ) ) ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < array - > array . size ( ) ; i + + ) {
2016-09-04 15:34:40 +02:00
Variant value ;
2017-03-05 16:44:50 +01:00
bool ret = _execute ( p_inputs , array - > array [ i ] , value , r_error_str , ce ) ;
2016-09-04 15:34:40 +02:00
if ( ret )
return true ;
2017-03-05 16:44:50 +01:00
arr [ i ] = value ;
2016-09-04 15:34:40 +02:00
}
2017-03-05 16:44:50 +01:00
r_ret = arr ;
2016-09-04 15:34:40 +02:00
} break ;
2017-03-05 16:44:50 +01:00
case VisualScriptExpression : : ENode : : TYPE_DICTIONARY : {
const VisualScriptExpression : : DictionaryNode * dictionary = static_cast < const VisualScriptExpression : : DictionaryNode * > ( p_node ) ;
2016-09-04 15:34:40 +02:00
Dictionary d ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < dictionary - > dict . size ( ) ; i + = 2 ) {
2016-09-04 15:34:40 +02:00
Variant key ;
2017-03-05 16:44:50 +01:00
bool ret = _execute ( p_inputs , dictionary - > dict [ i + 0 ] , key , r_error_str , ce ) ;
2016-09-04 15:34:40 +02:00
if ( ret )
return true ;
Variant value ;
2017-03-05 16:44:50 +01:00
ret = _execute ( p_inputs , dictionary - > dict [ i + 1 ] , value , r_error_str , ce ) ;
2016-09-04 15:34:40 +02:00
if ( ret )
return true ;
2017-03-05 16:44:50 +01:00
d [ key ] = value ;
2016-09-04 15:34:40 +02:00
}
2017-03-05 16:44:50 +01:00
r_ret = d ;
2016-09-04 15:34:40 +02:00
} break ;
2017-03-05 16:44:50 +01:00
case VisualScriptExpression : : ENode : : TYPE_CONSTRUCTOR : {
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
const VisualScriptExpression : : ConstructorNode * constructor = static_cast < const VisualScriptExpression : : ConstructorNode * > ( p_node ) ;
2016-09-04 15:34:40 +02:00
Vector < Variant > arr ;
2017-03-05 16:44:50 +01:00
Vector < const Variant * > argp ;
2016-09-04 15:34:40 +02:00
arr . resize ( constructor - > arguments . size ( ) ) ;
argp . resize ( constructor - > arguments . size ( ) ) ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < constructor - > arguments . size ( ) ; i + + ) {
2016-09-04 15:34:40 +02:00
Variant value ;
2017-03-05 16:44:50 +01:00
bool ret = _execute ( p_inputs , constructor - > arguments [ i ] , value , r_error_str , ce ) ;
2016-09-04 15:34:40 +02:00
if ( ret )
return true ;
2017-03-05 16:44:50 +01:00
arr [ i ] = value ;
argp [ i ] = & arr [ i ] ;
2016-09-04 15:34:40 +02:00
}
2017-03-05 16:44:50 +01:00
r_ret = Variant : : construct ( constructor - > data_type , argp . ptr ( ) , argp . size ( ) , ce ) ;
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
if ( ce . error ! = Variant : : CallError : : CALL_OK ) {
r_error_str = " Invalid arguments to construct ' " + Variant : : get_type_name ( constructor - > data_type ) + " '. " ;
2016-09-04 15:34:40 +02:00
return true ;
}
2016-09-07 00:12:28 +02:00
} break ;
2017-03-05 16:44:50 +01:00
case VisualScriptExpression : : ENode : : TYPE_BUILTIN_FUNC : {
2016-09-07 00:12:28 +02:00
2017-03-05 16:44:50 +01:00
const VisualScriptExpression : : BuiltinFuncNode * bifunc = static_cast < const VisualScriptExpression : : BuiltinFuncNode * > ( p_node ) ;
2016-09-07 00:12:28 +02:00
Vector < Variant > arr ;
2017-03-05 16:44:50 +01:00
Vector < const Variant * > argp ;
2016-09-07 00:12:28 +02:00
arr . resize ( bifunc - > arguments . size ( ) ) ;
argp . resize ( bifunc - > arguments . size ( ) ) ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < bifunc - > arguments . size ( ) ; i + + ) {
2016-09-07 00:12:28 +02:00
Variant value ;
2017-03-05 16:44:50 +01:00
bool ret = _execute ( p_inputs , bifunc - > arguments [ i ] , value , r_error_str , ce ) ;
2016-09-07 00:12:28 +02:00
if ( ret )
return true ;
2017-03-05 16:44:50 +01:00
arr [ i ] = value ;
argp [ i ] = & arr [ i ] ;
2016-09-07 00:12:28 +02:00
}
2017-03-05 16:44:50 +01:00
VisualScriptBuiltinFunc : : exec_func ( bifunc - > func , argp . ptr ( ) , & r_ret , ce , r_error_str ) ;
2016-09-07 00:12:28 +02:00
2017-03-05 16:44:50 +01:00
if ( ce . error ! = Variant : : CallError : : CALL_OK ) {
r_error_str = " Builtin Call Failed. " + r_error_str ;
2016-09-07 00:12:28 +02:00
return true ;
}
2016-09-04 15:34:40 +02:00
} break ;
2017-03-05 16:44:50 +01:00
case VisualScriptExpression : : ENode : : TYPE_CALL : {
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
const VisualScriptExpression : : CallNode * call = static_cast < const VisualScriptExpression : : CallNode * > ( p_node ) ;
2016-09-04 15:34:40 +02:00
Variant base ;
2017-03-05 16:44:50 +01:00
bool ret = _execute ( p_inputs , call - > base , base , r_error_str , ce ) ;
2016-09-04 15:34:40 +02:00
if ( ret )
return true ;
Vector < Variant > arr ;
2017-03-05 16:44:50 +01:00
Vector < const Variant * > argp ;
2016-09-04 15:34:40 +02:00
arr . resize ( call - > arguments . size ( ) ) ;
argp . resize ( call - > arguments . size ( ) ) ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < call - > arguments . size ( ) ; i + + ) {
2016-09-04 15:34:40 +02:00
Variant value ;
2017-03-05 16:44:50 +01:00
bool ret = _execute ( p_inputs , call - > arguments [ i ] , value , r_error_str , ce ) ;
2016-09-04 15:34:40 +02:00
if ( ret )
return true ;
2017-03-05 16:44:50 +01:00
arr [ i ] = value ;
argp [ i ] = & arr [ i ] ;
2016-09-04 15:34:40 +02:00
}
2017-03-05 16:44:50 +01:00
r_ret = base . call ( call - > method , argp . ptr ( ) , argp . size ( ) , ce ) ;
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
if ( ce . error ! = Variant : : CallError : : CALL_OK ) {
r_error_str = " On call to ' " + String ( call - > method ) + " ': " ;
2016-09-04 15:34:40 +02:00
return true ;
}
} break ;
}
return false ;
}
2017-03-05 16:44:50 +01:00
virtual int step ( const Variant * * p_inputs , Variant * * p_outputs , StartMode p_start_mode , Variant * p_working_mem , Variant : : CallError & r_error , String & r_error_str ) {
2016-09-04 15:34:40 +02:00
if ( ! expression - > root | | expression - > error_set ) {
2017-03-05 16:44:50 +01:00
r_error_str = expression - > error_str ;
r_error . error = Variant : : CallError : : CALL_ERROR_INVALID_METHOD ;
2016-09-04 15:34:40 +02:00
return 0 ;
}
2017-03-05 16:44:50 +01:00
bool error = _execute ( p_inputs , expression - > root , * p_outputs [ 0 ] , r_error_str , r_error ) ;
if ( error & & r_error . error = = Variant : : CallError : : CALL_OK ) {
r_error . error = Variant : : CallError : : CALL_ERROR_INVALID_METHOD ;
2016-09-04 15:34:40 +02:00
}
# ifdef DEBUG_ENABLED
2017-03-05 16:44:50 +01:00
if ( ! error & & expression - > output_type ! = Variant : : NIL & & ! Variant : : can_convert_strict ( p_outputs [ 0 ] - > get_type ( ) , expression - > output_type ) ) {
2016-09-04 15:34:40 +02:00
2017-03-05 16:44:50 +01:00
r_error_str + = " Can't convert expression result from " + Variant : : get_type_name ( p_outputs [ 0 ] - > get_type ( ) ) + " to " + Variant : : get_type_name ( expression - > output_type ) + " . " ;
r_error . error = Variant : : CallError : : CALL_ERROR_INVALID_METHOD ;
2016-09-04 15:34:40 +02:00
}
2016-09-05 23:50:30 +02:00
# endif
2016-09-04 15:34:40 +02:00
return 0 ;
}
} ;
2017-03-05 16:44:50 +01:00
VisualScriptNodeInstance * VisualScriptExpression : : instance ( VisualScriptInstance * p_instance ) {
2016-09-04 15:34:40 +02:00
_compile_expression ( ) ;
2017-03-05 16:44:50 +01:00
VisualScriptNodeInstanceExpression * instance = memnew ( VisualScriptNodeInstanceExpression ) ;
instance - > instance = p_instance ;
instance - > expression = this ;
2016-09-04 15:34:40 +02:00
return instance ;
}
2017-03-05 16:44:50 +01:00
VisualScriptExpression : : VisualScriptExpression ( ) {
output_type = Variant : : NIL ;
expression_dirty = true ;
error_set = true ;
root = NULL ;
nodes = NULL ;
sequenced = false ;
2016-09-04 15:34:40 +02:00
}
VisualScriptExpression : : ~ VisualScriptExpression ( ) {
if ( nodes ) {
memdelete ( nodes ) ;
}
}
void register_visual_script_expression_node ( ) {
2017-03-05 16:44:50 +01:00
VisualScriptLanguage : : singleton - > add_register_func ( " operators/expression " , create_node_generic < VisualScriptExpression > ) ;
2016-09-04 15:34:40 +02:00
}