Implement exponential operator (**) to GDScript/Expressions

This commit is contained in:
Yuri Roubinsky 2022-03-07 20:25:21 +03:00 committed by Yuri Rubinsky
parent 9963ae3553
commit dbd7a31507
15 changed files with 126 additions and 27 deletions

View file

@ -703,6 +703,7 @@ void register_global_constants() {
BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_NEGATE", Variant::OP_NEGATE);
BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_POSITIVE", Variant::OP_POSITIVE);
BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_MODULE", Variant::OP_MODULE);
BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_POWER", Variant::OP_POWER);
//bitwise
BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_SHIFT_LEFT", Variant::OP_SHIFT_LEFT);
BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_SHIFT_RIGHT", Variant::OP_SHIFT_RIGHT);

View file

@ -114,6 +114,7 @@ typedef enum {
GDNATIVE_VARIANT_OP_NEGATE,
GDNATIVE_VARIANT_OP_POSITIVE,
GDNATIVE_VARIANT_OP_MODULE,
GDNATIVE_VARIANT_OP_POWER,
/* bitwise */
GDNATIVE_VARIANT_OP_SHIFT_LEFT,
GDNATIVE_VARIANT_OP_SHIFT_RIGHT,

View file

@ -155,7 +155,12 @@ Error Expression::_get_token(Token &r_token) {
return OK;
}
case '*': {
r_token.type = TK_OP_MUL;
if (expression[str_ofs] == '*') {
r_token.type = TK_OP_POW;
str_ofs++;
} else {
r_token.type = TK_OP_MUL;
}
return OK;
}
case '%': {
@ -542,6 +547,7 @@ const char *Expression::token_name[TK_MAX] = {
"OP MUL",
"OP DIV",
"OP MOD",
"OP POW",
"OP SHIFT LEFT",
"OP SHIFT RIGHT",
"OP BIT AND",
@ -1013,6 +1019,9 @@ Expression::ENode *Expression::_parse_expression() {
case TK_OP_MOD:
op = Variant::OP_MODULE;
break;
case TK_OP_POW:
op = Variant::OP_POWER;
break;
case TK_OP_SHIFT_LEFT:
op = Variant::OP_SHIFT_LEFT;
break;
@ -1066,56 +1075,59 @@ Expression::ENode *Expression::_parse_expression() {
bool unary = false;
switch (expression[i].op) {
case Variant::OP_BIT_NEGATE:
case Variant::OP_POWER:
priority = 0;
break;
case Variant::OP_BIT_NEGATE:
priority = 1;
unary = true;
break;
case Variant::OP_NEGATE:
priority = 1;
priority = 2;
unary = true;
break;
case Variant::OP_MULTIPLY:
case Variant::OP_DIVIDE:
case Variant::OP_MODULE:
priority = 2;
priority = 3;
break;
case Variant::OP_ADD:
case Variant::OP_SUBTRACT:
priority = 3;
priority = 4;
break;
case Variant::OP_SHIFT_LEFT:
case Variant::OP_SHIFT_RIGHT:
priority = 4;
break;
case Variant::OP_BIT_AND:
priority = 5;
break;
case Variant::OP_BIT_XOR:
case Variant::OP_BIT_AND:
priority = 6;
break;
case Variant::OP_BIT_OR:
case Variant::OP_BIT_XOR:
priority = 7;
break;
case Variant::OP_BIT_OR:
priority = 8;
break;
case Variant::OP_LESS:
case Variant::OP_LESS_EQUAL:
case Variant::OP_GREATER:
case Variant::OP_GREATER_EQUAL:
case Variant::OP_EQUAL:
case Variant::OP_NOT_EQUAL:
priority = 8;
priority = 9;
break;
case Variant::OP_IN:
priority = 10;
priority = 11;
break;
case Variant::OP_NOT:
priority = 11;
priority = 12;
unary = true;
break;
case Variant::OP_AND:
priority = 12;
priority = 13;
break;
case Variant::OP_OR:
priority = 13;
priority = 14;
break;
default: {
_set_error("Parser bug, invalid operator in expression: " + itos(expression[i].op));

View file

@ -85,6 +85,7 @@ private:
TK_OP_MUL,
TK_OP_DIV,
TK_OP_MOD,
TK_OP_POW,
TK_OP_SHIFT_LEFT,
TK_OP_SHIFT_RIGHT,
TK_OP_BIT_AND,

View file

@ -473,6 +473,7 @@ public:
OP_NEGATE,
OP_POSITIVE,
OP_MODULE,
OP_POWER,
//bitwise
OP_SHIFT_LEFT,
OP_SHIFT_RIGHT,

View file

@ -361,6 +361,11 @@ void Variant::_register_variant_operators() {
register_op<OperatorEvaluatorStringModT<PackedVector3Array>>(Variant::OP_MODULE, Variant::STRING, Variant::PACKED_VECTOR3_ARRAY);
register_op<OperatorEvaluatorStringModT<PackedColorArray>>(Variant::OP_MODULE, Variant::STRING, Variant::PACKED_COLOR_ARRAY);
register_op<OperatorEvaluatorPow<int64_t, int64_t, int64_t>>(Variant::OP_POWER, Variant::INT, Variant::INT);
register_op<OperatorEvaluatorPow<double, int64_t, double>>(Variant::OP_POWER, Variant::INT, Variant::FLOAT);
register_op<OperatorEvaluatorPow<double, double, double>>(Variant::OP_POWER, Variant::FLOAT, Variant::FLOAT);
register_op<OperatorEvaluatorPow<double, double, int64_t>>(Variant::OP_POWER, Variant::FLOAT, Variant::INT);
register_op<OperatorEvaluatorNeg<int64_t, int64_t>>(Variant::OP_NEGATE, Variant::INT, Variant::NIL);
register_op<OperatorEvaluatorNeg<double, double>>(Variant::OP_NEGATE, Variant::FLOAT, Variant::NIL);
register_op<OperatorEvaluatorNeg<Vector2, Vector2>>(Variant::OP_NEGATE, Variant::VECTOR2, Variant::NIL);
@ -929,6 +934,7 @@ static const char *_op_names[Variant::OP_MAX] = {
"unary-",
"unary+",
"%",
"**",
"<<",
">>",
"&",

View file

@ -91,6 +91,24 @@ public:
static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; }
};
template <class R, class A, class B>
class OperatorEvaluatorPow {
public:
static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
const B &b = *VariantGetInternalPtr<B>::get_ptr(&p_right);
*r_ret = R(Math::pow((double)a, (double)b));
r_valid = true;
}
static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
*VariantGetInternalPtr<R>::get_ptr(r_ret) = R(Math::pow((double)*VariantGetInternalPtr<A>::get_ptr(left), (double)*VariantGetInternalPtr<B>::get_ptr(right)));
}
static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
PtrToArg<R>::encode(R(Math::pow((double)PtrToArg<A>::convert(left), (double)PtrToArg<B>::convert(right))), r_ret);
}
static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; }
};
template <class R, class A, class B>
class OperatorEvaluatorXForm {
public:

View file

@ -2799,40 +2799,43 @@
<constant name="OP_MODULE" value="12" enum="Variant.Operator">
Remainder/modulo operator ([code]%[/code]).
</constant>
<constant name="OP_SHIFT_LEFT" value="13" enum="Variant.Operator">
<constant name="OP_POWER" value="13" enum="Variant.Operator">
Power operator ([code]**[/code]).
</constant>
<constant name="OP_SHIFT_LEFT" value="14" enum="Variant.Operator">
Left shift operator ([code]&lt;&lt;[/code]).
</constant>
<constant name="OP_SHIFT_RIGHT" value="14" enum="Variant.Operator">
<constant name="OP_SHIFT_RIGHT" value="15" enum="Variant.Operator">
Right shift operator ([code]&gt;&gt;[/code]).
</constant>
<constant name="OP_BIT_AND" value="15" enum="Variant.Operator">
<constant name="OP_BIT_AND" value="16" enum="Variant.Operator">
Bitwise AND operator ([code]&amp;[/code]).
</constant>
<constant name="OP_BIT_OR" value="16" enum="Variant.Operator">
<constant name="OP_BIT_OR" value="17" enum="Variant.Operator">
Bitwise OR operator ([code]|[/code]).
</constant>
<constant name="OP_BIT_XOR" value="17" enum="Variant.Operator">
<constant name="OP_BIT_XOR" value="18" enum="Variant.Operator">
Bitwise XOR operator ([code]^[/code]).
</constant>
<constant name="OP_BIT_NEGATE" value="18" enum="Variant.Operator">
<constant name="OP_BIT_NEGATE" value="19" enum="Variant.Operator">
Bitwise NOT operator ([code]~[/code]).
</constant>
<constant name="OP_AND" value="19" enum="Variant.Operator">
<constant name="OP_AND" value="20" enum="Variant.Operator">
Logical AND operator ([code]and[/code] or [code]&amp;&amp;[/code]).
</constant>
<constant name="OP_OR" value="20" enum="Variant.Operator">
<constant name="OP_OR" value="21" enum="Variant.Operator">
Logical OR operator ([code]or[/code] or [code]||[/code]).
</constant>
<constant name="OP_XOR" value="21" enum="Variant.Operator">
<constant name="OP_XOR" value="22" enum="Variant.Operator">
Logical XOR operator (not implemented in GDScript).
</constant>
<constant name="OP_NOT" value="22" enum="Variant.Operator">
<constant name="OP_NOT" value="23" enum="Variant.Operator">
Logical NOT operator ([code]not[/code] or [code]![/code]).
</constant>
<constant name="OP_IN" value="23" enum="Variant.Operator">
<constant name="OP_IN" value="24" enum="Variant.Operator">
Logical IN operator ([code]in[/code]).
</constant>
<constant name="OP_MAX" value="24" enum="Variant.Operator">
<constant name="OP_MAX" value="25" enum="Variant.Operator">
Represents the size of the [enum Variant.Operator] enum.
</constant>
</constants>

View file

@ -124,6 +124,18 @@
Multiplies a [float] and an [int]. The result is a [float].
</description>
</operator>
<operator name="operator **">
<return type="float" />
<argument index="0" name="right" type="float" />
<description>
</description>
</operator>
<operator name="operator **">
<return type="float" />
<argument index="0" name="right" type="int" />
<description>
</description>
</operator>
<operator name="operator +">
<return type="float" />
<argument index="0" name="right" type="float" />

View file

@ -171,6 +171,18 @@
Multiplies two [int]s.
</description>
</operator>
<operator name="operator **">
<return type="float" />
<argument index="0" name="right" type="float" />
<description>
</description>
</operator>
<operator name="operator **">
<return type="int" />
<argument index="0" name="right" type="int" />
<description>
</description>
</operator>
<operator name="operator +">
<return type="String" />
<argument index="0" name="right" type="String" />

View file

@ -1409,6 +1409,8 @@ def sanitize_operator_name(dirty_name, state): # type: (str, State) -> str
clear_name = "div"
elif clear_name == "%":
clear_name = "mod"
elif clear_name == "**":
clear_name = "pow"
elif clear_name == "unary+":
clear_name = "unplus"

View file

@ -2320,6 +2320,10 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_binary_operator(Expression
operation->operation = BinaryOpNode::OP_MODULO;
operation->variant_op = Variant::OP_MODULE;
break;
case GDScriptTokenizer::Token::STAR_STAR:
operation->operation = BinaryOpNode::OP_POWER;
operation->variant_op = Variant::OP_POWER;
break;
case GDScriptTokenizer::Token::LESS_LESS:
operation->operation = BinaryOpNode::OP_BIT_LEFT_SHIFT;
operation->variant_op = Variant::OP_SHIFT_LEFT;
@ -2483,6 +2487,10 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_assignment(ExpressionNode
assignment->operation = AssignmentNode::OP_MULTIPLICATION;
assignment->variant_op = Variant::OP_MULTIPLY;
break;
case GDScriptTokenizer::Token::STAR_STAR_EQUAL:
assignment->operation = AssignmentNode::OP_POWER;
assignment->variant_op = Variant::OP_POWER;
break;
case GDScriptTokenizer::Token::SLASH_EQUAL:
assignment->operation = AssignmentNode::OP_DIVISION;
assignment->variant_op = Variant::OP_DIVIDE;
@ -3265,6 +3273,7 @@ GDScriptParser::ParseRule *GDScriptParser::get_rule(GDScriptTokenizer::Token::Ty
{ &GDScriptParser::parse_unary_operator, &GDScriptParser::parse_binary_operator, PREC_ADDITION_SUBTRACTION }, // PLUS,
{ &GDScriptParser::parse_unary_operator, &GDScriptParser::parse_binary_operator, PREC_ADDITION_SUBTRACTION }, // MINUS,
{ nullptr, &GDScriptParser::parse_binary_operator, PREC_FACTOR }, // STAR,
{ nullptr, &GDScriptParser::parse_binary_operator, PREC_FACTOR }, // STAR_STAR,
{ nullptr, &GDScriptParser::parse_binary_operator, PREC_FACTOR }, // SLASH,
{ nullptr, &GDScriptParser::parse_binary_operator, PREC_FACTOR }, // PERCENT,
// Assignment
@ -3272,6 +3281,7 @@ GDScriptParser::ParseRule *GDScriptParser::get_rule(GDScriptTokenizer::Token::Ty
{ nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // PLUS_EQUAL,
{ nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // MINUS_EQUAL,
{ nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // STAR_EQUAL,
{ nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // STAR_STAR_EQUAL,
{ nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // SLASH_EQUAL,
{ nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // PERCENT_EQUAL,
{ nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // LESS_LESS_EQUAL,
@ -3896,6 +3906,9 @@ void GDScriptParser::TreePrinter::print_assignment(AssignmentNode *p_assignment)
case AssignmentNode::OP_MODULO:
push_text("%");
break;
case AssignmentNode::OP_POWER:
push_text("**");
break;
case AssignmentNode::OP_BIT_SHIFT_LEFT:
push_text("<<");
break;
@ -3944,6 +3957,9 @@ void GDScriptParser::TreePrinter::print_binary_op(BinaryOpNode *p_binary_op) {
case BinaryOpNode::OP_MODULO:
push_text(" % ");
break;
case BinaryOpNode::OP_POWER:
push_text(" ** ");
break;
case BinaryOpNode::OP_BIT_LEFT_SHIFT:
push_text(" << ");
break;

View file

@ -360,6 +360,7 @@ public:
OP_MULTIPLICATION,
OP_DIVISION,
OP_MODULO,
OP_POWER,
OP_BIT_SHIFT_LEFT,
OP_BIT_SHIFT_RIGHT,
OP_BIT_AND,
@ -393,6 +394,7 @@ public:
OP_MULTIPLICATION,
OP_DIVISION,
OP_MODULO,
OP_POWER,
OP_BIT_LEFT_SHIFT,
OP_BIT_RIGHT_SHIFT,
OP_BIT_AND,

View file

@ -67,6 +67,7 @@ static const char *token_names[] = {
"+", // PLUS,
"-", // MINUS,
"*", // STAR,
"**", // STAR_STAR,
"/", // SLASH,
"%", // PERCENT,
// Assignment
@ -74,6 +75,7 @@ static const char *token_names[] = {
"+=", // PLUS_EQUAL,
"-=", // MINUS_EQUAL,
"*=", // STAR_EQUAL,
"**=", // STAR_STAR_EQUAL,
"/=", // SLASH_EQUAL,
"%=", // PERCENT_EQUAL,
"<<=", // LESS_LESS_EQUAL,
@ -1403,6 +1405,14 @@ GDScriptTokenizer::Token GDScriptTokenizer::scan() {
if (_peek() == '=') {
_advance();
return make_token(Token::STAR_EQUAL);
} else if (_peek() == '*') {
if (_peek(1) == '=') {
_advance();
_advance(); // Advance both '*' and '='
return make_token(Token::STAR_STAR_EQUAL);
}
_advance();
return make_token(Token::STAR_STAR);
} else {
return make_token(Token::STAR);
}

View file

@ -78,6 +78,7 @@ public:
PLUS,
MINUS,
STAR,
STAR_STAR,
SLASH,
PERCENT,
// Assignment
@ -85,6 +86,7 @@ public:
PLUS_EQUAL,
MINUS_EQUAL,
STAR_EQUAL,
STAR_STAR_EQUAL,
SLASH_EQUAL,
PERCENT_EQUAL,
LESS_LESS_EQUAL,