GDScript: Allow `is` operator to test built-in types

This commit is contained in:
George Marques 2018-08-26 13:31:23 -03:00
parent 653b4829f1
commit 4b974a36b7
No known key found for this signature in database
GPG Key ID: 046BD46A3201E43D
5 changed files with 75 additions and 3 deletions

View File

@ -1253,6 +1253,25 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
codegen.opcodes.push_back(src_address_b); // argument 2 (unary only takes one parameter)
} break;
case GDScriptParser::OperatorNode::OP_IS_BUILTIN: {
ERR_FAIL_COND_V(on->arguments.size() != 2, false);
ERR_FAIL_COND_V(on->arguments[1]->type != GDScriptParser::Node::TYPE_TYPE, false);
int slevel = p_stack_level;
int src_address_a = _parse_expression(codegen, on->arguments[0], slevel);
if (src_address_a < 0)
return -1;
if (src_address_a & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS)
slevel++; //uses stack for return, increase stack
const GDScriptParser::TypeNode *tn = static_cast<const GDScriptParser::TypeNode *>(on->arguments[1]);
codegen.opcodes.push_back(GDScriptFunction::OPCODE_IS_BUILTIN); // perform operator
codegen.opcodes.push_back(src_address_a); // argument 1
codegen.opcodes.push_back((int)tn->vtype); // argument 2 (unary only takes one parameter)
} break;
default: {
ERR_EXPLAIN("Bug in bytecode compiler, unexpected operator #" + itos(on->op) + " in parse tree while parsing expression.");

View File

@ -191,6 +191,7 @@ static String _get_var_type(const Variant *p_type) {
static const void *switch_table_ops[] = { \
&&OPCODE_OPERATOR, \
&&OPCODE_EXTENDS_TEST, \
&&OPCODE_IS_BUILTIN, \
&&OPCODE_SET, \
&&OPCODE_GET, \
&&OPCODE_SET_NAMED, \
@ -536,6 +537,21 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
}
DISPATCH_OPCODE;
OPCODE(OPCODE_IS_BUILTIN) {
CHECK_SPACE(4);
GET_VARIANT_PTR(value, 1);
Variant::Type var_type = (Variant::Type)_code_ptr[ip + 2];
GET_VARIANT_PTR(dst, 3);
GD_ERR_BREAK(var_type < 0 || var_type >= Variant::VARIANT_MAX);
*dst = value->get_type() == var_type;
ip += 4;
}
DISPATCH_OPCODE;
OPCODE(OPCODE_SET) {
CHECK_SPACE(3);

View File

@ -136,6 +136,7 @@ public:
enum Opcode {
OPCODE_OPERATOR,
OPCODE_EXTENDS_TEST,
OPCODE_IS_BUILTIN,
OPCODE_SET,
OPCODE_GET,
OPCODE_SET_NAMED,

View File

@ -861,6 +861,20 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
op->arguments.push_back(subexpr);
expr=op;*/
} else if (tokenizer->get_token() == GDScriptTokenizer::TK_PR_IS && tokenizer->get_token(1) == GDScriptTokenizer::TK_BUILT_IN_TYPE) {
// 'is' operator with built-in type
OperatorNode *op = alloc_node<OperatorNode>();
op->op = OperatorNode::OP_IS_BUILTIN;
op->arguments.push_back(expr);
tokenizer->advance();
TypeNode *tn = alloc_node<TypeNode>();
tn->vtype = tokenizer->get_token_type();
op->arguments.push_back(tn);
tokenizer->advance();
expr = op;
} else if (tokenizer->get_token() == GDScriptTokenizer::TK_BRACKET_OPEN) {
// array
tokenizer->advance();
@ -1071,6 +1085,15 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
expr = op;
} else if (tokenizer->get_token() == GDScriptTokenizer::TK_BUILT_IN_TYPE && expression.size() > 0 && expression[expression.size() - 1].is_op && expression[expression.size() - 1].op == OperatorNode::OP_IS) {
Expression e = expression[expression.size() - 1];
e.op = OperatorNode::OP_IS_BUILTIN;
expression.write[expression.size() - 1] = e;
TypeNode *tn = alloc_node<TypeNode>();
tn->vtype = tokenizer->get_token_type();
expr = tn;
tokenizer->advance();
} else {
//find list [ or find dictionary {
@ -1329,6 +1352,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
switch (expression[i].op) {
case OperatorNode::OP_IS:
case OperatorNode::OP_IS_BUILTIN:
priority = -1;
break; //before anything
@ -5793,6 +5817,13 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) {
case Node::TYPE_CONSTANT: {
node_type = _type_from_variant(static_cast<ConstantNode *>(p_node)->value);
} break;
case Node::TYPE_TYPE: {
TypeNode *tn = static_cast<TypeNode *>(p_node);
node_type.has_type = true;
node_type.is_meta_type = true;
node_type.kind = DataType::BUILTIN;
node_type.builtin_type = tn->vtype;
} break;
case Node::TYPE_ARRAY: {
node_type.has_type = true;
node_type.kind = DataType::BUILTIN;
@ -5896,7 +5927,8 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) {
// yield can return anything
node_type.has_type = false;
} break;
case OperatorNode::OP_IS: {
case OperatorNode::OP_IS:
case OperatorNode::OP_IS_BUILTIN: {
if (op->arguments.size() != 2) {
_set_error("Parser bug: binary operation without 2 arguments.", op->line);
@ -5913,8 +5945,11 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) {
}
type_type.is_meta_type = false; // Test the actual type
if (!_is_type_compatible(type_type, value_type) && !_is_type_compatible(value_type, type_type)) {
// TODO: Make this a warning?
_set_error("A value of type '" + value_type.to_string() + "' will never be an instance of '" + type_type.to_string() + "'.", op->line);
if (op->op == OperatorNode::OP_IS) {
_set_error("A value of type '" + value_type.to_string() + "' will never be an instance of '" + type_type.to_string() + "'.", op->line);
} else {
_set_error("A value of type '" + value_type.to_string() + "' will never be of type '" + type_type.to_string() + "'.", op->line);
}
return DataType();
}
}

View File

@ -345,6 +345,7 @@ public:
OP_PARENT_CALL,
OP_YIELD,
OP_IS,
OP_IS_BUILTIN,
//indexing operator
OP_INDEX,
OP_INDEX_NAMED,