diff options
Diffstat (limited to 'modules/gdscript/gd_compiler.cpp')
-rw-r--r-- | modules/gdscript/gd_compiler.cpp | 272 |
1 files changed, 251 insertions, 21 deletions
diff --git a/modules/gdscript/gd_compiler.cpp b/modules/gdscript/gd_compiler.cpp index ce8b6a6ea4..398c2cf82a 100644 --- a/modules/gdscript/gd_compiler.cpp +++ b/modules/gdscript/gd_compiler.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -29,6 +29,31 @@ #include "gd_compiler.h" #include "gd_script.h" +bool GDCompiler::_is_class_member_property(CodeGen & codegen, const StringName & p_name) { + + if (!codegen.function_node || codegen.function_node->_static) + return false; + + return _is_class_member_property(codegen.script,p_name); +} + +bool GDCompiler::_is_class_member_property(GDScript *owner, const StringName & p_name) { + + + GDScript *scr = owner; + GDNativeClass *nc=NULL; + while(scr) { + + if (scr->native.is_valid()) + nc=scr->native.ptr(); + scr=scr->_base; + } + + ERR_FAIL_COND_V(!nc,false); + + return ClassDB::has_property(nc->get_name(),p_name); +} + void GDCompiler::_set_error(const String& p_error,const GDParser::Node *p_node) { @@ -164,6 +189,17 @@ int GDCompiler::_parse_expression(CodeGen& codegen,const GDParser::Node *p_expre StringName identifier = in->name; + + if (_is_class_member_property(codegen,identifier)) { + //get property + codegen.opcodes.push_back(GDFunction::OPCODE_GET_MEMBER); // perform operator + codegen.opcodes.push_back(codegen.get_name_map_pos(identifier)); // argument 2 (unary only takes one parameter) + int dst_addr=(p_stack_level)|(GDFunction::ADDR_TYPE_STACK<<GDFunction::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; + } + // TRY STACK! if (!p_initializer && codegen.stack_identifiers.has(identifier)) { @@ -208,7 +244,7 @@ int GDCompiler::_parse_expression(CodeGen& codegen,const GDParser::Node *p_expre if (nc) { bool success=false; - int constant = ObjectTypeDB::get_integer_constant(nc->get_name(),identifier,&success); + int constant = ClassDB::get_integer_constant(nc->get_name(),identifier,&success); if (success) { Variant key=constant; int idx; @@ -662,6 +698,46 @@ int GDCompiler::_parse_expression(CodeGen& codegen,const GDParser::Node *p_expre return p_stack_level|GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS; } break; + // ternary operators + case GDParser::OperatorNode::OP_TERNARY_IF: { + + // x IF a ELSE y operator with early out on failure + + int res = _parse_expression(codegen,on->arguments[0],p_stack_level); + if (res<0) + return res; + codegen.opcodes.push_back(GDFunction::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; + + codegen.alloc_stack(p_stack_level); //it will be used.. + codegen.opcodes.push_back(GDFunction::OPCODE_ASSIGN); + codegen.opcodes.push_back(p_stack_level|GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS); + codegen.opcodes.push_back(res); + codegen.opcodes.push_back(GDFunction::OPCODE_JUMP); + int jump_past_pos=codegen.opcodes.size(); + codegen.opcodes.push_back(0); + + codegen.opcodes[jump_fail_pos]=codegen.opcodes.size(); + res = _parse_expression(codegen,on->arguments[2],p_stack_level); + if (res<0) + return res; + + codegen.opcodes.push_back(GDFunction::OPCODE_ASSIGN); + codegen.opcodes.push_back(p_stack_level|GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS); + codegen.opcodes.push_back(res); + + codegen.opcodes[jump_past_pos]=codegen.opcodes.size(); + + return p_stack_level|GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS; + + } break; //unary operators case GDParser::OperatorNode::OP_NEG: { if (!_create_unary_operator(codegen,on,Variant::OP_NEGATE,p_stack_level)) return -1;} break; case GDParser::OperatorNode::OP_NOT: { if (!_create_unary_operator(codegen,on,Variant::OP_NOT,p_stack_level)) return -1;} break; @@ -736,6 +812,8 @@ int GDCompiler::_parse_expression(CodeGen& codegen,const GDParser::Node *p_expre /* Find chain of sets */ + StringName assign_property; + List<GDParser::OperatorNode*> chain; { @@ -744,8 +822,20 @@ int GDCompiler::_parse_expression(CodeGen& codegen,const GDParser::Node *p_expre while(true) { chain.push_back(n); - if (n->arguments[0]->type!=GDParser::Node::TYPE_OPERATOR) + if (n->arguments[0]->type!=GDParser::Node::TYPE_OPERATOR) { + + //check for a built-in property + if (n->arguments[0]->type==GDParser::Node::TYPE_IDENTIFIER) { + + GDParser::IdentifierNode *identifier = static_cast<GDParser::IdentifierNode*>(n->arguments[0]); + if (_is_class_member_property(codegen,identifier->name)) { + assign_property = identifier->name; + + } + + } break; + } n = static_cast<GDParser::OperatorNode*>(n->arguments[0]); if (n->op!=GDParser::OperatorNode::OP_INDEX && n->op!=GDParser::OperatorNode::OP_INDEX_NAMED) break; @@ -770,6 +860,17 @@ int GDCompiler::_parse_expression(CodeGen& codegen,const GDParser::Node *p_expre 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(GDFunction::OPCODE_SET_MEMBER); + } + for(List<GDParser::OperatorNode*>::Element *E=chain.back();E;E=E->prev()) { @@ -800,7 +901,7 @@ int GDCompiler::_parse_expression(CodeGen& codegen,const GDParser::Node *p_expre } - if (key_idx<0) + if (key_idx<0) //error return key_idx; codegen.opcodes.push_back(named ? GDFunction::OPCODE_GET_NAMED : GDFunction::OPCODE_GET); @@ -812,7 +913,10 @@ int GDCompiler::_parse_expression(CodeGen& codegen,const GDParser::Node *p_expre codegen.opcodes.push_back(dst_pos); + //add in reverse order, since it will be reverted + + setchain.push_back(dst_pos); setchain.push_back(key_idx); setchain.push_back(prev_pos); @@ -841,7 +945,7 @@ int GDCompiler::_parse_expression(CodeGen& codegen,const GDParser::Node *p_expre } - if (set_index<0) + if (set_index<0) //error return set_index; if (set_index&GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS) { @@ -851,7 +955,7 @@ int GDCompiler::_parse_expression(CodeGen& codegen,const GDParser::Node *p_expre int set_value = _parse_assign_right_expression(codegen,on,slevel+1); - if (set_value<0) + if (set_value<0) //error return set_value; codegen.opcodes.push_back(named?GDFunction::OPCODE_SET_NAMED:GDFunction::OPCODE_SET); @@ -859,20 +963,36 @@ int GDCompiler::_parse_expression(CodeGen& codegen,const GDParser::Node *p_expre codegen.opcodes.push_back(set_index); codegen.opcodes.push_back(set_value); - for(int i=0;i<setchain.size();i+=4) { + for(int i=0;i<setchain.size();i++) { - codegen.opcodes.push_back(setchain[i+0]); - codegen.opcodes.push_back(setchain[i+1]); - codegen.opcodes.push_back(setchain[i+2]); - codegen.opcodes.push_back(setchain[i+3]); + codegen.opcodes.push_back(setchain[i]); } return retval; + } else if (on->arguments[0]->type==GDParser::Node::TYPE_IDENTIFIER && _is_class_member_property(codegen,static_cast<GDParser::IdentifierNode*>(on->arguments[0])->name)) { + //assignment to member property + + int slevel = p_stack_level; + + int src_address = _parse_assign_right_expression(codegen,on,slevel); + if (src_address<0) + return -1; + + StringName name = static_cast<GDParser::IdentifierNode*>(on->arguments[0])->name; + + codegen.opcodes.push_back(GDFunction::OPCODE_SET_MEMBER); + codegen.opcodes.push_back(codegen.get_name_map_pos(name)); + codegen.opcodes.push_back(src_address); + + return GDFunction::ADDR_TYPE_NIL<<GDFunction::ADDR_BITS; } else { - //ASSIGNMENT MODE!! + + + + //REGULAR ASSIGNMENT MODE!! int slevel = p_stack_level; @@ -965,12 +1085,12 @@ Error GDCompiler::_parse_block(CodeGen& codegen,const GDParser::BlockNode *p_blo switch(s->type) { case GDParser::Node::TYPE_NEWLINE: { - +#ifdef DEBUG_ENABLED const GDParser::NewLineNode *nl = static_cast<const GDParser::NewLineNode*>(s); codegen.opcodes.push_back(GDFunction::OPCODE_LINE); codegen.opcodes.push_back(nl->line); codegen.current_line=nl->line; - +#endif } break; case GDParser::Node::TYPE_CONTROL_FLOW: { // try subblocks @@ -979,7 +1099,72 @@ Error GDCompiler::_parse_block(CodeGen& codegen,const GDParser::BlockNode *p_blo switch(cf->cf_type) { - + case GDParser::ControlFlowNode::CF_MATCH: { + GDParser::MatchNode *match = cf->match; + + GDParser::IdentifierNode *id = memnew(GDParser::IdentifierNode); + id->name = "#match_value"; + + // 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++; + + GDParser::OperatorNode *op = memnew(GDParser::OperatorNode); + op->op=GDParser::OperatorNode::OP_ASSIGN; + op->arguments.push_back(id); + op->arguments.push_back(match->val_to_match); + + int ret = _parse_expression(codegen, op, p_stack_level); + if (ret < 0) { + return ERR_PARSE_ERROR; + } + + // break address + codegen.opcodes.push_back(GDFunction::OPCODE_JUMP); + codegen.opcodes.push_back(codegen.opcodes.size() + 3); + int break_addr = codegen.opcodes.size(); + codegen.opcodes.push_back(GDFunction::OPCODE_JUMP); + codegen.opcodes.push_back(0); // break addr + + for (int j = 0; j < match->compiled_pattern_branches.size(); j++) { + GDParser::MatchNode::CompiledPatternBranch branch = match->compiled_pattern_branches[j]; + + // jump over continue + // jump unconditionally + // continue address + // compile the condition + int ret = _parse_expression(codegen, branch.compiled_pattern, p_stack_level); + if (ret < 0) { + return ERR_PARSE_ERROR; + } + + codegen.opcodes.push_back(GDFunction::OPCODE_JUMP_IF); + codegen.opcodes.push_back(ret); + codegen.opcodes.push_back(codegen.opcodes.size() + 3); + int continue_addr = codegen.opcodes.size(); + codegen.opcodes.push_back(GDFunction::OPCODE_JUMP); + codegen.opcodes.push_back(0); + + + + Error err = _parse_block(codegen, branch.body, p_stack_level, p_break_addr, continue_addr); + if (err) { + return ERR_PARSE_ERROR; + } + + codegen.opcodes.push_back(GDFunction::OPCODE_JUMP); + codegen.opcodes.push_back(break_addr); + + codegen.opcodes[continue_addr + 1] = codegen.opcodes.size(); + } + + codegen.opcodes[break_addr + 1] = codegen.opcodes.size(); + + + } break; + case GDParser::ControlFlowNode::CF_IF: { #ifdef DEBUG_ENABLED @@ -1161,14 +1346,21 @@ Error GDCompiler::_parse_block(CodeGen& codegen,const GDParser::BlockNode *p_blo codegen.opcodes.push_back(ret); } break; case GDParser::Node::TYPE_BREAKPOINT: { +#ifdef DEBUG_ENABLED // try subblocks codegen.opcodes.push_back(GDFunction::OPCODE_BREAKPOINT); +#endif } break; case GDParser::Node::TYPE_LOCAL_VAR: { const GDParser::LocalVarNode *lv = static_cast<const GDParser::LocalVarNode*>(s); + if (_is_class_member_property(codegen,lv->name)) { + _set_error("Name for local variable '"+String(lv->name)+"' can't shadow class property of the same name.",lv); + return ERR_ALREADY_EXISTS; + } + codegen.add_stack_identifier(lv->name,p_stack_level++); codegen.alloc_stack(p_stack_level); new_identifiers++; @@ -1207,6 +1399,10 @@ Error GDCompiler::_parse_function(GDScript *p_script,const GDParser::ClassNode * if (p_func) { for(int i=0;i<p_func->arguments.size();i++) { + if (_is_class_member_property(p_script,p_func->arguments[i])) { + _set_error("Name for argument '"+String(p_func->arguments[i])+"' can't shadow class property of the same name.",p_func); + return ERR_ALREADY_EXISTS; + } codegen.add_stack_identifier(p_func->arguments[i],i); #ifdef TOOLS_ENABLED argnames.push_back(p_func->arguments[i]); @@ -1289,9 +1485,10 @@ Error GDCompiler::_parse_function(GDScript *p_script,const GDParser::ClassNode * GDFunction *gdfunc=NULL; - //if (String(p_func->name)=="") { //initializer func - // gdfunc = &p_script->initializer; - + /* + if (String(p_func->name)=="") { //initializer func + gdfunc = &p_script->initializer; + */ //} else { //regular func p_script->member_functions[func_name]=memnew(GDFunction); gdfunc = p_script->member_functions[func_name]; @@ -1379,7 +1576,7 @@ Error GDCompiler::_parse_function(GDScript *p_script,const GDParser::ClassNode * //funciton and class if (p_class->name) { - signature+="::"+String(p_class->name)+"."+String(func_name);; + signature+="::"+String(p_class->name)+"."+String(func_name); } else { signature+="::"+String(func_name); } @@ -1403,6 +1600,10 @@ Error GDCompiler::_parse_function(GDScript *p_script,const GDParser::ClassNode * #endif if (p_func) { gdfunc->_initial_line=p_func->line; +#ifdef TOOLS_ENABLED + + p_script->member_lines[func_name]=p_func->line; +#endif } else { gdfunc->_initial_line=0; } @@ -1594,9 +1795,14 @@ Error GDCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, const GDPa } + }else { + // without extends, implicitly extend Reference + int native_idx = GDScriptLanguage::get_singleton()->get_global_map()["Reference"]; + native = GDScriptLanguage::get_singleton()->get_global_array()[native_idx]; + ERR_FAIL_COND_V(native.is_null(), ERR_BUG); + p_script->native=native; } - //print_line("Script: "+p_script->get_path()+" indices: "+itos(p_script->member_indices.size())); @@ -1607,6 +1813,10 @@ Error GDCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, const GDPa _set_error("Member '"+name+"' already exists (in current or parent class)",p_class); return ERR_ALREADY_EXISTS; } + if (_is_class_member_property(p_script,name)) { + _set_error("Member '"+name+"' already exists as a class property.",p_class); + return ERR_ALREADY_EXISTS; + } if (p_class->variables[i]._export.type!=Variant::NIL) { @@ -1632,6 +1842,12 @@ Error GDCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, const GDPa p_script->member_indices[name]=minfo; p_script->members.insert(name); +#ifdef TOOLS_ENABLED + + p_script->member_lines[name]=p_class->variables[i].line; +#endif + + } for(int i=0;i<p_class->constant_expressions.size();i++) { @@ -1639,10 +1855,20 @@ Error GDCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, const GDPa StringName name = p_class->constant_expressions[i].identifier; ERR_CONTINUE( p_class->constant_expressions[i].expression->type!=GDParser::Node::TYPE_CONSTANT ); + if (_is_class_member_property(p_script,name)) { + _set_error("Member '"+name+"' already exists as a class property.",p_class); + return ERR_ALREADY_EXISTS; + } + GDParser::ConstantNode *constant = static_cast<GDParser::ConstantNode*>(p_class->constant_expressions[i].expression); p_script->constants.insert(name,constant->value); //p_script->constants[constant->value].make_const(); +#ifdef TOOLS_ENABLED + + p_script->member_lines[name]=p_class->constant_expressions[i].expression->line; +#endif + } for(int i=0;i<p_class->_signals.size();i++) { @@ -1666,7 +1892,7 @@ Error GDCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, const GDPa } if (native.is_valid()) { - if (ObjectTypeDB::has_signal(native->get_name(),name)) { + 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; } @@ -1691,6 +1917,10 @@ Error GDCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, const GDPa if (err) return err; +#ifdef TOOLS_ENABLED + + p_script->member_lines[name]=p_class->subclasses[i]->line; +#endif p_script->constants.insert(name,subclass); //once parsed, goes to the list of constants p_script->subclasses.insert(name,subclass); |