summaryrefslogtreecommitdiff
path: root/modules/gdscript/gdscript_compiler.cpp
diff options
context:
space:
mode:
authorGeorge Marques <george@gmarqu.es>2020-05-01 19:14:56 -0300
committerGeorge Marques <george@gmarqu.es>2020-07-20 11:38:39 -0300
commit5d6e8538065050d5f5579ec03cfa9e241811e062 (patch)
treec36cdc8d6b4353243dab6afb457db87c84c053e9 /modules/gdscript/gdscript_compiler.cpp
parent818bfbc5b53cc7df4f33493d3ca0a9b74e2cb34a (diff)
New GDScript tokenizer and parser
Sometimes to fix something you have to break it first. This get GDScript mostly working with the new tokenizer and parser but a lot of things isn't working yet. It compiles and it's usable, and that should be enough for now. Don't worry: other huge commits will come after this.
Diffstat (limited to 'modules/gdscript/gdscript_compiler.cpp')
-rw-r--r--modules/gdscript/gdscript_compiler.cpp2472
1 files changed, 1495 insertions, 977 deletions
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index 5bc9003c29..def2bed714 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -33,7 +33,7 @@
#include "gdscript.h"
bool GDScriptCompiler::_is_class_member_property(CodeGen &codegen, const StringName &p_name) {
- if (codegen.function_node && codegen.function_node->_static) {
+ if (codegen.function_node && codegen.function_node->is_static) {
return false;
}
@@ -66,18 +66,16 @@ void GDScriptCompiler::_set_error(const String &p_error, const GDScriptParser::N
error = p_error;
if (p_node) {
- err_line = p_node->line;
- err_column = p_node->column;
+ err_line = p_node->start_line;
+ err_column = p_node->leftmost_column;
} else {
err_line = 0;
err_column = 0;
}
}
-bool GDScriptCompiler::_create_unary_operator(CodeGen &codegen, const GDScriptParser::OperatorNode *on, Variant::Operator op, int p_stack_level) {
- ERR_FAIL_COND_V(on->arguments.size() != 1, false);
-
- int src_address_a = _parse_expression(codegen, on->arguments[0], p_stack_level);
+bool GDScriptCompiler::_create_unary_operator(CodeGen &codegen, const GDScriptParser::UnaryOpNode *on, Variant::Operator op, int p_stack_level) {
+ int src_address_a = _parse_expression(codegen, on->operand, p_stack_level);
if (src_address_a < 0) {
return false;
}
@@ -90,10 +88,8 @@ bool GDScriptCompiler::_create_unary_operator(CodeGen &codegen, const GDScriptPa
return true;
}
-bool GDScriptCompiler::_create_binary_operator(CodeGen &codegen, const GDScriptParser::OperatorNode *on, Variant::Operator op, int p_stack_level, bool p_initializer, int p_index_addr) {
- ERR_FAIL_COND_V(on->arguments.size() != 2, false);
-
- int src_address_a = _parse_expression(codegen, on->arguments[0], p_stack_level, false, p_initializer, p_index_addr);
+bool GDScriptCompiler::_create_binary_operator(CodeGen &codegen, const GDScriptParser::ExpressionNode *p_left_operand, const GDScriptParser::ExpressionNode *p_right_operand, Variant::Operator op, int p_stack_level, bool p_initializer, int p_index_addr) {
+ int src_address_a = _parse_expression(codegen, p_left_operand, p_stack_level, false, p_initializer, p_index_addr);
if (src_address_a < 0) {
return false;
}
@@ -101,7 +97,7 @@ bool GDScriptCompiler::_create_binary_operator(CodeGen &codegen, const GDScriptP
p_stack_level++; //uses stack for return, increase stack
}
- int src_address_b = _parse_expression(codegen, on->arguments[1], p_stack_level, false, p_initializer);
+ int src_address_b = _parse_expression(codegen, p_right_operand, p_stack_level, false, p_initializer);
if (src_address_b < 0) {
return false;
}
@@ -113,8 +109,12 @@ bool GDScriptCompiler::_create_binary_operator(CodeGen &codegen, const GDScriptP
return true;
}
+bool GDScriptCompiler::_create_binary_operator(CodeGen &codegen, const GDScriptParser::BinaryOpNode *on, Variant::Operator op, int p_stack_level, bool p_initializer, int p_index_addr) {
+ return _create_binary_operator(codegen, on->left_operand, on->right_operand, op, p_stack_level, p_initializer, p_index_addr);
+}
+
GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::DataType &p_datatype) const {
- if (!p_datatype.has_type) {
+ if (!p_datatype.is_set()) {
return GDScriptDataType();
}
@@ -135,34 +135,35 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
result.script_type = p_datatype.script_type;
result.native_type = result.script_type->get_instance_base_type();
} break;
- case GDScriptParser::DataType::GDSCRIPT: {
- result.kind = GDScriptDataType::GDSCRIPT;
- result.script_type = p_datatype.script_type;
- result.native_type = result.script_type->get_instance_base_type();
- } break;
case GDScriptParser::DataType::CLASS: {
// Locate class by constructing the path to it and following that path
- GDScriptParser::ClassNode *class_type = p_datatype.class_type;
- List<StringName> names;
- while (class_type->owner) {
- names.push_back(class_type->name);
- class_type = class_type->owner;
- }
+ GDScriptParser::ClassNode *class_type = p_datatype.gdscript_type;
+ if (class_type) {
+ List<StringName> names;
+ while (class_type->outer) {
+ names.push_back(class_type->identifier->name);
+ class_type = class_type->outer;
+ }
- Ref<GDScript> script = Ref<GDScript>(main_script);
- while (names.back()) {
- if (!script->subclasses.has(names.back()->get())) {
- ERR_PRINT("Parser bug: Cannot locate datatype class.");
- result.has_type = false;
- return GDScriptDataType();
+ Ref<GDScript> script = Ref<GDScript>(main_script);
+ while (names.back()) {
+ if (!script->subclasses.has(names.back()->get())) {
+ ERR_PRINT("Parser bug: Cannot locate datatype class.");
+ result.has_type = false;
+ return GDScriptDataType();
+ }
+ script = script->subclasses[names.back()->get()];
+ names.pop_back();
}
- script = script->subclasses[names.back()->get()];
- names.pop_back();
+ result.kind = GDScriptDataType::GDSCRIPT;
+ result.script_type = script;
+ result.native_type = script->get_instance_base_type();
+ } else {
+ result.kind = GDScriptDataType::GDSCRIPT;
+ result.script_type = p_datatype.script_type;
+ result.native_type = result.script_type->get_instance_base_type();
}
- result.kind = GDScriptDataType::GDSCRIPT;
- result.script_type = script;
- result.native_type = script->get_instance_base_type();
} break;
default: {
ERR_PRINT("Parser bug: converting unresolved type.");
@@ -173,42 +174,41 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
return result;
}
-int GDScriptCompiler::_parse_assign_right_expression(CodeGen &codegen, const GDScriptParser::OperatorNode *p_expression, int p_stack_level, int p_index_addr) {
+int GDScriptCompiler::_parse_assign_right_expression(CodeGen &codegen, const GDScriptParser::AssignmentNode *p_assignment, int p_stack_level, int p_index_addr) {
Variant::Operator var_op = Variant::OP_MAX;
- switch (p_expression->op) {
- case GDScriptParser::OperatorNode::OP_ASSIGN_ADD:
+ switch (p_assignment->operation) {
+ case GDScriptParser::AssignmentNode::OP_ADDITION:
var_op = Variant::OP_ADD;
break;
- case GDScriptParser::OperatorNode::OP_ASSIGN_SUB:
+ case GDScriptParser::AssignmentNode::OP_SUBTRACTION:
var_op = Variant::OP_SUBTRACT;
break;
- case GDScriptParser::OperatorNode::OP_ASSIGN_MUL:
+ case GDScriptParser::AssignmentNode::OP_MULTIPLICATION:
var_op = Variant::OP_MULTIPLY;
break;
- case GDScriptParser::OperatorNode::OP_ASSIGN_DIV:
+ case GDScriptParser::AssignmentNode::OP_DIVISION:
var_op = Variant::OP_DIVIDE;
break;
- case GDScriptParser::OperatorNode::OP_ASSIGN_MOD:
+ case GDScriptParser::AssignmentNode::OP_MODULO:
var_op = Variant::OP_MODULE;
break;
- case GDScriptParser::OperatorNode::OP_ASSIGN_SHIFT_LEFT:
+ case GDScriptParser::AssignmentNode::OP_BIT_SHIFT_LEFT:
var_op = Variant::OP_SHIFT_LEFT;
break;
- case GDScriptParser::OperatorNode::OP_ASSIGN_SHIFT_RIGHT:
+ case GDScriptParser::AssignmentNode::OP_BIT_SHIFT_RIGHT:
var_op = Variant::OP_SHIFT_RIGHT;
break;
- case GDScriptParser::OperatorNode::OP_ASSIGN_BIT_AND:
+ case GDScriptParser::AssignmentNode::OP_BIT_AND:
var_op = Variant::OP_BIT_AND;
break;
- case GDScriptParser::OperatorNode::OP_ASSIGN_BIT_OR:
+ case GDScriptParser::AssignmentNode::OP_BIT_OR:
var_op = Variant::OP_BIT_OR;
break;
- case GDScriptParser::OperatorNode::OP_ASSIGN_BIT_XOR:
+ case GDScriptParser::AssignmentNode::OP_BIT_XOR:
var_op = Variant::OP_BIT_XOR;
break;
- case GDScriptParser::OperatorNode::OP_INIT_ASSIGN:
- case GDScriptParser::OperatorNode::OP_ASSIGN: {
+ case GDScriptParser::AssignmentNode::OP_NONE: {
//none
} break;
default: {
@@ -216,13 +216,13 @@ int GDScriptCompiler::_parse_assign_right_expression(CodeGen &codegen, const GDS
}
}
- bool initializer = p_expression->op == GDScriptParser::OperatorNode::OP_INIT_ASSIGN;
+ // bool initializer = p_expression->op == GDScriptParser::OperatorNode::OP_INIT_ASSIGN;
if (var_op == Variant::OP_MAX) {
- return _parse_expression(codegen, p_expression->arguments[1], p_stack_level, false, initializer);
+ return _parse_expression(codegen, p_assignment->assigned_value, p_stack_level, false, false);
}
- if (!_create_binary_operator(codegen, p_expression, var_op, p_stack_level, initializer, p_index_addr)) {
+ if (!_create_binary_operator(codegen, p_assignment->assignee, p_assignment->assigned_value, var_op, p_stack_level, false, p_index_addr)) {
return -1;
}
@@ -232,10 +232,10 @@ int GDScriptCompiler::_parse_assign_right_expression(CodeGen &codegen, const GDS
return dst_addr;
}
-int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::Node *p_expression, int p_stack_level, bool p_root, bool p_initializer, int p_index_addr) {
+int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::ExpressionNode *p_expression, int p_stack_level, bool p_root, bool p_initializer, int p_index_addr) {
switch (p_expression->type) {
//should parse variable declaration and adjust stack accordingly...
- case GDScriptParser::Node::TYPE_IDENTIFIER: {
+ case GDScriptParser::Node::IDENTIFIER: {
//return identifier
//wait, identifier could be a local variable or something else... careful here, must reference properly
//as stack may be more interesting to work with
@@ -252,6 +252,11 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
return pos | (GDScriptFunction::ADDR_TYPE_STACK_VARIABLE << GDScriptFunction::ADDR_BITS);
}
+ // TRY LOCAL CONSTANTS!
+ if (codegen.local_named_constants.has(identifier)) {
+ return codegen.local_named_constants[identifier] | (GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS);
+ }
+
// TRY CLASS MEMBER
if (_is_class_member_property(codegen, identifier)) {
//get property
@@ -264,7 +269,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
}
//TRY MEMBERS!
- if (!codegen.function_node || !codegen.function_node->_static) {
+ if (!codegen.function_node || !codegen.function_node->is_static) {
// TRY MEMBER VARIABLES!
//static function
if (codegen.script->member_indices.has(identifier)) {
@@ -315,6 +320,21 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
owner = owner->_owner;
}
+ // TRY SIGNALS AND METHODS (can be made callables)
+ if (codegen.class_node->members_indices.has(identifier)) {
+ const GDScriptParser::ClassNode::Member &member = codegen.class_node->members[codegen.class_node->members_indices[identifier]];
+ if (member.type == GDScriptParser::ClassNode::Member::FUNCTION || member.type == GDScriptParser::ClassNode::Member::SIGNAL) {
+ // Get like it was a property.
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_GET_NAMED); // perform operator
+ codegen.opcodes.push_back(GDScriptFunction::ADDR_TYPE_SELF << GDScriptFunction::ADDR_BITS); // Self.
+ codegen.opcodes.push_back(codegen.get_name_map_pos(identifier)); // argument 2 (unary only takes one parameter)
+ int dst_addr = (p_stack_level) | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
+ codegen.opcodes.push_back(dst_addr); // append the stack level as destination address of the opcode
+ codegen.alloc_stack(p_stack_level);
+ return dst_addr;
+ }
+ }
+
if (GDScriptLanguage::get_singleton()->get_global_map().has(identifier)) {
int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier];
return idx | (GDScriptFunction::ADDR_TYPE_GLOBAL << GDScriptFunction::ADDR_BITS); //argument (stack root)
@@ -324,11 +344,11 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
if (ScriptServer::is_global_class(identifier)) {
const GDScriptParser::ClassNode *class_node = codegen.class_node;
- while (class_node->owner) {
- class_node = class_node->owner;
+ while (class_node->outer) {
+ class_node = class_node->outer;
}
- if (class_node->name == identifier) {
+ if (class_node->identifier && class_node->identifier->name == identifier) {
_set_error("Using own name in class file is not allowed (creates a cyclic reference)", p_expression);
return -1;
}
@@ -371,9 +391,9 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
return -1;
} break;
- case GDScriptParser::Node::TYPE_CONSTANT: {
+ case GDScriptParser::Node::LITERAL: {
//return constant
- const GDScriptParser::ConstantNode *cn = static_cast<const GDScriptParser::ConstantNode *>(p_expression);
+ const GDScriptParser::LiteralNode *cn = static_cast<const GDScriptParser::LiteralNode *>(p_expression);
int idx;
@@ -388,15 +408,15 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
return idx | (GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS); //argument (stack root)
} break;
- case GDScriptParser::Node::TYPE_SELF: {
+ case GDScriptParser::Node::SELF: {
//return constant
- if (codegen.function_node && codegen.function_node->_static) {
+ if (codegen.function_node && codegen.function_node->is_static) {
_set_error("'self' not present in static function!", p_expression);
return -1;
}
return (GDScriptFunction::ADDR_TYPE_SELF << GDScriptFunction::ADDR_BITS);
} break;
- case GDScriptParser::Node::TYPE_ARRAY: {
+ case GDScriptParser::Node::ARRAY: {
const GDScriptParser::ArrayNode *an = static_cast<const GDScriptParser::ArrayNode *>(p_expression);
Vector<int> values;
@@ -427,23 +447,35 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
return dst_addr;
} break;
- case GDScriptParser::Node::TYPE_DICTIONARY: {
+ case GDScriptParser::Node::DICTIONARY: {
const GDScriptParser::DictionaryNode *dn = static_cast<const GDScriptParser::DictionaryNode *>(p_expression);
- Vector<int> values;
+ Vector<int> elements;
int slevel = p_stack_level;
for (int i = 0; i < dn->elements.size(); i++) {
- int ret = _parse_expression(codegen, dn->elements[i].key, slevel);
- if (ret < 0) {
- return ret;
- }
- if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) {
- slevel++;
- codegen.alloc_stack(slevel);
+ // Key.
+ int ret = -1;
+ switch (dn->style) {
+ case GDScriptParser::DictionaryNode::PYTHON_DICT:
+ // Python-style: key is any expression.
+ ret = _parse_expression(codegen, dn->elements[i].key, slevel);
+ if (ret < 0) {
+ return ret;
+ }
+ if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) {
+ slevel++;
+ codegen.alloc_stack(slevel);
+ }
+ break;
+ case GDScriptParser::DictionaryNode::LUA_TABLE:
+ // Lua-style: key is an identifier interpreted as string.
+ String key = static_cast<const GDScriptParser::IdentifierNode *>(dn->elements[i].key)->name;
+ ret = codegen.get_constant_pos(key);
+ break;
}
- values.push_back(ret);
+ elements.push_back(ret);
ret = _parse_expression(codegen, dn->elements[i].value, slevel);
if (ret < 0) {
@@ -454,13 +486,13 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
codegen.alloc_stack(slevel);
}
- values.push_back(ret);
+ elements.push_back(ret);
}
codegen.opcodes.push_back(GDScriptFunction::OPCODE_CONSTRUCT_DICTIONARY);
codegen.opcodes.push_back(dn->elements.size());
- for (int i = 0; i < values.size(); i++) {
- codegen.opcodes.push_back(values[i]);
+ for (int i = 0; i < elements.size(); i++) {
+ codegen.opcodes.push_back(elements[i]);
}
int dst_addr = (p_stack_level) | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
@@ -469,11 +501,11 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
return dst_addr;
} break;
- case GDScriptParser::Node::TYPE_CAST: {
+ case GDScriptParser::Node::CAST: {
const GDScriptParser::CastNode *cn = static_cast<const GDScriptParser::CastNode *>(p_expression);
int slevel = p_stack_level;
- int src_addr = _parse_expression(codegen, cn->source_node, slevel);
+ int src_addr = _parse_expression(codegen, cn->operand, slevel);
if (src_addr < 0) {
return src_addr;
}
@@ -482,7 +514,8 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
codegen.alloc_stack(slevel);
}
- GDScriptDataType cast_type = _gdtype_from_datatype(cn->cast_type);
+ // FIXME: Allow actual cast.
+ GDScriptDataType cast_type; // = _gdtype_from_datatype(cn->cast_type);
switch (cast_type.kind) {
case GDScriptDataType::BUILTIN: {
@@ -504,8 +537,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
case GDScriptDataType::SCRIPT:
case GDScriptDataType::GDSCRIPT: {
Variant script = cast_type.script_type;
- int idx = codegen.get_constant_pos(script);
- idx |= GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS; //make it a local constant (faster access)
+ int idx = codegen.get_constant_pos(script); //make it a local constant (faster access)
codegen.opcodes.push_back(GDScriptFunction::OPCODE_CAST_TO_SCRIPT); // perform operator
codegen.opcodes.push_back(idx); // variable type
@@ -523,244 +555,312 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
return dst_addr;
} break;
- case GDScriptParser::Node::TYPE_OPERATOR: {
- //hell breaks loose
+ //hell breaks loose
+
+#define OPERATOR_RETURN \
+ int dst_addr = (p_stack_level) | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS); \
+ codegen.opcodes.push_back(dst_addr); \
+ codegen.alloc_stack(p_stack_level); \
+ return dst_addr
+
+ case GDScriptParser::Node::CALL: {
+ const GDScriptParser::CallNode *call = static_cast<const GDScriptParser::CallNode *>(p_expression);
+ if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name) != Variant::VARIANT_MAX) {
+ //construct a basic type
+
+ Variant::Type vtype = GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name);
+
+ Vector<int> arguments;
+ int slevel = p_stack_level;
+ for (int i = 0; i < call->arguments.size(); i++) {
+ int ret = _parse_expression(codegen, call->arguments[i], slevel);
+ if (ret < 0) {
+ return ret;
+ }
+ if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) {
+ slevel++;
+ codegen.alloc_stack(slevel);
+ }
+ arguments.push_back(ret);
+ }
- const GDScriptParser::OperatorNode *on = static_cast<const GDScriptParser::OperatorNode *>(p_expression);
- switch (on->op) {
- //call/constructor operator
- case GDScriptParser::OperatorNode::OP_PARENT_CALL: {
- ERR_FAIL_COND_V(on->arguments.size() < 1, -1);
+ //push call bytecode
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_CONSTRUCT); // basic type constructor
+ codegen.opcodes.push_back(vtype); //instance
+ codegen.opcodes.push_back(arguments.size()); //argument count
+ codegen.alloc_call(arguments.size());
+ for (int i = 0; i < arguments.size(); i++) {
+ codegen.opcodes.push_back(arguments[i]); //arguments
+ }
- const GDScriptParser::IdentifierNode *in = (const GDScriptParser::IdentifierNode *)on->arguments[0];
+ } else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_function(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name) != GDScriptFunctions::FUNC_MAX) {
+ //built in function
- Vector<int> arguments;
- int slevel = p_stack_level;
- for (int i = 1; i < on->arguments.size(); i++) {
- int ret = _parse_expression(codegen, on->arguments[i], slevel);
- if (ret < 0) {
- return ret;
- }
- if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) {
- slevel++;
- codegen.alloc_stack(slevel);
- }
- arguments.push_back(ret);
+ Vector<int> arguments;
+ int slevel = p_stack_level;
+ for (int i = 0; i < call->arguments.size(); i++) {
+ int ret = _parse_expression(codegen, call->arguments[i], slevel);
+ if (ret < 0) {
+ return ret;
}
- //push call bytecode
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_CALL_SELF_BASE); // basic type constructor
-
- codegen.opcodes.push_back(codegen.get_name_map_pos(in->name)); //instance
- codegen.opcodes.push_back(arguments.size()); //argument count
- codegen.alloc_call(arguments.size());
- for (int i = 0; i < arguments.size(); i++) {
- codegen.opcodes.push_back(arguments[i]); //arguments
+ if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) {
+ slevel++;
+ codegen.alloc_stack(slevel);
}
- } break;
- case GDScriptParser::OperatorNode::OP_CALL: {
- if (on->arguments[0]->type == GDScriptParser::Node::TYPE_TYPE) {
- //construct a basic type
- ERR_FAIL_COND_V(on->arguments.size() < 1, -1);
-
- const GDScriptParser::TypeNode *tn = (const GDScriptParser::TypeNode *)on->arguments[0];
- int vtype = tn->vtype;
-
- Vector<int> arguments;
- int slevel = p_stack_level;
- for (int i = 1; i < on->arguments.size(); i++) {
- int ret = _parse_expression(codegen, on->arguments[i], slevel);
- if (ret < 0) {
- return ret;
- }
- if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) {
- slevel++;
- codegen.alloc_stack(slevel);
- }
- arguments.push_back(ret);
- }
+ arguments.push_back(ret);
+ }
- //push call bytecode
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_CONSTRUCT); // basic type constructor
- codegen.opcodes.push_back(vtype); //instance
- codegen.opcodes.push_back(arguments.size()); //argument count
- codegen.alloc_call(arguments.size());
- for (int i = 0; i < arguments.size(); i++) {
- codegen.opcodes.push_back(arguments[i]); //arguments
- }
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_CALL_BUILT_IN);
+ codegen.opcodes.push_back(GDScriptParser::get_builtin_function(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name));
+ codegen.opcodes.push_back(arguments.size());
+ codegen.alloc_call(arguments.size());
+ for (int i = 0; i < arguments.size(); i++) {
+ codegen.opcodes.push_back(arguments[i]);
+ }
+
+ } else {
+ //regular function
+
+ const GDScriptParser::ExpressionNode *callee = call->callee;
- } else if (on->arguments[0]->type == GDScriptParser::Node::TYPE_BUILT_IN_FUNCTION) {
- //built in function
+ Vector<int> arguments;
+ int slevel = p_stack_level;
- ERR_FAIL_COND_V(on->arguments.size() < 1, -1);
+ // TODO: Use callables when possible if needed.
+ int ret = -1;
+ int super_address = -1;
+ if (call->is_super) {
+ // Super call.
+ if (call->callee == nullptr) {
+ // Implicit super function call.
+ super_address = codegen.get_name_map_pos(codegen.function_node->identifier->name);
+ } else {
+ super_address = codegen.get_name_map_pos(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name);
+ }
+ } else {
+ if (callee->type == GDScriptParser::Node::IDENTIFIER) {
+ // Self function call.
+ if (codegen.function_node && codegen.function_node->is_static) {
+ ret = (GDScriptFunction::ADDR_TYPE_CLASS << GDScriptFunction::ADDR_BITS);
+ } else {
+ ret = (GDScriptFunction::ADDR_TYPE_SELF << GDScriptFunction::ADDR_BITS);
+ }
+ arguments.push_back(ret);
+ ret = codegen.get_name_map_pos(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name);
+ arguments.push_back(ret);
+ } else if (callee->type == GDScriptParser::Node::SUBSCRIPT) {
+ const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(call->callee);
- Vector<int> arguments;
- int slevel = p_stack_level;
- for (int i = 1; i < on->arguments.size(); i++) {
- int ret = _parse_expression(codegen, on->arguments[i], slevel);
+ if (subscript->is_attribute) {
+ ret = _parse_expression(codegen, subscript->base, slevel);
if (ret < 0) {
return ret;
}
-
if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) {
slevel++;
codegen.alloc_stack(slevel);
}
-
arguments.push_back(ret);
+ arguments.push_back(codegen.get_name_map_pos(subscript->attribute->name));
+ } else {
+ // TODO: Validate this at parse time.
+ // TODO: Though might not be the case if callables are a thing.
+ _set_error("Cannot call something that isn't a function.", call->callee);
+ return -1;
}
-
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_CALL_BUILT_IN);
- codegen.opcodes.push_back(static_cast<const GDScriptParser::BuiltInFunctionNode *>(on->arguments[0])->function);
- codegen.opcodes.push_back(on->arguments.size() - 1);
- codegen.alloc_call(on->arguments.size() - 1);
- for (int i = 0; i < arguments.size(); i++) {
- codegen.opcodes.push_back(arguments[i]);
- }
-
} else {
- //regular function
- ERR_FAIL_COND_V(on->arguments.size() < 2, -1);
-
- const GDScriptParser::Node *instance = on->arguments[0];
+ _set_error("Cannot call something that isn't a function.", call->callee);
+ return -1;
+ }
+ }
- if (instance->type == GDScriptParser::Node::TYPE_SELF) {
- //room for optimization
- }
+ for (int i = 0; i < call->arguments.size(); i++) {
+ ret = _parse_expression(codegen, call->arguments[i], slevel);
+ if (ret < 0) {
+ return ret;
+ }
+ if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) {
+ slevel++;
+ codegen.alloc_stack(slevel);
+ }
+ arguments.push_back(ret);
+ }
- Vector<int> arguments;
- int slevel = p_stack_level;
+ int opcode = GDScriptFunction::OPCODE_CALL_RETURN;
+ if (call->is_super) {
+ opcode = GDScriptFunction::OPCODE_CALL_SELF_BASE;
+ } else if (within_await) {
+ opcode = GDScriptFunction::OPCODE_CALL_ASYNC;
+ } else if (p_root) {
+ opcode = GDScriptFunction::OPCODE_CALL;
+ }
- for (int i = 0; i < on->arguments.size(); i++) {
- int ret;
+ codegen.opcodes.push_back(opcode); // perform operator
+ if (call->is_super) {
+ codegen.opcodes.push_back(super_address);
+ }
+ codegen.opcodes.push_back(call->arguments.size());
+ codegen.alloc_call(call->arguments.size());
+ for (int i = 0; i < arguments.size(); i++) {
+ codegen.opcodes.push_back(arguments[i]);
+ }
+ }
+ OPERATOR_RETURN;
+ } break;
+ case GDScriptParser::Node::GET_NODE: {
+ const GDScriptParser::GetNodeNode *get_node = static_cast<const GDScriptParser::GetNodeNode *>(p_expression);
- if (i == 0 && on->arguments[i]->type == GDScriptParser::Node::TYPE_SELF && codegen.function_node && codegen.function_node->_static) {
- //static call to self
- ret = (GDScriptFunction::ADDR_TYPE_CLASS << GDScriptFunction::ADDR_BITS);
- } else if (i == 1) {
- if (on->arguments[i]->type != GDScriptParser::Node::TYPE_IDENTIFIER) {
- _set_error("Attempt to call a non-identifier.", on);
- return -1;
- }
- GDScriptParser::IdentifierNode *id = static_cast<GDScriptParser::IdentifierNode *>(on->arguments[i]);
- ret = codegen.get_name_map_pos(id->name);
+ String node_name;
+ if (get_node->string != nullptr) {
+ node_name += String(get_node->string->value);
+ } else {
+ for (int i = 0; i < get_node->chain.size(); i++) {
+ if (i > 0) {
+ node_name += "/";
+ }
+ node_name += get_node->chain[i]->name;
+ }
+ }
- } else {
- ret = _parse_expression(codegen, on->arguments[i], slevel);
- if (ret < 0) {
- return ret;
- }
- if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) {
- slevel++;
- codegen.alloc_stack(slevel);
- }
- }
- arguments.push_back(ret);
- }
+ int arg_address = codegen.get_constant_pos(NodePath(node_name));
- codegen.opcodes.push_back(p_root ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN); // perform operator
- codegen.opcodes.push_back(on->arguments.size() - 2);
- codegen.alloc_call(on->arguments.size() - 2);
- for (int i = 0; i < arguments.size(); i++) {
- codegen.opcodes.push_back(arguments[i]);
- }
- }
- } break;
- case GDScriptParser::OperatorNode::OP_YIELD: {
- ERR_FAIL_COND_V(on->arguments.size() && on->arguments.size() != 2, -1);
+ codegen.opcodes.push_back(p_root ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN);
+ codegen.opcodes.push_back(1); // number of arguments.
+ codegen.alloc_call(1);
+ codegen.opcodes.push_back(GDScriptFunction::ADDR_TYPE_SELF << GDScriptFunction::ADDR_BITS); // self.
+ codegen.opcodes.push_back(codegen.get_name_map_pos("get_node")); // function.
+ codegen.opcodes.push_back(arg_address); // argument (NodePath).
+ OPERATOR_RETURN;
+ } break;
+ case GDScriptParser::Node::PRELOAD: {
+ const GDScriptParser::PreloadNode *preload = static_cast<const GDScriptParser::PreloadNode *>(p_expression);
- Vector<int> arguments;
- int slevel = p_stack_level;
- for (int i = 0; i < on->arguments.size(); i++) {
- int ret = _parse_expression(codegen, on->arguments[i], slevel);
- if (ret < 0) {
- return ret;
- }
- if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) {
- slevel++;
- codegen.alloc_stack(slevel);
- }
- arguments.push_back(ret);
- }
+ // Add resource as constant.
+ return codegen.get_constant_pos(preload->resource);
+ } break;
+ case GDScriptParser::Node::AWAIT: {
+ const GDScriptParser::AwaitNode *await = static_cast<const GDScriptParser::AwaitNode *>(p_expression);
- //push call bytecode
- codegen.opcodes.push_back(arguments.size() == 0 ? GDScriptFunction::OPCODE_YIELD : GDScriptFunction::OPCODE_YIELD_SIGNAL); // basic type constructor
- for (int i = 0; i < arguments.size(); i++) {
- codegen.opcodes.push_back(arguments[i]); //arguments
- }
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_YIELD_RESUME);
- //next will be where to place the result :)
+ int slevel = p_stack_level;
+ within_await = true;
+ int argument = _parse_expression(codegen, await->to_await, slevel);
+ within_await = false;
+ if (argument < 0) {
+ return argument;
+ }
+ if ((argument >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) {
+ slevel++;
+ codegen.alloc_stack(slevel);
+ }
+ //push call bytecode
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_AWAIT);
+ codegen.opcodes.push_back(argument);
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_AWAIT_RESUME);
+ //next will be where to place the result :)
- } break;
+ OPERATOR_RETURN;
+ } break;
- //indexing operator
- case GDScriptParser::OperatorNode::OP_INDEX:
- case GDScriptParser::OperatorNode::OP_INDEX_NAMED: {
- ERR_FAIL_COND_V(on->arguments.size() != 2, -1);
+ //indexing operator
+ case GDScriptParser::Node::SUBSCRIPT: {
+ int slevel = p_stack_level;
- int slevel = p_stack_level;
- bool named = (on->op == GDScriptParser::OperatorNode::OP_INDEX_NAMED);
+ const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(p_expression);
- int from = _parse_expression(codegen, on->arguments[0], slevel);
- if (from < 0) {
- return from;
- }
+ int from = _parse_expression(codegen, subscript->base, slevel);
+ if (from < 0) {
+ return from;
+ }
- int index;
- if (p_index_addr != 0) {
- index = p_index_addr;
- } else if (named) {
- if (on->arguments[0]->type == GDScriptParser::Node::TYPE_SELF && codegen.script && codegen.function_node && !codegen.function_node->_static) {
- GDScriptParser::IdentifierNode *identifier = static_cast<GDScriptParser::IdentifierNode *>(on->arguments[1]);
- const Map<StringName, GDScript::MemberInfo>::Element *MI = codegen.script->member_indices.find(identifier->name);
+ bool named = subscript->is_attribute;
+ int index;
+ if (p_index_addr != 0) {
+ index = p_index_addr;
+ } else if (subscript->is_attribute) {
+ if (subscript->base->type == GDScriptParser::Node::SELF && codegen.script && codegen.function_node && !codegen.function_node->is_static) {
+ GDScriptParser::IdentifierNode *identifier = subscript->attribute;
+ const Map<StringName, GDScript::MemberInfo>::Element *MI = codegen.script->member_indices.find(identifier->name);
#ifdef DEBUG_ENABLED
- if (MI && MI->get().getter == codegen.function_node->name) {
- String n = static_cast<GDScriptParser::IdentifierNode *>(on->arguments[1])->name;
- _set_error("Must use '" + n + "' instead of 'self." + n + "' in getter.", on);
- return -1;
- }
+ if (MI && MI->get().getter == codegen.function_node->identifier->name) {
+ String n = identifier->name;
+ _set_error("Must use '" + n + "' instead of 'self." + n + "' in getter.", identifier);
+ return -1;
+ }
#endif
- if (MI && MI->get().getter == "") {
- // Faster than indexing self (as if no self. had been used)
- return (MI->get().index) | (GDScriptFunction::ADDR_TYPE_MEMBER << GDScriptFunction::ADDR_BITS);
- }
- }
-
- index = codegen.get_name_map_pos(static_cast<GDScriptParser::IdentifierNode *>(on->arguments[1])->name);
+ if (MI && MI->get().getter == "") {
+ // Faster than indexing self (as if no self. had been used)
+ return (MI->get().index) | (GDScriptFunction::ADDR_TYPE_MEMBER << GDScriptFunction::ADDR_BITS);
+ }
+ }
- } else {
- if (on->arguments[1]->type == GDScriptParser::Node::TYPE_CONSTANT && static_cast<const GDScriptParser::ConstantNode *>(on->arguments[1])->value.get_type() == Variant::STRING) {
- //also, somehow, named (speed up anyway)
- StringName name = static_cast<const GDScriptParser::ConstantNode *>(on->arguments[1])->value;
- index = codegen.get_name_map_pos(name);
- named = true;
+ index = codegen.get_name_map_pos(subscript->attribute->name);
- } else {
- //regular indexing
- if (from & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) {
- slevel++;
- codegen.alloc_stack(slevel);
- }
+ } else {
+ if (subscript->index->type == GDScriptParser::Node::LITERAL && static_cast<const GDScriptParser::LiteralNode *>(subscript->index)->value.get_type() == Variant::STRING) {
+ //also, somehow, named (speed up anyway)
+ StringName name = static_cast<const GDScriptParser::LiteralNode *>(subscript->index)->value;
+ index = codegen.get_name_map_pos(name);
+ named = true;
- index = _parse_expression(codegen, on->arguments[1], slevel);
- if (index < 0) {
- return index;
- }
- }
+ } else {
+ //regular indexing
+ if (from & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) {
+ slevel++;
+ codegen.alloc_stack(slevel);
}
- codegen.opcodes.push_back(named ? GDScriptFunction::OPCODE_GET_NAMED : GDScriptFunction::OPCODE_GET); // perform operator
- codegen.opcodes.push_back(from); // argument 1
- codegen.opcodes.push_back(index); // argument 2 (unary only takes one parameter)
+ index = _parse_expression(codegen, subscript->index, slevel);
+ if (index < 0) {
+ return index;
+ }
+ }
+ }
+ codegen.opcodes.push_back(named ? GDScriptFunction::OPCODE_GET_NAMED : GDScriptFunction::OPCODE_GET); // perform operator
+ codegen.opcodes.push_back(from); // argument 1
+ codegen.opcodes.push_back(index); // argument 2 (unary only takes one parameter)
+ OPERATOR_RETURN;
+ } break;
+ case GDScriptParser::Node::UNARY_OPERATOR: {
+ //unary operators
+ const GDScriptParser::UnaryOpNode *unary = static_cast<const GDScriptParser::UnaryOpNode *>(p_expression);
+ switch (unary->operation) {
+ case GDScriptParser::UnaryOpNode::OP_NEGATIVE: {
+ if (!_create_unary_operator(codegen, unary, Variant::OP_NEGATE, p_stack_level)) {
+ return -1;
+ }
} break;
- case GDScriptParser::OperatorNode::OP_AND: {
+ case GDScriptParser::UnaryOpNode::OP_POSITIVE: {
+ if (!_create_unary_operator(codegen, unary, Variant::OP_POSITIVE, p_stack_level)) {
+ return -1;
+ }
+ } break;
+ case GDScriptParser::UnaryOpNode::OP_LOGIC_NOT: {
+ if (!_create_unary_operator(codegen, unary, Variant::OP_NOT, p_stack_level)) {
+ return -1;
+ }
+ } break;
+ case GDScriptParser::UnaryOpNode::OP_COMPLEMENT: {
+ if (!_create_unary_operator(codegen, unary, Variant::OP_BIT_NEGATE, p_stack_level)) {
+ return -1;
+ }
+ } break;
+ }
+ OPERATOR_RETURN;
+ }
+ case GDScriptParser::Node::BINARY_OPERATOR: {
+ //binary operators (in precedence order)
+ const GDScriptParser::BinaryOpNode *binary = static_cast<const GDScriptParser::BinaryOpNode *>(p_expression);
+
+ switch (binary->operation) {
+ case GDScriptParser::BinaryOpNode::OP_LOGIC_AND: {
// AND operator with early out on failure
- int res = _parse_expression(codegen, on->arguments[0], p_stack_level);
+ int res = _parse_expression(codegen, binary->left_operand, p_stack_level);
if (res < 0) {
return res;
}
@@ -769,7 +869,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
int jump_fail_pos = codegen.opcodes.size();
codegen.opcodes.push_back(0);
- res = _parse_expression(codegen, on->arguments[1], p_stack_level);
+ res = _parse_expression(codegen, binary->right_operand, p_stack_level);
if (res < 0) {
return res;
}
@@ -791,10 +891,10 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
return p_stack_level | GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS;
} break;
- case GDScriptParser::OperatorNode::OP_OR: {
+ case GDScriptParser::BinaryOpNode::OP_LOGIC_OR: {
// OR operator with early out on success
- int res = _parse_expression(codegen, on->arguments[0], p_stack_level);
+ int res = _parse_expression(codegen, binary->left_operand, p_stack_level);
if (res < 0) {
return res;
}
@@ -803,7 +903,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
int jump_success_pos = codegen.opcodes.size();
codegen.opcodes.push_back(0);
- res = _parse_expression(codegen, on->arguments[1], p_stack_level);
+ res = _parse_expression(codegen, binary->right_operand, p_stack_level);
if (res < 0) {
return res;
}
@@ -825,736 +925,1026 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
return p_stack_level | GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS;
} break;
- // ternary operators
- case GDScriptParser::OperatorNode::OP_TERNARY_IF: {
- // x IF a ELSE y operator with early out on failure
+ case GDScriptParser::BinaryOpNode::OP_TYPE_TEST: {
+ int slevel = p_stack_level;
- int res = _parse_expression(codegen, on->arguments[0], p_stack_level);
- if (res < 0) {
- return res;
+ int src_address_a = _parse_expression(codegen, binary->left_operand, slevel);
+ if (src_address_a < 0) {
+ return -1;
}
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF_NOT);
- codegen.opcodes.push_back(res);
- int jump_fail_pos = codegen.opcodes.size();
- codegen.opcodes.push_back(0);
- res = _parse_expression(codegen, on->arguments[1], p_stack_level);
- if (res < 0) {
- return res;
+ if (src_address_a & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) {
+ slevel++; //uses stack for return, increase stack
}
- codegen.alloc_stack(p_stack_level); //it will be used..
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN);
- codegen.opcodes.push_back(p_stack_level | GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
- codegen.opcodes.push_back(res);
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
- int jump_past_pos = codegen.opcodes.size();
- codegen.opcodes.push_back(0);
-
- codegen.opcodes.write[jump_fail_pos] = codegen.opcodes.size();
- res = _parse_expression(codegen, on->arguments[2], p_stack_level);
- if (res < 0) {
- return res;
+ int src_address_b = -1;
+ bool builtin = false;
+ if (binary->right_operand->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(static_cast<const GDScriptParser::IdentifierNode *>(binary->right_operand)->name) != Variant::VARIANT_MAX) {
+ // `is` with builtin type
+ builtin = true;
+ src_address_b = (int)GDScriptParser::get_builtin_type(static_cast<const GDScriptParser::IdentifierNode *>(binary->right_operand)->name);
+ } else {
+ src_address_b = _parse_expression(codegen, binary->right_operand, slevel);
+ if (src_address_b < 0) {
+ return -1;
+ }
}
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN);
- codegen.opcodes.push_back(p_stack_level | GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
- codegen.opcodes.push_back(res);
-
- codegen.opcodes.write[jump_past_pos] = codegen.opcodes.size();
-
- return p_stack_level | GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS;
+ codegen.opcodes.push_back(builtin ? GDScriptFunction::OPCODE_IS_BUILTIN : GDScriptFunction::OPCODE_EXTENDS_TEST); // perform operator
+ codegen.opcodes.push_back(src_address_a); // argument 1
+ codegen.opcodes.push_back(src_address_b); // argument 2 (unary only takes one parameter)
} break;
- //unary operators
- case GDScriptParser::OperatorNode::OP_NEG: {
- if (!_create_unary_operator(codegen, on, Variant::OP_NEGATE, p_stack_level)) {
- return -1;
- }
- } break;
- case GDScriptParser::OperatorNode::OP_POS: {
- if (!_create_unary_operator(codegen, on, Variant::OP_POSITIVE, p_stack_level)) {
- return -1;
- }
- } break;
- case GDScriptParser::OperatorNode::OP_NOT: {
- if (!_create_unary_operator(codegen, on, Variant::OP_NOT, p_stack_level)) {
+ case GDScriptParser::BinaryOpNode::OP_CONTENT_TEST: {
+ if (!_create_binary_operator(codegen, binary, Variant::OP_IN, p_stack_level)) {
return -1;
}
} break;
- case GDScriptParser::OperatorNode::OP_BIT_INVERT: {
- if (!_create_unary_operator(codegen, on, Variant::OP_BIT_NEGATE, p_stack_level)) {
+ case GDScriptParser::BinaryOpNode::OP_COMP_EQUAL: {
+ if (!_create_binary_operator(codegen, binary, Variant::OP_EQUAL, p_stack_level)) {
return -1;
}
} break;
- //binary operators (in precedence order)
- case GDScriptParser::OperatorNode::OP_IN: {
- if (!_create_binary_operator(codegen, on, Variant::OP_IN, p_stack_level)) {
+ case GDScriptParser::BinaryOpNode::OP_COMP_NOT_EQUAL: {
+ if (!_create_binary_operator(codegen, binary, Variant::OP_NOT_EQUAL, p_stack_level)) {
return -1;
}
} break;
- case GDScriptParser::OperatorNode::OP_EQUAL: {
- if (!_create_binary_operator(codegen, on, Variant::OP_EQUAL, p_stack_level)) {
+ case GDScriptParser::BinaryOpNode::OP_COMP_LESS: {
+ if (!_create_binary_operator(codegen, binary, Variant::OP_LESS, p_stack_level)) {
return -1;
}
} break;
- case GDScriptParser::OperatorNode::OP_NOT_EQUAL: {
- if (!_create_binary_operator(codegen, on, Variant::OP_NOT_EQUAL, p_stack_level)) {
+ case GDScriptParser::BinaryOpNode::OP_COMP_LESS_EQUAL: {
+ if (!_create_binary_operator(codegen, binary, Variant::OP_LESS_EQUAL, p_stack_level)) {
return -1;
}
} break;
- case GDScriptParser::OperatorNode::OP_LESS: {
- if (!_create_binary_operator(codegen, on, Variant::OP_LESS, p_stack_level)) {
+ case GDScriptParser::BinaryOpNode::OP_COMP_GREATER: {
+ if (!_create_binary_operator(codegen, binary, Variant::OP_GREATER, p_stack_level)) {
return -1;
}
} break;
- case GDScriptParser::OperatorNode::OP_LESS_EQUAL: {
- if (!_create_binary_operator(codegen, on, Variant::OP_LESS_EQUAL, p_stack_level)) {
+ case GDScriptParser::BinaryOpNode::OP_COMP_GREATER_EQUAL: {
+ if (!_create_binary_operator(codegen, binary, Variant::OP_GREATER_EQUAL, p_stack_level)) {
return -1;
}
} break;
- case GDScriptParser::OperatorNode::OP_GREATER: {
- if (!_create_binary_operator(codegen, on, Variant::OP_GREATER, p_stack_level)) {
+ case GDScriptParser::BinaryOpNode::OP_ADDITION: {
+ if (!_create_binary_operator(codegen, binary, Variant::OP_ADD, p_stack_level)) {
return -1;
}
} break;
- case GDScriptParser::OperatorNode::OP_GREATER_EQUAL: {
- if (!_create_binary_operator(codegen, on, Variant::OP_GREATER_EQUAL, p_stack_level)) {
+ case GDScriptParser::BinaryOpNode::OP_SUBTRACTION: {
+ if (!_create_binary_operator(codegen, binary, Variant::OP_SUBTRACT, p_stack_level)) {
return -1;
}
} break;
- case GDScriptParser::OperatorNode::OP_ADD: {
- if (!_create_binary_operator(codegen, on, Variant::OP_ADD, p_stack_level)) {
+ case GDScriptParser::BinaryOpNode::OP_MULTIPLICATION: {
+ if (!_create_binary_operator(codegen, binary, Variant::OP_MULTIPLY, p_stack_level)) {
return -1;
}
} break;
- case GDScriptParser::OperatorNode::OP_SUB: {
- if (!_create_binary_operator(codegen, on, Variant::OP_SUBTRACT, p_stack_level)) {
+ case GDScriptParser::BinaryOpNode::OP_DIVISION: {
+ if (!_create_binary_operator(codegen, binary, Variant::OP_DIVIDE, p_stack_level)) {
return -1;
}
} break;
- case GDScriptParser::OperatorNode::OP_MUL: {
- if (!_create_binary_operator(codegen, on, Variant::OP_MULTIPLY, p_stack_level)) {
+ case GDScriptParser::BinaryOpNode::OP_MODULO: {
+ if (!_create_binary_operator(codegen, binary, Variant::OP_MODULE, p_stack_level)) {
return -1;
}
} break;
- case GDScriptParser::OperatorNode::OP_DIV: {
- if (!_create_binary_operator(codegen, on, Variant::OP_DIVIDE, p_stack_level)) {
+ case GDScriptParser::BinaryOpNode::OP_BIT_AND: {
+ if (!_create_binary_operator(codegen, binary, Variant::OP_BIT_AND, p_stack_level)) {
return -1;
}
} break;
- case GDScriptParser::OperatorNode::OP_MOD: {
- if (!_create_binary_operator(codegen, on, Variant::OP_MODULE, p_stack_level)) {
+ case GDScriptParser::BinaryOpNode::OP_BIT_OR: {
+ if (!_create_binary_operator(codegen, binary, Variant::OP_BIT_OR, p_stack_level)) {
return -1;
}
} break;
- //case GDScriptParser::OperatorNode::OP_SHIFT_LEFT: { if (!_create_binary_operator(codegen,on,Variant::OP_SHIFT_LEFT,p_stack_level)) return -1;} break;
- //case GDScriptParser::OperatorNode::OP_SHIFT_RIGHT: { if (!_create_binary_operator(codegen,on,Variant::OP_SHIFT_RIGHT,p_stack_level)) return -1;} break;
- case GDScriptParser::OperatorNode::OP_BIT_AND: {
- if (!_create_binary_operator(codegen, on, Variant::OP_BIT_AND, p_stack_level)) {
- return -1;
- }
- } break;
- case GDScriptParser::OperatorNode::OP_BIT_OR: {
- if (!_create_binary_operator(codegen, on, Variant::OP_BIT_OR, p_stack_level)) {
- return -1;
- }
- } break;
- case GDScriptParser::OperatorNode::OP_BIT_XOR: {
- if (!_create_binary_operator(codegen, on, Variant::OP_BIT_XOR, p_stack_level)) {
+ case GDScriptParser::BinaryOpNode::OP_BIT_XOR: {
+ if (!_create_binary_operator(codegen, binary, Variant::OP_BIT_XOR, p_stack_level)) {
return -1;
}
} break;
//shift
- case GDScriptParser::OperatorNode::OP_SHIFT_LEFT: {
- if (!_create_binary_operator(codegen, on, Variant::OP_SHIFT_LEFT, p_stack_level)) {
+ case GDScriptParser::BinaryOpNode::OP_BIT_LEFT_SHIFT: {
+ if (!_create_binary_operator(codegen, binary, Variant::OP_SHIFT_LEFT, p_stack_level)) {
return -1;
}
} break;
- case GDScriptParser::OperatorNode::OP_SHIFT_RIGHT: {
- if (!_create_binary_operator(codegen, on, Variant::OP_SHIFT_RIGHT, p_stack_level)) {
+ case GDScriptParser::BinaryOpNode::OP_BIT_RIGHT_SHIFT: {
+ if (!_create_binary_operator(codegen, binary, Variant::OP_SHIFT_RIGHT, p_stack_level)) {
return -1;
}
} break;
- //assignment operators
- case GDScriptParser::OperatorNode::OP_ASSIGN_ADD:
- case GDScriptParser::OperatorNode::OP_ASSIGN_SUB:
- case GDScriptParser::OperatorNode::OP_ASSIGN_MUL:
- case GDScriptParser::OperatorNode::OP_ASSIGN_DIV:
- case GDScriptParser::OperatorNode::OP_ASSIGN_MOD:
- case GDScriptParser::OperatorNode::OP_ASSIGN_SHIFT_LEFT:
- case GDScriptParser::OperatorNode::OP_ASSIGN_SHIFT_RIGHT:
- case GDScriptParser::OperatorNode::OP_ASSIGN_BIT_AND:
- case GDScriptParser::OperatorNode::OP_ASSIGN_BIT_OR:
- case GDScriptParser::OperatorNode::OP_ASSIGN_BIT_XOR:
- case GDScriptParser::OperatorNode::OP_INIT_ASSIGN:
- case GDScriptParser::OperatorNode::OP_ASSIGN: {
- ERR_FAIL_COND_V(on->arguments.size() != 2, -1);
-
- if (on->arguments[0]->type == GDScriptParser::Node::TYPE_OPERATOR && (static_cast<GDScriptParser::OperatorNode *>(on->arguments[0])->op == GDScriptParser::OperatorNode::OP_INDEX || static_cast<GDScriptParser::OperatorNode *>(on->arguments[0])->op == GDScriptParser::OperatorNode::OP_INDEX_NAMED)) {
- // SET (chained) MODE!
+ }
+ OPERATOR_RETURN;
+ } break;
+ // ternary operators
+ case GDScriptParser::Node::TERNARY_OPERATOR: {
+ // x IF a ELSE y operator with early out on failure
+
+ const GDScriptParser::TernaryOpNode *ternary = static_cast<const GDScriptParser::TernaryOpNode *>(p_expression);
+ int res = _parse_expression(codegen, ternary->condition, p_stack_level);
+ if (res < 0) {
+ return res;
+ }
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF_NOT);
+ codegen.opcodes.push_back(res);
+ int jump_fail_pos = codegen.opcodes.size();
+ codegen.opcodes.push_back(0);
+
+ res = _parse_expression(codegen, ternary->true_expr, p_stack_level);
+ if (res < 0) {
+ return res;
+ }
+
+ codegen.alloc_stack(p_stack_level); //it will be used..
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN);
+ codegen.opcodes.push_back(p_stack_level | GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
+ codegen.opcodes.push_back(res);
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
+ int jump_past_pos = codegen.opcodes.size();
+ codegen.opcodes.push_back(0);
+
+ codegen.opcodes.write[jump_fail_pos] = codegen.opcodes.size();
+ res = _parse_expression(codegen, ternary->false_expr, p_stack_level);
+ if (res < 0) {
+ return res;
+ }
+
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN);
+ codegen.opcodes.push_back(p_stack_level | GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
+ codegen.opcodes.push_back(res);
+
+ codegen.opcodes.write[jump_past_pos] = codegen.opcodes.size();
+
+ return p_stack_level | GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS;
+
+ } break;
+ //assignment operators
+ case GDScriptParser::Node::ASSIGNMENT: {
+ const GDScriptParser::AssignmentNode *assignment = static_cast<const GDScriptParser::AssignmentNode *>(p_expression);
+
+ if (assignment->assignee->type == GDScriptParser::Node::SUBSCRIPT) {
+ // SET (chained) MODE!
+ const GDScriptParser::SubscriptNode *subscript = static_cast<GDScriptParser::SubscriptNode *>(assignment->assignee);
#ifdef DEBUG_ENABLED
- if (static_cast<GDScriptParser::OperatorNode *>(on->arguments[0])->op == GDScriptParser::OperatorNode::OP_INDEX_NAMED) {
- const GDScriptParser::OperatorNode *inon = static_cast<GDScriptParser::OperatorNode *>(on->arguments[0]);
-
- if (inon->arguments[0]->type == GDScriptParser::Node::TYPE_SELF && codegen.script && codegen.function_node && !codegen.function_node->_static) {
- const Map<StringName, GDScript::MemberInfo>::Element *MI = codegen.script->member_indices.find(static_cast<GDScriptParser::IdentifierNode *>(inon->arguments[1])->name);
- if (MI && MI->get().setter == codegen.function_node->name) {
- String n = static_cast<GDScriptParser::IdentifierNode *>(inon->arguments[1])->name;
- _set_error("Must use '" + n + "' instead of 'self." + n + "' in setter.", inon);
- return -1;
- }
- }
+ if (subscript->is_attribute) {
+ if (subscript->base->type == GDScriptParser::Node::SELF && codegen.script && codegen.function_node && !codegen.function_node->is_static) {
+ const Map<StringName, GDScript::MemberInfo>::Element *MI = codegen.script->member_indices.find(subscript->attribute->name);
+ if (MI && MI->get().setter == codegen.function_node->identifier->name) {
+ String n = subscript->attribute->name;
+ _set_error("Must use '" + n + "' instead of 'self." + n + "' in setter.", subscript);
+ return -1;
}
+ }
+ }
#endif
- int slevel = p_stack_level;
+ int slevel = p_stack_level;
- GDScriptParser::OperatorNode *op = static_cast<GDScriptParser::OperatorNode *>(on->arguments[0]);
+ /* Find chain of sets */
- /* Find chain of sets */
+ StringName assign_property;
- StringName assign_property;
+ List<const GDScriptParser::SubscriptNode *> chain;
- List<GDScriptParser::OperatorNode *> chain;
+ {
+ //create get/set chain
+ const GDScriptParser::SubscriptNode *n = subscript;
+ while (true) {
+ chain.push_back(n);
- {
- //create get/set chain
- GDScriptParser::OperatorNode *n = op;
- while (true) {
- chain.push_back(n);
- if (n->arguments[0]->type != GDScriptParser::Node::TYPE_OPERATOR) {
- //check for a built-in property
- if (n->arguments[0]->type == GDScriptParser::Node::TYPE_IDENTIFIER) {
- GDScriptParser::IdentifierNode *identifier = static_cast<GDScriptParser::IdentifierNode *>(n->arguments[0]);
- if (_is_class_member_property(codegen, identifier->name)) {
- assign_property = identifier->name;
- }
- }
- break;
- }
- n = static_cast<GDScriptParser::OperatorNode *>(n->arguments[0]);
- if (n->op != GDScriptParser::OperatorNode::OP_INDEX && n->op != GDScriptParser::OperatorNode::OP_INDEX_NAMED) {
- break;
+ if (n->base->type != GDScriptParser::Node::SUBSCRIPT) {
+ //check for a built-in property
+ if (n->base->type == GDScriptParser::Node::IDENTIFIER) {
+ GDScriptParser::IdentifierNode *identifier = static_cast<GDScriptParser::IdentifierNode *>(n->base);
+ if (_is_class_member_property(codegen, identifier->name)) {
+ assign_property = identifier->name;
}
}
+ break;
}
+ n = static_cast<const GDScriptParser::SubscriptNode *>(n->base);
+ }
+ }
- /* Chain of gets */
+ /* Chain of gets */
- //get at (potential) root stack pos, so it can be returned
- int prev_pos = _parse_expression(codegen, chain.back()->get()->arguments[0], slevel);
- if (prev_pos < 0) {
- return prev_pos;
- }
- int retval = prev_pos;
+ //get at (potential) root stack pos, so it can be returned
+ int prev_pos = _parse_expression(codegen, chain.back()->get()->base, slevel);
+ if (prev_pos < 0) {
+ return prev_pos;
+ }
+ int retval = prev_pos;
- if (retval & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) {
- slevel++;
- codegen.alloc_stack(slevel);
- }
+ if (retval & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) {
+ slevel++;
+ codegen.alloc_stack(slevel);
+ }
- Vector<int> setchain;
+ Vector<int> setchain;
- if (assign_property != StringName()) {
- // recover and assign at the end, this allows stuff like
- // position.x+=2.0
- // in Node2D
- setchain.push_back(prev_pos);
- setchain.push_back(codegen.get_name_map_pos(assign_property));
- setchain.push_back(GDScriptFunction::OPCODE_SET_MEMBER);
- }
+ if (assign_property != StringName()) {
+ // recover and assign at the end, this allows stuff like
+ // position.x+=2.0
+ // in Node2D
+ setchain.push_back(prev_pos);
+ setchain.push_back(codegen.get_name_map_pos(assign_property));
+ setchain.push_back(GDScriptFunction::OPCODE_SET_MEMBER);
+ }
- for (List<GDScriptParser::OperatorNode *>::Element *E = chain.back(); E; E = E->prev()) {
- if (E == chain.front()) { //ignore first
- break;
- }
+ for (List<const GDScriptParser::SubscriptNode *>::Element *E = chain.back(); E; E = E->prev()) {
+ if (E == chain.front()) { //ignore first
+ break;
+ }
- bool named = E->get()->op == GDScriptParser::OperatorNode::OP_INDEX_NAMED;
- int key_idx;
+ const GDScriptParser::SubscriptNode *subscript_elem = E->get();
+ int key_idx;
- if (named) {
- key_idx = codegen.get_name_map_pos(static_cast<const GDScriptParser::IdentifierNode *>(E->get()->arguments[1])->name);
- //printf("named key %x\n",key_idx);
+ if (subscript_elem->is_attribute) {
+ key_idx = codegen.get_name_map_pos(subscript_elem->attribute->name);
+ //printf("named key %x\n",key_idx);
- } else {
- if (prev_pos & (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS)) {
- slevel++;
- codegen.alloc_stack(slevel);
- }
+ } else {
+ if (prev_pos & (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS)) {
+ slevel++;
+ codegen.alloc_stack(slevel);
+ }
- GDScriptParser::Node *key = E->get()->arguments[1];
- key_idx = _parse_expression(codegen, key, slevel);
- //printf("expr key %x\n",key_idx);
+ GDScriptParser::ExpressionNode *key = subscript_elem->index;
+ key_idx = _parse_expression(codegen, key, slevel);
+ //printf("expr key %x\n",key_idx);
- //stack was raised here if retval was stack but..
- }
+ //stack was raised here if retval was stack but..
+ }
- if (key_idx < 0) { //error
- return key_idx;
- }
+ if (key_idx < 0) { //error
+ return key_idx;
+ }
- codegen.opcodes.push_back(named ? GDScriptFunction::OPCODE_GET_NAMED : GDScriptFunction::OPCODE_GET);
- codegen.opcodes.push_back(prev_pos);
- codegen.opcodes.push_back(key_idx);
- slevel++;
- codegen.alloc_stack(slevel);
- int dst_pos = (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) | slevel;
+ codegen.opcodes.push_back(subscript_elem->is_attribute ? GDScriptFunction::OPCODE_GET_NAMED : GDScriptFunction::OPCODE_GET);
+ codegen.opcodes.push_back(prev_pos);
+ codegen.opcodes.push_back(key_idx);
+ slevel++;
+ codegen.alloc_stack(slevel);
+ int dst_pos = (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) | slevel;
- codegen.opcodes.push_back(dst_pos);
+ codegen.opcodes.push_back(dst_pos);
- //add in reverse order, since it will be reverted
+ //add in reverse order, since it will be reverted
- setchain.push_back(dst_pos);
- setchain.push_back(key_idx);
- setchain.push_back(prev_pos);
- setchain.push_back(named ? GDScriptFunction::OPCODE_SET_NAMED : GDScriptFunction::OPCODE_SET);
+ setchain.push_back(dst_pos);
+ setchain.push_back(key_idx);
+ setchain.push_back(prev_pos);
+ setchain.push_back(subscript_elem->is_attribute ? GDScriptFunction::OPCODE_SET_NAMED : GDScriptFunction::OPCODE_SET);
- prev_pos = dst_pos;
- }
+ prev_pos = dst_pos;
+ }
- setchain.invert();
+ setchain.invert();
- int set_index;
- bool named = false;
+ int set_index;
- if (op->op == GDScriptParser::OperatorNode::OP_INDEX_NAMED) {
- set_index = codegen.get_name_map_pos(static_cast<const GDScriptParser::IdentifierNode *>(op->arguments[1])->name);
- named = true;
- } else {
- set_index = _parse_expression(codegen, op->arguments[1], slevel + 1);
- named = false;
- }
+ if (subscript->is_attribute) {
+ set_index = codegen.get_name_map_pos(subscript->attribute->name);
+ } else {
+ set_index = _parse_expression(codegen, subscript->index, slevel + 1);
+ }
- if (set_index < 0) { //error
- return set_index;
- }
+ if (set_index < 0) { //error
+ return set_index;
+ }
- if (set_index & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) {
- slevel++;
- codegen.alloc_stack(slevel);
- }
+ if (set_index & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) {
+ slevel++;
+ codegen.alloc_stack(slevel);
+ }
- int set_value = _parse_assign_right_expression(codegen, on, slevel + 1, named ? 0 : set_index);
- if (set_value < 0) { //error
- return set_value;
- }
+ int set_value = _parse_assign_right_expression(codegen, assignment, slevel + 1, subscript->is_attribute ? 0 : set_index);
+ if (set_value < 0) { //error
+ return set_value;
+ }
- codegen.opcodes.push_back(named ? GDScriptFunction::OPCODE_SET_NAMED : GDScriptFunction::OPCODE_SET);
- codegen.opcodes.push_back(prev_pos);
- codegen.opcodes.push_back(set_index);
- codegen.opcodes.push_back(set_value);
+ codegen.opcodes.push_back(subscript->is_attribute ? GDScriptFunction::OPCODE_SET_NAMED : GDScriptFunction::OPCODE_SET);
+ codegen.opcodes.push_back(prev_pos);
+ codegen.opcodes.push_back(set_index);
+ codegen.opcodes.push_back(set_value);
- for (int i = 0; i < setchain.size(); i++) {
- codegen.opcodes.push_back(setchain[i]);
- }
+ for (int i = 0; i < setchain.size(); i++) {
+ codegen.opcodes.push_back(setchain[i]);
+ }
- return retval;
+ return retval;
- } else if (on->arguments[0]->type == GDScriptParser::Node::TYPE_IDENTIFIER && _is_class_member_property(codegen, static_cast<GDScriptParser::IdentifierNode *>(on->arguments[0])->name)) {
- //assignment to member property
+ } else if (assignment->assignee->type == GDScriptParser::Node::IDENTIFIER && _is_class_member_property(codegen, static_cast<GDScriptParser::IdentifierNode *>(assignment->assignee)->name)) {
+ //assignment to member property
- int slevel = p_stack_level;
+ int slevel = p_stack_level;
- int src_address = _parse_assign_right_expression(codegen, on, slevel);
- if (src_address < 0) {
- return -1;
- }
+ int src_address = _parse_assign_right_expression(codegen, assignment, slevel);
+ if (src_address < 0) {
+ return -1;
+ }
- StringName name = static_cast<GDScriptParser::IdentifierNode *>(on->arguments[0])->name;
+ StringName name = static_cast<GDScriptParser::IdentifierNode *>(assignment->assignee)->name;
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_SET_MEMBER);
- codegen.opcodes.push_back(codegen.get_name_map_pos(name));
- codegen.opcodes.push_back(src_address);
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_SET_MEMBER);
+ codegen.opcodes.push_back(codegen.get_name_map_pos(name));
+ codegen.opcodes.push_back(src_address);
- return GDScriptFunction::ADDR_TYPE_NIL << GDScriptFunction::ADDR_BITS;
- } else {
- //REGULAR ASSIGNMENT MODE!!
+ return GDScriptFunction::ADDR_TYPE_NIL << GDScriptFunction::ADDR_BITS;
+ } else {
+ //REGULAR ASSIGNMENT MODE!!
- int slevel = p_stack_level;
+ int slevel = p_stack_level;
- int dst_address_a = _parse_expression(codegen, on->arguments[0], slevel, false, on->op == GDScriptParser::OperatorNode::OP_INIT_ASSIGN);
- if (dst_address_a < 0) {
- return -1;
- }
+ int dst_address_a = _parse_expression(codegen, assignment->assignee, slevel);
+ if (dst_address_a < 0) {
+ return -1;
+ }
- if (dst_address_a & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) {
- slevel++;
- codegen.alloc_stack(slevel);
- }
+ if (dst_address_a & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) {
+ slevel++;
+ codegen.alloc_stack(slevel);
+ }
- int src_address_b = _parse_assign_right_expression(codegen, on, slevel);
- if (src_address_b < 0) {
- return -1;
- }
+ int src_address_b = _parse_assign_right_expression(codegen, assignment, slevel);
+ if (src_address_b < 0) {
+ return -1;
+ }
- GDScriptDataType assign_type = _gdtype_from_datatype(on->arguments[0]->get_datatype());
-
- if (assign_type.has_type && !on->datatype.has_type) {
- // Typed assignment
- switch (assign_type.kind) {
- case GDScriptDataType::BUILTIN: {
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN); // perform operator
- codegen.opcodes.push_back(assign_type.builtin_type); // variable type
- codegen.opcodes.push_back(dst_address_a); // argument 1
- codegen.opcodes.push_back(src_address_b); // argument 2
- } break;
- case GDScriptDataType::NATIVE: {
- int class_idx;
- if (GDScriptLanguage::get_singleton()->get_global_map().has(assign_type.native_type)) {
- class_idx = GDScriptLanguage::get_singleton()->get_global_map()[assign_type.native_type];
- class_idx |= (GDScriptFunction::ADDR_TYPE_GLOBAL << GDScriptFunction::ADDR_BITS); //argument (stack root)
- } else {
- _set_error("Invalid native class type '" + String(assign_type.native_type) + "'.", on->arguments[0]);
- return -1;
- }
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_TYPED_NATIVE); // perform operator
- codegen.opcodes.push_back(class_idx); // variable type
- codegen.opcodes.push_back(dst_address_a); // argument 1
- codegen.opcodes.push_back(src_address_b); // argument 2
- } break;
- case GDScriptDataType::SCRIPT:
- case GDScriptDataType::GDSCRIPT: {
- Variant script = assign_type.script_type;
- int idx = codegen.get_constant_pos(script);
- idx |= GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS; //make it a local constant (faster access)
-
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_TYPED_SCRIPT); // perform operator
- codegen.opcodes.push_back(idx); // variable type
- codegen.opcodes.push_back(dst_address_a); // argument 1
- codegen.opcodes.push_back(src_address_b); // argument 2
- } break;
- default: {
- ERR_PRINT("Compiler bug: unresolved assign.");
-
- // Shouldn't get here, but fail-safe to a regular assignment
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN); // perform operator
- codegen.opcodes.push_back(dst_address_a); // argument 1
- codegen.opcodes.push_back(src_address_b); // argument 2 (unary only takes one parameter)
- }
+ // FIXME: Actually check type.
+ GDScriptDataType assign_type; // = _gdtype_from_datatype(on->arguments[0]->get_datatype());
+
+ if (assign_type.has_type && !assignment->get_datatype().is_set()) {
+ // Typed assignment
+ switch (assign_type.kind) {
+ case GDScriptDataType::BUILTIN: {
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN); // perform operator
+ codegen.opcodes.push_back(assign_type.builtin_type); // variable type
+ codegen.opcodes.push_back(dst_address_a); // argument 1
+ codegen.opcodes.push_back(src_address_b); // argument 2
+ } break;
+ case GDScriptDataType::NATIVE: {
+ int class_idx;
+ if (GDScriptLanguage::get_singleton()->get_global_map().has(assign_type.native_type)) {
+ class_idx = GDScriptLanguage::get_singleton()->get_global_map()[assign_type.native_type];
+ class_idx |= (GDScriptFunction::ADDR_TYPE_GLOBAL << GDScriptFunction::ADDR_BITS); //argument (stack root)
+ } else {
+ // _set_error("Invalid native class type '" + String(assign_type.native_type) + "'.", on->arguments[0]);
+ return -1;
}
- } else {
- // Either untyped assignment or already type-checked by the parser
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_TYPED_NATIVE); // perform operator
+ codegen.opcodes.push_back(class_idx); // variable type
+ codegen.opcodes.push_back(dst_address_a); // argument 1
+ codegen.opcodes.push_back(src_address_b); // argument 2
+ } break;
+ case GDScriptDataType::SCRIPT:
+ case GDScriptDataType::GDSCRIPT: {
+ Variant script = assign_type.script_type;
+ int idx = codegen.get_constant_pos(script); //make it a local constant (faster access)
+
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_TYPED_SCRIPT); // perform operator
+ codegen.opcodes.push_back(idx); // variable type
+ codegen.opcodes.push_back(dst_address_a); // argument 1
+ codegen.opcodes.push_back(src_address_b); // argument 2
+ } break;
+ default: {
+ ERR_PRINT("Compiler bug: unresolved assign.");
+
+ // Shouldn't get here, but fail-safe to a regular assignment
codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN); // perform operator
codegen.opcodes.push_back(dst_address_a); // argument 1
codegen.opcodes.push_back(src_address_b); // argument 2 (unary only takes one parameter)
}
- return dst_address_a; //if anything, returns wathever was assigned or correct stack position
}
- } break;
- case GDScriptParser::OperatorNode::OP_IS: {
- ERR_FAIL_COND_V(on->arguments.size() != 2, false);
+ } else {
+ // Either untyped assignment or already type-checked by the parser
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN); // perform operator
+ codegen.opcodes.push_back(dst_address_a); // argument 1
+ codegen.opcodes.push_back(src_address_b); // argument 2 (unary only takes one parameter)
+ }
+ return dst_address_a; //if anything, returns wathever was assigned or correct stack position
+ }
+ } break;
+#undef OPERATOR_RETURN
+ //TYPE_TYPE,
+ default: {
+ ERR_FAIL_V_MSG(-1, "Bug in bytecode compiler, unexpected node in parse tree while parsing expression."); //unreachable code
+ } break;
+ }
+}
- int slevel = p_stack_level;
+Error GDScriptCompiler::_parse_match_pattern(CodeGen &codegen, const GDScriptParser::PatternNode *p_pattern, int p_stack_level, int p_value_addr, int p_type_addr, int &r_bound_variables, Vector<int> &r_patch_addresses, Vector<int> &r_block_patch_address) {
+ // TODO: Many "repeated" code here that could be abstracted. This compiler is going away when new VM arrives though, so...
+ switch (p_pattern->pattern_type) {
+ case GDScriptParser::PatternNode::PT_LITERAL: {
+ // Get literal type into constant map.
+ int literal_type_addr = -1;
+ if (!codegen.constant_map.has((int)p_pattern->literal->value.get_type())) {
+ literal_type_addr = codegen.constant_map.size();
+ codegen.constant_map[(int)p_pattern->literal->value.get_type()] = literal_type_addr;
- int src_address_a = _parse_expression(codegen, on->arguments[0], slevel);
- if (src_address_a < 0) {
- return -1;
- }
+ } else {
+ literal_type_addr = codegen.constant_map[(int)p_pattern->literal->value.get_type()];
+ }
+ literal_type_addr |= GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS;
- if (src_address_a & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) {
- slevel++; //uses stack for return, increase stack
- }
+ // Check type equality.
+ int equality_addr = p_stack_level++;
+ equality_addr |= GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS;
+ codegen.alloc_stack(p_stack_level);
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_OPERATOR);
+ codegen.opcodes.push_back(Variant::OP_EQUAL);
+ codegen.opcodes.push_back(p_type_addr);
+ codegen.opcodes.push_back(literal_type_addr);
+ codegen.opcodes.push_back(equality_addr); // Address to result.
+
+ // Jump away if not the same type.
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF_NOT);
+ codegen.opcodes.push_back(equality_addr);
+ r_patch_addresses.push_back(codegen.opcodes.size());
+ codegen.opcodes.push_back(0); // Will be replaced.
+
+ // Get literal.
+ int literal_addr = _parse_expression(codegen, p_pattern->literal, p_stack_level);
+
+ // Check value equality.
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_OPERATOR);
+ codegen.opcodes.push_back(Variant::OP_EQUAL);
+ codegen.opcodes.push_back(p_value_addr);
+ codegen.opcodes.push_back(literal_addr);
+ codegen.opcodes.push_back(equality_addr); // Address to result.
+
+ // Jump away if doesn't match.
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF_NOT);
+ codegen.opcodes.push_back(equality_addr);
+ r_patch_addresses.push_back(codegen.opcodes.size());
+ codegen.opcodes.push_back(0); // Will be replaced.
+
+ // Jump to the actual block since it matches. This is needed to take multi-pattern into account.
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
+ r_block_patch_address.push_back(codegen.opcodes.size());
+ codegen.opcodes.push_back(0); // Will be replaced.
+ } break;
+ case GDScriptParser::PatternNode::PT_EXPRESSION: {
+ // Evaluate expression.
+ int expr_addr = _parse_expression(codegen, p_pattern->expression, p_stack_level);
+ if ((expr_addr >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) {
+ p_stack_level++;
+ codegen.alloc_stack(p_stack_level);
+ }
- int src_address_b = _parse_expression(codegen, on->arguments[1], slevel);
- if (src_address_b < 0) {
- return -1;
- }
+ // Evaluate expression type.
+ int expr_type_addr = p_stack_level++;
+ expr_type_addr |= GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS;
+ codegen.alloc_stack(p_stack_level);
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_CALL_BUILT_IN);
+ codegen.opcodes.push_back(GDScriptFunctions::TYPE_OF);
+ codegen.opcodes.push_back(1); // One argument.
+ codegen.opcodes.push_back(expr_addr); // Argument is the value we want to test.
+ codegen.opcodes.push_back(expr_type_addr); // Address to result.
+
+ // Check type equality.
+ int equality_addr = p_stack_level++;
+ equality_addr |= GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS;
+ codegen.alloc_stack(p_stack_level);
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_OPERATOR);
+ codegen.opcodes.push_back(Variant::OP_EQUAL);
+ codegen.opcodes.push_back(p_type_addr);
+ codegen.opcodes.push_back(expr_type_addr);
+ codegen.opcodes.push_back(equality_addr); // Address to result.
+
+ // Jump away if not the same type.
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF_NOT);
+ codegen.opcodes.push_back(equality_addr);
+ r_patch_addresses.push_back(codegen.opcodes.size());
+ codegen.opcodes.push_back(0); // Will be replaced.
+
+ // Check value equality.
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_OPERATOR);
+ codegen.opcodes.push_back(Variant::OP_EQUAL);
+ codegen.opcodes.push_back(p_value_addr);
+ codegen.opcodes.push_back(expr_addr);
+ codegen.opcodes.push_back(equality_addr); // Address to result.
+
+ // Jump away if doesn't match.
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF_NOT);
+ codegen.opcodes.push_back(equality_addr);
+ r_patch_addresses.push_back(codegen.opcodes.size());
+ codegen.opcodes.push_back(0); // Will be replaced.
+
+ // Jump to the actual block since it matches. This is needed to take multi-pattern into account.
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
+ r_block_patch_address.push_back(codegen.opcodes.size());
+ codegen.opcodes.push_back(0); // Will be replaced.
+ } break;
+ case GDScriptParser::PatternNode::PT_BIND: {
+ // Create new stack variable.
+ int bind_addr = p_stack_level | (GDScriptFunction::ADDR_TYPE_STACK_VARIABLE << GDScriptFunction::ADDR_BITS);
+ codegen.add_stack_identifier(p_pattern->bind->name, p_stack_level++);
+ codegen.alloc_stack(p_stack_level);
+ r_bound_variables++;
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_EXTENDS_TEST); // perform operator
- codegen.opcodes.push_back(src_address_a); // argument 1
- codegen.opcodes.push_back(src_address_b); // argument 2 (unary only takes one parameter)
+ // Assign value to bound variable.
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN);
+ codegen.opcodes.push_back(bind_addr); // Destination.
+ codegen.opcodes.push_back(p_value_addr); // Source.
+ // Not need to block jump because bind happens only once.
+ } break;
+ case GDScriptParser::PatternNode::PT_ARRAY: {
+ int slevel = p_stack_level;
- } 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);
+ // Get array type into constant map.
+ int array_type_addr = codegen.get_constant_pos(Variant::ARRAY);
+
+ // Check type equality.
+ int equality_addr = slevel++;
+ equality_addr |= GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS;
+ codegen.alloc_stack(slevel);
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_OPERATOR);
+ codegen.opcodes.push_back(Variant::OP_EQUAL);
+ codegen.opcodes.push_back(p_type_addr);
+ codegen.opcodes.push_back(array_type_addr);
+ codegen.opcodes.push_back(equality_addr); // Address to result.
+
+ // Jump away if not the same type.
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF_NOT);
+ codegen.opcodes.push_back(equality_addr);
+ r_patch_addresses.push_back(codegen.opcodes.size());
+ codegen.opcodes.push_back(0); // Will be replaced.
+
+ // Store pattern length in constant map.
+ int array_length_addr = codegen.get_constant_pos(p_pattern->rest_used ? p_pattern->array.size() - 1 : p_pattern->array.size());
+
+ // Get value length.
+ int value_length_addr = slevel++;
+ codegen.alloc_stack(slevel);
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_CALL_BUILT_IN);
+ codegen.opcodes.push_back(GDScriptFunctions::LEN);
+ codegen.opcodes.push_back(1); // One argument.
+ codegen.opcodes.push_back(p_value_addr); // Argument is the value we want to test.
+ codegen.opcodes.push_back(value_length_addr); // Address to result.
+
+ // Test length compatibility.
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_OPERATOR);
+ codegen.opcodes.push_back(p_pattern->rest_used ? Variant::OP_GREATER_EQUAL : Variant::OP_EQUAL);
+ codegen.opcodes.push_back(value_length_addr);
+ codegen.opcodes.push_back(array_length_addr);
+ codegen.opcodes.push_back(equality_addr); // Address to result.
+
+ // Jump away if length is not compatible.
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF_NOT);
+ codegen.opcodes.push_back(equality_addr);
+ r_patch_addresses.push_back(codegen.opcodes.size());
+ codegen.opcodes.push_back(0); // Will be replaced.
+
+ // Evaluate element by element.
+ for (int i = 0; i < p_pattern->array.size(); i++) {
+ if (p_pattern->array[i]->pattern_type == GDScriptParser::PatternNode::PT_REST) {
+ // Don't want to access an extra element of the user array.
+ break;
+ }
- int slevel = p_stack_level;
+ int stlevel = p_stack_level;
+ Vector<int> element_block_patches; // I want to internal patterns try the next element instead of going to the block.
+ // Add index to constant map.
+ int index_addr = codegen.get_constant_pos(i);
+
+ // Get the actual element from the user-sent array.
+ int element_addr = stlevel++;
+ codegen.alloc_stack(stlevel);
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_GET);
+ codegen.opcodes.push_back(p_value_addr); // Source.
+ codegen.opcodes.push_back(index_addr); // Index.
+ codegen.opcodes.push_back(element_addr); // Destination.
+
+ // Also get type of element.
+ int element_type_addr = stlevel++;
+ element_type_addr |= GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS;
+ codegen.alloc_stack(stlevel);
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_CALL_BUILT_IN);
+ codegen.opcodes.push_back(GDScriptFunctions::TYPE_OF);
+ codegen.opcodes.push_back(1); // One argument.
+ codegen.opcodes.push_back(element_addr); // Argument is the value we want to test.
+ codegen.opcodes.push_back(element_type_addr); // Address to result.
+
+ // Try the pattern inside the element.
+ Error err = _parse_match_pattern(codegen, p_pattern->array[i], stlevel, element_addr, element_type_addr, r_bound_variables, r_patch_addresses, element_block_patches);
+ if (err != OK) {
+ return err;
+ }
- int src_address_a = _parse_expression(codegen, on->arguments[0], slevel);
- if (src_address_a < 0) {
- return -1;
- }
+ // Patch jumps to block to try the next element.
+ for (int j = 0; j < element_block_patches.size(); j++) {
+ codegen.opcodes.write[element_block_patches[j]] = codegen.opcodes.size();
+ }
+ }
- if (src_address_a & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) {
- slevel++; //uses stack for return, increase stack
- }
+ // Jump to the actual block since it matches. This is needed to take multi-pattern into account.
+ // Also here for the case of empty arrays.
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
+ r_block_patch_address.push_back(codegen.opcodes.size());
+ codegen.opcodes.push_back(0); // Will be replaced.
+ } break;
+ case GDScriptParser::PatternNode::PT_DICTIONARY: {
+ int slevel = p_stack_level;
- const GDScriptParser::TypeNode *tn = static_cast<const GDScriptParser::TypeNode *>(on->arguments[1]);
+ // Get dictionary type into constant map.
+ int dict_type_addr = codegen.get_constant_pos(Variant::DICTIONARY);
+
+ // Check type equality.
+ int equality_addr = slevel++;
+ equality_addr |= GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS;
+ codegen.alloc_stack(slevel);
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_OPERATOR);
+ codegen.opcodes.push_back(Variant::OP_EQUAL);
+ codegen.opcodes.push_back(p_type_addr);
+ codegen.opcodes.push_back(dict_type_addr);
+ codegen.opcodes.push_back(equality_addr); // Address to result.
+
+ // Jump away if not the same type.
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF_NOT);
+ codegen.opcodes.push_back(equality_addr);
+ r_patch_addresses.push_back(codegen.opcodes.size());
+ codegen.opcodes.push_back(0); // Will be replaced.
+
+ // Store pattern length in constant map.
+ int dict_length_addr = codegen.get_constant_pos(p_pattern->rest_used ? p_pattern->dictionary.size() - 1 : p_pattern->dictionary.size());
+
+ // Get user's dictionary length.
+ int value_length_addr = slevel++;
+ codegen.alloc_stack(slevel);
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_CALL_BUILT_IN);
+ codegen.opcodes.push_back(GDScriptFunctions::LEN);
+ codegen.opcodes.push_back(1); // One argument.
+ codegen.opcodes.push_back(p_value_addr); // Argument is the value we want to test.
+ codegen.opcodes.push_back(value_length_addr); // Address to result.
+
+ // Test length compatibility.
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_OPERATOR);
+ codegen.opcodes.push_back(p_pattern->rest_used ? Variant::OP_GREATER_EQUAL : Variant::OP_EQUAL);
+ codegen.opcodes.push_back(value_length_addr);
+ codegen.opcodes.push_back(dict_length_addr);
+ codegen.opcodes.push_back(equality_addr); // Address to result.
+
+ // Jump away if length is not compatible.
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF_NOT);
+ codegen.opcodes.push_back(equality_addr);
+ r_patch_addresses.push_back(codegen.opcodes.size());
+ codegen.opcodes.push_back(0); // Will be replaced.
+
+ // Evaluate element by element.
+ for (int i = 0; i < p_pattern->dictionary.size(); i++) {
+ const GDScriptParser::PatternNode::Pair &element = p_pattern->dictionary[i];
+ if (element.value_pattern->pattern_type == GDScriptParser::PatternNode::PT_REST) {
+ // Ignore rest pattern.
+ continue;
+ }
+ int stlevel = p_stack_level;
+ Vector<int> element_block_patches; // I want to internal patterns try the next element instead of going to the block.
- 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_FAIL_V_MSG(0, "Bug in bytecode compiler, unexpected operator #" + itos(on->op) + " in parse tree while parsing expression."); //unreachable code
+ // Get the pattern key.
+ int pattern_key_addr = _parse_expression(codegen, element.key, stlevel);
+ if (pattern_key_addr < 0) {
+ return ERR_PARSE_ERROR;
+ }
+ if ((pattern_key_addr >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) {
+ stlevel++;
+ codegen.alloc_stack(stlevel);
+ }
- } break;
+ // Create stack slot for test result.
+ int test_result = stlevel++;
+ test_result |= GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS;
+ codegen.alloc_stack(stlevel);
+
+ // Check if pattern key exists in user's dictionary.
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_CALL_RETURN);
+ codegen.opcodes.push_back(1); // Argument count.
+ codegen.opcodes.push_back(p_value_addr); // Base (user dictionary).
+ codegen.opcodes.push_back(codegen.get_name_map_pos("has")); // Function name.
+ codegen.opcodes.push_back(pattern_key_addr); // Argument (pattern key).
+ codegen.opcodes.push_back(test_result); // Return address.
+
+ // Jump away if key doesn't exist.
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF_NOT);
+ codegen.opcodes.push_back(test_result);
+ r_patch_addresses.push_back(codegen.opcodes.size());
+ codegen.opcodes.push_back(0); // Will be replaced.
+
+ // Get actual value from user dictionary.
+ int value_addr = stlevel++;
+ codegen.alloc_stack(stlevel);
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_GET);
+ codegen.opcodes.push_back(p_value_addr); // Source.
+ codegen.opcodes.push_back(pattern_key_addr); // Index.
+ codegen.opcodes.push_back(value_addr); // Destination.
+
+ // Also get type of value.
+ int value_type_addr = stlevel++;
+ value_type_addr |= GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS;
+ codegen.alloc_stack(stlevel);
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_CALL_BUILT_IN);
+ codegen.opcodes.push_back(GDScriptFunctions::TYPE_OF);
+ codegen.opcodes.push_back(1); // One argument.
+ codegen.opcodes.push_back(value_addr); // Argument is the value we want to test.
+ codegen.opcodes.push_back(value_type_addr); // Address to result.
+
+ // Try the pattern inside the value.
+ Error err = _parse_match_pattern(codegen, element.value_pattern, stlevel, value_addr, value_type_addr, r_bound_variables, r_patch_addresses, element_block_patches);
+ if (err != OK) {
+ return err;
+ }
+
+ // Patch jumps to block to try the next element.
+ for (int j = 0; j < element_block_patches.size(); j++) {
+ codegen.opcodes.write[element_block_patches[j]] = codegen.opcodes.size();
+ }
}
- int dst_addr = (p_stack_level) | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
- codegen.opcodes.push_back(dst_addr); // append the stack level as destination address of the opcode
- codegen.alloc_stack(p_stack_level);
- return dst_addr;
- } break;
- //TYPE_TYPE,
- default: {
- ERR_FAIL_V_MSG(-1, "Bug in bytecode compiler, unexpected node in parse tree while parsing expression."); //unreachable code
+ // Jump to the actual block since it matches. This is needed to take multi-pattern into account.
+ // Also here for the case of empty dictionaries.
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
+ r_block_patch_address.push_back(codegen.opcodes.size());
+ codegen.opcodes.push_back(0); // Will be replaced.
+
} break;
+ case GDScriptParser::PatternNode::PT_REST:
+ // Do nothing.
+ break;
+ case GDScriptParser::PatternNode::PT_WILDCARD:
+ // This matches anything so just do the jump.
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
+ r_block_patch_address.push_back(codegen.opcodes.size());
+ codegen.opcodes.push_back(0); // Will be replaced.
}
+ return OK;
}
-Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::BlockNode *p_block, int p_stack_level, int p_break_addr, int p_continue_addr) {
+Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block, int p_stack_level, int p_break_addr, int p_continue_addr) {
codegen.push_stack_identifiers();
int new_identifiers = 0;
- codegen.current_line = p_block->line;
+ codegen.current_line = p_block->start_line;
for (int i = 0; i < p_block->statements.size(); i++) {
const GDScriptParser::Node *s = p_block->statements[i];
- switch (s->type) {
- case GDScriptParser::Node::TYPE_NEWLINE: {
#ifdef DEBUG_ENABLED
- const GDScriptParser::NewLineNode *nl = static_cast<const GDScriptParser::NewLineNode *>(s);
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_LINE);
- codegen.opcodes.push_back(nl->line);
- codegen.current_line = nl->line;
+ // Add a newline before each statement, since the debugger needs those.
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_LINE);
+ codegen.opcodes.push_back(s->start_line);
+ codegen.current_line = s->start_line;
#endif
- } break;
- case GDScriptParser::Node::TYPE_CONTROL_FLOW: {
- // try subblocks
- const GDScriptParser::ControlFlowNode *cf = static_cast<const GDScriptParser::ControlFlowNode *>(s);
+ switch (s->type) {
+ case GDScriptParser::Node::MATCH: {
+ const GDScriptParser::MatchNode *match = static_cast<const GDScriptParser::MatchNode *>(s);
- switch (cf->cf_type) {
- case GDScriptParser::ControlFlowNode::CF_MATCH: {
- GDScriptParser::MatchNode *match = cf->match;
+ int slevel = p_stack_level;
- GDScriptParser::IdentifierNode *id = memnew(GDScriptParser::IdentifierNode);
- id->name = "#match_value";
+ // First, let's save the addres of the value match.
+ int temp_addr = _parse_expression(codegen, match->test, slevel);
+ if (temp_addr < 0) {
+ return ERR_PARSE_ERROR;
+ }
+ if ((temp_addr >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) {
+ slevel++;
+ codegen.alloc_stack(slevel);
+ }
- // var #match_value
- // copied because there is no _parse_statement :(
- codegen.add_stack_identifier(id->name, p_stack_level++);
- codegen.alloc_stack(p_stack_level);
- new_identifiers++;
+ // Then, let's save the type of the value in the stack too, so we can reuse for later comparisons.
+ int type_addr = slevel++;
+ type_addr |= GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS;
+ codegen.alloc_stack(slevel);
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_CALL_BUILT_IN);
+ codegen.opcodes.push_back(GDScriptFunctions::TYPE_OF);
+ codegen.opcodes.push_back(1); // One argument.
+ codegen.opcodes.push_back(temp_addr); // Argument is the value we want to test.
+ codegen.opcodes.push_back(type_addr); // Address to result.
- GDScriptParser::OperatorNode *op = memnew(GDScriptParser::OperatorNode);
- op->op = GDScriptParser::OperatorNode::OP_ASSIGN;
- op->arguments.push_back(id);
- op->arguments.push_back(match->val_to_match);
+ Vector<int> patch_match_end; // Will patch the jump to the end of match.
- int ret = _parse_expression(codegen, op, p_stack_level);
- if (ret < 0) {
- memdelete(id);
- memdelete(op);
- return ERR_PARSE_ERROR;
- }
+ // Now we can actually start testing.
+ // For each branch.
+ for (int j = 0; j < match->branches.size(); j++) {
+ const GDScriptParser::MatchBranchNode *branch = match->branches[j];
- // break address
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
- codegen.opcodes.push_back(codegen.opcodes.size() + 3);
- int break_addr = codegen.opcodes.size();
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
- codegen.opcodes.push_back(0); // break addr
-
- for (int j = 0; j < match->compiled_pattern_branches.size(); j++) {
- GDScriptParser::MatchNode::CompiledPatternBranch branch = match->compiled_pattern_branches[j];
-
- // jump over continue
- // jump unconditionally
- // continue address
- // compile the condition
- int ret2 = _parse_expression(codegen, branch.compiled_pattern, p_stack_level);
- if (ret2 < 0) {
- memdelete(id);
- memdelete(op);
- return ERR_PARSE_ERROR;
- }
+ int bound_variables = 0;
+ codegen.push_stack_identifiers(); // Create an extra block around for binds.
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF);
- codegen.opcodes.push_back(ret2);
- codegen.opcodes.push_back(codegen.opcodes.size() + 3);
- int continue_addr = codegen.opcodes.size();
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
- codegen.opcodes.push_back(0);
-
- Error err = _parse_block(codegen, branch.body, p_stack_level, p_break_addr, continue_addr);
- if (err) {
- memdelete(id);
- memdelete(op);
- return ERR_PARSE_ERROR;
+#ifdef DEBUG_ENABLED
+ // Add a newline before each branch, since the debugger needs those.
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_LINE);
+ codegen.opcodes.push_back(s->start_line);
+ codegen.current_line = s->start_line;
+#endif
+ Vector<int> patch_addrs; // Will patch with end of pattern to jump.
+ Vector<int> block_patch_addrs; // Will patch with start of block to jump.
+
+ // For each pattern in branch.
+ for (int k = 0; k < branch->patterns.size(); k++) {
+ if (k > 0) {
+ // Patch jumps per pattern to allow for multipattern. If a pattern fails it just tries the next.
+ for (int l = 0; l < patch_addrs.size(); l++) {
+ codegen.opcodes.write[patch_addrs[l]] = codegen.opcodes.size();
}
-
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
- codegen.opcodes.push_back(break_addr);
-
- codegen.opcodes.write[continue_addr + 1] = codegen.opcodes.size();
+ patch_addrs.clear();
+ }
+ Error err = _parse_match_pattern(codegen, branch->patterns[k], slevel, temp_addr, type_addr, bound_variables, patch_addrs, block_patch_addrs);
+ if (err != OK) {
+ return err;
}
+ }
+ // Patch jumps to the block.
+ for (int k = 0; k < block_patch_addrs.size(); k++) {
+ codegen.opcodes.write[block_patch_addrs[k]] = codegen.opcodes.size();
+ }
- codegen.opcodes.write[break_addr + 1] = codegen.opcodes.size();
+ // Leave space for bound variables.
+ slevel += bound_variables;
+ codegen.alloc_stack(slevel);
- memdelete(id);
- memdelete(op);
+ // Parse the branch block.
+ _parse_block(codegen, branch->block, slevel, p_break_addr, p_continue_addr);
- } break;
+ // Jump to end of match.
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
+ patch_match_end.push_back(codegen.opcodes.size());
+ codegen.opcodes.push_back(0); // Will be patched.
- case GDScriptParser::ControlFlowNode::CF_IF: {
- int ret2 = _parse_expression(codegen, cf->arguments[0], p_stack_level, false);
- if (ret2 < 0) {
- return ERR_PARSE_ERROR;
- }
+ // Patch the addresses of last pattern to jump to the end of the branch, into the next one.
+ for (int k = 0; k < patch_addrs.size(); k++) {
+ codegen.opcodes.write[patch_addrs[k]] = codegen.opcodes.size();
+ }
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF_NOT);
- codegen.opcodes.push_back(ret2);
- int else_addr = codegen.opcodes.size();
- codegen.opcodes.push_back(0); //temporary
+ codegen.pop_stack_identifiers(); // Get out of extra block.
+ }
+ // Patch the addresses to jump to the end of the match statement.
+ for (int j = 0; j < patch_match_end.size(); j++) {
+ codegen.opcodes.write[patch_match_end[j]] = codegen.opcodes.size();
+ }
+ } break;
- Error err = _parse_block(codegen, cf->body, p_stack_level, p_break_addr, p_continue_addr);
- if (err) {
- return err;
- }
+ case GDScriptParser::Node::IF: {
+ const GDScriptParser::IfNode *if_n = static_cast<const GDScriptParser::IfNode *>(s);
+ int ret2 = _parse_expression(codegen, if_n->condition, p_stack_level, false);
+ if (ret2 < 0) {
+ return ERR_PARSE_ERROR;
+ }
- if (cf->body_else) {
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
- int end_addr = codegen.opcodes.size();
- codegen.opcodes.push_back(0);
- codegen.opcodes.write[else_addr] = codegen.opcodes.size();
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF_NOT);
+ codegen.opcodes.push_back(ret2);
+ int else_addr = codegen.opcodes.size();
+ codegen.opcodes.push_back(0); //temporary
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_LINE);
- codegen.opcodes.push_back(cf->body_else->line);
- codegen.current_line = cf->body_else->line;
+ Error err = _parse_block(codegen, if_n->true_block, p_stack_level, p_break_addr, p_continue_addr);
+ if (err) {
+ return err;
+ }
- Error err2 = _parse_block(codegen, cf->body_else, p_stack_level, p_break_addr, p_continue_addr);
- if (err2) {
- return err2;
- }
+ if (if_n->false_block) {
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
+ int end_addr = codegen.opcodes.size();
+ codegen.opcodes.push_back(0);
+ codegen.opcodes.write[else_addr] = codegen.opcodes.size();
- codegen.opcodes.write[end_addr] = codegen.opcodes.size();
- } else {
- //end without else
- codegen.opcodes.write[else_addr] = codegen.opcodes.size();
- }
+ Error err2 = _parse_block(codegen, if_n->false_block, p_stack_level, p_break_addr, p_continue_addr);
+ if (err2) {
+ return err2;
+ }
- } break;
- case GDScriptParser::ControlFlowNode::CF_FOR: {
- int slevel = p_stack_level;
- int iter_stack_pos = slevel;
- int iterator_pos = (slevel++) | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
- int counter_pos = (slevel++) | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
- int container_pos = (slevel++) | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
- codegen.alloc_stack(slevel);
+ codegen.opcodes.write[end_addr] = codegen.opcodes.size();
+ } else {
+ //end without else
+ codegen.opcodes.write[else_addr] = codegen.opcodes.size();
+ }
- codegen.push_stack_identifiers();
- codegen.add_stack_identifier(static_cast<const GDScriptParser::IdentifierNode *>(cf->arguments[0])->name, iter_stack_pos);
+ } break;
+ case GDScriptParser::Node::FOR: {
+ const GDScriptParser::ForNode *for_n = static_cast<const GDScriptParser::ForNode *>(s);
+ int slevel = p_stack_level;
+ int iter_stack_pos = slevel;
+ int iterator_pos = (slevel++) | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
+ int counter_pos = (slevel++) | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
+ int container_pos = (slevel++) | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
+ codegen.alloc_stack(slevel);
- int ret2 = _parse_expression(codegen, cf->arguments[1], slevel, false);
- if (ret2 < 0) {
- return ERR_COMPILATION_FAILED;
- }
+ codegen.push_stack_identifiers();
+ codegen.add_stack_identifier(for_n->variable->name, iter_stack_pos);
- //assign container
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN);
- codegen.opcodes.push_back(container_pos);
- codegen.opcodes.push_back(ret2);
-
- //begin loop
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_ITERATE_BEGIN);
- codegen.opcodes.push_back(counter_pos);
- codegen.opcodes.push_back(container_pos);
- codegen.opcodes.push_back(codegen.opcodes.size() + 4);
- codegen.opcodes.push_back(iterator_pos);
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP); //skip code for next
- codegen.opcodes.push_back(codegen.opcodes.size() + 8);
- //break loop
- int break_pos = codegen.opcodes.size();
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP); //skip code for next
- codegen.opcodes.push_back(0); //skip code for next
- //next loop
- int continue_pos = codegen.opcodes.size();
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_ITERATE);
- codegen.opcodes.push_back(counter_pos);
- codegen.opcodes.push_back(container_pos);
- codegen.opcodes.push_back(break_pos);
- codegen.opcodes.push_back(iterator_pos);
-
- Error err = _parse_block(codegen, cf->body, slevel, break_pos, continue_pos);
- if (err) {
- return err;
- }
+ int ret2 = _parse_expression(codegen, for_n->list, slevel, false);
+ if (ret2 < 0) {
+ return ERR_COMPILATION_FAILED;
+ }
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
- codegen.opcodes.push_back(continue_pos);
- codegen.opcodes.write[break_pos + 1] = codegen.opcodes.size();
+ //assign container
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN);
+ codegen.opcodes.push_back(container_pos);
+ codegen.opcodes.push_back(ret2);
- codegen.pop_stack_identifiers();
+ //begin loop
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_ITERATE_BEGIN);
+ codegen.opcodes.push_back(counter_pos);
+ codegen.opcodes.push_back(container_pos);
+ codegen.opcodes.push_back(codegen.opcodes.size() + 4);
+ codegen.opcodes.push_back(iterator_pos);
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP); //skip code for next
+ codegen.opcodes.push_back(codegen.opcodes.size() + 8);
+ //break loop
+ int break_pos = codegen.opcodes.size();
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP); //skip code for next
+ codegen.opcodes.push_back(0); //skip code for next
+ //next loop
+ int continue_pos = codegen.opcodes.size();
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_ITERATE);
+ codegen.opcodes.push_back(counter_pos);
+ codegen.opcodes.push_back(container_pos);
+ codegen.opcodes.push_back(break_pos);
+ codegen.opcodes.push_back(iterator_pos);
+
+ Error err = _parse_block(codegen, for_n->loop, slevel, break_pos, continue_pos);
+ if (err) {
+ return err;
+ }
- } break;
- case GDScriptParser::ControlFlowNode::CF_WHILE: {
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
- codegen.opcodes.push_back(codegen.opcodes.size() + 3);
- int break_addr = codegen.opcodes.size();
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
- codegen.opcodes.push_back(0);
- int continue_addr = codegen.opcodes.size();
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
+ codegen.opcodes.push_back(continue_pos);
+ codegen.opcodes.write[break_pos + 1] = codegen.opcodes.size();
- int ret2 = _parse_expression(codegen, cf->arguments[0], p_stack_level, false);
- if (ret2 < 0) {
- return ERR_PARSE_ERROR;
- }
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF_NOT);
- codegen.opcodes.push_back(ret2);
- codegen.opcodes.push_back(break_addr);
- Error err = _parse_block(codegen, cf->body, p_stack_level, break_addr, continue_addr);
- if (err) {
- return err;
- }
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
- codegen.opcodes.push_back(continue_addr);
+ codegen.pop_stack_identifiers();
- codegen.opcodes.write[break_addr + 1] = codegen.opcodes.size();
+ } break;
+ case GDScriptParser::Node::WHILE: {
+ const GDScriptParser::WhileNode *while_n = static_cast<const GDScriptParser::WhileNode *>(s);
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
+ codegen.opcodes.push_back(codegen.opcodes.size() + 3);
+ int break_addr = codegen.opcodes.size();
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
+ codegen.opcodes.push_back(0);
+ int continue_addr = codegen.opcodes.size();
+
+ int ret2 = _parse_expression(codegen, while_n->condition, p_stack_level, false);
+ if (ret2 < 0) {
+ return ERR_PARSE_ERROR;
+ }
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF_NOT);
+ codegen.opcodes.push_back(ret2);
+ codegen.opcodes.push_back(break_addr);
+ Error err = _parse_block(codegen, while_n->loop, p_stack_level, break_addr, continue_addr);
+ if (err) {
+ return err;
+ }
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
+ codegen.opcodes.push_back(continue_addr);
- } break;
- case GDScriptParser::ControlFlowNode::CF_BREAK: {
- if (p_break_addr < 0) {
- _set_error("'break'' not within loop", cf);
- return ERR_COMPILATION_FAILED;
- }
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
- codegen.opcodes.push_back(p_break_addr);
-
- } break;
- case GDScriptParser::ControlFlowNode::CF_CONTINUE: {
- if (p_continue_addr < 0) {
- _set_error("'continue' not within loop", cf);
- return ERR_COMPILATION_FAILED;
- }
+ codegen.opcodes.write[break_addr + 1] = codegen.opcodes.size();
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
- codegen.opcodes.push_back(p_continue_addr);
+ } break;
+ case GDScriptParser::Node::BREAK: {
+ if (p_break_addr < 0) {
+ _set_error("'break'' not within loop", s);
+ return ERR_COMPILATION_FAILED;
+ }
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
+ codegen.opcodes.push_back(p_break_addr);
- } break;
- case GDScriptParser::ControlFlowNode::CF_RETURN: {
- int ret2;
+ } break;
+ case GDScriptParser::Node::CONTINUE: {
+ if (p_continue_addr < 0) {
+ _set_error("'continue' not within loop", s);
+ return ERR_COMPILATION_FAILED;
+ }
- if (cf->arguments.size()) {
- ret2 = _parse_expression(codegen, cf->arguments[0], p_stack_level, false);
- if (ret2 < 0) {
- return ERR_PARSE_ERROR;
- }
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
+ codegen.opcodes.push_back(p_continue_addr);
- } else {
- ret2 = GDScriptFunction::ADDR_TYPE_NIL << GDScriptFunction::ADDR_BITS;
- }
+ } break;
+ case GDScriptParser::Node::RETURN: {
+ const GDScriptParser::ReturnNode *return_n = static_cast<const GDScriptParser::ReturnNode *>(s);
+ int ret2;
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_RETURN);
- codegen.opcodes.push_back(ret2);
+ if (return_n->return_value != nullptr) {
+ ret2 = _parse_expression(codegen, return_n->return_value, p_stack_level, false);
+ if (ret2 < 0) {
+ return ERR_PARSE_ERROR;
+ }
- } break;
+ } else {
+ ret2 = GDScriptFunction::ADDR_TYPE_NIL << GDScriptFunction::ADDR_BITS;
}
+
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_RETURN);
+ codegen.opcodes.push_back(ret2);
+
} break;
- case GDScriptParser::Node::TYPE_ASSERT: {
+ case GDScriptParser::Node::ASSERT: {
#ifdef DEBUG_ENABLED
// try subblocks
@@ -1578,14 +1968,14 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo
codegen.opcodes.push_back(message_ret);
#endif
} break;
- case GDScriptParser::Node::TYPE_BREAKPOINT: {
+ case GDScriptParser::Node::BREAKPOINT: {
#ifdef DEBUG_ENABLED
// try subblocks
codegen.opcodes.push_back(GDScriptFunction::OPCODE_BREAKPOINT);
#endif
} break;
- case GDScriptParser::Node::TYPE_LOCAL_VAR: {
- const GDScriptParser::LocalVarNode *lv = static_cast<const GDScriptParser::LocalVarNode *>(s);
+ case GDScriptParser::Node::VARIABLE: {
+ const GDScriptParser::VariableNode *lv = static_cast<const GDScriptParser::VariableNode *>(s);
// since we are using properties now for most class access, allow shadowing of class members to make user's life easier.
//
@@ -1594,20 +1984,50 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo
// return ERR_ALREADY_EXISTS;
//}
- codegen.add_stack_identifier(lv->name, p_stack_level++);
+ codegen.add_stack_identifier(lv->identifier->name, p_stack_level++);
codegen.alloc_stack(p_stack_level);
new_identifiers++;
+ if (lv->initializer != nullptr) {
+ int dst_address = codegen.stack_identifiers[lv->identifier->name];
+ dst_address |= GDScriptFunction::ADDR_TYPE_STACK_VARIABLE << GDScriptFunction::ADDR_BITS;
+
+ int src_address = _parse_expression(codegen, lv->initializer, p_stack_level);
+ if (src_address < 0) {
+ return ERR_PARSE_ERROR;
+ }
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN);
+ codegen.opcodes.push_back(dst_address);
+ codegen.opcodes.push_back(src_address);
+ }
+ } break;
+ case GDScriptParser::Node::CONSTANT: {
+ // Local constants.
+ const GDScriptParser::ConstantNode *lc = static_cast<const GDScriptParser::ConstantNode *>(s);
+ // FIXME: Need to properly reduce expressions to avoid limiting this so much.
+ if (lc->initializer->type != GDScriptParser::Node::LITERAL) {
+ _set_error("Local constant must have a literal as initializer.", lc->initializer);
+ return ERR_PARSE_ERROR;
+ }
+ codegen.local_named_constants[lc->identifier->name] = codegen.get_constant_pos(static_cast<const GDScriptParser::LiteralNode *>(lc->initializer)->value);
} break;
+ case GDScriptParser::Node::PASS:
+ // Nothing to do.
+ break;
default: {
//expression
- int ret2 = _parse_expression(codegen, s, p_stack_level, true);
- if (ret2 < 0) {
- return ERR_PARSE_ERROR;
+ if (s->is_expression()) {
+ int ret2 = _parse_expression(codegen, static_cast<const GDScriptParser::ExpressionNode *>(s), p_stack_level, true);
+ if (ret2 < 0) {
+ return ERR_PARSE_ERROR;
+ }
+ } else {
+ ERR_FAIL_V_MSG(ERR_INVALID_DATA, "Bug in bytecode compiler, unexpected node in parse tree while parsing statement."); //unreachable code
}
} break;
}
}
+
codegen.pop_stack_identifiers();
return OK;
}
@@ -1626,9 +2046,10 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
Vector<StringName> argnames;
int stack_level = 0;
+ int optional_parameters = 0;
if (p_func) {
- for (int i = 0; i < p_func->arguments.size(); i++) {
+ for (int i = 0; i < p_func->parameters.size(); i++) {
// since we are using properties now for most class access, allow shadowing of class members to make user's life easier.
//
//if (_is_class_member_property(p_script, p_func->arguments[i])) {
@@ -1636,42 +2057,81 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
// return ERR_ALREADY_EXISTS;
//}
- codegen.add_stack_identifier(p_func->arguments[i], i);
+ codegen.add_stack_identifier(p_func->parameters[i]->identifier->name, i);
#ifdef TOOLS_ENABLED
- argnames.push_back(p_func->arguments[i]);
+ argnames.push_back(p_func->parameters[i]->identifier->name);
#endif
+ if (p_func->parameters[i]->default_value != nullptr) {
+ optional_parameters++;
+ }
}
- stack_level = p_func->arguments.size();
+ stack_level = p_func->parameters.size();
}
codegen.alloc_stack(stack_level);
/* Parse initializer -if applies- */
- bool is_initializer = !p_for_ready && !p_func;
+ bool is_implicit_initializer = !p_for_ready && !p_func;
+ bool is_initializer = p_func && String(p_func->identifier->name) == GDScriptLanguage::get_singleton()->strings._init;
- if (is_initializer || (p_func && String(p_func->name) == "_init")) {
- //parse initializer for class members
- if (!p_func && p_class->extends_used && p_script->native.is_null()) {
- //call implicit parent constructor
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_CALL_SELF_BASE);
- codegen.opcodes.push_back(codegen.get_name_map_pos("_init"));
- codegen.opcodes.push_back(0);
- codegen.opcodes.push_back((GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) | 0);
- }
- Error err = _parse_block(codegen, p_class->initializer, stack_level);
- if (err) {
- return err;
+ if (is_implicit_initializer) {
+ // Initialize class fields.
+ for (int i = 0; i < p_class->members.size(); i++) {
+ if (p_class->members[i].type != GDScriptParser::ClassNode::Member::VARIABLE) {
+ continue;
+ }
+ const GDScriptParser::VariableNode *field = p_class->members[i].variable;
+ if (field->onready) {
+ // Only initialize in _ready.
+ continue;
+ }
+
+ if (field->initializer) {
+ // Emit proper line change.
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_LINE);
+ codegen.opcodes.push_back(field->initializer->start_line);
+
+ int src_address = _parse_expression(codegen, field->initializer, stack_level, false, true);
+ if (src_address < 0) {
+ return ERR_PARSE_ERROR;
+ }
+ int dst_address = codegen.script->member_indices[field->identifier->name].index;
+ dst_address |= GDScriptFunction::ADDR_TYPE_MEMBER << GDScriptFunction::ADDR_BITS;
+
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN);
+ codegen.opcodes.push_back(dst_address);
+ codegen.opcodes.push_back(src_address);
+ }
}
- is_initializer = true;
}
- if (p_for_ready || (p_func && String(p_func->name) == "_ready")) {
- //parse initializer for class members
- if (p_class->ready->statements.size()) {
- Error err = _parse_block(codegen, p_class->ready, stack_level);
- if (err) {
- return err;
+ if (p_for_ready || (p_func && String(p_func->identifier->name) == "_ready")) {
+ // Initialize class fields on ready.
+ for (int i = 0; i < p_class->members.size(); i++) {
+ if (p_class->members[i].type != GDScriptParser::ClassNode::Member::VARIABLE) {
+ continue;
+ }
+ const GDScriptParser::VariableNode *field = p_class->members[i].variable;
+ if (!field->onready) {
+ continue;
+ }
+
+ if (field->initializer) {
+ // Emit proper line change.
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_LINE);
+ codegen.opcodes.push_back(field->initializer->start_line);
+
+ int src_address = _parse_expression(codegen, field->initializer, stack_level, false, true);
+ if (src_address < 0) {
+ return ERR_PARSE_ERROR;
+ }
+ int dst_address = codegen.script->member_indices[field->identifier->name].index;
+ dst_address |= GDScriptFunction::ADDR_TYPE_MEMBER << GDScriptFunction::ADDR_BITS;
+
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN);
+ codegen.opcodes.push_back(dst_address);
+ codegen.opcodes.push_back(src_address);
}
}
}
@@ -1682,14 +2142,19 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
StringName func_name;
if (p_func) {
- if (p_func->default_values.size()) {
+ if (optional_parameters > 0) {
codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_TO_DEF_ARGUMENT);
defarg_addr.push_back(codegen.opcodes.size());
- for (int i = 0; i < p_func->default_values.size(); i++) {
- _parse_expression(codegen, p_func->default_values[i], stack_level, true);
+ for (int i = p_func->parameters.size() - optional_parameters; i < p_func->parameters.size(); i++) {
+ int src_addr = _parse_expression(codegen, p_func->parameters[i]->default_value, stack_level, true);
+ if (src_addr < 0) {
+ return ERR_PARSE_ERROR;
+ }
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN);
+ codegen.opcodes.push_back(codegen.stack_identifiers[p_func->parameters[i]->identifier->name] | (GDScriptFunction::ADDR_TYPE_STACK_VARIABLE << GDScriptFunction::ADDR_BITS));
+ codegen.opcodes.push_back(src_addr);
defarg_addr.push_back(codegen.opcodes.size());
}
-
defarg_addr.invert();
}
@@ -1698,12 +2163,12 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
return err;
}
- func_name = p_func->name;
+ func_name = p_func->identifier->name;
} else {
if (p_for_ready) {
func_name = "_ready";
} else {
- func_name = "_init";
+ func_name = "@implicit_new";
}
}
@@ -1719,13 +2184,14 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
//}
if (p_func) {
- gdfunc->_static = p_func->_static;
+ gdfunc->_static = p_func->is_static;
gdfunc->rpc_mode = p_func->rpc_mode;
- gdfunc->argument_types.resize(p_func->argument_types.size());
- for (int i = 0; i < p_func->argument_types.size(); i++) {
- gdfunc->argument_types.write[i] = _gdtype_from_datatype(p_func->argument_types[i]);
+ // FIXME: Add actual parameters types;
+ gdfunc->argument_types.resize(p_func->parameters.size());
+ for (int i = 0; i < p_func->parameters.size(); i++) {
+ gdfunc->argument_types.write[i] = GDScriptDataType(); //_gdtype_from_datatype(p_func->argument_types[i]);
}
- gdfunc->return_type = _gdtype_from_datatype(p_func->return_type);
+ gdfunc->return_type = GDScriptDataType(); //_gdtype_from_datatype(p_func->return_type);
} else {
gdfunc->_static = false;
gdfunc->rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
@@ -1797,7 +2263,7 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
gdfunc->_default_arg_ptr = nullptr;
}
- gdfunc->_argument_count = p_func ? p_func->arguments.size() : 0;
+ gdfunc->_argument_count = p_func ? p_func->parameters.size() : 0;
gdfunc->_stack_size = codegen.stack_max;
gdfunc->_call_size = codegen.call_max;
gdfunc->name = func_name;
@@ -1810,15 +2276,15 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
}
//loc
if (p_func) {
- signature += "::" + itos(p_func->body->line);
+ signature += "::" + itos(p_func->body->start_line);
} else {
signature += "::0";
}
//function and class
- if (p_class->name) {
- signature += "::" + String(p_class->name) + "." + String(func_name);
+ if (p_class->identifier) {
+ signature += "::" + String(p_class->identifier->name) + "." + String(func_name);
} else {
signature += "::" + String(func_name);
}
@@ -1838,10 +2304,10 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
#endif
if (p_func) {
- gdfunc->_initial_line = p_func->line;
+ gdfunc->_initial_line = p_func->start_line;
#ifdef TOOLS_ENABLED
- p_script->member_lines[func_name] = p_func->line;
+ p_script->member_lines[func_name] = p_func->start_line;
#endif
} else {
gdfunc->_initial_line = 0;
@@ -1854,6 +2320,9 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
if (is_initializer) {
p_script->initializer = gdfunc;
}
+ if (is_implicit_initializer) {
+ p_script->implicit_initializer = gdfunc;
+ }
return OK;
}
@@ -1861,14 +2330,14 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
parsing_classes.insert(p_script);
- if (p_class->owner && p_class->owner->owner) {
+ if (p_class->outer && p_class->outer->outer) {
// Owner is not root
if (!parsed_classes.has(p_script->_owner)) {
if (parsing_classes.has(p_script->_owner)) {
- _set_error("Cyclic class reference for '" + String(p_class->name) + "'.", p_class);
+ _set_error("Cyclic class reference for '" + String(p_class->identifier->name) + "'.", p_class);
return ERR_PARSE_ERROR;
}
- Error err = _parse_class_level(p_script->_owner, p_class->owner, p_keep_state);
+ Error err = _parse_class_level(p_script->_owner, p_class->outer, p_keep_state);
if (err) {
return err;
}
@@ -1889,8 +2358,8 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
p_script->_signals.clear();
p_script->initializer = nullptr;
- p_script->tool = p_class->tool;
- p_script->name = p_class->name;
+ p_script->tool = parser->is_tool();
+ p_script->name = p_class->identifier ? p_class->identifier->name : "";
Ref<GDScriptNativeClass> native;
@@ -1909,13 +2378,14 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
p_script->_base = base.ptr();
p_script->member_indices = base->member_indices;
- if (p_class->base_type.kind == GDScriptParser::DataType::CLASS) {
+ if (p_class->base_type.kind == GDScriptParser::DataType::CLASS && p_class->base_type.gdscript_type != nullptr) {
if (!parsed_classes.has(p_script->_base)) {
if (parsing_classes.has(p_script->_base)) {
- _set_error("Cyclic class reference for '" + String(p_class->name) + "'.", p_class);
+ String class_name = p_class->identifier ? p_class->identifier->name : "<main>";
+ _set_error("Cyclic class reference for '" + class_name + "'.", p_class);
return ERR_PARSE_ERROR;
}
- Error err = _parse_class_level(p_script->_base, p_class->base_type.class_type, p_keep_state);
+ Error err = _parse_class_level(p_script->_base, p_class->base_type.gdscript_type, p_keep_state);
if (err) {
return err;
}
@@ -1928,86 +2398,130 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
} break;
}
- for (int i = 0; i < p_class->variables.size(); i++) {
- StringName name = p_class->variables[i].identifier;
-
- GDScript::MemberInfo minfo;
- minfo.index = p_script->member_indices.size();
- minfo.setter = p_class->variables[i].setter;
- minfo.getter = p_class->variables[i].getter;
- minfo.rpc_mode = p_class->variables[i].rpc_mode;
- minfo.data_type = _gdtype_from_datatype(p_class->variables[i].data_type);
-
- PropertyInfo prop_info = minfo.data_type;
- prop_info.name = name;
- PropertyInfo export_info = p_class->variables[i]._export;
-
- if (export_info.type != Variant::NIL) {
- if (!minfo.data_type.has_type) {
- prop_info.type = export_info.type;
- prop_info.class_name = export_info.class_name;
- }
- prop_info.hint = export_info.hint;
- prop_info.hint_string = export_info.hint_string;
- prop_info.usage = export_info.usage;
+ for (int i = 0; i < p_class->members.size(); i++) {
+ const GDScriptParser::ClassNode::Member &member = p_class->members[i];
+ switch (member.type) {
+ case GDScriptParser::ClassNode::Member::VARIABLE: {
+ const GDScriptParser::VariableNode *variable = member.variable;
+ StringName name = variable->identifier->name;
+
+ GDScript::MemberInfo minfo;
+ minfo.index = p_script->member_indices.size();
+ // FIXME: Getter and setter.
+ // minfo.setter = p_class->variables[i].setter;
+ // minfo.getter = p_class->variables[i].getter;
+ minfo.rpc_mode = variable->rpc_mode;
+ // FIXME: Types.
+ // minfo.data_type = _gdtype_from_datatype(p_class->variables[i].data_type);
+
+ PropertyInfo prop_info = minfo.data_type;
+ prop_info.name = name;
+ PropertyInfo export_info = variable->export_info;
+
+ if (variable->exported) {
+ if (!minfo.data_type.has_type) {
+ prop_info.type = export_info.type;
+ prop_info.class_name = export_info.class_name;
+ }
+ prop_info.hint = export_info.hint;
+ prop_info.hint_string = export_info.hint_string;
+ prop_info.usage = export_info.usage;
#ifdef TOOLS_ENABLED
- if (p_class->variables[i].default_value.get_type() != Variant::NIL) {
- p_script->member_default_values[name] = p_class->variables[i].default_value;
- }
+ if (variable->initializer != nullptr && variable->initializer->type == GDScriptParser::Node::LITERAL) {
+ p_script->member_default_values[name] = static_cast<const GDScriptParser::LiteralNode *>(variable->initializer)->value;
+ }
#endif
- } else {
- prop_info.usage = PROPERTY_USAGE_SCRIPT_VARIABLE;
- }
+ } else {
+ prop_info.usage = PROPERTY_USAGE_SCRIPT_VARIABLE;
+ }
- p_script->member_info[name] = prop_info;
- p_script->member_indices[name] = minfo;
- p_script->members.insert(name);
+ p_script->member_info[name] = prop_info;
+ p_script->member_indices[name] = minfo;
+ p_script->members.insert(name);
#ifdef TOOLS_ENABLED
- p_script->member_lines[name] = p_class->variables[i].line;
+ p_script->member_lines[name] = variable->start_line;
#endif
- }
+ } break;
- for (Map<StringName, GDScriptParser::ClassNode::Constant>::Element *E = p_class->constant_expressions.front(); E; E = E->next()) {
- StringName name = E->key();
+ case GDScriptParser::ClassNode::Member::CONSTANT: {
+ const GDScriptParser::ConstantNode *constant = member.constant;
+ StringName name = constant->identifier->name;
- ERR_CONTINUE(E->get().expression->type != GDScriptParser::Node::TYPE_CONSTANT);
+ ERR_CONTINUE(constant->initializer->type != GDScriptParser::Node::LITERAL);
- GDScriptParser::ConstantNode *constant = static_cast<GDScriptParser::ConstantNode *>(E->get().expression);
+ const GDScriptParser::LiteralNode *literal = static_cast<const GDScriptParser::LiteralNode *>(constant->initializer);
- p_script->constants.insert(name, constant->value);
+ p_script->constants.insert(name, literal->value);
#ifdef TOOLS_ENABLED
- p_script->member_lines[name] = E->get().expression->line;
+ p_script->member_lines[name] = constant->start_line;
#endif
- }
+ } break;
- for (int i = 0; i < p_class->_signals.size(); i++) {
- StringName name = p_class->_signals[i].name;
+ case GDScriptParser::ClassNode::Member::ENUM_VALUE: {
+ const GDScriptParser::EnumNode::Value &enum_value = member.enum_value;
+ StringName name = enum_value.identifier->name;
- GDScript *c = p_script;
+ p_script->constants.insert(name, enum_value.value);
+#ifdef TOOLS_ENABLED
+ p_script->member_lines[name] = enum_value.identifier->start_line;
+#endif
+ } break;
- while (c) {
- if (c->_signals.has(name)) {
- _set_error("Signal '" + name + "' redefined (in current or parent class)", p_class);
- return ERR_ALREADY_EXISTS;
- }
+ case GDScriptParser::ClassNode::Member::SIGNAL: {
+ const GDScriptParser::SignalNode *signal = member.signal;
+ StringName name = signal->identifier->name;
- if (c->base.is_valid()) {
- c = c->base.ptr();
- } else {
- c = nullptr;
- }
- }
+ GDScript *c = p_script;
- if (native.is_valid()) {
- if (ClassDB::has_signal(native->get_name(), name)) {
- _set_error("Signal '" + name + "' redefined (original in native class '" + String(native->get_name()) + "')", p_class);
- return ERR_ALREADY_EXISTS;
- }
- }
+ while (c) {
+ if (c->_signals.has(name)) {
+ _set_error("Signal '" + name + "' redefined (in current or parent class)", p_class);
+ return ERR_ALREADY_EXISTS;
+ }
+
+ if (c->base.is_valid()) {
+ c = c->base.ptr();
+ } else {
+ c = nullptr;
+ }
+ }
+
+ if (native.is_valid()) {
+ if (ClassDB::has_signal(native->get_name(), name)) {
+ _set_error("Signal '" + name + "' redefined (original in native class '" + String(native->get_name()) + "')", p_class);
+ return ERR_ALREADY_EXISTS;
+ }
+ }
+
+ Vector<StringName> parameters_names;
+ parameters_names.resize(signal->parameters.size());
+ for (int j = 0; j < signal->parameters.size(); j++) {
+ parameters_names.write[j] = signal->parameters[j]->identifier->name;
+ }
+ p_script->_signals[name] = parameters_names;
+ } break;
- p_script->_signals[name] = p_class->_signals[i].arguments;
+ case GDScriptParser::ClassNode::Member::ENUM: {
+ const GDScriptParser::EnumNode *enum_n = member.m_enum;
+
+ // TODO: Make enums not be just a dictionary?
+ Dictionary new_enum;
+ for (int j = 0; j < enum_n->values.size(); j++) {
+ int value = enum_n->values[j].value;
+ // Needs to be string because Variant::get will convert to String.
+ new_enum[String(enum_n->values[j].identifier->name)] = value;
+ }
+
+ p_script->constants.insert(enum_n->identifier->name, new_enum);
+#ifdef TOOLS_ENABLED
+ p_script->member_lines[enum_n->identifier->name] = enum_n->start_line;
+#endif
+ } break;
+ default:
+ break; // Nothing to do here.
+ }
}
parsed_classes.insert(p_script);
@@ -2015,14 +2529,19 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
//parse sub-classes
- for (int i = 0; i < p_class->subclasses.size(); i++) {
- StringName name = p_class->subclasses[i]->name;
+ for (int i = 0; i < p_class->members.size(); i++) {
+ const GDScriptParser::ClassNode::Member &member = p_class->members[i];
+ if (member.type != member.CLASS) {
+ continue;
+ }
+ const GDScriptParser::ClassNode *inner_class = member.m_class;
+ StringName name = inner_class->identifier->name;
Ref<GDScript> &subclass = p_script->subclasses[name];
GDScript *subclass_ptr = subclass.ptr();
// Subclass might still be parsing, just skip it
if (!parsed_classes.has(subclass_ptr) && !parsing_classes.has(subclass_ptr)) {
- Error err = _parse_class_level(subclass_ptr, p_class->subclasses[i], p_keep_state);
+ Error err = _parse_class_level(subclass_ptr, inner_class, p_keep_state);
if (err) {
return err;
}
@@ -2030,7 +2549,7 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
#ifdef TOOLS_ENABLED
- p_script->member_lines[name] = p_class->subclasses[i]->line;
+ p_script->member_lines[name] = inner_class->start_line;
#endif
p_script->constants.insert(name, subclass); //once parsed, goes to the list of constants
@@ -2042,41 +2561,33 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
//parse methods
- bool has_initializer = false;
bool has_ready = false;
- for (int i = 0; i < p_class->functions.size(); i++) {
- if (!has_initializer && p_class->functions[i]->name == "_init") {
- has_initializer = true;
+ for (int i = 0; i < p_class->members.size(); i++) {
+ const GDScriptParser::ClassNode::Member &member = p_class->members[i];
+ if (member.type != member.FUNCTION) {
+ continue;
}
- if (!has_ready && p_class->functions[i]->name == "_ready") {
+ const GDScriptParser::FunctionNode *function = member.function;
+ if (!has_ready && function->identifier->name == "_ready") {
has_ready = true;
}
- Error err = _parse_function(p_script, p_class, p_class->functions[i]);
+ Error err = _parse_function(p_script, p_class, function);
if (err) {
return err;
}
}
- //parse static methods
-
- for (int i = 0; i < p_class->static_functions.size(); i++) {
- Error err = _parse_function(p_script, p_class, p_class->static_functions[i]);
- if (err) {
- return err;
- }
- }
-
- if (!has_initializer) {
- //create a constructor
+ {
+ // Create an implicit constructor in any case.
Error err = _parse_function(p_script, p_class, nullptr);
if (err) {
return err;
}
}
- if (!has_ready && p_class->ready->statements.size()) {
- //create a constructor
+ if (!has_ready && p_class->onready_used) {
+ //create a _ready constructor
Error err = _parse_function(p_script, p_class, nullptr, true);
if (err) {
return err;
@@ -2132,11 +2643,15 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa
}
#endif
- for (int i = 0; i < p_class->subclasses.size(); i++) {
- StringName name = p_class->subclasses[i]->name;
+ for (int i = 0; i < p_class->members.size(); i++) {
+ if (p_class->members[i].type != GDScriptParser::ClassNode::Member::CLASS) {
+ continue;
+ }
+ const GDScriptParser::ClassNode *inner_class = p_class->members[i].m_class;
+ StringName name = inner_class->identifier->name;
GDScript *subclass = p_script->subclasses[name].ptr();
- Error err = _parse_class_blocks(subclass, p_class->subclasses[i], p_keep_state);
+ Error err = _parse_class_blocks(subclass, inner_class, p_keep_state);
if (err) {
return err;
}
@@ -2155,8 +2670,12 @@ void GDScriptCompiler::_make_scripts(GDScript *p_script, const GDScriptParser::C
p_script->subclasses.clear();
- for (int i = 0; i < p_class->subclasses.size(); i++) {
- StringName name = p_class->subclasses[i]->name;
+ for (int i = 0; i < p_class->members.size(); i++) {
+ if (p_class->members[i].type != GDScriptParser::ClassNode::Member::CLASS) {
+ continue;
+ }
+ const GDScriptParser::ClassNode *inner_class = p_class->members[i].m_class;
+ StringName name = inner_class->identifier->name;
Ref<GDScript> subclass;
String fully_qualified_name = p_script->fully_qualified_name + "::" + name;
@@ -2176,7 +2695,7 @@ void GDScriptCompiler::_make_scripts(GDScript *p_script, const GDScriptParser::C
subclass->fully_qualified_name = fully_qualified_name;
p_script->subclasses.insert(name, subclass);
- _make_scripts(subclass.ptr(), p_class->subclasses[i], false);
+ _make_scripts(subclass.ptr(), inner_class, false);
}
}
@@ -2186,8 +2705,7 @@ Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_scri
error = "";
parser = p_parser;
main_script = p_script;
- const GDScriptParser::Node *root = parser->get_parse_tree();
- ERR_FAIL_COND_V(root->type != GDScriptParser::Node::TYPE_CLASS, ERR_INVALID_DATA);
+ const GDScriptParser::ClassNode *root = parser->get_tree();
source = p_script->get_path();
@@ -2195,16 +2713,16 @@ Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_scri
p_script->fully_qualified_name = p_script->path;
// Create scripts for subclasses beforehand so they can be referenced
- _make_scripts(p_script, static_cast<const GDScriptParser::ClassNode *>(root), p_keep_state);
+ _make_scripts(p_script, root, p_keep_state);
p_script->_owner = nullptr;
- Error err = _parse_class_level(p_script, static_cast<const GDScriptParser::ClassNode *>(root), p_keep_state);
+ Error err = _parse_class_level(p_script, root, p_keep_state);
if (err) {
return err;
}
- err = _parse_class_blocks(p_script, static_cast<const GDScriptParser::ClassNode *>(root), p_keep_state);
+ err = _parse_class_blocks(p_script, root, p_keep_state);
if (err) {
return err;