summaryrefslogtreecommitdiff
path: root/modules/visual_script
diff options
context:
space:
mode:
Diffstat (limited to 'modules/visual_script')
-rw-r--r--modules/visual_script/SCsub4
-rw-r--r--modules/visual_script/register_types.cpp4
-rw-r--r--modules/visual_script/visual_script.cpp60
-rw-r--r--modules/visual_script/visual_script.h3
-rw-r--r--modules/visual_script/visual_script_builtin_funcs.cpp797
-rw-r--r--modules/visual_script/visual_script_builtin_funcs.h6
-rw-r--r--modules/visual_script/visual_script_editor.cpp46
-rw-r--r--modules/visual_script/visual_script_editor.h1
-rw-r--r--modules/visual_script/visual_script_expression.cpp1523
-rw-r--r--modules/visual_script/visual_script_expression.h280
-rw-r--r--modules/visual_script/visual_script_func_nodes.cpp8
-rw-r--r--modules/visual_script/visual_script_nodes.cpp2
12 files changed, 2317 insertions, 417 deletions
diff --git a/modules/visual_script/SCsub b/modules/visual_script/SCsub
index 403fe68f66..0882406761 100644
--- a/modules/visual_script/SCsub
+++ b/modules/visual_script/SCsub
@@ -1,5 +1,7 @@
+#!/usr/bin/env python
+
Import('env')
-env.add_source_files(env.modules_sources,"*.cpp")
+env.add_source_files(env.modules_sources, "*.cpp")
Export('env')
diff --git a/modules/visual_script/register_types.cpp b/modules/visual_script/register_types.cpp
index 0e97107660..ad54149b51 100644
--- a/modules/visual_script/register_types.cpp
+++ b/modules/visual_script/register_types.cpp
@@ -36,6 +36,7 @@
#include "visual_script_builtin_funcs.h"
#include "visual_script_flow_control.h"
#include "visual_script_yield_nodes.h"
+#include "visual_script_expression.h"
VisualScriptLanguage *visual_script_language=NULL;
@@ -98,11 +99,14 @@ void register_visual_script_types() {
ObjectTypeDB::register_type<VisualScriptBuiltinFunc>();
+ ObjectTypeDB::register_type<VisualScriptExpression>();
+
register_visual_script_nodes();
register_visual_script_func_nodes();
register_visual_script_builtin_func_node();
register_visual_script_flow_control_nodes();
register_visual_script_yield_nodes();
+ register_visual_script_expression_node();
#ifdef TOOLS_ENABLED
VisualScriptEditor::register_editor();
diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp
index e69e6ac278..bd042c8989 100644
--- a/modules/visual_script/visual_script.cpp
+++ b/modules/visual_script/visual_script.cpp
@@ -3,7 +3,7 @@
#include "scene/main/node.h"
#include "os/os.h"
#include "globals.h"
-#define SCRIPT_VARIABLES_PREFIX "script_variables/"
+
//used by editor, this is not really saved
@@ -615,8 +615,6 @@ void VisualScript::add_variable(const StringName& p_name,const Variant& p_defaul
v._export=p_export;
variables[p_name]=v;
- script_variable_remap[SCRIPT_VARIABLES_PREFIX+String(p_name)]=p_name;
-
#ifdef TOOLS_ENABLED
_update_placeholders();
@@ -633,7 +631,6 @@ void VisualScript::remove_variable(const StringName& p_name) {
ERR_FAIL_COND(!variables.has(p_name));
variables.erase(p_name);
- script_variable_remap.erase(SCRIPT_VARIABLES_PREFIX+String(p_name));
#ifdef TOOLS_ENABLED
_update_placeholders();
@@ -928,7 +925,7 @@ void VisualScript::_update_placeholders() {
continue;
PropertyInfo p = E->get().info;
- p.name=SCRIPT_VARIABLES_PREFIX+String(E->key());
+ p.name=String(E->key());
pinfo.push_back(p);
values[p.name]=E->get().default_value;
}
@@ -964,7 +961,7 @@ ScriptInstance* VisualScript::instance_create(Object *p_this) {
continue;
PropertyInfo p = E->get().info;
- p.name=SCRIPT_VARIABLES_PREFIX+String(E->key());
+ p.name=String(E->key());
pinfo.push_back(p);
values[p.name]=E->get().default_value;
}
@@ -1062,10 +1059,10 @@ void VisualScript::get_script_signal_list(List<MethodInfo> *r_signals) const {
bool VisualScript::get_property_default_value(const StringName& p_property,Variant& r_value) const {
- if (!script_variable_remap.has(p_property))
+ if (!variables.has(p_property))
return false;
- r_value=variables[ script_variable_remap[p_property] ].default_value;
+ r_value=variables[ p_property ].default_value;
return true;
}
void VisualScript::get_script_method_list(List<MethodInfo> *p_list) const {
@@ -1133,6 +1130,7 @@ void VisualScript::get_script_property_list(List<PropertyInfo> *p_list) const {
}
}
+#ifdef TOOLS_ENABLED
bool VisualScript::are_subnodes_edited() const {
for(const Map<StringName,Function>::Element *E=functions.front();E;E=E->next()) {
@@ -1146,7 +1144,7 @@ bool VisualScript::are_subnodes_edited() const {
return false;
}
-
+#endif
void VisualScript::_set_data(const Dictionary& p_data) {
@@ -1402,12 +1400,10 @@ VisualScript::~VisualScript() {
bool VisualScriptInstance::set(const StringName& p_name, const Variant& p_value) {
- const Map<StringName,StringName>::Element *remap = script->script_variable_remap.find(p_name);
- if (!remap)
- return false;
- Map<StringName,Variant>::Element *E=variables.find(remap->get());
- ERR_FAIL_COND_V(!E,false);
+ Map<StringName,Variant>::Element *E=variables.find(p_name);
+ if (!E)
+ return false;
E->get()=p_value;
@@ -1417,14 +1413,12 @@ bool VisualScriptInstance::set(const StringName& p_name, const Variant& p_value)
bool VisualScriptInstance::get(const StringName& p_name, Variant &r_ret) const {
- const Map<StringName,StringName>::Element *remap = script->script_variable_remap.find(p_name);
- if (!remap)
+ const Map<StringName,Variant>::Element *E=variables.find(p_name);
+ if (!E)
return false;
- const Map<StringName,Variant>::Element *E=variables.find(remap->get());
- ERR_FAIL_COND_V(!E,false);
-
- return E->get();
+ r_ret=E->get();
+ return true;
}
void VisualScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const{
@@ -1433,21 +1427,15 @@ void VisualScriptInstance::get_property_list(List<PropertyInfo> *p_properties) c
if (!E->get()._export)
continue;
PropertyInfo p = E->get().info;
- p.name=SCRIPT_VARIABLES_PREFIX+String(E->key());
+ p.name=String(E->key());
p_properties->push_back(p);
}
}
Variant::Type VisualScriptInstance::get_property_type(const StringName& p_name,bool *r_is_valid) const{
- const Map<StringName,StringName>::Element *remap = script->script_variable_remap.find(p_name);
- if (!remap) {
- if (r_is_valid)
- *r_is_valid=false;
- return Variant::NIL;
- }
- const Map<StringName,VisualScript::Variable>::Element *E=script->variables.find(remap->get());
+ const Map<StringName,VisualScript::Variable>::Element *E=script->variables.find(p_name);
if (!E) {
if (r_is_valid)
*r_is_valid=false;
@@ -1909,17 +1897,21 @@ Variant VisualScriptInstance::_call_internal(const StringName& p_method, void* p
if (node && (r_error.error!=Variant::CallError::CALL_ERROR_INVALID_METHOD || error_str==String())) {
+ if (error_str!=String()) {
+ error_str+=" ";
+ }
+
if (r_error.error==Variant::CallError::CALL_ERROR_INVALID_ARGUMENT) {
int errorarg=r_error.argument;
- error_str="Cannot convert argument "+itos(errorarg+1)+" to "+Variant::get_type_name(r_error.expected)+".";
+ error_str+="Cannot convert argument "+itos(errorarg+1)+" to "+Variant::get_type_name(r_error.expected)+".";
} else if (r_error.error==Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS) {
- error_str="Expected "+itos(r_error.argument)+" arguments.";
+ error_str+="Expected "+itos(r_error.argument)+" arguments.";
} else if (r_error.error==Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS) {
- error_str="Expected "+itos(r_error.argument)+" arguments.";
+ error_str+="Expected "+itos(r_error.argument)+" arguments.";
} else if (r_error.error==Variant::CallError::CALL_ERROR_INVALID_METHOD) {
- error_str="Invalid Call.";
+ error_str+="Invalid Call.";
} else if (r_error.error==Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL) {
- error_str="Instance is null";
+ error_str+="Base Instance is null";
}
}
@@ -2447,7 +2439,7 @@ void VisualScriptFunctionState::_bind_methods() {
ObjectTypeDB::bind_method(_MD("connect_to_signal","obj","signals","args"),&VisualScriptFunctionState::connect_to_signal);
ObjectTypeDB::bind_method(_MD("resume:Array","args"),&VisualScriptFunctionState::resume,DEFVAL(Variant()));
ObjectTypeDB::bind_method(_MD("is_valid"),&VisualScriptFunctionState::is_valid);
- ObjectTypeDB::bind_native_method(METHOD_FLAGS_DEFAULT,"_signal_callback",&VisualScriptFunctionState::_signal_callback,MethodInfo("_signal_callback"));
+ ObjectTypeDB::bind_vararg_method(METHOD_FLAGS_DEFAULT,"_signal_callback",&VisualScriptFunctionState::_signal_callback,MethodInfo("_signal_callback"));
}
VisualScriptFunctionState::VisualScriptFunctionState() {
diff --git a/modules/visual_script/visual_script.h b/modules/visual_script/visual_script.h
index e59dd76313..63b018b0c2 100644
--- a/modules/visual_script/visual_script.h
+++ b/modules/visual_script/visual_script.h
@@ -227,7 +227,6 @@ friend class VisualScriptInstance;
Map<StringName,Function> functions;
Map<StringName,Variable> variables;
- Map<StringName,StringName> script_variable_remap;
Map<StringName,Vector<Argument> > custom_signals;
Map<Object*,VisualScriptInstance*> instances;
@@ -345,7 +344,9 @@ public:
virtual void get_script_property_list(List<PropertyInfo> *p_list) const;
+#ifdef TOOLS_ENABLED
virtual bool are_subnodes_edited() const;
+#endif
VisualScript();
~VisualScript();
diff --git a/modules/visual_script/visual_script_builtin_funcs.cpp b/modules/visual_script/visual_script_builtin_funcs.cpp
index 1d0bca0b30..2bfb6bc9ea 100644
--- a/modules/visual_script/visual_script_builtin_funcs.cpp
+++ b/modules/visual_script/visual_script_builtin_funcs.cpp
@@ -55,6 +55,7 @@ const char* VisualScriptBuiltinFunc::func_name[VisualScriptBuiltinFunc::FUNC_MAX
"convert",
"typeof",
"type_exists",
+ "char",
"str",
"print",
"printerr",
@@ -65,6 +66,21 @@ const char* VisualScriptBuiltinFunc::func_name[VisualScriptBuiltinFunc::FUNC_MAX
"bytes2var",
};
+VisualScriptBuiltinFunc::BuiltinFunc VisualScriptBuiltinFunc::find_function(const String& p_string) {
+
+ for(int i=0;i<FUNC_MAX;i++) {
+ if (p_string==func_name[i])
+ return BuiltinFunc(i);
+ }
+
+ return FUNC_MAX;
+}
+
+String VisualScriptBuiltinFunc::get_func_name(BuiltinFunc p_func) {
+
+ ERR_FAIL_INDEX_V(p_func,FUNC_MAX,String());
+ return func_name[p_func];
+}
int VisualScriptBuiltinFunc::get_output_sequence_port_count() const {
@@ -88,10 +104,10 @@ bool VisualScriptBuiltinFunc::has_input_sequence_port() const{
}
-int VisualScriptBuiltinFunc::get_input_value_port_count() const{
+int VisualScriptBuiltinFunc::get_func_argument_count(BuiltinFunc p_func) {
- switch(func) {
+ switch(p_func) {
case MATH_RANDOMIZE:
case MATH_RAND:
@@ -126,6 +142,7 @@ int VisualScriptBuiltinFunc::get_input_value_port_count() const{
case LOGIC_NEAREST_PO2:
case OBJ_WEAKREF:
case TYPE_OF:
+ case TEXT_CHAR:
case TEXT_STR:
case TEXT_PRINT:
case TEXT_PRINTERR:
@@ -157,6 +174,11 @@ int VisualScriptBuiltinFunc::get_input_value_port_count() const{
}
return 0;
}
+
+int VisualScriptBuiltinFunc::get_input_value_port_count() const{
+
+ return get_func_argument_count(func);
+}
int VisualScriptBuiltinFunc::get_output_value_port_count() const{
switch(func) {
@@ -342,6 +364,12 @@ PropertyInfo VisualScriptBuiltinFunc::get_input_value_port_info(int p_idx) const
return PropertyInfo(Variant::STRING,"type");
} break;
+ case TEXT_CHAR: {
+
+ return PropertyInfo(Variant::INT,"ascii");
+
+
+ } break;
case TEXT_STR: {
return PropertyInfo(Variant::NIL,"value");
@@ -497,6 +525,7 @@ PropertyInfo VisualScriptBuiltinFunc::get_output_value_port_info(int p_idx) cons
t=Variant::BOOL;
} break;
+ case TEXT_CHAR:
case TEXT_STR: {
t=Variant::STRING;
@@ -560,118 +589,124 @@ VisualScriptBuiltinFunc::BuiltinFunc VisualScriptBuiltinFunc::get_func() {
r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;\
r_error.argument=m_arg;\
r_error.expected=Variant::REAL;\
- return 0;\
+ return;\
}
-class VisualScriptNodeInstanceBuiltinFunc : public VisualScriptNodeInstance {
-public:
- VisualScriptBuiltinFunc *node;
- VisualScriptInstance *instance;
+void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func,const Variant** p_inputs,Variant* r_return,Variant::CallError& r_error,String& r_error_str) {
- VisualScriptBuiltinFunc::BuiltinFunc func;
+ switch(p_func) {
+ case VisualScriptBuiltinFunc::MATH_SIN: {
+ VALIDATE_ARG_NUM(0);
+ *r_return=Math::sin(*p_inputs[0]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_COS: {
- //virtual int get_working_memory_size() const { return 0; }
- //virtual bool is_output_port_unsequenced(int p_idx) const { return false; }
- //virtual bool get_output_port_unsequenced(int p_idx,Variant* r_value,Variant* p_working_mem,String &r_error) const { return true; }
+ VALIDATE_ARG_NUM(0);
+ *r_return=Math::cos(*p_inputs[0]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_TAN: {
- 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) {
+ VALIDATE_ARG_NUM(0);
+ *r_return=Math::tan(*p_inputs[0]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_SINH: {
- switch(func) {
- case VisualScriptBuiltinFunc::MATH_SIN: {
+ VALIDATE_ARG_NUM(0);
+ *r_return=Math::sinh(*p_inputs[0]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_COSH: {
- VALIDATE_ARG_NUM(0);
- *p_outputs[0]=Math::sin(*p_inputs[0]);
- } break;
- case VisualScriptBuiltinFunc::MATH_COS: {
+ VALIDATE_ARG_NUM(0);
+ *r_return=Math::cosh(*p_inputs[0]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_TANH: {
- VALIDATE_ARG_NUM(0);
- *p_outputs[0]=Math::cos(*p_inputs[0]);
- } break;
- case VisualScriptBuiltinFunc::MATH_TAN: {
+ VALIDATE_ARG_NUM(0);
+ *r_return=Math::tanh(*p_inputs[0]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_ASIN: {
- VALIDATE_ARG_NUM(0);
- *p_outputs[0]=Math::tan(*p_inputs[0]);
- } break;
- case VisualScriptBuiltinFunc::MATH_SINH: {
+ VALIDATE_ARG_NUM(0);
+ *r_return=Math::asin(*p_inputs[0]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_ACOS: {
- VALIDATE_ARG_NUM(0);
- *p_outputs[0]=Math::sinh(*p_inputs[0]);
- } break;
- case VisualScriptBuiltinFunc::MATH_COSH: {
+ VALIDATE_ARG_NUM(0);
+ *r_return=Math::acos(*p_inputs[0]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_ATAN: {
- VALIDATE_ARG_NUM(0);
- *p_outputs[0]=Math::cosh(*p_inputs[0]);
- } break;
- case VisualScriptBuiltinFunc::MATH_TANH: {
+ VALIDATE_ARG_NUM(0);
+ *r_return=Math::atan(*p_inputs[0]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_ATAN2: {
- VALIDATE_ARG_NUM(0);
- *p_outputs[0]=Math::tanh(*p_inputs[0]);
- } break;
- case VisualScriptBuiltinFunc::MATH_ASIN: {
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ *r_return=Math::atan2(*p_inputs[0],*p_inputs[1]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_SQRT: {
- VALIDATE_ARG_NUM(0);
- *p_outputs[0]=Math::asin(*p_inputs[0]);
- } break;
- case VisualScriptBuiltinFunc::MATH_ACOS: {
+ VALIDATE_ARG_NUM(0);
+ *r_return=Math::sqrt(*p_inputs[0]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_FMOD: {
- VALIDATE_ARG_NUM(0);
- *p_outputs[0]=Math::acos(*p_inputs[0]);
- } break;
- case VisualScriptBuiltinFunc::MATH_ATAN: {
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ *r_return=Math::fmod(*p_inputs[0],*p_inputs[1]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_FPOSMOD: {
- VALIDATE_ARG_NUM(0);
- *p_outputs[0]=Math::atan(*p_inputs[0]);
- } break;
- case VisualScriptBuiltinFunc::MATH_ATAN2: {
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ *r_return=Math::fposmod(*p_inputs[0],*p_inputs[1]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_FLOOR: {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- *p_outputs[0]=Math::atan2(*p_inputs[0],*p_inputs[1]);
- } break;
- case VisualScriptBuiltinFunc::MATH_SQRT: {
+ VALIDATE_ARG_NUM(0);
+ *r_return=Math::floor(*p_inputs[0]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_CEIL: {
- VALIDATE_ARG_NUM(0);
- *p_outputs[0]=Math::sqrt(*p_inputs[0]);
- } break;
- case VisualScriptBuiltinFunc::MATH_FMOD: {
+ VALIDATE_ARG_NUM(0);
+ *r_return=Math::ceil(*p_inputs[0]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_ROUND: {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- *p_outputs[0]=Math::fmod(*p_inputs[0],*p_inputs[1]);
- } break;
- case VisualScriptBuiltinFunc::MATH_FPOSMOD: {
+ VALIDATE_ARG_NUM(0);
+ *r_return=Math::round(*p_inputs[0]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_ABS: {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- *p_outputs[0]=Math::fposmod(*p_inputs[0],*p_inputs[1]);
- } break;
- case VisualScriptBuiltinFunc::MATH_FLOOR: {
+ if (p_inputs[0]->get_type()==Variant::INT) {
- VALIDATE_ARG_NUM(0);
- *p_outputs[0]=Math::floor(*p_inputs[0]);
- } break;
- case VisualScriptBuiltinFunc::MATH_CEIL: {
+ int64_t i = *p_inputs[0];
+ *r_return=ABS(i);
+ } else if (p_inputs[0]->get_type()==Variant::REAL) {
- VALIDATE_ARG_NUM(0);
- *p_outputs[0]=Math::ceil(*p_inputs[0]);
- } break;
- case VisualScriptBuiltinFunc::MATH_ROUND: {
+ real_t r = *p_inputs[0];
+ *r_return=Math::abs(r);
+ } else {
- VALIDATE_ARG_NUM(0);
- *p_outputs[0]=Math::round(*p_inputs[0]);
- } break;
- case VisualScriptBuiltinFunc::MATH_ABS: {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=0;
+ r_error.expected=Variant::REAL;
+
+ }
+ } break;
+ case VisualScriptBuiltinFunc::MATH_SIGN: {
if (p_inputs[0]->get_type()==Variant::INT) {
int64_t i = *p_inputs[0];
- *p_outputs[0]=ABS(i);
+ *r_return= i < 0 ? -1 : ( i > 0 ? +1 : 0);
} else if (p_inputs[0]->get_type()==Variant::REAL) {
real_t r = *p_inputs[0];
- *p_outputs[0]=Math::abs(r);
+ *r_return= r < 0.0 ? -1.0 : ( r > 0.0 ? +1.0 : 0.0);
} else {
r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
@@ -679,410 +714,419 @@ public:
r_error.expected=Variant::REAL;
}
- } break;
- case VisualScriptBuiltinFunc::MATH_SIGN: {
+ } break;
+ case VisualScriptBuiltinFunc::MATH_POW: {
- if (p_inputs[0]->get_type()==Variant::INT) {
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ *r_return=Math::pow(*p_inputs[0],*p_inputs[1]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_LOG: {
- int64_t i = *p_inputs[0];
- *p_outputs[0]= i < 0 ? -1 : ( i > 0 ? +1 : 0);
- } else if (p_inputs[0]->get_type()==Variant::REAL) {
+ VALIDATE_ARG_NUM(0);
+ *r_return=Math::log(*p_inputs[0]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_EXP: {
- real_t r = *p_inputs[0];
- *p_outputs[0]= r < 0.0 ? -1.0 : ( r > 0.0 ? +1.0 : 0.0);
- } else {
+ VALIDATE_ARG_NUM(0);
+ *r_return=Math::exp(*p_inputs[0]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_ISNAN: {
- r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument=0;
- r_error.expected=Variant::REAL;
+ VALIDATE_ARG_NUM(0);
+ *r_return=Math::is_nan(*p_inputs[0]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_ISINF: {
- }
- } break;
- case VisualScriptBuiltinFunc::MATH_POW: {
+ VALIDATE_ARG_NUM(0);
+ *r_return=Math::is_inf(*p_inputs[0]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_EASE: {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- *p_outputs[0]=Math::pow(*p_inputs[0],*p_inputs[1]);
- } break;
- case VisualScriptBuiltinFunc::MATH_LOG: {
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ *r_return=Math::ease(*p_inputs[0],*p_inputs[1]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_DECIMALS: {
- VALIDATE_ARG_NUM(0);
- *p_outputs[0]=Math::log(*p_inputs[0]);
- } break;
- case VisualScriptBuiltinFunc::MATH_EXP: {
+ VALIDATE_ARG_NUM(0);
+ *r_return=Math::step_decimals(*p_inputs[0]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_STEPIFY: {
- VALIDATE_ARG_NUM(0);
- *p_outputs[0]=Math::exp(*p_inputs[0]);
- } break;
- case VisualScriptBuiltinFunc::MATH_ISNAN: {
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ *r_return=Math::stepify(*p_inputs[0],*p_inputs[1]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_LERP: {
- VALIDATE_ARG_NUM(0);
- *p_outputs[0]=Math::is_nan(*p_inputs[0]);
- } break;
- case VisualScriptBuiltinFunc::MATH_ISINF: {
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ VALIDATE_ARG_NUM(2);
+ *r_return=Math::lerp(*p_inputs[0],*p_inputs[1],*p_inputs[2]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_DECTIME: {
- VALIDATE_ARG_NUM(0);
- *p_outputs[0]=Math::is_inf(*p_inputs[0]);
- } break;
- case VisualScriptBuiltinFunc::MATH_EASE: {
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ VALIDATE_ARG_NUM(2);
+ *r_return=Math::dectime(*p_inputs[0],*p_inputs[1],*p_inputs[2]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_RANDOMIZE: {
+ Math::randomize();
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- *p_outputs[0]=Math::ease(*p_inputs[0],*p_inputs[1]);
- } break;
- case VisualScriptBuiltinFunc::MATH_DECIMALS: {
+ } break;
+ case VisualScriptBuiltinFunc::MATH_RAND: {
+ *r_return=Math::rand();
+ } break;
+ case VisualScriptBuiltinFunc::MATH_RANDF: {
+ *r_return=Math::randf();
+ } break;
+ case VisualScriptBuiltinFunc::MATH_RANDOM: {
- VALIDATE_ARG_NUM(0);
- *p_outputs[0]=Math::step_decimals(*p_inputs[0]);
- } break;
- case VisualScriptBuiltinFunc::MATH_STEPIFY: {
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ *r_return=Math::random(*p_inputs[0],*p_inputs[1]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_SEED: {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- *p_outputs[0]=Math::stepify(*p_inputs[0],*p_inputs[1]);
- } break;
- case VisualScriptBuiltinFunc::MATH_LERP: {
+ VALIDATE_ARG_NUM(0);
+ uint32_t seed=*p_inputs[0];
+ Math::seed(seed);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- VALIDATE_ARG_NUM(2);
- *p_outputs[0]=Math::lerp(*p_inputs[0],*p_inputs[1],*p_inputs[2]);
- } break;
- case VisualScriptBuiltinFunc::MATH_DECTIME: {
+ } break;
+ case VisualScriptBuiltinFunc::MATH_RANDSEED: {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- VALIDATE_ARG_NUM(2);
- *p_outputs[0]=Math::dectime(*p_inputs[0],*p_inputs[1],*p_inputs[2]);
- } break;
- case VisualScriptBuiltinFunc::MATH_RANDOMIZE: {
- Math::randomize();
-
- } break;
- case VisualScriptBuiltinFunc::MATH_RAND: {
- *p_outputs[0]=Math::rand();
- } break;
- case VisualScriptBuiltinFunc::MATH_RANDF: {
- *p_outputs[0]=Math::randf();
- } break;
- case VisualScriptBuiltinFunc::MATH_RANDOM: {
+ VALIDATE_ARG_NUM(0);
+ uint32_t seed=*p_inputs[0];
+ int ret = Math::rand_from_seed(&seed);
+ Array reta;
+ reta.push_back(ret);
+ reta.push_back(seed);
+ *r_return=reta;
+
+ } break;
+ case VisualScriptBuiltinFunc::MATH_DEG2RAD: {
+
+ VALIDATE_ARG_NUM(0);
+ *r_return=Math::deg2rad(*p_inputs[0]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_RAD2DEG: {
+ VALIDATE_ARG_NUM(0);
+ *r_return=Math::rad2deg(*p_inputs[0]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_LINEAR2DB: {
+
+ VALIDATE_ARG_NUM(0);
+ *r_return=Math::linear2db(*p_inputs[0]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_DB2LINEAR: {
+
+ VALIDATE_ARG_NUM(0);
+ *r_return=Math::db2linear(*p_inputs[0]);
+ } break;
+ case VisualScriptBuiltinFunc::LOGIC_MAX: {
+
+ if (p_inputs[0]->get_type()==Variant::INT && p_inputs[1]->get_type()==Variant::INT) {
+
+ int64_t a = *p_inputs[0];
+ int64_t b = *p_inputs[1];
+ *r_return=MAX(a,b);
+ } else {
VALIDATE_ARG_NUM(0);
VALIDATE_ARG_NUM(1);
- *p_outputs[0]=Math::random(*p_inputs[0],*p_inputs[1]);
- } break;
- case VisualScriptBuiltinFunc::MATH_SEED: {
- VALIDATE_ARG_NUM(0);
- uint32_t seed=*p_inputs[0];
- Math::seed(seed);
+ real_t a = *p_inputs[0];
+ real_t b = *p_inputs[1];
- } break;
- case VisualScriptBuiltinFunc::MATH_RANDSEED: {
+ *r_return=MAX(a,b);
+ }
- VALIDATE_ARG_NUM(0);
- uint32_t seed=*p_inputs[0];
- int ret = Math::rand_from_seed(&seed);
- Array reta;
- reta.push_back(ret);
- reta.push_back(seed);
- *p_outputs[0]=reta;
+ } break;
+ case VisualScriptBuiltinFunc::LOGIC_MIN: {
- } break;
- case VisualScriptBuiltinFunc::MATH_DEG2RAD: {
+ if (p_inputs[0]->get_type()==Variant::INT && p_inputs[1]->get_type()==Variant::INT) {
+ int64_t a = *p_inputs[0];
+ int64_t b = *p_inputs[1];
+ *r_return=MIN(a,b);
+ } else {
VALIDATE_ARG_NUM(0);
- *p_outputs[0]=Math::deg2rad(*p_inputs[0]);
- } break;
- case VisualScriptBuiltinFunc::MATH_RAD2DEG: {
+ VALIDATE_ARG_NUM(1);
- VALIDATE_ARG_NUM(0);
- *p_outputs[0]=Math::rad2deg(*p_inputs[0]);
- } break;
- case VisualScriptBuiltinFunc::MATH_LINEAR2DB: {
+ real_t a = *p_inputs[0];
+ real_t b = *p_inputs[1];
- VALIDATE_ARG_NUM(0);
- *p_outputs[0]=Math::linear2db(*p_inputs[0]);
- } break;
- case VisualScriptBuiltinFunc::MATH_DB2LINEAR: {
+ *r_return=MIN(a,b);
+ }
+ } break;
+ case VisualScriptBuiltinFunc::LOGIC_CLAMP: {
- VALIDATE_ARG_NUM(0);
- *p_outputs[0]=Math::db2linear(*p_inputs[0]);
- } break;
- case VisualScriptBuiltinFunc::LOGIC_MAX: {
+ if (p_inputs[0]->get_type()==Variant::INT && p_inputs[1]->get_type()==Variant::INT && p_inputs[2]->get_type()==Variant::INT) {
- if (p_inputs[0]->get_type()==Variant::INT && p_inputs[1]->get_type()==Variant::INT) {
+ int64_t a = *p_inputs[0];
+ int64_t b = *p_inputs[1];
+ int64_t c = *p_inputs[2];
+ *r_return=CLAMP(a,b,c);
+ } else {
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ VALIDATE_ARG_NUM(2);
- int64_t a = *p_inputs[0];
- int64_t b = *p_inputs[1];
- *p_outputs[0]=MAX(a,b);
- } else {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
+ real_t a = *p_inputs[0];
+ real_t b = *p_inputs[1];
+ real_t c = *p_inputs[2];
- real_t a = *p_inputs[0];
- real_t b = *p_inputs[1];
+ *r_return=CLAMP(a,b,c);
+ }
+ } break;
+ case VisualScriptBuiltinFunc::LOGIC_NEAREST_PO2: {
- *p_outputs[0]=MAX(a,b);
- }
+ VALIDATE_ARG_NUM(0);
+ int64_t num = *p_inputs[0];
+ *r_return = nearest_power_of_2(num);
+ } break;
+ case VisualScriptBuiltinFunc::OBJ_WEAKREF: {
- } break;
- case VisualScriptBuiltinFunc::LOGIC_MIN: {
+ if (p_inputs[0]->get_type()!=Variant::OBJECT) {
- if (p_inputs[0]->get_type()==Variant::INT && p_inputs[1]->get_type()==Variant::INT) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=0;
+ r_error.expected=Variant::OBJECT;
- int64_t a = *p_inputs[0];
- int64_t b = *p_inputs[1];
- *p_outputs[0]=MIN(a,b);
- } else {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
+ return;
- real_t a = *p_inputs[0];
- real_t b = *p_inputs[1];
+ }
- *p_outputs[0]=MIN(a,b);
- }
- } break;
- case VisualScriptBuiltinFunc::LOGIC_CLAMP: {
+ if (p_inputs[0]->is_ref()) {
- if (p_inputs[0]->get_type()==Variant::INT && p_inputs[1]->get_type()==Variant::INT && p_inputs[2]->get_type()==Variant::INT) {
+ REF r = *p_inputs[0];
+ if (!r.is_valid()) {
- int64_t a = *p_inputs[0];
- int64_t b = *p_inputs[1];
- int64_t c = *p_inputs[2];
- *p_outputs[0]=CLAMP(a,b,c);
- } else {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- VALIDATE_ARG_NUM(2);
+ return;
+ }
- real_t a = *p_inputs[0];
- real_t b = *p_inputs[1];
- real_t c = *p_inputs[2];
+ Ref<WeakRef> wref = memnew( WeakRef );
+ wref->set_ref(r);
+ *r_return=wref;
+ } else {
+ Object *obj = *p_inputs[0];
+ if (!obj) {
- *p_outputs[0]=CLAMP(a,b,c);
+ return;
}
- } break;
- case VisualScriptBuiltinFunc::LOGIC_NEAREST_PO2: {
+ Ref<WeakRef> wref = memnew( WeakRef );
+ wref->set_obj(obj);
+ *r_return=wref;
+ }
- VALIDATE_ARG_NUM(0);
- int64_t num = *p_inputs[0];
- *p_outputs[0] = nearest_power_of_2(num);
- } break;
- case VisualScriptBuiltinFunc::OBJ_WEAKREF: {
- if (p_inputs[0]->get_type()!=Variant::OBJECT) {
- r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument=0;
- r_error.expected=Variant::OBJECT;
- return 0;
+ } break;
+ case VisualScriptBuiltinFunc::FUNC_FUNCREF: {
- }
+ if (p_inputs[0]->get_type()!=Variant::OBJECT) {
- if (p_inputs[0]->is_ref()) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=0;
+ r_error.expected=Variant::OBJECT;
- REF r = *p_inputs[0];
- if (!r.is_valid()) {
+ return;
- return 0;
- }
+ }
+ if (p_inputs[1]->get_type()!=Variant::STRING && p_inputs[1]->get_type()!=Variant::NODE_PATH) {
- Ref<WeakRef> wref = memnew( WeakRef );
- wref->set_ref(r);
- *p_outputs[0]=wref;
- } else {
- Object *obj = *p_inputs[0];
- if (!obj) {
-
- return 0;
- }
- Ref<WeakRef> wref = memnew( WeakRef );
- wref->set_obj(obj);
- *p_outputs[0]=wref;
- }
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=1;
+ r_error.expected=Variant::STRING;
+ return;
+ }
+ Ref<FuncRef> fr = memnew( FuncRef);
- } break;
- case VisualScriptBuiltinFunc::FUNC_FUNCREF: {
+ fr->set_instance(*p_inputs[0]);
+ fr->set_function(*p_inputs[1]);
- if (p_inputs[0]->get_type()!=Variant::OBJECT) {
+ *r_return=fr;
- r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument=0;
- r_error.expected=Variant::OBJECT;
+ } break;
+ case VisualScriptBuiltinFunc::TYPE_CONVERT: {
- return 0;
+ VALIDATE_ARG_NUM(1);
+ int type=*p_inputs[1];
+ if (type<0 || type>=Variant::VARIANT_MAX) {
- }
- if (p_inputs[1]->get_type()!=Variant::STRING && p_inputs[1]->get_type()!=Variant::NODE_PATH) {
+ r_error_str=RTR("Invalid type argument to convert(), use TYPE_* constants.");
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=0;
+ r_error.expected=Variant::INT;
+ return;
- r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument=1;
- r_error.expected=Variant::STRING;
+ } else {
- return 0;
- }
+ *r_return=Variant::construct(Variant::Type(type),p_inputs,1,r_error);
+ }
+ } break;
+ case VisualScriptBuiltinFunc::TYPE_OF: {
- Ref<FuncRef> fr = memnew( FuncRef);
- fr->set_instance(*p_inputs[0]);
- fr->set_function(*p_inputs[1]);
+ *r_return = p_inputs[0]->get_type();
- *p_outputs[0]=fr;
+ } break;
+ case VisualScriptBuiltinFunc::TYPE_EXISTS: {
- } break;
- case VisualScriptBuiltinFunc::TYPE_CONVERT: {
- VALIDATE_ARG_NUM(1);
- int type=*p_inputs[1];
- if (type<0 || type>=Variant::VARIANT_MAX) {
+ *r_return = ObjectTypeDB::type_exists(*p_inputs[0]);
- *p_outputs[0]=RTR("Invalid type argument to convert(), use TYPE_* constants.");
- r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument=0;
- r_error.expected=Variant::INT;
- return 0;
+ } break;
+ case VisualScriptBuiltinFunc::TEXT_CHAR: {
- } else {
+ CharType result[2] = {*p_inputs[0], 0};
+
+ *r_return=String(result);
+ } break;
+ case VisualScriptBuiltinFunc::TEXT_STR: {
- *p_outputs[0]=Variant::construct(Variant::Type(type),p_inputs,1,r_error);
- }
- } break;
- case VisualScriptBuiltinFunc::TYPE_OF: {
+ String str = *p_inputs[0];
+ *r_return=str;
- *p_outputs[0] = p_inputs[0]->get_type();
+ } break;
+ case VisualScriptBuiltinFunc::TEXT_PRINT: {
- } break;
- case VisualScriptBuiltinFunc::TYPE_EXISTS: {
+ String str = *p_inputs[0];
+ print_line(str);
- *p_outputs[0] = ObjectTypeDB::type_exists(*p_inputs[0]);
+ } break;
- } break;
- case VisualScriptBuiltinFunc::TEXT_STR: {
+ case VisualScriptBuiltinFunc::TEXT_PRINTERR: {
- String str = *p_inputs[0];
+ String str = *p_inputs[0];
- *p_outputs[0]=str;
+ //str+="\n";
+ OS::get_singleton()->printerr("%s\n",str.utf8().get_data());
- } break;
- case VisualScriptBuiltinFunc::TEXT_PRINT: {
- String str = *p_inputs[0];
- print_line(str);
+ } break;
+ case VisualScriptBuiltinFunc::TEXT_PRINTRAW: {
+ String str = *p_inputs[0];
+ //str+="\n";
+ OS::get_singleton()->print("%s",str.utf8().get_data());
- } break;
- case VisualScriptBuiltinFunc::TEXT_PRINTERR: {
+ } break;
+ case VisualScriptBuiltinFunc::VAR_TO_STR: {
- String str = *p_inputs[0];
+ String vars;
+ VariantWriter::write_to_string(*p_inputs[0],vars);
+ *r_return=vars;
+ } break;
+ case VisualScriptBuiltinFunc::STR_TO_VAR: {
- //str+="\n";
- OS::get_singleton()->printerr("%s\n",str.utf8().get_data());
+ if (p_inputs[0]->get_type()!=Variant::STRING) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=0;
+ r_error.expected=Variant::STRING;
+ return;
+ }
- } break;
- case VisualScriptBuiltinFunc::TEXT_PRINTRAW: {
- String str = *p_inputs[0];
+ VariantParser::StreamString ss;
+ ss.s=*p_inputs[0];
- //str+="\n";
- OS::get_singleton()->print("%s",str.utf8().get_data());
+ String errs;
+ int line;
+ Error err = VariantParser::parse(&ss,*r_return,errs,line);
+ if (err!=OK) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=0;
+ r_error.expected=Variant::STRING;
+ *r_return="Parse error at line "+itos(line)+": "+errs;
+ return;
+ }
- } break;
- case VisualScriptBuiltinFunc::VAR_TO_STR: {
+ } break;
+ case VisualScriptBuiltinFunc::VAR_TO_BYTES: {
- String vars;
- VariantWriter::write_to_string(*p_inputs[0],vars);
- *p_outputs[0]=vars;
- } break;
- case VisualScriptBuiltinFunc::STR_TO_VAR: {
- if (p_inputs[0]->get_type()!=Variant::STRING) {
- r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument=0;
- r_error.expected=Variant::STRING;
+ ByteArray barr;
+ int len;
+ Error err = encode_variant(*p_inputs[0],NULL,len);
+ if (err) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=0;
+ r_error.expected=Variant::NIL;
+ r_error_str="Unexpected error encoding variable to bytes, likely unserializable type found (Object or RID).";
+ return;
+ }
- return 0;
- }
+ barr.resize(len);
+ {
+ ByteArray::Write w = barr.write();
+ encode_variant(*p_inputs[0],w.ptr(),len);
- VariantParser::StreamString ss;
- ss.s=*p_inputs[0];
+ }
+ *r_return=barr;
+ } break;
+ case VisualScriptBuiltinFunc::BYTES_TO_VAR: {
- String errs;
- int line;
- Error err = VariantParser::parse(&ss,*p_outputs[0],errs,line);
+ if (p_inputs[0]->get_type()!=Variant::RAW_ARRAY) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=0;
+ r_error.expected=Variant::RAW_ARRAY;
+ return;
+ }
+
+ ByteArray varr=*p_inputs[0];
+ Variant ret;
+ {
+ ByteArray::Read r=varr.read();
+ Error err = decode_variant(ret,r.ptr(),varr.size(),NULL);
if (err!=OK) {
+ r_error_str=RTR("Not enough bytes for decoding bytes, or invalid format.");
r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument=0;
- r_error.expected=Variant::STRING;
- *p_outputs[0]="Parse error at line "+itos(line)+": "+errs;
- return 0;
+ r_error.expected=Variant::RAW_ARRAY;
+ return;
}
- } break;
- case VisualScriptBuiltinFunc::VAR_TO_BYTES: {
+ }
+ *r_return=ret;
- ByteArray barr;
- int len;
- Error err = encode_variant(*p_inputs[0],NULL,len);
- if (err) {
- r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument=0;
- r_error.expected=Variant::NIL;
- *p_outputs[0]="Unexpected error encoding variable to bytes, likely unserializable type found (Object or RID).";
- return 0;
- }
+ } break;
+ default: {}
+ }
- barr.resize(len);
- {
- ByteArray::Write w = barr.write();
- encode_variant(*p_inputs[0],w.ptr(),len);
+}
- }
- *p_outputs[0]=barr;
- } break;
- case VisualScriptBuiltinFunc::BYTES_TO_VAR: {
- if (p_inputs[0]->get_type()!=Variant::RAW_ARRAY) {
- r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument=0;
- r_error.expected=Variant::RAW_ARRAY;
+class VisualScriptNodeInstanceBuiltinFunc : public VisualScriptNodeInstance {
+public:
- return 0;
- }
+ VisualScriptBuiltinFunc *node;
+ VisualScriptInstance *instance;
- ByteArray varr=*p_inputs[0];
- Variant ret;
- {
- ByteArray::Read r=varr.read();
- Error err = decode_variant(ret,r.ptr(),varr.size(),NULL);
- if (err!=OK) {
- *p_outputs[0]=RTR("Not enough bytes for decoding bytes, or invalid format.");
- r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument=0;
- r_error.expected=Variant::RAW_ARRAY;
- return 0;
- }
+ VisualScriptBuiltinFunc::BuiltinFunc func;
- }
- *p_outputs[0]=ret;
+ //virtual int get_working_memory_size() const { return 0; }
+ //virtual bool is_output_port_unsequenced(int p_idx) const { return false; }
+ //virtual bool get_output_port_unsequenced(int p_idx,Variant* r_value,Variant* p_working_mem,String &r_error) const { return true; }
+
+ 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) {
- } break;
- default: {}
- }
+ VisualScriptBuiltinFunc::exec_func(func,p_inputs,p_outputs[0],r_error,r_error_str);
return 0;
}
@@ -1185,6 +1229,7 @@ void register_visual_script_builtin_func_node() {
VisualScriptLanguage::singleton->add_register_func("functions/built_in/convert",create_builtin_func_node<VisualScriptBuiltinFunc::TYPE_CONVERT>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/typeof",create_builtin_func_node<VisualScriptBuiltinFunc::TYPE_OF>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/type_exists",create_builtin_func_node<VisualScriptBuiltinFunc::TYPE_EXISTS>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/char",create_builtin_func_node<VisualScriptBuiltinFunc::TEXT_CHAR>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/str",create_builtin_func_node<VisualScriptBuiltinFunc::TEXT_STR>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/print",create_builtin_func_node<VisualScriptBuiltinFunc::TEXT_PRINT>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/printerr",create_builtin_func_node<VisualScriptBuiltinFunc::TEXT_PRINTERR>);
diff --git a/modules/visual_script/visual_script_builtin_funcs.h b/modules/visual_script/visual_script_builtin_funcs.h
index ebf227a192..8b0d70c1ea 100644
--- a/modules/visual_script/visual_script_builtin_funcs.h
+++ b/modules/visual_script/visual_script_builtin_funcs.h
@@ -57,6 +57,7 @@ public:
TYPE_CONVERT,
TYPE_OF,
TYPE_EXISTS,
+ TEXT_CHAR,
TEXT_STR,
TEXT_PRINT,
TEXT_PRINTERR,
@@ -68,6 +69,11 @@ public:
FUNC_MAX
};
+ static int get_func_argument_count(BuiltinFunc p_func);
+ static String get_func_name(BuiltinFunc p_func);
+ static void exec_func(BuiltinFunc p_func, const Variant** p_inputs, Variant* r_return, Variant::CallError& r_error, String& r_error_str);
+ static BuiltinFunc find_function(const String& p_string);
+
private:
static const char* func_name[FUNC_MAX];
BuiltinFunc func;
diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp
index ccd8635930..02d3c453c4 100644
--- a/modules/visual_script/visual_script_editor.cpp
+++ b/modules/visual_script/visual_script_editor.cpp
@@ -3,6 +3,7 @@
#include "visual_script_nodes.h"
#include "visual_script_flow_control.h"
#include "visual_script_func_nodes.h"
+#include "visual_script_expression.h"
#include "os/input.h"
#include "tools/editor/editor_resource_preview.h"
#include "os/keyboard.h"
@@ -330,7 +331,7 @@ static Color _color_from_type(Variant::Type p_type) {
case Variant::QUAT: color = Color::html("ec69a3"); break;
case Variant::_AABB: color = Color::html("ee7991"); break;
case Variant::MATRIX3: color = Color::html("e3ec69"); break;
- case Variant::TRANSFORM: color = Color::html("ecd669"); break;
+ case Variant::TRANSFORM: color = Color::html("f6a86e"); break;
case Variant::COLOR: color = Color::html("9dff70"); break;
case Variant::IMAGE: color = Color::html("93f1b9"); break;
@@ -502,9 +503,20 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
}
- Label *text = memnew( Label );
- text->set_text(node->get_text());
- gnode->add_child(text);
+ if (node->cast_to<VisualScriptExpression>()) {
+
+ LineEdit *line_edit = memnew( LineEdit );
+ line_edit->set_text(node->get_text());
+ line_edit->set_expand_to_text_length(true);
+ line_edit->add_font_override("font",get_font("source","EditorFonts"));
+ gnode->add_child(line_edit);
+ line_edit->connect("text_changed",this,"_expression_text_changed",varray(E->get()));
+ } else {
+ Label *text = memnew( Label );
+ text->set_text(node->get_text());
+ gnode->add_child(text);
+ }
+
if (node->cast_to<VisualScriptComment>()) {
Ref<VisualScriptComment> vsc=node;
@@ -1197,6 +1209,30 @@ void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_butt
}
}
+void VisualScriptEditor::_expression_text_changed(const String& p_text,int p_id) {
+
+ Ref<VisualScriptExpression> vse = script->get_node(edited_func,p_id);
+ if (!vse.is_valid())
+ return;
+
+
+ updating_graph=true;
+
+ undo_redo->create_action(TTR("Change Expression"),UndoRedo::MERGE_ENDS);
+ undo_redo->add_do_property(vse.ptr(),"expression",p_text);
+ undo_redo->add_undo_property(vse.ptr(),"expression",vse->get("expression"));
+ undo_redo->add_do_method(this,"_update_graph",p_id);
+ undo_redo->add_undo_method(this,"_update_graph",p_id);
+ undo_redo->commit_action();
+
+ Node *node = graph->get_node(itos(p_id));
+ if (node->cast_to<Control>())
+ node->cast_to<Control>()->set_size(Vector2(1,1)); //shrink if text is smaller
+
+ updating_graph=false;
+
+}
+
void VisualScriptEditor::_available_node_doubleclicked() {
TreeItem *item = nodes->get_selected();
@@ -2104,6 +2140,7 @@ Vector<String> VisualScriptEditor::get_functions(){
void VisualScriptEditor::set_edited_script(const Ref<Script>& p_script){
+
script=p_script;
signal_editor->script=p_script;
signal_editor->undo_redo=undo_redo;
@@ -3220,6 +3257,7 @@ void VisualScriptEditor::_bind_methods() {
ObjectTypeDB::bind_method("_button_resource_previewed",&VisualScriptEditor::_button_resource_previewed);
ObjectTypeDB::bind_method("_port_action_menu",&VisualScriptEditor::_port_action_menu);
ObjectTypeDB::bind_method("_selected_connect_node_method_or_setget",&VisualScriptEditor::_selected_connect_node_method_or_setget);
+ ObjectTypeDB::bind_method("_expression_text_changed",&VisualScriptEditor::_expression_text_changed);
diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h
index 5191ed540a..483ae1644c 100644
--- a/modules/visual_script/visual_script_editor.h
+++ b/modules/visual_script/visual_script_editor.h
@@ -168,6 +168,7 @@ class VisualScriptEditor : public ScriptEditorBase {
void _member_button(Object *p_item, int p_column, int p_button);
+ void _expression_text_changed(const String& p_text,int p_id);
String revert_on_drag;
diff --git a/modules/visual_script/visual_script_expression.cpp b/modules/visual_script/visual_script_expression.cpp
new file mode 100644
index 0000000000..cc3b5f2174
--- /dev/null
+++ b/modules/visual_script/visual_script_expression.cpp
@@ -0,0 +1,1523 @@
+#include "visual_script_expression.h"
+
+
+bool VisualScriptExpression::_set(const StringName& p_name, const Variant& p_value) {
+
+ if (String(p_name)=="expression") {
+ expression=p_value;
+ expression_dirty=true;
+ ports_changed_notify();
+ return true;
+ }
+
+ if (String(p_name)=="out_type") {
+ output_type=Variant::Type(int(p_value));
+ expression_dirty=true;
+ ports_changed_notify();
+ return true;
+ }
+ if (String(p_name)=="sequenced") {
+ sequenced=p_value;
+ ports_changed_notify();
+ return true;
+ }
+
+ if (String(p_name)=="input_count") {
+
+ int from=inputs.size();
+ inputs.resize(int(p_value));
+ for(int i=from;i<inputs.size();i++) {
+ inputs[i].name=String::chr('a'+i);
+ if (from==0) {
+ inputs[i].type=output_type;
+ } else {
+ inputs[i].type=inputs[from-1].type;
+ }
+ }
+ expression_dirty=true;
+ ports_changed_notify();
+ _change_notify();
+ return true;
+ }
+
+ if (String(p_name).begins_with("input/")) {
+
+ int idx=String(p_name).get_slice("/",1).to_int();
+ ERR_FAIL_INDEX_V(idx,inputs.size(),false);
+
+ String what=String(p_name).get_slice("/",2);
+
+ if (what=="type") {
+
+ inputs[idx].type=Variant::Type(int(p_value));
+ } else if (what=="name") {
+
+ inputs[idx].name=p_value;
+ } else {
+ return false;
+ }
+
+ expression_dirty=true;
+ ports_changed_notify();
+ return true;
+ }
+
+
+ return false;
+
+}
+
+bool VisualScriptExpression::_get(const StringName& p_name,Variant &r_ret) const {
+
+ if (String(p_name)=="expression") {
+ r_ret=expression;
+ return true;
+ }
+
+ if (String(p_name)=="out_type") {
+ r_ret=output_type;
+ return true;
+ }
+
+ if (String(p_name)=="sequenced") {
+ r_ret=sequenced;
+ return true;
+ }
+
+ if (String(p_name)=="input_count") {
+ r_ret=inputs.size();
+ return true;
+ }
+
+ if (String(p_name).begins_with("input/")) {
+
+ int idx=String(p_name).get_slice("/",1).to_int();
+ ERR_FAIL_INDEX_V(idx,inputs.size(),false);
+
+ String what=String(p_name).get_slice("/",2);
+
+ if (what=="type") {
+
+ r_ret=inputs[idx].type;
+ } else if (what=="name") {
+
+ r_ret=inputs[idx].name;
+ } else {
+ return false;
+ }
+
+ return true;
+ }
+
+
+ return false;
+}
+void VisualScriptExpression::_get_property_list( List<PropertyInfo> *p_list) const {
+
+
+ String argt="Any";
+ for(int i=1;i<Variant::VARIANT_MAX;i++) {
+ argt+=","+Variant::get_type_name(Variant::Type(i));
+ }
+
+ 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"));
+
+ for(int i=0;i<inputs.size();i++) {
+
+ 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"));
+ }
+}
+
+int VisualScriptExpression::get_output_sequence_port_count() const {
+
+ return sequenced?1:0;
+}
+bool VisualScriptExpression::has_input_sequence_port() const{
+
+ return sequenced;
+}
+
+
+String VisualScriptExpression::get_output_sequence_port_text(int p_port) const{
+
+ return String();
+}
+
+
+int VisualScriptExpression::get_input_value_port_count() const{
+
+ return inputs.size();
+
+}
+int VisualScriptExpression::get_output_value_port_count() const{
+
+ return 1;
+}
+
+
+PropertyInfo VisualScriptExpression::get_input_value_port_info(int p_idx) const{
+
+ return PropertyInfo(inputs[p_idx].type,inputs[p_idx].name);
+}
+PropertyInfo VisualScriptExpression::get_output_value_port_info(int p_idx) const{
+
+ return PropertyInfo(output_type,"result");
+}
+
+String VisualScriptExpression::get_caption() const{
+
+ return "Expression";
+}
+String VisualScriptExpression::get_text() const{
+
+ return expression;
+}
+
+
+Error VisualScriptExpression::_get_token(Token& r_token) {
+
+ while (true) {
+#define GET_CHAR() (str_ofs>=expression.length()?0:expression[str_ofs++])
+
+ CharType cchar = GET_CHAR();
+ if (cchar==0) {
+ r_token.type=TK_EOF;
+ return OK;
+ }
+
+
+ switch(cchar) {
+
+ case 0: {
+ r_token.type=TK_EOF;
+ return OK;
+ } break;
+ case '{': {
+
+ r_token.type=TK_CURLY_BRACKET_OPEN;
+ return OK;
+ };
+ case '}': {
+
+ r_token.type=TK_CURLY_BRACKET_CLOSE;
+ return OK;
+ };
+ case '[': {
+
+ r_token.type=TK_BRACKET_OPEN;
+ return OK;
+ };
+ case ']': {
+
+ r_token.type=TK_BRACKET_CLOSE;
+ return OK;
+ };
+ case '(': {
+
+ r_token.type=TK_PARENTHESIS_OPEN;
+ return OK;
+ };
+ case ')': {
+
+ r_token.type=TK_PARENTHESIS_CLOSE;
+ return OK;
+ };
+ case ',': {
+
+ r_token.type=TK_COMMA;
+ return OK;
+ };
+ case ':': {
+
+ r_token.type=TK_COLON;
+ return OK;
+ };
+ case '.': {
+
+ r_token.type=TK_PERIOD;
+ return OK;
+ };
+ case '=': {
+
+ cchar=GET_CHAR();
+ if (cchar=='=') {
+ r_token.type=TK_OP_EQUAL;
+ } else {
+ _set_error("Expected '='");
+ r_token.type=TK_ERROR;
+ return ERR_PARSE_ERROR;
+ }
+ return OK;
+ };
+ case '!': {
+
+ if (expression[str_ofs]=='=') {
+ r_token.type=TK_OP_NOT_EQUAL;
+ str_ofs++;
+ } else {
+ r_token.type=TK_OP_NOT;
+ }
+ return OK;
+ };
+ case '>': {
+
+ if (expression[str_ofs]=='=') {
+ r_token.type=TK_OP_GREATER_EQUAL;
+ str_ofs++;
+ } else if (expression[str_ofs]=='>') {
+ r_token.type=TK_OP_SHIFT_RIGHT;
+ str_ofs++;
+ } else {
+ r_token.type=TK_OP_GREATER;
+ }
+ return OK;
+ };
+ case '<': {
+
+ if (expression[str_ofs]=='=') {
+ r_token.type=TK_OP_LESS_EQUAL;
+ str_ofs++;
+ } else if (expression[str_ofs]=='<') {
+ r_token.type=TK_OP_SHIFT_LEFT;
+ str_ofs++;
+ } else {
+ r_token.type=TK_OP_LESS;
+ }
+ return OK;
+ };
+ case '+': {
+ r_token.type=TK_OP_ADD;
+ return OK;
+ };
+ case '-': {
+ r_token.type=TK_OP_SUB;
+ return OK;
+ };
+ case '/': {
+ r_token.type=TK_OP_DIV;
+ return OK;
+ };
+ case '*': {
+ r_token.type=TK_OP_MUL;
+ return OK;
+ };
+ case '%': {
+ r_token.type=TK_OP_MOD;
+ return OK;
+ };
+ case '&': {
+
+ if (expression[str_ofs]=='&') {
+ r_token.type=TK_OP_AND;
+ str_ofs++;
+ } else {
+ r_token.type=TK_OP_BIT_AND;
+ }
+ return OK;
+ };
+ case '|': {
+
+ if (expression[str_ofs]=='|') {
+ r_token.type=TK_OP_OR;
+ str_ofs++;
+ } else {
+ r_token.type=TK_OP_BIT_OR;
+ }
+ return OK;
+ };
+ case '^': {
+
+ r_token.type=TK_OP_BIT_XOR;
+
+ return OK;
+ };
+ case '~': {
+
+ r_token.type=TK_OP_BIT_INVERT;
+
+ return OK;
+ };
+ case '"': {
+
+
+ String str;
+ while(true) {
+
+ CharType ch=GET_CHAR();
+
+ if (ch==0) {
+ _set_error("Unterminated String");
+ r_token.type=TK_ERROR;
+ return ERR_PARSE_ERROR;
+ } else if (ch=='"') {
+ break;
+ } else if (ch=='\\') {
+ //escaped characters...
+
+ CharType next = GET_CHAR();
+ if (next==0) {
+ _set_error("Unterminated String");
+ r_token.type=TK_ERROR;
+ return ERR_PARSE_ERROR;
+ }
+ CharType res=0;
+
+ switch(next) {
+
+ 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;
+ case 'u': {
+ //hexnumbarh - oct is deprecated
+
+
+ for(int j=0;j<4;j++) {
+ CharType c = GET_CHAR();
+
+ if (c==0) {
+ _set_error("Unterminated String");
+ r_token.type=TK_ERROR;
+ return ERR_PARSE_ERROR;
+ }
+ if (!((c>='0' && c<='9') || (c>='a' && c<='f') || (c>='A' && c<='F'))) {
+
+ _set_error("Malformed hex constant in string");
+ r_token.type=TK_ERROR;
+ return ERR_PARSE_ERROR;
+ }
+ CharType v;
+ 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;
+ } else {
+ ERR_PRINT("BUG");
+ v=0;
+ }
+
+ res<<=4;
+ res|=v;
+
+
+ }
+
+
+
+ } break;
+ //case '\"': res='\"'; break;
+ //case '\\': res='\\'; break;
+ //case '/': res='/'; break;
+ default: {
+ res = next;
+ //r_err_str="Invalid escape sequence";
+ //return ERR_PARSE_ERROR;
+ } break;
+ }
+
+ str+=res;
+
+ } else {
+ str+=ch;
+ }
+ }
+
+ r_token.type=TK_CONSTANT;
+ r_token.value=str;
+ return OK;
+
+ } break;
+ default: {
+
+ if (cchar<=32) {
+ break;
+ }
+
+ if (cchar=='-' || (cchar>='0' && cchar<='9')) {
+ //a number
+
+
+ String num;
+#define READING_SIGN 0
+#define READING_INT 1
+#define READING_DEC 2
+#define READING_EXP 3
+#define READING_DONE 4
+ int reading=READING_INT;
+
+ if (cchar=='-') {
+ num+='-';
+ cchar=GET_CHAR();
+
+ }
+
+
+
+ CharType c = cchar;
+ bool exp_sign=false;
+ bool exp_beg=false;
+ bool is_float=false;
+
+ while(true) {
+
+ switch(reading) {
+ case READING_INT: {
+
+ if (c>='0' && c<='9') {
+ //pass
+ } else if (c=='.') {
+ reading=READING_DEC;
+ is_float=true;
+ } else if (c=='e') {
+ reading=READING_EXP;
+ } else {
+ reading=READING_DONE;
+ }
+
+ } break;
+ case READING_DEC: {
+
+ if (c>='0' && c<='9') {
+
+ } else if (c=='e') {
+ reading=READING_EXP;
+
+ } else {
+ reading=READING_DONE;
+ }
+
+ } break;
+ case READING_EXP: {
+
+ if (c>='0' && c<='9') {
+ exp_beg=true;
+
+ } else if ((c=='-' || c=='+') && !exp_sign && !exp_beg) {
+ if (c=='-')
+ is_float=true;
+ exp_sign=true;
+
+ } else {
+ reading=READING_DONE;
+ }
+ } break;
+ }
+
+ if (reading==READING_DONE)
+ break;
+ num+=String::chr(c);
+ c = GET_CHAR();
+
+
+ }
+
+ str_ofs--;
+
+ r_token.type=TK_CONSTANT;
+
+ if (is_float)
+ r_token.value=num.to_double();
+ else
+ r_token.value=num.to_int();
+ return OK;
+
+ } else if ((cchar>='A' && cchar<='Z') || (cchar>='a' && cchar<='z') || cchar=='_') {
+
+ String id;
+ bool first=true;
+
+ while((cchar>='A' && cchar<='Z') || (cchar>='a' && cchar<='z') || cchar=='_' || (!first && cchar>='0' && cchar<='9')) {
+
+ id+=String::chr(cchar);
+ cchar=GET_CHAR();
+ first=false;
+ }
+
+ str_ofs--; //go back one
+
+ 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;
+ } 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;
+ } else {
+
+ 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;
+ return OK;
+ break;
+ }
+ }
+
+ VisualScriptBuiltinFunc::BuiltinFunc bifunc = VisualScriptBuiltinFunc::find_function(id);
+ if (bifunc!=VisualScriptBuiltinFunc::FUNC_MAX) {
+ r_token.type=TK_BUILTIN_FUNC;
+ r_token.value=bifunc;
+ return OK;
+ }
+
+ r_token.type=TK_IDENTIFIER;
+ r_token.value=id;
+ }
+
+ return OK;
+ } else {
+ _set_error("Unexpected character.");
+ r_token.type=TK_ERROR;
+ return ERR_PARSE_ERROR;
+ }
+ }
+ }
+ }
+
+ r_token.type=TK_ERROR;
+ return ERR_PARSE_ERROR;
+}
+
+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"
+};
+
+VisualScriptExpression::ENode* VisualScriptExpression::_parse_expression() {
+
+
+ Vector<Expression> expression;
+
+ while(true) {
+ //keep appending stuff to expression
+ ENode*expr=NULL;
+
+ Token tk;
+ _get_token(tk);
+ if (error_set)
+ return NULL;
+
+
+
+ switch(tk.type) {
+ case TK_CURLY_BRACKET_OPEN: {
+ //a dictionary
+ DictionaryNode *dn = alloc_node<DictionaryNode>();
+
+
+ while(true) {
+
+ int cofs=str_ofs;
+ _get_token(tk);
+ if (tk.type==TK_CURLY_BRACKET_CLOSE) {
+ break;
+ }
+ str_ofs=cofs; //revert
+ //parse an expression
+ ENode* expr=_parse_expression();
+ if (!expr)
+ return NULL;
+ dn->dict.push_back(expr);
+
+ _get_token(tk);
+ if (tk.type!=TK_COLON) {
+ _set_error("Expected ':'");
+ return NULL;
+ }
+
+ expr=_parse_expression();
+ if (!expr)
+ return NULL;
+
+ dn->dict.push_back(expr);
+
+ cofs=str_ofs;
+ _get_token(tk);
+ if (tk.type==TK_COMMA) {
+ //all good
+ } else if (tk.type==TK_CURLY_BRACKET_CLOSE) {
+ str_ofs=cofs;
+ } else {
+ _set_error("Expected ',' or '}'");
+ }
+ }
+
+ expr=dn;
+ } break;
+ case TK_BRACKET_OPEN: {
+ //an array
+
+ ArrayNode *an = alloc_node<ArrayNode>();
+
+
+ while(true) {
+
+ int cofs=str_ofs;
+ _get_token(tk);
+ if (tk.type==TK_BRACKET_CLOSE) {
+ break;
+ }
+ str_ofs=cofs; //revert
+ //parse an expression
+ ENode* expr=_parse_expression();
+ if (!expr)
+ return NULL;
+ an->array.push_back(expr);
+
+ cofs=str_ofs;
+ _get_token(tk);
+ if (tk.type==TK_COMMA) {
+ //all good
+ } else if (tk.type==TK_BRACKET_CLOSE) {
+ str_ofs=cofs;
+ } else {
+ _set_error("Expected ',' or ']'");
+ }
+ }
+
+ expr=an;
+ } break;
+ case TK_PARENTHESIS_OPEN: {
+ //a suexpression
+ ENode* e=_parse_expression();
+ if (error_set)
+ return NULL;
+ _get_token(tk);
+ if (tk.type!=TK_PARENTHESIS_CLOSE) {
+ _set_error("Expected ')'");
+ return NULL;
+ }
+
+ expr=e;
+
+ } break;
+ case TK_IDENTIFIER: {
+
+ String what = tk.value;
+ int index=-1;
+ for(int i=0;i<inputs.size();i++) {
+ if (what==inputs[i].name) {
+ index=i;
+ break;
+ }
+ }
+
+ if (index!=-1) {
+ InputNode *input = alloc_node<InputNode>();
+ input->index=index;
+ expr=input;
+ } else {
+ _set_error("Invalid input identifier '"+what+"'. For script variables, use self (locals are for inputs)."+what);
+ return NULL;
+ }
+ } break;
+ case TK_SELF: {
+
+ SelfNode *self = alloc_node<SelfNode>();
+ expr=self;
+ } break;
+ case TK_CONSTANT: {
+ ConstantNode *constant = alloc_node<ConstantNode>();
+ constant->value=tk.value;
+ expr=constant;
+ } break;
+ case TK_BASIC_TYPE: {
+ //constructor..
+
+ Variant::Type bt = Variant::Type(int(tk.value));
+ _get_token(tk);
+ if (tk.type!=TK_PARENTHESIS_OPEN) {
+ _set_error("Expected '('");
+ return NULL;
+ }
+
+ ConstructorNode *constructor = alloc_node<ConstructorNode>();
+ constructor->data_type=bt;
+
+ while(true) {
+
+ int cofs=str_ofs;
+ _get_token(tk);
+ if (tk.type==TK_PARENTHESIS_CLOSE) {
+ break;
+ }
+ str_ofs=cofs; //revert
+ //parse an expression
+ ENode* expr=_parse_expression();
+ if (!expr)
+ return NULL;
+
+ constructor->arguments.push_back(expr);
+
+ cofs=str_ofs;
+ _get_token(tk);
+ if (tk.type==TK_COMMA) {
+ //all good
+ } else if (tk.type==TK_PARENTHESIS_CLOSE) {
+ str_ofs=cofs;
+ } else {
+ _set_error("Expected ',' or ')'");
+ }
+ }
+
+ expr=constructor;
+
+ } break;
+ case TK_BUILTIN_FUNC: {
+ //builtin function
+
+ Variant::Type bt = Variant::Type(int(tk.value));
+ _get_token(tk);
+ if (tk.type!=TK_PARENTHESIS_OPEN) {
+ _set_error("Expected '('");
+ return NULL;
+ }
+
+ BuiltinFuncNode *bifunc = alloc_node<BuiltinFuncNode>();
+ bifunc->func=VisualScriptBuiltinFunc::BuiltinFunc(int(tk.value));
+
+ while(true) {
+
+ int cofs=str_ofs;
+ _get_token(tk);
+ if (tk.type==TK_PARENTHESIS_CLOSE) {
+ break;
+ }
+ str_ofs=cofs; //revert
+ //parse an expression
+ ENode* expr=_parse_expression();
+ if (!expr)
+ return NULL;
+
+ bifunc->arguments.push_back(expr);
+
+ cofs=str_ofs;
+ _get_token(tk);
+ if (tk.type==TK_COMMA) {
+ //all good
+ } else if (tk.type==TK_PARENTHESIS_CLOSE) {
+ str_ofs=cofs;
+ } else {
+ _set_error("Expected ',' or ')'");
+ }
+ }
+
+ int expected_args = VisualScriptBuiltinFunc::get_func_argument_count(bifunc->func);
+ if (bifunc->arguments.size() != expected_args) {
+ _set_error("Builtin func '"+VisualScriptBuiltinFunc::get_func_name(bifunc->func)+"' expects "+itos(expected_args)+" arguments.");
+ }
+
+ expr=bifunc;
+
+ } break;
+ case TK_OP_SUB: {
+
+ Expression e;
+ e.is_op=true;
+ e.op=Variant::OP_NEGATE;
+ expression.push_back(e);
+ continue;
+ } break;
+ case TK_OP_NOT: {
+
+ Expression e;
+ e.is_op=true;
+ e.op=Variant::OP_NOT;
+ expression.push_back(e);
+ continue;
+ } break;
+
+ default: {
+ _set_error("Expected expression.");
+ return NULL;
+ } break;
+
+ }
+
+ //before going to operators, must check indexing!
+
+ while(true) {
+ int cofs2=str_ofs;
+ _get_token(tk);
+ if (error_set)
+ return NULL;
+
+ bool done=false;
+
+ switch(tk.type) {
+ case TK_BRACKET_OPEN: {
+ //value indexing
+
+ IndexNode *index = alloc_node<IndexNode>();
+ index->base=expr;
+
+ ENode* what=_parse_expression();
+ if (!what)
+ return NULL;
+
+ index->index=what;
+
+ _get_token(tk);
+ if (tk.type!=TK_BRACKET_CLOSE) {
+ _set_error("Expected ']' at end of index.");
+ return NULL;
+ }
+ expr=index;
+
+ } break;
+ case TK_PERIOD: {
+ //named indexing or function call
+ _get_token(tk);
+ if (tk.type!=TK_IDENTIFIER) {
+ _set_error("Expected identifier after '.'");
+ return NULL;
+ }
+
+ StringName identifier=tk.value;
+
+ int cofs=str_ofs;
+ _get_token(tk);
+ if (tk.type==TK_PARENTHESIS_OPEN) {
+ //function call
+ CallNode *func_call = alloc_node<CallNode>();
+ func_call->method=identifier;
+ func_call->base=expr;
+
+ while(true) {
+
+ int cofs=str_ofs;
+ _get_token(tk);
+ if (tk.type==TK_PARENTHESIS_CLOSE) {
+ break;
+ }
+ str_ofs=cofs; //revert
+ //parse an expression
+ ENode* expr=_parse_expression();
+ if (!expr)
+ return NULL;
+
+ func_call->arguments.push_back(expr);
+
+ cofs=str_ofs;
+ _get_token(tk);
+ if (tk.type==TK_COMMA) {
+ //all good
+ } else if (tk.type==TK_PARENTHESIS_CLOSE) {
+ str_ofs=cofs;
+ } else {
+ _set_error("Expected ',' or ')'");
+ }
+ }
+
+ expr=func_call;
+ } else {
+ //named indexing
+ str_ofs=cofs;
+
+ NamedIndexNode *index = alloc_node<NamedIndexNode>();
+ index->base=expr;
+ index->name=identifier;
+ expr=index;
+
+ }
+
+ } break;
+ default: {
+ str_ofs=cofs2;
+ done=true;
+ } break;
+ }
+
+ if (done)
+ break;
+ }
+
+ //push expression
+ {
+ Expression e;
+ e.is_op=false;
+ e.node=expr;
+ expression.push_back(e);
+ }
+
+ //ok finally look for an operator
+
+
+ int cofs=str_ofs;
+ _get_token(tk);
+ if (error_set)
+ return NULL;
+
+
+ Variant::Operator op = Variant::OP_MAX;
+
+ 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;
+ default: {};
+ }
+
+ if (op==Variant::OP_MAX) { //stop appending stuff
+ str_ofs=cofs;
+ break;
+ }
+
+ //push operator and go on
+ {
+ Expression e;
+ e.is_op=true;
+ e.op=op;
+ expression.push_back(e);
+ }
+ }
+
+
+ /* Reduce the set set of expressions and place them in an operator tree, respecting precedence */
+
+
+ while(expression.size()>1) {
+
+ int next_op=-1;
+ int min_priority=0xFFFFF;
+ bool is_unary=false;
+
+ for(int i=0;i<expression.size();i++) {
+
+
+
+ if (!expression[i].is_op) {
+
+ continue;
+ }
+
+ int priority;
+
+ bool unary=false;
+
+ switch(expression[i].op) {
+
+
+ case Variant::OP_BIT_NEGATE: priority=0; unary=true; break;
+ case Variant::OP_NEGATE: priority=1; unary=true; break;
+
+ case Variant::OP_MULTIPLY: priority=2; break;
+ case Variant::OP_DIVIDE: priority=2; break;
+ case Variant::OP_MODULE: priority=2; break;
+
+ case Variant::OP_ADD: priority=3; break;
+ case Variant::OP_SUBSTRACT: priority=3; break;
+
+ case Variant::OP_SHIFT_LEFT: priority=4; break;
+ case Variant::OP_SHIFT_RIGHT: priority=4; break;
+
+ case Variant::OP_BIT_AND: priority=5; break;
+ case Variant::OP_BIT_XOR: priority=6; break;
+ case Variant::OP_BIT_OR: priority=7; break;
+
+ 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;
+
+ case Variant::OP_EQUAL: priority=8; break;
+ case Variant::OP_NOT_EQUAL: priority=8; break;
+
+ case Variant::OP_IN: priority=10; break;
+
+ case Variant::OP_NOT: priority=11; unary=true; break;
+ case Variant::OP_AND: priority=12; break;
+ case Variant::OP_OR: priority=13; break;
+
+
+ default: {
+ _set_error("Parser bug, invalid operator in expression: "+itos(expression[i].op));
+ return NULL;
+ }
+
+ }
+
+ if (priority<min_priority) {
+ // < is used for left to right (default)
+ // <= is used for right to left
+
+ next_op=i;
+ min_priority=priority;
+ is_unary=unary;
+ }
+
+ }
+
+ if (next_op==-1) {
+
+
+ _set_error("Yet another parser bug....");
+ ERR_FAIL_COND_V(next_op==-1,NULL);
+ }
+
+
+ // OK! create operator..
+ if (is_unary) {
+
+ int expr_pos=next_op;
+ while(expression[expr_pos].is_op) {
+
+ expr_pos++;
+ if (expr_pos==expression.size()) {
+ //can happen..
+ _set_error("Unexpected end of expression..");
+ return NULL;
+ }
+ }
+
+ //consecutively do unary opeators
+ for(int i=expr_pos-1;i>=next_op;i--) {
+
+ OperatorNode *op = alloc_node<OperatorNode>();
+ 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);
+ }
+
+
+ } else {
+
+ if (next_op <1 || next_op>=(expression.size()-1)) {
+ _set_error("Parser bug..");
+ ERR_FAIL_V(NULL);
+ }
+
+ OperatorNode *op = alloc_node<OperatorNode>();
+ op->op=expression[next_op].op;
+
+ if (expression[next_op-1].is_op) {
+
+ _set_error("Parser bug..");
+ ERR_FAIL_V(NULL);
+ }
+
+ if (expression[next_op+1].is_op) {
+ // 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,
+ // due to how precedence works, unaries will always dissapear first
+
+ _set_error("Unexpected two consecutive operators.");
+ return NULL;
+ }
+
+
+ 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
+
+ //replace all 3 nodes by this operator and make it an expression
+ expression[next_op-1].node=op;
+ 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);
+ nodes=NULL;
+ root=NULL;
+
+ }
+
+ error_str=String();
+ error_set=false;
+ str_ofs=0;
+
+ root=_parse_expression();
+
+ if (error_set) {
+ root=NULL;
+ if (nodes) {
+ memdelete(nodes);
+ }
+ nodes=NULL;
+ return true;
+ }
+
+ expression_dirty=false;
+ return false;
+}
+
+
+class VisualScriptNodeInstanceExpression : public VisualScriptNodeInstance {
+public:
+
+ VisualScriptInstance* instance;
+ VisualScriptExpression *expression;
+
+ //virtual int get_working_memory_size() const { return 0; }
+ //execute by parsing the tree directly
+ virtual bool _execute(const Variant** p_inputs,VisualScriptExpression::ENode *p_node,Variant& r_ret,String& r_error_str,Variant::CallError &ce) {
+
+ switch(p_node->type) {
+ case VisualScriptExpression::ENode::TYPE_INPUT: {
+
+ const VisualScriptExpression::InputNode *in = static_cast<const VisualScriptExpression::InputNode*>(p_node);
+ r_ret=*p_inputs[in->index];
+ } break;
+ case VisualScriptExpression::ENode::TYPE_CONSTANT: {
+
+ const VisualScriptExpression::ConstantNode *c = static_cast<const VisualScriptExpression::ConstantNode*>(p_node);
+ r_ret=c->value;
+
+ } break;
+ case VisualScriptExpression::ENode::TYPE_SELF: {
+
+ r_ret=instance->get_owner_ptr();
+ } break;
+ case VisualScriptExpression::ENode::TYPE_OPERATOR: {
+
+
+ const VisualScriptExpression::OperatorNode *op = static_cast<const VisualScriptExpression::OperatorNode*>(p_node);
+
+ Variant a;
+ bool ret = _execute(p_inputs,op->nodes[0],a,r_error_str,ce);
+ if (ret)
+ return true;
+
+ Variant b;
+
+ if (op->nodes[1]) {
+ ret = _execute(p_inputs,op->nodes[1],b,r_error_str,ce);
+ if (ret)
+ return true;
+ }
+
+ bool valid=true;
+ Variant::evaluate(op->op,a,b,r_ret,valid);
+ if (!valid) {
+ 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())+".";
+ return true;
+ }
+
+ } break;
+ case VisualScriptExpression::ENode::TYPE_INDEX: {
+
+ const VisualScriptExpression::IndexNode *index = static_cast<const VisualScriptExpression::IndexNode*>(p_node);
+
+ Variant base;
+ bool ret = _execute(p_inputs,index->base,base,r_error_str,ce);
+ if (ret)
+ return true;
+
+ Variant idx;
+
+ ret = _execute(p_inputs,index->index,idx,r_error_str,ce);
+ if (ret)
+ return true;
+
+ bool valid;
+ r_ret=base.get(idx,&valid);
+ if (!valid) {
+ 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())+".";
+ return true;
+ }
+
+
+
+ } break;
+ case VisualScriptExpression::ENode::TYPE_NAMED_INDEX: {
+
+ const VisualScriptExpression::NamedIndexNode *index = static_cast<const VisualScriptExpression::NamedIndexNode*>(p_node);
+
+ Variant base;
+ bool ret = _execute(p_inputs,index->base,base,r_error_str,ce);
+ if (ret)
+ return true;
+
+ bool valid;
+ r_ret=base.get_named(index->name,&valid);
+ if (!valid) {
+ r_error_str="Invalid index '"+String(index->name)+"' for base of type "+Variant::get_type_name(base.get_type())+".";
+ return true;
+ }
+
+ } break;
+ case VisualScriptExpression::ENode::TYPE_ARRAY: {
+ const VisualScriptExpression::ArrayNode *array = static_cast<const VisualScriptExpression::ArrayNode*>(p_node);
+
+ Array arr;
+ arr.resize(array->array.size());
+ for (int i=0;i<array->array.size();i++) {
+
+ Variant value;
+ bool ret = _execute(p_inputs,array->array[i],value,r_error_str,ce);
+ if (ret)
+ return true;
+ arr[i]=value;
+ }
+
+ r_ret=arr;
+
+ } break;
+ case VisualScriptExpression::ENode::TYPE_DICTIONARY: {
+ const VisualScriptExpression::DictionaryNode *dictionary = static_cast<const VisualScriptExpression::DictionaryNode*>(p_node);
+
+ Dictionary d;
+ for (int i=0;i<dictionary->dict.size();i+=2) {
+
+ Variant key;
+ bool ret = _execute(p_inputs,dictionary->dict[i+0],key,r_error_str,ce);
+ if (ret)
+ return true;
+
+ Variant value;
+ ret = _execute(p_inputs,dictionary->dict[i+1],value,r_error_str,ce);
+ if (ret)
+ return true;
+
+ d[key]=value;
+ }
+
+ r_ret=d;
+ } break;
+ case VisualScriptExpression::ENode::TYPE_CONSTRUCTOR: {
+
+ const VisualScriptExpression::ConstructorNode *constructor = static_cast<const VisualScriptExpression::ConstructorNode*>(p_node);
+
+ Vector<Variant> arr;
+ Vector<const Variant*> argp;
+ arr.resize(constructor->arguments.size());
+ argp.resize(constructor->arguments.size());
+
+ for (int i=0;i<constructor->arguments.size();i++) {
+
+ Variant value;
+ bool ret = _execute(p_inputs,constructor->arguments[i],value,r_error_str,ce);
+ if (ret)
+ return true;
+ arr[i]=value;
+ argp[i]=&arr[i];
+ }
+
+
+ r_ret=Variant::construct(constructor->data_type,argp.ptr(),argp.size(),ce);
+
+ if (ce.error!=Variant::CallError::CALL_OK) {
+ r_error_str="Invalid arguments to construct '"+Variant::get_type_name(constructor->data_type)+"'.";
+ return true;
+ }
+
+
+ } break;
+ case VisualScriptExpression::ENode::TYPE_BUILTIN_FUNC: {
+
+ const VisualScriptExpression::BuiltinFuncNode *bifunc = static_cast<const VisualScriptExpression::BuiltinFuncNode*>(p_node);
+
+ Vector<Variant> arr;
+ Vector<const Variant*> argp;
+ arr.resize(bifunc->arguments.size());
+ argp.resize(bifunc->arguments.size());
+
+ for (int i=0;i<bifunc->arguments.size();i++) {
+
+ Variant value;
+ bool ret = _execute(p_inputs,bifunc->arguments[i],value,r_error_str,ce);
+ if (ret)
+ return true;
+ arr[i]=value;
+ argp[i]=&arr[i];
+ }
+
+
+ VisualScriptBuiltinFunc::exec_func(bifunc->func,argp.ptr(),&r_ret,ce,r_error_str);
+
+ if (ce.error!=Variant::CallError::CALL_OK) {
+ r_error_str="Builtin Call Failed. "+r_error_str;
+ return true;
+ }
+
+ } break;
+ case VisualScriptExpression::ENode::TYPE_CALL: {
+
+ const VisualScriptExpression::CallNode *call = static_cast<const VisualScriptExpression::CallNode*>(p_node);
+
+
+ Variant base;
+ bool ret = _execute(p_inputs,call->base,base,r_error_str,ce);
+ if (ret)
+ return true;
+
+ Vector<Variant> arr;
+ Vector<const Variant*> argp;
+ arr.resize(call->arguments.size());
+ argp.resize(call->arguments.size());
+
+ for (int i=0;i<call->arguments.size();i++) {
+
+ Variant value;
+ bool ret = _execute(p_inputs,call->arguments[i],value,r_error_str,ce);
+ if (ret)
+ return true;
+ arr[i]=value;
+ argp[i]=&arr[i];
+ }
+
+
+ r_ret=base.call(call->method,argp.ptr(),argp.size(),ce);
+
+ if (ce.error!=Variant::CallError::CALL_OK) {
+ r_error_str="On call to '"+String(call->method)+"':";
+ return true;
+ }
+
+ } break;
+ }
+ return false;
+ }
+
+ 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) {
+
+ if (!expression->root || expression->error_set) {
+ r_error_str=expression->error_str;
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ return 0;
+ }
+
+
+ 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;
+ }
+
+#ifdef DEBUG_ENABLED
+ if (!error && expression->output_type!=Variant::NIL && !Variant::can_convert_strict(p_outputs[0]->get_type(),expression->output_type)) {
+
+ 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;
+
+ }
+#endif
+
+ return 0;
+ }
+
+
+};
+
+VisualScriptNodeInstance* VisualScriptExpression::instance(VisualScriptInstance* p_instance){
+
+ _compile_expression();
+ VisualScriptNodeInstanceExpression *instance = memnew( VisualScriptNodeInstanceExpression );
+ instance->instance=p_instance;
+ instance->expression=this;
+ return instance;
+}
+
+
+VisualScriptExpression::VisualScriptExpression()
+{
+ output_type=Variant::NIL;
+ expression_dirty=true;
+ error_set=true;
+ root=NULL;
+ nodes=NULL;
+ sequenced=false;
+}
+
+VisualScriptExpression::~VisualScriptExpression() {
+
+ if (nodes) {
+ memdelete(nodes);
+ }
+}
+
+
+void register_visual_script_expression_node() {
+
+ VisualScriptLanguage::singleton->add_register_func("operators/expression",create_node_generic<VisualScriptExpression>);
+
+}
diff --git a/modules/visual_script/visual_script_expression.h b/modules/visual_script/visual_script_expression.h
new file mode 100644
index 0000000000..4edae133c7
--- /dev/null
+++ b/modules/visual_script/visual_script_expression.h
@@ -0,0 +1,280 @@
+#ifndef VISUALSCRIPTEXPRESSION_H
+#define VISUALSCRIPTEXPRESSION_H
+
+#include "visual_script.h"
+#include "visual_script_builtin_funcs.h"
+
+class VisualScriptExpression : public VisualScriptNode {
+
+ OBJ_TYPE(VisualScriptExpression,VisualScriptNode)
+friend class VisualScriptNodeInstanceExpression;
+
+ struct Input {
+
+ Variant::Type type;
+ String name;
+
+ Input() { type=Variant::NIL; }
+ };
+
+ Vector<Input> inputs;
+ Variant::Type output_type;
+
+ String expression;
+
+ bool sequenced;
+ int str_ofs;
+ bool expression_dirty;
+
+ bool _compile_expression();
+
+ enum TokenType {
+ TK_CURLY_BRACKET_OPEN,
+ TK_CURLY_BRACKET_CLOSE,
+ TK_BRACKET_OPEN,
+ TK_BRACKET_CLOSE,
+ TK_PARENTHESIS_OPEN,
+ TK_PARENTHESIS_CLOSE,
+ TK_IDENTIFIER,
+ TK_BUILTIN_FUNC,
+ TK_SELF,
+ TK_CONSTANT,
+ TK_BASIC_TYPE,
+ TK_COLON,
+ TK_COMMA,
+ TK_PERIOD,
+ TK_OP_IN,
+ TK_OP_EQUAL,
+ TK_OP_NOT_EQUAL,
+ TK_OP_LESS,
+ TK_OP_LESS_EQUAL,
+ TK_OP_GREATER,
+ TK_OP_GREATER_EQUAL,
+ TK_OP_AND,
+ TK_OP_OR,
+ TK_OP_NOT,
+ TK_OP_ADD,
+ TK_OP_SUB,
+ TK_OP_MUL,
+ TK_OP_DIV,
+ TK_OP_MOD,
+ TK_OP_SHIFT_LEFT,
+ TK_OP_SHIFT_RIGHT,
+ TK_OP_BIT_AND,
+ TK_OP_BIT_OR,
+ TK_OP_BIT_XOR,
+ TK_OP_BIT_INVERT,
+ TK_EOF,
+ TK_ERROR,
+ TK_MAX
+ };
+
+ static const char* token_name[TK_MAX];
+ struct Token {
+
+ TokenType type;
+ Variant value;
+ };
+
+
+ void _set_error(const String& p_err) {
+ if (error_set)
+ return;
+ error_str=p_err;
+ error_set=true;
+ }
+
+ Error _get_token(Token& r_token);
+
+ String error_str;
+ bool error_set;
+
+
+
+ struct ENode {
+
+ enum Type {
+ TYPE_INPUT,
+ TYPE_CONSTANT,
+ TYPE_SELF,
+ TYPE_OPERATOR,
+ TYPE_INDEX,
+ TYPE_NAMED_INDEX,
+ TYPE_ARRAY,
+ TYPE_DICTIONARY,
+ TYPE_CONSTRUCTOR,
+ TYPE_BUILTIN_FUNC,
+ TYPE_CALL
+ };
+
+ ENode *next;
+
+ Type type;
+
+ ENode() { next=NULL; }
+ virtual ~ENode() { if (next) { memdelete(next); } }
+ };
+
+ struct Expression {
+
+ bool is_op;
+ union {
+ Variant::Operator op;
+ ENode *node;
+ };
+ };
+
+ ENode* _parse_expression();
+
+ struct InputNode : public ENode {
+
+ int index;
+ InputNode() {
+ type=TYPE_INPUT;
+ }
+ };
+
+
+ struct ConstantNode : public ENode {
+
+ Variant value;
+ ConstantNode() {
+ type=TYPE_CONSTANT;
+ }
+ };
+
+ struct OperatorNode : public ENode {
+
+ Variant::Operator op;
+
+ ENode* nodes[2];
+
+ OperatorNode() {
+ type=TYPE_OPERATOR;
+ }
+ };
+
+ struct SelfNode : public ENode {
+
+
+ SelfNode() {
+ type=TYPE_SELF;
+ }
+ };
+
+ struct IndexNode : public ENode {
+ ENode*base;
+ ENode*index;
+
+ IndexNode() {
+ type=TYPE_INDEX;
+ }
+ };
+
+ struct NamedIndexNode : public ENode {
+ ENode*base;
+ StringName name;
+
+ NamedIndexNode() {
+ type=TYPE_NAMED_INDEX;
+ }
+
+ };
+
+ struct ConstructorNode : public ENode {
+ Variant::Type data_type;
+ Vector<ENode*> arguments;
+
+ ConstructorNode() {
+ type=TYPE_CONSTRUCTOR;
+ }
+ };
+
+ struct CallNode : public ENode {
+ ENode*base;
+ StringName method;
+ Vector<ENode*> arguments;
+
+ CallNode() {
+ type=TYPE_CALL;
+ }
+
+ };
+
+ struct ArrayNode : public ENode {
+ Vector<ENode*> array;
+ ArrayNode() {
+ type=TYPE_ARRAY;
+ }
+
+ };
+
+ struct DictionaryNode : public ENode {
+ Vector<ENode*> dict;
+ DictionaryNode() {
+ type=TYPE_DICTIONARY;
+ }
+
+ };
+
+ struct BuiltinFuncNode : public ENode {
+ VisualScriptBuiltinFunc::BuiltinFunc func;
+ Vector<ENode*> arguments;
+ BuiltinFuncNode() {
+ type=TYPE_BUILTIN_FUNC;
+ }
+ };
+
+ template<class T>
+ T* alloc_node() {
+ T* node = memnew(T);
+ node->next=nodes;
+ nodes=node;
+ return node;
+ }
+
+ ENode *root;
+ ENode *nodes;
+
+
+
+
+
+protected:
+
+ bool _set(const StringName& p_name, const Variant& p_value);
+ bool _get(const StringName& p_name,Variant &r_ret) const;
+ void _get_property_list( List<PropertyInfo> *p_list) const;
+
+public:
+
+
+ virtual int get_output_sequence_port_count() const;
+ virtual bool has_input_sequence_port() const;
+
+
+ virtual String get_output_sequence_port_text(int p_port) const;
+
+
+ virtual int get_input_value_port_count() const;
+ virtual int get_output_value_port_count() const;
+
+
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const;
+ virtual PropertyInfo get_output_value_port_info(int p_idx) const;
+
+ virtual String get_caption() const;
+ virtual String get_text() const;
+ virtual String get_category() const { return "operators"; }
+
+ virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance);
+
+ VisualScriptExpression();
+ ~VisualScriptExpression();
+};
+
+
+void register_visual_script_expression_node();
+
+
+#endif // VISUALSCRIPTEXPRESSION_H
diff --git a/modules/visual_script/visual_script_func_nodes.cpp b/modules/visual_script/visual_script_func_nodes.cpp
index de99beacaf..5a21cb40e9 100644
--- a/modules/visual_script/visual_script_func_nodes.cpp
+++ b/modules/visual_script/visual_script_func_nodes.cpp
@@ -417,6 +417,14 @@ void VisualScriptFunctionCall::_update_method_cache() {
method_cache.return_val = mb->get_argument_info(-1);
#endif
+
+ if (mb->is_vararg()) {
+ //for vararg just give it 10 arguments (should be enough for most use cases)
+ for(int i=0;i<10;i++) {
+ method_cache.arguments.push_back(PropertyInfo(Variant::NIL,"arg"+itos(i)));
+ use_default_args++;
+ }
+ }
} else if (script.is_valid() && script->has_method(function)) {
method_cache = script->get_method_info(function);
diff --git a/modules/visual_script/visual_script_nodes.cpp b/modules/visual_script/visual_script_nodes.cpp
index 03fd70b0a3..7ada292b13 100644
--- a/modules/visual_script/visual_script_nodes.cpp
+++ b/modules/visual_script/visual_script_nodes.cpp
@@ -553,7 +553,7 @@ void VisualScriptOperator::_bind_methods() {
argt+=","+Variant::get_type_name(Variant::Type(i));
}
- ADD_PROPERTY(PropertyInfo(Variant::INT,"operator_value/type",PROPERTY_HINT_ENUM,types,PROPERTY_USAGE_NOEDITOR),_SCS("set_operator"),_SCS("get_operator"));
+ ADD_PROPERTY(PropertyInfo(Variant::INT,"operator_value/type",PROPERTY_HINT_ENUM,types),_SCS("set_operator"),_SCS("get_operator"));
ADD_PROPERTY(PropertyInfo(Variant::INT,"typed_value/typed",PROPERTY_HINT_ENUM,argt),_SCS("set_typed"),_SCS("get_typed"));
}