added ability to define signals in script

closes #2175
This commit is contained in:
Juan Linietsky
2015-06-24 13:29:23 -03:00
parent 199ad16bbc
commit 48f1d02da4
12 changed files with 271 additions and 65 deletions

View File

@ -28,15 +28,6 @@
/*************************************************************************/
#include "gd_compiler.h"
#include "gd_script.h"
/* TODO:
*AND and OR need early abort
-Inheritance properly process (done?)
*create built in initializer and constructor
*assign operators
*build arrays and dictionaries
*call parent constructor
*/
void GDCompiler::_set_error(const String& p_error,const GDParser::Node *p_node) {
@ -1397,13 +1388,14 @@ Error GDCompiler::_parse_class(GDScript *p_script,GDScript *p_owner,const GDPars
int index_from=0;
Ref<GDNativeClass> native;
if (p_class->extends_used) {
//do inheritance
String path = p_class->extends_file;
Ref<GDScript> script;
Ref<GDNativeClass> native;
if (path!="") {
//path (and optionally subclasses)
@ -1573,7 +1565,35 @@ Error GDCompiler::_parse_class(GDScript *p_script,GDScript *p_owner,const GDPars
//p_script->constants[constant->value].make_const();
}
for(int i=0;i<p_class->_signals.size();i++) {
StringName name = p_class->_signals[i].name;
GDScript *c = p_script;
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=NULL;
}
}
if (native.is_valid()) {
if (ObjectTypeDB::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;
}
}
p_script->_signals[name]=p_class->_signals[i].arguments;
}
//parse sub-classes
for(int i=0;i<p_class->subclasses.size();i++) {

View File

@ -1520,8 +1520,10 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) {
op->arguments.push_back(assigned);
p_block->statements.push_back(op);
_end_statement();
if (!_end_statement()) {
_set_error("Expected end of statement (var)");
return;
}
} break;
case GDTokenizer::TK_CF_IF: {
@ -1946,8 +1948,10 @@ void GDParser::_parse_class(ClassNode *p_class) {
_parse_extends(p_class);
if (error_set)
return;
_end_statement();
if (!_end_statement()) {
_set_error("Expected end of statement after extends");
return;
}
} break;
case GDTokenizer::TK_PR_TOOL: {
@ -2227,6 +2231,53 @@ void GDParser::_parse_class(ClassNode *p_class) {
//arguments
} break;
case GDTokenizer::TK_PR_SIGNAL: {
tokenizer->advance();
if (tokenizer->get_token()!=GDTokenizer::TK_IDENTIFIER) {
_set_error("Expected identifier after 'signal'.");
return;
}
ClassNode::Signal sig;
sig.name = tokenizer->get_token_identifier();
tokenizer->advance();
if (tokenizer->get_token()==GDTokenizer::TK_PARENTHESIS_OPEN) {
tokenizer->advance();
while(true) {
if (tokenizer->get_token()==GDTokenizer::TK_PARENTHESIS_CLOSE) {
tokenizer->advance();
break;
}
if (tokenizer->get_token()!=GDTokenizer::TK_IDENTIFIER) {
_set_error("Expected identifier in signal argument.");
return;
}
sig.arguments.push_back(tokenizer->get_token_identifier());
tokenizer->advance();
if (tokenizer->get_token()==GDTokenizer::TK_COMMA) {
tokenizer->advance();
} else if (tokenizer->get_token()!=GDTokenizer::TK_PARENTHESIS_CLOSE) {
_set_error("Expected ',' or ')' after signal parameter identifier.");
return;
}
}
}
p_class->_signals.push_back(sig);
if (!_end_statement()) {
_set_error("Expected end of statement (signal)");
return;
}
} break;
case GDTokenizer::TK_PR_EXPORT: {
tokenizer->advance();
@ -2644,8 +2695,10 @@ void GDParser::_parse_class(ClassNode *p_class) {
p_class->variables.push_back(member);
_end_statement();
if (!_end_statement()) {
_set_error("Expected end of statement (continue)");
return;
}
} break;
case GDTokenizer::TK_PR_CONST: {
//variale declaration and (eventual) initialization
@ -2682,8 +2735,10 @@ void GDParser::_parse_class(ClassNode *p_class) {
p_class->constant_expressions.push_back(constant);
_end_statement();
if (!_end_statement()) {
_set_error("Expected end of statement (constant)");
return;
}
} break;

View File

@ -76,6 +76,7 @@ public:
StringName extends_file;
Vector<StringName> extends_class;
struct Member {
PropertyInfo _export;
#ifdef TOOLS_ENABLED
@ -92,11 +93,17 @@ public:
Node *expression;
};
struct Signal {
StringName name;
Vector<StringName> arguments;
};
Vector<ClassNode*> subclasses;
Vector<Member> variables;
Vector<Constant> constant_expressions;
Vector<FunctionNode*> functions;
Vector<FunctionNode*> static_functions;
Vector<Signal> _signals;
BlockNode *initializer;
ClassNode *owner;
//Vector<Node*> initializers;

View File

@ -1756,6 +1756,12 @@ bool GDScript::_update_exports() {
//print_line("found "+c->variables[i]._export.name);
member_default_values_cache[c->variables[i].identifier]=c->variables[i].default_value;
}
_signals.clear();
for(int i=0;i<c->_signals.size();i++) {
_signals[c->_signals[i].name]=c->_signals[i].arguments;
}
}
} else {
//print_line("unchaged is "+get_path());
@ -2100,6 +2106,47 @@ Ref<GDScript> GDScript::get_base() const {
return base;
}
bool GDScript::has_script_signal(const StringName& p_signal) const {
if (_signals.has(p_signal))
return true;
if (base.is_valid()) {
return base->has_script_signal(p_signal);
}
#ifdef TOOLS_ENABLED
else if (base_cache.is_valid()){
return base_cache->has_script_signal(p_signal);
}
#endif
return false;
}
void GDScript::get_script_signal_list(List<MethodInfo> *r_signals) const {
for(const Map<StringName,Vector<StringName> >::Element *E=_signals.front();E;E=E->next()) {
MethodInfo mi;
mi.name=E->key();
for(int i=0;i<E->get().size();i++) {
PropertyInfo arg;
arg.name=E->get()[i];
mi.arguments.push_back(arg);
}
r_signals->push_back(mi);
}
if (base.is_valid()) {
base->get_script_signal_list(r_signals);
}
#ifdef TOOLS_ENABLED
else if (base_cache.is_valid()){
base_cache->get_script_signal_list(r_signals);
}
#endif
}
GDScript::GDScript() {
@ -2594,6 +2641,7 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const {
"static",
"float",
"int",
"signal",
0};

View File

@ -260,6 +260,7 @@ friend class GDScriptLanguage;
Map<StringName,GDFunction> member_functions;
Map<StringName,MemberInfo> member_indices; //members are just indices to the instanced script.
Map<StringName,Ref<GDScript> > subclasses;
Map<StringName,Vector<StringName> > _signals;
#ifdef TOOLS_ENABLED
@ -318,6 +319,9 @@ public:
const Map<StringName,GDFunction>& get_member_functions() const { return member_functions; }
const Ref<GDNativeClass>& get_native() const { return native; }
virtual bool has_script_signal(const StringName& p_signal) const;
virtual void get_script_signal_list(List<MethodInfo> *r_signals) const;
bool is_tool() const { return tool; }
Ref<GDScript> get_base() const;

View File

@ -856,6 +856,7 @@ void GDTokenizerText::_advance() {
{TK_PR_PRELOAD,"preload"},
{TK_PR_ASSERT,"assert"},
{TK_PR_YIELD,"yield"},
{TK_PR_SIGNAL,"signal"},
{TK_PR_CONST,"const"},
//controlflow
{TK_CF_IF,"if"},

View File

@ -104,6 +104,7 @@ public:
TK_PR_PRELOAD,
TK_PR_ASSERT,
TK_PR_YIELD,
TK_PR_SIGNAL,
TK_BRACKET_OPEN,
TK_BRACKET_CLOSE,
TK_CURLY_BRACKET_OPEN,