2014-02-10 02:10:30 +01:00
/*************************************************************************/
2017-11-16 18:38:18 +01:00
/* gdscript_parser.cpp */
2014-02-10 02:10:30 +01:00
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
2017-08-27 14:16:55 +02:00
/* https://godotengine.org */
2014-02-10 02:10:30 +01:00
/*************************************************************************/
2019-01-01 12:53:14 +01:00
/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
2014-02-10 02:10:30 +01:00
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
2018-01-05 00:50:27 +01:00
2017-11-16 18:38:18 +01:00
# include "gdscript_parser.h"
2018-05-30 04:16:54 +02:00
# include "core/core_string_names.h"
# include "core/engine.h"
2018-09-11 18:13:45 +02:00
# include "core/io/resource_loader.h"
# include "core/os/file_access.h"
# include "core/print_string.h"
2018-05-30 04:16:54 +02:00
# include "core/project_settings.h"
# include "core/reference.h"
2018-09-11 18:13:45 +02:00
# include "core/script_language.h"
2017-11-16 18:38:18 +01:00
# include "gdscript.h"
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
template < class T >
2017-11-16 18:38:18 +01:00
T * GDScriptParser : : alloc_node ( ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
T * t = memnew ( T ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
t - > next = list ;
list = t ;
2014-02-10 02:10:30 +01:00
if ( ! head )
2017-03-05 16:44:50 +01:00
head = t ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
t - > line = tokenizer - > get_token_line ( ) ;
t - > column = tokenizer - > get_token_column ( ) ;
2014-02-10 02:10:30 +01:00
return t ;
}
2018-10-03 16:13:34 +02:00
# ifdef DEBUG_ENABLED
2018-07-01 18:17:40 +02:00
static String _find_function_name ( const GDScriptParser : : OperatorNode * p_call ) ;
2018-10-03 16:13:34 +02:00
# endif // DEBUG_ENABLED
2018-07-01 18:17:40 +02:00
2017-11-16 18:38:18 +01:00
bool GDScriptParser : : _end_statement ( ) {
2014-02-10 02:10:30 +01:00
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_SEMICOLON ) {
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
return true ; //handle next
2017-11-16 18:38:18 +01:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_NEWLINE | | tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_EOF ) {
2014-02-10 02:10:30 +01:00
return true ; //will be handled properly
}
return false ;
}
2017-11-16 18:38:18 +01:00
bool GDScriptParser : : _enter_indent_block ( BlockNode * p_block ) {
2014-02-10 02:10:30 +01:00
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_COLON ) {
2015-11-29 23:45:28 +01:00
// report location at the previous token (on the previous line)
int error_line = tokenizer - > get_token_line ( - 1 ) ;
int error_column = tokenizer - > get_token_column ( - 1 ) ;
2017-03-05 16:44:50 +01:00
_set_error ( " ':' expected at end of line. " , error_line , error_column ) ;
2014-02-10 02:10:30 +01:00
return false ;
}
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
2018-09-16 23:29:17 +02:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_EOF ) {
return false ;
}
2014-02-10 02:10:30 +01:00
2018-09-16 23:29:17 +02:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_NEWLINE ) {
2015-12-29 15:41:37 +01:00
// be more python-like
int current = tab_level . back ( ) - > get ( ) ;
2017-03-23 19:07:02 +01:00
tab_level . push_back ( current ) ;
2015-12-29 15:41:37 +01:00
return true ;
//_set_error("newline expected after ':'.");
//return false;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
while ( true ) {
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_NEWLINE ) {
2016-05-29 16:37:26 +02:00
2014-02-10 02:10:30 +01:00
return false ; //wtf
2018-09-16 23:29:17 +02:00
} else if ( tokenizer - > get_token ( 1 ) = = GDScriptTokenizer : : TK_EOF ) {
return false ;
2017-11-16 18:38:18 +01:00
} else if ( tokenizer - > get_token ( 1 ) ! = GDScriptTokenizer : : TK_NEWLINE ) {
2014-02-10 02:10:30 +01:00
2014-02-25 13:31:47 +01:00
int indent = tokenizer - > get_token_line_indent ( ) ;
2014-02-10 02:10:30 +01:00
int current = tab_level . back ( ) - > get ( ) ;
2017-03-05 16:44:50 +01:00
if ( indent < = current ) {
2014-02-10 02:10:30 +01:00
return false ;
2015-12-29 15:41:37 +01:00
}
2014-02-10 02:10:30 +01:00
tab_level . push_back ( indent ) ;
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
return true ;
} else if ( p_block ) {
NewLineNode * nl = alloc_node < NewLineNode > ( ) ;
2017-03-05 16:44:50 +01:00
nl - > line = tokenizer - > get_token_line ( ) ;
2014-02-10 02:10:30 +01:00
p_block - > statements . push_back ( nl ) ;
}
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ; // go to next newline
2014-02-10 02:10:30 +01:00
}
}
2017-11-16 18:38:18 +01:00
bool GDScriptParser : : _parse_arguments ( Node * p_parent , Vector < Node * > & p_args , bool p_static , bool p_can_codecomplete ) {
2014-02-10 02:10:30 +01:00
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE ) {
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
} else {
2017-03-05 16:44:50 +01:00
parenthesis + + ;
int argidx = 0 ;
2014-12-17 02:31:57 +01:00
2017-03-05 16:44:50 +01:00
while ( true ) {
2014-02-10 02:10:30 +01:00
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CURSOR ) {
2014-12-17 02:31:57 +01:00
_make_completable_call ( argidx ) ;
2017-03-05 16:44:50 +01:00
completion_node = p_parent ;
2017-11-16 18:38:18 +01:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CONSTANT & & tokenizer - > get_token_constant ( ) . get_type ( ) = = Variant : : STRING & & tokenizer - > get_token ( 1 ) = = GDScriptTokenizer : : TK_CURSOR ) {
2015-06-26 06:14:31 +02:00
//completing a string argument..
2017-03-05 16:44:50 +01:00
completion_cursor = tokenizer - > get_token_constant ( ) ;
2015-06-26 06:14:31 +02:00
_make_completable_call ( argidx ) ;
2017-03-05 16:44:50 +01:00
completion_node = p_parent ;
2015-06-26 06:14:31 +02:00
tokenizer - > advance ( 1 ) ;
return false ;
2014-12-17 02:31:57 +01:00
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
Node * arg = _parse_expression ( p_parent , p_static ) ;
2018-06-21 03:41:26 +02:00
if ( ! arg ) {
2014-02-10 02:10:30 +01:00
return false ;
2018-06-21 03:41:26 +02:00
}
2014-02-10 02:10:30 +01:00
p_args . push_back ( arg ) ;
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE ) {
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
break ;
2017-11-16 18:38:18 +01:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COMMA ) {
2014-02-10 02:10:30 +01:00
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( 1 ) = = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE ) {
2014-02-10 02:10:30 +01:00
_set_error ( " Expression expected " ) ;
return false ;
}
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-12-17 02:31:57 +01:00
argidx + + ;
2014-02-10 02:10:30 +01:00
} else {
// something is broken
_set_error ( " Expected ',' or ')' " ) ;
return false ;
}
}
2017-03-05 16:44:50 +01:00
parenthesis - - ;
2014-02-10 02:10:30 +01:00
}
return true ;
}
2017-11-16 18:38:18 +01:00
void GDScriptParser : : _make_completable_call ( int p_arg ) {
2014-12-17 02:31:57 +01:00
2017-03-05 16:44:50 +01:00
completion_cursor = StringName ( ) ;
completion_type = COMPLETION_CALL_ARGUMENTS ;
completion_class = current_class ;
completion_function = current_function ;
completion_line = tokenizer - > get_token_line ( ) ;
completion_argument = p_arg ;
completion_block = current_block ;
completion_found = true ;
2014-12-17 02:31:57 +01:00
tokenizer - > advance ( ) ;
}
2017-11-16 18:38:18 +01:00
bool GDScriptParser : : _get_completable_identifier ( CompletionType p_type , StringName & identifier ) {
2014-12-17 02:31:57 +01:00
2017-03-05 16:44:50 +01:00
identifier = StringName ( ) ;
2017-03-31 19:28:34 +02:00
if ( tokenizer - > is_token_literal ( ) ) {
identifier = tokenizer - > get_token_literal ( ) ;
2014-12-17 02:31:57 +01:00
tokenizer - > advance ( ) ;
}
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CURSOR ) {
2017-03-05 16:44:50 +01:00
completion_cursor = identifier ;
completion_type = p_type ;
completion_class = current_class ;
completion_function = current_function ;
completion_line = tokenizer - > get_token_line ( ) ;
completion_block = current_block ;
completion_found = true ;
completion_ident_is_call = false ;
2014-12-17 02:31:57 +01:00
tokenizer - > advance ( ) ;
2017-03-31 19:28:34 +02:00
if ( tokenizer - > is_token_literal ( ) ) {
identifier = identifier . operator String ( ) + tokenizer - > get_token_literal ( ) . operator String ( ) ;
2014-12-17 02:31:57 +01:00
tokenizer - > advance ( ) ;
}
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PARENTHESIS_OPEN ) {
2017-03-05 16:44:50 +01:00
completion_ident_is_call = true ;
2016-09-12 15:52:29 +02:00
}
2014-12-17 02:31:57 +01:00
return true ;
}
return false ;
}
2017-11-16 18:38:18 +01:00
GDScriptParser : : Node * GDScriptParser : : _parse_expression ( Node * p_parent , bool p_static , bool p_allow_assign , bool p_parsing_constant ) {
2014-02-10 02:10:30 +01:00
2017-01-14 12:26:56 +01:00
//Vector<Node*> expressions;
//Vector<OperatorNode::Operator> operators;
2014-02-10 02:10:30 +01:00
Vector < Expression > expression ;
2017-03-05 16:44:50 +01:00
Node * expr = NULL ;
2014-02-10 02:10:30 +01:00
2016-07-22 14:22:34 +02:00
int op_line = tokenizer - > get_token_line ( ) ; // when operators are created at the bottom, the line might have been changed (\n found)
2017-03-05 16:44:50 +01:00
while ( true ) {
2014-02-10 02:10:30 +01:00
/*****************/
/* Parse Operand */
/*****************/
2017-03-05 16:44:50 +01:00
if ( parenthesis > 0 ) {
2014-04-05 23:50:09 +02:00
//remove empty space (only allowed if inside parenthesis
2017-11-16 18:38:18 +01:00
while ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_NEWLINE ) {
2014-04-05 23:50:09 +02:00
tokenizer - > advance ( ) ;
}
}
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PARENTHESIS_OPEN ) {
2014-02-10 02:10:30 +01:00
//subexpression ()
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-04-05 23:50:09 +02:00
parenthesis + + ;
2017-03-05 16:44:50 +01:00
Node * subexpr = _parse_expression ( p_parent , p_static , p_allow_assign , p_parsing_constant ) ;
2014-04-05 23:50:09 +02:00
parenthesis - - ;
2014-02-10 02:10:30 +01:00
if ( ! subexpr )
return NULL ;
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE ) {
2014-02-10 02:10:30 +01:00
_set_error ( " Expected ')' in expression " ) ;
return NULL ;
}
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2017-03-05 16:44:50 +01:00
expr = subexpr ;
2017-11-16 18:38:18 +01:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_DOLLAR ) {
2017-01-08 06:04:53 +01:00
tokenizer - > advance ( ) ;
String path ;
2017-03-05 16:44:50 +01:00
bool need_identifier = true ;
bool done = false ;
2018-06-06 03:57:44 +02:00
int line = tokenizer - > get_token_line ( ) ;
2017-01-08 06:04:53 +01:00
2017-03-05 16:44:50 +01:00
while ( ! done ) {
2017-01-08 06:04:53 +01:00
2017-03-05 16:44:50 +01:00
switch ( tokenizer - > get_token ( ) ) {
2017-11-16 18:38:18 +01:00
case GDScriptTokenizer : : TK_CURSOR : {
2017-03-05 16:44:50 +01:00
completion_cursor = StringName ( ) ;
completion_type = COMPLETION_GET_NODE ;
completion_class = current_class ;
completion_function = current_function ;
completion_line = tokenizer - > get_token_line ( ) ;
completion_cursor = path ;
completion_argument = 0 ;
completion_block = current_block ;
completion_found = true ;
2017-01-08 06:04:53 +01:00
tokenizer - > advance ( ) ;
} break ;
2017-11-16 18:38:18 +01:00
case GDScriptTokenizer : : TK_CONSTANT : {
2017-01-08 06:04:53 +01:00
2017-01-08 07:01:52 +01:00
if ( ! need_identifier ) {
2017-03-05 16:44:50 +01:00
done = true ;
2017-01-08 06:04:53 +01:00
break ;
2017-01-08 07:01:52 +01:00
}
2017-01-08 06:04:53 +01:00
2017-03-05 16:44:50 +01:00
if ( tokenizer - > get_token_constant ( ) . get_type ( ) ! = Variant : : STRING ) {
2017-01-08 06:04:53 +01:00
_set_error ( " Expected string constant or identifier after '$' or '/'. " ) ;
return NULL ;
}
2017-03-05 16:44:50 +01:00
path + = String ( tokenizer - > get_token_constant ( ) ) ;
2017-01-08 06:04:53 +01:00
tokenizer - > advance ( ) ;
2017-03-05 16:44:50 +01:00
need_identifier = false ;
2017-01-08 06:04:53 +01:00
} break ;
2017-11-16 18:38:18 +01:00
case GDScriptTokenizer : : TK_OP_DIV : {
2017-01-08 06:04:53 +01:00
2017-01-08 07:01:52 +01:00
if ( need_identifier ) {
2017-03-05 16:44:50 +01:00
done = true ;
2017-01-08 06:04:53 +01:00
break ;
2017-01-08 07:01:52 +01:00
}
2017-01-08 06:04:53 +01:00
2017-03-05 16:44:50 +01:00
path + = " / " ;
2017-01-08 06:04:53 +01:00
tokenizer - > advance ( ) ;
2017-03-05 16:44:50 +01:00
need_identifier = true ;
2017-01-08 06:04:53 +01:00
} break ;
default : {
2017-03-31 19:28:34 +02:00
// Instead of checking for TK_IDENTIFIER, we check with is_token_literal, as this allows us to use match/sync/etc. as a name
if ( need_identifier & & tokenizer - > is_token_literal ( ) ) {
path + = String ( tokenizer - > get_token_literal ( ) ) ;
tokenizer - > advance ( ) ;
need_identifier = false ;
2017-07-26 17:51:03 +02:00
} else {
done = true ;
2017-03-31 19:28:34 +02:00
}
2017-01-08 06:04:53 +01:00
break ;
}
}
}
2017-03-05 16:44:50 +01:00
if ( path = = " " ) {
2017-01-08 06:04:53 +01:00
_set_error ( " Path expected after $. " ) ;
return NULL ;
}
OperatorNode * op = alloc_node < OperatorNode > ( ) ;
2017-03-05 16:44:50 +01:00
op - > op = OperatorNode : : OP_CALL ;
2018-06-06 03:57:44 +02:00
op - > line = line ;
2017-01-08 06:04:53 +01:00
op - > arguments . push_back ( alloc_node < SelfNode > ( ) ) ;
2018-06-06 03:57:44 +02:00
op - > arguments [ 0 ] - > line = line ;
2017-01-08 06:04:53 +01:00
IdentifierNode * funcname = alloc_node < IdentifierNode > ( ) ;
2017-03-05 16:44:50 +01:00
funcname - > name = " get_node " ;
2018-06-06 03:57:44 +02:00
funcname - > line = line ;
2017-01-08 06:04:53 +01:00
op - > arguments . push_back ( funcname ) ;
ConstantNode * nodepath = alloc_node < ConstantNode > ( ) ;
nodepath - > value = NodePath ( StringName ( path ) ) ;
2018-05-30 04:16:54 +02:00
nodepath - > datatype = _type_from_variant ( nodepath - > value ) ;
2018-06-06 03:57:44 +02:00
nodepath - > line = line ;
2017-01-08 06:04:53 +01:00
op - > arguments . push_back ( nodepath ) ;
2017-03-05 16:44:50 +01:00
expr = op ;
2017-01-08 06:04:53 +01:00
2017-11-16 18:38:18 +01:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CURSOR ) {
2014-12-17 02:31:57 +01:00
tokenizer - > advance ( ) ;
continue ; //no point in cursor in the middle of expression
2014-02-10 02:10:30 +01:00
2017-11-16 18:38:18 +01:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CONSTANT ) {
2014-02-10 02:10:30 +01:00
//constant defined by tokenizer
ConstantNode * constant = alloc_node < ConstantNode > ( ) ;
2017-03-05 16:44:50 +01:00
constant - > value = tokenizer - > get_token_constant ( ) ;
2018-05-30 04:16:54 +02:00
constant - > datatype = _type_from_variant ( constant - > value ) ;
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2017-03-05 16:44:50 +01:00
expr = constant ;
2017-11-16 18:38:18 +01:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CONST_PI ) {
2016-01-02 21:56:45 +01:00
//constant defined by tokenizer
ConstantNode * constant = alloc_node < ConstantNode > ( ) ;
2017-03-05 16:44:50 +01:00
constant - > value = Math_PI ;
2018-05-30 04:16:54 +02:00
constant - > datatype = _type_from_variant ( constant - > value ) ;
2016-01-02 21:56:45 +01:00
tokenizer - > advance ( ) ;
2017-03-05 16:44:50 +01:00
expr = constant ;
2017-11-16 18:38:18 +01:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CONST_TAU ) {
2017-11-04 10:34:27 +01:00
//constant defined by tokenizer
ConstantNode * constant = alloc_node < ConstantNode > ( ) ;
constant - > value = Math_TAU ;
2018-05-30 04:16:54 +02:00
constant - > datatype = _type_from_variant ( constant - > value ) ;
2017-11-04 10:34:27 +01:00
tokenizer - > advance ( ) ;
expr = constant ;
2017-11-16 18:38:18 +01:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CONST_INF ) {
2017-02-06 23:44:22 +01:00
//constant defined by tokenizer
ConstantNode * constant = alloc_node < ConstantNode > ( ) ;
constant - > value = Math_INF ;
2018-05-30 04:16:54 +02:00
constant - > datatype = _type_from_variant ( constant - > value ) ;
2017-02-06 23:44:22 +01:00
tokenizer - > advance ( ) ;
expr = constant ;
2017-11-16 18:38:18 +01:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CONST_NAN ) {
2017-02-06 23:44:22 +01:00
//constant defined by tokenizer
ConstantNode * constant = alloc_node < ConstantNode > ( ) ;
constant - > value = Math_NAN ;
2018-05-30 04:16:54 +02:00
constant - > datatype = _type_from_variant ( constant - > value ) ;
2017-02-06 23:44:22 +01:00
tokenizer - > advance ( ) ;
expr = constant ;
2017-11-16 18:38:18 +01:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PR_PRELOAD ) {
2014-02-10 02:10:30 +01:00
//constant defined by tokenizer
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PARENTHESIS_OPEN ) {
2014-02-10 02:10:30 +01:00
_set_error ( " Expected '(' after 'preload' " ) ;
return NULL ;
}
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2016-01-24 23:45:11 +01:00
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CURSOR ) {
2017-05-08 21:39:27 +02:00
completion_cursor = StringName ( ) ;
completion_node = p_parent ;
completion_type = COMPLETION_RESOURCE_PATH ;
completion_class = current_class ;
completion_function = current_function ;
completion_line = tokenizer - > get_token_line ( ) ;
completion_block = current_block ;
completion_argument = 0 ;
completion_found = true ;
tokenizer - > advance ( ) ;
}
2016-01-24 23:45:11 +01:00
String path ;
2017-02-16 14:29:18 +01:00
bool found_constant = false ;
2016-01-24 23:45:11 +01:00
bool valid = false ;
2017-02-16 14:29:18 +01:00
ConstantNode * cn ;
2016-01-24 23:45:11 +01:00
Node * subexpr = _parse_and_reduce_expression ( p_parent , p_static ) ;
if ( subexpr ) {
if ( subexpr - > type = = Node : : TYPE_CONSTANT ) {
2017-03-05 16:44:50 +01:00
cn = static_cast < ConstantNode * > ( subexpr ) ;
2017-02-16 14:29:18 +01:00
found_constant = true ;
}
if ( subexpr - > type = = Node : : TYPE_IDENTIFIER ) {
2017-03-05 16:44:50 +01:00
IdentifierNode * in = static_cast < IdentifierNode * > ( subexpr ) ;
2017-02-16 14:29:18 +01:00
// Try to find the constant expression by the identifier
2018-05-30 04:16:54 +02:00
if ( current_class - > constant_expressions . has ( in - > name ) ) {
Node * cn_exp = current_class - > constant_expressions [ in - > name ] . expression ;
if ( cn_exp - > type = = Node : : TYPE_CONSTANT ) {
cn = static_cast < ConstantNode * > ( cn_exp ) ;
found_constant = true ;
2017-02-16 14:29:18 +01:00
}
2016-01-24 23:45:11 +01:00
}
}
2017-02-16 14:29:18 +01:00
if ( found_constant & & cn - > value . get_type ( ) = = Variant : : STRING ) {
valid = true ;
2017-03-05 16:44:50 +01:00
path = ( String ) cn - > value ;
2017-02-16 14:29:18 +01:00
}
2016-01-24 23:45:11 +01:00
}
2017-02-16 14:29:18 +01:00
2016-01-24 23:45:11 +01:00
if ( ! valid ) {
_set_error ( " expected string constant as 'preload' argument. " ) ;
2014-02-10 02:10:30 +01:00
return NULL ;
}
2017-02-16 14:29:18 +01:00
2017-03-05 16:44:50 +01:00
if ( ! path . is_abs_path ( ) & & base_path ! = " " )
path = base_path + " / " + path ;
path = path . replace ( " /// " , " // " ) . simplify_path ( ) ;
if ( path = = self_path ) {
2014-12-07 06:04:20 +01:00
_set_error ( " Can't preload itself (use 'get_script()'). " ) ;
return NULL ;
}
2014-06-28 04:21:45 +02:00
Ref < Resource > res ;
if ( ! validating ) {
//this can be too slow for just validating code
2018-01-21 10:35:47 +01:00
if ( for_completion & & ScriptCodeCompletionCache : : get_singleton ( ) & & FileAccess : : exists ( path ) ) {
2018-01-18 21:37:17 +01:00
res = ScriptCodeCompletionCache : : get_singleton ( ) - > get_cached_resource ( path ) ;
2018-01-21 10:35:47 +01:00
} else if ( ! for_completion | | FileAccess : : exists ( path ) ) {
2015-06-26 06:14:31 +02:00
res = ResourceLoader : : load ( path ) ;
}
2014-06-28 04:21:45 +02:00
} else {
if ( ! FileAccess : : exists ( path ) ) {
2017-03-05 16:44:50 +01:00
_set_error ( " Can't preload resource at path: " + path ) ;
2014-06-28 04:21:45 +02:00
return NULL ;
2018-03-04 17:53:21 +01:00
} else if ( ScriptCodeCompletionCache : : get_singleton ( ) ) {
res = ScriptCodeCompletionCache : : get_singleton ( ) - > get_cached_resource ( path ) ;
2014-06-28 04:21:45 +02:00
}
2014-02-10 02:10:30 +01:00
}
2018-03-04 17:53:21 +01:00
if ( ! res . is_valid ( ) ) {
_set_error ( " Can't preload resource at path: " + path ) ;
return NULL ;
}
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE ) {
2014-02-10 02:10:30 +01:00
_set_error ( " Expected ')' after 'preload' path " ) ;
return NULL ;
}
2018-05-30 04:16:54 +02:00
Ref < GDScript > gds = res ;
if ( gds . is_valid ( ) & & ! gds - > is_valid ( ) ) {
_set_error ( " Could not fully preload the script, possible cyclic reference or compilation error. " ) ;
return NULL ;
}
2017-05-08 21:39:27 +02:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
ConstantNode * constant = alloc_node < ConstantNode > ( ) ;
2017-03-05 16:44:50 +01:00
constant - > value = res ;
2018-05-30 04:16:54 +02:00
constant - > datatype = _type_from_variant ( constant - > value ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
expr = constant ;
2017-11-16 18:38:18 +01:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PR_YIELD ) {
2014-09-15 16:33:30 +02:00
2018-05-30 04:16:54 +02:00
if ( ! current_function ) {
_set_error ( " yield() can only be used inside function blocks. " ) ;
return NULL ;
}
current_function - > has_yield = true ;
2014-09-15 16:33:30 +02:00
tokenizer - > advance ( ) ;
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PARENTHESIS_OPEN ) {
2014-09-15 16:33:30 +02:00
_set_error ( " Expected '(' after 'yield' " ) ;
return NULL ;
}
tokenizer - > advance ( ) ;
OperatorNode * yield = alloc_node < OperatorNode > ( ) ;
2017-03-05 16:44:50 +01:00
yield - > op = OperatorNode : : OP_YIELD ;
2014-09-15 16:33:30 +02:00
2017-11-16 18:38:18 +01:00
while ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_NEWLINE ) {
2016-10-03 20:18:21 +02:00
tokenizer - > advance ( ) ;
}
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE ) {
2017-03-05 16:44:50 +01:00
expr = yield ;
2014-09-15 16:33:30 +02:00
tokenizer - > advance ( ) ;
} else {
2017-03-05 16:44:50 +01:00
parenthesis + + ;
2016-10-03 20:18:21 +02:00
2017-03-05 16:44:50 +01:00
Node * object = _parse_and_reduce_expression ( p_parent , p_static ) ;
2014-09-15 16:33:30 +02:00
if ( ! object )
return NULL ;
yield - > arguments . push_back ( object ) ;
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_COMMA ) {
2014-09-15 16:33:30 +02:00
_set_error ( " Expected ',' after first argument of 'yield' " ) ;
return NULL ;
}
tokenizer - > advance ( ) ;
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CURSOR ) {
2017-03-05 16:44:50 +01:00
completion_cursor = StringName ( ) ;
completion_node = object ;
completion_type = COMPLETION_YIELD ;
completion_class = current_class ;
completion_function = current_function ;
completion_line = tokenizer - > get_token_line ( ) ;
completion_argument = 0 ;
completion_block = current_block ;
completion_found = true ;
2016-08-07 03:11:03 +02:00
tokenizer - > advance ( ) ;
}
2017-03-05 16:44:50 +01:00
Node * signal = _parse_and_reduce_expression ( p_parent , p_static ) ;
2014-09-15 16:33:30 +02:00
if ( ! signal )
return NULL ;
yield - > arguments . push_back ( signal ) ;
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE ) {
2014-09-15 16:33:30 +02:00
_set_error ( " Expected ')' after second argument of 'yield' " ) ;
return NULL ;
}
2017-03-05 16:44:50 +01:00
parenthesis - - ;
2016-10-03 20:18:21 +02:00
2014-09-15 16:33:30 +02:00
tokenizer - > advance ( ) ;
2017-03-05 16:44:50 +01:00
expr = yield ;
2014-09-15 16:33:30 +02:00
}
2017-11-16 18:38:18 +01:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_SELF ) {
2014-02-10 02:10:30 +01:00
if ( p_static ) {
_set_error ( " 'self'' not allowed in static function or constant expression " ) ;
return NULL ;
}
//constant defined by tokenizer
SelfNode * self = alloc_node < SelfNode > ( ) ;
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2017-03-05 16:44:50 +01:00
expr = self ;
2017-11-16 18:38:18 +01:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_BUILT_IN_TYPE & & tokenizer - > get_token ( 1 ) = = GDScriptTokenizer : : TK_PERIOD ) {
2014-02-10 02:10:30 +01:00
2014-02-25 13:31:47 +01:00
Variant : : Type bi_type = tokenizer - > get_token_type ( ) ;
tokenizer - > advance ( 2 ) ;
2014-12-17 02:31:57 +01:00
StringName identifier ;
2017-03-05 16:44:50 +01:00
if ( _get_completable_identifier ( COMPLETION_BUILT_IN_TYPE_CONSTANT , identifier ) ) {
2014-12-17 02:31:57 +01:00
2017-03-05 16:44:50 +01:00
completion_built_in_constant = bi_type ;
2014-12-17 02:31:57 +01:00
}
2017-03-05 16:44:50 +01:00
if ( identifier = = StringName ( ) ) {
2014-02-10 02:10:30 +01:00
2018-01-18 22:03:34 +01:00
_set_error ( " Built-in type constant or static function expected after '.' " ) ;
2014-02-10 02:10:30 +01:00
return NULL ;
}
2017-12-15 16:43:27 +01:00
if ( ! Variant : : has_constant ( bi_type , identifier ) ) {
2014-02-10 02:10:30 +01:00
2018-01-18 22:03:34 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PARENTHESIS_OPEN & &
Variant : : is_method_const ( bi_type , identifier ) & &
Variant : : get_method_return_type ( bi_type , identifier ) = = bi_type ) {
tokenizer - > advance ( ) ;
OperatorNode * construct = alloc_node < OperatorNode > ( ) ;
construct - > op = OperatorNode : : OP_CALL ;
TypeNode * tn = alloc_node < TypeNode > ( ) ;
tn - > vtype = bi_type ;
construct - > arguments . push_back ( tn ) ;
OperatorNode * op = alloc_node < OperatorNode > ( ) ;
op - > op = OperatorNode : : OP_CALL ;
op - > arguments . push_back ( construct ) ;
IdentifierNode * id = alloc_node < IdentifierNode > ( ) ;
id - > name = identifier ;
op - > arguments . push_back ( id ) ;
2014-02-10 02:10:30 +01:00
2018-01-18 22:03:34 +01:00
if ( ! _parse_arguments ( op , op - > arguments , p_static , true ) )
return NULL ;
expr = op ;
} else {
2018-09-10 15:31:36 +02:00
// Object is a special case
bool valid = false ;
if ( bi_type = = Variant : : OBJECT ) {
int object_constant = ClassDB : : get_integer_constant ( " Object " , identifier , & valid ) ;
if ( valid ) {
ConstantNode * cn = alloc_node < ConstantNode > ( ) ;
cn - > value = object_constant ;
cn - > datatype = _type_from_variant ( cn - > value ) ;
expr = cn ;
}
}
if ( ! valid ) {
_set_error ( " Static constant ' " + identifier . operator String ( ) + " ' not present in built-in type " + Variant : : get_type_name ( bi_type ) + " . " ) ;
return NULL ;
}
2018-01-18 22:03:34 +01:00
}
} else {
ConstantNode * cn = alloc_node < ConstantNode > ( ) ;
2017-12-15 16:43:27 +01:00
cn - > value = Variant : : get_constant_value ( bi_type , identifier ) ;
2018-05-30 04:16:54 +02:00
cn - > datatype = _type_from_variant ( cn - > value ) ;
2018-01-18 22:03:34 +01:00
expr = cn ;
}
2014-12-17 02:31:57 +01:00
2017-11-16 18:38:18 +01:00
} else if ( tokenizer - > get_token ( 1 ) = = GDScriptTokenizer : : TK_PARENTHESIS_OPEN & & tokenizer - > is_token_literal ( ) ) {
2017-03-31 19:28:34 +02:00
// We check with is_token_literal, as this allows us to use match/sync/etc. as a name
2014-02-10 02:10:30 +01:00
//function or constructor
OperatorNode * op = alloc_node < OperatorNode > ( ) ;
2017-03-05 16:44:50 +01:00
op - > op = OperatorNode : : OP_CALL ;
2014-02-10 02:10:30 +01:00
2017-11-17 05:42:24 +01:00
//Do a quick Array and Dictionary Check. Replace if either require no arguments.
bool replaced = false ;
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_BUILT_IN_TYPE ) {
2017-11-17 05:42:24 +01:00
Variant : : Type ct = tokenizer - > get_token_type ( ) ;
2018-10-06 22:20:41 +02:00
if ( ! p_parsing_constant ) {
2017-11-17 05:42:24 +01:00
if ( ct = = Variant : : ARRAY ) {
if ( tokenizer - > get_token ( 2 ) = = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE ) {
ArrayNode * arr = alloc_node < ArrayNode > ( ) ;
expr = arr ;
replaced = true ;
tokenizer - > advance ( 3 ) ;
}
}
if ( ct = = Variant : : DICTIONARY ) {
if ( tokenizer - > get_token ( 2 ) = = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE ) {
DictionaryNode * dict = alloc_node < DictionaryNode > ( ) ;
expr = dict ;
replaced = true ;
tokenizer - > advance ( 3 ) ;
}
}
}
2014-02-10 02:10:30 +01:00
2017-11-17 05:42:24 +01:00
if ( ! replaced ) {
TypeNode * tn = alloc_node < TypeNode > ( ) ;
tn - > vtype = tokenizer - > get_token_type ( ) ;
op - > arguments . push_back ( tn ) ;
tokenizer - > advance ( 2 ) ;
}
2017-11-16 18:38:18 +01:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_BUILT_IN_FUNC ) {
2014-02-10 02:10:30 +01:00
BuiltInFunctionNode * bn = alloc_node < BuiltInFunctionNode > ( ) ;
2017-03-05 16:44:50 +01:00
bn - > function = tokenizer - > get_token_built_in_func ( ) ;
2014-02-10 02:10:30 +01:00
op - > arguments . push_back ( bn ) ;
2014-12-17 02:31:57 +01:00
tokenizer - > advance ( 2 ) ;
2014-02-10 02:10:30 +01:00
} else {
SelfNode * self = alloc_node < SelfNode > ( ) ;
op - > arguments . push_back ( self ) ;
2014-12-17 02:31:57 +01:00
StringName identifier ;
2017-03-05 16:44:50 +01:00
if ( _get_completable_identifier ( COMPLETION_FUNCTION , identifier ) ) {
2014-12-17 02:31:57 +01:00
}
2017-03-05 16:44:50 +01:00
IdentifierNode * id = alloc_node < IdentifierNode > ( ) ;
id - > name = identifier ;
2014-02-10 02:10:30 +01:00
op - > arguments . push_back ( id ) ;
2014-12-17 02:31:57 +01:00
tokenizer - > advance ( 1 ) ;
2014-02-10 02:10:30 +01:00
}
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CURSOR ) {
2014-12-17 02:31:57 +01:00
_make_completable_call ( 0 ) ;
2017-03-05 16:44:50 +01:00
completion_node = op ;
2014-12-17 02:31:57 +01:00
}
2017-11-17 05:42:24 +01:00
if ( ! replaced ) {
if ( ! _parse_arguments ( op , op - > arguments , p_static , true ) )
return NULL ;
expr = op ;
}
2017-03-31 19:28:34 +02:00
} else if ( tokenizer - > is_token_literal ( 0 , true ) ) {
// We check with is_token_literal, as this allows us to use match/sync/etc. as a name
2014-02-10 02:10:30 +01:00
//identifier (reference)
2017-03-05 16:44:50 +01:00
const ClassNode * cln = current_class ;
bool bfn = false ;
StringName identifier ;
2018-05-30 04:16:54 +02:00
int id_line = tokenizer - > get_token_line ( ) ;
2017-03-05 16:44:50 +01:00
if ( _get_completable_identifier ( COMPLETION_IDENTIFIER , identifier ) ) {
2014-12-17 02:31:57 +01:00
}
2018-05-30 04:16:54 +02:00
BlockNode * b = current_block ;
2018-07-01 18:17:40 +02:00
while ( ! bfn & & b ) {
2018-05-30 04:16:54 +02:00
if ( b - > variables . has ( identifier ) ) {
IdentifierNode * id = alloc_node < IdentifierNode > ( ) ;
id - > name = identifier ;
id - > declared_block = b ;
id - > line = id_line ;
expr = id ;
bfn = true ;
2014-12-17 02:31:57 +01:00
2018-07-01 18:17:40 +02:00
# ifdef DEBUG_ENABLED
2018-10-03 16:13:34 +02:00
LocalVarNode * lv = b - > variables [ identifier ] ;
2018-05-30 04:16:54 +02:00
switch ( tokenizer - > get_token ( ) ) {
case GDScriptTokenizer : : TK_OP_ASSIGN_ADD :
case GDScriptTokenizer : : TK_OP_ASSIGN_BIT_AND :
case GDScriptTokenizer : : TK_OP_ASSIGN_BIT_OR :
case GDScriptTokenizer : : TK_OP_ASSIGN_BIT_XOR :
case GDScriptTokenizer : : TK_OP_ASSIGN_DIV :
case GDScriptTokenizer : : TK_OP_ASSIGN_MOD :
case GDScriptTokenizer : : TK_OP_ASSIGN_MUL :
case GDScriptTokenizer : : TK_OP_ASSIGN_SHIFT_LEFT :
case GDScriptTokenizer : : TK_OP_ASSIGN_SHIFT_RIGHT :
case GDScriptTokenizer : : TK_OP_ASSIGN_SUB : {
2018-07-01 18:17:40 +02:00
if ( lv - > assignments = = 0 ) {
if ( ! lv - > datatype . has_type ) {
_set_error ( " Using assignment with operation on a variable that was never assigned. " ) ;
return NULL ;
}
_add_warning ( GDScriptWarning : : UNASSIGNED_VARIABLE_OP_ASSIGN , - 1 , identifier . operator String ( ) ) ;
2018-05-30 04:16:54 +02:00
}
} // fallthrough
case GDScriptTokenizer : : TK_OP_ASSIGN : {
lv - > assignments + = 1 ;
2018-07-01 18:17:40 +02:00
lv - > usages - - ; // Assignment is not really usage
} break ;
default : {
lv - > usages + + ;
2018-05-30 04:16:54 +02:00
}
2016-06-30 15:40:13 +02:00
}
2018-07-01 18:17:40 +02:00
# endif // DEBUG_ENABLED
2018-05-30 04:16:54 +02:00
break ;
2014-12-17 02:31:57 +01:00
}
2018-05-30 04:16:54 +02:00
b = b - > parent_block ;
}
2014-12-17 02:31:57 +01:00
2018-05-30 04:16:54 +02:00
if ( ! bfn & & p_parsing_constant ) {
if ( cln - > constant_expressions . has ( identifier ) ) {
expr = cln - > constant_expressions [ identifier ] . expression ;
bfn = true ;
} else if ( GDScriptLanguage : : get_singleton ( ) - > get_global_map ( ) . has ( identifier ) ) {
2016-06-30 15:40:13 +02:00
//check from constants
ConstantNode * constant = alloc_node < ConstantNode > ( ) ;
2017-03-05 16:44:50 +01:00
constant - > value = GDScriptLanguage : : get_singleton ( ) - > get_global_array ( ) [ GDScriptLanguage : : get_singleton ( ) - > get_global_map ( ) [ identifier ] ] ;
2018-05-30 04:16:54 +02:00
constant - > datatype = _type_from_variant ( constant - > value ) ;
constant - > line = id_line ;
2017-03-05 16:44:50 +01:00
expr = constant ;
2016-06-30 15:40:13 +02:00
bfn = true ;
}
2018-05-28 18:38:35 +02:00
if ( ! bfn & & GDScriptLanguage : : get_singleton ( ) - > get_named_globals_map ( ) . has ( identifier ) ) {
//check from singletons
ConstantNode * constant = alloc_node < ConstantNode > ( ) ;
constant - > value = GDScriptLanguage : : get_singleton ( ) - > get_named_globals_map ( ) [ identifier ] ;
expr = constant ;
bfn = true ;
}
2016-06-30 03:17:55 +02:00
}
2017-03-05 16:44:50 +01:00
if ( ! bfn ) {
2018-07-01 18:17:40 +02:00
# ifdef DEBUG_ENABLED
if ( current_function ) {
int arg_idx = current_function - > arguments . find ( identifier ) ;
if ( arg_idx ! = - 1 ) {
switch ( tokenizer - > get_token ( ) ) {
case GDScriptTokenizer : : TK_OP_ASSIGN_ADD :
case GDScriptTokenizer : : TK_OP_ASSIGN_BIT_AND :
case GDScriptTokenizer : : TK_OP_ASSIGN_BIT_OR :
case GDScriptTokenizer : : TK_OP_ASSIGN_BIT_XOR :
case GDScriptTokenizer : : TK_OP_ASSIGN_DIV :
case GDScriptTokenizer : : TK_OP_ASSIGN_MOD :
case GDScriptTokenizer : : TK_OP_ASSIGN_MUL :
case GDScriptTokenizer : : TK_OP_ASSIGN_SHIFT_LEFT :
case GDScriptTokenizer : : TK_OP_ASSIGN_SHIFT_RIGHT :
case GDScriptTokenizer : : TK_OP_ASSIGN_SUB :
case GDScriptTokenizer : : TK_OP_ASSIGN : {
// Assignment is not really usage
current_function - > arguments_usage . write [ arg_idx ] = current_function - > arguments_usage [ arg_idx ] - 1 ;
} break ;
default : {
current_function - > arguments_usage . write [ arg_idx ] = current_function - > arguments_usage [ arg_idx ] + 1 ;
}
}
}
}
# endif // DEBUG_ENABLED
2014-12-17 02:31:57 +01:00
IdentifierNode * id = alloc_node < IdentifierNode > ( ) ;
id - > name = identifier ;
2018-05-30 04:16:54 +02:00
id - > line = id_line ;
2014-12-17 02:31:57 +01:00
expr = id ;
}
2014-02-10 02:10:30 +01:00
2017-11-16 18:38:18 +01:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_OP_ADD | | tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_OP_SUB | | tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_OP_NOT | | tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_OP_BIT_INVERT ) {
2014-02-10 02:10:30 +01:00
2016-10-12 23:43:59 +02:00
//single prefix operators like !expr +expr -expr ++expr --expr
2016-07-09 03:12:50 +02:00
alloc_node < OperatorNode > ( ) ;
2014-02-10 02:10:30 +01:00
Expression e ;
2017-03-05 16:44:50 +01:00
e . is_op = true ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
switch ( tokenizer - > get_token ( ) ) {
2017-11-16 18:38:18 +01:00
case GDScriptTokenizer : : TK_OP_ADD : e . op = OperatorNode : : OP_POS ; break ;
case GDScriptTokenizer : : TK_OP_SUB : e . op = OperatorNode : : OP_NEG ; break ;
case GDScriptTokenizer : : TK_OP_NOT : e . op = OperatorNode : : OP_NOT ; break ;
case GDScriptTokenizer : : TK_OP_BIT_INVERT : e . op = OperatorNode : : OP_BIT_INVERT ; break ;
2014-02-10 02:10:30 +01:00
default : { }
}
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
2017-11-16 18:38:18 +01:00
if ( e . op ! = OperatorNode : : OP_NOT & & tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_OP_NOT ) {
2014-02-10 02:10:30 +01:00
_set_error ( " Misplaced 'not'. " ) ;
return NULL ;
}
expression . push_back ( e ) ;
continue ; //only exception, must continue...
/*
Node * subexpr = _parse_expression ( op , p_static ) ;
if ( ! subexpr )
return NULL ;
op - > arguments . push_back ( subexpr ) ;
expr = op ; */
2018-08-26 18:31:23 +02:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PR_IS & & tokenizer - > get_token ( 1 ) = = GDScriptTokenizer : : TK_BUILT_IN_TYPE ) {
// 'is' operator with built-in type
OperatorNode * op = alloc_node < OperatorNode > ( ) ;
op - > op = OperatorNode : : OP_IS_BUILTIN ;
op - > arguments . push_back ( expr ) ;
tokenizer - > advance ( ) ;
TypeNode * tn = alloc_node < TypeNode > ( ) ;
tn - > vtype = tokenizer - > get_token_type ( ) ;
op - > arguments . push_back ( tn ) ;
tokenizer - > advance ( ) ;
expr = op ;
2017-11-16 18:38:18 +01:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_BRACKET_OPEN ) {
2014-02-10 02:10:30 +01:00
// array
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
ArrayNode * arr = alloc_node < ArrayNode > ( ) ;
2017-03-05 16:44:50 +01:00
bool expecting_comma = false ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
while ( true ) {
2014-02-10 02:10:30 +01:00
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_EOF ) {
2014-02-10 02:10:30 +01:00
_set_error ( " Unterminated array " ) ;
return NULL ;
2017-11-16 18:38:18 +01:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_BRACKET_CLOSE ) {
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
break ;
2017-11-16 18:38:18 +01:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_NEWLINE ) {
2014-02-10 02:10:30 +01:00
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ; //ignore newline
2017-11-16 18:38:18 +01:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COMMA ) {
2014-02-10 02:10:30 +01:00
if ( ! expecting_comma ) {
_set_error ( " expression or ']' expected " ) ;
return NULL ;
}
2017-03-05 16:44:50 +01:00
expecting_comma = false ;
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ; //ignore newline
2014-02-10 02:10:30 +01:00
} else {
//parse expression
if ( expecting_comma ) {
_set_error ( " ',' or ']' expected " ) ;
return NULL ;
}
2017-03-05 16:44:50 +01:00
Node * n = _parse_expression ( arr , p_static , p_allow_assign , p_parsing_constant ) ;
2014-02-10 02:10:30 +01:00
if ( ! n )
return NULL ;
arr - > elements . push_back ( n ) ;
2017-03-05 16:44:50 +01:00
expecting_comma = true ;
2014-02-10 02:10:30 +01:00
}
}
2017-03-05 16:44:50 +01:00
expr = arr ;
2017-11-16 18:38:18 +01:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CURLY_BRACKET_OPEN ) {
2014-02-10 02:10:30 +01:00
// array
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
DictionaryNode * dict = alloc_node < DictionaryNode > ( ) ;
enum DictExpect {
DICT_EXPECT_KEY ,
DICT_EXPECT_COLON ,
DICT_EXPECT_VALUE ,
DICT_EXPECT_COMMA
} ;
2017-03-05 16:44:50 +01:00
Node * key = NULL ;
2016-11-26 13:40:13 +01:00
Set < Variant > keys ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
DictExpect expecting = DICT_EXPECT_KEY ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
while ( true ) {
2014-02-10 02:10:30 +01:00
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_EOF ) {
2014-02-10 02:10:30 +01:00
_set_error ( " Unterminated dictionary " ) ;
return NULL ;
2017-11-16 18:38:18 +01:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CURLY_BRACKET_CLOSE ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( expecting = = DICT_EXPECT_COLON ) {
2014-02-10 02:10:30 +01:00
_set_error ( " ':' expected " ) ;
return NULL ;
}
2017-03-05 16:44:50 +01:00
if ( expecting = = DICT_EXPECT_VALUE ) {
2014-02-10 02:10:30 +01:00
_set_error ( " value expected " ) ;
return NULL ;
}
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
break ;
2017-11-16 18:38:18 +01:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_NEWLINE ) {
2014-02-10 02:10:30 +01:00
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ; //ignore newline
2017-11-16 18:38:18 +01:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COMMA ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( expecting = = DICT_EXPECT_KEY ) {
2014-02-10 02:10:30 +01:00
_set_error ( " key or '}' expected " ) ;
return NULL ;
}
2017-03-05 16:44:50 +01:00
if ( expecting = = DICT_EXPECT_VALUE ) {
2014-02-10 02:10:30 +01:00
_set_error ( " value expected " ) ;
return NULL ;
}
2017-03-05 16:44:50 +01:00
if ( expecting = = DICT_EXPECT_COLON ) {
2014-02-10 02:10:30 +01:00
_set_error ( " ':' expected " ) ;
return NULL ;
}
2017-03-05 16:44:50 +01:00
expecting = DICT_EXPECT_KEY ;
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ; //ignore newline
2014-02-10 02:10:30 +01:00
2017-11-16 18:38:18 +01:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COLON ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( expecting = = DICT_EXPECT_KEY ) {
2014-02-10 02:10:30 +01:00
_set_error ( " key or '}' expected " ) ;
return NULL ;
}
2017-03-05 16:44:50 +01:00
if ( expecting = = DICT_EXPECT_VALUE ) {
2014-02-10 02:10:30 +01:00
_set_error ( " value expected " ) ;
return NULL ;
}
2017-03-05 16:44:50 +01:00
if ( expecting = = DICT_EXPECT_COMMA ) {
2014-02-10 02:10:30 +01:00
_set_error ( " ',' or '}' expected " ) ;
return NULL ;
}
2017-03-05 16:44:50 +01:00
expecting = DICT_EXPECT_VALUE ;
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ; //ignore newline
2014-02-10 02:10:30 +01:00
} else {
2017-03-05 16:44:50 +01:00
if ( expecting = = DICT_EXPECT_COMMA ) {
2014-02-10 02:10:30 +01:00
_set_error ( " ',' or '}' expected " ) ;
return NULL ;
}
2017-03-05 16:44:50 +01:00
if ( expecting = = DICT_EXPECT_COLON ) {
2014-02-10 02:10:30 +01:00
_set_error ( " ':' expected " ) ;
return NULL ;
}
2017-03-05 16:44:50 +01:00
if ( expecting = = DICT_EXPECT_KEY ) {
2014-02-10 02:10:30 +01:00
2017-11-16 18:38:18 +01:00
if ( tokenizer - > is_token_literal ( ) & & tokenizer - > get_token ( 1 ) = = GDScriptTokenizer : : TK_OP_ASSIGN ) {
2017-03-31 19:28:34 +02:00
// We check with is_token_literal, as this allows us to use match/sync/etc. as a name
2014-02-10 02:10:30 +01:00
//lua style identifier, easier to write
ConstantNode * cn = alloc_node < ConstantNode > ( ) ;
2017-03-31 19:28:34 +02:00
cn - > value = tokenizer - > get_token_literal ( ) ;
2018-05-30 04:16:54 +02:00
cn - > datatype = _type_from_variant ( cn - > value ) ;
2014-02-10 02:10:30 +01:00
key = cn ;
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( 2 ) ;
2017-03-05 16:44:50 +01:00
expecting = DICT_EXPECT_VALUE ;
2014-02-10 02:10:30 +01:00
} else {
//python/js style more flexible
2017-03-05 16:44:50 +01:00
key = _parse_expression ( dict , p_static , p_allow_assign , p_parsing_constant ) ;
2014-02-10 02:10:30 +01:00
if ( ! key )
return NULL ;
2017-03-05 16:44:50 +01:00
expecting = DICT_EXPECT_COLON ;
2014-02-10 02:10:30 +01:00
}
}
2017-03-05 16:44:50 +01:00
if ( expecting = = DICT_EXPECT_VALUE ) {
Node * value = _parse_expression ( dict , p_static , p_allow_assign , p_parsing_constant ) ;
2014-02-10 02:10:30 +01:00
if ( ! value )
return NULL ;
2017-03-05 16:44:50 +01:00
expecting = DICT_EXPECT_COMMA ;
2014-02-10 02:10:30 +01:00
2017-11-16 18:38:18 +01:00
if ( key - > type = = GDScriptParser : : Node : : TYPE_CONSTANT ) {
Variant const & keyName = static_cast < const GDScriptParser : : ConstantNode * > ( key ) - > value ;
2016-11-26 13:40:13 +01:00
if ( keys . has ( keyName ) ) {
_set_error ( " Duplicate key found in Dictionary literal " ) ;
return NULL ;
}
keys . insert ( keyName ) ;
}
2014-02-10 02:10:30 +01:00
DictionaryNode : : Pair pair ;
2017-03-05 16:44:50 +01:00
pair . key = key ;
pair . value = value ;
2014-02-10 02:10:30 +01:00
dict - > elements . push_back ( pair ) ;
2017-03-05 16:44:50 +01:00
key = NULL ;
2014-02-10 02:10:30 +01:00
}
}
}
2017-03-05 16:44:50 +01:00
expr = dict ;
2014-02-10 02:10:30 +01:00
2018-06-21 03:41:26 +02:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PERIOD & & ( tokenizer - > is_token_literal ( 1 ) | | tokenizer - > get_token ( 1 ) = = GDScriptTokenizer : : TK_CURSOR ) ) {
2017-03-31 19:28:34 +02:00
// We check with is_token_literal, as this allows us to use match/sync/etc. as a name
2014-02-10 02:10:30 +01:00
// parent call
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ; //goto identifier
2014-02-10 02:10:30 +01:00
OperatorNode * op = alloc_node < OperatorNode > ( ) ;
2017-03-05 16:44:50 +01:00
op - > op = OperatorNode : : OP_PARENT_CALL ;
2014-02-10 02:10:30 +01:00
/*SelfNode *self = alloc_node<SelfNode>();
op - > arguments . push_back ( self ) ;
forbidden for now */
2014-12-17 02:31:57 +01:00
StringName identifier ;
2018-06-21 03:41:26 +02:00
bool is_completion = _get_completable_identifier ( COMPLETION_PARENT_FUNCTION , identifier ) & & for_completion ;
2014-02-10 02:10:30 +01:00
2014-12-17 02:31:57 +01:00
IdentifierNode * id = alloc_node < IdentifierNode > ( ) ;
2017-03-05 16:44:50 +01:00
id - > name = identifier ;
2014-02-10 02:10:30 +01:00
op - > arguments . push_back ( id ) ;
2018-06-21 03:41:26 +02:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PARENTHESIS_OPEN ) {
if ( ! is_completion ) {
_set_error ( " Expected '(' for parent function call. " ) ;
return NULL ;
}
} else {
tokenizer - > advance ( ) ;
if ( ! _parse_arguments ( op , op - > arguments , p_static ) ) {
return NULL ;
}
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
expr = op ;
2014-02-10 02:10:30 +01:00
2018-08-26 18:31:23 +02:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_BUILT_IN_TYPE & & expression . size ( ) > 0 & & expression [ expression . size ( ) - 1 ] . is_op & & expression [ expression . size ( ) - 1 ] . op = = OperatorNode : : OP_IS ) {
Expression e = expression [ expression . size ( ) - 1 ] ;
e . op = OperatorNode : : OP_IS_BUILTIN ;
expression . write [ expression . size ( ) - 1 ] = e ;
TypeNode * tn = alloc_node < TypeNode > ( ) ;
tn - > vtype = tokenizer - > get_token_type ( ) ;
expr = tn ;
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
} else {
//find list [ or find dictionary {
2017-03-05 16:44:50 +01:00
_set_error ( " Error parsing expression, misplaced: " + String ( tokenizer - > get_token_name ( tokenizer - > get_token ( ) ) ) ) ;
return NULL ; //nothing
2014-02-10 02:10:30 +01:00
}
if ( ! expr ) {
2018-04-22 19:36:01 +02:00
ERR_EXPLAIN ( " GDScriptParser bug, couldn't figure out what expression is... " ) ;
2017-03-05 16:44:50 +01:00
ERR_FAIL_COND_V ( ! expr , NULL ) ;
2014-02-10 02:10:30 +01:00
}
/******************/
/* Parse Indexing */
/******************/
while ( true ) {
//expressions can be indexed any number of times
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PERIOD ) {
2014-02-10 02:10:30 +01:00
//indexing using "."
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( 1 ) ! = GDScriptTokenizer : : TK_CURSOR & & ! tokenizer - > is_token_literal ( 1 ) ) {
2017-03-31 19:28:34 +02:00
// We check with is_token_literal, as this allows us to use match/sync/etc. as a name
2014-02-10 02:10:30 +01:00
_set_error ( " Expected identifier as member " ) ;
return NULL ;
2017-11-16 18:38:18 +01:00
} else if ( tokenizer - > get_token ( 2 ) = = GDScriptTokenizer : : TK_PARENTHESIS_OPEN ) {
2014-02-10 02:10:30 +01:00
//call!!
2017-03-05 16:44:50 +01:00
OperatorNode * op = alloc_node < OperatorNode > ( ) ;
op - > op = OperatorNode : : OP_CALL ;
2014-02-10 02:10:30 +01:00
2014-12-17 02:31:57 +01:00
tokenizer - > advance ( ) ;
2017-03-05 16:44:50 +01:00
IdentifierNode * id = alloc_node < IdentifierNode > ( ) ;
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_BUILT_IN_FUNC ) {
2014-02-16 01:16:33 +01:00
//small hack so built in funcs don't obfuscate methods
2017-11-16 18:38:18 +01:00
id - > name = GDScriptFunctions : : get_func_name ( tokenizer - > get_token_built_in_func ( ) ) ;
2014-12-17 02:31:57 +01:00
tokenizer - > advance ( ) ;
2014-02-16 01:16:33 +01:00
} else {
2014-12-17 02:31:57 +01:00
StringName identifier ;
2017-03-05 16:44:50 +01:00
if ( _get_completable_identifier ( COMPLETION_METHOD , identifier ) ) {
completion_node = op ;
2014-12-17 02:31:57 +01:00
//indexing stuff
}
2017-03-05 16:44:50 +01:00
id - > name = identifier ;
2014-02-16 01:16:33 +01:00
}
2014-02-10 02:10:30 +01:00
op - > arguments . push_back ( expr ) ; // call what
op - > arguments . push_back ( id ) ; // call func
//get arguments
2014-12-17 02:31:57 +01:00
tokenizer - > advance ( 1 ) ;
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CURSOR ) {
2014-12-17 02:31:57 +01:00
_make_completable_call ( 0 ) ;
2017-03-05 16:44:50 +01:00
completion_node = op ;
2014-12-17 02:31:57 +01:00
}
2017-03-05 16:44:50 +01:00
if ( ! _parse_arguments ( op , op - > arguments , p_static , true ) )
2014-02-10 02:10:30 +01:00
return NULL ;
2017-03-05 16:44:50 +01:00
expr = op ;
2014-02-10 02:10:30 +01:00
} else {
//simple indexing!
2014-12-17 02:31:57 +01:00
2017-03-05 16:44:50 +01:00
OperatorNode * op = alloc_node < OperatorNode > ( ) ;
op - > op = OperatorNode : : OP_INDEX_NAMED ;
2014-12-17 02:31:57 +01:00
tokenizer - > advance ( ) ;
StringName identifier ;
2017-03-05 16:44:50 +01:00
if ( _get_completable_identifier ( COMPLETION_INDEX , identifier ) ) {
2014-12-17 02:31:57 +01:00
2017-03-05 16:44:50 +01:00
if ( identifier = = StringName ( ) ) {
identifier = " @temp " ; //so it parses allright
2014-12-17 02:31:57 +01:00
}
2017-03-05 16:44:50 +01:00
completion_node = op ;
2014-12-17 02:31:57 +01:00
//indexing stuff
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
IdentifierNode * id = alloc_node < IdentifierNode > ( ) ;
id - > name = identifier ;
2014-02-10 02:10:30 +01:00
op - > arguments . push_back ( expr ) ;
op - > arguments . push_back ( id ) ;
2017-03-05 16:44:50 +01:00
expr = op ;
2014-02-10 02:10:30 +01:00
}
2017-11-16 18:38:18 +01:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_BRACKET_OPEN ) {
2014-02-10 02:10:30 +01:00
//indexing using "[]"
2017-03-05 16:44:50 +01:00
OperatorNode * op = alloc_node < OperatorNode > ( ) ;
op - > op = OperatorNode : : OP_INDEX ;
2014-02-10 02:10:30 +01:00
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( 1 ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
Node * subexpr = _parse_expression ( op , p_static , p_allow_assign , p_parsing_constant ) ;
2014-02-10 02:10:30 +01:00
if ( ! subexpr ) {
return NULL ;
}
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_BRACKET_CLOSE ) {
2014-02-10 02:10:30 +01:00
_set_error ( " Expected ']' " ) ;
return NULL ;
}
op - > arguments . push_back ( expr ) ;
op - > arguments . push_back ( subexpr ) ;
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( 1 ) ;
2017-03-05 16:44:50 +01:00
expr = op ;
2014-02-10 02:10:30 +01:00
} else
break ;
}
2018-05-30 04:16:51 +02:00
/*****************/
/* Parse Casting */
/*****************/
bool has_casting = expr - > type = = Node : : TYPE_CAST ;
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PR_AS ) {
if ( has_casting ) {
_set_error ( " Unexpected 'as'. " ) ;
return NULL ;
}
CastNode * cn = alloc_node < CastNode > ( ) ;
2018-05-30 04:16:53 +02:00
if ( ! _parse_type ( cn - > cast_type ) ) {
2018-05-30 04:16:51 +02:00
_set_error ( " Expected type after 'as'. " ) ;
return NULL ;
}
has_casting = true ;
cn - > source_node = expr ;
expr = cn ;
}
2014-02-10 02:10:30 +01:00
/******************/
/* Parse Operator */
/******************/
2017-03-05 16:44:50 +01:00
if ( parenthesis > 0 ) {
2014-04-05 23:50:09 +02:00
//remove empty space (only allowed if inside parenthesis
2017-11-16 18:38:18 +01:00
while ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_NEWLINE ) {
2014-04-05 23:50:09 +02:00
tokenizer - > advance ( ) ;
}
}
2014-02-10 02:10:30 +01:00
Expression e ;
2017-03-05 16:44:50 +01:00
e . is_op = false ;
e . node = expr ;
2014-02-10 02:10:30 +01:00
expression . push_back ( e ) ;
// determine which operator is next
OperatorNode : : Operator op ;
2017-03-05 16:44:50 +01:00
bool valid = true ;
2014-02-10 02:10:30 +01:00
2017-03-24 21:45:31 +01:00
//assign, if allowed is only allowed on the first operator
2017-03-05 16:44:50 +01:00
# define _VALIDATE_ASSIGN \
2018-05-30 04:16:51 +02:00
if ( ! p_allow_assign | | has_casting ) { \
2017-03-05 16:44:50 +01:00
_set_error ( " Unexpected assign. " ) ; \
return NULL ; \
} \
p_allow_assign = false ;
2017-08-24 05:06:56 +02:00
2017-03-05 16:44:50 +01:00
switch ( tokenizer - > get_token ( ) ) { //see operator
2017-11-16 18:38:18 +01:00
case GDScriptTokenizer : : TK_OP_IN : op = OperatorNode : : OP_IN ; break ;
case GDScriptTokenizer : : TK_OP_EQUAL : op = OperatorNode : : OP_EQUAL ; break ;
case GDScriptTokenizer : : TK_OP_NOT_EQUAL : op = OperatorNode : : OP_NOT_EQUAL ; break ;
case GDScriptTokenizer : : TK_OP_LESS : op = OperatorNode : : OP_LESS ; break ;
case GDScriptTokenizer : : TK_OP_LESS_EQUAL : op = OperatorNode : : OP_LESS_EQUAL ; break ;
case GDScriptTokenizer : : TK_OP_GREATER : op = OperatorNode : : OP_GREATER ; break ;
case GDScriptTokenizer : : TK_OP_GREATER_EQUAL : op = OperatorNode : : OP_GREATER_EQUAL ; break ;
case GDScriptTokenizer : : TK_OP_AND : op = OperatorNode : : OP_AND ; break ;
case GDScriptTokenizer : : TK_OP_OR : op = OperatorNode : : OP_OR ; break ;
case GDScriptTokenizer : : TK_OP_ADD : op = OperatorNode : : OP_ADD ; break ;
case GDScriptTokenizer : : TK_OP_SUB : op = OperatorNode : : OP_SUB ; break ;
case GDScriptTokenizer : : TK_OP_MUL : op = OperatorNode : : OP_MUL ; break ;
case GDScriptTokenizer : : TK_OP_DIV : op = OperatorNode : : OP_DIV ; break ;
case GDScriptTokenizer : : TK_OP_MOD :
2017-03-05 16:44:50 +01:00
op = OperatorNode : : OP_MOD ;
break ;
2017-11-16 18:38:18 +01:00
//case GDScriptTokenizer::TK_OP_NEG: op=OperatorNode::OP_NEG ; break;
case GDScriptTokenizer : : TK_OP_SHIFT_LEFT : op = OperatorNode : : OP_SHIFT_LEFT ; break ;
case GDScriptTokenizer : : TK_OP_SHIFT_RIGHT : op = OperatorNode : : OP_SHIFT_RIGHT ; break ;
case GDScriptTokenizer : : TK_OP_ASSIGN : {
2017-08-24 05:06:56 +02:00
_VALIDATE_ASSIGN op = OperatorNode : : OP_ASSIGN ;
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( 1 ) = = GDScriptTokenizer : : TK_CURSOR ) {
2017-08-24 05:06:56 +02:00
//code complete assignment
completion_type = COMPLETION_ASSIGN ;
completion_node = expr ;
completion_class = current_class ;
completion_function = current_function ;
completion_line = tokenizer - > get_token_line ( ) ;
completion_block = current_block ;
completion_found = true ;
tokenizer - > advance ( ) ;
}
} break ;
2017-11-16 18:38:18 +01:00
case GDScriptTokenizer : : TK_OP_ASSIGN_ADD : _VALIDATE_ASSIGN op = OperatorNode : : OP_ASSIGN_ADD ; break ;
case GDScriptTokenizer : : TK_OP_ASSIGN_SUB : _VALIDATE_ASSIGN op = OperatorNode : : OP_ASSIGN_SUB ; break ;
case GDScriptTokenizer : : TK_OP_ASSIGN_MUL : _VALIDATE_ASSIGN op = OperatorNode : : OP_ASSIGN_MUL ; break ;
case GDScriptTokenizer : : TK_OP_ASSIGN_DIV : _VALIDATE_ASSIGN op = OperatorNode : : OP_ASSIGN_DIV ; break ;
case GDScriptTokenizer : : TK_OP_ASSIGN_MOD : _VALIDATE_ASSIGN op = OperatorNode : : OP_ASSIGN_MOD ; break ;
case GDScriptTokenizer : : TK_OP_ASSIGN_SHIFT_LEFT : _VALIDATE_ASSIGN op = OperatorNode : : OP_ASSIGN_SHIFT_LEFT ; break ;
case GDScriptTokenizer : : TK_OP_ASSIGN_SHIFT_RIGHT : _VALIDATE_ASSIGN op = OperatorNode : : OP_ASSIGN_SHIFT_RIGHT ; break ;
case GDScriptTokenizer : : TK_OP_ASSIGN_BIT_AND : _VALIDATE_ASSIGN op = OperatorNode : : OP_ASSIGN_BIT_AND ; break ;
case GDScriptTokenizer : : TK_OP_ASSIGN_BIT_OR : _VALIDATE_ASSIGN op = OperatorNode : : OP_ASSIGN_BIT_OR ; break ;
case GDScriptTokenizer : : TK_OP_ASSIGN_BIT_XOR : _VALIDATE_ASSIGN op = OperatorNode : : OP_ASSIGN_BIT_XOR ; break ;
case GDScriptTokenizer : : TK_OP_BIT_AND : op = OperatorNode : : OP_BIT_AND ; break ;
case GDScriptTokenizer : : TK_OP_BIT_OR : op = OperatorNode : : OP_BIT_OR ; break ;
case GDScriptTokenizer : : TK_OP_BIT_XOR : op = OperatorNode : : OP_BIT_XOR ; break ;
case GDScriptTokenizer : : TK_PR_IS : op = OperatorNode : : OP_IS ; break ;
case GDScriptTokenizer : : TK_CF_IF : op = OperatorNode : : OP_TERNARY_IF ; break ;
case GDScriptTokenizer : : TK_CF_ELSE : op = OperatorNode : : OP_TERNARY_ELSE ; break ;
2017-03-05 16:44:50 +01:00
default : valid = false ; break ;
2014-02-10 02:10:30 +01:00
}
2016-03-09 00:00:52 +01:00
if ( valid ) {
2017-03-05 16:44:50 +01:00
e . is_op = true ;
e . op = op ;
2014-02-10 02:10:30 +01:00
expression . push_back ( e ) ;
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
} else {
break ;
}
}
/* Reduce the set set of expressions and place them in an operator tree, respecting precedence */
2017-03-05 16:44:50 +01:00
while ( expression . size ( ) > 1 ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
int next_op = - 1 ;
int min_priority = 0xFFFFF ;
bool is_unary = false ;
bool is_ternary = false ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < expression . size ( ) ; i + + ) {
2014-02-10 02:10:30 +01:00
if ( ! expression [ i ] . is_op ) {
continue ;
}
int priority ;
2017-03-05 16:44:50 +01:00
bool unary = false ;
bool ternary = false ;
bool error = false ;
2017-12-11 14:36:32 +01:00
bool right_to_left = false ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
switch ( expression [ i ] . op ) {
2014-02-10 02:10:30 +01:00
2017-05-26 19:45:39 +02:00
case OperatorNode : : OP_IS :
2018-08-26 18:31:23 +02:00
case OperatorNode : : OP_IS_BUILTIN :
2017-03-05 16:44:50 +01:00
priority = - 1 ;
break ; //before anything
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
case OperatorNode : : OP_BIT_INVERT :
priority = 0 ;
unary = true ;
break ;
case OperatorNode : : OP_NEG :
priority = 1 ;
unary = true ;
break ;
case OperatorNode : : OP_POS :
priority = 1 ;
unary = true ;
break ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
case OperatorNode : : OP_MUL : priority = 2 ; break ;
case OperatorNode : : OP_DIV : priority = 2 ; break ;
case OperatorNode : : OP_MOD : priority = 2 ; break ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
case OperatorNode : : OP_ADD : priority = 3 ; break ;
case OperatorNode : : OP_SUB : priority = 3 ; break ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
case OperatorNode : : OP_SHIFT_LEFT : priority = 4 ; break ;
case OperatorNode : : OP_SHIFT_RIGHT : priority = 4 ; break ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
case OperatorNode : : OP_BIT_AND : priority = 5 ; break ;
case OperatorNode : : OP_BIT_XOR : priority = 6 ; break ;
case OperatorNode : : OP_BIT_OR : priority = 7 ; break ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
case OperatorNode : : OP_LESS : priority = 8 ; break ;
case OperatorNode : : OP_LESS_EQUAL : priority = 8 ; break ;
case OperatorNode : : OP_GREATER : priority = 8 ; break ;
case OperatorNode : : OP_GREATER_EQUAL : priority = 8 ; break ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
case OperatorNode : : OP_EQUAL : priority = 8 ; break ;
case OperatorNode : : OP_NOT_EQUAL : priority = 8 ; break ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
case OperatorNode : : OP_IN : priority = 10 ; break ;
2016-08-25 20:18:35 +02:00
2017-03-05 16:44:50 +01:00
case OperatorNode : : OP_NOT :
priority = 11 ;
unary = true ;
break ;
case OperatorNode : : OP_AND : priority = 12 ; break ;
case OperatorNode : : OP_OR : priority = 13 ; break ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
case OperatorNode : : OP_TERNARY_IF :
priority = 14 ;
ternary = true ;
2017-12-11 14:36:32 +01:00
right_to_left = true ;
2017-03-05 16:44:50 +01:00
break ;
case OperatorNode : : OP_TERNARY_ELSE :
priority = 14 ;
error = true ;
2017-12-11 14:36:32 +01:00
// Rigth-to-left should be false in this case, otherwise it would always error.
break ;
2017-03-05 16:44:50 +01:00
case OperatorNode : : OP_ASSIGN : priority = 15 ; break ;
case OperatorNode : : OP_ASSIGN_ADD : priority = 15 ; break ;
case OperatorNode : : OP_ASSIGN_SUB : priority = 15 ; break ;
case OperatorNode : : OP_ASSIGN_MUL : priority = 15 ; break ;
case OperatorNode : : OP_ASSIGN_DIV : priority = 15 ; break ;
case OperatorNode : : OP_ASSIGN_MOD : priority = 15 ; break ;
case OperatorNode : : OP_ASSIGN_SHIFT_LEFT : priority = 15 ; break ;
case OperatorNode : : OP_ASSIGN_SHIFT_RIGHT : priority = 15 ; break ;
case OperatorNode : : OP_ASSIGN_BIT_AND : priority = 15 ; break ;
case OperatorNode : : OP_ASSIGN_BIT_OR : priority = 15 ; break ;
case OperatorNode : : OP_ASSIGN_BIT_XOR : priority = 15 ; break ;
2014-02-10 02:10:30 +01:00
default : {
2017-11-16 18:38:18 +01:00
_set_error ( " GDScriptParser bug, invalid operator in expression: " + itos ( expression [ i ] . op ) ) ;
2014-02-10 02:10:30 +01:00
return NULL ;
}
}
2017-12-11 14:36:32 +01:00
if ( priority < min_priority | | ( right_to_left & & priority = = min_priority ) ) {
// < is used for left to right (default)
// <= is used for right to left
2017-03-05 16:44:50 +01:00
if ( error ) {
2016-08-25 20:18:35 +02:00
_set_error ( " Unexpected operator " ) ;
return NULL ;
}
2017-03-05 16:44:50 +01:00
next_op = i ;
min_priority = priority ;
is_unary = unary ;
is_ternary = ternary ;
2014-02-10 02:10:30 +01:00
}
}
2017-03-05 16:44:50 +01:00
if ( next_op = = - 1 ) {
2014-02-10 02:10:30 +01:00
_set_error ( " Yet another parser bug.... " ) ;
2017-03-05 16:44:50 +01:00
ERR_FAIL_COND_V ( next_op = = - 1 , NULL ) ;
2014-02-10 02:10:30 +01:00
}
// OK! create operator..
if ( is_unary ) {
2017-03-05 16:44:50 +01:00
int expr_pos = next_op ;
while ( expression [ expr_pos ] . is_op ) {
2014-02-10 02:10:30 +01:00
expr_pos + + ;
2017-03-05 16:44:50 +01:00
if ( expr_pos = = expression . size ( ) ) {
2014-02-10 02:10:30 +01:00
//can happen..
2018-04-22 19:36:01 +02:00
_set_error ( " Unexpected end of expression... " ) ;
2014-02-10 02:10:30 +01:00
return NULL ;
}
}
//consecutively do unary opeators
2017-03-05 16:44:50 +01:00
for ( int i = expr_pos - 1 ; i > = next_op ; i - - ) {
2014-02-10 02:10:30 +01:00
OperatorNode * op = alloc_node < OperatorNode > ( ) ;
2017-03-05 16:44:50 +01:00
op - > op = expression [ i ] . op ;
op - > arguments . push_back ( expression [ i + 1 ] . node ) ;
op - > line = op_line ; //line might have been changed from a \n
2018-07-25 03:11:03 +02:00
expression . write [ i ] . is_op = false ;
expression . write [ i ] . node = op ;
2017-03-05 16:44:50 +01:00
expression . remove ( i + 1 ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
} else if ( is_ternary ) {
if ( next_op < 1 | | next_op > = ( expression . size ( ) - 1 ) ) {
2018-04-22 19:36:01 +02:00
_set_error ( " Parser bug... " ) ;
2016-08-25 20:18:35 +02:00
ERR_FAIL_V ( NULL ) ;
}
2017-03-05 16:44:50 +01:00
if ( next_op > = ( expression . size ( ) - 2 ) | | expression [ next_op + 2 ] . op ! = OperatorNode : : OP_TERNARY_ELSE ) {
2016-08-25 20:18:35 +02:00
_set_error ( " Expected else after ternary if. " ) ;
2018-05-30 04:16:54 +02:00
return NULL ;
2016-08-25 20:18:35 +02:00
}
2017-03-05 16:44:50 +01:00
if ( next_op > = ( expression . size ( ) - 3 ) ) {
2016-08-25 20:18:35 +02:00
_set_error ( " Expected value after ternary else. " ) ;
2018-05-30 04:16:54 +02:00
return NULL ;
2016-08-25 20:18:35 +02:00
}
OperatorNode * op = alloc_node < OperatorNode > ( ) ;
2017-03-05 16:44:50 +01:00
op - > op = expression [ next_op ] . op ;
op - > line = op_line ; //line might have been changed from a \n
2016-08-25 20:18:35 +02:00
2017-03-05 16:44:50 +01:00
if ( expression [ next_op - 1 ] . is_op ) {
2016-08-25 20:18:35 +02:00
2018-04-22 19:36:01 +02:00
_set_error ( " Parser bug... " ) ;
2016-08-25 20:18:35 +02:00
ERR_FAIL_V ( NULL ) ;
}
2017-03-05 16:44:50 +01:00
if ( expression [ next_op + 1 ] . is_op ) {
2016-08-25 20:18:35 +02:00
// this is not invalid and can really appear
// but it becomes invalid anyway because no binary op
2017-09-02 16:19:06 +02:00
// can be followed by a unary op in a valid combination,
2017-03-24 21:45:31 +01:00
// due to how precedence works, unaries will always disappear first
2016-08-25 20:18:35 +02:00
_set_error ( " Unexpected two consecutive operators after ternary if. " ) ;
return NULL ;
}
2017-03-05 16:44:50 +01:00
if ( expression [ next_op + 3 ] . is_op ) {
2016-08-25 20:18:35 +02:00
// this is not invalid and can really appear
// but it becomes invalid anyway because no binary op
2017-09-02 16:19:06 +02:00
// can be followed by a unary op in a valid combination,
2017-03-24 21:45:31 +01:00
// due to how precedence works, unaries will always disappear first
2016-08-25 20:18:35 +02:00
_set_error ( " Unexpected two consecutive operators after ternary else. " ) ;
return NULL ;
}
2017-03-05 16:44:50 +01:00
op - > arguments . push_back ( expression [ next_op + 1 ] . node ) ; //next expression goes as first
op - > arguments . push_back ( expression [ next_op - 1 ] . node ) ; //left expression goes as when-true
op - > arguments . push_back ( expression [ next_op + 3 ] . node ) ; //expression after next goes as when-false
2016-08-25 20:18:35 +02:00
//replace all 3 nodes by this operator and make it an expression
2018-07-25 03:11:03 +02:00
expression . write [ next_op - 1 ] . node = op ;
2016-08-25 20:18:35 +02:00
expression . remove ( next_op ) ;
expression . remove ( next_op ) ;
expression . remove ( next_op ) ;
expression . remove ( next_op ) ;
2014-02-10 02:10:30 +01:00
} else {
2017-03-05 16:44:50 +01:00
if ( next_op < 1 | | next_op > = ( expression . size ( ) - 1 ) ) {
2018-04-22 19:36:01 +02:00
_set_error ( " Parser bug... " ) ;
2014-02-10 02:10:30 +01:00
ERR_FAIL_V ( NULL ) ;
}
OperatorNode * op = alloc_node < OperatorNode > ( ) ;
2017-03-05 16:44:50 +01:00
op - > op = expression [ next_op ] . op ;
op - > line = op_line ; //line might have been changed from a \n
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( expression [ next_op - 1 ] . is_op ) {
2014-02-10 02:10:30 +01:00
2018-04-22 19:36:01 +02:00
_set_error ( " Parser bug... " ) ;
2014-02-10 02:10:30 +01:00
ERR_FAIL_V ( NULL ) ;
}
2017-03-05 16:44:50 +01:00
if ( expression [ next_op + 1 ] . is_op ) {
2014-02-10 02:10:30 +01:00
// this is not invalid and can really appear
// but it becomes invalid anyway because no binary op
2017-09-02 16:19:06 +02:00
// can be followed by a unary op in a valid combination,
2017-03-24 21:45:31 +01:00
// due to how precedence works, unaries will always disappear first
2014-02-10 02:10:30 +01:00
2016-06-24 15:30:36 +02:00
_set_error ( " Unexpected two consecutive operators. " ) ;
return NULL ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
op - > arguments . push_back ( expression [ next_op - 1 ] . node ) ; //expression goes as left
op - > arguments . push_back ( expression [ next_op + 1 ] . node ) ; //next expression goes as right
2014-02-10 02:10:30 +01:00
//replace all 3 nodes by this operator and make it an expression
2018-07-25 03:11:03 +02:00
expression . write [ next_op - 1 ] . node = op ;
2014-02-10 02:10:30 +01:00
expression . remove ( next_op ) ;
expression . remove ( next_op ) ;
}
}
return expression [ 0 ] . node ;
}
2017-11-16 18:38:18 +01:00
GDScriptParser : : Node * GDScriptParser : : _reduce_expression ( Node * p_node , bool p_to_const ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
switch ( p_node - > type ) {
2014-02-10 02:10:30 +01:00
case Node : : TYPE_BUILT_IN_FUNCTION : {
//many may probably be optimizable
return p_node ;
} break ;
case Node : : TYPE_ARRAY : {
2017-03-05 16:44:50 +01:00
ArrayNode * an = static_cast < ArrayNode * > ( p_node ) ;
bool all_constants = true ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < an - > elements . size ( ) ; i + + ) {
2014-02-10 02:10:30 +01:00
2018-07-25 03:11:03 +02:00
an - > elements . write [ i ] = _reduce_expression ( an - > elements [ i ] , p_to_const ) ;
2017-03-05 16:44:50 +01:00
if ( an - > elements [ i ] - > type ! = Node : : TYPE_CONSTANT )
all_constants = false ;
2014-02-10 02:10:30 +01:00
}
if ( all_constants & & p_to_const ) {
//reduce constant array expression
ConstantNode * cn = alloc_node < ConstantNode > ( ) ;
2017-01-11 12:53:31 +01:00
Array arr ;
2014-02-10 02:10:30 +01:00
arr . resize ( an - > elements . size ( ) ) ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < an - > elements . size ( ) ; i + + ) {
ConstantNode * acn = static_cast < ConstantNode * > ( an - > elements [ i ] ) ;
arr [ i ] = acn - > value ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
cn - > value = arr ;
2018-05-30 04:16:54 +02:00
cn - > datatype = _type_from_variant ( cn - > value ) ;
2014-02-10 02:10:30 +01:00
return cn ;
}
return an ;
} break ;
case Node : : TYPE_DICTIONARY : {
2017-03-05 16:44:50 +01:00
DictionaryNode * dn = static_cast < DictionaryNode * > ( p_node ) ;
bool all_constants = true ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < dn - > elements . size ( ) ; i + + ) {
2014-02-10 02:10:30 +01:00
2018-07-25 03:11:03 +02:00
dn - > elements . write [ i ] . key = _reduce_expression ( dn - > elements [ i ] . key , p_to_const ) ;
2017-03-05 16:44:50 +01:00
if ( dn - > elements [ i ] . key - > type ! = Node : : TYPE_CONSTANT )
all_constants = false ;
2018-07-25 03:11:03 +02:00
dn - > elements . write [ i ] . value = _reduce_expression ( dn - > elements [ i ] . value , p_to_const ) ;
2017-03-05 16:44:50 +01:00
if ( dn - > elements [ i ] . value - > type ! = Node : : TYPE_CONSTANT )
all_constants = false ;
2014-02-10 02:10:30 +01:00
}
if ( all_constants & & p_to_const ) {
//reduce constant array expression
ConstantNode * cn = alloc_node < ConstantNode > ( ) ;
2017-01-11 12:53:31 +01:00
Dictionary dict ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < dn - > elements . size ( ) ; i + + ) {
ConstantNode * key_c = static_cast < ConstantNode * > ( dn - > elements [ i ] . key ) ;
ConstantNode * value_c = static_cast < ConstantNode * > ( dn - > elements [ i ] . value ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
dict [ key_c - > value ] = value_c - > value ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
cn - > value = dict ;
2018-05-30 04:16:54 +02:00
cn - > datatype = _type_from_variant ( cn - > value ) ;
2014-02-10 02:10:30 +01:00
return cn ;
}
return dn ;
} break ;
case Node : : TYPE_OPERATOR : {
2017-03-05 16:44:50 +01:00
OperatorNode * op = static_cast < OperatorNode * > ( p_node ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
bool all_constants = true ;
int last_not_constant = - 1 ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < op - > arguments . size ( ) ; i + + ) {
2014-02-10 02:10:30 +01:00
2018-07-25 03:11:03 +02:00
op - > arguments . write [ i ] = _reduce_expression ( op - > arguments [ i ] , p_to_const ) ;
2017-03-05 16:44:50 +01:00
if ( op - > arguments [ i ] - > type ! = Node : : TYPE_CONSTANT ) {
all_constants = false ;
last_not_constant = i ;
2014-02-10 02:10:30 +01:00
}
}
2017-05-26 19:45:39 +02:00
if ( op - > op = = OperatorNode : : OP_IS ) {
2014-02-10 02:10:30 +01:00
//nothing much
return op ;
2017-03-05 16:44:50 +01:00
}
if ( op - > op = = OperatorNode : : OP_PARENT_CALL ) {
2014-02-10 02:10:30 +01:00
//nothing much
return op ;
2017-03-05 16:44:50 +01:00
} else if ( op - > op = = OperatorNode : : OP_CALL ) {
2014-02-10 02:10:30 +01:00
//can reduce base type constructors
2017-11-16 18:38:18 +01:00
if ( ( op - > arguments [ 0 ] - > type = = Node : : TYPE_TYPE | | ( op - > arguments [ 0 ] - > type = = Node : : TYPE_BUILT_IN_FUNCTION & & GDScriptFunctions : : is_deterministic ( static_cast < BuiltInFunctionNode * > ( op - > arguments [ 0 ] ) - > function ) ) ) & & last_not_constant = = 0 ) {
2014-02-10 02:10:30 +01:00
//native type constructor or intrinsic function
2017-03-05 16:44:50 +01:00
const Variant * * vptr = NULL ;
Vector < Variant * > ptrs ;
if ( op - > arguments . size ( ) > 1 ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
ptrs . resize ( op - > arguments . size ( ) - 1 ) ;
for ( int i = 0 ; i < ptrs . size ( ) ; i + + ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
ConstantNode * cn = static_cast < ConstantNode * > ( op - > arguments [ i + 1 ] ) ;
2018-07-25 03:11:03 +02:00
ptrs . write [ i ] = & cn - > value ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
vptr = ( const Variant * * ) & ptrs [ 0 ] ;
2014-02-10 02:10:30 +01:00
}
Variant : : CallError ce ;
Variant v ;
2017-03-05 16:44:50 +01:00
if ( op - > arguments [ 0 ] - > type = = Node : : TYPE_TYPE ) {
TypeNode * tn = static_cast < TypeNode * > ( op - > arguments [ 0 ] ) ;
v = Variant : : construct ( tn - > vtype , vptr , ptrs . size ( ) , ce ) ;
2014-02-10 02:10:30 +01:00
} else {
2017-11-16 18:38:18 +01:00
GDScriptFunctions : : Function func = static_cast < BuiltInFunctionNode * > ( op - > arguments [ 0 ] ) - > function ;
GDScriptFunctions : : call ( func , vptr , ptrs . size ( ) , v , ce ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
if ( ce . error ! = Variant : : CallError : : CALL_OK ) {
2014-02-10 02:10:30 +01:00
String errwhere ;
2017-03-05 16:44:50 +01:00
if ( op - > arguments [ 0 ] - > type = = Node : : TYPE_TYPE ) {
TypeNode * tn = static_cast < TypeNode * > ( op - > arguments [ 0 ] ) ;
2018-01-30 04:32:08 +01:00
errwhere = " ' " + Variant : : get_type_name ( tn - > vtype ) + " ' constructor " ;
2014-02-10 02:10:30 +01:00
} else {
2017-11-16 18:38:18 +01:00
GDScriptFunctions : : Function func = static_cast < BuiltInFunctionNode * > ( op - > arguments [ 0 ] ) - > function ;
2018-01-30 04:32:08 +01:00
errwhere = String ( " ' " ) + GDScriptFunctions : : get_func_name ( func ) + " ' intrinsic function " ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
switch ( ce . error ) {
2014-02-10 02:10:30 +01:00
case Variant : : CallError : : CALL_ERROR_INVALID_ARGUMENT : {
2017-03-05 16:44:50 +01:00
_set_error ( " Invalid argument (# " + itos ( ce . argument + 1 ) + " ) for " + errwhere + " . " ) ;
2014-02-10 02:10:30 +01:00
} break ;
case Variant : : CallError : : CALL_ERROR_TOO_MANY_ARGUMENTS : {
2017-03-05 16:44:50 +01:00
_set_error ( " Too many arguments for " + errwhere + " . " ) ;
2014-02-10 02:10:30 +01:00
} break ;
case Variant : : CallError : : CALL_ERROR_TOO_FEW_ARGUMENTS : {
2017-03-05 16:44:50 +01:00
_set_error ( " Too few arguments for " + errwhere + " . " ) ;
2014-02-10 02:10:30 +01:00
} break ;
default : {
2017-03-05 16:44:50 +01:00
_set_error ( " Invalid arguments for " + errwhere + " . " ) ;
2014-02-10 02:10:30 +01:00
} break ;
}
2017-03-05 16:44:50 +01:00
error_line = op - > line ;
2016-07-22 14:22:34 +02:00
2014-02-10 02:10:30 +01:00
return p_node ;
}
ConstantNode * cn = alloc_node < ConstantNode > ( ) ;
2017-03-05 16:44:50 +01:00
cn - > value = v ;
2018-05-30 04:16:54 +02:00
cn - > datatype = _type_from_variant ( v ) ;
2014-02-10 02:10:30 +01:00
return cn ;
2017-03-05 16:44:50 +01:00
} else if ( op - > arguments [ 0 ] - > type = = Node : : TYPE_BUILT_IN_FUNCTION & & last_not_constant = = 0 ) {
2014-02-10 02:10:30 +01:00
}
return op ; //don't reduce yet
2014-09-15 16:33:30 +02:00
2017-03-05 16:44:50 +01:00
} else if ( op - > op = = OperatorNode : : OP_YIELD ) {
2014-09-15 16:33:30 +02:00
return op ;
2017-03-05 16:44:50 +01:00
} else if ( op - > op = = OperatorNode : : OP_INDEX ) {
2014-02-10 02:10:30 +01:00
//can reduce indices into constant arrays or dictionaries
if ( all_constants ) {
2017-03-05 16:44:50 +01:00
ConstantNode * ca = static_cast < ConstantNode * > ( op - > arguments [ 0 ] ) ;
ConstantNode * cb = static_cast < ConstantNode * > ( op - > arguments [ 1 ] ) ;
2014-02-10 02:10:30 +01:00
bool valid ;
2017-03-05 16:44:50 +01:00
Variant v = ca - > value . get ( cb - > value , & valid ) ;
2014-02-10 02:10:30 +01:00
if ( ! valid ) {
_set_error ( " invalid index in constant expression " ) ;
2017-03-05 16:44:50 +01:00
error_line = op - > line ;
2014-02-10 02:10:30 +01:00
return op ;
}
ConstantNode * cn = alloc_node < ConstantNode > ( ) ;
2017-03-05 16:44:50 +01:00
cn - > value = v ;
2018-05-30 04:16:54 +02:00
cn - > datatype = _type_from_variant ( v ) ;
2014-02-10 02:10:30 +01:00
return cn ;
2018-05-30 04:16:54 +02:00
}
2014-02-10 02:10:30 +01:00
2014-11-06 01:20:42 +01:00
return op ;
2017-03-05 16:44:50 +01:00
} else if ( op - > op = = OperatorNode : : OP_INDEX_NAMED ) {
2014-11-06 01:20:42 +01:00
2017-03-05 16:44:50 +01:00
if ( op - > arguments [ 0 ] - > type = = Node : : TYPE_CONSTANT & & op - > arguments [ 1 ] - > type = = Node : : TYPE_IDENTIFIER ) {
2014-11-06 01:20:42 +01:00
2017-03-05 16:44:50 +01:00
ConstantNode * ca = static_cast < ConstantNode * > ( op - > arguments [ 0 ] ) ;
IdentifierNode * ib = static_cast < IdentifierNode * > ( op - > arguments [ 1 ] ) ;
2014-11-06 01:20:42 +01:00
bool valid ;
2017-03-05 16:44:50 +01:00
Variant v = ca - > value . get_named ( ib - > name , & valid ) ;
2014-11-06 01:20:42 +01:00
if ( ! valid ) {
2017-03-05 16:44:50 +01:00
_set_error ( " invalid index ' " + String ( ib - > name ) + " ' in constant expression " ) ;
error_line = op - > line ;
2014-11-06 01:20:42 +01:00
return op ;
}
ConstantNode * cn = alloc_node < ConstantNode > ( ) ;
2017-03-05 16:44:50 +01:00
cn - > value = v ;
2018-05-30 04:16:54 +02:00
cn - > datatype = _type_from_variant ( v ) ;
2014-11-06 01:20:42 +01:00
return cn ;
2014-02-10 02:10:30 +01:00
}
return op ;
}
2018-05-30 04:16:54 +02:00
//validate assignment (don't assign to constant expression
2017-03-05 16:44:50 +01:00
switch ( op - > op ) {
2014-02-10 02:10:30 +01:00
case OperatorNode : : OP_ASSIGN :
case OperatorNode : : OP_ASSIGN_ADD :
case OperatorNode : : OP_ASSIGN_SUB :
case OperatorNode : : OP_ASSIGN_MUL :
case OperatorNode : : OP_ASSIGN_DIV :
case OperatorNode : : OP_ASSIGN_MOD :
case OperatorNode : : OP_ASSIGN_SHIFT_LEFT :
case OperatorNode : : OP_ASSIGN_SHIFT_RIGHT :
case OperatorNode : : OP_ASSIGN_BIT_AND :
case OperatorNode : : OP_ASSIGN_BIT_OR :
case OperatorNode : : OP_ASSIGN_BIT_XOR : {
2017-03-05 16:44:50 +01:00
if ( op - > arguments [ 0 ] - > type = = Node : : TYPE_CONSTANT ) {
_set_error ( " Can't assign to constant " , tokenizer - > get_token_line ( ) - 1 ) ;
error_line = op - > line ;
2014-02-10 02:10:30 +01:00
return op ;
}
2017-03-05 16:44:50 +01:00
if ( op - > arguments [ 0 ] - > type = = Node : : TYPE_OPERATOR ) {
OperatorNode * on = static_cast < OperatorNode * > ( op - > arguments [ 0 ] ) ;
2016-12-29 11:31:19 +01:00
if ( on - > op ! = OperatorNode : : OP_INDEX & & on - > op ! = OperatorNode : : OP_INDEX_NAMED ) {
2017-03-05 16:44:50 +01:00
_set_error ( " Can't assign to an expression " , tokenizer - > get_token_line ( ) - 1 ) ;
error_line = op - > line ;
2016-12-29 11:31:19 +01:00
return op ;
}
}
2014-02-10 02:10:30 +01:00
} break ;
default : { break ; }
}
//now se if all are constants
if ( ! all_constants )
return op ; //nothing to reduce from here on
2017-03-05 16:44:50 +01:00
# define _REDUCE_UNARY(m_vop) \
bool valid = false ; \
Variant res ; \
Variant : : evaluate ( m_vop , static_cast < ConstantNode * > ( op - > arguments [ 0 ] ) - > value , Variant ( ) , res , valid ) ; \
if ( ! valid ) { \
_set_error ( " Invalid operand for unary operator " ) ; \
error_line = op - > line ; \
return p_node ; \
} \
ConstantNode * cn = alloc_node < ConstantNode > ( ) ; \
cn - > value = res ; \
2018-05-30 04:16:54 +02:00
cn - > datatype = _type_from_variant ( res ) ; \
2014-02-10 02:10:30 +01:00
return cn ;
2017-03-05 16:44:50 +01:00
# define _REDUCE_BINARY(m_vop) \
bool valid = false ; \
Variant res ; \
Variant : : evaluate ( m_vop , static_cast < ConstantNode * > ( op - > arguments [ 0 ] ) - > value , static_cast < ConstantNode * > ( op - > arguments [ 1 ] ) - > value , res , valid ) ; \
if ( ! valid ) { \
_set_error ( " Invalid operands for operator " ) ; \
error_line = op - > line ; \
return p_node ; \
} \
ConstantNode * cn = alloc_node < ConstantNode > ( ) ; \
cn - > value = res ; \
2018-05-30 04:16:54 +02:00
cn - > datatype = _type_from_variant ( res ) ; \
2014-02-10 02:10:30 +01:00
return cn ;
2017-03-05 16:44:50 +01:00
switch ( op - > op ) {
2014-02-10 02:10:30 +01:00
//unary operators
2017-03-05 16:44:50 +01:00
case OperatorNode : : OP_NEG : {
_REDUCE_UNARY ( Variant : : OP_NEGATE ) ;
} break ;
case OperatorNode : : OP_POS : {
_REDUCE_UNARY ( Variant : : OP_POSITIVE ) ;
} break ;
case OperatorNode : : OP_NOT : {
_REDUCE_UNARY ( Variant : : OP_NOT ) ;
} break ;
case OperatorNode : : OP_BIT_INVERT : {
_REDUCE_UNARY ( Variant : : OP_BIT_NEGATE ) ;
} break ;
2014-02-10 02:10:30 +01:00
//binary operators (in precedence order)
2017-03-05 16:44:50 +01:00
case OperatorNode : : OP_IN : {
_REDUCE_BINARY ( Variant : : OP_IN ) ;
} break ;
case OperatorNode : : OP_EQUAL : {
_REDUCE_BINARY ( Variant : : OP_EQUAL ) ;
} break ;
case OperatorNode : : OP_NOT_EQUAL : {
_REDUCE_BINARY ( Variant : : OP_NOT_EQUAL ) ;
} break ;
case OperatorNode : : OP_LESS : {
_REDUCE_BINARY ( Variant : : OP_LESS ) ;
} break ;
case OperatorNode : : OP_LESS_EQUAL : {
_REDUCE_BINARY ( Variant : : OP_LESS_EQUAL ) ;
} break ;
case OperatorNode : : OP_GREATER : {
_REDUCE_BINARY ( Variant : : OP_GREATER ) ;
} break ;
case OperatorNode : : OP_GREATER_EQUAL : {
_REDUCE_BINARY ( Variant : : OP_GREATER_EQUAL ) ;
} break ;
case OperatorNode : : OP_AND : {
_REDUCE_BINARY ( Variant : : OP_AND ) ;
} break ;
case OperatorNode : : OP_OR : {
_REDUCE_BINARY ( Variant : : OP_OR ) ;
} break ;
case OperatorNode : : OP_ADD : {
_REDUCE_BINARY ( Variant : : OP_ADD ) ;
} break ;
case OperatorNode : : OP_SUB : {
2017-09-17 02:32:05 +02:00
_REDUCE_BINARY ( Variant : : OP_SUBTRACT ) ;
2017-03-05 16:44:50 +01:00
} break ;
case OperatorNode : : OP_MUL : {
_REDUCE_BINARY ( Variant : : OP_MULTIPLY ) ;
} break ;
case OperatorNode : : OP_DIV : {
_REDUCE_BINARY ( Variant : : OP_DIVIDE ) ;
} break ;
case OperatorNode : : OP_MOD : {
_REDUCE_BINARY ( Variant : : OP_MODULE ) ;
} break ;
case OperatorNode : : OP_SHIFT_LEFT : {
_REDUCE_BINARY ( Variant : : OP_SHIFT_LEFT ) ;
} break ;
case OperatorNode : : OP_SHIFT_RIGHT : {
_REDUCE_BINARY ( Variant : : OP_SHIFT_RIGHT ) ;
} break ;
case OperatorNode : : OP_BIT_AND : {
_REDUCE_BINARY ( Variant : : OP_BIT_AND ) ;
} break ;
case OperatorNode : : OP_BIT_OR : {
_REDUCE_BINARY ( Variant : : OP_BIT_OR ) ;
} break ;
case OperatorNode : : OP_BIT_XOR : {
_REDUCE_BINARY ( Variant : : OP_BIT_XOR ) ;
} break ;
2018-05-30 04:16:54 +02:00
case OperatorNode : : OP_TERNARY_IF : {
if ( static_cast < ConstantNode * > ( op - > arguments [ 0 ] ) - > value . booleanize ( ) ) {
return op - > arguments [ 1 ] ;
} else {
return op - > arguments [ 2 ] ;
}
} break ;
2014-02-10 02:10:30 +01:00
default : { ERR_FAIL_V ( op ) ; }
}
ERR_FAIL_V ( op ) ;
} break ;
default : {
return p_node ;
} break ;
}
}
2017-11-16 18:38:18 +01:00
GDScriptParser : : Node * GDScriptParser : : _parse_and_reduce_expression ( Node * p_parent , bool p_static , bool p_reduce_const , bool p_allow_assign ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
Node * expr = _parse_expression ( p_parent , p_static , p_allow_assign , p_reduce_const ) ;
2014-02-10 02:10:30 +01:00
if ( ! expr | | error_set )
return NULL ;
2017-03-05 16:44:50 +01:00
expr = _reduce_expression ( expr , p_reduce_const ) ;
2014-02-10 02:10:30 +01:00
if ( ! expr | | error_set )
return NULL ;
return expr ;
}
2017-11-16 18:38:18 +01:00
bool GDScriptParser : : _recover_from_completion ( ) {
2015-08-30 16:50:10 +02:00
if ( ! completion_found ) {
return false ; //can't recover if no completion
}
//skip stuff until newline
2017-11-16 18:38:18 +01:00
while ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_NEWLINE & & tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_EOF & & tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_ERROR ) {
2015-08-30 16:50:10 +02:00
tokenizer - > advance ( ) ;
}
2017-03-05 16:44:50 +01:00
completion_found = false ;
error_set = false ;
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_ERROR ) {
2015-09-02 05:56:51 +02:00
error_set = true ;
}
2015-08-30 16:50:10 +02:00
return true ;
}
2017-11-16 18:38:18 +01:00
GDScriptParser : : PatternNode * GDScriptParser : : _parse_pattern ( bool p_static ) {
2017-03-05 16:44:50 +01:00
2016-10-05 18:48:38 +02:00
PatternNode * pattern = alloc_node < PatternNode > ( ) ;
2017-03-05 16:44:50 +01:00
2017-11-16 18:38:18 +01:00
GDScriptTokenizer : : Token token = tokenizer - > get_token ( ) ;
2016-09-30 21:40:31 +02:00
if ( error_set )
return NULL ;
2017-03-05 16:44:50 +01:00
2017-11-16 18:38:18 +01:00
if ( token = = GDScriptTokenizer : : TK_EOF ) {
2016-10-05 18:48:38 +02:00
return NULL ;
}
2017-03-05 16:44:50 +01:00
2016-09-30 21:40:31 +02:00
switch ( token ) {
2017-01-20 09:25:15 +01:00
// array
2017-11-16 18:38:18 +01:00
case GDScriptTokenizer : : TK_BRACKET_OPEN : {
2016-09-30 21:40:31 +02:00
tokenizer - > advance ( ) ;
2017-11-16 18:38:18 +01:00
pattern - > pt_type = GDScriptParser : : PatternNode : : PT_ARRAY ;
2016-09-30 21:40:31 +02:00
while ( true ) {
2017-03-05 16:44:50 +01:00
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_BRACKET_CLOSE ) {
2016-09-30 21:40:31 +02:00
tokenizer - > advance ( ) ;
break ;
}
2017-03-05 16:44:50 +01:00
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PERIOD & & tokenizer - > get_token ( 1 ) = = GDScriptTokenizer : : TK_PERIOD ) {
2016-09-30 21:40:31 +02:00
// match everything
tokenizer - > advance ( 2 ) ;
2016-10-05 18:48:38 +02:00
PatternNode * sub_pattern = alloc_node < PatternNode > ( ) ;
2017-11-16 18:38:18 +01:00
sub_pattern - > pt_type = GDScriptParser : : PatternNode : : PT_IGNORE_REST ;
2016-10-05 18:48:38 +02:00
pattern - > array . push_back ( sub_pattern ) ;
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COMMA & & tokenizer - > get_token ( 1 ) = = GDScriptTokenizer : : TK_BRACKET_CLOSE ) {
2016-09-30 21:40:31 +02:00
tokenizer - > advance ( 2 ) ;
break ;
2017-11-16 18:38:18 +01:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_BRACKET_CLOSE ) {
2016-09-30 21:40:31 +02:00
tokenizer - > advance ( 1 ) ;
break ;
} else {
_set_error ( " '..' pattern only allowed at the end of an array pattern " ) ;
return NULL ;
}
}
2017-03-05 16:44:50 +01:00
2016-09-30 21:40:31 +02:00
PatternNode * sub_pattern = _parse_pattern ( p_static ) ;
if ( ! sub_pattern ) {
return NULL ;
}
2017-03-05 16:44:50 +01:00
2016-09-30 21:40:31 +02:00
pattern - > array . push_back ( sub_pattern ) ;
2017-03-05 16:44:50 +01:00
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COMMA ) {
2016-09-30 21:40:31 +02:00
tokenizer - > advance ( ) ;
continue ;
2017-11-16 18:38:18 +01:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_BRACKET_CLOSE ) {
2016-09-30 21:40:31 +02:00
tokenizer - > advance ( ) ;
break ;
} else {
_set_error ( " Not a valid pattern " ) ;
return NULL ;
}
}
} break ;
2016-10-16 13:20:28 +02:00
// bind
2017-11-16 18:38:18 +01:00
case GDScriptTokenizer : : TK_PR_VAR : {
2016-10-05 18:48:38 +02:00
tokenizer - > advance ( ) ;
2018-08-21 18:38:18 +02:00
if ( ! tokenizer - > is_token_literal ( ) ) {
_set_error ( " Expected identifier for binding variable name. " ) ;
return NULL ;
}
2017-11-16 18:38:18 +01:00
pattern - > pt_type = GDScriptParser : : PatternNode : : PT_BIND ;
2016-09-30 21:40:31 +02:00
pattern - > bind = tokenizer - > get_token_identifier ( ) ;
2018-08-21 18:38:18 +02:00
// Check if variable name is already used
BlockNode * bl = current_block ;
while ( bl ) {
if ( bl - > variables . has ( pattern - > bind ) ) {
_set_error ( " Binding name of ' " + pattern - > bind . operator String ( ) + " ' is already declared in this scope. " ) ;
return NULL ;
}
bl = bl - > parent_block ;
2018-05-30 04:16:54 +02:00
}
// Create local variable for proper identifier detection later
LocalVarNode * lv = alloc_node < LocalVarNode > ( ) ;
lv - > name = pattern - > bind ;
current_block - > variables . insert ( lv - > name , lv ) ;
2016-09-30 21:40:31 +02:00
tokenizer - > advance ( ) ;
} break ;
2017-01-20 09:25:15 +01:00
// dictionary
2017-11-16 18:38:18 +01:00
case GDScriptTokenizer : : TK_CURLY_BRACKET_OPEN : {
2016-09-30 21:40:31 +02:00
tokenizer - > advance ( ) ;
2017-11-16 18:38:18 +01:00
pattern - > pt_type = GDScriptParser : : PatternNode : : PT_DICTIONARY ;
2016-09-30 21:40:31 +02:00
while ( true ) {
2017-03-05 16:44:50 +01:00
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CURLY_BRACKET_CLOSE ) {
2016-09-30 21:40:31 +02:00
tokenizer - > advance ( ) ;
break ;
}
2017-03-05 16:44:50 +01:00
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PERIOD & & tokenizer - > get_token ( 1 ) = = GDScriptTokenizer : : TK_PERIOD ) {
2016-09-30 21:40:31 +02:00
// match everything
tokenizer - > advance ( 2 ) ;
2016-10-05 18:48:38 +02:00
PatternNode * sub_pattern = alloc_node < PatternNode > ( ) ;
sub_pattern - > pt_type = PatternNode : : PT_IGNORE_REST ;
pattern - > array . push_back ( sub_pattern ) ;
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COMMA & & tokenizer - > get_token ( 1 ) = = GDScriptTokenizer : : TK_CURLY_BRACKET_CLOSE ) {
2016-09-30 21:40:31 +02:00
tokenizer - > advance ( 2 ) ;
break ;
2017-11-16 18:38:18 +01:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CURLY_BRACKET_CLOSE ) {
2016-09-30 21:40:31 +02:00
tokenizer - > advance ( 1 ) ;
break ;
} else {
2017-09-02 16:19:06 +02:00
_set_error ( " '..' pattern only allowed at the end of a dictionary pattern " ) ;
2016-09-30 21:40:31 +02:00
return NULL ;
}
}
2017-03-05 16:44:50 +01:00
2016-09-30 21:40:31 +02:00
Node * key = _parse_and_reduce_expression ( pattern , p_static ) ;
if ( ! key ) {
_set_error ( " Not a valid key in pattern " ) ;
return NULL ;
}
2017-03-05 16:44:50 +01:00
2017-11-16 18:38:18 +01:00
if ( key - > type ! = GDScriptParser : : Node : : TYPE_CONSTANT ) {
2016-09-30 21:40:31 +02:00
_set_error ( " Not a constant expression as key " ) ;
return NULL ;
}
2017-03-05 16:44:50 +01:00
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COLON ) {
2016-09-30 21:40:31 +02:00
tokenizer - > advance ( ) ;
2017-03-05 16:44:50 +01:00
2016-09-30 21:40:31 +02:00
PatternNode * value = _parse_pattern ( p_static ) ;
if ( ! value ) {
_set_error ( " Expected pattern in dictionary value " ) ;
return NULL ;
}
2017-03-05 16:44:50 +01:00
pattern - > dictionary . insert ( static_cast < ConstantNode * > ( key ) , value ) ;
2016-09-30 21:40:31 +02:00
} else {
2017-03-05 16:44:50 +01:00
pattern - > dictionary . insert ( static_cast < ConstantNode * > ( key ) , NULL ) ;
2016-09-30 21:40:31 +02:00
}
2017-03-05 16:44:50 +01:00
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COMMA ) {
2016-09-30 21:40:31 +02:00
tokenizer - > advance ( ) ;
continue ;
2017-11-16 18:38:18 +01:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CURLY_BRACKET_CLOSE ) {
2016-09-30 21:40:31 +02:00
tokenizer - > advance ( ) ;
break ;
} else {
_set_error ( " Not a valid pattern " ) ;
return NULL ;
}
}
} break ;
2017-11-16 18:38:18 +01:00
case GDScriptTokenizer : : TK_WILDCARD : {
2017-01-20 09:25:15 +01:00
tokenizer - > advance ( ) ;
pattern - > pt_type = PatternNode : : PT_WILDCARD ;
} break ;
2016-10-16 13:20:28 +02:00
// all the constants like strings and numbers
2016-09-30 21:40:31 +02:00
default : {
2016-10-05 18:48:38 +02:00
Node * value = _parse_and_reduce_expression ( pattern , p_static ) ;
2017-10-24 07:07:21 +02:00
if ( ! value ) {
_set_error ( " Expect constant expression or variables in a pattern " ) ;
2016-10-05 18:48:38 +02:00
return NULL ;
}
2017-03-05 16:44:50 +01:00
2017-08-22 14:54:52 +02:00
if ( value - > type = = Node : : TYPE_OPERATOR ) {
// Maybe it's SomeEnum.VALUE
Node * current_value = value ;
while ( current_value - > type = = Node : : TYPE_OPERATOR ) {
OperatorNode * op_node = static_cast < OperatorNode * > ( current_value ) ;
if ( op_node - > op ! = OperatorNode : : OP_INDEX_NAMED ) {
_set_error ( " Invalid operator in pattern. Only index (`A.B`) is allowed " ) ;
return NULL ;
}
current_value = op_node - > arguments [ 0 ] ;
}
if ( current_value - > type ! = Node : : TYPE_IDENTIFIER ) {
_set_error ( " Only constant expression or variables allowed in a pattern " ) ;
return NULL ;
}
} else if ( value - > type ! = Node : : TYPE_IDENTIFIER & & value - > type ! = Node : : TYPE_CONSTANT ) {
2016-10-16 13:20:28 +02:00
_set_error ( " Only constant expressions or variables allowed in a pattern " ) ;
return NULL ;
}
2017-03-05 16:44:50 +01:00
2016-10-05 18:48:38 +02:00
pattern - > pt_type = PatternNode : : PT_CONSTANT ;
pattern - > constant = value ;
} break ;
2016-09-30 21:40:31 +02:00
}
2017-03-05 16:44:50 +01:00
2016-09-30 21:40:31 +02:00
return pattern ;
}
2017-11-16 18:38:18 +01:00
void GDScriptParser : : _parse_pattern_block ( BlockNode * p_block , Vector < PatternBranchNode * > & p_branches , bool p_static ) {
2016-09-30 21:40:31 +02:00
int indent_level = tab_level . back ( ) - > get ( ) ;
2017-03-05 16:44:50 +01:00
2016-09-30 21:40:31 +02:00
while ( true ) {
2017-03-05 16:44:50 +01:00
2017-11-16 18:38:18 +01:00
while ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_NEWLINE & & _parse_newline ( ) )
2017-03-05 16:44:50 +01:00
;
2017-11-16 18:38:18 +01:00
// GDScriptTokenizer::Token token = tokenizer->get_token();
2016-09-30 21:40:31 +02:00
if ( error_set )
return ;
2017-03-05 16:44:50 +01:00
2016-09-30 21:40:31 +02:00
if ( indent_level > tab_level . back ( ) - > get ( ) ) {
return ; // go back a level
}
2017-03-05 16:44:50 +01:00
if ( pending_newline ! = - 1 ) {
pending_newline = - 1 ;
2016-09-30 21:40:31 +02:00
}
2017-03-05 16:44:50 +01:00
2016-10-05 18:48:38 +02:00
PatternBranchNode * branch = alloc_node < PatternBranchNode > ( ) ;
2018-05-30 04:16:54 +02:00
branch - > body = alloc_node < BlockNode > ( ) ;
branch - > body - > parent_block = p_block ;
p_block - > sub_blocks . push_back ( branch - > body ) ;
current_block = branch - > body ;
2017-03-05 16:44:50 +01:00
2016-10-16 13:20:28 +02:00
branch - > patterns . push_back ( _parse_pattern ( p_static ) ) ;
if ( ! branch - > patterns [ 0 ] ) {
2016-09-30 21:40:31 +02:00
return ;
}
2017-03-05 16:44:50 +01:00
2018-05-30 04:16:54 +02:00
bool has_binding = branch - > patterns [ 0 ] - > pt_type = = PatternNode : : PT_BIND ;
bool catch_all = has_binding | | branch - > patterns [ 0 ] - > pt_type = = PatternNode : : PT_WILDCARD ;
2017-11-16 18:38:18 +01:00
while ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COMMA ) {
2016-10-16 13:20:28 +02:00
tokenizer - > advance ( ) ;
branch - > patterns . push_back ( _parse_pattern ( p_static ) ) ;
if ( ! branch - > patterns [ branch - > patterns . size ( ) - 1 ] ) {
return ;
}
2018-05-30 04:16:54 +02:00
PatternNode : : PatternType pt = branch - > patterns [ branch - > patterns . size ( ) - 1 ] - > pt_type ;
if ( pt = = PatternNode : : PT_BIND ) {
_set_error ( " Cannot use bindings with multipattern. " ) ;
return ;
}
catch_all = catch_all | | pt = = PatternNode : : PT_WILDCARD ;
2016-10-16 13:20:28 +02:00
}
2017-03-05 16:44:50 +01:00
if ( ! _enter_indent_block ( ) ) {
2016-09-30 21:40:31 +02:00
_set_error ( " Expected block in pattern branch " ) ;
return ;
}
2017-03-05 16:44:50 +01:00
2016-09-30 21:40:31 +02:00
_parse_block ( branch - > body , p_static ) ;
2017-03-05 16:44:50 +01:00
2016-10-05 18:48:38 +02:00
current_block = p_block ;
2017-03-05 16:44:50 +01:00
2018-05-30 04:16:54 +02:00
if ( catch_all & & branch - > body - > has_return ) {
p_block - > has_return = true ;
}
2016-10-05 18:48:38 +02:00
p_branches . push_back ( branch ) ;
}
}
2017-11-16 18:38:18 +01:00
void GDScriptParser : : _generate_pattern ( PatternNode * p_pattern , Node * p_node_to_match , Node * & p_resulting_node , Map < StringName , Node * > & p_bindings ) {
2018-05-30 04:16:54 +02:00
const DataType & to_match_type = p_node_to_match - > get_datatype ( ) ;
2016-10-16 13:20:28 +02:00
switch ( p_pattern - > pt_type ) {
case PatternNode : : PT_CONSTANT : {
2017-03-05 16:44:50 +01:00
2018-05-30 04:16:54 +02:00
DataType pattern_type = _reduce_node_type ( p_pattern - > constant ) ;
if ( error_set ) {
return ;
}
OperatorNode * type_comp = NULL ;
// static type check if possible
if ( pattern_type . has_type & & to_match_type . has_type ) {
if ( ! _is_type_compatible ( to_match_type , pattern_type ) & & ! _is_type_compatible ( pattern_type , to_match_type ) ) {
_set_error ( " Pattern type ( " + pattern_type . to_string ( ) + " ) is not compatible with the type of the value to match ( " + to_match_type . to_string ( ) + " ). " ,
p_pattern - > line ) ;
return ;
}
} else {
// runtime typecheck
BuiltInFunctionNode * typeof_node = alloc_node < BuiltInFunctionNode > ( ) ;
typeof_node - > function = GDScriptFunctions : : TYPE_OF ;
2017-03-05 16:44:50 +01:00
2018-05-30 04:16:54 +02:00
OperatorNode * typeof_match_value = alloc_node < OperatorNode > ( ) ;
typeof_match_value - > op = OperatorNode : : OP_CALL ;
typeof_match_value - > arguments . push_back ( typeof_node ) ;
typeof_match_value - > arguments . push_back ( p_node_to_match ) ;
2017-03-05 16:44:50 +01:00
2018-05-30 04:16:54 +02:00
OperatorNode * typeof_pattern_value = alloc_node < OperatorNode > ( ) ;
typeof_pattern_value - > op = OperatorNode : : OP_CALL ;
typeof_pattern_value - > arguments . push_back ( typeof_node ) ;
typeof_pattern_value - > arguments . push_back ( p_pattern - > constant ) ;
2017-03-05 16:44:50 +01:00
2018-05-30 04:16:54 +02:00
type_comp = alloc_node < OperatorNode > ( ) ;
type_comp - > op = OperatorNode : : OP_EQUAL ;
type_comp - > arguments . push_back ( typeof_match_value ) ;
type_comp - > arguments . push_back ( typeof_pattern_value ) ;
}
2017-03-05 16:44:50 +01:00
2018-05-30 04:16:54 +02:00
// compare the actual values
2016-10-16 13:20:28 +02:00
OperatorNode * value_comp = alloc_node < OperatorNode > ( ) ;
value_comp - > op = OperatorNode : : OP_EQUAL ;
value_comp - > arguments . push_back ( p_pattern - > constant ) ;
value_comp - > arguments . push_back ( p_node_to_match ) ;
2017-03-05 16:44:50 +01:00
2018-05-30 04:16:54 +02:00
if ( type_comp ) {
OperatorNode * full_comparison = alloc_node < OperatorNode > ( ) ;
full_comparison - > op = OperatorNode : : OP_AND ;
full_comparison - > arguments . push_back ( type_comp ) ;
full_comparison - > arguments . push_back ( value_comp ) ;
2017-03-05 16:44:50 +01:00
2018-05-30 04:16:54 +02:00
p_resulting_node = full_comparison ;
} else {
p_resulting_node = value_comp ;
}
2017-03-05 16:44:50 +01:00
2016-10-05 18:48:38 +02:00
} break ;
case PatternNode : : PT_BIND : {
2016-10-16 13:20:28 +02:00
p_bindings [ p_pattern - > bind ] = p_node_to_match ;
2017-03-05 16:44:50 +01:00
2016-10-16 13:20:28 +02:00
// a bind always matches
2016-10-05 18:48:38 +02:00
ConstantNode * true_value = alloc_node < ConstantNode > ( ) ;
true_value - > value = Variant ( true ) ;
p_resulting_node = true_value ;
} break ;
case PatternNode : : PT_ARRAY : {
2017-03-05 16:44:50 +01:00
2016-10-16 13:20:28 +02:00
bool open_ended = false ;
2017-03-05 16:44:50 +01:00
2016-10-16 13:20:28 +02:00
if ( p_pattern - > array . size ( ) > 0 ) {
if ( p_pattern - > array [ p_pattern - > array . size ( ) - 1 ] - > pt_type = = PatternNode : : PT_IGNORE_REST ) {
open_ended = true ;
}
}
2017-03-05 16:44:50 +01:00
2016-10-16 13:20:28 +02:00
// typeof(value_to_match) == TYPE_ARRAY && value_to_match.size() >= length
// typeof(value_to_match) == TYPE_ARRAY && value_to_match.size() == length
2017-03-05 16:44:50 +01:00
2016-10-16 13:20:28 +02:00
{
2018-05-30 04:16:54 +02:00
OperatorNode * type_comp = NULL ;
// static type check if possible
if ( to_match_type . has_type ) {
// must be an array
if ( to_match_type . kind ! = DataType : : BUILTIN | | to_match_type . builtin_type ! = Variant : : ARRAY ) {
_set_error ( " Cannot match an array pattern with a non-array expression. " , p_pattern - > line ) ;
return ;
}
} else {
// runtime typecheck
BuiltInFunctionNode * typeof_node = alloc_node < BuiltInFunctionNode > ( ) ;
typeof_node - > function = GDScriptFunctions : : TYPE_OF ;
2017-03-05 16:44:50 +01:00
2018-05-30 04:16:54 +02:00
OperatorNode * typeof_match_value = alloc_node < OperatorNode > ( ) ;
typeof_match_value - > op = OperatorNode : : OP_CALL ;
typeof_match_value - > arguments . push_back ( typeof_node ) ;
typeof_match_value - > arguments . push_back ( p_node_to_match ) ;
2017-03-05 16:44:50 +01:00
2018-05-30 04:16:54 +02:00
IdentifierNode * typeof_array = alloc_node < IdentifierNode > ( ) ;
typeof_array - > name = " TYPE_ARRAY " ;
2017-03-05 16:44:50 +01:00
2018-05-30 04:16:54 +02:00
type_comp = alloc_node < OperatorNode > ( ) ;
type_comp - > op = OperatorNode : : OP_EQUAL ;
type_comp - > arguments . push_back ( typeof_match_value ) ;
type_comp - > arguments . push_back ( typeof_array ) ;
}
2017-03-05 16:44:50 +01:00
2016-10-16 13:20:28 +02:00
// size
ConstantNode * length = alloc_node < ConstantNode > ( ) ;
length - > value = Variant ( open_ended ? p_pattern - > array . size ( ) - 1 : p_pattern - > array . size ( ) ) ;
2017-03-05 16:44:50 +01:00
2016-10-16 13:20:28 +02:00
OperatorNode * call = alloc_node < OperatorNode > ( ) ;
call - > op = OperatorNode : : OP_CALL ;
call - > arguments . push_back ( p_node_to_match ) ;
2017-03-05 16:44:50 +01:00
2016-10-16 13:20:28 +02:00
IdentifierNode * size = alloc_node < IdentifierNode > ( ) ;
size - > name = " size " ;
call - > arguments . push_back ( size ) ;
2017-03-05 16:44:50 +01:00
2016-10-16 13:20:28 +02:00
OperatorNode * length_comparison = alloc_node < OperatorNode > ( ) ;
length_comparison - > op = open_ended ? OperatorNode : : OP_GREATER_EQUAL : OperatorNode : : OP_EQUAL ;
length_comparison - > arguments . push_back ( call ) ;
length_comparison - > arguments . push_back ( length ) ;
2017-03-05 16:44:50 +01:00
2018-05-30 04:16:54 +02:00
if ( type_comp ) {
OperatorNode * type_and_length_comparison = alloc_node < OperatorNode > ( ) ;
type_and_length_comparison - > op = OperatorNode : : OP_AND ;
type_and_length_comparison - > arguments . push_back ( type_comp ) ;
type_and_length_comparison - > arguments . push_back ( length_comparison ) ;
2017-03-05 16:44:50 +01:00
2018-05-30 04:16:54 +02:00
p_resulting_node = type_and_length_comparison ;
} else {
p_resulting_node = length_comparison ;
}
2016-10-16 13:20:28 +02:00
}
2017-03-05 16:44:50 +01:00
2016-10-16 13:20:28 +02:00
for ( int i = 0 ; i < p_pattern - > array . size ( ) ; i + + ) {
PatternNode * pattern = p_pattern - > array [ i ] ;
2017-03-05 16:44:50 +01:00
2016-10-16 13:20:28 +02:00
Node * condition = NULL ;
2017-03-05 16:44:50 +01:00
2016-10-16 13:20:28 +02:00
ConstantNode * index = alloc_node < ConstantNode > ( ) ;
index - > value = Variant ( i ) ;
2017-03-05 16:44:50 +01:00
2016-10-16 13:20:28 +02:00
OperatorNode * indexed_value = alloc_node < OperatorNode > ( ) ;
indexed_value - > op = OperatorNode : : OP_INDEX ;
indexed_value - > arguments . push_back ( p_node_to_match ) ;
indexed_value - > arguments . push_back ( index ) ;
2017-03-05 16:44:50 +01:00
2016-10-16 13:20:28 +02:00
_generate_pattern ( pattern , indexed_value , condition , p_bindings ) ;
2017-03-05 16:44:50 +01:00
2016-10-16 13:20:28 +02:00
// concatenate all the patterns with &&
OperatorNode * and_node = alloc_node < OperatorNode > ( ) ;
and_node - > op = OperatorNode : : OP_AND ;
and_node - > arguments . push_back ( p_resulting_node ) ;
and_node - > arguments . push_back ( condition ) ;
2017-03-05 16:44:50 +01:00
2016-10-16 13:20:28 +02:00
p_resulting_node = and_node ;
}
2017-03-05 16:44:50 +01:00
2016-10-05 18:48:38 +02:00
} break ;
case PatternNode : : PT_DICTIONARY : {
2017-03-05 16:44:50 +01:00
2016-10-16 13:20:28 +02:00
bool open_ended = false ;
2017-03-05 16:44:50 +01:00
2016-10-16 13:20:28 +02:00
if ( p_pattern - > array . size ( ) > 0 ) {
open_ended = true ;
}
2017-03-05 16:44:50 +01:00
2016-10-16 13:20:28 +02:00
// typeof(value_to_match) == TYPE_DICTIONARY && value_to_match.size() >= length
// typeof(value_to_match) == TYPE_DICTIONARY && value_to_match.size() == length
2017-03-05 16:44:50 +01:00
2016-10-16 13:20:28 +02:00
{
2018-05-30 04:16:54 +02:00
OperatorNode * type_comp = NULL ;
// static type check if possible
if ( to_match_type . has_type ) {
// must be an dictionary
if ( to_match_type . kind ! = DataType : : BUILTIN | | to_match_type . builtin_type ! = Variant : : DICTIONARY ) {
_set_error ( " Cannot match an dictionary pattern with a non-dictionary expression. " , p_pattern - > line ) ;
return ;
}
} else {
// runtime typecheck
BuiltInFunctionNode * typeof_node = alloc_node < BuiltInFunctionNode > ( ) ;
typeof_node - > function = GDScriptFunctions : : TYPE_OF ;
2017-03-05 16:44:50 +01:00
2018-05-30 04:16:54 +02:00
OperatorNode * typeof_match_value = alloc_node < OperatorNode > ( ) ;
typeof_match_value - > op = OperatorNode : : OP_CALL ;
typeof_match_value - > arguments . push_back ( typeof_node ) ;
typeof_match_value - > arguments . push_back ( p_node_to_match ) ;
2017-03-05 16:44:50 +01:00
2018-05-30 04:16:54 +02:00
IdentifierNode * typeof_dictionary = alloc_node < IdentifierNode > ( ) ;
typeof_dictionary - > name = " TYPE_DICTIONARY " ;
2017-03-05 16:44:50 +01:00
2018-05-30 04:16:54 +02:00
type_comp = alloc_node < OperatorNode > ( ) ;
type_comp - > op = OperatorNode : : OP_EQUAL ;
type_comp - > arguments . push_back ( typeof_match_value ) ;
type_comp - > arguments . push_back ( typeof_dictionary ) ;
}
2017-03-05 16:44:50 +01:00
2016-10-16 13:20:28 +02:00
// size
ConstantNode * length = alloc_node < ConstantNode > ( ) ;
length - > value = Variant ( open_ended ? p_pattern - > dictionary . size ( ) - 1 : p_pattern - > dictionary . size ( ) ) ;
2017-03-05 16:44:50 +01:00
2016-10-16 13:20:28 +02:00
OperatorNode * call = alloc_node < OperatorNode > ( ) ;
call - > op = OperatorNode : : OP_CALL ;
call - > arguments . push_back ( p_node_to_match ) ;
2017-03-05 16:44:50 +01:00
2016-10-16 13:20:28 +02:00
IdentifierNode * size = alloc_node < IdentifierNode > ( ) ;
size - > name = " size " ;
call - > arguments . push_back ( size ) ;
2017-03-05 16:44:50 +01:00
2016-10-16 13:20:28 +02:00
OperatorNode * length_comparison = alloc_node < OperatorNode > ( ) ;
length_comparison - > op = open_ended ? OperatorNode : : OP_GREATER_EQUAL : OperatorNode : : OP_EQUAL ;
length_comparison - > arguments . push_back ( call ) ;
length_comparison - > arguments . push_back ( length ) ;
2017-03-05 16:44:50 +01:00
2018-05-30 04:16:54 +02:00
if ( type_comp ) {
OperatorNode * type_and_length_comparison = alloc_node < OperatorNode > ( ) ;
type_and_length_comparison - > op = OperatorNode : : OP_AND ;
type_and_length_comparison - > arguments . push_back ( type_comp ) ;
type_and_length_comparison - > arguments . push_back ( length_comparison ) ;
2017-03-05 16:44:50 +01:00
2018-05-30 04:16:54 +02:00
p_resulting_node = type_and_length_comparison ;
} else {
p_resulting_node = length_comparison ;
}
2016-10-16 13:20:28 +02:00
}
2017-03-05 16:44:50 +01:00
for ( Map < ConstantNode * , PatternNode * > : : Element * e = p_pattern - > dictionary . front ( ) ; e ; e = e - > next ( ) ) {
2016-10-16 13:20:28 +02:00
Node * condition = NULL ;
2017-03-05 16:44:50 +01:00
2018-09-13 03:38:39 +02:00
// check for has, then for pattern
2017-03-05 16:44:50 +01:00
2016-10-16 13:20:28 +02:00
IdentifierNode * has = alloc_node < IdentifierNode > ( ) ;
has - > name = " has " ;
2017-03-05 16:44:50 +01:00
2016-10-16 13:20:28 +02:00
OperatorNode * has_call = alloc_node < OperatorNode > ( ) ;
has_call - > op = OperatorNode : : OP_CALL ;
has_call - > arguments . push_back ( p_node_to_match ) ;
has_call - > arguments . push_back ( has ) ;
has_call - > arguments . push_back ( e - > key ( ) ) ;
2017-03-05 16:44:50 +01:00
2016-10-16 13:20:28 +02:00
if ( e - > value ( ) ) {
2017-03-05 16:44:50 +01:00
2016-10-16 13:20:28 +02:00
OperatorNode * indexed_value = alloc_node < OperatorNode > ( ) ;
indexed_value - > op = OperatorNode : : OP_INDEX ;
indexed_value - > arguments . push_back ( p_node_to_match ) ;
indexed_value - > arguments . push_back ( e - > key ( ) ) ;
2017-03-05 16:44:50 +01:00
2016-10-16 13:20:28 +02:00
_generate_pattern ( e - > value ( ) , indexed_value , condition , p_bindings ) ;
2017-03-05 16:44:50 +01:00
2016-10-16 13:20:28 +02:00
OperatorNode * has_and_pattern = alloc_node < OperatorNode > ( ) ;
has_and_pattern - > op = OperatorNode : : OP_AND ;
has_and_pattern - > arguments . push_back ( has_call ) ;
has_and_pattern - > arguments . push_back ( condition ) ;
2017-03-05 16:44:50 +01:00
2016-10-16 13:20:28 +02:00
condition = has_and_pattern ;
} else {
condition = has_call ;
}
2017-03-05 16:44:50 +01:00
2016-10-16 13:20:28 +02:00
// concatenate all the patterns with &&
OperatorNode * and_node = alloc_node < OperatorNode > ( ) ;
and_node - > op = OperatorNode : : OP_AND ;
and_node - > arguments . push_back ( p_resulting_node ) ;
and_node - > arguments . push_back ( condition ) ;
2017-03-05 16:44:50 +01:00
2016-10-16 13:20:28 +02:00
p_resulting_node = and_node ;
}
2017-03-05 16:44:50 +01:00
2016-10-05 18:48:38 +02:00
} break ;
2016-10-16 13:20:28 +02:00
case PatternNode : : PT_IGNORE_REST :
2016-10-05 18:48:38 +02:00
case PatternNode : : PT_WILDCARD : {
// simply generate a `true`
ConstantNode * true_value = alloc_node < ConstantNode > ( ) ;
true_value - > value = Variant ( true ) ;
p_resulting_node = true_value ;
} break ;
default : {
2017-03-05 16:44:50 +01:00
2016-10-05 18:48:38 +02:00
} break ;
}
}
2018-05-30 04:16:54 +02:00
void GDScriptParser : : _transform_match_statment ( MatchNode * p_match_statement ) {
2016-10-05 18:48:38 +02:00
IdentifierNode * id = alloc_node < IdentifierNode > ( ) ;
2016-10-16 13:20:28 +02:00
id - > name = " #match_value " ;
2018-06-06 03:57:44 +02:00
id - > line = p_match_statement - > line ;
2018-05-30 04:16:54 +02:00
id - > datatype = _reduce_node_type ( p_match_statement - > val_to_match ) ;
2018-06-05 18:50:21 +02:00
if ( id - > datatype . has_type ) {
_mark_line_as_safe ( id - > line ) ;
} else {
_mark_line_as_unsafe ( id - > line ) ;
}
2018-05-30 04:16:54 +02:00
if ( error_set ) {
return ;
}
2017-03-05 16:44:50 +01:00
2016-10-05 18:48:38 +02:00
for ( int i = 0 ; i < p_match_statement - > branches . size ( ) ; i + + ) {
2017-03-05 16:44:50 +01:00
2016-10-05 18:48:38 +02:00
PatternBranchNode * branch = p_match_statement - > branches [ i ] ;
2017-03-05 16:44:50 +01:00
2016-10-16 13:20:28 +02:00
MatchNode : : CompiledPatternBranch compiled_branch ;
compiled_branch . compiled_pattern = NULL ;
2017-03-05 16:44:50 +01:00
Map < StringName , Node * > binding ;
2016-10-16 13:20:28 +02:00
for ( int j = 0 ; j < branch - > patterns . size ( ) ; j + + ) {
PatternNode * pattern = branch - > patterns [ j ] ;
2018-06-05 18:50:21 +02:00
_mark_line_as_safe ( pattern - > line ) ;
2017-03-05 16:44:50 +01:00
Map < StringName , Node * > bindings ;
2018-05-30 04:16:54 +02:00
Node * resulting_node = NULL ;
2016-10-16 13:20:28 +02:00
_generate_pattern ( pattern , id , resulting_node , bindings ) ;
2017-03-05 16:44:50 +01:00
2018-05-30 04:16:54 +02:00
if ( ! resulting_node ) {
return ;
}
2016-10-16 13:20:28 +02:00
if ( ! binding . empty ( ) & & ! bindings . empty ( ) ) {
_set_error ( " Multipatterns can't contain bindings " ) ;
return ;
} else {
binding = bindings ;
}
2017-03-05 16:44:50 +01:00
2018-05-30 04:16:54 +02:00
// Result is always a boolean
DataType resulting_node_type ;
resulting_node_type . has_type = true ;
resulting_node_type . is_constant = true ;
resulting_node_type . kind = DataType : : BUILTIN ;
resulting_node_type . builtin_type = Variant : : BOOL ;
resulting_node - > set_datatype ( resulting_node_type ) ;
2016-10-16 13:20:28 +02:00
if ( compiled_branch . compiled_pattern ) {
OperatorNode * or_node = alloc_node < OperatorNode > ( ) ;
or_node - > op = OperatorNode : : OP_OR ;
or_node - > arguments . push_back ( compiled_branch . compiled_pattern ) ;
or_node - > arguments . push_back ( resulting_node ) ;
2017-03-05 16:44:50 +01:00
2016-10-16 13:20:28 +02:00
compiled_branch . compiled_pattern = or_node ;
} else {
// single pattern | first one
compiled_branch . compiled_pattern = resulting_node ;
}
}
2017-03-05 16:44:50 +01:00
2016-10-16 13:20:28 +02:00
// prepare the body ...hehe
2017-03-05 16:44:50 +01:00
for ( Map < StringName , Node * > : : Element * e = binding . front ( ) ; e ; e = e - > next ( ) ) {
2018-05-30 04:16:54 +02:00
if ( ! branch - > body - > variables . has ( e - > key ( ) ) ) {
_set_error ( " Parser bug: missing pattern bind variable. " , branch - > line ) ;
ERR_FAIL ( ) ;
}
LocalVarNode * local_var = branch - > body - > variables [ e - > key ( ) ] ;
2016-10-16 13:20:28 +02:00
local_var - > assign = e - > value ( ) ;
2018-05-30 04:16:54 +02:00
local_var - > set_datatype ( local_var - > assign - > get_datatype ( ) ) ;
2016-10-16 13:20:28 +02:00
IdentifierNode * id = alloc_node < IdentifierNode > ( ) ;
id - > name = local_var - > name ;
2018-05-30 04:16:54 +02:00
id - > declared_block = branch - > body ;
id - > set_datatype ( local_var - > assign - > get_datatype ( ) ) ;
2016-10-16 13:20:28 +02:00
OperatorNode * op = alloc_node < OperatorNode > ( ) ;
2017-03-05 16:44:50 +01:00
op - > op = OperatorNode : : OP_ASSIGN ;
2016-10-16 13:20:28 +02:00
op - > arguments . push_back ( id ) ;
op - > arguments . push_back ( local_var - > assign ) ;
2017-03-05 16:44:50 +01:00
2016-10-16 13:20:28 +02:00
branch - > body - > statements . push_front ( op ) ;
branch - > body - > statements . push_front ( local_var ) ;
}
2017-03-05 16:44:50 +01:00
2016-10-16 13:20:28 +02:00
compiled_branch . body = branch - > body ;
2017-03-05 16:44:50 +01:00
2016-10-16 13:20:28 +02:00
p_match_statement - > compiled_pattern_branches . push_back ( compiled_branch ) ;
2016-10-05 18:48:38 +02:00
}
2016-09-30 21:40:31 +02:00
}
2017-11-16 18:38:18 +01:00
void GDScriptParser : : _parse_block ( BlockNode * p_block , bool p_static ) {
2014-02-10 02:10:30 +01:00
int indent_level = tab_level . back ( ) - > get ( ) ;
# ifdef DEBUG_ENABLED
NewLineNode * nl = alloc_node < NewLineNode > ( ) ;
2017-03-05 16:44:50 +01:00
nl - > line = tokenizer - > get_token_line ( ) ;
2014-02-10 02:10:30 +01:00
p_block - > statements . push_back ( nl ) ;
# endif
2017-03-23 19:07:02 +01:00
bool is_first_line = true ;
2017-03-05 16:44:50 +01:00
while ( true ) {
2017-03-23 19:07:02 +01:00
if ( ! is_first_line & & tab_level . back ( ) - > prev ( ) & & tab_level . back ( ) - > prev ( ) - > get ( ) = = indent_level ) {
// pythonic single-line expression, don't parse future lines
tab_level . pop_back ( ) ;
2017-04-08 18:41:08 +02:00
p_block - > end_line = tokenizer - > get_token_line ( ) ;
2017-03-23 19:07:02 +01:00
return ;
}
is_first_line = false ;
2014-02-10 02:10:30 +01:00
2017-11-16 18:38:18 +01:00
GDScriptTokenizer : : Token token = tokenizer - > get_token ( ) ;
2014-02-10 02:10:30 +01:00
if ( error_set )
return ;
2017-03-05 16:44:50 +01:00
if ( indent_level > tab_level . back ( ) - > get ( ) ) {
p_block - > end_line = tokenizer - > get_token_line ( ) ;
2014-02-10 02:10:30 +01:00
return ; //go back a level
}
2017-03-05 16:44:50 +01:00
if ( pending_newline ! = - 1 ) {
2014-05-24 06:35:47 +02:00
NewLineNode * nl = alloc_node < NewLineNode > ( ) ;
2017-03-05 16:44:50 +01:00
nl - > line = pending_newline ;
2014-05-24 06:35:47 +02:00
p_block - > statements . push_back ( nl ) ;
2017-03-05 16:44:50 +01:00
pending_newline = - 1 ;
2014-05-24 06:35:47 +02:00
}
2018-07-01 18:17:40 +02:00
# ifdef DEBUG_ENABLED
2017-03-05 16:44:50 +01:00
switch ( token ) {
2018-05-30 04:16:54 +02:00
case GDScriptTokenizer : : TK_EOF :
case GDScriptTokenizer : : TK_ERROR :
case GDScriptTokenizer : : TK_NEWLINE :
case GDScriptTokenizer : : TK_CF_PASS : {
// will check later
} break ;
default : {
2018-07-01 18:17:40 +02:00
if ( p_block - > has_return & & ! current_function - > has_unreachable_code ) {
_add_warning ( GDScriptWarning : : UNREACHABLE_CODE , - 1 , current_function - > name . operator String ( ) ) ;
current_function - > has_unreachable_code = true ;
}
2018-05-30 04:16:54 +02:00
} break ;
}
2018-07-01 18:17:40 +02:00
# endif // DEBUG_ENABLED
2018-05-30 04:16:54 +02:00
switch ( token ) {
2017-11-16 18:38:18 +01:00
case GDScriptTokenizer : : TK_EOF :
2017-03-05 16:44:50 +01:00
p_block - > end_line = tokenizer - > get_token_line ( ) ;
2017-11-16 18:38:18 +01:00
case GDScriptTokenizer : : TK_ERROR : {
2014-02-10 02:10:30 +01:00
return ; //go back
//end of file!
} break ;
2017-11-16 18:38:18 +01:00
case GDScriptTokenizer : : TK_NEWLINE : {
2014-02-10 02:10:30 +01:00
if ( ! _parse_newline ( ) ) {
if ( ! error_set ) {
2017-03-05 16:44:50 +01:00
p_block - > end_line = tokenizer - > get_token_line ( ) ;
pending_newline = p_block - > end_line ;
2014-02-10 02:10:30 +01:00
}
return ;
}
2014-05-24 06:35:47 +02:00
NewLineNode * nl = alloc_node < NewLineNode > ( ) ;
2017-03-05 16:44:50 +01:00
nl - > line = tokenizer - > get_token_line ( ) ;
2014-05-24 06:35:47 +02:00
p_block - > statements . push_back ( nl ) ;
2014-02-10 02:10:30 +01:00
} break ;
2017-11-16 18:38:18 +01:00
case GDScriptTokenizer : : TK_CF_PASS : {
if ( tokenizer - > get_token ( 1 ) ! = GDScriptTokenizer : : TK_SEMICOLON & & tokenizer - > get_token ( 1 ) ! = GDScriptTokenizer : : TK_NEWLINE & & tokenizer - > get_token ( 1 ) ! = GDScriptTokenizer : : TK_EOF ) {
2014-02-10 02:10:30 +01:00
_set_error ( " Expected ';' or <NewLine>. " ) ;
return ;
}
2018-06-05 18:50:21 +02:00
_mark_line_as_safe ( tokenizer - > get_token_line ( ) ) ;
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_SEMICOLON ) {
2016-07-10 17:20:53 +02:00
// Ignore semicolon after 'pass'
tokenizer - > advance ( ) ;
}
2014-02-10 02:10:30 +01:00
} break ;
2017-11-16 18:38:18 +01:00
case GDScriptTokenizer : : TK_PR_VAR : {
2014-02-10 02:10:30 +01:00
//variale declaration and (eventual) initialization
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2018-05-30 04:16:54 +02:00
int var_line = tokenizer - > get_token_line ( ) ;
2017-03-31 19:28:34 +02:00
if ( ! tokenizer - > is_token_literal ( 0 , true ) ) {
2014-02-10 02:10:30 +01:00
_set_error ( " Expected identifier for local variable name. " ) ;
return ;
}
2017-03-31 19:28:34 +02:00
StringName n = tokenizer - > get_token_literal ( ) ;
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2017-03-05 16:44:50 +01:00
if ( current_function ) {
for ( int i = 0 ; i < current_function - > arguments . size ( ) ; i + + ) {
if ( n = = current_function - > arguments [ i ] ) {
_set_error ( " Variable ' " + String ( n ) + " ' already defined in the scope (at line: " + itos ( current_function - > line ) + " ). " ) ;
2016-05-30 17:22:34 +02:00
return ;
}
}
}
BlockNode * check_block = p_block ;
2017-03-05 16:44:50 +01:00
while ( check_block ) {
2018-05-30 04:16:54 +02:00
if ( check_block - > variables . has ( n ) ) {
_set_error ( " Variable ' " + String ( n ) + " ' already defined in the scope (at line: " + itos ( check_block - > variables [ n ] - > line ) + " ). " ) ;
return ;
2016-05-30 17:22:34 +02:00
}
check_block = check_block - > parent_block ;
}
2014-02-10 02:10:30 +01:00
//must know when the local variable is declared
LocalVarNode * lv = alloc_node < LocalVarNode > ( ) ;
2017-03-05 16:44:50 +01:00
lv - > name = n ;
2018-05-30 04:16:54 +02:00
lv - > line = var_line ;
2014-02-10 02:10:30 +01:00
p_block - > statements . push_back ( lv ) ;
2017-03-05 16:44:50 +01:00
Node * assigned = NULL ;
2014-02-10 02:10:30 +01:00
2018-05-30 04:16:51 +02:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COLON ) {
2018-06-19 07:55:52 +02:00
if ( tokenizer - > get_token ( 1 ) = = GDScriptTokenizer : : TK_OP_ASSIGN ) {
lv - > datatype = DataType ( ) ;
# ifdef DEBUG_ENABLED
lv - > datatype . infer_type = true ;
# endif
tokenizer - > advance ( ) ;
} else if ( ! _parse_type ( lv - > datatype ) ) {
2018-05-30 04:16:51 +02:00
_set_error ( " Expected type for variable. " ) ;
return ;
}
}
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_OP_ASSIGN ) {
2014-02-10 02:10:30 +01:00
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2017-08-21 21:15:36 +02:00
Node * subexpr = _parse_and_reduce_expression ( p_block , p_static ) ;
2015-08-30 16:50:10 +02:00
if ( ! subexpr ) {
if ( _recover_from_completion ( ) ) {
break ;
}
2014-02-10 02:10:30 +01:00
return ;
2015-08-30 16:50:10 +02:00
}
2018-05-30 04:16:54 +02:00
lv - > assignments + + ;
2017-03-05 16:44:50 +01:00
assigned = subexpr ;
2014-02-10 02:10:30 +01:00
} else {
ConstantNode * c = alloc_node < ConstantNode > ( ) ;
2018-05-30 04:16:54 +02:00
if ( lv - > datatype . has_type & & lv - > datatype . kind = = DataType : : BUILTIN ) {
Variant : : CallError err ;
c - > value = Variant : : construct ( lv - > datatype . builtin_type , NULL , 0 , err ) ;
} else {
c - > value = Variant ( ) ;
}
2018-06-06 03:57:44 +02:00
c - > line = var_line ;
2014-02-10 02:10:30 +01:00
assigned = c ;
}
2018-07-01 18:17:40 +02:00
lv - > assign = assigned ;
2017-08-08 16:44:49 +02:00
//must be added later, to avoid self-referencing.
2018-05-30 04:16:54 +02:00
p_block - > variables . insert ( n , lv ) ;
2017-08-08 16:44:49 +02:00
2014-02-10 02:10:30 +01:00
IdentifierNode * id = alloc_node < IdentifierNode > ( ) ;
2017-03-05 16:44:50 +01:00
id - > name = n ;
2018-05-30 04:16:54 +02:00
id - > declared_block = p_block ;
2018-06-06 03:57:44 +02:00
id - > line = var_line ;
2014-02-10 02:10:30 +01:00
OperatorNode * op = alloc_node < OperatorNode > ( ) ;
2017-03-05 16:44:50 +01:00
op - > op = OperatorNode : : OP_ASSIGN ;
2014-02-10 02:10:30 +01:00
op - > arguments . push_back ( id ) ;
op - > arguments . push_back ( assigned ) ;
2018-06-06 03:57:44 +02:00
op - > line = var_line ;
2014-02-10 02:10:30 +01:00
p_block - > statements . push_back ( op ) ;
2018-05-30 04:16:54 +02:00
lv - > assign_op = op ;
lv - > assign = assigned ;
2014-02-10 02:10:30 +01:00
2018-07-01 18:17:40 +02:00
lv - > assign_op = op ;
2015-06-24 18:29:23 +02:00
if ( ! _end_statement ( ) ) {
_set_error ( " Expected end of statement (var) " ) ;
return ;
}
2014-02-10 02:10:30 +01:00
} break ;
2017-11-16 18:38:18 +01:00
case GDScriptTokenizer : : TK_CF_IF : {
2014-02-10 02:10:30 +01:00
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2017-03-05 16:44:50 +01:00
Node * condition = _parse_and_reduce_expression ( p_block , p_static ) ;
2015-08-30 16:50:10 +02:00
if ( ! condition ) {
if ( _recover_from_completion ( ) ) {
break ;
}
2014-02-10 02:10:30 +01:00
return ;
2015-08-30 16:50:10 +02:00
}
2014-02-10 02:10:30 +01:00
ControlFlowNode * cf_if = alloc_node < ControlFlowNode > ( ) ;
2017-03-05 16:44:50 +01:00
cf_if - > cf_type = ControlFlowNode : : CF_IF ;
2014-02-10 02:10:30 +01:00
cf_if - > arguments . push_back ( condition ) ;
cf_if - > body = alloc_node < BlockNode > ( ) ;
2017-03-05 16:44:50 +01:00
cf_if - > body - > parent_block = p_block ;
2017-08-25 05:34:32 +02:00
cf_if - > body - > if_condition = condition ; //helps code completion
2014-02-10 02:10:30 +01:00
p_block - > sub_blocks . push_back ( cf_if - > body ) ;
if ( ! _enter_indent_block ( cf_if - > body ) ) {
2017-04-06 05:11:05 +02:00
_set_error ( " Expected indented block after 'if' " ) ;
2017-03-05 16:44:50 +01:00
p_block - > end_line = tokenizer - > get_token_line ( ) ;
2014-02-10 02:10:30 +01:00
return ;
}
2017-03-05 16:44:50 +01:00
current_block = cf_if - > body ;
_parse_block ( cf_if - > body , p_static ) ;
current_block = p_block ;
2014-12-17 02:31:57 +01:00
2014-02-10 02:10:30 +01:00
if ( error_set )
return ;
p_block - > statements . push_back ( cf_if ) ;
2018-05-30 04:16:54 +02:00
bool all_have_return = cf_if - > body - > has_return ;
bool have_else = false ;
2017-03-05 16:44:50 +01:00
while ( true ) {
2014-02-10 02:10:30 +01:00
2017-11-16 18:38:18 +01:00
while ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_NEWLINE & & _parse_newline ( ) )
2017-04-08 18:41:08 +02:00
;
2014-02-10 02:10:30 +01:00
if ( tab_level . back ( ) - > get ( ) < indent_level ) { //not at current indent level
2017-03-05 16:44:50 +01:00
p_block - > end_line = tokenizer - > get_token_line ( ) ;
2014-02-10 02:10:30 +01:00
return ;
}
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CF_ELIF ) {
2014-02-10 02:10:30 +01:00
if ( tab_level . back ( ) - > get ( ) > indent_level ) {
_set_error ( " Invalid indent " ) ;
return ;
}
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
cf_if - > body_else = alloc_node < BlockNode > ( ) ;
cf_if - > body_else - > parent_block = p_block ;
2014-02-10 02:10:30 +01:00
p_block - > sub_blocks . push_back ( cf_if - > body_else ) ;
ControlFlowNode * cf_else = alloc_node < ControlFlowNode > ( ) ;
2017-03-05 16:44:50 +01:00
cf_else - > cf_type = ControlFlowNode : : CF_IF ;
2014-02-10 02:10:30 +01:00
//condition
2017-03-05 16:44:50 +01:00
Node * condition = _parse_and_reduce_expression ( p_block , p_static ) ;
2015-08-30 16:50:10 +02:00
if ( ! condition ) {
if ( _recover_from_completion ( ) ) {
break ;
}
2014-02-10 02:10:30 +01:00
return ;
2015-08-30 16:50:10 +02:00
}
2014-02-10 02:10:30 +01:00
cf_else - > arguments . push_back ( condition ) ;
2017-03-05 16:44:50 +01:00
cf_else - > cf_type = ControlFlowNode : : CF_IF ;
2014-02-10 02:10:30 +01:00
cf_if - > body_else - > statements . push_back ( cf_else ) ;
2017-03-05 16:44:50 +01:00
cf_if = cf_else ;
cf_if - > body = alloc_node < BlockNode > ( ) ;
cf_if - > body - > parent_block = p_block ;
2014-02-10 02:10:30 +01:00
p_block - > sub_blocks . push_back ( cf_if - > body ) ;
if ( ! _enter_indent_block ( cf_if - > body ) ) {
2015-12-29 15:41:37 +01:00
_set_error ( " Expected indented block after 'elif' " ) ;
2017-03-05 16:44:50 +01:00
p_block - > end_line = tokenizer - > get_token_line ( ) ;
2014-02-10 02:10:30 +01:00
return ;
}
2017-03-05 16:44:50 +01:00
current_block = cf_else - > body ;
_parse_block ( cf_else - > body , p_static ) ;
current_block = p_block ;
2014-02-10 02:10:30 +01:00
if ( error_set )
return ;
2018-05-30 04:16:54 +02:00
all_have_return = all_have_return & & cf_else - > body - > has_return ;
2017-11-16 18:38:18 +01:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CF_ELSE ) {
2014-02-10 02:10:30 +01:00
if ( tab_level . back ( ) - > get ( ) > indent_level ) {
_set_error ( " Invalid indent " ) ;
return ;
}
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2017-03-05 16:44:50 +01:00
cf_if - > body_else = alloc_node < BlockNode > ( ) ;
cf_if - > body_else - > parent_block = p_block ;
2014-02-10 02:10:30 +01:00
p_block - > sub_blocks . push_back ( cf_if - > body_else ) ;
if ( ! _enter_indent_block ( cf_if - > body_else ) ) {
2015-12-29 15:41:37 +01:00
_set_error ( " Expected indented block after 'else' " ) ;
2017-03-05 16:44:50 +01:00
p_block - > end_line = tokenizer - > get_token_line ( ) ;
2014-02-10 02:10:30 +01:00
return ;
}
2017-03-05 16:44:50 +01:00
current_block = cf_if - > body_else ;
_parse_block ( cf_if - > body_else , p_static ) ;
current_block = p_block ;
2014-02-10 02:10:30 +01:00
if ( error_set )
return ;
2018-05-30 04:16:54 +02:00
all_have_return = all_have_return & & cf_if - > body_else - > has_return ;
have_else = true ;
2014-02-10 02:10:30 +01:00
break ; //after else, exit
} else
break ;
}
2018-05-30 04:16:54 +02:00
cf_if - > body - > has_return = all_have_return ;
// If there's no else block, path out of the if might not have a return
p_block - > has_return = all_have_return & & have_else ;
2014-02-10 02:10:30 +01:00
} break ;
2017-11-16 18:38:18 +01:00
case GDScriptTokenizer : : TK_CF_WHILE : {
2014-02-10 02:10:30 +01:00
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2017-03-05 16:44:50 +01:00
Node * condition = _parse_and_reduce_expression ( p_block , p_static ) ;
2015-08-30 16:50:10 +02:00
if ( ! condition ) {
if ( _recover_from_completion ( ) ) {
break ;
}
2014-02-10 02:10:30 +01:00
return ;
2015-08-30 16:50:10 +02:00
}
2014-02-10 02:10:30 +01:00
ControlFlowNode * cf_while = alloc_node < ControlFlowNode > ( ) ;
2017-03-05 16:44:50 +01:00
cf_while - > cf_type = ControlFlowNode : : CF_WHILE ;
2014-02-10 02:10:30 +01:00
cf_while - > arguments . push_back ( condition ) ;
cf_while - > body = alloc_node < BlockNode > ( ) ;
2017-03-05 16:44:50 +01:00
cf_while - > body - > parent_block = p_block ;
2014-02-10 02:10:30 +01:00
p_block - > sub_blocks . push_back ( cf_while - > body ) ;
if ( ! _enter_indent_block ( cf_while - > body ) ) {
2015-12-29 15:41:37 +01:00
_set_error ( " Expected indented block after 'while' " ) ;
2017-03-05 16:44:50 +01:00
p_block - > end_line = tokenizer - > get_token_line ( ) ;
2014-02-10 02:10:30 +01:00
return ;
}
2017-03-05 16:44:50 +01:00
current_block = cf_while - > body ;
_parse_block ( cf_while - > body , p_static ) ;
current_block = p_block ;
2014-02-10 02:10:30 +01:00
if ( error_set )
return ;
2018-05-30 04:16:54 +02:00
p_block - > has_return = cf_while - > body - > has_return ;
2014-02-10 02:10:30 +01:00
p_block - > statements . push_back ( cf_while ) ;
} break ;
2017-11-16 18:38:18 +01:00
case GDScriptTokenizer : : TK_CF_FOR : {
2014-02-10 02:10:30 +01:00
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
2017-03-31 19:28:34 +02:00
if ( ! tokenizer - > is_token_literal ( 0 , true ) ) {
2014-02-10 02:10:30 +01:00
_set_error ( " identifier expected after 'for' " ) ;
}
IdentifierNode * id = alloc_node < IdentifierNode > ( ) ;
2017-03-05 16:44:50 +01:00
id - > name = tokenizer - > get_token_identifier ( ) ;
2014-02-10 02:10:30 +01:00
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_OP_IN ) {
2014-02-10 02:10:30 +01:00
_set_error ( " 'in' expected after identifier " ) ;
return ;
}
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
Node * container = _parse_and_reduce_expression ( p_block , p_static ) ;
2015-08-30 16:50:10 +02:00
if ( ! container ) {
if ( _recover_from_completion ( ) ) {
break ;
}
2014-02-10 02:10:30 +01:00
return ;
2015-08-30 16:50:10 +02:00
}
2014-02-10 02:10:30 +01:00
2018-05-30 04:16:54 +02:00
DataType iter_type ;
iter_type . is_constant = true ;
2017-03-05 16:44:50 +01:00
if ( container - > type = = Node : : TYPE_OPERATOR ) {
2017-01-12 00:09:45 +01:00
2017-03-05 16:44:50 +01:00
OperatorNode * op = static_cast < OperatorNode * > ( container ) ;
2017-11-16 18:38:18 +01:00
if ( op - > op = = OperatorNode : : OP_CALL & & op - > arguments [ 0 ] - > type = = Node : : TYPE_BUILT_IN_FUNCTION & & static_cast < BuiltInFunctionNode * > ( op - > arguments [ 0 ] ) - > function = = GDScriptFunctions : : GEN_RANGE ) {
2017-01-12 00:09:45 +01:00
//iterating a range, so see if range() can be optimized without allocating memory, by replacing it by vectors (which can work as iterable too!)
2017-03-05 16:44:50 +01:00
Vector < Node * > args ;
2017-01-12 00:09:45 +01:00
Vector < double > constants ;
2017-03-05 16:44:50 +01:00
bool constant = false ;
2017-01-12 00:09:45 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = 1 ; i < op - > arguments . size ( ) ; i + + ) {
2017-01-12 00:09:45 +01:00
args . push_back ( op - > arguments [ i ] ) ;
2017-03-05 16:44:50 +01:00
if ( constant & & op - > arguments [ i ] - > type = = Node : : TYPE_CONSTANT ) {
ConstantNode * c = static_cast < ConstantNode * > ( op - > arguments [ i ] ) ;
if ( c - > value . get_type ( ) = = Variant : : REAL | | c - > value . get_type ( ) = = Variant : : INT ) {
2017-01-12 00:09:45 +01:00
constants . push_back ( c - > value ) ;
2017-03-05 16:44:50 +01:00
constant = true ;
2017-01-12 00:09:45 +01:00
}
2017-02-13 02:51:16 +01:00
} else {
2017-03-05 16:44:50 +01:00
constant = false ;
2017-01-12 00:09:45 +01:00
}
}
2017-03-05 16:44:50 +01:00
if ( args . size ( ) > 0 & & args . size ( ) < 4 ) {
2017-01-12 00:09:45 +01:00
if ( constant ) {
ConstantNode * cn = alloc_node < ConstantNode > ( ) ;
2017-03-05 16:44:50 +01:00
switch ( args . size ( ) ) {
2017-05-16 12:56:49 +02:00
case 1 : cn - > value = ( int ) constants [ 0 ] ; break ;
2017-03-05 16:44:50 +01:00
case 2 : cn - > value = Vector2 ( constants [ 0 ] , constants [ 1 ] ) ; break ;
case 3 : cn - > value = Vector3 ( constants [ 0 ] , constants [ 1 ] , constants [ 2 ] ) ; break ;
2017-01-12 00:09:45 +01:00
}
2018-05-30 04:16:54 +02:00
cn - > datatype = _type_from_variant ( cn - > value ) ;
2017-03-05 16:44:50 +01:00
container = cn ;
2017-01-12 00:09:45 +01:00
} else {
OperatorNode * on = alloc_node < OperatorNode > ( ) ;
2017-03-05 16:44:50 +01:00
on - > op = OperatorNode : : OP_CALL ;
2017-01-12 00:09:45 +01:00
TypeNode * tn = alloc_node < TypeNode > ( ) ;
on - > arguments . push_back ( tn ) ;
2017-03-05 16:44:50 +01:00
switch ( args . size ( ) ) {
2017-05-16 12:56:49 +02:00
case 1 : tn - > vtype = Variant : : INT ; break ;
2017-03-05 16:44:50 +01:00
case 2 : tn - > vtype = Variant : : VECTOR2 ; break ;
case 3 : tn - > vtype = Variant : : VECTOR3 ; break ;
2017-01-12 00:09:45 +01:00
}
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < args . size ( ) ; i + + ) {
2017-01-12 00:09:45 +01:00
on - > arguments . push_back ( args [ i ] ) ;
}
2017-03-05 16:44:50 +01:00
container = on ;
2017-01-12 00:09:45 +01:00
}
}
2018-05-30 04:16:54 +02:00
iter_type . has_type = true ;
iter_type . kind = DataType : : BUILTIN ;
iter_type . builtin_type = Variant : : INT ;
2017-01-12 00:09:45 +01:00
}
}
2014-02-10 02:10:30 +01:00
ControlFlowNode * cf_for = alloc_node < ControlFlowNode > ( ) ;
2017-03-05 16:44:50 +01:00
cf_for - > cf_type = ControlFlowNode : : CF_FOR ;
2014-02-10 02:10:30 +01:00
cf_for - > arguments . push_back ( id ) ;
cf_for - > arguments . push_back ( container ) ;
cf_for - > body = alloc_node < BlockNode > ( ) ;
2017-03-05 16:44:50 +01:00
cf_for - > body - > parent_block = p_block ;
2014-02-10 02:10:30 +01:00
p_block - > sub_blocks . push_back ( cf_for - > body ) ;
if ( ! _enter_indent_block ( cf_for - > body ) ) {
2016-02-24 00:08:53 +01:00
_set_error ( " Expected indented block after 'for' " ) ;
2017-03-05 16:44:50 +01:00
p_block - > end_line = tokenizer - > get_token_line ( ) ;
2014-02-10 02:10:30 +01:00
return ;
}
2017-03-05 16:44:50 +01:00
current_block = cf_for - > body ;
2016-05-30 17:22:34 +02:00
// this is for checking variable for redefining
// inside this _parse_block
2018-05-30 04:16:54 +02:00
LocalVarNode * lv = alloc_node < LocalVarNode > ( ) ;
lv - > name = id - > name ;
lv - > line = id - > line ;
lv - > assignments + + ;
id - > declared_block = cf_for - > body ;
lv - > set_datatype ( iter_type ) ;
id - > set_datatype ( iter_type ) ;
cf_for - > body - > variables . insert ( id - > name , lv ) ;
2017-03-05 16:44:50 +01:00
_parse_block ( cf_for - > body , p_static ) ;
current_block = p_block ;
2014-12-17 02:31:57 +01:00
2014-02-10 02:10:30 +01:00
if ( error_set )
return ;
2018-05-30 04:16:54 +02:00
p_block - > has_return = cf_for - > body - > has_return ;
2014-02-10 02:10:30 +01:00
p_block - > statements . push_back ( cf_for ) ;
} break ;
2017-11-16 18:38:18 +01:00
case GDScriptTokenizer : : TK_CF_CONTINUE : {
2014-02-10 02:10:30 +01:00
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
ControlFlowNode * cf_continue = alloc_node < ControlFlowNode > ( ) ;
2017-03-05 16:44:50 +01:00
cf_continue - > cf_type = ControlFlowNode : : CF_CONTINUE ;
2014-02-10 02:10:30 +01:00
p_block - > statements . push_back ( cf_continue ) ;
if ( ! _end_statement ( ) ) {
_set_error ( " Expected end of statement (continue) " ) ;
return ;
}
} break ;
2017-11-16 18:38:18 +01:00
case GDScriptTokenizer : : TK_CF_BREAK : {
2014-02-10 02:10:30 +01:00
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
ControlFlowNode * cf_break = alloc_node < ControlFlowNode > ( ) ;
2017-03-05 16:44:50 +01:00
cf_break - > cf_type = ControlFlowNode : : CF_BREAK ;
2014-02-10 02:10:30 +01:00
p_block - > statements . push_back ( cf_break ) ;
if ( ! _end_statement ( ) ) {
_set_error ( " Expected end of statement (break) " ) ;
return ;
}
} break ;
2017-11-16 18:38:18 +01:00
case GDScriptTokenizer : : TK_CF_RETURN : {
2014-02-10 02:10:30 +01:00
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
ControlFlowNode * cf_return = alloc_node < ControlFlowNode > ( ) ;
2017-03-05 16:44:50 +01:00
cf_return - > cf_type = ControlFlowNode : : CF_RETURN ;
2018-06-06 03:57:44 +02:00
cf_return - > line = tokenizer - > get_token_line ( - 1 ) ;
2014-02-10 02:10:30 +01:00
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_SEMICOLON | | tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_NEWLINE | | tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_EOF ) {
2014-02-10 02:10:30 +01:00
//expect end of statement
p_block - > statements . push_back ( cf_return ) ;
if ( ! _end_statement ( ) ) {
return ;
}
} else {
//expect expression
2017-03-05 16:44:50 +01:00
Node * retexpr = _parse_and_reduce_expression ( p_block , p_static ) ;
2015-08-30 16:50:10 +02:00
if ( ! retexpr ) {
if ( _recover_from_completion ( ) ) {
break ;
}
2014-02-10 02:10:30 +01:00
return ;
2015-08-30 16:50:10 +02:00
}
2014-02-10 02:10:30 +01:00
cf_return - > arguments . push_back ( retexpr ) ;
p_block - > statements . push_back ( cf_return ) ;
if ( ! _end_statement ( ) ) {
_set_error ( " Expected end of statement after return expression. " ) ;
return ;
}
}
2018-05-30 04:16:54 +02:00
p_block - > has_return = true ;
2014-02-10 02:10:30 +01:00
2016-09-30 21:40:31 +02:00
} break ;
2017-11-16 18:38:18 +01:00
case GDScriptTokenizer : : TK_CF_MATCH : {
2017-03-05 16:44:50 +01:00
2016-09-30 21:40:31 +02:00
tokenizer - > advance ( ) ;
2017-03-05 16:44:50 +01:00
2016-10-05 18:48:38 +02:00
MatchNode * match_node = alloc_node < MatchNode > ( ) ;
2017-03-05 16:44:50 +01:00
2016-09-30 21:40:31 +02:00
Node * val_to_match = _parse_and_reduce_expression ( p_block , p_static ) ;
2017-03-05 16:44:50 +01:00
2016-09-30 21:40:31 +02:00
if ( ! val_to_match ) {
if ( _recover_from_completion ( ) ) {
break ;
}
return ;
}
2017-03-05 16:44:50 +01:00
2016-10-05 18:48:38 +02:00
match_node - > val_to_match = val_to_match ;
2017-03-05 16:44:50 +01:00
2016-09-30 21:40:31 +02:00
if ( ! _enter_indent_block ( ) ) {
_set_error ( " Expected indented pattern matching block after 'match' " ) ;
return ;
}
2017-03-05 16:44:50 +01:00
2016-10-05 18:48:38 +02:00
BlockNode * compiled_branches = alloc_node < BlockNode > ( ) ;
compiled_branches - > parent_block = p_block ;
compiled_branches - > parent_class = p_block - > parent_class ;
2017-03-05 16:44:50 +01:00
2016-10-05 18:48:38 +02:00
p_block - > sub_blocks . push_back ( compiled_branches ) ;
2017-03-05 16:44:50 +01:00
2016-10-05 18:48:38 +02:00
_parse_pattern_block ( compiled_branches , match_node - > branches , p_static ) ;
2017-03-05 16:44:50 +01:00
2018-05-30 04:16:54 +02:00
if ( error_set ) return ;
2017-03-05 16:44:50 +01:00
2016-10-05 18:48:38 +02:00
ControlFlowNode * match_cf_node = alloc_node < ControlFlowNode > ( ) ;
match_cf_node - > cf_type = ControlFlowNode : : CF_MATCH ;
match_cf_node - > match = match_node ;
2018-05-30 04:16:54 +02:00
match_cf_node - > body = compiled_branches ;
2017-03-05 16:44:50 +01:00
2018-05-30 04:16:54 +02:00
p_block - > has_return = p_block - > has_return | | compiled_branches - > has_return ;
2016-10-05 18:48:38 +02:00
p_block - > statements . push_back ( match_cf_node ) ;
2017-03-05 16:44:50 +01:00
2016-10-05 18:48:38 +02:00
_end_statement ( ) ;
2014-02-10 02:10:30 +01:00
} break ;
2017-11-16 18:38:18 +01:00
case GDScriptTokenizer : : TK_PR_ASSERT : {
2014-02-10 02:10:30 +01:00
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2017-03-05 16:44:50 +01:00
Node * condition = _parse_and_reduce_expression ( p_block , p_static ) ;
2015-08-30 16:50:10 +02:00
if ( ! condition ) {
if ( _recover_from_completion ( ) ) {
break ;
}
2014-02-10 02:10:30 +01:00
return ;
2015-08-30 16:50:10 +02:00
}
2014-02-10 02:10:30 +01:00
AssertNode * an = alloc_node < AssertNode > ( ) ;
2017-03-05 16:44:50 +01:00
an - > condition = condition ;
2014-02-10 02:10:30 +01:00
p_block - > statements . push_back ( an ) ;
if ( ! _end_statement ( ) ) {
_set_error ( " Expected end of statement after assert. " ) ;
return ;
}
} break ;
2017-11-16 18:38:18 +01:00
case GDScriptTokenizer : : TK_PR_BREAKPOINT : {
2015-12-29 16:11:21 +01:00
tokenizer - > advance ( ) ;
BreakpointNode * bn = alloc_node < BreakpointNode > ( ) ;
p_block - > statements . push_back ( bn ) ;
if ( ! _end_statement ( ) ) {
_set_error ( " Expected end of statement after breakpoint. " ) ;
return ;
}
} break ;
2014-02-10 02:10:30 +01:00
default : {
2017-03-05 16:44:50 +01:00
Node * expression = _parse_and_reduce_expression ( p_block , p_static , false , true ) ;
2015-08-30 16:50:10 +02:00
if ( ! expression ) {
if ( _recover_from_completion ( ) ) {
break ;
}
2014-02-10 02:10:30 +01:00
return ;
2015-08-30 16:50:10 +02:00
}
2014-02-10 02:10:30 +01:00
p_block - > statements . push_back ( expression ) ;
if ( ! _end_statement ( ) ) {
_set_error ( " Expected end of statement after expression. " ) ;
return ;
}
} break ;
}
}
}
2017-11-16 18:38:18 +01:00
bool GDScriptParser : : _parse_newline ( ) {
2014-02-10 02:10:30 +01:00
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( 1 ) ! = GDScriptTokenizer : : TK_EOF & & tokenizer - > get_token ( 1 ) ! = GDScriptTokenizer : : TK_NEWLINE ) {
2014-02-10 02:10:30 +01:00
2014-02-25 13:31:47 +01:00
int indent = tokenizer - > get_token_line_indent ( ) ;
2014-02-10 02:10:30 +01:00
int current_indent = tab_level . back ( ) - > get ( ) ;
2017-03-05 16:44:50 +01:00
if ( indent > current_indent ) {
2014-02-10 02:10:30 +01:00
_set_error ( " Unexpected indent. " ) ;
return false ;
}
2017-03-05 16:44:50 +01:00
if ( indent < current_indent ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
while ( indent < current_indent ) {
2014-02-10 02:10:30 +01:00
//exit block
2017-03-05 16:44:50 +01:00
if ( tab_level . size ( ) = = 1 ) {
2014-02-10 02:10:30 +01:00
_set_error ( " Invalid indent. BUG? " ) ;
return false ;
}
tab_level . pop_back ( ) ;
2017-03-05 16:44:50 +01:00
if ( tab_level . back ( ) - > get ( ) < indent ) {
2014-02-10 02:10:30 +01:00
_set_error ( " Unindent does not match any outer indentation level. " ) ;
return false ;
}
current_indent = tab_level . back ( ) - > get ( ) ;
}
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
return false ;
}
}
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
return true ;
}
2017-11-16 18:38:18 +01:00
void GDScriptParser : : _parse_extends ( ClassNode * p_class ) {
2014-02-10 02:10:30 +01:00
if ( p_class - > extends_used ) {
_set_error ( " 'extends' already used for this class. " ) ;
return ;
}
if ( ! p_class - > constant_expressions . empty ( ) | | ! p_class - > subclasses . empty ( ) | | ! p_class - > functions . empty ( ) | | ! p_class - > variables . empty ( ) ) {
_set_error ( " 'extends' must be used before anything else. " ) ;
return ;
}
2017-03-05 16:44:50 +01:00
p_class - > extends_used = true ;
2014-02-10 02:10:30 +01:00
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_BUILT_IN_TYPE & & tokenizer - > get_token_type ( ) = = Variant : : OBJECT ) {
2015-11-12 15:50:20 +01:00
p_class - > extends_class . push_back ( Variant : : get_type_name ( Variant : : OBJECT ) ) ;
tokenizer - > advance ( ) ;
return ;
}
// see if inheritance happens from a file
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CONSTANT ) {
2014-02-10 02:10:30 +01:00
2014-02-25 13:31:47 +01:00
Variant constant = tokenizer - > get_token_constant ( ) ;
2017-03-05 16:44:50 +01:00
if ( constant . get_type ( ) ! = Variant : : STRING ) {
2014-02-10 02:10:30 +01:00
_set_error ( " 'extends' constant must be a string. " ) ;
return ;
}
2017-03-05 16:44:50 +01:00
p_class - > extends_file = constant ;
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PERIOD ) {
2014-02-10 02:10:30 +01:00
return ;
} else
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
while ( true ) {
2014-02-10 02:10:30 +01:00
2017-10-30 12:59:11 +01:00
switch ( tokenizer - > get_token ( ) ) {
case GDScriptTokenizer : : TK_IDENTIFIER : {
2017-12-06 19:54:22 +01:00
StringName identifier = tokenizer - > get_token_identifier ( ) ;
p_class - > extends_class . push_back ( identifier ) ;
} break ;
2017-10-30 12:59:11 +01:00
case GDScriptTokenizer : : TK_PERIOD :
break ;
default : {
2014-02-10 02:10:30 +01:00
2017-10-30 12:59:11 +01:00
_set_error ( " Invalid 'extends' syntax, expected string constant (path) and/or identifier (parent class). " ) ;
return ;
}
}
2014-02-10 02:10:30 +01:00
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( 1 ) ;
2017-10-30 12:59:11 +01:00
switch ( tokenizer - > get_token ( ) ) {
case GDScriptTokenizer : : TK_IDENTIFIER :
case GDScriptTokenizer : : TK_PERIOD :
continue ;
default :
return ;
}
2014-02-10 02:10:30 +01:00
}
}
2017-11-16 18:38:18 +01:00
void GDScriptParser : : _parse_class ( ClassNode * p_class ) {
2014-02-10 02:10:30 +01:00
int indent_level = tab_level . back ( ) - > get ( ) ;
2017-03-05 16:44:50 +01:00
while ( true ) {
2014-02-10 02:10:30 +01:00
2017-11-16 18:38:18 +01:00
GDScriptTokenizer : : Token token = tokenizer - > get_token ( ) ;
2014-02-10 02:10:30 +01:00
if ( error_set )
return ;
2017-03-05 16:44:50 +01:00
if ( indent_level > tab_level . back ( ) - > get ( ) ) {
p_class - > end_line = tokenizer - > get_token_line ( ) ;
2014-02-10 02:10:30 +01:00
return ; //go back a level
}
2017-03-05 16:44:50 +01:00
switch ( token ) {
2014-02-10 02:10:30 +01:00
2018-07-25 18:39:05 +02:00
case GDScriptTokenizer : : TK_CURSOR : {
tokenizer - > advance ( ) ;
} break ;
2017-11-16 18:38:18 +01:00
case GDScriptTokenizer : : TK_EOF :
2017-03-05 16:44:50 +01:00
p_class - > end_line = tokenizer - > get_token_line ( ) ;
2017-11-16 18:38:18 +01:00
case GDScriptTokenizer : : TK_ERROR : {
2014-02-10 02:10:30 +01:00
return ; //go back
//end of file!
} break ;
2017-11-16 18:38:18 +01:00
case GDScriptTokenizer : : TK_NEWLINE : {
2014-02-10 02:10:30 +01:00
if ( ! _parse_newline ( ) ) {
if ( ! error_set ) {
2017-03-05 16:44:50 +01:00
p_class - > end_line = tokenizer - > get_token_line ( ) ;
2014-02-10 02:10:30 +01:00
}
return ;
}
} break ;
2017-11-16 18:38:18 +01:00
case GDScriptTokenizer : : TK_PR_EXTENDS : {
2014-02-10 02:10:30 +01:00
2018-06-05 18:50:21 +02:00
_mark_line_as_safe ( tokenizer - > get_token_line ( ) ) ;
2014-02-10 02:10:30 +01:00
_parse_extends ( p_class ) ;
if ( error_set )
return ;
2015-06-24 18:29:23 +02:00
if ( ! _end_statement ( ) ) {
_set_error ( " Expected end of statement after extends " ) ;
return ;
}
2014-02-10 02:10:30 +01:00
2018-07-16 00:29:00 +02:00
} break ;
case GDScriptTokenizer : : TK_PR_CLASS_NAME : {
if ( p_class - > owner ) {
_set_error ( " 'class_name' is only valid for the main class namespace. " ) ;
return ;
}
if ( tokenizer - > get_token ( 1 ) ! = GDScriptTokenizer : : TK_IDENTIFIER ) {
_set_error ( " 'class_name' syntax: 'class_name <UniqueName>' " ) ;
return ;
}
p_class - > name = tokenizer - > get_token_identifier ( 1 ) ;
if ( self_path ! = String ( ) & & ScriptServer : : is_global_class ( p_class - > name ) & & ScriptServer : : get_global_class_path ( p_class - > name ) ! = self_path ) {
_set_error ( " Unique global class ' " + p_class - > name + " ' already exists at path: " + ScriptServer : : get_global_class_path ( p_class - > name ) ) ;
return ;
}
2018-05-30 04:16:54 +02:00
if ( ClassDB : : class_exists ( p_class - > name ) ) {
_set_error ( " Class ' " + p_class - > name + " ' shadows a native class. " ) ;
return ;
}
2018-07-16 00:29:00 +02:00
tokenizer - > advance ( 2 ) ;
2018-07-29 05:36:43 +02:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COMMA ) {
tokenizer - > advance ( ) ;
if ( ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CONSTANT & & tokenizer - > get_token_constant ( ) . get_type ( ) = = Variant : : STRING ) ) {
Variant constant = tokenizer - > get_token_constant ( ) ;
String icon_path = constant . operator String ( ) ;
String abs_icon_path = icon_path . is_rel_path ( ) ? self_path . get_base_dir ( ) . plus_file ( icon_path ) . simplify_path ( ) : icon_path ;
if ( ! FileAccess : : exists ( abs_icon_path ) ) {
_set_error ( " No class icon found at: " + abs_icon_path ) ;
return ;
}
p_class - > icon_path = icon_path ;
tokenizer - > advance ( ) ;
} else {
_set_error ( " Optional parameter after 'class_name' must be a string constant file path to an icon. " ) ;
return ;
}
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CONSTANT ) {
_set_error ( " Class icon must be separated by a comma. " ) ;
return ;
}
2014-02-10 02:10:30 +01:00
} break ;
2017-11-16 18:38:18 +01:00
case GDScriptTokenizer : : TK_PR_TOOL : {
2014-02-10 02:10:30 +01:00
if ( p_class - > tool ) {
_set_error ( " tool used more than once " ) ;
return ;
}
2017-03-05 16:44:50 +01:00
p_class - > tool = true ;
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
} break ;
2017-11-16 18:38:18 +01:00
case GDScriptTokenizer : : TK_PR_CLASS : {
2014-02-10 02:10:30 +01:00
//class inside class :D
StringName name ;
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( 1 ) ! = GDScriptTokenizer : : TK_IDENTIFIER ) {
2014-02-10 02:10:30 +01:00
_set_error ( " 'class' syntax: 'class <Name>:' or 'class <Name> extends <BaseClass>:' " ) ;
return ;
}
2014-02-25 13:31:47 +01:00
name = tokenizer - > get_token_identifier ( 1 ) ;
tokenizer - > advance ( 2 ) ;
2014-02-10 02:10:30 +01:00
2018-05-30 04:16:52 +02:00
// Check if name is shadowing something else
2018-05-30 04:16:54 +02:00
if ( ClassDB : : class_exists ( name ) | | ClassDB : : class_exists ( " _ " + name . operator String ( ) ) ) {
2018-05-30 04:16:52 +02:00
_set_error ( " Class ' " + String ( name ) + " ' shadows a native class. " ) ;
return ;
}
2018-07-16 00:29:00 +02:00
if ( ScriptServer : : is_global_class ( name ) ) {
_set_error ( " Can't override name of unique global class ' " + name + " ' already exists at path: " + ScriptServer : : get_global_class_path ( p_class - > name ) ) ;
return ;
}
2018-05-30 04:16:54 +02:00
ClassNode * outer_class = p_class ;
while ( outer_class ) {
for ( int i = 0 ; i < outer_class - > subclasses . size ( ) ; i + + ) {
if ( outer_class - > subclasses [ i ] - > name = = name ) {
_set_error ( " Another class named ' " + String ( name ) + " ' already exists in this scope (at line " + itos ( outer_class - > subclasses [ i ] - > line ) + " ). " ) ;
return ;
}
}
if ( outer_class - > constant_expressions . has ( name ) ) {
_set_error ( " A constant named ' " + String ( name ) + " ' already exists in the outer class scope (at line " + itos ( outer_class - > constant_expressions [ name ] . expression - > line ) + " ). " ) ;
2018-05-30 04:16:52 +02:00
return ;
}
2018-05-30 04:16:54 +02:00
outer_class = outer_class - > owner ;
2018-05-30 04:16:52 +02:00
}
2018-07-16 00:29:00 +02:00
2014-02-10 02:10:30 +01:00
ClassNode * newclass = alloc_node < ClassNode > ( ) ;
newclass - > initializer = alloc_node < BlockNode > ( ) ;
2017-03-05 16:44:50 +01:00
newclass - > initializer - > parent_class = newclass ;
2015-12-30 01:23:26 +01:00
newclass - > ready = alloc_node < BlockNode > ( ) ;
2017-03-05 16:44:50 +01:00
newclass - > ready - > parent_class = newclass ;
newclass - > name = name ;
newclass - > owner = p_class ;
2014-02-10 02:10:30 +01:00
p_class - > subclasses . push_back ( newclass ) ;
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PR_EXTENDS ) {
2014-02-10 02:10:30 +01:00
_parse_extends ( newclass ) ;
if ( error_set )
return ;
}
if ( ! _enter_indent_block ( ) ) {
_set_error ( " Indented block expected. " ) ;
return ;
}
2017-03-05 16:44:50 +01:00
current_class = newclass ;
2014-02-10 02:10:30 +01:00
_parse_class ( newclass ) ;
2017-03-05 16:44:50 +01:00
current_class = p_class ;
2014-02-10 02:10:30 +01:00
} break ;
/* this is for functions....
2017-11-16 18:38:18 +01:00
case GDScriptTokenizer : : TK_CF_PASS : {
2014-02-10 02:10:30 +01:00
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( 1 ) ;
2014-02-10 02:10:30 +01:00
} break ;
*/
2017-11-16 18:38:18 +01:00
case GDScriptTokenizer : : TK_PR_STATIC : {
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PR_FUNCTION ) {
2014-02-10 02:10:30 +01:00
_set_error ( " Expected 'func'. " ) ;
return ;
}
} ; //fallthrough to function
2017-11-16 18:38:18 +01:00
case GDScriptTokenizer : : TK_PR_FUNCTION : {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
bool _static = false ;
pending_newline = - 1 ;
2014-02-10 02:10:30 +01:00
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( - 1 ) = = GDScriptTokenizer : : TK_PR_STATIC ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
_static = true ;
2014-02-10 02:10:30 +01:00
}
2015-01-03 17:03:13 +01:00
tokenizer - > advance ( ) ;
StringName name ;
2017-03-05 16:44:50 +01:00
if ( _get_completable_identifier ( COMPLETION_VIRTUAL_FUNC , name ) ) {
2015-01-03 17:03:13 +01:00
}
2017-03-05 16:44:50 +01:00
if ( name = = StringName ( ) ) {
2014-02-10 02:10:30 +01:00
_set_error ( " Expected identifier after 'func' (syntax: 'func <identifier>([arguments]):' ). " ) ;
return ;
}
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < p_class - > functions . size ( ) ; i + + ) {
if ( p_class - > functions [ i ] - > name = = name ) {
_set_error ( " Function ' " + String ( name ) + " ' already exists in this class (at line: " + itos ( p_class - > functions [ i ] - > line ) + " ). " ) ;
2014-02-10 02:10:30 +01:00
}
}
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < p_class - > static_functions . size ( ) ; i + + ) {
if ( p_class - > static_functions [ i ] - > name = = name ) {
_set_error ( " Function ' " + String ( name ) + " ' already exists in this class (at line: " + itos ( p_class - > static_functions [ i ] - > line ) + " ). " ) ;
2014-02-10 02:10:30 +01:00
}
}
2015-01-03 17:03:13 +01:00
2018-07-01 18:17:40 +02:00
# ifdef DEBUG_ENABLED
if ( p_class - > constant_expressions . has ( name ) ) {
_add_warning ( GDScriptWarning : : FUNCTION_CONFLICTS_CONSTANT , - 1 , name ) ;
}
for ( int i = 0 ; i < p_class - > variables . size ( ) ; i + + ) {
if ( p_class - > variables [ i ] . identifier = = name ) {
_add_warning ( GDScriptWarning : : FUNCTION_CONFLICTS_VARIABLE , - 1 , name ) ;
}
}
# endif // DEBUG_ENABLED
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PARENTHESIS_OPEN ) {
2014-02-10 02:10:30 +01:00
_set_error ( " Expected '(' after identifier (syntax: 'func <identifier>([arguments]):' ). " ) ;
return ;
}
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
Vector < StringName > arguments ;
2018-05-30 04:16:53 +02:00
Vector < DataType > argument_types ;
2017-03-05 16:44:50 +01:00
Vector < Node * > default_values ;
2018-07-01 18:17:40 +02:00
# ifdef DEBUG_ENABLED
Vector < int > arguments_usage ;
# endif // DEBUG_ENABLED
2014-02-10 02:10:30 +01:00
2014-02-25 13:31:47 +01:00
int fnline = tokenizer - > get_token_line ( ) ;
2014-02-10 02:10:30 +01:00
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE ) {
2014-02-10 02:10:30 +01:00
//has arguments
2017-03-05 16:44:50 +01:00
bool defaulting = false ;
while ( true ) {
2014-02-10 02:10:30 +01:00
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_NEWLINE ) {
2016-10-03 20:18:21 +02:00
tokenizer - > advance ( ) ;
continue ;
}
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PR_VAR ) {
2014-02-10 02:10:30 +01:00
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ; //var before the identifier is allowed
2014-02-10 02:10:30 +01:00
}
2017-03-31 19:28:34 +02:00
if ( ! tokenizer - > is_token_literal ( 0 , true ) ) {
2014-02-10 02:10:30 +01:00
_set_error ( " Expected identifier for argument. " ) ;
return ;
}
2017-03-05 16:44:50 +01:00
StringName argname = tokenizer - > get_token_identifier ( ) ;
2014-02-10 02:10:30 +01:00
arguments . push_back ( argname ) ;
2018-07-01 18:17:40 +02:00
# ifdef DEBUG_ENABLED
arguments_usage . push_back ( 0 ) ;
# endif // DEBUG_ENABLED
2014-02-10 02:10:30 +01:00
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
2018-05-30 04:16:53 +02:00
DataType argtype ;
2018-05-30 04:16:51 +02:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COLON ) {
2018-07-26 01:48:22 +02:00
if ( tokenizer - > get_token ( 1 ) = = GDScriptTokenizer : : TK_OP_ASSIGN ) {
argtype . infer_type = true ;
tokenizer - > advance ( ) ;
} else if ( ! _parse_type ( argtype ) ) {
2018-05-30 04:16:51 +02:00
_set_error ( " Expected type for argument. " ) ;
return ;
}
}
2018-05-30 04:16:53 +02:00
argument_types . push_back ( argtype ) ;
2018-05-30 04:16:51 +02:00
2017-11-16 18:38:18 +01:00
if ( defaulting & & tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_OP_ASSIGN ) {
2014-02-10 02:10:30 +01:00
_set_error ( " Default parameter expected. " ) ;
return ;
}
2014-02-25 13:31:47 +01:00
//tokenizer->advance();
2014-02-10 02:10:30 +01:00
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_OP_ASSIGN ) {
2017-03-05 16:44:50 +01:00
defaulting = true ;
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( 1 ) ;
2017-08-21 21:15:36 +02:00
Node * defval = _parse_and_reduce_expression ( p_class , _static ) ;
2014-02-10 02:10:30 +01:00
if ( ! defval | | error_set )
return ;
OperatorNode * on = alloc_node < OperatorNode > ( ) ;
2017-03-05 16:44:50 +01:00
on - > op = OperatorNode : : OP_ASSIGN ;
2018-06-06 03:57:44 +02:00
on - > line = fnline ;
2014-02-10 02:10:30 +01:00
IdentifierNode * in = alloc_node < IdentifierNode > ( ) ;
2017-03-05 16:44:50 +01:00
in - > name = argname ;
2018-06-06 03:57:44 +02:00
in - > line = fnline ;
2014-02-10 02:10:30 +01:00
on - > arguments . push_back ( in ) ;
on - > arguments . push_back ( defval ) ;
/* no ..
if ( defval - > type ! = Node : : TYPE_CONSTANT ) {
_set_error ( " default argument must be constant " ) ;
}
*/
default_values . push_back ( on ) ;
}
2017-11-16 18:38:18 +01:00
while ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_NEWLINE ) {
2016-10-03 20:18:21 +02:00
tokenizer - > advance ( ) ;
}
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COMMA ) {
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
continue ;
2017-11-16 18:38:18 +01:00
} else if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE ) {
2014-02-10 02:10:30 +01:00
_set_error ( " Expected ',' or ')'. " ) ;
return ;
}
break ;
}
}
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
BlockNode * block = alloc_node < BlockNode > ( ) ;
2017-03-05 16:44:50 +01:00
block - > parent_class = p_class ;
2014-02-10 02:10:30 +01:00
2018-11-16 10:46:05 +01:00
FunctionNode * function = alloc_node < FunctionNode > ( ) ;
function - > name = name ;
function - > arguments = arguments ;
function - > argument_types = argument_types ;
function - > default_values = default_values ;
function - > _static = _static ;
function - > line = fnline ;
# ifdef DEBUG_ENABLED
function - > arguments_usage = arguments_usage ;
# endif // DEBUG_ENABLED
function - > rpc_mode = rpc_mode ;
rpc_mode = MultiplayerAPI : : RPC_MODE_DISABLED ;
2017-03-05 16:44:50 +01:00
if ( name = = " _init " ) {
2014-02-10 02:10:30 +01:00
2018-05-30 04:16:54 +02:00
if ( _static ) {
_set_error ( " Constructor cannot be static. " ) ;
return ;
}
2014-02-10 02:10:30 +01:00
if ( p_class - > extends_used ) {
OperatorNode * cparent = alloc_node < OperatorNode > ( ) ;
2017-03-05 16:44:50 +01:00
cparent - > op = OperatorNode : : OP_PARENT_CALL ;
2014-02-10 02:10:30 +01:00
block - > statements . push_back ( cparent ) ;
IdentifierNode * id = alloc_node < IdentifierNode > ( ) ;
2017-03-05 16:44:50 +01:00
id - > name = " _init " ;
2014-02-10 02:10:30 +01:00
cparent - > arguments . push_back ( id ) ;
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PERIOD ) {
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PARENTHESIS_OPEN ) {
2014-02-10 02:10:30 +01:00
_set_error ( " expected '(' for parent constructor arguments. " ) ;
2018-05-30 04:16:54 +02:00
return ;
2014-02-10 02:10:30 +01:00
}
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE ) {
2014-02-10 02:10:30 +01:00
//has arguments
2017-03-05 16:44:50 +01:00
parenthesis + + ;
while ( true ) {
2014-02-10 02:10:30 +01:00
2018-11-16 10:46:05 +01:00
current_function = function ;
2017-03-05 16:44:50 +01:00
Node * arg = _parse_and_reduce_expression ( p_class , _static ) ;
2018-11-16 10:46:05 +01:00
current_function = NULL ;
2014-02-10 02:10:30 +01:00
cparent - > arguments . push_back ( arg ) ;
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COMMA ) {
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
continue ;
2017-11-16 18:38:18 +01:00
} else if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE ) {
2014-02-10 02:10:30 +01:00
_set_error ( " Expected ',' or ')'. " ) ;
return ;
}
break ;
}
2017-03-05 16:44:50 +01:00
parenthesis - - ;
2014-02-10 02:10:30 +01:00
}
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
}
} else {
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PERIOD ) {
2014-02-10 02:10:30 +01:00
_set_error ( " Parent constructor call found for a class without inheritance. " ) ;
return ;
}
}
}
2018-05-30 04:16:53 +02:00
DataType return_type ;
2018-05-30 04:16:51 +02:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_FORWARD_ARROW ) {
2018-05-30 04:16:53 +02:00
if ( ! _parse_type ( return_type , true ) ) {
2018-05-30 04:16:51 +02:00
_set_error ( " Expected return type for function. " ) ;
return ;
}
}
2014-02-10 02:10:30 +01:00
if ( ! _enter_indent_block ( block ) ) {
_set_error ( " Indented block expected. " ) ;
return ;
}
2018-05-30 04:16:53 +02:00
function - > return_type = return_type ;
2014-02-10 02:10:30 +01:00
if ( _static )
p_class - > static_functions . push_back ( function ) ;
else
p_class - > functions . push_back ( function ) ;
2017-03-05 16:44:50 +01:00
current_function = function ;
function - > body = block ;
current_block = block ;
_parse_block ( block , _static ) ;
current_block = NULL ;
2014-12-17 02:31:57 +01:00
2014-02-10 02:10:30 +01:00
//arguments
} break ;
2017-11-16 18:38:18 +01:00
case GDScriptTokenizer : : TK_PR_SIGNAL : {
2015-06-24 18:29:23 +02:00
tokenizer - > advance ( ) ;
2017-03-31 19:28:34 +02:00
if ( ! tokenizer - > is_token_literal ( ) ) {
2015-06-24 18:29:23 +02:00
_set_error ( " Expected identifier after 'signal'. " ) ;
return ;
}
ClassNode : : Signal sig ;
sig . name = tokenizer - > get_token_identifier ( ) ;
2018-07-01 18:17:40 +02:00
sig . emissions = 0 ;
sig . line = tokenizer - > get_token_line ( ) ;
2015-06-24 18:29:23 +02:00
tokenizer - > advance ( ) ;
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PARENTHESIS_OPEN ) {
2015-06-24 18:29:23 +02:00
tokenizer - > advance ( ) ;
2017-03-05 16:44:50 +01:00
while ( true ) {
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_NEWLINE ) {
2016-10-03 20:18:21 +02:00
tokenizer - > advance ( ) ;
continue ;
}
2015-06-24 18:29:23 +02:00
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE ) {
2015-06-24 18:29:23 +02:00
tokenizer - > advance ( ) ;
break ;
}
2017-07-25 20:01:19 +02:00
if ( ! tokenizer - > is_token_literal ( 0 , true ) ) {
2015-06-24 18:29:23 +02:00
_set_error ( " Expected identifier in signal argument. " ) ;
return ;
}
sig . arguments . push_back ( tokenizer - > get_token_identifier ( ) ) ;
tokenizer - > advance ( ) ;
2017-11-16 18:38:18 +01:00
while ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_NEWLINE ) {
2016-10-03 20:18:21 +02:00
tokenizer - > advance ( ) ;
}
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COMMA ) {
2015-06-24 18:29:23 +02:00
tokenizer - > advance ( ) ;
2017-11-16 18:38:18 +01:00
} else if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE ) {
2015-06-24 18:29:23 +02:00
_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 ;
2017-11-16 18:38:18 +01:00
case GDScriptTokenizer : : TK_PR_EXPORT : {
2014-02-10 02:10:30 +01:00
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PARENTHESIS_OPEN ) {
2014-02-10 02:10:30 +01:00
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2018-02-05 15:41:13 +01:00
String hint_prefix = " " ;
bool is_arrayed = false ;
while ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_BUILT_IN_TYPE & &
tokenizer - > get_token_type ( ) = = Variant : : ARRAY & &
tokenizer - > get_token ( 1 ) = = GDScriptTokenizer : : TK_COMMA ) {
tokenizer - > advance ( ) ; // Array
tokenizer - > advance ( ) ; // Comma
if ( is_arrayed ) {
hint_prefix + = itos ( Variant : : ARRAY ) + " : " ;
} else {
is_arrayed = true ;
}
}
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_BUILT_IN_TYPE ) {
2014-02-10 02:10:30 +01:00
2014-02-25 13:31:47 +01:00
Variant : : Type type = tokenizer - > get_token_type ( ) ;
2017-03-05 16:44:50 +01:00
if ( type = = Variant : : NIL ) {
2014-02-10 02:10:30 +01:00
_set_error ( " Can't export null type. " ) ;
return ;
}
2017-12-09 18:33:23 +01:00
if ( type = = Variant : : OBJECT ) {
_set_error ( " Can't export raw object type. " ) ;
return ;
}
2017-03-05 16:44:50 +01:00
current_export . type = type ;
current_export . usage | = PROPERTY_USAGE_SCRIPT_VARIABLE ;
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2017-03-05 16:44:50 +01:00
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COMMA ) {
2014-02-10 02:10:30 +01:00
// hint expected next!
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2016-10-26 13:38:41 +02:00
2017-03-05 16:44:50 +01:00
switch ( type ) {
2014-02-10 02:10:30 +01:00
case Variant : : INT : {
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_IDENTIFIER & & tokenizer - > get_token_identifier ( ) = = " FLAGS " ) {
2015-07-20 20:02:46 +02:00
tokenizer - > advance ( ) ;
2015-12-11 15:15:57 +01:00
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE ) {
2018-08-24 08:54:52 +02:00
ERR_EXPLAIN ( " Exporting bit flags hint requires string constants. " ) ;
WARN_DEPRECATED
2015-12-11 15:15:57 +01:00
break ;
}
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_COMMA ) {
2018-08-24 08:54:52 +02:00
_set_error ( " Expected ',' in bit flags hint. " ) ;
2015-07-20 20:02:46 +02:00
return ;
}
2015-12-11 15:15:57 +01:00
2017-03-05 16:44:50 +01:00
current_export . hint = PROPERTY_HINT_FLAGS ;
2015-12-11 15:15:57 +01:00
tokenizer - > advance ( ) ;
bool first = true ;
2017-03-05 16:44:50 +01:00
while ( true ) {
2015-12-11 15:15:57 +01:00
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_CONSTANT | | tokenizer - > get_token_constant ( ) . get_type ( ) ! = Variant : : STRING ) {
2017-03-05 16:44:50 +01:00
current_export = PropertyInfo ( ) ;
2015-12-11 15:15:57 +01:00
_set_error ( " Expected a string constant in named bit flags hint. " ) ;
return ;
}
String c = tokenizer - > get_token_constant ( ) ;
if ( ! first )
2017-03-05 16:44:50 +01:00
current_export . hint_string + = " , " ;
2015-12-11 15:15:57 +01:00
else
2017-03-05 16:44:50 +01:00
first = false ;
2015-12-11 15:15:57 +01:00
2017-03-05 16:44:50 +01:00
current_export . hint_string + = c . xml_escape ( ) ;
2015-12-11 15:15:57 +01:00
tokenizer - > advance ( ) ;
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE )
2015-12-11 15:15:57 +01:00
break ;
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_COMMA ) {
2017-03-05 16:44:50 +01:00
current_export = PropertyInfo ( ) ;
2015-12-11 15:15:57 +01:00
_set_error ( " Expected ')' or ',' in named bit flags hint. " ) ;
return ;
}
tokenizer - > advance ( ) ;
}
2015-07-20 20:02:46 +02:00
break ;
}
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CONSTANT & & tokenizer - > get_token_constant ( ) . get_type ( ) = = Variant : : STRING ) {
2014-02-10 02:10:30 +01:00
//enumeration
2017-03-05 16:44:50 +01:00
current_export . hint = PROPERTY_HINT_ENUM ;
bool first = true ;
while ( true ) {
2014-02-10 02:10:30 +01:00
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_CONSTANT | | tokenizer - > get_token_constant ( ) . get_type ( ) ! = Variant : : STRING ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
current_export = PropertyInfo ( ) ;
2014-02-10 02:10:30 +01:00
_set_error ( " Expected a string constant in enumeration hint. " ) ;
2014-09-19 23:39:50 +02:00
return ;
2014-02-10 02:10:30 +01:00
}
2014-02-25 13:31:47 +01:00
String c = tokenizer - > get_token_constant ( ) ;
2014-02-10 02:10:30 +01:00
if ( ! first )
2017-03-05 16:44:50 +01:00
current_export . hint_string + = " , " ;
2014-02-10 02:10:30 +01:00
else
2017-03-05 16:44:50 +01:00
first = false ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
current_export . hint_string + = c . xml_escape ( ) ;
2014-02-10 02:10:30 +01:00
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE )
2014-02-10 02:10:30 +01:00
break ;
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_COMMA ) {
2017-03-05 16:44:50 +01:00
current_export = PropertyInfo ( ) ;
2014-02-10 02:10:30 +01:00
_set_error ( " Expected ')' or ',' in enumeration hint. " ) ;
2014-09-19 23:39:50 +02:00
return ;
2014-02-10 02:10:30 +01:00
}
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
}
break ;
}
2014-09-21 06:43:42 +02:00
} ; //fallthrough to use the same
2014-02-10 02:10:30 +01:00
case Variant : : REAL : {
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_IDENTIFIER & & tokenizer - > get_token_identifier ( ) = = " EASE " ) {
2017-03-05 16:44:50 +01:00
current_export . hint = PROPERTY_HINT_EXP_EASING ;
2015-10-16 20:37:13 +02:00
tokenizer - > advance ( ) ;
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE ) {
2015-10-16 20:37:13 +02:00
_set_error ( " Expected ')' in hint. " ) ;
return ;
}
break ;
}
Add GDScript export hints for global filesystem and exponential ranges
- File in global filesystem: `String, FILE, GLOBAL, "*.png"`, tool scripts only
- Directory in global filesystem: `String, DIR, GLOBAL`, tool scripts only
- Exponential range: `float, EXP, 50, 150, 2`
2015-12-11 06:45:03 +01:00
// range
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_IDENTIFIER & & tokenizer - > get_token_identifier ( ) = = " EXP " ) {
Add GDScript export hints for global filesystem and exponential ranges
- File in global filesystem: `String, FILE, GLOBAL, "*.png"`, tool scripts only
- Directory in global filesystem: `String, DIR, GLOBAL`, tool scripts only
- Exponential range: `float, EXP, 50, 150, 2`
2015-12-11 06:45:03 +01:00
2017-03-05 16:44:50 +01:00
current_export . hint = PROPERTY_HINT_EXP_RANGE ;
Add GDScript export hints for global filesystem and exponential ranges
- File in global filesystem: `String, FILE, GLOBAL, "*.png"`, tool scripts only
- Directory in global filesystem: `String, DIR, GLOBAL`, tool scripts only
- Exponential range: `float, EXP, 50, 150, 2`
2015-12-11 06:45:03 +01:00
tokenizer - > advance ( ) ;
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE )
Add GDScript export hints for global filesystem and exponential ranges
- File in global filesystem: `String, FILE, GLOBAL, "*.png"`, tool scripts only
- Directory in global filesystem: `String, DIR, GLOBAL`, tool scripts only
- Exponential range: `float, EXP, 50, 150, 2`
2015-12-11 06:45:03 +01:00
break ;
2017-11-16 18:38:18 +01:00
else if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_COMMA ) {
Add GDScript export hints for global filesystem and exponential ranges
- File in global filesystem: `String, FILE, GLOBAL, "*.png"`, tool scripts only
- Directory in global filesystem: `String, DIR, GLOBAL`, tool scripts only
- Exponential range: `float, EXP, 50, 150, 2`
2015-12-11 06:45:03 +01:00
_set_error ( " Expected ')' or ',' in exponential range hint. " ) ;
return ;
}
tokenizer - > advance ( ) ;
2017-03-05 16:44:50 +01:00
} else
current_export . hint = PROPERTY_HINT_RANGE ;
Add GDScript export hints for global filesystem and exponential ranges
- File in global filesystem: `String, FILE, GLOBAL, "*.png"`, tool scripts only
- Directory in global filesystem: `String, DIR, GLOBAL`, tool scripts only
- Exponential range: `float, EXP, 50, 150, 2`
2015-12-11 06:45:03 +01:00
2017-03-05 16:44:50 +01:00
float sign = 1.0 ;
2014-09-21 06:43:42 +02:00
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_OP_SUB ) {
2017-03-05 16:44:50 +01:00
sign = - 1 ;
2014-09-21 06:43:42 +02:00
tokenizer - > advance ( ) ;
}
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_CONSTANT | | ! tokenizer - > get_token_constant ( ) . is_num ( ) ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
current_export = PropertyInfo ( ) ;
2014-02-10 02:10:30 +01:00
_set_error ( " Expected a range in numeric hint. " ) ;
2014-09-19 23:39:50 +02:00
return ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
current_export . hint_string = rtos ( sign * double ( tokenizer - > get_token_constant ( ) ) ) ;
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE ) {
2017-03-05 16:44:50 +01:00
current_export . hint_string = " 0, " + current_export . hint_string ;
2014-02-10 02:10:30 +01:00
break ;
}
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_COMMA ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
current_export = PropertyInfo ( ) ;
2014-02-10 02:10:30 +01:00
_set_error ( " Expected ',' or ')' in numeric range hint. " ) ;
2014-09-19 23:39:50 +02:00
return ;
2014-02-10 02:10:30 +01:00
}
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
sign = 1.0 ;
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_OP_SUB ) {
2017-03-05 16:44:50 +01:00
sign = - 1 ;
2014-09-21 06:43:42 +02:00
tokenizer - > advance ( ) ;
}
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_CONSTANT | | ! tokenizer - > get_token_constant ( ) . is_num ( ) ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
current_export = PropertyInfo ( ) ;
2014-02-10 02:10:30 +01:00
_set_error ( " Expected a number as upper bound in numeric range hint. " ) ;
2014-09-19 23:39:50 +02:00
return ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
current_export . hint_string + = " , " + rtos ( sign * double ( tokenizer - > get_token_constant ( ) ) ) ;
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE )
2014-02-10 02:10:30 +01:00
break ;
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_COMMA ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
current_export = PropertyInfo ( ) ;
2014-02-10 02:10:30 +01:00
_set_error ( " Expected ',' or ')' in numeric range hint. " ) ;
2014-09-19 23:39:50 +02:00
return ;
2014-02-10 02:10:30 +01:00
}
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2017-03-05 16:44:50 +01:00
sign = 1.0 ;
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_OP_SUB ) {
2017-03-05 16:44:50 +01:00
sign = - 1 ;
2014-09-21 06:43:42 +02:00
tokenizer - > advance ( ) ;
}
2014-02-10 02:10:30 +01:00
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_CONSTANT | | ! tokenizer - > get_token_constant ( ) . is_num ( ) ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
current_export = PropertyInfo ( ) ;
2014-02-10 02:10:30 +01:00
_set_error ( " Expected a number as step in numeric range hint. " ) ;
2014-09-19 23:39:50 +02:00
return ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
current_export . hint_string + = " , " + rtos ( sign * double ( tokenizer - > get_token_constant ( ) ) ) ;
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
} break ;
case Variant : : STRING : {
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CONSTANT & & tokenizer - > get_token_constant ( ) . get_type ( ) = = Variant : : STRING ) {
2014-02-10 02:10:30 +01:00
//enumeration
2017-03-05 16:44:50 +01:00
current_export . hint = PROPERTY_HINT_ENUM ;
bool first = true ;
while ( true ) {
2014-02-10 02:10:30 +01:00
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_CONSTANT | | tokenizer - > get_token_constant ( ) . get_type ( ) ! = Variant : : STRING ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
current_export = PropertyInfo ( ) ;
2014-02-10 02:10:30 +01:00
_set_error ( " Expected a string constant in enumeration hint. " ) ;
2014-09-19 23:39:50 +02:00
return ;
2014-02-10 02:10:30 +01:00
}
2014-02-25 13:31:47 +01:00
String c = tokenizer - > get_token_constant ( ) ;
2014-02-10 02:10:30 +01:00
if ( ! first )
2017-03-05 16:44:50 +01:00
current_export . hint_string + = " , " ;
2014-02-10 02:10:30 +01:00
else
2017-03-05 16:44:50 +01:00
first = false ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
current_export . hint_string + = c . xml_escape ( ) ;
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE )
2014-02-10 02:10:30 +01:00
break ;
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_COMMA ) {
2017-03-05 16:44:50 +01:00
current_export = PropertyInfo ( ) ;
2014-02-10 02:10:30 +01:00
_set_error ( " Expected ')' or ',' in enumeration hint. " ) ;
return ;
}
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
}
break ;
}
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_IDENTIFIER & & tokenizer - > get_token_identifier ( ) = = " DIR " ) {
2014-02-10 02:10:30 +01:00
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
Add GDScript export hints for global filesystem and exponential ranges
- File in global filesystem: `String, FILE, GLOBAL, "*.png"`, tool scripts only
- Directory in global filesystem: `String, DIR, GLOBAL`, tool scripts only
- Exponential range: `float, EXP, 50, 150, 2`
2015-12-11 06:45:03 +01:00
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE )
2017-03-05 16:44:50 +01:00
current_export . hint = PROPERTY_HINT_DIR ;
2017-11-16 18:38:18 +01:00
else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COMMA ) {
Add GDScript export hints for global filesystem and exponential ranges
- File in global filesystem: `String, FILE, GLOBAL, "*.png"`, tool scripts only
- Directory in global filesystem: `String, DIR, GLOBAL`, tool scripts only
- Exponential range: `float, EXP, 50, 150, 2`
2015-12-11 06:45:03 +01:00
tokenizer - > advance ( ) ;
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_IDENTIFIER | | ! ( tokenizer - > get_token_identifier ( ) = = " GLOBAL " ) ) {
Add GDScript export hints for global filesystem and exponential ranges
- File in global filesystem: `String, FILE, GLOBAL, "*.png"`, tool scripts only
- Directory in global filesystem: `String, DIR, GLOBAL`, tool scripts only
- Exponential range: `float, EXP, 50, 150, 2`
2015-12-11 06:45:03 +01:00
_set_error ( " Expected 'GLOBAL' after comma in directory hint. " ) ;
return ;
}
if ( ! p_class - > tool ) {
_set_error ( " Global filesystem hints may only be used in tool scripts. " ) ;
return ;
}
2017-03-05 16:44:50 +01:00
current_export . hint = PROPERTY_HINT_GLOBAL_DIR ;
Add GDScript export hints for global filesystem and exponential ranges
- File in global filesystem: `String, FILE, GLOBAL, "*.png"`, tool scripts only
- Directory in global filesystem: `String, DIR, GLOBAL`, tool scripts only
- Exponential range: `float, EXP, 50, 150, 2`
2015-12-11 06:45:03 +01:00
tokenizer - > advance ( ) ;
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE ) {
Add GDScript export hints for global filesystem and exponential ranges
- File in global filesystem: `String, FILE, GLOBAL, "*.png"`, tool scripts only
- Directory in global filesystem: `String, DIR, GLOBAL`, tool scripts only
- Exponential range: `float, EXP, 50, 150, 2`
2015-12-11 06:45:03 +01:00
_set_error ( " Expected ')' in hint. " ) ;
return ;
}
2017-03-05 16:44:50 +01:00
} else {
Add GDScript export hints for global filesystem and exponential ranges
- File in global filesystem: `String, FILE, GLOBAL, "*.png"`, tool scripts only
- Directory in global filesystem: `String, DIR, GLOBAL`, tool scripts only
- Exponential range: `float, EXP, 50, 150, 2`
2015-12-11 06:45:03 +01:00
_set_error ( " Expected ')' or ',' in hint. " ) ;
2014-02-10 02:10:30 +01:00
return ;
}
break ;
}
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_IDENTIFIER & & tokenizer - > get_token_identifier ( ) = = " FILE " ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
current_export . hint = PROPERTY_HINT_FILE ;
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COMMA ) {
2014-02-10 02:10:30 +01:00
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
Add GDScript export hints for global filesystem and exponential ranges
- File in global filesystem: `String, FILE, GLOBAL, "*.png"`, tool scripts only
- Directory in global filesystem: `String, DIR, GLOBAL`, tool scripts only
- Exponential range: `float, EXP, 50, 150, 2`
2015-12-11 06:45:03 +01:00
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_IDENTIFIER & & tokenizer - > get_token_identifier ( ) = = " GLOBAL " ) {
Add GDScript export hints for global filesystem and exponential ranges
- File in global filesystem: `String, FILE, GLOBAL, "*.png"`, tool scripts only
- Directory in global filesystem: `String, DIR, GLOBAL`, tool scripts only
- Exponential range: `float, EXP, 50, 150, 2`
2015-12-11 06:45:03 +01:00
if ( ! p_class - > tool ) {
_set_error ( " Global filesystem hints may only be used in tool scripts. " ) ;
return ;
}
2017-03-05 16:44:50 +01:00
current_export . hint = PROPERTY_HINT_GLOBAL_FILE ;
Add GDScript export hints for global filesystem and exponential ranges
- File in global filesystem: `String, FILE, GLOBAL, "*.png"`, tool scripts only
- Directory in global filesystem: `String, DIR, GLOBAL`, tool scripts only
- Exponential range: `float, EXP, 50, 150, 2`
2015-12-11 06:45:03 +01:00
tokenizer - > advance ( ) ;
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE )
Add GDScript export hints for global filesystem and exponential ranges
- File in global filesystem: `String, FILE, GLOBAL, "*.png"`, tool scripts only
- Directory in global filesystem: `String, DIR, GLOBAL`, tool scripts only
- Exponential range: `float, EXP, 50, 150, 2`
2015-12-11 06:45:03 +01:00
break ;
2017-11-16 18:38:18 +01:00
else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COMMA )
Add GDScript export hints for global filesystem and exponential ranges
- File in global filesystem: `String, FILE, GLOBAL, "*.png"`, tool scripts only
- Directory in global filesystem: `String, DIR, GLOBAL`, tool scripts only
- Exponential range: `float, EXP, 50, 150, 2`
2015-12-11 06:45:03 +01:00
tokenizer - > advance ( ) ;
else {
_set_error ( " Expected ')' or ',' in hint. " ) ;
return ;
}
}
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_CONSTANT | | tokenizer - > get_token_constant ( ) . get_type ( ) ! = Variant : : STRING ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( current_export . hint = = PROPERTY_HINT_GLOBAL_FILE )
Add GDScript export hints for global filesystem and exponential ranges
- File in global filesystem: `String, FILE, GLOBAL, "*.png"`, tool scripts only
- Directory in global filesystem: `String, DIR, GLOBAL`, tool scripts only
- Exponential range: `float, EXP, 50, 150, 2`
2015-12-11 06:45:03 +01:00
_set_error ( " Expected string constant with filter " ) ;
else
_set_error ( " Expected 'GLOBAL' or string constant with filter " ) ;
2014-02-10 02:10:30 +01:00
return ;
}
2017-03-05 16:44:50 +01:00
current_export . hint_string = tokenizer - > get_token_constant ( ) ;
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
}
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE ) {
2014-02-10 02:10:30 +01:00
_set_error ( " Expected ')' in hint. " ) ;
return ;
}
break ;
}
2015-10-16 16:39:59 +02:00
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_IDENTIFIER & & tokenizer - > get_token_identifier ( ) = = " MULTILINE " ) {
2015-10-16 16:39:59 +02:00
2017-03-05 16:44:50 +01:00
current_export . hint = PROPERTY_HINT_MULTILINE_TEXT ;
2015-10-16 16:39:59 +02:00
tokenizer - > advance ( ) ;
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE ) {
2015-10-16 16:39:59 +02:00
_set_error ( " Expected ')' in hint. " ) ;
return ;
}
break ;
}
2014-02-10 02:10:30 +01:00
} break ;
case Variant : : COLOR : {
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_IDENTIFIER ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
current_export = PropertyInfo ( ) ;
2014-02-10 02:10:30 +01:00
_set_error ( " Color type hint expects RGB or RGBA as hints " ) ;
return ;
}
2014-02-25 13:31:47 +01:00
String identifier = tokenizer - > get_token_identifier ( ) ;
2017-03-05 16:44:50 +01:00
if ( identifier = = " RGB " ) {
current_export . hint = PROPERTY_HINT_COLOR_NO_ALPHA ;
} else if ( identifier = = " RGBA " ) {
2014-02-10 02:10:30 +01:00
//none
} else {
2017-03-05 16:44:50 +01:00
current_export = PropertyInfo ( ) ;
2014-02-10 02:10:30 +01:00
_set_error ( " Color type hint expects RGB or RGBA as hints " ) ;
return ;
}
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
} break ;
default : {
2017-03-05 16:44:50 +01:00
current_export = PropertyInfo ( ) ;
_set_error ( " Type ' " + Variant : : get_type_name ( type ) + " ' can't take hints. " ) ;
2014-02-10 02:10:30 +01:00
return ;
} break ;
}
2016-10-26 13:38:41 +02:00
}
2014-02-10 02:10:30 +01:00
2017-11-15 19:50:37 +01:00
} else {
2014-02-10 02:10:30 +01:00
2017-11-15 19:50:37 +01:00
parenthesis + + ;
Node * subexpr = _parse_and_reduce_expression ( p_class , true , true ) ;
if ( ! subexpr ) {
if ( _recover_from_completion ( ) ) {
break ;
}
return ;
}
parenthesis - - ;
2015-10-16 16:18:46 +02:00
2017-11-15 19:50:37 +01:00
if ( subexpr - > type ! = Node : : TYPE_CONSTANT ) {
2017-03-05 16:44:50 +01:00
current_export = PropertyInfo ( ) ;
2017-11-15 19:50:37 +01:00
_set_error ( " Expected a constant expression. " ) ;
2014-02-10 02:10:30 +01:00
}
2015-10-16 16:18:46 +02:00
2017-11-15 19:50:37 +01:00
Variant constant = static_cast < ConstantNode * > ( subexpr ) - > value ;
2016-06-11 23:31:22 +02:00
2017-11-15 19:50:37 +01:00
if ( constant . get_type ( ) = = Variant : : OBJECT ) {
GDScriptNativeClass * native_class = Object : : cast_to < GDScriptNativeClass > ( constant ) ;
2015-10-16 16:18:46 +02:00
2017-11-15 19:50:37 +01:00
if ( native_class & & ClassDB : : is_parent_class ( native_class - > get_name ( ) , " Resource " ) ) {
current_export . type = Variant : : OBJECT ;
current_export . hint = PROPERTY_HINT_RESOURCE_TYPE ;
current_export . usage | = PROPERTY_USAGE_SCRIPT_VARIABLE ;
current_export . hint_string = native_class - > get_name ( ) ;
2018-07-25 18:01:26 +02:00
current_export . class_name = native_class - > get_name ( ) ;
2017-11-15 19:50:37 +01:00
} else {
current_export = PropertyInfo ( ) ;
_set_error ( " Export hint not a resource type. " ) ;
}
} else if ( constant . get_type ( ) = = Variant : : DICTIONARY ) {
// Enumeration
bool is_flags = false ;
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COMMA ) {
tokenizer - > advance ( ) ;
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_IDENTIFIER & & tokenizer - > get_token_identifier ( ) = = " FLAGS " ) {
is_flags = true ;
tokenizer - > advance ( ) ;
} else {
current_export = PropertyInfo ( ) ;
_set_error ( " Expected 'FLAGS' after comma. " ) ;
}
}
current_export . type = Variant : : INT ;
current_export . hint = is_flags ? PROPERTY_HINT_FLAGS : PROPERTY_HINT_ENUM ;
2018-11-06 09:53:53 +01:00
current_export . usage | = PROPERTY_USAGE_SCRIPT_VARIABLE ;
2017-11-15 19:50:37 +01:00
Dictionary enum_values = constant ;
List < Variant > keys ;
enum_values . get_key_list ( & keys ) ;
bool first = true ;
for ( List < Variant > : : Element * E = keys . front ( ) ; E ; E = E - > next ( ) ) {
if ( enum_values [ E - > get ( ) ] . get_type ( ) = = Variant : : INT ) {
if ( ! first )
current_export . hint_string + = " , " ;
else
first = false ;
current_export . hint_string + = E - > get ( ) . operator String ( ) . camelcase_to_underscore ( true ) . capitalize ( ) . xml_escape ( ) ;
if ( ! is_flags ) {
current_export . hint_string + = " : " ;
current_export . hint_string + = enum_values [ E - > get ( ) ] . operator String ( ) . xml_escape ( ) ;
}
}
}
} else {
current_export = PropertyInfo ( ) ;
_set_error ( " Expected type for export. " ) ;
return ;
}
2014-02-10 02:10:30 +01:00
}
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
current_export = PropertyInfo ( ) ;
2014-02-10 02:10:30 +01:00
_set_error ( " Expected ')' or ',' after export hint. " ) ;
return ;
}
2018-02-05 15:41:13 +01:00
if ( is_arrayed ) {
hint_prefix + = itos ( current_export . type ) ;
if ( current_export . hint ) {
hint_prefix + = " / " + itos ( current_export . hint ) ;
}
current_export . hint_string = hint_prefix + " : " + current_export . hint_string ;
current_export . hint = PROPERTY_HINT_TYPE_STRING ;
current_export . type = Variant : : ARRAY ;
}
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
}
2018-09-14 21:59:47 +02:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PR_VAR & & tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PR_ONREADY & & tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PR_REMOTE & & tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PR_MASTER & & tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PR_PUPPET & & tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PR_SYNC & & tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PR_REMOTESYNC & & tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PR_MASTERSYNC & & tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PR_PUPPETSYNC & & tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PR_SLAVE ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
current_export = PropertyInfo ( ) ;
2018-09-14 21:59:47 +02:00
_set_error ( " Expected 'var', 'onready', 'remote', 'master', 'puppet', 'sync', 'remotesync', 'mastersync', 'puppetsync'. " ) ;
2014-02-10 02:10:30 +01:00
return ;
}
2016-08-19 21:48:08 +02:00
continue ;
} break ;
2017-11-16 18:38:18 +01:00
case GDScriptTokenizer : : TK_PR_ONREADY : {
2015-12-28 23:31:52 +01:00
2016-08-19 21:48:08 +02:00
//may be fallthrough from export, ignore if so
tokenizer - > advance ( ) ;
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PR_VAR ) {
2016-08-19 21:48:08 +02:00
_set_error ( " Expected 'var'. " ) ;
return ;
}
continue ;
} break ;
2017-11-16 18:38:18 +01:00
case GDScriptTokenizer : : TK_PR_REMOTE : {
2016-08-19 21:48:08 +02:00
//may be fallthrough from export, ignore if so
tokenizer - > advance ( ) ;
2017-03-05 16:44:50 +01:00
if ( current_export . type ) {
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PR_VAR ) {
2015-12-28 23:31:52 +01:00
_set_error ( " Expected 'var'. " ) ;
return ;
}
2016-08-19 21:48:08 +02:00
} else {
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PR_VAR & & tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PR_FUNCTION ) {
2016-08-19 21:48:08 +02:00
_set_error ( " Expected 'var' or 'func'. " ) ;
return ;
}
2015-12-28 23:31:52 +01:00
}
2018-05-13 07:07:56 +02:00
rpc_mode = MultiplayerAPI : : RPC_MODE_REMOTE ;
2016-08-19 21:48:08 +02:00
continue ;
} break ;
2017-11-16 18:38:18 +01:00
case GDScriptTokenizer : : TK_PR_MASTER : {
2016-08-19 21:48:08 +02:00
//may be fallthrough from export, ignore if so
tokenizer - > advance ( ) ;
2017-03-05 16:44:50 +01:00
if ( current_export . type ) {
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PR_VAR ) {
2016-08-19 21:48:08 +02:00
_set_error ( " Expected 'var'. " ) ;
return ;
}
} else {
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PR_VAR & & tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PR_FUNCTION ) {
2016-08-19 21:48:08 +02:00
_set_error ( " Expected 'var' or 'func'. " ) ;
return ;
}
}
2018-05-13 07:07:56 +02:00
rpc_mode = MultiplayerAPI : : RPC_MODE_MASTER ;
2016-08-19 21:48:08 +02:00
continue ;
} break ;
2018-09-14 21:59:47 +02:00
case GDScriptTokenizer : : TK_PR_SLAVE :
2018-09-16 19:01:57 +02:00
# ifdef DEBUG_ENABLED
2018-09-15 01:45:43 +02:00
_add_warning ( GDScriptWarning : : DEPRECATED_KEYWORD , tokenizer - > get_token_line ( ) , " slave " , " puppet " ) ;
2018-09-16 19:01:57 +02:00
# endif
2018-09-14 21:59:47 +02:00
case GDScriptTokenizer : : TK_PR_PUPPET : {
2016-08-19 21:48:08 +02:00
//may be fallthrough from export, ignore if so
tokenizer - > advance ( ) ;
2017-03-05 16:44:50 +01:00
if ( current_export . type ) {
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PR_VAR ) {
2016-08-19 21:48:08 +02:00
_set_error ( " Expected 'var'. " ) ;
return ;
}
} else {
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PR_VAR & & tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PR_FUNCTION ) {
2016-08-19 21:48:08 +02:00
_set_error ( " Expected 'var' or 'func'. " ) ;
return ;
}
}
2018-09-14 21:59:47 +02:00
rpc_mode = MultiplayerAPI : : RPC_MODE_PUPPET ;
2016-08-19 21:48:08 +02:00
continue ;
} break ;
2018-05-26 10:30:36 +02:00
case GDScriptTokenizer : : TK_PR_REMOTESYNC :
2017-11-16 18:38:18 +01:00
case GDScriptTokenizer : : TK_PR_SYNC : {
2016-08-19 21:48:08 +02:00
//may be fallthrough from export, ignore if so
tokenizer - > advance ( ) ;
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PR_VAR & & tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PR_FUNCTION ) {
2016-08-19 21:48:08 +02:00
if ( current_export . type )
_set_error ( " Expected 'var'. " ) ;
else
_set_error ( " Expected 'var' or 'func'. " ) ;
return ;
}
2018-09-14 23:10:12 +02:00
rpc_mode = MultiplayerAPI : : RPC_MODE_REMOTESYNC ;
2016-08-19 21:48:08 +02:00
continue ;
} break ;
2018-05-26 10:30:36 +02:00
case GDScriptTokenizer : : TK_PR_MASTERSYNC : {
//may be fallthrough from export, ignore if so
tokenizer - > advance ( ) ;
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PR_VAR & & tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PR_FUNCTION ) {
if ( current_export . type )
_set_error ( " Expected 'var'. " ) ;
else
_set_error ( " Expected 'var' or 'func'. " ) ;
return ;
}
rpc_mode = MultiplayerAPI : : RPC_MODE_MASTERSYNC ;
continue ;
} break ;
2018-09-14 21:59:47 +02:00
case GDScriptTokenizer : : TK_PR_PUPPETSYNC : {
2018-05-26 10:30:36 +02:00
//may be fallthrough from export, ignore if so
tokenizer - > advance ( ) ;
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PR_VAR & & tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PR_FUNCTION ) {
if ( current_export . type )
_set_error ( " Expected 'var'. " ) ;
else
_set_error ( " Expected 'var' or 'func'. " ) ;
return ;
}
2018-09-14 21:59:47 +02:00
rpc_mode = MultiplayerAPI : : RPC_MODE_PUPPETSYNC ;
2018-05-26 10:30:36 +02:00
continue ;
} break ;
2017-11-16 18:38:18 +01:00
case GDScriptTokenizer : : TK_PR_VAR : {
2014-02-10 02:10:30 +01:00
//variale declaration and (eventual) initialization
ClassNode : : Member member ;
2017-11-16 18:38:18 +01:00
bool autoexport = tokenizer - > get_token ( - 1 ) = = GDScriptTokenizer : : TK_PR_EXPORT ;
2017-03-05 16:44:50 +01:00
if ( current_export . type ! = Variant : : NIL ) {
member . _export = current_export ;
current_export = PropertyInfo ( ) ;
2014-02-10 02:10:30 +01:00
}
2017-11-16 18:38:18 +01:00
bool onready = tokenizer - > get_token ( - 1 ) = = GDScriptTokenizer : : TK_PR_ONREADY ;
2015-12-28 23:31:52 +01:00
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2017-03-31 19:28:34 +02:00
if ( ! tokenizer - > is_token_literal ( 0 , true ) ) {
2014-02-10 02:10:30 +01:00
_set_error ( " Expected identifier for member variable name. " ) ;
return ;
}
2017-03-31 19:28:34 +02:00
member . identifier = tokenizer - > get_token_literal ( ) ;
2017-03-05 16:44:50 +01:00
member . expression = NULL ;
member . _export . name = member . identifier ;
member . line = tokenizer - > get_token_line ( ) ;
2018-07-01 18:17:40 +02:00
member . usages = 0 ;
2017-03-05 16:44:50 +01:00
member . rpc_mode = rpc_mode ;
2016-08-19 21:48:08 +02:00
2018-05-30 04:16:54 +02:00
if ( current_class - > constant_expressions . has ( member . identifier ) ) {
2018-09-13 03:38:39 +02:00
_set_error ( " A constant named ' " + String ( member . identifier ) + " ' already exists in this class (at line: " +
2018-05-30 04:16:54 +02:00
itos ( current_class - > constant_expressions [ member . identifier ] . expression - > line ) + " ). " ) ;
return ;
}
for ( int i = 0 ; i < current_class - > variables . size ( ) ; i + + ) {
if ( current_class - > variables [ i ] . identifier = = member . identifier ) {
2018-09-13 03:38:39 +02:00
_set_error ( " Variable ' " + String ( member . identifier ) + " ' already exists in this class (at line: " +
2018-05-30 04:16:54 +02:00
itos ( current_class - > variables [ i ] . line ) + " ). " ) ;
return ;
}
}
2018-07-01 18:17:40 +02:00
# ifdef DEBUG_ENABLED
for ( int i = 0 ; i < current_class - > functions . size ( ) ; i + + ) {
if ( current_class - > functions [ i ] - > name = = member . identifier ) {
_add_warning ( GDScriptWarning : : VARIABLE_CONFLICTS_FUNCTION , member . line , member . identifier ) ;
break ;
}
}
for ( int i = 0 ; i < current_class - > static_functions . size ( ) ; i + + ) {
if ( current_class - > static_functions [ i ] - > name = = member . identifier ) {
_add_warning ( GDScriptWarning : : VARIABLE_CONFLICTS_FUNCTION , member . line , member . identifier ) ;
break ;
}
}
# endif // DEBUG_ENABLED
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
2018-05-13 07:07:56 +02:00
rpc_mode = MultiplayerAPI : : RPC_MODE_DISABLED ;
2016-08-19 21:48:08 +02:00
2018-05-30 04:16:51 +02:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COLON ) {
2018-06-19 07:55:52 +02:00
if ( tokenizer - > get_token ( 1 ) = = GDScriptTokenizer : : TK_OP_ASSIGN ) {
member . data_type = DataType ( ) ;
# ifdef DEBUG_ENABLED
member . data_type . infer_type = true ;
# endif
tokenizer - > advance ( ) ;
} else if ( ! _parse_type ( member . data_type ) ) {
2018-05-30 04:16:51 +02:00
_set_error ( " Expected type for class variable. " ) ;
return ;
}
}
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_OP_ASSIGN ) {
2014-02-10 02:10:30 +01:00
2014-10-28 02:54:32 +01:00
# ifdef DEBUG_ENABLED
int line = tokenizer - > get_token_line ( ) ;
# endif
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
2018-06-18 21:24:31 +02:00
Node * subexpr = _parse_and_reduce_expression ( p_class , false , autoexport | | member . _export . type ! = Variant : : NIL ) ;
2015-08-30 16:50:10 +02:00
if ( ! subexpr ) {
if ( _recover_from_completion ( ) ) {
break ;
}
2014-02-10 02:10:30 +01:00
return ;
2015-08-30 16:50:10 +02:00
}
2014-02-10 02:10:30 +01:00
2015-12-28 23:31:52 +01:00
//discourage common error
2017-03-05 16:44:50 +01:00
if ( ! onready & & subexpr - > type = = Node : : TYPE_OPERATOR ) {
2015-12-28 23:31:52 +01:00
2017-03-05 16:44:50 +01:00
OperatorNode * op = static_cast < OperatorNode * > ( subexpr ) ;
if ( op - > op = = OperatorNode : : OP_CALL & & op - > arguments [ 0 ] - > type = = Node : : TYPE_SELF & & op - > arguments [ 1 ] - > type = = Node : : TYPE_IDENTIFIER ) {
IdentifierNode * id = static_cast < IdentifierNode * > ( op - > arguments [ 1 ] ) ;
if ( id - > name = = " get_node " ) {
2015-12-28 23:31:52 +01:00
2017-03-05 16:44:50 +01:00
_set_error ( " Use 'onready var " + String ( member . identifier ) + " = get_node(..)' instead " ) ;
2015-12-28 23:31:52 +01:00
return ;
}
}
}
2017-03-05 16:44:50 +01:00
member . expression = subexpr ;
2014-12-17 02:31:57 +01:00
2018-05-30 04:16:54 +02:00
if ( autoexport & & ! member . data_type . has_type ) {
2014-02-10 02:10:30 +01:00
2018-05-30 04:16:54 +02:00
if ( subexpr - > type ! = Node : : TYPE_CONSTANT ) {
2014-02-10 02:10:30 +01:00
2018-05-30 04:16:54 +02:00
_set_error ( " Type-less export needs a constant expression assigned to infer type. " ) ;
return ;
}
2014-02-10 02:10:30 +01:00
2018-05-30 04:16:54 +02:00
ConstantNode * cn = static_cast < ConstantNode * > ( subexpr ) ;
if ( cn - > value . get_type ( ) = = Variant : : NIL ) {
2014-02-10 02:10:30 +01:00
2018-05-30 04:16:54 +02:00
_set_error ( " Can't accept a null constant expression for inferring export type. " ) ;
return ;
}
member . _export . type = cn - > value . get_type ( ) ;
member . _export . usage | = PROPERTY_USAGE_SCRIPT_VARIABLE ;
if ( cn - > value . get_type ( ) = = Variant : : OBJECT ) {
Object * obj = cn - > value ;
Resource * res = Object : : cast_to < Resource > ( obj ) ;
if ( res = = NULL ) {
_set_error ( " Exported constant not a type or resource. " ) ;
2014-10-28 02:54:32 +01:00
return ;
}
2018-05-30 04:16:54 +02:00
member . _export . hint = PROPERTY_HINT_RESOURCE_TYPE ;
member . _export . hint_string = res - > get_class ( ) ;
2014-02-10 02:10:30 +01:00
}
2014-10-28 02:54:32 +01:00
}
# ifdef TOOLS_ENABLED
2017-03-05 16:44:50 +01:00
if ( subexpr - > type = = Node : : TYPE_CONSTANT & & member . _export . type ! = Variant : : NIL ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
ConstantNode * cn = static_cast < ConstantNode * > ( subexpr ) ;
if ( cn - > value . get_type ( ) ! = Variant : : NIL ) {
member . default_value = cn - > value ;
2014-02-10 02:10:30 +01:00
}
}
2014-10-28 02:54:32 +01:00
# endif
IdentifierNode * id = alloc_node < IdentifierNode > ( ) ;
2017-03-05 16:44:50 +01:00
id - > name = member . identifier ;
2014-10-28 02:54:32 +01:00
OperatorNode * op = alloc_node < OperatorNode > ( ) ;
2017-03-05 16:44:50 +01:00
op - > op = OperatorNode : : OP_INIT_ASSIGN ;
2014-10-28 02:54:32 +01:00
op - > arguments . push_back ( id ) ;
op - > arguments . push_back ( subexpr ) ;
# ifdef DEBUG_ENABLED
NewLineNode * nl = alloc_node < NewLineNode > ( ) ;
2017-03-05 16:44:50 +01:00
nl - > line = line ;
2015-12-28 23:31:52 +01:00
if ( onready )
p_class - > ready - > statements . push_back ( nl ) ;
else
p_class - > initializer - > statements . push_back ( nl ) ;
2014-10-28 02:54:32 +01:00
# endif
2015-12-28 23:31:52 +01:00
if ( onready )
p_class - > ready - > statements . push_back ( op ) ;
else
p_class - > initializer - > statements . push_back ( op ) ;
2014-02-10 02:10:30 +01:00
2018-05-30 04:16:54 +02:00
member . initial_assignment = op ;
2014-10-28 02:54:32 +01:00
2018-05-30 04:16:54 +02:00
} else {
2014-10-28 02:54:32 +01:00
2018-05-30 04:16:54 +02:00
if ( autoexport & & ! member . data_type . has_type ) {
2014-10-28 02:54:32 +01:00
_set_error ( " Type-less export needs a constant expression assigned to infer type. " ) ;
return ;
2014-02-10 02:10:30 +01:00
}
}
2018-05-30 04:16:54 +02:00
if ( autoexport & & member . data_type . has_type ) {
if ( member . data_type . kind = = DataType : : BUILTIN ) {
member . _export . type = member . data_type . builtin_type ;
} else if ( member . data_type . kind = = DataType : : NATIVE ) {
if ( ClassDB : : is_parent_class ( member . data_type . native_type , " Resource " ) ) {
member . _export . type = Variant : : OBJECT ;
member . _export . hint = PROPERTY_HINT_RESOURCE_TYPE ;
member . _export . usage | = PROPERTY_USAGE_SCRIPT_VARIABLE ;
2018-07-25 18:01:26 +02:00
member . _export . hint_string = member . data_type . native_type ;
2018-05-30 04:16:54 +02:00
member . _export . class_name = member . data_type . native_type ;
} else {
_set_error ( " Invalid export type. Only built-in and native resource types can be exported. " , member . line ) ;
return ;
}
} else {
_set_error ( " Invalid export type. Only built-in and native resource types can be exported. " , member . line ) ;
return ;
}
}
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PR_SETGET ) {
2014-02-10 02:10:30 +01:00
2014-10-28 02:54:32 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_COMMA ) {
2014-10-28 02:54:32 +01:00
//just comma means using only getter
2017-03-31 19:28:34 +02:00
if ( ! tokenizer - > is_token_literal ( ) ) {
_set_error ( " Expected identifier for setter function after 'setget'. " ) ;
2014-10-28 02:54:32 +01:00
}
2014-02-10 02:10:30 +01:00
2017-03-31 19:28:34 +02:00
member . setter = tokenizer - > get_token_literal ( ) ;
2014-10-28 02:54:32 +01:00
tokenizer - > advance ( ) ;
}
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COMMA ) {
2014-10-28 02:54:32 +01:00
//there is a getter
tokenizer - > advance ( ) ;
2017-03-31 19:28:34 +02:00
if ( ! tokenizer - > is_token_literal ( ) ) {
2014-10-28 02:54:32 +01:00
_set_error ( " Expected identifier for getter function after ','. " ) ;
}
2017-03-31 19:28:34 +02:00
member . getter = tokenizer - > get_token_literal ( ) ;
2014-10-28 02:54:32 +01:00
tokenizer - > advance ( ) ;
}
}
p_class - > variables . push_back ( member ) ;
2014-02-10 02:10:30 +01:00
2015-06-24 18:29:23 +02:00
if ( ! _end_statement ( ) ) {
_set_error ( " Expected end of statement (continue) " ) ;
return ;
}
2014-02-10 02:10:30 +01:00
} break ;
2017-11-16 18:38:18 +01:00
case GDScriptTokenizer : : TK_PR_CONST : {
2018-05-30 04:16:54 +02:00
// constant declaration and initialization
2014-02-10 02:10:30 +01:00
ClassNode : : Constant constant ;
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2017-03-31 19:28:34 +02:00
if ( ! tokenizer - > is_token_literal ( 0 , true ) ) {
2014-02-10 02:10:30 +01:00
_set_error ( " Expected name (identifier) for constant. " ) ;
return ;
}
2018-05-30 04:16:54 +02:00
StringName const_id = tokenizer - > get_token_literal ( ) ;
2018-06-06 03:57:44 +02:00
int line = tokenizer - > get_token_line ( ) ;
2018-05-30 04:16:54 +02:00
if ( current_class - > constant_expressions . has ( const_id ) ) {
2018-09-13 03:38:39 +02:00
_set_error ( " Constant ' " + String ( const_id ) + " ' already exists in this class (at line: " +
2018-05-30 04:16:54 +02:00
itos ( current_class - > constant_expressions [ const_id ] . expression - > line ) + " ). " ) ;
return ;
}
for ( int i = 0 ; i < current_class - > variables . size ( ) ; i + + ) {
if ( current_class - > variables [ i ] . identifier = = const_id ) {
2018-09-13 03:38:39 +02:00
_set_error ( " A variable named ' " + String ( const_id ) + " ' already exists in this class (at line: " +
2018-05-30 04:16:54 +02:00
itos ( current_class - > variables [ i ] . line ) + " ). " ) ;
return ;
}
}
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
2018-05-30 04:16:51 +02:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COLON ) {
2018-06-19 07:55:52 +02:00
if ( tokenizer - > get_token ( 1 ) = = GDScriptTokenizer : : TK_OP_ASSIGN ) {
constant . type = DataType ( ) ;
# ifdef DEBUG_ENABLED
constant . type . infer_type = true ;
# endif
tokenizer - > advance ( ) ;
} else if ( ! _parse_type ( constant . type ) ) {
2018-05-30 04:16:51 +02:00
_set_error ( " Expected type for class constant. " ) ;
return ;
}
}
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_OP_ASSIGN ) {
2014-02-10 02:10:30 +01:00
_set_error ( " Constant expects assignment. " ) ;
return ;
}
2014-02-25 13:31:47 +01:00
tokenizer - > advance ( ) ;
2014-02-10 02:10:30 +01:00
2017-08-21 21:15:36 +02:00
Node * subexpr = _parse_and_reduce_expression ( p_class , true , true ) ;
2015-08-30 16:50:10 +02:00
if ( ! subexpr ) {
if ( _recover_from_completion ( ) ) {
break ;
}
2014-02-10 02:10:30 +01:00
return ;
2015-08-30 16:50:10 +02:00
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( subexpr - > type ! = Node : : TYPE_CONSTANT ) {
2018-05-30 04:16:52 +02:00
_set_error ( " Expected constant expression " , line ) ;
return ;
2014-02-10 02:10:30 +01:00
}
2018-06-06 03:57:44 +02:00
subexpr - > line = line ;
2017-03-05 16:44:50 +01:00
constant . expression = subexpr ;
2014-02-10 02:10:30 +01:00
2018-05-30 04:16:54 +02:00
p_class - > constant_expressions . insert ( const_id , constant ) ;
2014-02-10 02:10:30 +01:00
2015-06-24 18:29:23 +02:00
if ( ! _end_statement ( ) ) {
2018-05-30 04:16:52 +02:00
_set_error ( " Expected end of statement (constant) " , line ) ;
2015-06-24 18:29:23 +02:00
return ;
}
2014-02-10 02:10:30 +01:00
2016-08-26 13:15:45 +02:00
} break ;
2017-11-16 18:38:18 +01:00
case GDScriptTokenizer : : TK_PR_ENUM : {
2018-02-21 17:30:55 +01:00
//multiple constant declarations..
2016-08-26 13:15:45 +02:00
2018-09-13 03:38:39 +02:00
int last_assign = - 1 ; // Incremented by 1 right before the assignment.
2016-08-27 14:56:51 +02:00
String enum_name ;
Dictionary enum_dict ;
2016-08-26 13:15:45 +02:00
tokenizer - > advance ( ) ;
2017-03-31 19:28:34 +02:00
if ( tokenizer - > is_token_literal ( 0 , true ) ) {
enum_name = tokenizer - > get_token_literal ( ) ;
2018-11-11 04:42:20 +01:00
if ( current_class - > constant_expressions . has ( enum_name ) ) {
_set_error ( " A constant named ' " + String ( enum_name ) + " ' already exists in this class (at line: " +
itos ( current_class - > constant_expressions [ enum_name ] . expression - > line ) + " ). " ) ;
return ;
}
for ( int i = 0 ; i < current_class - > variables . size ( ) ; i + + ) {
if ( current_class - > variables [ i ] . identifier = = enum_name ) {
_set_error ( " A variable named ' " + String ( enum_name ) + " ' already exists in this class (at line: " +
itos ( current_class - > variables [ i ] . line ) + " ). " ) ;
return ;
}
}
2016-08-27 14:56:51 +02:00
tokenizer - > advance ( ) ;
}
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_CURLY_BRACKET_OPEN ) {
2016-08-26 13:15:45 +02:00
_set_error ( " Expected '{' in enum declaration " ) ;
return ;
}
tokenizer - > advance ( ) ;
2017-03-05 16:44:50 +01:00
while ( true ) {
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_NEWLINE ) {
2017-03-05 16:44:50 +01:00
2016-08-26 13:15:45 +02:00
tokenizer - > advance ( ) ; // Ignore newlines
2017-11-16 18:38:18 +01:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CURLY_BRACKET_CLOSE ) {
2017-03-05 16:44:50 +01:00
2016-08-26 13:15:45 +02:00
tokenizer - > advance ( ) ;
break ; // End of enum
2017-03-31 19:28:34 +02:00
} else if ( ! tokenizer - > is_token_literal ( 0 , true ) ) {
2017-03-05 16:44:50 +01:00
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_EOF ) {
2016-08-26 13:15:45 +02:00
_set_error ( " Unexpected end of file. " ) ;
} else {
2017-11-16 18:38:18 +01:00
_set_error ( String ( " Unexpected " ) + GDScriptTokenizer : : get_token_name ( tokenizer - > get_token ( ) ) + " , expected identifier " ) ;
2016-08-26 13:15:45 +02:00
}
2017-03-05 16:44:50 +01:00
2016-08-26 13:15:45 +02:00
return ;
2017-03-31 19:28:34 +02:00
} else { // tokenizer->is_token_literal(0, true)
2018-05-30 04:16:54 +02:00
StringName const_id = tokenizer - > get_token_literal ( ) ;
2017-03-05 16:44:50 +01:00
2016-08-26 13:15:45 +02:00
tokenizer - > advance ( ) ;
2017-03-05 16:44:50 +01:00
2018-11-11 04:42:20 +01:00
ConstantNode * enum_value_expr ;
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_OP_ASSIGN ) {
2016-08-26 13:15:45 +02:00
tokenizer - > advance ( ) ;
2017-08-21 21:15:36 +02:00
Node * subexpr = _parse_and_reduce_expression ( p_class , true , true ) ;
2016-08-26 13:15:45 +02:00
if ( ! subexpr ) {
if ( _recover_from_completion ( ) ) {
break ;
}
return ;
}
2017-03-05 16:44:50 +01:00
if ( subexpr - > type ! = Node : : TYPE_CONSTANT ) {
2016-08-26 13:15:45 +02:00
_set_error ( " Expected constant expression " ) ;
2018-05-30 04:16:52 +02:00
return ;
2016-08-26 13:15:45 +02:00
}
2017-03-05 16:44:50 +01:00
2018-11-11 04:42:20 +01:00
enum_value_expr = static_cast < ConstantNode * > ( subexpr ) ;
2017-03-05 16:44:50 +01:00
2018-11-11 04:42:20 +01:00
if ( enum_value_expr - > value . get_type ( ) ! = Variant : : INT ) {
2016-08-26 13:15:45 +02:00
_set_error ( " Expected an int value for enum " ) ;
2018-05-30 04:16:52 +02:00
return ;
2016-08-26 13:15:45 +02:00
}
2017-03-05 16:44:50 +01:00
2018-11-11 04:42:20 +01:00
last_assign = enum_value_expr - > value ;
2016-08-26 13:15:45 +02:00
} else {
last_assign = last_assign + 1 ;
2018-11-11 04:42:20 +01:00
enum_value_expr = alloc_node < ConstantNode > ( ) ;
enum_value_expr - > value = last_assign ;
enum_value_expr - > datatype = _type_from_variant ( enum_value_expr - > value ) ;
2016-08-26 13:15:45 +02:00
}
2017-03-05 16:44:50 +01:00
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COMMA ) {
2016-08-26 13:15:45 +02:00
tokenizer - > advance ( ) ;
}
2017-03-05 16:44:50 +01:00
if ( enum_name ! = " " ) {
2018-11-11 04:42:20 +01:00
enum_dict [ const_id ] = enum_value_expr - > value ;
} else {
if ( current_class - > constant_expressions . has ( const_id ) ) {
_set_error ( " A constant named ' " + String ( const_id ) + " ' already exists in this class (at line: " +
itos ( current_class - > constant_expressions [ const_id ] . expression - > line ) + " ). " ) ;
return ;
}
2016-08-27 14:56:51 +02:00
2018-11-11 04:42:20 +01:00
for ( int i = 0 ; i < current_class - > variables . size ( ) ; i + + ) {
if ( current_class - > variables [ i ] . identifier = = const_id ) {
_set_error ( " A variable named ' " + String ( const_id ) + " ' already exists in this class (at line: " +
itos ( current_class - > variables [ i ] . line ) + " ). " ) ;
return ;
}
}
ClassNode : : Constant constant ;
constant . type . has_type = true ;
constant . type . kind = DataType : : BUILTIN ;
constant . type . builtin_type = Variant : : INT ;
constant . expression = enum_value_expr ;
p_class - > constant_expressions . insert ( const_id , constant ) ;
}
2016-08-26 13:15:45 +02:00
}
}
2017-03-05 16:44:50 +01:00
if ( enum_name ! = " " ) {
2016-08-27 14:56:51 +02:00
ClassNode : : Constant enum_constant ;
ConstantNode * cn = alloc_node < ConstantNode > ( ) ;
cn - > value = enum_dict ;
2018-05-30 04:16:54 +02:00
cn - > datatype = _type_from_variant ( cn - > value ) ;
2017-03-05 16:44:50 +01:00
enum_constant . expression = cn ;
2018-05-30 04:16:53 +02:00
enum_constant . type = cn - > datatype ;
2018-05-30 04:16:54 +02:00
p_class - > constant_expressions . insert ( enum_name , enum_constant ) ;
2016-08-27 14:56:51 +02:00
}
2016-08-26 13:15:45 +02:00
if ( ! _end_statement ( ) ) {
_set_error ( " Expected end of statement (enum) " ) ;
return ;
}
2014-02-10 02:10:30 +01:00
} break ;
2017-03-05 16:44:50 +01:00
2017-11-16 18:38:18 +01:00
case GDScriptTokenizer : : TK_CONSTANT : {
2017-03-05 16:44:50 +01:00
if ( tokenizer - > get_token_constant ( ) . get_type ( ) = = Variant : : STRING ) {
2016-11-03 11:26:38 +01:00
tokenizer - > advance ( ) ;
// Ignore
} else {
2017-03-05 16:44:50 +01:00
_set_error ( String ( ) + " Unexpected constant of type: " + Variant : : get_type_name ( tokenizer - > get_token_constant ( ) . get_type ( ) ) ) ;
2016-11-03 11:26:38 +01:00
return ;
}
} break ;
2014-02-10 02:10:30 +01:00
default : {
2017-03-05 16:44:50 +01:00
_set_error ( String ( ) + " Unexpected token: " + tokenizer - > get_token_name ( tokenizer - > get_token ( ) ) + " : " + tokenizer - > get_token_identifier ( ) ) ;
2014-02-10 02:10:30 +01:00
return ;
} break ;
}
}
}
2018-05-30 04:16:52 +02:00
void GDScriptParser : : _determine_inheritance ( ClassNode * p_class ) {
if ( p_class - > extends_used ) {
//do inheritance
String path = p_class - > extends_file ;
Ref < GDScript > script ;
StringName native ;
ClassNode * base_class = NULL ;
if ( path ! = " " ) {
//path (and optionally subclasses)
if ( path . is_rel_path ( ) ) {
2018-06-21 03:41:26 +02:00
String base = base_path ;
2018-05-30 04:16:52 +02:00
if ( base = = " " | | base . is_rel_path ( ) ) {
_set_error ( " Could not resolve relative path for parent class: " + path , p_class - > line ) ;
return ;
}
2018-06-21 03:41:26 +02:00
path = base . plus_file ( path ) . simplify_path ( ) ;
2018-05-30 04:16:52 +02:00
}
script = ResourceLoader : : load ( path ) ;
if ( script . is_null ( ) ) {
_set_error ( " Could not load base class: " + path , p_class - > line ) ;
return ;
}
if ( ! script - > is_valid ( ) ) {
_set_error ( " Script not fully loaded (cyclic preload?): " + path , p_class - > line ) ;
return ;
}
if ( p_class - > extends_class . size ( ) ) {
for ( int i = 0 ; i < p_class - > extends_class . size ( ) ; i + + ) {
String sub = p_class - > extends_class [ i ] ;
if ( script - > get_subclasses ( ) . has ( sub ) ) {
Ref < Script > subclass = script - > get_subclasses ( ) [ sub ] ; //avoid reference from disappearing
script = subclass ;
} else {
_set_error ( " Could not find subclass: " + sub , p_class - > line ) ;
return ;
}
}
}
} else {
if ( p_class - > extends_class . size ( ) = = 0 ) {
_set_error ( " Parser bug: undecidable inheritance. " , p_class - > line ) ;
ERR_FAIL ( ) ;
}
//look around for the subclasses
int extend_iter = 1 ;
String base = p_class - > extends_class [ 0 ] ;
ClassNode * p = p_class - > owner ;
Ref < GDScript > base_script ;
if ( ScriptServer : : is_global_class ( base ) ) {
base_script = ResourceLoader : : load ( ScriptServer : : get_global_class_path ( base ) ) ;
if ( ! base_script . is_valid ( ) ) {
_set_error ( " Class ' " + base + " ' could not be fully loaded (script error or cyclic inheritance). " , p_class - > line ) ;
return ;
}
p = NULL ;
}
while ( p ) {
bool found = false ;
for ( int i = 0 ; i < p - > subclasses . size ( ) ; i + + ) {
if ( p - > subclasses [ i ] - > name = = base ) {
ClassNode * test = p - > subclasses [ i ] ;
while ( test ) {
if ( test = = p_class ) {
_set_error ( " Cyclic inheritance. " , test - > line ) ;
return ;
}
if ( test - > base_type . kind = = DataType : : CLASS ) {
test = test - > base_type . class_type ;
} else {
break ;
}
}
found = true ;
if ( extend_iter < p_class - > extends_class . size ( ) ) {
// Keep looking at current classes if possible
base = p_class - > extends_class [ extend_iter + + ] ;
p = p - > subclasses [ i ] ;
} else {
base_class = p - > subclasses [ i ] ;
}
break ;
}
}
if ( base_class ) break ;
if ( found ) continue ;
2018-05-30 04:16:54 +02:00
if ( p - > constant_expressions . has ( base ) ) {
2018-09-27 10:43:12 +02:00
if ( p - > constant_expressions [ base ] . expression - > type ! = Node : : TYPE_CONSTANT ) {
2018-05-30 04:16:54 +02:00
_set_error ( " Could not resolve constant ' " + base + " '. " , p_class - > line ) ;
return ;
}
const ConstantNode * cn = static_cast < const ConstantNode * > ( p - > constant_expressions [ base ] . expression ) ;
base_script = cn - > value ;
if ( base_script . is_null ( ) ) {
_set_error ( " Constant is not a class: " + base , p_class - > line ) ;
return ;
2018-05-30 04:16:52 +02:00
}
2018-05-30 04:16:54 +02:00
break ;
2018-05-30 04:16:52 +02:00
}
p = p - > owner ;
}
if ( base_script . is_valid ( ) ) {
String ident = base ;
for ( int i = extend_iter ; i < p_class - > extends_class . size ( ) ; i + + ) {
String subclass = p_class - > extends_class [ i ] ;
ident + = ( " . " + subclass ) ;
if ( base_script - > get_subclasses ( ) . has ( subclass ) ) {
base_script = base_script - > get_subclasses ( ) [ subclass ] ;
} else if ( base_script - > get_constants ( ) . has ( subclass ) ) {
Ref < GDScript > new_base_class = base_script - > get_constants ( ) [ subclass ] ;
if ( new_base_class . is_null ( ) ) {
_set_error ( " Constant is not a class: " + ident , p_class - > line ) ;
return ;
}
base_script = new_base_class ;
} else {
_set_error ( " Could not find subclass: " + ident , p_class - > line ) ;
return ;
}
}
script = base_script ;
} else if ( ! base_class ) {
if ( p_class - > extends_class . size ( ) > 1 ) {
_set_error ( " Invalid inheritance (unknown class + subclasses) " , p_class - > line ) ;
return ;
}
//if not found, try engine classes
if ( ! GDScriptLanguage : : get_singleton ( ) - > get_global_map ( ) . has ( base ) ) {
_set_error ( " Unknown class: ' " + base + " ' " , p_class - > line ) ;
return ;
}
native = base ;
}
}
if ( base_class ) {
p_class - > base_type . has_type = true ;
p_class - > base_type . kind = DataType : : CLASS ;
p_class - > base_type . class_type = base_class ;
} else if ( script . is_valid ( ) ) {
p_class - > base_type . has_type = true ;
p_class - > base_type . kind = DataType : : GDSCRIPT ;
p_class - > base_type . script_type = script ;
p_class - > base_type . native_type = script - > get_instance_base_type ( ) ;
} else if ( native ! = StringName ( ) ) {
p_class - > base_type . has_type = true ;
p_class - > base_type . kind = DataType : : NATIVE ;
p_class - > base_type . native_type = native ;
} else {
_set_error ( " Could not determine inheritance " , p_class - > line ) ;
return ;
}
} else {
// without extends, implicitly extend Reference
p_class - > base_type . has_type = true ;
p_class - > base_type . kind = DataType : : NATIVE ;
p_class - > base_type . native_type = " Reference " ;
}
// Recursively determine subclasses
for ( int i = 0 ; i < p_class - > subclasses . size ( ) ; i + + ) {
_determine_inheritance ( p_class - > subclasses [ i ] ) ;
}
}
2018-05-30 04:16:57 +02:00
String GDScriptParser : : DataType : : to_string ( ) const {
if ( ! has_type ) return " var " ;
switch ( kind ) {
case BUILTIN : {
if ( builtin_type = = Variant : : NIL ) return " null " ;
return Variant : : get_type_name ( builtin_type ) ;
} break ;
case NATIVE : {
if ( is_meta_type ) {
return " GDScriptNativeClass " ;
}
return native_type . operator String ( ) ;
} break ;
case GDSCRIPT : {
Ref < GDScript > gds = script_type ;
const String & gds_class = gds - > get_script_class_name ( ) ;
if ( ! gds_class . empty ( ) ) {
return gds_class ;
}
} // fallthrough
case SCRIPT : {
if ( is_meta_type ) {
return script_type - > get_class_name ( ) . operator String ( ) ;
}
String name = script_type - > get_name ( ) ;
if ( name ! = String ( ) ) {
return name ;
}
name = script_type - > get_path ( ) . get_file ( ) ;
if ( name ! = String ( ) ) {
return name ;
}
return native_type . operator String ( ) ;
} break ;
case CLASS : {
ERR_FAIL_COND_V ( ! class_type , String ( ) ) ;
if ( is_meta_type ) {
return " GDScript " ;
}
if ( class_type - > name = = StringName ( ) ) {
return " self " ;
}
return class_type - > name . operator String ( ) ;
} break ;
2018-09-26 13:13:56 +02:00
case UNRESOLVED : {
} break ;
2018-05-30 04:16:57 +02:00
}
return " Unresolved " ;
}
2018-05-30 04:16:51 +02:00
bool GDScriptParser : : _parse_type ( DataType & r_type , bool p_can_be_void ) {
tokenizer - > advance ( ) ;
r_type . has_type = true ;
2018-05-30 04:16:54 +02:00
bool finished = false ;
bool can_index = false ;
String full_name ;
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CURSOR ) {
completion_cursor = StringName ( ) ;
completion_type = COMPLETION_TYPE_HINT ;
completion_class = current_class ;
completion_function = current_function ;
completion_line = tokenizer - > get_token_line ( ) ;
completion_argument = 0 ;
completion_block = current_block ;
completion_found = true ;
completion_ident_is_call = p_can_be_void ;
tokenizer - > advance ( ) ;
}
2018-05-30 04:16:51 +02:00
switch ( tokenizer - > get_token ( ) ) {
case GDScriptTokenizer : : TK_PR_VOID : {
if ( ! p_can_be_void ) {
return false ;
}
2018-05-30 04:16:53 +02:00
r_type . kind = DataType : : BUILTIN ;
r_type . builtin_type = Variant : : NIL ;
} break ;
case GDScriptTokenizer : : TK_BUILT_IN_TYPE : {
r_type . builtin_type = tokenizer - > get_token_type ( ) ;
2018-05-30 04:16:54 +02:00
if ( tokenizer - > get_token_type ( ) = = Variant : : OBJECT ) {
r_type . kind = DataType : : NATIVE ;
r_type . native_type = " Object " ;
} else {
r_type . kind = DataType : : BUILTIN ;
}
2018-05-30 04:16:53 +02:00
} break ;
case GDScriptTokenizer : : TK_IDENTIFIER : {
2018-05-30 04:16:54 +02:00
r_type . native_type = tokenizer - > get_token_identifier ( ) ;
if ( ClassDB : : class_exists ( r_type . native_type ) | | ClassDB : : class_exists ( " _ " + r_type . native_type . operator String ( ) ) ) {
2018-05-30 04:16:53 +02:00
r_type . kind = DataType : : NATIVE ;
2018-05-30 04:16:54 +02:00
} else {
r_type . kind = DataType : : UNRESOLVED ;
can_index = true ;
full_name = r_type . native_type ;
2018-05-30 04:16:53 +02:00
}
2018-05-30 04:16:51 +02:00
} break ;
default : {
return false ;
}
}
tokenizer - > advance ( ) ;
2018-05-30 04:16:54 +02:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CURSOR ) {
completion_cursor = r_type . native_type ;
completion_type = COMPLETION_TYPE_HINT ;
completion_class = current_class ;
completion_function = current_function ;
completion_line = tokenizer - > get_token_line ( ) ;
completion_argument = 0 ;
completion_block = current_block ;
completion_found = true ;
completion_ident_is_call = p_can_be_void ;
tokenizer - > advance ( ) ;
}
2014-02-10 02:10:30 +01:00
2018-05-30 04:16:54 +02:00
if ( can_index ) {
while ( ! finished ) {
switch ( tokenizer - > get_token ( ) ) {
case GDScriptTokenizer : : TK_PERIOD : {
if ( ! can_index ) {
_set_error ( " Unexpected '.'. " ) ;
return false ;
}
can_index = false ;
tokenizer - > advance ( ) ;
} break ;
case GDScriptTokenizer : : TK_IDENTIFIER : {
if ( can_index ) {
_set_error ( " Unexpected identifier. " ) ;
return false ;
}
2014-02-10 02:10:30 +01:00
2018-05-30 04:16:54 +02:00
StringName id ;
bool has_completion = _get_completable_identifier ( COMPLETION_TYPE_HINT_INDEX , id ) ;
if ( id = = StringName ( ) ) {
id = " @temp " ;
}
full_name + = " . " + id . operator String ( ) ;
can_index = true ;
if ( has_completion ) {
completion_cursor = full_name ;
}
} break ;
default : {
finished = true ;
} break ;
}
}
if ( tokenizer - > get_token ( - 1 ) = = GDScriptTokenizer : : TK_PERIOD ) {
_set_error ( " Expected subclass identifier. " ) ;
return false ;
}
r_type . native_type = full_name ;
}
return true ;
}
GDScriptParser : : DataType GDScriptParser : : _resolve_type ( const DataType & p_source , int p_line ) {
if ( ! p_source . has_type ) return p_source ;
if ( p_source . kind ! = DataType : : UNRESOLVED ) return p_source ;
Vector < String > full_name = p_source . native_type . operator String ( ) . split ( " . " , false ) ;
int name_part = 0 ;
DataType result ;
result . has_type = true ;
while ( name_part < full_name . size ( ) ) {
bool found = false ;
StringName id = full_name [ name_part ] ;
DataType base_type = result ;
ClassNode * p = NULL ;
if ( name_part = = 0 ) {
if ( ScriptServer : : is_global_class ( id ) ) {
String script_path = ScriptServer : : get_global_class_path ( id ) ;
if ( script_path = = self_path ) {
result . kind = DataType : : CLASS ;
result . class_type = current_class ;
} else {
Ref < Script > script = ResourceLoader : : load ( script_path ) ;
Ref < GDScript > gds = script ;
if ( gds . is_valid ( ) ) {
if ( ! gds - > is_valid ( ) ) {
_set_error ( " Class ' " + id + " ' could not be fully loaded (script error or cyclic inheritance). " , p_line ) ;
return DataType ( ) ;
}
result . kind = DataType : : GDSCRIPT ;
result . script_type = gds ;
} else if ( script . is_valid ( ) ) {
result . kind = DataType : : SCRIPT ;
result . script_type = script ;
} else {
_set_error ( " Class ' " + id + " ' was found in global scope but its script could not be loaded. " , p_line ) ;
return DataType ( ) ;
}
}
name_part + + ;
continue ;
} else {
p = current_class ;
}
} else if ( base_type . kind = = DataType : : CLASS ) {
p = base_type . class_type ;
}
while ( p ) {
if ( p - > constant_expressions . has ( id ) ) {
if ( p - > constant_expressions [ id ] . expression - > type ! = Node : : TYPE_CONSTANT ) {
_set_error ( " Parser bug: unresolved constant. " , p_line ) ;
ERR_FAIL_V ( result ) ;
}
const ConstantNode * cn = static_cast < const ConstantNode * > ( p - > constant_expressions [ id ] . expression ) ;
Ref < GDScript > gds = cn - > value ;
if ( gds . is_valid ( ) ) {
result . kind = DataType : : GDSCRIPT ;
result . script_type = gds ;
found = true ;
} else {
Ref < Script > scr = cn - > value ;
if ( scr . is_valid ( ) ) {
result . kind = DataType : : SCRIPT ;
result . script_type = scr ;
found = true ;
}
}
break ;
}
// Inner classes
ClassNode * outer_class = p ;
while ( outer_class ) {
for ( int i = 0 ; i < outer_class - > subclasses . size ( ) ; i + + ) {
if ( outer_class - > subclasses [ i ] = = p ) {
continue ;
}
if ( outer_class - > subclasses [ i ] - > name = = id ) {
found = true ;
result . kind = DataType : : CLASS ;
result . class_type = outer_class - > subclasses [ i ] ;
break ;
}
}
if ( found ) {
break ;
}
outer_class = outer_class - > owner ;
}
if ( ! found & & p - > base_type . kind = = DataType : : CLASS ) {
p = p - > base_type . class_type ;
} else {
base_type = p - > base_type ;
break ;
}
}
// Still look for class constants in parent script
if ( ! found & & ( base_type . kind = = DataType : : GDSCRIPT | | base_type . kind = = DataType : : SCRIPT ) ) {
Ref < Script > scr = base_type . script_type ;
ERR_FAIL_COND_V ( scr . is_null ( ) , result ) ;
Map < StringName , Variant > constants ;
scr - > get_constants ( & constants ) ;
if ( constants . has ( id ) ) {
Ref < GDScript > gds = constants [ id ] ;
if ( gds . is_valid ( ) ) {
result . kind = DataType : : GDSCRIPT ;
result . script_type = gds ;
found = true ;
} else {
Ref < Script > scr = constants [ id ] ;
if ( scr . is_valid ( ) ) {
result . kind = DataType : : SCRIPT ;
result . script_type = scr ;
found = true ;
}
}
}
}
if ( ! found & & ! for_completion ) {
String base ;
if ( name_part = = 0 ) {
base = " self " ;
} else {
base = result . to_string ( ) ;
}
_set_error ( " Identifier ' " + String ( id ) + " ' is not a valid type (not a script or class), or could not be found on base ' " +
base + " '. " ,
p_line ) ;
return DataType ( ) ;
}
name_part + + ;
}
return result ;
}
GDScriptParser : : DataType GDScriptParser : : _type_from_variant ( const Variant & p_value ) const {
DataType result ;
result . has_type = true ;
result . is_constant = true ;
result . kind = DataType : : BUILTIN ;
result . builtin_type = p_value . get_type ( ) ;
if ( result . builtin_type = = Variant : : OBJECT ) {
Object * obj = p_value . operator Object * ( ) ;
if ( ! obj ) {
return DataType ( ) ;
}
result . native_type = obj - > get_class_name ( ) ;
Ref < Script > scr = p_value ;
if ( scr . is_valid ( ) ) {
result . is_meta_type = true ;
} else {
result . is_meta_type = false ;
scr = obj - > get_script ( ) ;
}
if ( scr . is_valid ( ) ) {
result . script_type = scr ;
Ref < GDScript > gds = scr ;
if ( gds . is_valid ( ) ) {
result . kind = DataType : : GDSCRIPT ;
} else {
result . kind = DataType : : SCRIPT ;
}
result . native_type = scr - > get_instance_base_type ( ) ;
} else {
result . kind = DataType : : NATIVE ;
}
}
return result ;
}
GDScriptParser : : DataType GDScriptParser : : _type_from_property ( const PropertyInfo & p_property , bool p_nil_is_variant ) const {
DataType ret ;
if ( p_property . type = = Variant : : NIL & & ( p_nil_is_variant | | ( p_property . usage & PROPERTY_USAGE_NIL_IS_VARIANT ) ) ) {
// Variant
return ret ;
}
ret . has_type = true ;
ret . builtin_type = p_property . type ;
if ( p_property . type = = Variant : : OBJECT ) {
ret . kind = DataType : : NATIVE ;
ret . native_type = p_property . class_name = = StringName ( ) ? " Object " : p_property . class_name ;
} else {
ret . kind = DataType : : BUILTIN ;
}
return ret ;
}
GDScriptParser : : DataType GDScriptParser : : _type_from_gdtype ( const GDScriptDataType & p_gdtype ) const {
DataType result ;
if ( ! p_gdtype . has_type ) {
return result ;
}
result . has_type = true ;
result . builtin_type = p_gdtype . builtin_type ;
result . native_type = p_gdtype . native_type ;
result . script_type = p_gdtype . script_type ;
switch ( p_gdtype . kind ) {
case GDScriptDataType : : BUILTIN : {
result . kind = DataType : : BUILTIN ;
} break ;
case GDScriptDataType : : NATIVE : {
result . kind = DataType : : NATIVE ;
} break ;
case GDScriptDataType : : GDSCRIPT : {
result . kind = DataType : : GDSCRIPT ;
} break ;
case GDScriptDataType : : SCRIPT : {
result . kind = DataType : : SCRIPT ;
} break ;
}
return result ;
}
GDScriptParser : : DataType GDScriptParser : : _get_operation_type ( const Variant : : Operator p_op , const DataType & p_a , const DataType & p_b , bool & r_valid ) const {
if ( ! p_a . has_type | | ! p_b . has_type ) {
r_valid = true ;
return DataType ( ) ;
}
Variant : : Type a_type = p_a . kind = = DataType : : BUILTIN ? p_a . builtin_type : Variant : : OBJECT ;
Variant : : Type b_type = p_b . kind = = DataType : : BUILTIN ? p_b . builtin_type : Variant : : OBJECT ;
Variant a ;
REF a_ref ;
if ( a_type = = Variant : : OBJECT ) {
a_ref . instance ( ) ;
a = a_ref ;
} else {
Variant : : CallError err ;
a = Variant : : construct ( a_type , NULL , 0 , err ) ;
if ( err . error ! = Variant : : CallError : : CALL_OK ) {
r_valid = false ;
return DataType ( ) ;
}
}
Variant b ;
REF b_ref ;
if ( b_type = = Variant : : OBJECT ) {
b_ref . instance ( ) ;
b = b_ref ;
} else {
Variant : : CallError err ;
b = Variant : : construct ( b_type , NULL , 0 , err ) ;
if ( err . error ! = Variant : : CallError : : CALL_OK ) {
r_valid = false ;
return DataType ( ) ;
}
}
// Avoid division by zero
if ( a_type = = Variant : : INT | | a_type = = Variant : : REAL ) {
Variant : : evaluate ( Variant : : OP_ADD , a , 1 , a , r_valid ) ;
}
if ( b_type = = Variant : : INT | | b_type = = Variant : : REAL ) {
Variant : : evaluate ( Variant : : OP_ADD , b , 1 , b , r_valid ) ;
}
2018-07-26 15:52:11 +02:00
if ( a_type = = Variant : : STRING & & b_type ! = Variant : : ARRAY ) {
2018-07-26 01:38:02 +02:00
a = " %s " ; // Work around for formatting operator (%)
}
2018-05-30 04:16:54 +02:00
Variant ret ;
Variant : : evaluate ( p_op , a , b , ret , r_valid ) ;
if ( r_valid ) {
return _type_from_variant ( ret ) ;
}
return DataType ( ) ;
}
Variant : : Operator GDScriptParser : : _get_variant_operation ( const OperatorNode : : Operator & p_op ) const {
switch ( p_op ) {
case OperatorNode : : OP_NEG : {
return Variant : : OP_NEGATE ;
} break ;
case OperatorNode : : OP_POS : {
return Variant : : OP_POSITIVE ;
} break ;
case OperatorNode : : OP_NOT : {
return Variant : : OP_NOT ;
} break ;
case OperatorNode : : OP_BIT_INVERT : {
return Variant : : OP_BIT_NEGATE ;
} break ;
case OperatorNode : : OP_IN : {
return Variant : : OP_IN ;
} break ;
case OperatorNode : : OP_EQUAL : {
return Variant : : OP_EQUAL ;
} break ;
case OperatorNode : : OP_NOT_EQUAL : {
return Variant : : OP_NOT_EQUAL ;
} break ;
case OperatorNode : : OP_LESS : {
return Variant : : OP_LESS ;
} break ;
case OperatorNode : : OP_LESS_EQUAL : {
return Variant : : OP_LESS_EQUAL ;
} break ;
case OperatorNode : : OP_GREATER : {
return Variant : : OP_GREATER ;
} break ;
case OperatorNode : : OP_GREATER_EQUAL : {
return Variant : : OP_GREATER_EQUAL ;
} break ;
case OperatorNode : : OP_AND : {
return Variant : : OP_AND ;
} break ;
case OperatorNode : : OP_OR : {
return Variant : : OP_OR ;
} break ;
case OperatorNode : : OP_ASSIGN_ADD :
case OperatorNode : : OP_ADD : {
return Variant : : OP_ADD ;
} break ;
case OperatorNode : : OP_ASSIGN_SUB :
case OperatorNode : : OP_SUB : {
return Variant : : OP_SUBTRACT ;
} break ;
case OperatorNode : : OP_ASSIGN_MUL :
case OperatorNode : : OP_MUL : {
return Variant : : OP_MULTIPLY ;
} break ;
case OperatorNode : : OP_ASSIGN_DIV :
case OperatorNode : : OP_DIV : {
return Variant : : OP_DIVIDE ;
} break ;
case OperatorNode : : OP_ASSIGN_MOD :
case OperatorNode : : OP_MOD : {
return Variant : : OP_MODULE ;
} break ;
case OperatorNode : : OP_ASSIGN_BIT_AND :
case OperatorNode : : OP_BIT_AND : {
return Variant : : OP_BIT_AND ;
} break ;
case OperatorNode : : OP_ASSIGN_BIT_OR :
case OperatorNode : : OP_BIT_OR : {
return Variant : : OP_BIT_OR ;
} break ;
case OperatorNode : : OP_ASSIGN_BIT_XOR :
case OperatorNode : : OP_BIT_XOR : {
return Variant : : OP_BIT_XOR ;
} break ;
case OperatorNode : : OP_ASSIGN_SHIFT_LEFT :
case OperatorNode : : OP_SHIFT_LEFT : {
return Variant : : OP_SHIFT_LEFT ;
}
case OperatorNode : : OP_ASSIGN_SHIFT_RIGHT :
case OperatorNode : : OP_SHIFT_RIGHT : {
return Variant : : OP_SHIFT_RIGHT ;
}
default : {
return Variant : : OP_MAX ;
} break ;
}
}
bool GDScriptParser : : _is_type_compatible ( const DataType & p_container , const DataType & p_expression , bool p_allow_implicit_conversion ) const {
// Ignore for completion
if ( ! check_types | | for_completion ) {
return true ;
}
// Can't test if not all have type
if ( ! p_container . has_type | | ! p_expression . has_type ) {
return true ;
}
// Should never get here unresolved
ERR_FAIL_COND_V ( p_container . kind = = DataType : : UNRESOLVED , false ) ;
ERR_FAIL_COND_V ( p_expression . kind = = DataType : : UNRESOLVED , false ) ;
if ( p_container . kind = = DataType : : BUILTIN & & p_expression . kind = = DataType : : BUILTIN ) {
bool valid = p_container . builtin_type = = p_expression . builtin_type ;
if ( p_allow_implicit_conversion ) {
2018-12-08 02:53:45 +01:00
valid = valid | | Variant : : can_convert_strict ( p_expression . builtin_type , p_container . builtin_type ) ;
2018-05-30 04:16:54 +02:00
}
return valid ;
}
2018-12-08 02:53:45 +01:00
if ( p_container . kind = = DataType : : BUILTIN & & p_container . builtin_type = = Variant : : OBJECT ) {
// Object built-in is a special case, it's compatible with any object and with null
if ( p_expression . kind = = DataType : : BUILTIN & & p_expression . builtin_type = = Variant : : NIL ) {
return true ;
}
if ( p_expression . kind = = DataType : : BUILTIN ) {
return false ;
}
// If it's not a built-in, must be an object
return true ;
}
2018-05-30 04:16:54 +02:00
if ( p_container . kind = = DataType : : BUILTIN | | ( p_expression . kind = = DataType : : BUILTIN & & p_expression . builtin_type ! = Variant : : NIL ) ) {
// Can't mix built-ins with objects
return false ;
}
// From now on everything is objects, check polymorphism
// The container must be the same class or a superclass of the expression
if ( p_expression . kind = = DataType : : BUILTIN & & p_expression . builtin_type = = Variant : : NIL ) {
// Null can be assigned to object types
return true ;
}
StringName expr_native ;
Ref < Script > expr_script ;
ClassNode * expr_class = NULL ;
switch ( p_expression . kind ) {
case DataType : : NATIVE : {
if ( p_container . kind ! = DataType : : NATIVE ) {
// Non-native type can't be a superclass of a native type
return false ;
}
if ( p_expression . is_meta_type ) {
expr_native = GDScriptNativeClass : : get_class_static ( ) ;
} else {
expr_native = p_expression . native_type ;
}
} break ;
case DataType : : SCRIPT :
case DataType : : GDSCRIPT : {
if ( p_container . kind = = DataType : : CLASS ) {
// This cannot be resolved without cyclic dependencies, so just bail out
return false ;
}
if ( p_expression . is_meta_type ) {
expr_native = p_expression . script_type - > get_class_name ( ) ;
} else {
expr_script = p_expression . script_type ;
expr_native = expr_script - > get_instance_base_type ( ) ;
}
} break ;
case DataType : : CLASS : {
if ( p_expression . is_meta_type ) {
expr_native = GDScript : : get_class_static ( ) ;
} else {
expr_class = p_expression . class_type ;
ClassNode * base = expr_class ;
while ( base - > base_type . kind = = DataType : : CLASS ) {
base = base - > base_type . class_type ;
}
expr_native = base - > base_type . native_type ;
expr_script = base - > base_type . script_type ;
}
2018-09-26 13:13:56 +02:00
} break ;
case DataType : : BUILTIN : // Already handled above
case DataType : : UNRESOLVED : // Not allowed, see above
break ;
2018-05-30 04:16:54 +02:00
}
switch ( p_container . kind ) {
case DataType : : NATIVE : {
if ( p_container . is_meta_type ) {
return ClassDB : : is_parent_class ( expr_native , GDScriptNativeClass : : get_class_static ( ) ) ;
} else {
return ClassDB : : is_parent_class ( expr_native , p_container . native_type ) ;
}
} break ;
case DataType : : SCRIPT :
case DataType : : GDSCRIPT : {
if ( p_container . is_meta_type ) {
return ClassDB : : is_parent_class ( expr_native , GDScript : : get_class_static ( ) ) ;
}
if ( expr_class = = head & & p_container . script_type - > get_path ( ) = = self_path ) {
// Special case: container is self script and expression is self
return true ;
}
while ( expr_script . is_valid ( ) ) {
if ( expr_script = = p_container . script_type ) {
return true ;
}
expr_script = expr_script - > get_base_script ( ) ;
}
return false ;
} break ;
case DataType : : CLASS : {
if ( p_container . is_meta_type ) {
return ClassDB : : is_parent_class ( expr_native , GDScript : : get_class_static ( ) ) ;
}
if ( p_container . class_type = = head & & expr_script . is_valid ( ) & & expr_script - > get_path ( ) = = self_path ) {
// Special case: container is self and expression is self script
return true ;
}
while ( expr_class ) {
if ( expr_class = = p_container . class_type ) {
return true ;
}
expr_class = expr_class - > base_type . class_type ;
}
return false ;
2018-09-26 13:13:56 +02:00
} break ;
case DataType : : BUILTIN : // Already handled above
case DataType : : UNRESOLVED : // Not allowed, see above
break ;
2018-05-30 04:16:54 +02:00
}
return false ;
}
GDScriptParser : : DataType GDScriptParser : : _reduce_node_type ( Node * p_node ) {
if ( p_node - > get_datatype ( ) . has_type ) {
return p_node - > get_datatype ( ) ;
}
DataType node_type ;
switch ( p_node - > type ) {
case Node : : TYPE_CONSTANT : {
node_type = _type_from_variant ( static_cast < ConstantNode * > ( p_node ) - > value ) ;
} break ;
2018-08-26 18:31:23 +02:00
case Node : : TYPE_TYPE : {
TypeNode * tn = static_cast < TypeNode * > ( p_node ) ;
node_type . has_type = true ;
node_type . is_meta_type = true ;
node_type . kind = DataType : : BUILTIN ;
node_type . builtin_type = tn - > vtype ;
} break ;
2018-05-30 04:16:54 +02:00
case Node : : TYPE_ARRAY : {
node_type . has_type = true ;
node_type . kind = DataType : : BUILTIN ;
node_type . builtin_type = Variant : : ARRAY ;
2018-07-01 18:17:40 +02:00
# ifdef DEBUG_ENABLED
// Check stuff inside the array
ArrayNode * an = static_cast < ArrayNode * > ( p_node ) ;
for ( int i = 0 ; i < an - > elements . size ( ) ; i + + ) {
_reduce_node_type ( an - > elements [ i ] ) ;
}
# endif // DEBUG_ENABLED
2018-05-30 04:16:54 +02:00
} break ;
case Node : : TYPE_DICTIONARY : {
node_type . has_type = true ;
node_type . kind = DataType : : BUILTIN ;
node_type . builtin_type = Variant : : DICTIONARY ;
2018-07-01 18:17:40 +02:00
# ifdef DEBUG_ENABLED
// Check stuff inside the dictionarty
DictionaryNode * dn = static_cast < DictionaryNode * > ( p_node ) ;
for ( int i = 0 ; i < dn - > elements . size ( ) ; i + + ) {
_reduce_node_type ( dn - > elements [ i ] . key ) ;
_reduce_node_type ( dn - > elements [ i ] . value ) ;
}
# endif // DEBUG_ENABLED
2018-05-30 04:16:54 +02:00
} break ;
case Node : : TYPE_SELF : {
node_type . has_type = true ;
node_type . kind = DataType : : CLASS ;
node_type . class_type = current_class ;
} break ;
case Node : : TYPE_IDENTIFIER : {
IdentifierNode * id = static_cast < IdentifierNode * > ( p_node ) ;
if ( id - > declared_block ) {
node_type = id - > declared_block - > variables [ id - > name ] - > get_datatype ( ) ;
2018-07-01 18:17:40 +02:00
id - > declared_block - > variables [ id - > name ] - > usages + = 1 ;
2018-05-30 04:16:54 +02:00
} else if ( id - > name = = " #match_value " ) {
// It's a special id just for the match statetement, ignore
break ;
} else if ( current_function & & current_function - > arguments . find ( id - > name ) > = 0 ) {
int idx = current_function - > arguments . find ( id - > name ) ;
node_type = current_function - > argument_types [ idx ] ;
} else {
node_type = _reduce_identifier_type ( NULL , id - > name , id - > line ) ;
}
} break ;
case Node : : TYPE_CAST : {
CastNode * cn = static_cast < CastNode * > ( p_node ) ;
DataType source_type = _reduce_node_type ( cn - > source_node ) ;
cn - > cast_type = _resolve_type ( cn - > cast_type , cn - > line ) ;
if ( source_type . has_type ) {
bool valid = false ;
if ( check_types ) {
if ( cn - > cast_type . kind = = DataType : : BUILTIN & & source_type . kind = = DataType : : BUILTIN ) {
valid = Variant : : can_convert ( source_type . builtin_type , cn - > cast_type . builtin_type ) ;
}
if ( cn - > cast_type . kind ! = DataType : : BUILTIN & & source_type . kind ! = DataType : : BUILTIN ) {
valid = _is_type_compatible ( cn - > cast_type , source_type ) | | _is_type_compatible ( source_type , cn - > cast_type ) ;
}
if ( ! valid ) {
_set_error ( " Invalid cast. Cannot convert from ' " + source_type . to_string ( ) +
" ' to ' " + cn - > cast_type . to_string ( ) + " '. " ,
cn - > line ) ;
return DataType ( ) ;
}
}
2018-06-05 18:50:21 +02:00
} else {
2018-07-01 18:17:40 +02:00
# ifdef DEBUG_ENABLED
_add_warning ( GDScriptWarning : : UNSAFE_CAST , cn - > line , cn - > cast_type . to_string ( ) ) ;
# endif // DEBUG_ENABLED
2018-06-05 18:50:21 +02:00
_mark_line_as_unsafe ( cn - > line ) ;
2018-05-30 04:16:54 +02:00
}
node_type = cn - > cast_type ;
} break ;
case Node : : TYPE_OPERATOR : {
OperatorNode * op = static_cast < OperatorNode * > ( p_node ) ;
switch ( op - > op ) {
case OperatorNode : : OP_CALL :
case OperatorNode : : OP_PARENT_CALL : {
node_type = _reduce_function_call_type ( op ) ;
} break ;
case OperatorNode : : OP_YIELD : {
if ( op - > arguments . size ( ) = = 2 ) {
DataType base_type = _reduce_node_type ( op - > arguments [ 0 ] ) ;
DataType signal_type = _reduce_node_type ( op - > arguments [ 1 ] ) ;
// TODO: Check if signal exists when it's a constant
if ( base_type . has_type & & base_type . kind = = DataType : : BUILTIN & & base_type . builtin_type ! = Variant : : NIL & & base_type . builtin_type ! = Variant : : OBJECT ) {
_set_error ( " First argument of 'yield()' must be an object. " , op - > line ) ;
return DataType ( ) ;
}
if ( signal_type . has_type & & ( signal_type . kind ! = DataType : : BUILTIN | | signal_type . builtin_type ! = Variant : : STRING ) ) {
_set_error ( " Second argument of 'yield()' must be a string. " , op - > line ) ;
return DataType ( ) ;
}
}
// yield can return anything
node_type . has_type = false ;
} break ;
2018-08-26 18:31:23 +02:00
case OperatorNode : : OP_IS :
case OperatorNode : : OP_IS_BUILTIN : {
2018-05-30 04:16:54 +02:00
if ( op - > arguments . size ( ) ! = 2 ) {
_set_error ( " Parser bug: binary operation without 2 arguments. " , op - > line ) ;
ERR_FAIL_V ( DataType ( ) ) ;
}
DataType value_type = _reduce_node_type ( op - > arguments [ 0 ] ) ;
DataType type_type = _reduce_node_type ( op - > arguments [ 1 ] ) ;
if ( check_types & & type_type . has_type ) {
if ( ! type_type . is_meta_type & & ( type_type . kind ! = DataType : : NATIVE | | ! ClassDB : : is_parent_class ( type_type . native_type , " Script " ) ) ) {
_set_error ( " Invalid 'is' test: right operand is not a type (not a native type nor a script). " , op - > line ) ;
return DataType ( ) ;
}
type_type . is_meta_type = false ; // Test the actual type
if ( ! _is_type_compatible ( type_type , value_type ) & & ! _is_type_compatible ( value_type , type_type ) ) {
2018-08-26 18:31:23 +02:00
if ( op - > op = = OperatorNode : : OP_IS ) {
_set_error ( " A value of type ' " + value_type . to_string ( ) + " ' will never be an instance of ' " + type_type . to_string ( ) + " '. " , op - > line ) ;
} else {
_set_error ( " A value of type ' " + value_type . to_string ( ) + " ' will never be of type ' " + type_type . to_string ( ) + " '. " , op - > line ) ;
}
2018-05-30 04:16:54 +02:00
return DataType ( ) ;
}
}
node_type . has_type = true ;
node_type . is_constant = true ;
node_type . is_meta_type = false ;
node_type . kind = DataType : : BUILTIN ;
node_type . builtin_type = Variant : : BOOL ;
} break ;
// Unary operators
case OperatorNode : : OP_NEG :
case OperatorNode : : OP_POS :
case OperatorNode : : OP_NOT :
case OperatorNode : : OP_BIT_INVERT : {
DataType argument_type = _reduce_node_type ( op - > arguments [ 0 ] ) ;
if ( ! argument_type . has_type ) {
break ;
}
Variant : : Operator var_op = _get_variant_operation ( op - > op ) ;
bool valid = false ;
node_type = _get_operation_type ( var_op , argument_type , argument_type , valid ) ;
if ( check_types & & ! valid ) {
_set_error ( " Invalid operand type (' " + argument_type . to_string ( ) +
" ') to unary operator ' " + Variant : : get_operator_name ( var_op ) + " '. " ,
op - > line , op - > column ) ;
return DataType ( ) ;
}
} break ;
// Binary operators
case OperatorNode : : OP_IN :
case OperatorNode : : OP_EQUAL :
case OperatorNode : : OP_NOT_EQUAL :
case OperatorNode : : OP_LESS :
case OperatorNode : : OP_LESS_EQUAL :
case OperatorNode : : OP_GREATER :
case OperatorNode : : OP_GREATER_EQUAL :
case OperatorNode : : OP_AND :
case OperatorNode : : OP_OR :
case OperatorNode : : OP_ADD :
case OperatorNode : : OP_SUB :
case OperatorNode : : OP_MUL :
case OperatorNode : : OP_DIV :
case OperatorNode : : OP_MOD :
case OperatorNode : : OP_SHIFT_LEFT :
case OperatorNode : : OP_SHIFT_RIGHT :
case OperatorNode : : OP_BIT_AND :
case OperatorNode : : OP_BIT_OR :
case OperatorNode : : OP_BIT_XOR : {
if ( op - > arguments . size ( ) ! = 2 ) {
_set_error ( " Parser bug: binary operation without 2 arguments. " , op - > line ) ;
ERR_FAIL_V ( DataType ( ) ) ;
}
DataType argument_a_type = _reduce_node_type ( op - > arguments [ 0 ] ) ;
DataType argument_b_type = _reduce_node_type ( op - > arguments [ 1 ] ) ;
if ( ! argument_a_type . has_type | | ! argument_b_type . has_type ) {
2018-06-05 18:50:21 +02:00
_mark_line_as_unsafe ( op - > line ) ;
2018-05-30 04:16:54 +02:00
break ;
}
Variant : : Operator var_op = _get_variant_operation ( op - > op ) ;
bool valid = false ;
node_type = _get_operation_type ( var_op , argument_a_type , argument_b_type , valid ) ;
if ( check_types & & ! valid ) {
_set_error ( " Invalid operand types (' " + argument_a_type . to_string ( ) + " ' and ' " +
argument_b_type . to_string ( ) + " ') to operator ' " + Variant : : get_operator_name ( var_op ) + " '. " ,
op - > line , op - > column ) ;
return DataType ( ) ;
}
2018-07-01 18:17:40 +02:00
# ifdef DEBUG_ENABLED
if ( var_op = = Variant : : OP_DIVIDE & & argument_a_type . has_type & & argument_a_type . kind = = DataType : : BUILTIN & & argument_a_type . builtin_type = = Variant : : INT & &
argument_b_type . has_type & & argument_b_type . kind = = DataType : : BUILTIN & & argument_b_type . builtin_type = = Variant : : INT ) {
_add_warning ( GDScriptWarning : : INTEGER_DIVISION , op - > line ) ;
}
# endif // DEBUG_ENABLED
2018-05-30 04:16:54 +02:00
} break ;
// Ternary operators
case OperatorNode : : OP_TERNARY_IF : {
if ( op - > arguments . size ( ) ! = 3 ) {
_set_error ( " Parser bug: ternary operation without 3 arguments " ) ;
ERR_FAIL_V ( DataType ( ) ) ;
}
DataType true_type = _reduce_node_type ( op - > arguments [ 1 ] ) ;
DataType false_type = _reduce_node_type ( op - > arguments [ 2 ] ) ;
// If types are equal, then the expression is of the same type
// If they are compatible, return the broader type
if ( true_type = = false_type | | _is_type_compatible ( true_type , false_type ) ) {
node_type = true_type ;
} else if ( _is_type_compatible ( false_type , true_type ) ) {
node_type = false_type ;
2018-07-01 18:17:40 +02:00
} else {
# ifdef DEBUG_ENABLED
_add_warning ( GDScriptWarning : : INCOMPATIBLE_TERNARY , op - > line ) ;
# endif // DEBUG_ENABLED
2018-05-30 04:16:54 +02:00
}
} break ;
// Assignment should never happen within an expression
case OperatorNode : : OP_ASSIGN :
case OperatorNode : : OP_ASSIGN_ADD :
case OperatorNode : : OP_ASSIGN_SUB :
case OperatorNode : : OP_ASSIGN_MUL :
case OperatorNode : : OP_ASSIGN_DIV :
case OperatorNode : : OP_ASSIGN_MOD :
case OperatorNode : : OP_ASSIGN_SHIFT_LEFT :
case OperatorNode : : OP_ASSIGN_SHIFT_RIGHT :
case OperatorNode : : OP_ASSIGN_BIT_AND :
case OperatorNode : : OP_ASSIGN_BIT_OR :
case OperatorNode : : OP_ASSIGN_BIT_XOR :
case OperatorNode : : OP_INIT_ASSIGN : {
_set_error ( " Assignment inside expression is not allowed (parser bug?). " , op - > line ) ;
return DataType ( ) ;
} break ;
case OperatorNode : : OP_INDEX_NAMED : {
if ( op - > arguments . size ( ) ! = 2 ) {
_set_error ( " Parser bug: named index with invalid arguments. " , op - > line ) ;
ERR_FAIL_V ( DataType ( ) ) ;
}
if ( op - > arguments [ 1 ] - > type ! = Node : : TYPE_IDENTIFIER ) {
_set_error ( " Parser bug: named index without identifier argument. " , op - > line ) ;
ERR_FAIL_V ( DataType ( ) ) ;
}
DataType base_type = _reduce_node_type ( op - > arguments [ 0 ] ) ;
IdentifierNode * member_id = static_cast < IdentifierNode * > ( op - > arguments [ 1 ] ) ;
if ( base_type . has_type ) {
if ( check_types & & base_type . kind = = DataType : : BUILTIN ) {
// Variant type, just test if it's possible
DataType result ;
switch ( base_type . builtin_type ) {
case Variant : : NIL :
case Variant : : DICTIONARY : {
result . has_type = false ;
} break ;
default : {
Variant : : CallError err ;
Variant temp = Variant : : construct ( base_type . builtin_type , NULL , 0 , err ) ;
bool valid = false ;
Variant res = temp . get ( member_id - > name . operator String ( ) , & valid ) ;
if ( valid ) {
result = _type_from_variant ( res ) ;
} else if ( check_types ) {
_set_error ( " Can't get index ' " + String ( member_id - > name . operator String ( ) ) + " ' on base ' " +
base_type . to_string ( ) + " '. " ,
op - > line ) ;
return DataType ( ) ;
}
} break ;
}
result . is_constant = false ;
node_type = result ;
} else {
node_type = _reduce_identifier_type ( & base_type , member_id - > name , op - > line ) ;
2018-07-01 18:17:40 +02:00
# ifdef DEBUG_ENABLED
if ( ! node_type . has_type ) {
_add_warning ( GDScriptWarning : : UNSAFE_PROPERTY_ACCESS , op - > line , member_id - > name . operator String ( ) , base_type . to_string ( ) ) ;
}
# endif // DEBUG_ENABLED
2018-05-30 04:16:54 +02:00
}
2018-06-05 18:50:21 +02:00
} else {
_mark_line_as_unsafe ( op - > line ) ;
2018-05-30 04:16:54 +02:00
}
if ( error_set ) {
return DataType ( ) ;
}
} break ;
case OperatorNode : : OP_INDEX : {
if ( op - > arguments [ 1 ] - > type = = Node : : TYPE_CONSTANT ) {
ConstantNode * cn = static_cast < ConstantNode * > ( op - > arguments [ 1 ] ) ;
if ( cn - > value . get_type ( ) = = Variant : : STRING ) {
// Treat this as named indexing
IdentifierNode * id = alloc_node < IdentifierNode > ( ) ;
id - > name = cn - > value . operator StringName ( ) ;
op - > op = OperatorNode : : OP_INDEX_NAMED ;
2018-07-25 03:11:03 +02:00
op - > arguments . write [ 1 ] = id ;
2018-05-30 04:16:54 +02:00
return _reduce_node_type ( op ) ;
}
}
DataType base_type = _reduce_node_type ( op - > arguments [ 0 ] ) ;
DataType index_type = _reduce_node_type ( op - > arguments [ 1 ] ) ;
if ( ! base_type . has_type ) {
2018-06-05 18:50:21 +02:00
_mark_line_as_unsafe ( op - > line ) ;
2018-05-30 04:16:54 +02:00
break ;
}
if ( check_types & & index_type . has_type ) {
if ( base_type . kind = = DataType : : BUILTIN ) {
// Check if indexing is valid
bool error = index_type . kind ! = DataType : : BUILTIN ;
if ( ! error ) {
switch ( base_type . builtin_type ) {
// Expect int or real as index
case Variant : : POOL_BYTE_ARRAY :
case Variant : : POOL_COLOR_ARRAY :
case Variant : : POOL_INT_ARRAY :
case Variant : : POOL_REAL_ARRAY :
case Variant : : POOL_STRING_ARRAY :
case Variant : : POOL_VECTOR2_ARRAY :
case Variant : : POOL_VECTOR3_ARRAY :
case Variant : : ARRAY :
case Variant : : STRING : {
error = index_type . builtin_type ! = Variant : : INT & & index_type . builtin_type ! = Variant : : REAL ;
} break ;
// Expect String only
case Variant : : RECT2 :
case Variant : : PLANE :
case Variant : : QUAT :
case Variant : : AABB :
case Variant : : OBJECT : {
error = index_type . builtin_type ! = Variant : : STRING ;
} break ;
// Expect String or number
case Variant : : VECTOR2 :
case Variant : : VECTOR3 :
case Variant : : TRANSFORM2D :
case Variant : : BASIS :
case Variant : : TRANSFORM : {
error = index_type . builtin_type ! = Variant : : INT & & index_type . builtin_type ! = Variant : : REAL & &
index_type . builtin_type ! = Variant : : STRING ;
} break ;
// Expect String or int
case Variant : : COLOR : {
error = index_type . builtin_type ! = Variant : : INT & & index_type . builtin_type ! = Variant : : STRING ;
} break ;
2018-09-26 13:13:56 +02:00
default : { }
2018-05-30 04:16:54 +02:00
}
}
if ( error ) {
_set_error ( " Invalid index type ( " + index_type . to_string ( ) + " ) for base ' " + base_type . to_string ( ) + " '. " ,
op - > line ) ;
return DataType ( ) ;
}
if ( op - > arguments [ 1 ] - > type = = GDScriptParser : : Node : : TYPE_CONSTANT ) {
ConstantNode * cn = static_cast < ConstantNode * > ( op - > arguments [ 1 ] ) ;
// Index is a constant, just try it if possible
switch ( base_type . builtin_type ) {
// Arrays/string have variable indexing, can't test directly
case Variant : : STRING :
case Variant : : ARRAY :
case Variant : : DICTIONARY :
case Variant : : POOL_BYTE_ARRAY :
case Variant : : POOL_COLOR_ARRAY :
case Variant : : POOL_INT_ARRAY :
case Variant : : POOL_REAL_ARRAY :
case Variant : : POOL_STRING_ARRAY :
case Variant : : POOL_VECTOR2_ARRAY :
case Variant : : POOL_VECTOR3_ARRAY : {
break ;
}
default : {
Variant : : CallError err ;
Variant temp = Variant : : construct ( base_type . builtin_type , NULL , 0 , err ) ;
bool valid = false ;
Variant res = temp . get ( cn - > value , & valid ) ;
if ( valid ) {
node_type = _type_from_variant ( res ) ;
node_type . is_constant = false ;
} else if ( check_types ) {
_set_error ( " Can't get index ' " + String ( cn - > value ) + " ' on base ' " +
base_type . to_string ( ) + " '. " ,
op - > line ) ;
return DataType ( ) ;
}
} break ;
}
2018-06-05 18:50:21 +02:00
} else {
_mark_line_as_unsafe ( op - > line ) ;
2018-05-30 04:16:54 +02:00
}
} else if ( ! for_completion & & ( index_type . kind ! = DataType : : BUILTIN | | index_type . builtin_type ! = Variant : : STRING ) ) {
_set_error ( " Only strings can be used as index in the base type ' " + base_type . to_string ( ) + " '. " , op - > line ) ;
return DataType ( ) ;
}
}
if ( check_types & & ! node_type . has_type ) {
// Can infer indexing type for some variant types
DataType result ;
result . has_type = true ;
result . kind = DataType : : BUILTIN ;
switch ( base_type . builtin_type ) {
// Can't index at all
case Variant : : NIL :
case Variant : : BOOL :
case Variant : : INT :
case Variant : : REAL :
case Variant : : NODE_PATH :
case Variant : : _RID : {
_set_error ( " Can't index on a value of type ' " + base_type . to_string ( ) + " '. " , op - > line ) ;
return DataType ( ) ;
} break ;
// Return int
case Variant : : POOL_BYTE_ARRAY :
case Variant : : POOL_INT_ARRAY : {
result . builtin_type = Variant : : INT ;
} break ;
// Return real
case Variant : : POOL_REAL_ARRAY :
case Variant : : VECTOR2 :
case Variant : : VECTOR3 :
case Variant : : QUAT : {
result . builtin_type = Variant : : REAL ;
} break ;
// Return color
case Variant : : POOL_COLOR_ARRAY : {
result . builtin_type = Variant : : COLOR ;
} break ;
// Return string
case Variant : : POOL_STRING_ARRAY :
case Variant : : STRING : {
result . builtin_type = Variant : : STRING ;
} break ;
// Return Vector2
case Variant : : POOL_VECTOR2_ARRAY :
case Variant : : TRANSFORM2D :
case Variant : : RECT2 : {
result . builtin_type = Variant : : VECTOR2 ;
} break ;
// Return Vector3
case Variant : : POOL_VECTOR3_ARRAY :
case Variant : : AABB :
case Variant : : BASIS : {
result . builtin_type = Variant : : VECTOR3 ;
} break ;
// Depends on the index
case Variant : : TRANSFORM :
case Variant : : PLANE :
case Variant : : COLOR :
default : {
result . has_type = false ;
} break ;
}
node_type = result ;
}
} break ;
default : {
_set_error ( " Parser bug: unhandled operation. " , op - > line ) ;
ERR_FAIL_V ( DataType ( ) ) ;
}
}
} break ;
2018-09-26 13:13:56 +02:00
default : { }
2018-05-30 04:16:54 +02:00
}
p_node - > set_datatype ( _resolve_type ( node_type , p_node - > line ) ) ;
return node_type ;
}
bool GDScriptParser : : _get_function_signature ( DataType & p_base_type , const StringName & p_function , DataType & r_return_type , List < DataType > & r_arg_types , int & r_default_arg_count , bool & r_static , bool & r_vararg ) const {
r_static = false ;
r_default_arg_count = 0 ;
DataType original_type = p_base_type ;
ClassNode * base = NULL ;
FunctionNode * callee = NULL ;
if ( p_base_type . kind = = DataType : : CLASS ) {
base = p_base_type . class_type ;
}
// Look up the current file (parse tree)
while ( ! callee & & base ) {
for ( int i = 0 ; i < base - > static_functions . size ( ) ; i + + ) {
FunctionNode * func = base - > static_functions [ i ] ;
if ( p_function = = func - > name ) {
r_static = true ;
callee = func ;
break ;
}
}
if ( ! callee & & ! p_base_type . is_meta_type ) {
for ( int i = 0 ; i < base - > functions . size ( ) ; i + + ) {
FunctionNode * func = base - > functions [ i ] ;
if ( p_function = = func - > name ) {
callee = func ;
break ;
}
}
}
p_base_type = base - > base_type ;
if ( p_base_type . kind = = DataType : : CLASS ) {
base = p_base_type . class_type ;
} else {
break ;
}
}
if ( callee ) {
r_return_type = callee - > get_datatype ( ) ;
for ( int i = 0 ; i < callee - > argument_types . size ( ) ; i + + ) {
r_arg_types . push_back ( callee - > argument_types [ i ] ) ;
}
r_default_arg_count = callee - > default_values . size ( ) ;
return true ;
}
// Nothing in current file, check parent script
Ref < GDScript > base_gdscript ;
Ref < Script > base_script ;
StringName native ;
if ( p_base_type . kind = = DataType : : GDSCRIPT ) {
base_gdscript = p_base_type . script_type ;
2018-11-18 02:23:40 +01:00
if ( base_gdscript . is_null ( ) | | ! base_gdscript - > is_valid ( ) ) {
// GDScript wasn't properly compíled, don't bother trying
return false ;
}
2018-05-30 04:16:54 +02:00
} else if ( p_base_type . kind = = DataType : : SCRIPT ) {
base_script = p_base_type . script_type ;
} else if ( p_base_type . kind = = DataType : : NATIVE ) {
native = p_base_type . native_type ;
}
while ( base_gdscript . is_valid ( ) ) {
native = base_gdscript - > get_instance_base_type ( ) ;
Map < StringName , GDScriptFunction * > funcs = base_gdscript - > get_member_functions ( ) ;
if ( funcs . has ( p_function ) ) {
GDScriptFunction * f = funcs [ p_function ] ;
r_static = f - > is_static ( ) ;
r_default_arg_count = f - > get_default_argument_count ( ) ;
r_return_type = _type_from_gdtype ( f - > get_return_type ( ) ) ;
for ( int i = 0 ; i < f - > get_argument_count ( ) ; i + + ) {
r_arg_types . push_back ( _type_from_gdtype ( f - > get_argument_type ( i ) ) ) ;
}
return true ;
}
base_gdscript = base_gdscript - > get_base_script ( ) ;
}
while ( base_script . is_valid ( ) ) {
native = base_script - > get_instance_base_type ( ) ;
MethodInfo mi = base_script - > get_method_info ( p_function ) ;
if ( ! ( mi = = MethodInfo ( ) ) ) {
r_return_type = _type_from_property ( mi . return_val , false ) ;
r_default_arg_count = mi . default_arguments . size ( ) ;
for ( List < PropertyInfo > : : Element * E = mi . arguments . front ( ) ; E ; E = E - > next ( ) ) {
r_arg_types . push_back ( _type_from_property ( E - > get ( ) ) ) ;
}
return true ;
}
base_script = base_script - > get_base_script ( ) ;
}
2018-11-18 02:23:40 +01:00
if ( native = = StringName ( ) ) {
// Empty native class, might happen in some Script implementations
// Just ignore it
return false ;
}
2018-05-30 04:16:54 +02:00
# ifdef DEBUG_METHODS_ENABLED
// Only native remains
if ( ! ClassDB : : class_exists ( native ) ) {
native = " _ " + native . operator String ( ) ;
}
if ( ! ClassDB : : class_exists ( native ) ) {
if ( ! check_types ) return false ;
ERR_EXPLAIN ( " Parser bug: Class ' " + String ( native ) + " ' not found. " ) ;
ERR_FAIL_V ( false ) ;
}
MethodBind * method = ClassDB : : get_method ( native , p_function ) ;
if ( ! method ) {
// Try virtual methods
List < MethodInfo > virtuals ;
ClassDB : : get_virtual_methods ( native , & virtuals ) ;
for ( const List < MethodInfo > : : Element * E = virtuals . front ( ) ; E ; E = E - > next ( ) ) {
const MethodInfo & mi = E - > get ( ) ;
if ( mi . name = = p_function ) {
r_default_arg_count = mi . default_arguments . size ( ) ;
for ( const List < PropertyInfo > : : Element * pi = mi . arguments . front ( ) ; pi ; pi = pi - > next ( ) ) {
r_arg_types . push_back ( _type_from_property ( pi - > get ( ) ) ) ;
}
r_return_type = _type_from_property ( mi . return_val , false ) ;
r_vararg = mi . flags & METHOD_FLAG_VARARG ;
return true ;
}
}
// If the base is a script, it might be trying to access members of the Script class itself
if ( original_type . is_meta_type & & ! ( p_function = = " new " ) & & ( original_type . kind = = DataType : : SCRIPT | | original_type . kind = = DataType : : GDSCRIPT ) ) {
method = ClassDB : : get_method ( original_type . script_type - > get_class_name ( ) , p_function ) ;
if ( method ) {
r_static = true ;
} else {
// Try virtual methods of the script type
virtuals . clear ( ) ;
ClassDB : : get_virtual_methods ( original_type . script_type - > get_class_name ( ) , & virtuals ) ;
for ( const List < MethodInfo > : : Element * E = virtuals . front ( ) ; E ; E = E - > next ( ) ) {
const MethodInfo & mi = E - > get ( ) ;
if ( mi . name = = p_function ) {
r_default_arg_count = mi . default_arguments . size ( ) ;
for ( const List < PropertyInfo > : : Element * pi = mi . arguments . front ( ) ; pi ; pi = pi - > next ( ) ) {
r_arg_types . push_back ( _type_from_property ( pi - > get ( ) ) ) ;
}
r_return_type = _type_from_property ( mi . return_val , false ) ;
r_static = true ;
r_vararg = mi . flags & METHOD_FLAG_VARARG ;
return true ;
}
}
return false ;
}
} else {
return false ;
}
}
r_default_arg_count = method - > get_default_argument_count ( ) ;
r_return_type = _type_from_property ( method - > get_return_info ( ) , false ) ;
r_vararg = method - > is_vararg ( ) ;
for ( int i = 0 ; i < method - > get_argument_count ( ) ; i + + ) {
r_arg_types . push_back ( _type_from_property ( method - > get_argument_info ( i ) ) ) ;
}
return true ;
# else
return false ;
# endif
}
GDScriptParser : : DataType GDScriptParser : : _reduce_function_call_type ( const OperatorNode * p_call ) {
if ( p_call - > arguments . size ( ) < 1 ) {
_set_error ( " Parser bug: function call without enough arguments. " , p_call - > line ) ;
ERR_FAIL_V ( DataType ( ) ) ;
}
DataType return_type ;
List < DataType > arg_types ;
int default_args_count = 0 ;
int arg_count = p_call - > arguments . size ( ) ;
String callee_name ;
bool is_vararg = false ;
switch ( p_call - > arguments [ 0 ] - > type ) {
case GDScriptParser : : Node : : TYPE_TYPE : {
// Built-in constructor, special case
TypeNode * tn = static_cast < TypeNode * > ( p_call - > arguments [ 0 ] ) ;
Vector < DataType > par_types ;
par_types . resize ( p_call - > arguments . size ( ) - 1 ) ;
for ( int i = 1 ; i < p_call - > arguments . size ( ) ; i + + ) {
2018-07-25 03:11:03 +02:00
par_types . write [ i - 1 ] = _reduce_node_type ( p_call - > arguments [ i ] ) ;
2018-05-30 04:16:54 +02:00
}
if ( error_set ) return DataType ( ) ;
bool match = false ;
List < MethodInfo > constructors ;
Variant : : get_constructor_list ( tn - > vtype , & constructors ) ;
PropertyInfo return_type ;
for ( List < MethodInfo > : : Element * E = constructors . front ( ) ; E ; E = E - > next ( ) ) {
MethodInfo & mi = E - > get ( ) ;
if ( p_call - > arguments . size ( ) - 1 < mi . arguments . size ( ) - mi . default_arguments . size ( ) ) {
continue ;
}
if ( p_call - > arguments . size ( ) - 1 > mi . arguments . size ( ) ) {
continue ;
}
bool types_match = true ;
for ( int i = 0 ; i < par_types . size ( ) ; i + + ) {
DataType arg_type ;
if ( mi . arguments [ i ] . type ! = Variant : : NIL ) {
arg_type . has_type = true ;
arg_type . kind = mi . arguments [ i ] . type = = Variant : : OBJECT ? DataType : : NATIVE : DataType : : BUILTIN ;
arg_type . builtin_type = mi . arguments [ i ] . type ;
arg_type . native_type = mi . arguments [ i ] . class_name ;
}
if ( ! _is_type_compatible ( arg_type , par_types [ i ] , true ) ) {
types_match = false ;
break ;
2018-07-01 18:17:40 +02:00
} else {
# ifdef DEBUG_ENABLED
if ( arg_type . kind = = DataType : : BUILTIN & & arg_type . builtin_type = = Variant : : INT & & par_types [ i ] . kind = = DataType : : BUILTIN & & par_types [ i ] . builtin_type = = Variant : : REAL ) {
_add_warning ( GDScriptWarning : : NARROWING_CONVERSION , p_call - > line , Variant : : get_type_name ( tn - > vtype ) ) ;
}
if ( par_types [ i ] . may_yield & & p_call - > arguments [ i + 1 ] - > type = = Node : : TYPE_OPERATOR ) {
_add_warning ( GDScriptWarning : : FUNCTION_MAY_YIELD , p_call - > line , _find_function_name ( static_cast < OperatorNode * > ( p_call - > arguments [ i + 1 ] ) ) ) ;
}
# endif // DEBUG_ENABLED
2018-05-30 04:16:54 +02:00
}
}
if ( types_match ) {
match = true ;
return_type = mi . return_val ;
break ;
}
}
if ( match ) {
return _type_from_property ( return_type , false ) ;
} else if ( check_types ) {
String err = " No constructor of ' " ;
err + = Variant : : get_type_name ( tn - > vtype ) ;
err + = " ' matches the signature ' " ;
err + = Variant : : get_type_name ( tn - > vtype ) + " ( " ;
for ( int i = 0 ; i < par_types . size ( ) ; i + + ) {
if ( i > 0 ) err + = " , " ;
err + = par_types [ i ] . to_string ( ) ;
}
err + = " )'. " ;
_set_error ( err , p_call - > line , p_call - > column ) ;
return DataType ( ) ;
}
return DataType ( ) ;
} break ;
case GDScriptParser : : Node : : TYPE_BUILT_IN_FUNCTION : {
BuiltInFunctionNode * func = static_cast < BuiltInFunctionNode * > ( p_call - > arguments [ 0 ] ) ;
MethodInfo mi = GDScriptFunctions : : get_info ( func - > function ) ;
return_type = _type_from_property ( mi . return_val , false ) ;
2018-07-01 18:17:40 +02:00
# ifdef DEBUG_ENABLED
// Check all arguments beforehand to solve warnings
for ( int i = 1 ; i < p_call - > arguments . size ( ) ; i + + ) {
_reduce_node_type ( p_call - > arguments [ i ] ) ;
}
# endif // DEBUG_ENABLED
2018-05-30 04:16:54 +02:00
// Check arguments
is_vararg = mi . flags & METHOD_FLAG_VARARG ;
default_args_count = mi . default_arguments . size ( ) ;
callee_name = mi . name ;
arg_count - = 1 ;
// Check each argument type
for ( List < PropertyInfo > : : Element * E = mi . arguments . front ( ) ; E ; E = E - > next ( ) ) {
arg_types . push_back ( _type_from_property ( E - > get ( ) ) ) ;
}
} break ;
default : {
if ( p_call - > op = = OperatorNode : : OP_CALL & & p_call - > arguments . size ( ) < 2 ) {
_set_error ( " Parser bug: self method call without enough arguments. " , p_call - > line ) ;
ERR_FAIL_V ( DataType ( ) ) ;
}
int arg_id = p_call - > op = = OperatorNode : : OP_CALL ? 1 : 0 ;
if ( p_call - > arguments [ arg_id ] - > type ! = Node : : TYPE_IDENTIFIER ) {
_set_error ( " Parser bug: invalid function call argument. " , p_call - > line ) ;
ERR_FAIL_V ( DataType ( ) ) ;
}
2018-07-01 18:17:40 +02:00
# ifdef DEBUG_ENABLED
// Check all arguments beforehand to solve warnings
for ( int i = arg_id + 1 ; i < p_call - > arguments . size ( ) ; i + + ) {
_reduce_node_type ( p_call - > arguments [ i ] ) ;
}
# endif // DEBUG_ENABLED
2018-05-30 04:16:54 +02:00
IdentifierNode * func_id = static_cast < IdentifierNode * > ( p_call - > arguments [ arg_id ] ) ;
callee_name = func_id - > name ;
arg_count - = 1 + arg_id ;
DataType base_type ;
if ( p_call - > op = = OperatorNode : : OP_PARENT_CALL ) {
base_type = current_class - > base_type ;
} else {
base_type = _reduce_node_type ( p_call - > arguments [ 0 ] ) ;
}
if ( ! base_type . has_type | | ( base_type . kind = = DataType : : BUILTIN & & base_type . builtin_type = = Variant : : NIL ) ) {
2018-06-05 18:50:21 +02:00
_mark_line_as_unsafe ( p_call - > line ) ;
2018-05-30 04:16:54 +02:00
return DataType ( ) ;
}
if ( base_type . kind = = DataType : : BUILTIN ) {
Variant : : CallError err ;
Variant tmp = Variant : : construct ( base_type . builtin_type , NULL , 0 , err ) ;
if ( check_types ) {
if ( ! tmp . has_method ( callee_name ) ) {
2018-06-05 18:50:21 +02:00
_set_error ( " Method ' " + callee_name + " ' is not declared on base ' " + base_type . to_string ( ) + " '. " , p_call - > line ) ;
2018-05-30 04:16:54 +02:00
return DataType ( ) ;
}
default_args_count = Variant : : get_method_default_arguments ( base_type . builtin_type , callee_name ) . size ( ) ;
const Vector < Variant : : Type > & var_arg_types = Variant : : get_method_argument_types ( base_type . builtin_type , callee_name ) ;
for ( int i = 0 ; i < var_arg_types . size ( ) ; i + + ) {
DataType argtype ;
if ( var_arg_types [ i ] ! = Variant : : NIL ) {
argtype . has_type = true ;
argtype . kind = DataType : : BUILTIN ;
argtype . builtin_type = var_arg_types [ i ] ;
}
arg_types . push_back ( argtype ) ;
}
}
2018-10-10 00:50:47 +02:00
bool rets = false ;
2018-05-30 04:16:54 +02:00
return_type . has_type = true ;
return_type . kind = DataType : : BUILTIN ;
2018-10-10 00:50:47 +02:00
return_type . builtin_type = Variant : : get_method_return_type ( base_type . builtin_type , callee_name , & rets ) ;
// If the method returns, but it might return any type, (Variant::NIL), pretend we don't know the type.
// At least make sure we know that it returns
if ( rets & & return_type . builtin_type = = Variant : : NIL ) {
return_type . has_type = false ;
}
2018-05-30 04:16:54 +02:00
break ;
}
DataType original_type = base_type ;
bool is_initializer = callee_name = = " new " ;
bool is_static = false ;
bool valid = false ;
if ( is_initializer & & original_type . is_meta_type ) {
// Try to check it as initializer
base_type = original_type ;
callee_name = " _init " ;
base_type . is_meta_type = false ;
valid = _get_function_signature ( base_type , callee_name , return_type , arg_types ,
default_args_count , is_static , is_vararg ) ;
if ( valid ) {
return_type = original_type ;
return_type . is_meta_type = false ;
}
}
if ( ! valid ) {
base_type = original_type ;
return_type = DataType ( ) ;
valid = _get_function_signature ( base_type , callee_name , return_type , arg_types ,
default_args_count , is_static , is_vararg ) ;
}
if ( ! valid ) {
2018-06-19 07:55:52 +02:00
# ifdef DEBUG_ENABLED
2018-05-30 04:16:54 +02:00
if ( p_call - > arguments [ 0 ] - > type = = Node : : TYPE_SELF ) {
_set_error ( " Method ' " + callee_name + " ' is not declared in the current class. " , p_call - > line ) ;
return DataType ( ) ;
}
2018-07-01 18:17:40 +02:00
DataType tmp_type ;
valid = _get_member_type ( original_type , func_id - > name , tmp_type ) ;
if ( valid ) {
if ( tmp_type . is_constant ) {
_add_warning ( GDScriptWarning : : CONSTANT_USED_AS_FUNCTION , p_call - > line , callee_name , original_type . to_string ( ) ) ;
} else {
_add_warning ( GDScriptWarning : : PROPERTY_USED_AS_FUNCTION , p_call - > line , callee_name , original_type . to_string ( ) ) ;
}
}
_add_warning ( GDScriptWarning : : UNSAFE_METHOD_ACCESS , p_call - > line , callee_name , original_type . to_string ( ) ) ;
2018-05-30 04:16:54 +02:00
_mark_line_as_unsafe ( p_call - > line ) ;
2018-07-01 18:17:40 +02:00
# endif // DEBUG_ENABLED
2018-05-30 04:16:54 +02:00
return DataType ( ) ;
}
2018-06-19 07:55:52 +02:00
# ifdef DEBUG_ENABLED
2018-05-30 04:16:54 +02:00
if ( current_function & & ! for_completion & & ! is_static & & p_call - > arguments [ 0 ] - > type = = Node : : TYPE_SELF & & current_function - > _static ) {
if ( current_function & & current_function - > _static & & p_call - > arguments [ 0 ] - > type = = Node : : TYPE_SELF ) {
_set_error ( " Can't call non-static function from a static function. " , p_call - > line ) ;
return DataType ( ) ;
}
}
if ( check_types & & ! is_static & & ! is_initializer & & base_type . is_meta_type ) {
_set_error ( " Non-static function ' " + String ( callee_name ) + " ' can only be called from an instance. " , p_call - > line ) ;
return DataType ( ) ;
}
2018-07-01 18:17:40 +02:00
// Check signal emission for warnings
if ( callee_name = = " emit_signal " & & p_call - > op = = OperatorNode : : OP_CALL & & p_call - > arguments [ 0 ] - > type = = Node : : TYPE_SELF & & p_call - > arguments . size ( ) > = 3 & & p_call - > arguments [ 2 ] - > type = = Node : : TYPE_CONSTANT ) {
ConstantNode * sig = static_cast < ConstantNode * > ( p_call - > arguments [ 2 ] ) ;
String emitted = sig - > value . get_type ( ) = = Variant : : STRING ? sig - > value . operator String ( ) : " " ;
for ( int i = 0 ; i < current_class - > _signals . size ( ) ; i + + ) {
if ( current_class - > _signals [ i ] . name = = emitted ) {
current_class - > _signals . write [ i ] . emissions + = 1 ;
break ;
}
}
}
# endif // DEBUG_ENABLED
2018-05-30 04:16:54 +02:00
} break ;
}
if ( ! check_types ) {
return return_type ;
}
if ( arg_count < arg_types . size ( ) - default_args_count ) {
_set_error ( " Too few arguments for ' " + callee_name + " ()' call. Expected at least " + itos ( arg_types . size ( ) - default_args_count ) + " . " , p_call - > line ) ;
return return_type ;
}
if ( ! is_vararg & & arg_count > arg_types . size ( ) ) {
_set_error ( " Too many arguments for ' " + callee_name + " ()' call. Expected at most " + itos ( arg_types . size ( ) ) + " . " , p_call - > line ) ;
return return_type ;
}
int arg_diff = p_call - > arguments . size ( ) - arg_count ;
for ( int i = arg_diff ; i < p_call - > arguments . size ( ) ; i + + ) {
DataType par_type = _reduce_node_type ( p_call - > arguments [ i ] ) ;
if ( ( i - arg_diff ) > = arg_types . size ( ) ) {
continue ;
}
2018-07-01 18:17:40 +02:00
DataType arg_type = arg_types [ i - arg_diff ] ;
2018-06-05 18:50:21 +02:00
if ( ! par_type . has_type ) {
_mark_line_as_unsafe ( p_call - > line ) ;
2018-07-01 18:17:40 +02:00
# ifdef DEBUG_ENABLED
if ( par_type . may_yield & & p_call - > arguments [ i ] - > type = = Node : : TYPE_OPERATOR ) {
_add_warning ( GDScriptWarning : : FUNCTION_MAY_YIELD , p_call - > line , _find_function_name ( static_cast < OperatorNode * > ( p_call - > arguments [ i ] ) ) ) ;
}
# endif // DEBUG_ENABLED
2018-06-05 18:50:21 +02:00
} else if ( ! _is_type_compatible ( arg_types [ i - arg_diff ] , par_type , true ) ) {
// Supertypes are acceptable for dynamic compliance
if ( ! _is_type_compatible ( par_type , arg_types [ i - arg_diff ] ) ) {
_set_error ( " At ' " + callee_name + " ()' call, argument " + itos ( i - arg_diff + 1 ) + " . Assigned type ( " +
par_type . to_string ( ) + " ) doesn't match the function argument's type ( " +
arg_types [ i - arg_diff ] . to_string ( ) + " ). " ,
p_call - > line ) ;
return DataType ( ) ;
} else {
_mark_line_as_unsafe ( p_call - > line ) ;
}
2018-07-01 18:17:40 +02:00
} else {
# ifdef DEBUG_ENABLED
if ( arg_type . kind = = DataType : : BUILTIN & & arg_type . builtin_type = = Variant : : INT & & par_type . kind = = DataType : : BUILTIN & & par_type . builtin_type = = Variant : : REAL ) {
_add_warning ( GDScriptWarning : : NARROWING_CONVERSION , p_call - > line , callee_name ) ;
}
# endif // DEBUG_ENABLED
2018-05-30 04:16:54 +02:00
}
}
return return_type ;
}
bool GDScriptParser : : _get_member_type ( const DataType & p_base_type , const StringName & p_member , DataType & r_member_type ) const {
DataType base_type = p_base_type ;
// Check classes in current file
ClassNode * base = NULL ;
if ( base_type . kind = = DataType : : CLASS ) {
base = base_type . class_type ;
}
while ( base ) {
if ( base - > constant_expressions . has ( p_member ) ) {
r_member_type = base - > constant_expressions [ p_member ] . expression - > get_datatype ( ) ;
return true ;
}
if ( ! base_type . is_meta_type ) {
for ( int i = 0 ; i < base - > variables . size ( ) ; i + + ) {
ClassNode : : Member m = base - > variables [ i ] ;
if ( m . identifier = = p_member ) {
r_member_type = m . data_type ;
return true ;
}
}
} else {
for ( int i = 0 ; i < base - > subclasses . size ( ) ; i + + ) {
ClassNode * c = base - > subclasses [ i ] ;
if ( c - > name = = p_member ) {
DataType class_type ;
class_type . has_type = true ;
class_type . is_constant = true ;
class_type . is_meta_type = true ;
class_type . kind = DataType : : CLASS ;
class_type . class_type = c ;
r_member_type = class_type ;
return true ;
}
}
}
base_type = base - > base_type ;
if ( base_type . kind = = DataType : : CLASS ) {
base = base_type . class_type ;
} else {
break ;
}
}
Ref < GDScript > gds ;
if ( base_type . kind = = DataType : : GDSCRIPT ) {
gds = base_type . script_type ;
2018-11-18 02:23:40 +01:00
if ( gds . is_null ( ) | | ! gds - > is_valid ( ) ) {
// GDScript wasn't properly compíled, don't bother trying
return false ;
}
2018-05-30 04:16:54 +02:00
}
Ref < Script > scr ;
if ( base_type . kind = = DataType : : SCRIPT ) {
scr = base_type . script_type ;
}
StringName native ;
if ( base_type . kind = = DataType : : NATIVE ) {
native = base_type . native_type ;
}
// Check GDScripts
while ( gds . is_valid ( ) ) {
if ( gds - > get_constants ( ) . has ( p_member ) ) {
Variant c = gds - > get_constants ( ) [ p_member ] ;
r_member_type = _type_from_variant ( c ) ;
return true ;
}
if ( ! base_type . is_meta_type ) {
if ( gds - > get_members ( ) . has ( p_member ) ) {
r_member_type = _type_from_gdtype ( gds - > get_member_type ( p_member ) ) ;
return true ;
}
}
native = gds - > get_instance_base_type ( ) ;
if ( gds - > get_base_script ( ) . is_valid ( ) ) {
gds = gds - > get_base_script ( ) ;
scr = gds - > get_base_script ( ) ;
bool is_meta = base_type . is_meta_type ;
base_type = _type_from_variant ( scr . operator Variant ( ) ) ;
base_type . is_meta_type = is_meta ;
} else {
break ;
}
}
// Check other script types
while ( scr . is_valid ( ) ) {
Map < StringName , Variant > constants ;
scr - > get_constants ( & constants ) ;
if ( constants . has ( p_member ) ) {
r_member_type = _type_from_variant ( constants [ p_member ] ) ;
return true ;
}
List < PropertyInfo > properties ;
scr - > get_script_property_list ( & properties ) ;
for ( List < PropertyInfo > : : Element * E = properties . front ( ) ; E ; E = E - > next ( ) ) {
if ( E - > get ( ) . name = = p_member ) {
r_member_type = _type_from_property ( E - > get ( ) ) ;
return true ;
}
}
base_type = _type_from_variant ( scr . operator Variant ( ) ) ;
native = scr - > get_instance_base_type ( ) ;
scr = scr - > get_base_script ( ) ;
}
2018-11-18 02:23:40 +01:00
if ( native = = StringName ( ) ) {
// Empty native class, might happen in some Script implementations
// Just ignore it
return false ;
}
2018-05-30 04:16:54 +02:00
// Check ClassDB
if ( ! ClassDB : : class_exists ( native ) ) {
native = " _ " + native . operator String ( ) ;
}
if ( ! ClassDB : : class_exists ( native ) ) {
if ( ! check_types ) return false ;
ERR_EXPLAIN ( " Parser bug: Class ' " + String ( native ) + " ' not found. " ) ;
ERR_FAIL_V ( false ) ;
}
bool valid = false ;
ClassDB : : get_integer_constant ( native , p_member , & valid ) ;
if ( valid ) {
DataType ct ;
ct . has_type = true ;
ct . is_constant = true ;
ct . kind = DataType : : BUILTIN ;
ct . builtin_type = Variant : : INT ;
r_member_type = ct ;
return true ;
}
if ( ! base_type . is_meta_type ) {
List < PropertyInfo > properties ;
ClassDB : : get_property_list ( native , & properties ) ;
for ( List < PropertyInfo > : : Element * E = properties . front ( ) ; E ; E = E - > next ( ) ) {
if ( E - > get ( ) . name = = p_member ) {
// Check if a getter exists
StringName getter_name = ClassDB : : get_property_getter ( native , p_member ) ;
if ( getter_name ! = StringName ( ) ) {
// Use the getter return type
# ifdef DEBUG_METHODS_ENABLED
MethodBind * getter_method = ClassDB : : get_method ( native , getter_name ) ;
if ( getter_method ) {
r_member_type = _type_from_property ( getter_method - > get_return_info ( ) ) ;
} else {
r_member_type = DataType ( ) ;
}
# else
r_member_type = DataType ( ) ;
# endif
} else {
r_member_type = _type_from_property ( E - > get ( ) ) ;
}
return true ;
}
}
}
// If the base is a script, it might be trying to access members of the Script class itself
if ( p_base_type . is_meta_type & & ( p_base_type . kind = = DataType : : SCRIPT | | p_base_type . kind = = DataType : : GDSCRIPT ) ) {
native = p_base_type . script_type - > get_class_name ( ) ;
ClassDB : : get_integer_constant ( native , p_member , & valid ) ;
if ( valid ) {
DataType ct ;
ct . has_type = true ;
ct . is_constant = true ;
ct . kind = DataType : : BUILTIN ;
ct . builtin_type = Variant : : INT ;
r_member_type = ct ;
return true ;
}
List < PropertyInfo > properties ;
ClassDB : : get_property_list ( native , & properties ) ;
for ( List < PropertyInfo > : : Element * E = properties . front ( ) ; E ; E = E - > next ( ) ) {
if ( E - > get ( ) . name = = p_member ) {
// Check if a getter exists
StringName getter_name = ClassDB : : get_property_getter ( native , p_member ) ;
if ( getter_name ! = StringName ( ) ) {
// Use the getter return type
# ifdef DEBUG_METHODS_ENABLED
MethodBind * getter_method = ClassDB : : get_method ( native , getter_name ) ;
if ( getter_method ) {
r_member_type = _type_from_property ( getter_method - > get_return_info ( ) ) ;
} else {
r_member_type = DataType ( ) ;
}
# else
r_member_type = DataType ( ) ;
# endif
} else {
r_member_type = _type_from_property ( E - > get ( ) ) ;
}
return true ;
}
}
}
return false ;
}
GDScriptParser : : DataType GDScriptParser : : _reduce_identifier_type ( const DataType * p_base_type , const StringName & p_identifier , int p_line ) {
if ( p_base_type & & ! p_base_type - > has_type ) {
return DataType ( ) ;
}
DataType base_type ;
// Check classes in current file
ClassNode * base = NULL ;
if ( ! p_base_type ) {
2018-07-25 15:20:11 +02:00
base = current_class ;
base_type . has_type = true ;
base_type . is_constant = true ;
base_type . kind = DataType : : CLASS ;
base_type . class_type = base ;
} else {
base_type = DataType ( * p_base_type ) ;
if ( base_type . kind = = DataType : : CLASS ) {
base = base_type . class_type ;
}
}
DataType member_type ;
2018-07-01 18:17:40 +02:00
for ( int i = 0 ; i < current_class - > variables . size ( ) ; i + + ) {
if ( current_class - > variables [ i ] . identifier = = p_identifier ) {
member_type = current_class - > variables [ i ] . data_type ;
current_class - > variables . write [ i ] . usages + = 1 ;
return member_type ;
}
}
2018-07-25 15:20:11 +02:00
if ( _get_member_type ( base_type , p_identifier , member_type ) ) {
return member_type ;
}
if ( ! p_base_type ) {
// Possibly this is a global, check before failing
2018-05-30 04:16:54 +02:00
if ( ClassDB : : class_exists ( p_identifier ) | | ClassDB : : class_exists ( " _ " + p_identifier . operator String ( ) ) ) {
DataType result ;
result . has_type = true ;
result . is_constant = true ;
result . is_meta_type = true ;
if ( Engine : : get_singleton ( ) - > has_singleton ( p_identifier ) | | Engine : : get_singleton ( ) - > has_singleton ( " _ " + p_identifier . operator String ( ) ) ) {
result . is_meta_type = false ;
}
result . kind = DataType : : NATIVE ;
result . native_type = p_identifier ;
return result ;
}
ClassNode * outer_class = current_class ;
while ( outer_class ) {
if ( outer_class - > name = = p_identifier ) {
DataType result ;
result . has_type = true ;
result . is_constant = true ;
result . is_meta_type = true ;
result . kind = DataType : : CLASS ;
result . class_type = outer_class ;
return result ;
}
2018-07-25 23:18:53 +02:00
if ( outer_class - > constant_expressions . has ( p_identifier ) ) {
return outer_class - > constant_expressions [ p_identifier ] . type ;
}
2018-05-30 04:16:54 +02:00
for ( int i = 0 ; i < outer_class - > subclasses . size ( ) ; i + + ) {
if ( outer_class - > subclasses [ i ] = = current_class ) {
continue ;
}
if ( outer_class - > subclasses [ i ] - > name = = p_identifier ) {
DataType result ;
result . has_type = true ;
result . is_constant = true ;
result . is_meta_type = true ;
result . kind = DataType : : CLASS ;
result . class_type = outer_class - > subclasses [ i ] ;
return result ;
}
}
outer_class = outer_class - > owner ;
}
if ( ScriptServer : : is_global_class ( p_identifier ) ) {
Ref < Script > scr = ResourceLoader : : load ( ScriptServer : : get_global_class_path ( p_identifier ) ) ;
if ( scr . is_valid ( ) ) {
DataType result ;
result . has_type = true ;
result . script_type = scr ;
result . is_meta_type = true ;
Ref < GDScript > gds = scr ;
if ( gds . is_valid ( ) ) {
if ( ! gds - > is_valid ( ) ) {
_set_error ( " Class ' " + p_identifier + " ' could not be fully loaded (script error or cyclic inheritance). " ) ;
return DataType ( ) ;
}
result . kind = DataType : : GDSCRIPT ;
} else {
result . kind = DataType : : SCRIPT ;
}
return result ;
}
_set_error ( " Class ' " + p_identifier + " ' was found in global scope but its script could not be loaded. " ) ;
return DataType ( ) ;
}
if ( GDScriptLanguage : : get_singleton ( ) - > get_global_map ( ) . has ( p_identifier ) ) {
int idx = GDScriptLanguage : : get_singleton ( ) - > get_global_map ( ) [ p_identifier ] ;
Variant g = GDScriptLanguage : : get_singleton ( ) - > get_global_array ( ) [ idx ] ;
return _type_from_variant ( g ) ;
}
if ( GDScriptLanguage : : get_singleton ( ) - > get_named_globals_map ( ) . has ( p_identifier ) ) {
Variant g = GDScriptLanguage : : get_singleton ( ) - > get_named_globals_map ( ) [ p_identifier ] ;
return _type_from_variant ( g ) ;
}
// Non-tool singletons aren't loaded, check project settings
List < PropertyInfo > props ;
ProjectSettings : : get_singleton ( ) - > get_property_list ( & props ) ;
for ( List < PropertyInfo > : : Element * E = props . front ( ) ; E ; E = E - > next ( ) ) {
String s = E - > get ( ) . name ;
if ( ! s . begins_with ( " autoload/ " ) ) {
continue ;
}
String name = s . get_slice ( " / " , 1 ) ;
if ( name = = p_identifier ) {
String script = ProjectSettings : : get_singleton ( ) - > get ( s ) ;
if ( script . begins_with ( " * " ) ) {
script = script . right ( 1 ) ;
}
if ( ! script . begins_with ( " res:// " ) ) {
script = " res:// " + script ;
}
Ref < Script > singleton = ResourceLoader : : load ( script ) ;
if ( singleton . is_valid ( ) ) {
DataType result ;
result . has_type = true ;
result . script_type = singleton ;
Ref < GDScript > gds = singleton ;
if ( gds . is_valid ( ) ) {
if ( ! gds - > is_valid ( ) ) {
_set_error ( " Couldn't fully load singleton script ' " + p_identifier + " ' (possible cyclic reference or parse error). " , p_line ) ;
return DataType ( ) ;
}
result . kind = DataType : : GDSCRIPT ;
} else {
result . kind = DataType : : SCRIPT ;
}
}
}
}
2018-06-05 18:50:21 +02:00
// This means looking in the current class, which type is always known
_set_error ( " Identifier ' " + p_identifier . operator String ( ) + " ' is not declared in the current scope. " , p_line ) ;
2018-05-30 04:16:54 +02:00
}
2018-06-05 18:50:21 +02:00
2018-07-01 18:17:40 +02:00
# ifdef DEBUG_ENABLED
{
DataType tmp_type ;
List < DataType > arg_types ;
int argcount ;
bool _static ;
bool vararg ;
if ( _get_function_signature ( base_type , p_identifier , tmp_type , arg_types , argcount , _static , vararg ) ) {
_add_warning ( GDScriptWarning : : FUNCTION_USED_AS_PROPERTY , p_line , p_identifier . operator String ( ) , base_type . to_string ( ) ) ;
}
}
# endif // DEBUG_ENABLED
2018-06-05 18:50:21 +02:00
_mark_line_as_unsafe ( p_line ) ;
2018-05-30 04:16:54 +02:00
return DataType ( ) ;
}
void GDScriptParser : : _check_class_level_types ( ClassNode * p_class ) {
2018-06-05 18:50:21 +02:00
_mark_line_as_safe ( p_class - > line ) ;
2018-05-30 04:16:54 +02:00
// Constants
for ( Map < StringName , ClassNode : : Constant > : : Element * E = p_class - > constant_expressions . front ( ) ; E ; E = E - > next ( ) ) {
ClassNode : : Constant & c = E - > get ( ) ;
2018-06-05 18:50:21 +02:00
_mark_line_as_safe ( c . expression - > line ) ;
2018-05-30 04:16:54 +02:00
DataType cont = _resolve_type ( c . type , c . expression - > line ) ;
DataType expr = _resolve_type ( c . expression - > get_datatype ( ) , c . expression - > line ) ;
if ( ! _is_type_compatible ( cont , expr ) ) {
_set_error ( " Constant value type ( " + expr . to_string ( ) + " ) is not compatible with declared type ( " + cont . to_string ( ) + " ). " ,
c . expression - > line ) ;
return ;
}
expr . is_constant = true ;
c . type = expr ;
c . expression - > set_datatype ( expr ) ;
2018-09-10 16:06:03 +02:00
DataType tmp ;
if ( _get_member_type ( p_class - > base_type , E - > key ( ) , tmp ) ) {
_set_error ( " Member ' " + String ( E - > key ( ) ) + " ' already exists in parent class. " , c . expression - > line ) ;
return ;
}
2018-05-30 04:16:54 +02:00
}
// Function declarations
for ( int i = 0 ; i < p_class - > static_functions . size ( ) ; i + + ) {
_check_function_types ( p_class - > static_functions [ i ] ) ;
if ( error_set ) return ;
}
for ( int i = 0 ; i < p_class - > functions . size ( ) ; i + + ) {
_check_function_types ( p_class - > functions [ i ] ) ;
if ( error_set ) return ;
}
// Class variables
for ( int i = 0 ; i < p_class - > variables . size ( ) ; i + + ) {
2018-07-25 03:11:03 +02:00
ClassNode : : Member & v = p_class - > variables . write [ i ] ;
2018-05-30 04:16:54 +02:00
DataType tmp ;
if ( _get_member_type ( p_class - > base_type , v . identifier , tmp ) ) {
_set_error ( " Member ' " + String ( v . identifier ) + " ' already exists in parent class. " , v . line ) ;
return ;
}
2018-06-05 18:50:21 +02:00
_mark_line_as_safe ( v . line ) ;
2018-05-30 04:16:54 +02:00
v . data_type = _resolve_type ( v . data_type , v . line ) ;
if ( v . expression ) {
DataType expr_type = _reduce_node_type ( v . expression ) ;
if ( ! _is_type_compatible ( v . data_type , expr_type ) ) {
2018-06-05 18:50:21 +02:00
// Try supertype test
if ( _is_type_compatible ( expr_type , v . data_type ) ) {
_mark_line_as_unsafe ( v . line ) ;
} else {
// Try with implicit conversion
if ( v . data_type . kind ! = DataType : : BUILTIN | | ! _is_type_compatible ( v . data_type , expr_type , true ) ) {
_set_error ( " Assigned expression type ( " + expr_type . to_string ( ) + " ) doesn't match the variable's type ( " +
v . data_type . to_string ( ) + " ). " ,
v . line ) ;
return ;
}
2018-05-30 04:16:54 +02:00
2018-09-13 03:38:39 +02:00
// Replace assignment with implict conversion
2018-06-05 18:50:21 +02:00
BuiltInFunctionNode * convert = alloc_node < BuiltInFunctionNode > ( ) ;
convert - > line = v . line ;
convert - > function = GDScriptFunctions : : TYPE_CONVERT ;
2018-05-30 04:16:54 +02:00
2018-06-05 18:50:21 +02:00
ConstantNode * tgt_type = alloc_node < ConstantNode > ( ) ;
tgt_type - > line = v . line ;
tgt_type - > value = ( int ) v . data_type . builtin_type ;
2018-05-30 04:16:54 +02:00
2018-06-05 18:50:21 +02:00
OperatorNode * convert_call = alloc_node < OperatorNode > ( ) ;
convert_call - > line = v . line ;
convert_call - > op = OperatorNode : : OP_CALL ;
convert_call - > arguments . push_back ( convert ) ;
convert_call - > arguments . push_back ( v . expression ) ;
convert_call - > arguments . push_back ( tgt_type ) ;
2018-05-30 04:16:54 +02:00
2018-06-05 18:50:21 +02:00
v . expression = convert_call ;
2018-07-25 03:11:03 +02:00
v . initial_assignment - > arguments . write [ 1 ] = convert_call ;
2018-06-05 18:50:21 +02:00
}
2018-05-30 04:16:54 +02:00
}
2018-06-19 07:55:52 +02:00
if ( v . data_type . infer_type ) {
if ( ! expr_type . has_type ) {
_set_error ( " Assigned value does not have a set type, variable type cannot be inferred. " , v . line ) ;
return ;
}
v . data_type = expr_type ;
v . data_type . is_constant = false ;
}
2018-05-30 04:16:54 +02:00
} else if ( v . data_type . has_type & & v . data_type . kind = = DataType : : BUILTIN ) {
// Create default value based on the type
IdentifierNode * id = alloc_node < IdentifierNode > ( ) ;
id - > line = v . line ;
id - > name = v . identifier ;
ConstantNode * init = alloc_node < ConstantNode > ( ) ;
init - > line = v . line ;
Variant : : CallError err ;
init - > value = Variant : : construct ( v . data_type . builtin_type , NULL , 0 , err ) ;
OperatorNode * op = alloc_node < OperatorNode > ( ) ;
op - > line = v . line ;
op - > op = OperatorNode : : OP_INIT_ASSIGN ;
op - > arguments . push_back ( id ) ;
op - > arguments . push_back ( init ) ;
p_class - > initializer - > statements . push_front ( op ) ;
v . initial_assignment = op ;
# ifdef DEBUG_ENABLED
NewLineNode * nl = alloc_node < NewLineNode > ( ) ;
nl - > line = v . line - 1 ;
p_class - > initializer - > statements . push_front ( nl ) ;
# endif
}
// Check export hint
2018-06-05 18:50:21 +02:00
if ( v . data_type . has_type & & v . _export . type ! = Variant : : NIL ) {
2018-05-30 04:16:54 +02:00
DataType export_type = _type_from_property ( v . _export ) ;
if ( ! _is_type_compatible ( v . data_type , export_type , true ) ) {
_set_error ( " Export hint type ( " + export_type . to_string ( ) + " ) doesn't match the variable's type ( " +
v . data_type . to_string ( ) + " ). " ,
v . line ) ;
return ;
}
}
// Setter and getter
if ( v . setter = = StringName ( ) & & v . getter = = StringName ( ) ) continue ;
bool found_getter = false ;
bool found_setter = false ;
for ( int j = 0 ; j < p_class - > functions . size ( ) ; j + + ) {
if ( v . setter = = p_class - > functions [ j ] - > name ) {
found_setter = true ;
FunctionNode * setter = p_class - > functions [ j ] ;
if ( setter - > arguments . size ( ) ! = 1 ) {
_set_error ( " Setter function needs to receive exactly 1 argument. See ' " + setter - > name +
" ()' definition at line " + itos ( setter - > line ) + " . " ,
v . line ) ;
return ;
}
if ( ! _is_type_compatible ( v . data_type , setter - > argument_types [ 0 ] ) ) {
_set_error ( " Setter argument type ( " + setter - > argument_types [ 0 ] . to_string ( ) +
" ) doesn't match the variable's type ( " + v . data_type . to_string ( ) + " ). See ' " +
setter - > name + " ()' definition at line " + itos ( setter - > line ) + " . " ,
v . line ) ;
return ;
}
continue ;
}
if ( v . getter = = p_class - > functions [ j ] - > name ) {
found_getter = true ;
FunctionNode * getter = p_class - > functions [ j ] ;
if ( getter - > arguments . size ( ) ! = 0 ) {
_set_error ( " Getter function can't receive arguments. See ' " + getter - > name +
" ()' definition at line " + itos ( getter - > line ) + " . " ,
v . line ) ;
return ;
}
if ( ! _is_type_compatible ( v . data_type , getter - > get_datatype ( ) ) ) {
_set_error ( " Getter return type ( " + getter - > get_datatype ( ) . to_string ( ) +
" ) doesn't match the variable's type ( " + v . data_type . to_string ( ) +
" ). See ' " + getter - > name + " ()' definition at line " + itos ( getter - > line ) + " . " ,
v . line ) ;
return ;
}
}
if ( found_getter & & found_setter ) break ;
}
if ( ( found_getter | | v . getter = = StringName ( ) ) & & ( found_setter | | v . setter = = StringName ( ) ) ) continue ;
// Check for static functions
for ( int j = 0 ; j < p_class - > static_functions . size ( ) ; j + + ) {
if ( v . setter = = p_class - > static_functions [ j ] - > name ) {
FunctionNode * setter = p_class - > static_functions [ j ] ;
_set_error ( " Setter can't be a static function. See ' " + setter - > name + " ()' definition at line " + itos ( setter - > line ) + " . " , v . line ) ;
return ;
}
if ( v . getter = = p_class - > static_functions [ j ] - > name ) {
FunctionNode * getter = p_class - > static_functions [ j ] ;
_set_error ( " Getter can't be a static function. See ' " + getter - > name + " ()' definition at line " + itos ( getter - > line ) + " . " , v . line ) ;
return ;
}
}
if ( ! found_setter & & v . setter ! = StringName ( ) ) {
_set_error ( " Setter function is not defined. " , v . line ) ;
return ;
}
if ( ! found_getter & & v . getter ! = StringName ( ) ) {
_set_error ( " Getter function is not defined. " , v . line ) ;
return ;
}
}
// Inner classes
for ( int i = 0 ; i < p_class - > subclasses . size ( ) ; i + + ) {
current_class = p_class - > subclasses [ i ] ;
_check_class_level_types ( current_class ) ;
if ( error_set ) return ;
current_class = p_class ;
}
}
void GDScriptParser : : _check_function_types ( FunctionNode * p_function ) {
p_function - > return_type = _resolve_type ( p_function - > return_type , p_function - > line ) ;
// Arguments
int defaults_ofs = p_function - > arguments . size ( ) - p_function - > default_values . size ( ) ;
for ( int i = 0 ; i < p_function - > arguments . size ( ) ; i + + ) {
2018-07-26 01:48:22 +02:00
if ( i < defaults_ofs ) {
p_function - > argument_types . write [ i ] = _resolve_type ( p_function - > argument_types [ i ] , p_function - > line ) ;
} else {
2018-05-30 04:16:54 +02:00
if ( p_function - > default_values [ i - defaults_ofs ] - > type ! = Node : : TYPE_OPERATOR ) {
_set_error ( " Parser bug: invalid argument default value. " , p_function - > line , p_function - > column ) ;
return ;
}
OperatorNode * op = static_cast < OperatorNode * > ( p_function - > default_values [ i - defaults_ofs ] ) ;
if ( op - > op ! = OperatorNode : : OP_ASSIGN | | op - > arguments . size ( ) ! = 2 ) {
_set_error ( " Parser bug: invalid argument default value operation. " , p_function - > line ) ;
return ;
}
DataType def_type = _reduce_node_type ( op - > arguments [ 1 ] ) ;
2018-07-26 01:48:22 +02:00
if ( p_function - > argument_types [ i ] . infer_type ) {
def_type . is_constant = false ;
p_function - > argument_types . write [ i ] = def_type ;
} else {
p_function - > return_type = _resolve_type ( p_function - > return_type , p_function - > line ) ;
if ( ! _is_type_compatible ( p_function - > argument_types [ i ] , def_type , true ) ) {
String arg_name = p_function - > arguments [ i ] ;
_set_error ( " Value type ( " + def_type . to_string ( ) + " ) doesn't match the type of argument ' " +
arg_name + " ' ( " + p_function - > arguments [ i ] + " ) " ,
p_function - > line ) ;
}
2018-05-30 04:16:54 +02:00
}
}
2018-07-01 18:17:40 +02:00
# ifdef DEBUG_ENABLED
2018-08-22 01:19:35 +02:00
if ( p_function - > arguments_usage [ i ] = = 0 & & ! p_function - > arguments [ i ] . operator String ( ) . begins_with ( " _ " ) ) {
2018-07-01 18:17:40 +02:00
_add_warning ( GDScriptWarning : : UNUSED_ARGUMENT , p_function - > line , p_function - > name , p_function - > arguments [ i ] . operator String ( ) ) ;
}
# endif // DEBUG_ENABLED
2018-05-30 04:16:54 +02:00
}
if ( ! ( p_function - > name = = " _init " ) ) {
// Signature for the initializer may vary
2018-07-25 17:12:46 +02:00
# ifdef DEBUG_ENABLED
2018-05-30 04:16:54 +02:00
DataType return_type ;
List < DataType > arg_types ;
int default_arg_count = 0 ;
bool _static = false ;
bool vararg = false ;
DataType base_type = current_class - > base_type ;
if ( _get_function_signature ( base_type , p_function - > name , return_type , arg_types , default_arg_count , _static , vararg ) ) {
bool valid = _static = = p_function - > _static ;
valid = valid & & return_type = = p_function - > return_type ;
2018-07-25 17:12:46 +02:00
int argsize_diff = p_function - > arguments . size ( ) - arg_types . size ( ) ;
valid = valid & & argsize_diff > = 0 ;
valid = valid & & p_function - > default_values . size ( ) > = default_arg_count + argsize_diff ;
2018-05-30 04:16:54 +02:00
int i = 0 ;
for ( List < DataType > : : Element * E = arg_types . front ( ) ; valid & & E ; E = E - > next ( ) ) {
valid = valid & & E - > get ( ) = = p_function - > argument_types [ i + + ] ;
}
if ( ! valid ) {
2018-07-25 17:12:46 +02:00
String parent_signature = return_type . has_type ? return_type . to_string ( ) : " Variant " ;
if ( parent_signature = = " null " ) {
parent_signature = " void " ;
}
parent_signature + = " " + p_function - > name + " ( " ;
if ( arg_types . size ( ) ) {
int i = 0 ;
for ( List < DataType > : : Element * E = arg_types . front ( ) ; E ; E = E - > next ( ) ) {
if ( E ! = arg_types . front ( ) ) {
parent_signature + = " , " ;
}
String arg = E - > get ( ) . to_string ( ) ;
if ( arg = = " null " | | arg = = " var " ) {
arg = " Variant " ;
}
parent_signature + = arg ;
if ( i = = arg_types . size ( ) - default_arg_count ) {
parent_signature + = " =default " ;
}
i + + ;
}
}
parent_signature + = " ) " ;
_set_error ( " Function signature doesn't match the parent. Parent signature is: ' " + parent_signature + " '. " , p_function - > line ) ;
2018-05-30 04:16:54 +02:00
return ;
}
}
2018-07-25 17:12:46 +02:00
# endif // DEBUG_ENABLED
2018-05-30 04:16:54 +02:00
} else {
if ( p_function - > return_type . has_type & & ( p_function - > return_type . kind ! = DataType : : BUILTIN | | p_function - > return_type . builtin_type ! = Variant : : NIL ) ) {
_set_error ( " Constructor cannot return a value. " , p_function - > line ) ;
return ;
}
}
if ( p_function - > return_type . has_type & & ( p_function - > return_type . kind ! = DataType : : BUILTIN | | p_function - > return_type . builtin_type ! = Variant : : NIL ) ) {
if ( ! p_function - > body - > has_return ) {
_set_error ( " Non-void function must return a value in all possible paths. " , p_function - > line ) ;
return ;
}
}
if ( p_function - > has_yield ) {
// yield() will make the function return a GDScriptFunctionState, so the type is ambiguous
p_function - > return_type . has_type = false ;
2018-07-01 18:17:40 +02:00
p_function - > return_type . may_yield = true ;
2018-05-30 04:16:54 +02:00
}
}
void GDScriptParser : : _check_class_blocks_types ( ClassNode * p_class ) {
// Function blocks
for ( int i = 0 ; i < p_class - > static_functions . size ( ) ; i + + ) {
current_function = p_class - > static_functions [ i ] ;
current_block = current_function - > body ;
2018-06-05 18:50:21 +02:00
_mark_line_as_safe ( current_function - > line ) ;
2018-05-30 04:16:54 +02:00
_check_block_types ( current_block ) ;
current_block = NULL ;
current_function = NULL ;
if ( error_set ) return ;
}
for ( int i = 0 ; i < p_class - > functions . size ( ) ; i + + ) {
current_function = p_class - > functions [ i ] ;
current_block = current_function - > body ;
2018-06-05 18:50:21 +02:00
_mark_line_as_safe ( current_function - > line ) ;
2018-05-30 04:16:54 +02:00
_check_block_types ( current_block ) ;
current_block = NULL ;
current_function = NULL ;
if ( error_set ) return ;
}
2018-07-01 18:17:40 +02:00
# ifdef DEBUG_ENABLED
// Warnings
for ( int i = 0 ; i < p_class - > variables . size ( ) ; i + + ) {
if ( p_class - > variables [ i ] . usages = = 0 ) {
_add_warning ( GDScriptWarning : : UNUSED_CLASS_VARIABLE , p_class - > variables [ i ] . line , p_class - > variables [ i ] . identifier ) ;
}
}
for ( int i = 0 ; i < p_class - > _signals . size ( ) ; i + + ) {
if ( p_class - > _signals [ i ] . emissions = = 0 ) {
_add_warning ( GDScriptWarning : : UNUSED_SIGNAL , p_class - > _signals [ i ] . line , p_class - > _signals [ i ] . name ) ;
}
}
# endif // DEBUG_ENABLED
2018-05-30 04:16:54 +02:00
// Inner classes
for ( int i = 0 ; i < p_class - > subclasses . size ( ) ; i + + ) {
current_class = p_class - > subclasses [ i ] ;
_check_class_blocks_types ( current_class ) ;
if ( error_set ) return ;
current_class = p_class ;
}
}
2018-07-01 18:17:40 +02:00
# ifdef DEBUG_ENABLED
static String _find_function_name ( const GDScriptParser : : OperatorNode * p_call ) {
switch ( p_call - > arguments [ 0 ] - > type ) {
case GDScriptParser : : Node : : TYPE_TYPE : {
return Variant : : get_type_name ( static_cast < GDScriptParser : : TypeNode * > ( p_call - > arguments [ 0 ] ) - > vtype ) ;
} break ;
case GDScriptParser : : Node : : TYPE_BUILT_IN_FUNCTION : {
return GDScriptFunctions : : get_func_name ( static_cast < GDScriptParser : : BuiltInFunctionNode * > ( p_call - > arguments [ 0 ] ) - > function ) ;
} break ;
default : {
int id_index = p_call - > op = = GDScriptParser : : OperatorNode : : OP_PARENT_CALL ? 0 : 1 ;
if ( p_call - > arguments . size ( ) > id_index & & p_call - > arguments [ id_index ] - > type = = GDScriptParser : : Node : : TYPE_IDENTIFIER ) {
return static_cast < GDScriptParser : : IdentifierNode * > ( p_call - > arguments [ id_index ] ) - > name ;
}
} break ;
}
return String ( ) ;
}
# endif // DEBUG_ENABLED
2018-05-30 04:16:54 +02:00
void GDScriptParser : : _check_block_types ( BlockNode * p_block ) {
2018-06-05 18:50:21 +02:00
Node * last_var_assign = NULL ;
2018-05-30 04:16:54 +02:00
// Check each statement
for ( List < Node * > : : Element * E = p_block - > statements . front ( ) ; E ; E = E - > next ( ) ) {
Node * statement = E - > get ( ) ;
switch ( statement - > type ) {
case Node : : TYPE_NEWLINE :
case Node : : TYPE_BREAKPOINT :
case Node : : TYPE_ASSERT : {
// Nothing to do
} break ;
case Node : : TYPE_LOCAL_VAR : {
LocalVarNode * lv = static_cast < LocalVarNode * > ( statement ) ;
lv - > datatype = _resolve_type ( lv - > datatype , lv - > line ) ;
2018-06-05 18:50:21 +02:00
_mark_line_as_safe ( lv - > line ) ;
2018-05-30 04:16:54 +02:00
2018-07-01 18:17:40 +02:00
last_var_assign = lv - > assign ;
2018-05-30 04:16:54 +02:00
if ( lv - > assign ) {
DataType assign_type = _reduce_node_type ( lv - > assign ) ;
2018-07-01 18:17:40 +02:00
# ifdef DEBUG_ENABLED
if ( assign_type . has_type & & assign_type . kind = = DataType : : BUILTIN & & assign_type . builtin_type = = Variant : : NIL ) {
if ( lv - > assign - > type = = Node : : TYPE_OPERATOR ) {
OperatorNode * call = static_cast < OperatorNode * > ( lv - > assign ) ;
if ( call - > op = = OperatorNode : : OP_CALL | | call - > op = = OperatorNode : : OP_PARENT_CALL ) {
_add_warning ( GDScriptWarning : : VOID_ASSIGNMENT , lv - > line , _find_function_name ( call ) ) ;
}
}
}
if ( lv - > datatype . has_type & & assign_type . may_yield & & lv - > assign - > type = = Node : : TYPE_OPERATOR ) {
_add_warning ( GDScriptWarning : : FUNCTION_MAY_YIELD , lv - > line , _find_function_name ( static_cast < OperatorNode * > ( lv - > assign ) ) ) ;
}
# endif // DEBUG_ENABLED
2018-05-30 04:16:54 +02:00
if ( ! _is_type_compatible ( lv - > datatype , assign_type ) ) {
2018-06-05 18:50:21 +02:00
// Try supertype test
if ( _is_type_compatible ( assign_type , lv - > datatype ) ) {
_mark_line_as_unsafe ( lv - > line ) ;
} else {
// Try implict conversion
if ( lv - > datatype . kind ! = DataType : : BUILTIN | | ! _is_type_compatible ( lv - > datatype , assign_type , true ) ) {
_set_error ( " Assigned value type ( " + assign_type . to_string ( ) + " ) doesn't match the variable's type ( " +
lv - > datatype . to_string ( ) + " ). " ,
lv - > line ) ;
return ;
}
2018-09-13 03:38:39 +02:00
// Replace assignment with implict conversion
2018-06-05 18:50:21 +02:00
BuiltInFunctionNode * convert = alloc_node < BuiltInFunctionNode > ( ) ;
convert - > line = lv - > line ;
convert - > function = GDScriptFunctions : : TYPE_CONVERT ;
2018-05-30 04:16:54 +02:00
2018-06-05 18:50:21 +02:00
ConstantNode * tgt_type = alloc_node < ConstantNode > ( ) ;
tgt_type - > line = lv - > line ;
tgt_type - > value = ( int ) lv - > datatype . builtin_type ;
2018-05-30 04:16:54 +02:00
2018-06-05 18:50:21 +02:00
OperatorNode * convert_call = alloc_node < OperatorNode > ( ) ;
convert_call - > line = lv - > line ;
convert_call - > op = OperatorNode : : OP_CALL ;
convert_call - > arguments . push_back ( convert ) ;
convert_call - > arguments . push_back ( lv - > assign ) ;
convert_call - > arguments . push_back ( tgt_type ) ;
2018-05-30 04:16:54 +02:00
2018-06-05 18:50:21 +02:00
lv - > assign = convert_call ;
2018-07-25 03:11:03 +02:00
lv - > assign_op - > arguments . write [ 1 ] = convert_call ;
2018-07-01 18:17:40 +02:00
# ifdef DEBUG_ENABLED
if ( lv - > datatype . builtin_type = = Variant : : INT & & assign_type . builtin_type = = Variant : : REAL ) {
_add_warning ( GDScriptWarning : : NARROWING_CONVERSION , lv - > line ) ;
}
# endif // DEBUG_ENABLED
2018-06-05 18:50:21 +02:00
}
}
2018-06-19 07:55:52 +02:00
if ( lv - > datatype . infer_type ) {
if ( ! assign_type . has_type ) {
_set_error ( " Assigned value does not have a set type, variable type cannot be inferred. " , lv - > line ) ;
return ;
}
lv - > datatype = assign_type ;
lv - > datatype . is_constant = false ;
}
2018-06-05 18:50:21 +02:00
if ( lv - > datatype . has_type & & ! assign_type . has_type ) {
_mark_line_as_unsafe ( lv - > line ) ;
2018-05-30 04:16:54 +02:00
}
}
} break ;
case Node : : TYPE_OPERATOR : {
OperatorNode * op = static_cast < OperatorNode * > ( statement ) ;
switch ( op - > op ) {
case OperatorNode : : OP_ASSIGN :
case OperatorNode : : OP_ASSIGN_ADD :
case OperatorNode : : OP_ASSIGN_SUB :
case OperatorNode : : OP_ASSIGN_MUL :
case OperatorNode : : OP_ASSIGN_DIV :
case OperatorNode : : OP_ASSIGN_MOD :
case OperatorNode : : OP_ASSIGN_SHIFT_LEFT :
case OperatorNode : : OP_ASSIGN_SHIFT_RIGHT :
case OperatorNode : : OP_ASSIGN_BIT_AND :
case OperatorNode : : OP_ASSIGN_BIT_OR :
case OperatorNode : : OP_ASSIGN_BIT_XOR : {
if ( op - > arguments . size ( ) < 2 ) {
_set_error ( " Parser bug: operation without enough arguments. " , op - > line , op - > column ) ;
return ;
}
2018-06-05 18:50:21 +02:00
if ( op - > arguments [ 1 ] = = last_var_assign ) {
// Assignment was already checked
break ;
}
_mark_line_as_safe ( op - > line ) ;
2018-05-30 04:16:54 +02:00
DataType lh_type = _reduce_node_type ( op - > arguments [ 0 ] ) ;
if ( error_set ) {
return ;
}
if ( ! lh_type . has_type ) {
2018-06-05 18:50:21 +02:00
if ( op - > arguments [ 0 ] - > type = = Node : : TYPE_OPERATOR ) {
_mark_line_as_unsafe ( op - > line ) ;
}
} else if ( lh_type . is_constant ) {
2018-05-30 04:16:54 +02:00
_set_error ( " Cannot assign a new value to a constant. " , op - > line ) ;
return ;
}
DataType rh_type ;
if ( op - > op ! = OperatorNode : : OP_ASSIGN ) {
// Validate operation
DataType arg_type = _reduce_node_type ( op - > arguments [ 1 ] ) ;
if ( ! arg_type . has_type ) {
2018-06-05 18:50:21 +02:00
_mark_line_as_unsafe ( op - > line ) ;
2018-05-30 04:16:54 +02:00
break ;
}
Variant : : Operator oper = _get_variant_operation ( op - > op ) ;
bool valid = false ;
rh_type = _get_operation_type ( oper , lh_type , arg_type , valid ) ;
if ( ! valid ) {
_set_error ( " Invalid operand types (' " + lh_type . to_string ( ) + " ' and ' " + arg_type . to_string ( ) +
" ') to assignment operator ' " + Variant : : get_operator_name ( oper ) + " '. " ,
op - > line ) ;
return ;
}
} else {
rh_type = _reduce_node_type ( op - > arguments [ 1 ] ) ;
}
2018-07-01 18:17:40 +02:00
# ifdef DEBUG_ENABLED
if ( rh_type . has_type & & rh_type . kind = = DataType : : BUILTIN & & rh_type . builtin_type = = Variant : : NIL ) {
if ( op - > arguments [ 1 ] - > type = = Node : : TYPE_OPERATOR ) {
OperatorNode * call = static_cast < OperatorNode * > ( op - > arguments [ 1 ] ) ;
if ( call - > op = = OperatorNode : : OP_CALL | | call - > op = = OperatorNode : : OP_PARENT_CALL ) {
_add_warning ( GDScriptWarning : : VOID_ASSIGNMENT , op - > line , _find_function_name ( call ) ) ;
}
}
}
if ( lh_type . has_type & & rh_type . may_yield & & op - > arguments [ 1 ] - > type = = Node : : TYPE_OPERATOR ) {
_add_warning ( GDScriptWarning : : FUNCTION_MAY_YIELD , op - > line , _find_function_name ( static_cast < OperatorNode * > ( op - > arguments [ 1 ] ) ) ) ;
}
# endif // DEBUG_ENABLED
2018-05-30 04:16:54 +02:00
if ( ! _is_type_compatible ( lh_type , rh_type ) ) {
2018-06-05 18:50:21 +02:00
// Try supertype test
if ( _is_type_compatible ( rh_type , lh_type ) ) {
_mark_line_as_unsafe ( op - > line ) ;
} else {
// Try implict conversion
if ( lh_type . kind ! = DataType : : BUILTIN | | ! _is_type_compatible ( lh_type , rh_type , true ) ) {
_set_error ( " Assigned value type ( " + rh_type . to_string ( ) + " ) doesn't match the variable's type ( " +
lh_type . to_string ( ) + " ). " ,
op - > line ) ;
return ;
}
2018-09-13 03:38:39 +02:00
// Replace assignment with implict conversion
2018-06-05 18:50:21 +02:00
BuiltInFunctionNode * convert = alloc_node < BuiltInFunctionNode > ( ) ;
convert - > line = op - > line ;
convert - > function = GDScriptFunctions : : TYPE_CONVERT ;
ConstantNode * tgt_type = alloc_node < ConstantNode > ( ) ;
tgt_type - > line = op - > line ;
tgt_type - > value = ( int ) lh_type . builtin_type ;
OperatorNode * convert_call = alloc_node < OperatorNode > ( ) ;
convert_call - > line = op - > line ;
convert_call - > op = OperatorNode : : OP_CALL ;
convert_call - > arguments . push_back ( convert ) ;
convert_call - > arguments . push_back ( op - > arguments [ 1 ] ) ;
convert_call - > arguments . push_back ( tgt_type ) ;
2018-07-25 03:11:03 +02:00
op - > arguments . write [ 1 ] = convert_call ;
2018-07-01 18:17:40 +02:00
# ifdef DEBUG_ENABLED
if ( lh_type . builtin_type = = Variant : : INT & & rh_type . builtin_type = = Variant : : REAL ) {
_add_warning ( GDScriptWarning : : NARROWING_CONVERSION , op - > line ) ;
}
# endif // DEBUG_ENABLED
2018-05-30 04:16:54 +02:00
}
2018-06-05 18:50:21 +02:00
}
if ( ! rh_type . has_type & & ( op - > op ! = OperatorNode : : OP_ASSIGN | | lh_type . has_type | | op - > arguments [ 0 ] - > type = = Node : : TYPE_OPERATOR ) ) {
_mark_line_as_unsafe ( op - > line ) ;
2018-05-30 04:16:54 +02:00
}
} break ;
case OperatorNode : : OP_CALL :
case OperatorNode : : OP_PARENT_CALL : {
2018-06-05 18:50:21 +02:00
_mark_line_as_safe ( op - > line ) ;
2018-07-01 18:17:40 +02:00
DataType func_type = _reduce_function_call_type ( op ) ;
# ifdef DEBUG_ENABLED
if ( func_type . has_type & & ( func_type . kind ! = DataType : : BUILTIN | | func_type . builtin_type ! = Variant : : NIL ) ) {
// Figure out function name for warning
String func_name = _find_function_name ( op ) ;
if ( func_name . empty ( ) ) {
2018-10-01 17:27:00 +02:00
func_name = " <undetected name> " ;
2018-07-01 18:17:40 +02:00
}
_add_warning ( GDScriptWarning : : RETURN_VALUE_DISCARDED , op - > line , func_name ) ;
}
# endif // DEBUG_ENABLED
2018-05-30 04:16:54 +02:00
if ( error_set ) return ;
} break ;
2018-07-01 18:17:40 +02:00
case OperatorNode : : OP_YIELD : {
_mark_line_as_safe ( op - > line ) ;
_reduce_node_type ( op ) ;
} break ;
2018-05-30 04:16:54 +02:00
default : {
2018-06-05 18:50:21 +02:00
_mark_line_as_safe ( op - > line ) ;
_reduce_node_type ( op ) ; // Test for safety anyway
2018-07-01 18:17:40 +02:00
# ifdef DEBUG_ENABLED
_add_warning ( GDScriptWarning : : STANDALONE_EXPRESSION , statement - > line ) ;
# endif // DEBUG_ENABLED
2018-05-30 04:16:54 +02:00
}
}
} break ;
case Node : : TYPE_CONTROL_FLOW : {
ControlFlowNode * cf = static_cast < ControlFlowNode * > ( statement ) ;
2018-06-05 18:50:21 +02:00
_mark_line_as_safe ( cf - > line ) ;
2018-05-30 04:16:54 +02:00
switch ( cf - > cf_type ) {
case ControlFlowNode : : CF_RETURN : {
DataType function_type = current_function - > get_datatype ( ) ;
2018-06-05 18:50:21 +02:00
DataType ret_type ;
if ( cf - > arguments . size ( ) > 0 ) {
ret_type = _reduce_node_type ( cf - > arguments [ 0 ] ) ;
if ( error_set ) {
return ;
}
}
2018-05-30 04:16:54 +02:00
if ( ! function_type . has_type ) break ;
if ( function_type . kind = = DataType : : BUILTIN & & function_type . builtin_type = = Variant : : NIL ) {
// Return void, should not have arguments
if ( cf - > arguments . size ( ) > 0 ) {
_set_error ( " Void function cannot return a value. " , cf - > line , cf - > column ) ;
return ;
}
} else {
// Return something, cannot be empty
if ( cf - > arguments . size ( ) = = 0 ) {
_set_error ( " Non-void function must return a value. " , cf - > line , cf - > column ) ;
return ;
}
if ( ! _is_type_compatible ( function_type , ret_type ) ) {
_set_error ( " Returned value type ( " + ret_type . to_string ( ) + " ) doesn't match the function return type ( " +
function_type . to_string ( ) + " ). " ,
cf - > line , cf - > column ) ;
return ;
}
}
} break ;
case ControlFlowNode : : CF_MATCH : {
MatchNode * match_node = cf - > match ;
_transform_match_statment ( match_node ) ;
} break ;
2018-06-05 18:50:21 +02:00
default : {
if ( cf - > body_else ) {
_mark_line_as_safe ( cf - > body_else - > line ) ;
}
for ( int i = 0 ; i < cf - > arguments . size ( ) ; i + + ) {
_reduce_node_type ( cf - > arguments [ i ] ) ;
}
} break ;
2018-05-30 04:16:54 +02:00
}
} break ;
case Node : : TYPE_CONSTANT : {
ConstantNode * cn = static_cast < ConstantNode * > ( statement ) ;
// Strings are fine since they can be multiline comments
if ( cn - > value . get_type ( ) = = Variant : : STRING ) {
break ;
}
} // falthrough
default : {
2018-06-05 18:50:21 +02:00
_mark_line_as_safe ( statement - > line ) ;
_reduce_node_type ( statement ) ; // Test for safety anyway
2018-07-01 18:17:40 +02:00
# ifdef DEBUG_ENABLED
_add_warning ( GDScriptWarning : : STANDALONE_EXPRESSION , statement - > line ) ;
# endif // DEBUG_ENABLED
2018-05-30 04:16:54 +02:00
}
}
}
// Parse sub blocks
for ( int i = 0 ; i < p_block - > sub_blocks . size ( ) ; i + + ) {
current_block = p_block - > sub_blocks [ i ] ;
_check_block_types ( current_block ) ;
current_block = p_block ;
if ( error_set ) return ;
}
2018-07-01 18:17:40 +02:00
# ifdef DEBUG_ENABLED
// Warnings check
for ( Map < StringName , LocalVarNode * > : : Element * E = p_block - > variables . front ( ) ; E ; E = E - > next ( ) ) {
LocalVarNode * lv = E - > get ( ) ;
2018-08-22 01:19:35 +02:00
if ( ! lv - > name . operator String ( ) . begins_with ( " _ " ) ) {
if ( lv - > usages = = 0 ) {
_add_warning ( GDScriptWarning : : UNUSED_VARIABLE , lv - > line , lv - > name ) ;
} else if ( lv - > assignments = = 0 ) {
_add_warning ( GDScriptWarning : : UNASSIGNED_VARIABLE , lv - > line , lv - > name ) ;
}
2018-07-01 18:17:40 +02:00
}
}
# endif // DEBUG_ENABLED
2018-05-30 04:16:54 +02:00
}
void GDScriptParser : : _set_error ( const String & p_error , int p_line , int p_column ) {
if ( error_set )
return ; //allow no further errors
error = p_error ;
error_line = p_line < 0 ? tokenizer - > get_token_line ( ) : p_line ;
error_column = p_column < 0 ? tokenizer - > get_token_column ( ) : p_column ;
2017-03-05 16:44:50 +01:00
error_set = true ;
2014-02-10 02:10:30 +01:00
}
2018-07-01 18:17:40 +02:00
# ifdef DEBUG_ENABLED
void GDScriptParser : : _add_warning ( int p_code , int p_line , const String & p_symbol1 , const String & p_symbol2 , const String & p_symbol3 , const String & p_symbol4 ) {
Vector < String > symbols ;
if ( ! p_symbol1 . empty ( ) ) {
symbols . push_back ( p_symbol1 ) ;
}
if ( ! p_symbol2 . empty ( ) ) {
symbols . push_back ( p_symbol2 ) ;
}
if ( ! p_symbol3 . empty ( ) ) {
symbols . push_back ( p_symbol3 ) ;
}
if ( ! p_symbol4 . empty ( ) ) {
symbols . push_back ( p_symbol4 ) ;
}
_add_warning ( p_code , p_line , symbols ) ;
}
void GDScriptParser : : _add_warning ( int p_code , int p_line , const Vector < String > & p_symbols ) {
if ( tokenizer - > is_ignoring_warnings ( ) | | ! GLOBAL_GET ( " debug/gdscript/warnings/enable " ) . booleanize ( ) ) {
return ;
}
String warn_name = GDScriptWarning : : get_name_from_code ( ( GDScriptWarning : : Code ) p_code ) . to_lower ( ) ;
if ( tokenizer - > get_warning_global_skips ( ) . has ( warn_name ) ) {
return ;
}
if ( ! GLOBAL_GET ( " debug/gdscript/warnings/ " + warn_name ) ) {
return ;
}
GDScriptWarning warn ;
warn . code = ( GDScriptWarning : : Code ) p_code ;
warn . symbols = p_symbols ;
warn . line = p_line = = - 1 ? tokenizer - > get_token_line ( ) : p_line ;
List < GDScriptWarning > : : Element * before = NULL ;
for ( List < GDScriptWarning > : : Element * E = warnings . front ( ) ; E ; E = E - > next ( ) ) {
if ( E - > get ( ) . line > warn . line ) {
break ;
}
before = E ;
}
if ( before ) {
warnings . insert_after ( before , warn ) ;
} else {
warnings . push_front ( warn ) ;
}
}
# endif // DEBUG_ENABLED
2017-11-16 18:38:18 +01:00
String GDScriptParser : : get_error ( ) const {
2014-02-10 02:10:30 +01:00
return error ;
}
2017-11-16 18:38:18 +01:00
int GDScriptParser : : get_error_line ( ) const {
2014-02-10 02:10:30 +01:00
return error_line ;
}
2017-11-16 18:38:18 +01:00
int GDScriptParser : : get_error_column ( ) const {
2014-02-10 02:10:30 +01:00
return error_column ;
}
2017-11-16 18:38:18 +01:00
Error GDScriptParser : : _parse ( const String & p_base_path ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
base_path = p_base_path ;
2014-02-10 02:10:30 +01:00
//assume class
ClassNode * main_class = alloc_node < ClassNode > ( ) ;
main_class - > initializer = alloc_node < BlockNode > ( ) ;
2017-03-05 16:44:50 +01:00
main_class - > initializer - > parent_class = main_class ;
2015-12-28 23:31:52 +01:00
main_class - > ready = alloc_node < BlockNode > ( ) ;
2017-03-05 16:44:50 +01:00
main_class - > ready - > parent_class = main_class ;
current_class = main_class ;
2014-02-10 02:10:30 +01:00
_parse_class ( main_class ) ;
2017-11-16 18:38:18 +01:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_ERROR ) {
2017-03-05 16:44:50 +01:00
error_set = false ;
_set_error ( " Parse Error: " + tokenizer - > get_token_error ( ) ) ;
2014-02-10 02:10:30 +01:00
}
2018-06-21 03:41:26 +02:00
if ( error_set & & ! for_completion ) {
2018-05-30 04:16:52 +02:00
return ERR_PARSE_ERROR ;
}
2014-02-10 02:10:30 +01:00
2018-05-30 04:16:52 +02:00
_determine_inheritance ( main_class ) ;
if ( error_set ) {
2014-02-10 02:10:30 +01:00
return ERR_PARSE_ERROR ;
}
2018-05-30 04:16:52 +02:00
2018-05-30 04:16:54 +02:00
current_class = main_class ;
current_function = NULL ;
current_block = NULL ;
2018-06-19 07:55:52 +02:00
# ifdef DEBUG_ENABLED
2018-05-30 04:16:54 +02:00
if ( for_completion ) check_types = false ;
2018-06-19 07:55:52 +02:00
# else
check_types = false ;
# endif
2018-05-30 04:16:54 +02:00
2018-06-21 03:41:26 +02:00
// Resolve all class-level stuff before getting into function blocks
_check_class_level_types ( main_class ) ;
2018-05-30 04:16:54 +02:00
2018-06-21 03:41:26 +02:00
if ( error_set ) {
return ERR_PARSE_ERROR ;
2018-05-30 04:16:54 +02:00
}
// Resolve the function blocks
_check_class_blocks_types ( main_class ) ;
if ( error_set ) {
return ERR_PARSE_ERROR ;
}
2018-07-01 18:17:40 +02:00
# ifdef DEBUG_ENABLED
// Resolve warning ignores
Vector < Pair < int , String > > warning_skips = tokenizer - > get_warning_skips ( ) ;
bool warning_is_error = GLOBAL_GET ( " debug/gdscript/warnings/treat_warnings_as_errors " ) . booleanize ( ) ;
for ( List < GDScriptWarning > : : Element * E = warnings . front ( ) ; E ; ) {
GDScriptWarning & w = E - > get ( ) ;
int skip_index = - 1 ;
for ( int i = 0 ; i < warning_skips . size ( ) ; i + + ) {
if ( warning_skips [ i ] . first > = w . line ) {
break ;
}
skip_index = i ;
}
List < GDScriptWarning > : : Element * next = E - > next ( ) ;
bool erase = false ;
if ( skip_index ! = - 1 ) {
if ( warning_skips [ skip_index ] . second = = GDScriptWarning : : get_name_from_code ( w . code ) . to_lower ( ) ) {
erase = true ;
}
warning_skips . remove ( skip_index ) ;
}
if ( erase ) {
warnings . erase ( E ) ;
} else if ( warning_is_error ) {
_set_error ( w . get_message ( ) + " (warning treated as error) " , w . line ) ;
return ERR_PARSE_ERROR ;
}
E = next ;
}
# endif // DEBUG_ENABLED
2014-02-10 02:10:30 +01:00
return OK ;
}
2017-11-16 18:38:18 +01:00
Error GDScriptParser : : parse_bytecode ( const Vector < uint8_t > & p_bytecode , const String & p_base_path , const String & p_self_path ) {
2017-03-05 16:44:50 +01:00
2018-01-21 10:35:47 +01:00
clear ( ) ;
2017-03-05 16:44:50 +01:00
self_path = p_self_path ;
2017-11-16 18:38:18 +01:00
GDScriptTokenizerBuffer * tb = memnew ( GDScriptTokenizerBuffer ) ;
2014-02-25 13:31:47 +01:00
tb - > set_code_buffer ( p_bytecode ) ;
2017-03-05 16:44:50 +01:00
tokenizer = tb ;
2014-02-25 13:31:47 +01:00
Error ret = _parse ( p_base_path ) ;
memdelete ( tb ) ;
2017-03-05 16:44:50 +01:00
tokenizer = NULL ;
2014-02-25 13:31:47 +01:00
return ret ;
}
2018-06-05 18:50:21 +02:00
Error GDScriptParser : : parse ( const String & p_code , const String & p_base_path , bool p_just_validate , const String & p_self_path , bool p_for_completion , Set < int > * r_safe_lines ) {
2014-02-25 13:31:47 +01:00
2018-01-21 10:35:47 +01:00
clear ( ) ;
2014-12-17 02:31:57 +01:00
2017-03-05 16:44:50 +01:00
self_path = p_self_path ;
2017-11-16 18:38:18 +01:00
GDScriptTokenizerText * tt = memnew ( GDScriptTokenizerText ) ;
2014-02-25 13:31:47 +01:00
tt - > set_code ( p_code ) ;
2017-03-05 16:44:50 +01:00
validating = p_just_validate ;
for_completion = p_for_completion ;
2018-06-05 18:50:21 +02:00
# ifdef DEBUG_ENABLED
safe_lines = r_safe_lines ;
# endif // DEBUG_ENABLED
2017-03-05 16:44:50 +01:00
tokenizer = tt ;
2014-02-25 13:31:47 +01:00
Error ret = _parse ( p_base_path ) ;
memdelete ( tt ) ;
2017-03-05 16:44:50 +01:00
tokenizer = NULL ;
2014-02-25 13:31:47 +01:00
return ret ;
}
2017-11-16 18:38:18 +01:00
bool GDScriptParser : : is_tool_script ( ) const {
2016-01-23 19:36:03 +01:00
2017-03-05 16:44:50 +01:00
return ( head & & head - > type = = Node : : TYPE_CLASS & & static_cast < const ClassNode * > ( head ) - > tool ) ;
2016-01-23 19:36:03 +01:00
}
2017-11-16 18:38:18 +01:00
const GDScriptParser : : Node * GDScriptParser : : get_parse_tree ( ) const {
2014-02-10 02:10:30 +01:00
return head ;
}
2017-11-16 18:38:18 +01:00
void GDScriptParser : : clear ( ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
while ( list ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
Node * l = list ;
list = list - > next ;
2014-02-10 02:10:30 +01:00
memdelete ( l ) ;
}
2017-03-05 16:44:50 +01:00
head = NULL ;
list = NULL ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
completion_type = COMPLETION_NONE ;
completion_node = NULL ;
completion_class = NULL ;
completion_function = NULL ;
completion_block = NULL ;
current_block = NULL ;
current_class = NULL ;
2014-12-17 03:46:55 +01:00
2017-03-05 16:44:50 +01:00
completion_found = false ;
2018-05-13 07:07:56 +02:00
rpc_mode = MultiplayerAPI : : RPC_MODE_DISABLED ;
2015-08-30 16:50:10 +02:00
2017-03-05 16:44:50 +01:00
current_function = NULL ;
2014-12-17 02:31:57 +01:00
2017-03-05 16:44:50 +01:00
validating = false ;
for_completion = false ;
error_set = false ;
2014-02-10 02:10:30 +01:00
tab_level . clear ( ) ;
tab_level . push_back ( 0 ) ;
2017-03-05 16:44:50 +01:00
error_line = 0 ;
error_column = 0 ;
pending_newline = - 1 ;
parenthesis = 0 ;
current_export . type = Variant : : NIL ;
2018-05-30 04:16:54 +02:00
check_types = true ;
2017-03-05 16:44:50 +01:00
error = " " ;
2018-06-05 18:50:21 +02:00
# ifdef DEBUG_ENABLED
safe_lines = NULL ;
# endif // DEBUG_ENABLED
2014-02-10 02:10:30 +01:00
}
2017-11-16 18:38:18 +01:00
GDScriptParser : : CompletionType GDScriptParser : : get_completion_type ( ) {
2014-12-17 02:31:57 +01:00
return completion_type ;
}
2017-11-16 18:38:18 +01:00
StringName GDScriptParser : : get_completion_cursor ( ) {
2014-12-17 02:31:57 +01:00
return completion_cursor ;
}
2017-11-16 18:38:18 +01:00
int GDScriptParser : : get_completion_line ( ) {
2014-12-17 02:31:57 +01:00
return completion_line ;
}
2017-11-16 18:38:18 +01:00
Variant : : Type GDScriptParser : : get_completion_built_in_constant ( ) {
2014-12-17 02:31:57 +01:00
return completion_built_in_constant ;
}
2017-11-16 18:38:18 +01:00
GDScriptParser : : Node * GDScriptParser : : get_completion_node ( ) {
2014-12-17 02:31:57 +01:00
return completion_node ;
}
2017-11-16 18:38:18 +01:00
GDScriptParser : : BlockNode * GDScriptParser : : get_completion_block ( ) {
2014-12-17 02:31:57 +01:00
return completion_block ;
}
2017-11-16 18:38:18 +01:00
GDScriptParser : : ClassNode * GDScriptParser : : get_completion_class ( ) {
2014-12-17 02:31:57 +01:00
return completion_class ;
}
2017-11-16 18:38:18 +01:00
GDScriptParser : : FunctionNode * GDScriptParser : : get_completion_function ( ) {
2014-12-17 02:31:57 +01:00
return completion_function ;
}
2017-11-16 18:38:18 +01:00
int GDScriptParser : : get_completion_argument_index ( ) {
2014-12-17 02:31:57 +01:00
return completion_argument ;
}
2017-11-16 18:38:18 +01:00
int GDScriptParser : : get_completion_identifier_is_function ( ) {
2016-09-12 15:52:29 +02:00
return completion_ident_is_call ;
}
2017-11-16 18:38:18 +01:00
GDScriptParser : : GDScriptParser ( ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
head = NULL ;
list = NULL ;
tokenizer = NULL ;
pending_newline = - 1 ;
2014-02-10 02:10:30 +01:00
clear ( ) ;
}
2017-11-16 18:38:18 +01:00
GDScriptParser : : ~ GDScriptParser ( ) {
2014-02-10 02:10:30 +01:00
clear ( ) ;
}