-Add Expression class, used to evaluate expressions

-Added expression evaluation to EditorSpinSlider, fixes #20813, fixes #18932
This commit is contained in:
Juan Linietsky 2018-08-08 17:34:24 -03:00
parent 77e38f9ddf
commit 934c641a15
5 changed files with 2469 additions and 4 deletions

2120
core/math/expression.cpp Normal file

File diff suppressed because it is too large Load diff

323
core/math/expression.h Normal file
View file

@ -0,0 +1,323 @@
#ifndef EXPRESSION_H
#define EXPRESSION_H
#include "core/reference.h"
class Expression : public Reference {
GDCLASS(Expression, Reference)
public:
enum BuiltinFunc {
MATH_SIN,
MATH_COS,
MATH_TAN,
MATH_SINH,
MATH_COSH,
MATH_TANH,
MATH_ASIN,
MATH_ACOS,
MATH_ATAN,
MATH_ATAN2,
MATH_SQRT,
MATH_FMOD,
MATH_FPOSMOD,
MATH_FLOOR,
MATH_CEIL,
MATH_ROUND,
MATH_ABS,
MATH_SIGN,
MATH_POW,
MATH_LOG,
MATH_EXP,
MATH_ISNAN,
MATH_ISINF,
MATH_EASE,
MATH_DECIMALS,
MATH_STEPIFY,
MATH_LERP,
MATH_INVERSE_LERP,
MATH_RANGE_LERP,
MATH_DECTIME,
MATH_RANDOMIZE,
MATH_RAND,
MATH_RANDF,
MATH_RANDOM,
MATH_SEED,
MATH_RANDSEED,
MATH_DEG2RAD,
MATH_RAD2DEG,
MATH_LINEAR2DB,
MATH_DB2LINEAR,
MATH_POLAR2CARTESIAN,
MATH_CARTESIAN2POLAR,
MATH_WRAP,
MATH_WRAPF,
LOGIC_MAX,
LOGIC_MIN,
LOGIC_CLAMP,
LOGIC_NEAREST_PO2,
OBJ_WEAKREF,
FUNC_FUNCREF,
TYPE_CONVERT,
TYPE_OF,
TYPE_EXISTS,
TEXT_CHAR,
TEXT_STR,
TEXT_PRINT,
TEXT_PRINTERR,
TEXT_PRINTRAW,
VAR_TO_STR,
STR_TO_VAR,
VAR_TO_BYTES,
BYTES_TO_VAR,
COLORN,
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];
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_INPUT,
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 ExpressionNode {
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 {
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;
bool execution_error;
bool _execute(const Array &p_inputs, Object *p_instance, Expression::ENode *p_node, Variant &r_ret, String &r_error_str);
protected:
static void _bind_methods();
public:
Error parse(const String &p_expression);
Variant execute(Array p_inputs, Object *p_base = NULL, bool p_show_error = true);
bool has_execute_failed() const;
String get_error_text() const;
Expression();
~Expression();
};
#endif // EXPRESSION_H

View file

@ -54,6 +54,7 @@
#include "io/tcp_server.h"
#include "io/translation_loader_po.h"
#include "math/a_star.h"
#include "math/expression.h"
#include "math/triangle_mesh.h"
#include "os/input.h"
#include "os/main_loop.h"
@ -216,6 +217,7 @@ void register_core_singletons() {
ClassDB::register_virtual_class<Input>();
ClassDB::register_class<InputMap>();
ClassDB::register_class<_JSON>();
ClassDB::register_class<Expression>();
Engine::get_singleton()->add_singleton(Engine::Singleton("ProjectSettings", ProjectSettings::get_singleton()));
Engine::get_singleton()->add_singleton(Engine::Singleton("IP", IP::get_singleton()));

View file

@ -30,6 +30,7 @@
#include "editor_spin_slider.h"
#include "editor_scale.h"
#include "math/expression.h"
#include "os/input.h"
String EditorSpinSlider::get_tooltip(const Point2 &p_pos) const {
@ -275,10 +276,12 @@ void EditorSpinSlider::_notification(int p_what) {
if (p_what == NOTIFICATION_FOCUS_ENTER) {
/* Sorry, I dont like this, it makes navigating the different fields with arrows more difficult.
* Just press enter to edit.
* if (!Input::get_singleton()->is_mouse_button_pressed(BUTTON_LEFT) && !value_input_just_closed) {
* if (Input::get_singleton()->is_mouse_button_pressed(BUTTON_LEFT) && !value_input_just_closed) {
_focus_entered();
}*/
if ((Input::get_singleton()->is_action_pressed("ui_focus_next") || Input::get_singleton()->is_action_pressed("ui_focus_prev")) && !value_input_just_closed) {
_focus_entered();
}
value_input_just_closed = false;
}
}
@ -312,6 +315,21 @@ String EditorSpinSlider::get_label() const {
return label;
}
void EditorSpinSlider::_evaluate_input_text() {
String text = value_input->get_text();
Ref<Expression> expr;
expr.instance();
Error err = expr->parse(text);
if (err != OK) {
return;
}
Variant v = expr->execute(Array(), NULL, false);
if (v.get_type() == Variant::NIL)
return;
set_value(v);
}
//text_entered signal
void EditorSpinSlider::_value_input_entered(const String &p_text) {
value_input_just_closed = true;
@ -320,13 +338,13 @@ void EditorSpinSlider::_value_input_entered(const String &p_text) {
//modal_closed signal
void EditorSpinSlider::_value_input_closed() {
set_value(value_input->get_text().to_double());
_evaluate_input_text();
value_input_just_closed = true;
}
//focus_exited signal
void EditorSpinSlider::_value_focus_exited() {
set_value(value_input->get_text().to_double());
_evaluate_input_text();
// focus is not on the same element after the vlalue_input was exited
// -> focus is on next element
// -> TAB was pressed

View file

@ -73,6 +73,8 @@ class EditorSpinSlider : public Range {
bool use_custom_label_color;
Color custom_label_color;
void _evaluate_input_text();
protected:
void _notification(int p_what);
void _gui_input(const Ref<InputEvent> &p_event);