2014-02-10 01:10:30 +00:00
/*************************************************************************/
2017-11-16 17:38:18 +00:00
/* gdscript_parser.cpp */
2014-02-10 01:10:30 +00:00
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
2017-08-27 12:16:55 +00:00
/* https://godotengine.org */
2014-02-10 01:10:30 +00:00
/*************************************************************************/
2020-01-01 10:16:22 +00:00
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
2014-02-10 01:10:30 +00: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-04 23:50:27 +00:00
2017-11-16 17:38:18 +00:00
# include "gdscript_parser.h"
2018-05-30 02:16:54 +00:00
# include "core/core_string_names.h"
# include "core/engine.h"
2018-09-11 16:13:45 +00:00
# include "core/io/resource_loader.h"
# include "core/os/file_access.h"
# include "core/print_string.h"
2018-05-30 02:16:54 +00:00
# include "core/project_settings.h"
# include "core/reference.h"
2018-09-11 16:13:45 +00:00
# include "core/script_language.h"
2017-11-16 17:38:18 +00:00
# include "gdscript.h"
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
template < class T >
2017-11-16 17:38:18 +00:00
T * GDScriptParser : : alloc_node ( ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
T * t = memnew ( T ) ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
t - > next = list ;
list = t ;
2014-02-10 01:10:30 +00:00
if ( ! head )
2017-03-05 15:44:50 +00:00
head = t ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
t - > line = tokenizer - > get_token_line ( ) ;
t - > column = tokenizer - > get_token_column ( ) ;
2014-02-10 01:10:30 +00:00
return t ;
}
2018-10-03 14:13:34 +00:00
# ifdef DEBUG_ENABLED
2018-07-01 16:17:40 +00:00
static String _find_function_name ( const GDScriptParser : : OperatorNode * p_call ) ;
2018-10-03 14:13:34 +00:00
# endif // DEBUG_ENABLED
2018-07-01 16:17:40 +00:00
2017-11-16 17:38:18 +00:00
bool GDScriptParser : : _end_statement ( ) {
2014-02-10 01:10:30 +00:00
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_SEMICOLON ) {
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2014-02-10 01:10:30 +00:00
return true ; //handle next
2017-11-16 17:38:18 +00:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_NEWLINE | | tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_EOF ) {
2014-02-10 01:10:30 +00:00
return true ; //will be handled properly
}
return false ;
}
2017-11-16 17:38:18 +00:00
bool GDScriptParser : : _enter_indent_block ( BlockNode * p_block ) {
2014-02-10 01:10:30 +00:00
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_COLON ) {
2015-11-29 22:45:28 +00: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 15:44:50 +00:00
_set_error ( " ':' expected at end of line. " , error_line , error_column ) ;
2014-02-10 01:10:30 +00:00
return false ;
}
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2014-02-10 01:10:30 +00:00
2018-09-16 21:29:17 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_EOF ) {
return false ;
}
2014-02-10 01:10:30 +00:00
2018-09-16 21:29:17 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_NEWLINE ) {
2015-12-29 14:41:37 +00:00
// be more python-like
2019-10-13 19:48:18 +00:00
IndentLevel current_level = indent_level . back ( ) - > get ( ) ;
indent_level . push_back ( current_level ) ;
2015-12-29 14:41:37 +00:00
return true ;
//_set_error("newline expected after ':'.");
//return false;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
while ( true ) {
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_NEWLINE ) {
2016-05-29 14:37:26 +00:00
2014-02-10 01:10:30 +00:00
return false ; //wtf
2018-09-16 21:29:17 +00:00
} else if ( tokenizer - > get_token ( 1 ) = = GDScriptTokenizer : : TK_EOF ) {
return false ;
2017-11-16 17:38:18 +00:00
} else if ( tokenizer - > get_token ( 1 ) ! = GDScriptTokenizer : : TK_NEWLINE ) {
2014-02-10 01:10:30 +00:00
2014-02-25 12:31:47 +00:00
int indent = tokenizer - > get_token_line_indent ( ) ;
2019-10-13 19:48:18 +00:00
int tabs = tokenizer - > get_token_line_tab_indent ( ) ;
IndentLevel current_level = indent_level . back ( ) - > get ( ) ;
IndentLevel new_indent ( indent , tabs ) ;
if ( new_indent . is_mixed ( current_level ) ) {
_set_error ( " Mixed tabs and spaces in indentation. " ) ;
2014-02-10 01:10:30 +00:00
return false ;
2015-12-29 14:41:37 +00:00
}
2014-02-10 01:10:30 +00:00
2019-10-13 19:48:18 +00:00
if ( indent < = current_level . indent ) {
return false ;
}
indent_level . push_back ( new_indent ) ;
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2014-02-10 01:10:30 +00:00
return true ;
} else if ( p_block ) {
NewLineNode * nl = alloc_node < NewLineNode > ( ) ;
2017-03-05 15:44:50 +00:00
nl - > line = tokenizer - > get_token_line ( ) ;
2014-02-10 01:10:30 +00:00
p_block - > statements . push_back ( nl ) ;
}
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ; // go to next newline
2014-02-10 01:10:30 +00:00
}
}
2019-07-03 14:28:50 +00:00
bool GDScriptParser : : _parse_arguments ( Node * p_parent , Vector < Node * > & p_args , bool p_static , bool p_can_codecomplete , bool p_parsing_constant ) {
2014-02-10 01:10:30 +00:00
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE ) {
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2014-02-10 01:10:30 +00:00
} else {
2017-03-05 15:44:50 +00:00
parenthesis + + ;
int argidx = 0 ;
2014-12-17 01:31:57 +00:00
2017-03-05 15:44:50 +00:00
while ( true ) {
2014-02-10 01:10:30 +00:00
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CURSOR ) {
2014-12-17 01:31:57 +00:00
_make_completable_call ( argidx ) ;
2017-03-05 15:44:50 +00:00
completion_node = p_parent ;
2017-11-16 17:38:18 +00: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 04:14:31 +00:00
//completing a string argument..
2017-03-05 15:44:50 +00:00
completion_cursor = tokenizer - > get_token_constant ( ) ;
2015-06-26 04:14:31 +00:00
_make_completable_call ( argidx ) ;
2017-03-05 15:44:50 +00:00
completion_node = p_parent ;
2015-06-26 04:14:31 +00:00
tokenizer - > advance ( 1 ) ;
return false ;
2014-12-17 01:31:57 +00:00
}
2014-02-10 01:10:30 +00:00
2019-07-03 14:28:50 +00:00
Node * arg = _parse_expression ( p_parent , p_static , false , p_parsing_constant ) ;
2018-06-21 01:41:26 +00:00
if ( ! arg ) {
2014-02-10 01:10:30 +00:00
return false ;
2018-06-21 01:41:26 +00:00
}
2014-02-10 01:10:30 +00:00
p_args . push_back ( arg ) ;
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE ) {
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2014-02-10 01:10:30 +00:00
break ;
2017-11-16 17:38:18 +00:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COMMA ) {
2014-02-10 01:10:30 +00:00
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( 1 ) = = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE ) {
2014-02-10 01:10:30 +00:00
_set_error ( " Expression expected " ) ;
return false ;
}
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2014-12-17 01:31:57 +00:00
argidx + + ;
2014-02-10 01:10:30 +00:00
} else {
// something is broken
_set_error ( " Expected ',' or ')' " ) ;
return false ;
}
}
2017-03-05 15:44:50 +00:00
parenthesis - - ;
2014-02-10 01:10:30 +00:00
}
return true ;
}
2017-11-16 17:38:18 +00:00
void GDScriptParser : : _make_completable_call ( int p_arg ) {
2014-12-17 01:31:57 +00:00
2017-03-05 15:44:50 +00: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 01:31:57 +00:00
tokenizer - > advance ( ) ;
}
2017-11-16 17:38:18 +00:00
bool GDScriptParser : : _get_completable_identifier ( CompletionType p_type , StringName & identifier ) {
2014-12-17 01:31:57 +00:00
2017-03-05 15:44:50 +00:00
identifier = StringName ( ) ;
2017-03-31 17:28:34 +00:00
if ( tokenizer - > is_token_literal ( ) ) {
identifier = tokenizer - > get_token_literal ( ) ;
2014-12-17 01:31:57 +00:00
tokenizer - > advance ( ) ;
}
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CURSOR ) {
2017-03-05 15:44:50 +00: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 01:31:57 +00:00
tokenizer - > advance ( ) ;
2017-03-31 17:28:34 +00:00
if ( tokenizer - > is_token_literal ( ) ) {
identifier = identifier . operator String ( ) + tokenizer - > get_token_literal ( ) . operator String ( ) ;
2014-12-17 01:31:57 +00:00
tokenizer - > advance ( ) ;
}
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PARENTHESIS_OPEN ) {
2017-03-05 15:44:50 +00:00
completion_ident_is_call = true ;
2016-09-12 13:52:29 +00:00
}
2014-12-17 01:31:57 +00:00
return true ;
}
return false ;
}
2017-11-16 17:38:18 +00:00
GDScriptParser : : Node * GDScriptParser : : _parse_expression ( Node * p_parent , bool p_static , bool p_allow_assign , bool p_parsing_constant ) {
2014-02-10 01:10:30 +00:00
2017-01-14 11:26:56 +00:00
//Vector<Node*> expressions;
//Vector<OperatorNode::Operator> operators;
2014-02-10 01:10:30 +00:00
Vector < Expression > expression ;
2017-03-05 15:44:50 +00:00
Node * expr = NULL ;
2014-02-10 01:10:30 +00:00
2016-07-22 12:22:34 +00: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 15:44:50 +00:00
while ( true ) {
2014-02-10 01:10:30 +00:00
/*****************/
/* Parse Operand */
/*****************/
2017-03-05 15:44:50 +00:00
if ( parenthesis > 0 ) {
2014-04-05 21:50:09 +00:00
//remove empty space (only allowed if inside parenthesis
2017-11-16 17:38:18 +00:00
while ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_NEWLINE ) {
2014-04-05 21:50:09 +00:00
tokenizer - > advance ( ) ;
}
}
2019-09-02 11:46:38 +00:00
// Check that the next token is not TK_CURSOR and if it is, the offset should be incremented.
int next_valid_offset = 1 ;
if ( tokenizer - > get_token ( next_valid_offset ) = = GDScriptTokenizer : : TK_CURSOR ) {
next_valid_offset + + ;
// There is a chunk of the identifier that also needs to be ignored (not always there!)
if ( tokenizer - > get_token ( next_valid_offset ) = = GDScriptTokenizer : : TK_IDENTIFIER ) {
next_valid_offset + + ;
}
}
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PARENTHESIS_OPEN ) {
2014-02-10 01:10:30 +00:00
//subexpression ()
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2014-04-05 21:50:09 +00:00
parenthesis + + ;
2017-03-05 15:44:50 +00:00
Node * subexpr = _parse_expression ( p_parent , p_static , p_allow_assign , p_parsing_constant ) ;
2014-04-05 21:50:09 +00:00
parenthesis - - ;
2014-02-10 01:10:30 +00:00
if ( ! subexpr )
return NULL ;
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE ) {
2014-02-10 01:10:30 +00:00
_set_error ( " Expected ')' in expression " ) ;
return NULL ;
}
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2017-03-05 15:44:50 +00:00
expr = subexpr ;
2017-11-16 17:38:18 +00:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_DOLLAR ) {
2017-01-08 05:04:53 +00:00
tokenizer - > advance ( ) ;
String path ;
2017-03-05 15:44:50 +00:00
bool need_identifier = true ;
bool done = false ;
2018-06-06 01:57:44 +00:00
int line = tokenizer - > get_token_line ( ) ;
2017-01-08 05:04:53 +00:00
2017-03-05 15:44:50 +00:00
while ( ! done ) {
2017-01-08 05:04:53 +00:00
2017-03-05 15:44:50 +00:00
switch ( tokenizer - > get_token ( ) ) {
2017-11-16 17:38:18 +00:00
case GDScriptTokenizer : : TK_CURSOR : {
2017-03-05 15:44:50 +00:00
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 05:04:53 +00:00
tokenizer - > advance ( ) ;
} break ;
2017-11-16 17:38:18 +00:00
case GDScriptTokenizer : : TK_CONSTANT : {
2017-01-08 05:04:53 +00:00
2017-01-08 06:01:52 +00:00
if ( ! need_identifier ) {
2017-03-05 15:44:50 +00:00
done = true ;
2017-01-08 05:04:53 +00:00
break ;
2017-01-08 06:01:52 +00:00
}
2017-01-08 05:04:53 +00:00
2017-03-05 15:44:50 +00:00
if ( tokenizer - > get_token_constant ( ) . get_type ( ) ! = Variant : : STRING ) {
2017-01-08 05:04:53 +00:00
_set_error ( " Expected string constant or identifier after '$' or '/'. " ) ;
return NULL ;
}
2017-03-05 15:44:50 +00:00
path + = String ( tokenizer - > get_token_constant ( ) ) ;
2017-01-08 05:04:53 +00:00
tokenizer - > advance ( ) ;
2017-03-05 15:44:50 +00:00
need_identifier = false ;
2017-01-08 05:04:53 +00:00
} break ;
2017-11-16 17:38:18 +00:00
case GDScriptTokenizer : : TK_OP_DIV : {
2017-01-08 05:04:53 +00:00
2017-01-08 06:01:52 +00:00
if ( need_identifier ) {
2017-03-05 15:44:50 +00:00
done = true ;
2017-01-08 05:04:53 +00:00
break ;
2017-01-08 06:01:52 +00:00
}
2017-01-08 05:04:53 +00:00
2017-03-05 15:44:50 +00:00
path + = " / " ;
2017-01-08 05:04:53 +00:00
tokenizer - > advance ( ) ;
2017-03-05 15:44:50 +00:00
need_identifier = true ;
2017-01-08 05:04:53 +00:00
} break ;
default : {
2017-03-31 17:28:34 +00: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 15:51:03 +00:00
} else {
done = true ;
2017-03-31 17:28:34 +00:00
}
2017-01-08 05:04:53 +00:00
break ;
}
}
}
2017-03-05 15:44:50 +00:00
if ( path = = " " ) {
2017-01-08 05:04:53 +00:00
_set_error ( " Path expected after $. " ) ;
return NULL ;
}
OperatorNode * op = alloc_node < OperatorNode > ( ) ;
2017-03-05 15:44:50 +00:00
op - > op = OperatorNode : : OP_CALL ;
2018-06-06 01:57:44 +00:00
op - > line = line ;
2017-01-08 05:04:53 +00:00
op - > arguments . push_back ( alloc_node < SelfNode > ( ) ) ;
2018-06-06 01:57:44 +00:00
op - > arguments [ 0 ] - > line = line ;
2017-01-08 05:04:53 +00:00
IdentifierNode * funcname = alloc_node < IdentifierNode > ( ) ;
2017-03-05 15:44:50 +00:00
funcname - > name = " get_node " ;
2018-06-06 01:57:44 +00:00
funcname - > line = line ;
2017-01-08 05:04:53 +00:00
op - > arguments . push_back ( funcname ) ;
ConstantNode * nodepath = alloc_node < ConstantNode > ( ) ;
nodepath - > value = NodePath ( StringName ( path ) ) ;
2018-05-30 02:16:54 +00:00
nodepath - > datatype = _type_from_variant ( nodepath - > value ) ;
2018-06-06 01:57:44 +00:00
nodepath - > line = line ;
2017-01-08 05:04:53 +00:00
op - > arguments . push_back ( nodepath ) ;
2017-03-05 15:44:50 +00:00
expr = op ;
2017-01-08 05:04:53 +00:00
2017-11-16 17:38:18 +00:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CURSOR ) {
2014-12-17 01:31:57 +00:00
tokenizer - > advance ( ) ;
continue ; //no point in cursor in the middle of expression
2014-02-10 01:10:30 +00:00
2017-11-16 17:38:18 +00:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CONSTANT ) {
2014-02-10 01:10:30 +00:00
//constant defined by tokenizer
ConstantNode * constant = alloc_node < ConstantNode > ( ) ;
2017-03-05 15:44:50 +00:00
constant - > value = tokenizer - > get_token_constant ( ) ;
2018-05-30 02:16:54 +00:00
constant - > datatype = _type_from_variant ( constant - > value ) ;
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2017-03-05 15:44:50 +00:00
expr = constant ;
2017-11-16 17:38:18 +00:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CONST_PI ) {
2016-01-02 20:56:45 +00:00
//constant defined by tokenizer
ConstantNode * constant = alloc_node < ConstantNode > ( ) ;
2017-03-05 15:44:50 +00:00
constant - > value = Math_PI ;
2018-05-30 02:16:54 +00:00
constant - > datatype = _type_from_variant ( constant - > value ) ;
2016-01-02 20:56:45 +00:00
tokenizer - > advance ( ) ;
2017-03-05 15:44:50 +00:00
expr = constant ;
2017-11-16 17:38:18 +00:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CONST_TAU ) {
2017-11-04 09:34:27 +00:00
//constant defined by tokenizer
ConstantNode * constant = alloc_node < ConstantNode > ( ) ;
constant - > value = Math_TAU ;
2018-05-30 02:16:54 +00:00
constant - > datatype = _type_from_variant ( constant - > value ) ;
2017-11-04 09:34:27 +00:00
tokenizer - > advance ( ) ;
expr = constant ;
2017-11-16 17:38:18 +00:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CONST_INF ) {
2017-02-06 22:44:22 +00:00
//constant defined by tokenizer
ConstantNode * constant = alloc_node < ConstantNode > ( ) ;
constant - > value = Math_INF ;
2018-05-30 02:16:54 +00:00
constant - > datatype = _type_from_variant ( constant - > value ) ;
2017-02-06 22:44:22 +00:00
tokenizer - > advance ( ) ;
expr = constant ;
2017-11-16 17:38:18 +00:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CONST_NAN ) {
2017-02-06 22:44:22 +00:00
//constant defined by tokenizer
ConstantNode * constant = alloc_node < ConstantNode > ( ) ;
constant - > value = Math_NAN ;
2018-05-30 02:16:54 +00:00
constant - > datatype = _type_from_variant ( constant - > value ) ;
2017-02-06 22:44:22 +00:00
tokenizer - > advance ( ) ;
expr = constant ;
2017-11-16 17:38:18 +00:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PR_PRELOAD ) {
2014-02-10 01:10:30 +00:00
//constant defined by tokenizer
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PARENTHESIS_OPEN ) {
2014-02-10 01:10:30 +00:00
_set_error ( " Expected '(' after 'preload' " ) ;
return NULL ;
}
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2016-01-24 22:45:11 +00:00
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CURSOR ) {
2017-05-08 19:39:27 +00: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 22:45:11 +00:00
String path ;
2017-02-16 13:29:18 +00:00
bool found_constant = false ;
2016-01-24 22:45:11 +00:00
bool valid = false ;
2017-02-16 13:29:18 +00:00
ConstantNode * cn ;
2016-01-24 22:45:11 +00:00
Node * subexpr = _parse_and_reduce_expression ( p_parent , p_static ) ;
if ( subexpr ) {
if ( subexpr - > type = = Node : : TYPE_CONSTANT ) {
2017-03-05 15:44:50 +00:00
cn = static_cast < ConstantNode * > ( subexpr ) ;
2017-02-16 13:29:18 +00:00
found_constant = true ;
}
if ( subexpr - > type = = Node : : TYPE_IDENTIFIER ) {
2017-03-05 15:44:50 +00:00
IdentifierNode * in = static_cast < IdentifierNode * > ( subexpr ) ;
2017-02-16 13:29:18 +00:00
// Try to find the constant expression by the identifier
2018-05-30 02:16:54 +00: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 13:29:18 +00:00
}
2016-01-24 22:45:11 +00:00
}
}
2017-02-16 13:29:18 +00:00
if ( found_constant & & cn - > value . get_type ( ) = = Variant : : STRING ) {
valid = true ;
2017-03-05 15:44:50 +00:00
path = ( String ) cn - > value ;
2017-02-16 13:29:18 +00:00
}
2016-01-24 22:45:11 +00:00
}
2017-02-16 13:29:18 +00:00
2016-01-24 22:45:11 +00:00
if ( ! valid ) {
_set_error ( " expected string constant as 'preload' argument. " ) ;
2014-02-10 01:10:30 +00:00
return NULL ;
}
2017-02-16 13:29:18 +00:00
2017-03-05 15:44:50 +00:00
if ( ! path . is_abs_path ( ) & & base_path ! = " " )
2019-06-16 12:31:57 +00:00
path = base_path . plus_file ( path ) ;
2017-03-05 15:44:50 +00:00
path = path . replace ( " /// " , " // " ) . simplify_path ( ) ;
if ( path = = self_path ) {
2014-12-07 05:04:20 +00:00
_set_error ( " Can't preload itself (use 'get_script()'). " ) ;
return NULL ;
}
2014-06-28 02:21:45 +00:00
Ref < Resource > res ;
2019-03-03 19:36:42 +00:00
dependencies . push_back ( path ) ;
if ( ! dependencies_only ) {
if ( ! validating ) {
2014-06-28 02:21:45 +00:00
2019-03-03 19:36:42 +00:00
//this can be too slow for just validating code
if ( for_completion & & ScriptCodeCompletionCache : : get_singleton ( ) & & FileAccess : : exists ( path ) ) {
res = ScriptCodeCompletionCache : : get_singleton ( ) - > get_cached_resource ( path ) ;
} else if ( ! for_completion | | FileAccess : : exists ( path ) ) {
res = ResourceLoader : : load ( path ) ;
}
} else {
2014-06-28 02:21:45 +00:00
2019-03-03 19:36:42 +00:00
if ( ! FileAccess : : exists ( path ) ) {
_set_error ( " Can't preload resource at path: " + path ) ;
return NULL ;
} else if ( ScriptCodeCompletionCache : : get_singleton ( ) ) {
res = ScriptCodeCompletionCache : : get_singleton ( ) - > get_cached_resource ( path ) ;
}
}
if ( ! res . is_valid ( ) ) {
2017-03-05 15:44:50 +00:00
_set_error ( " Can't preload resource at path: " + path ) ;
2014-06-28 02:21:45 +00:00
return NULL ;
}
2014-02-10 01:10:30 +00:00
}
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE ) {
2014-02-10 01:10:30 +00:00
_set_error ( " Expected ')' after 'preload' path " ) ;
return NULL ;
}
2018-05-30 02:16:54 +00:00
Ref < GDScript > gds = res ;
if ( gds . is_valid ( ) & & ! gds - > is_valid ( ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Couldn't fully preload the script, possible cyclic reference or compilation error. Use \" load() \" instead if a cyclic reference is intended. " ) ;
2018-05-30 02:16:54 +00:00
return NULL ;
}
2017-05-08 19:39:27 +00:00
tokenizer - > advance ( ) ;
2014-02-10 01:10:30 +00:00
ConstantNode * constant = alloc_node < ConstantNode > ( ) ;
2017-03-05 15:44:50 +00:00
constant - > value = res ;
2018-05-30 02:16:54 +00:00
constant - > datatype = _type_from_variant ( constant - > value ) ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
expr = constant ;
2017-11-16 17:38:18 +00:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PR_YIELD ) {
2014-09-15 14:33:30 +00:00
2018-05-30 02:16:54 +00:00
if ( ! current_function ) {
2019-08-23 18:51:42 +00:00
_set_error ( " \" yield() \" can only be used inside function blocks. " ) ;
2018-05-30 02:16:54 +00:00
return NULL ;
}
current_function - > has_yield = true ;
2014-09-15 14:33:30 +00:00
tokenizer - > advance ( ) ;
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PARENTHESIS_OPEN ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected \" ( \" after \" yield \" . " ) ;
2014-09-15 14:33:30 +00:00
return NULL ;
}
tokenizer - > advance ( ) ;
OperatorNode * yield = alloc_node < OperatorNode > ( ) ;
2017-03-05 15:44:50 +00:00
yield - > op = OperatorNode : : OP_YIELD ;
2014-09-15 14:33:30 +00:00
2017-11-16 17:38:18 +00:00
while ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_NEWLINE ) {
2016-10-03 18:18:21 +00:00
tokenizer - > advance ( ) ;
}
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE ) {
2017-03-05 15:44:50 +00:00
expr = yield ;
2014-09-15 14:33:30 +00:00
tokenizer - > advance ( ) ;
} else {
2017-03-05 15:44:50 +00:00
parenthesis + + ;
2016-10-03 18:18:21 +00:00
2017-03-05 15:44:50 +00:00
Node * object = _parse_and_reduce_expression ( p_parent , p_static ) ;
2014-09-15 14:33:30 +00:00
if ( ! object )
return NULL ;
yield - > arguments . push_back ( object ) ;
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_COMMA ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected \" , \" after the first argument of \" yield \" . " ) ;
2014-09-15 14:33:30 +00:00
return NULL ;
}
tokenizer - > advance ( ) ;
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CURSOR ) {
2017-03-05 15:44:50 +00: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 01:11:03 +00:00
tokenizer - > advance ( ) ;
}
2017-03-05 15:44:50 +00:00
Node * signal = _parse_and_reduce_expression ( p_parent , p_static ) ;
2014-09-15 14:33:30 +00:00
if ( ! signal )
return NULL ;
yield - > arguments . push_back ( signal ) ;
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected \" ) \" after the second argument of \" yield \" . " ) ;
2014-09-15 14:33:30 +00:00
return NULL ;
}
2017-03-05 15:44:50 +00:00
parenthesis - - ;
2016-10-03 18:18:21 +00:00
2014-09-15 14:33:30 +00:00
tokenizer - > advance ( ) ;
2017-03-05 15:44:50 +00:00
expr = yield ;
2014-09-15 14:33:30 +00:00
}
2017-11-16 17:38:18 +00:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_SELF ) {
2014-02-10 01:10:30 +00:00
if ( p_static ) {
2019-08-23 18:51:42 +00:00
_set_error ( " \" self \" isn't allowed in a static function or constant expression. " ) ;
2014-02-10 01:10:30 +00:00
return NULL ;
}
//constant defined by tokenizer
SelfNode * self = alloc_node < SelfNode > ( ) ;
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2017-03-05 15:44:50 +00:00
expr = self ;
2017-11-16 17:38:18 +00:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_BUILT_IN_TYPE & & tokenizer - > get_token ( 1 ) = = GDScriptTokenizer : : TK_PERIOD ) {
2014-02-10 01:10:30 +00:00
2014-02-25 12:31:47 +00:00
Variant : : Type bi_type = tokenizer - > get_token_type ( ) ;
tokenizer - > advance ( 2 ) ;
2014-12-17 01:31:57 +00:00
StringName identifier ;
2017-03-05 15:44:50 +00:00
if ( _get_completable_identifier ( COMPLETION_BUILT_IN_TYPE_CONSTANT , identifier ) ) {
2014-12-17 01:31:57 +00:00
2017-03-05 15:44:50 +00:00
completion_built_in_constant = bi_type ;
2014-12-17 01:31:57 +00:00
}
2017-03-05 15:44:50 +00:00
if ( identifier = = StringName ( ) ) {
2014-02-10 01:10:30 +00:00
2019-08-23 18:51:42 +00:00
_set_error ( " Built-in type constant or static function expected after \" . \" . " ) ;
2014-02-10 01:10:30 +00:00
return NULL ;
}
2017-12-15 15:43:27 +00:00
if ( ! Variant : : has_constant ( bi_type , identifier ) ) {
2014-02-10 01:10:30 +00:00
2018-01-18 21:03:34 +00: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 01:10:30 +00:00
2019-07-03 14:28:50 +00:00
if ( ! _parse_arguments ( op , op - > arguments , p_static , true , p_parsing_constant ) )
2018-01-18 21:03:34 +00:00
return NULL ;
expr = op ;
} else {
2018-09-10 13:31:36 +00: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 21:03:34 +00:00
}
} else {
ConstantNode * cn = alloc_node < ConstantNode > ( ) ;
2017-12-15 15:43:27 +00:00
cn - > value = Variant : : get_constant_value ( bi_type , identifier ) ;
2018-05-30 02:16:54 +00:00
cn - > datatype = _type_from_variant ( cn - > value ) ;
2018-01-18 21:03:34 +00:00
expr = cn ;
}
2014-12-17 01:31:57 +00:00
2019-09-02 11:46:38 +00:00
} else if ( tokenizer - > get_token ( next_valid_offset ) = = GDScriptTokenizer : : TK_PARENTHESIS_OPEN & & tokenizer - > is_token_literal ( ) ) {
2017-03-31 17:28:34 +00:00
// We check with is_token_literal, as this allows us to use match/sync/etc. as a name
2014-02-10 01:10:30 +00:00
//function or constructor
OperatorNode * op = alloc_node < OperatorNode > ( ) ;
2017-03-05 15:44:50 +00:00
op - > op = OperatorNode : : OP_CALL ;
2014-02-10 01:10:30 +00:00
2017-11-17 04:42:24 +00:00
//Do a quick Array and Dictionary Check. Replace if either require no arguments.
bool replaced = false ;
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_BUILT_IN_TYPE ) {
2017-11-17 04:42:24 +00:00
Variant : : Type ct = tokenizer - > get_token_type ( ) ;
2018-10-06 20:20:41 +00:00
if ( ! p_parsing_constant ) {
2017-11-17 04:42:24 +00: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 01:10:30 +00:00
2017-11-17 04:42:24 +00: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 17:38:18 +00:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_BUILT_IN_FUNC ) {
2014-02-10 01:10:30 +00:00
BuiltInFunctionNode * bn = alloc_node < BuiltInFunctionNode > ( ) ;
2017-03-05 15:44:50 +00:00
bn - > function = tokenizer - > get_token_built_in_func ( ) ;
2014-02-10 01:10:30 +00:00
op - > arguments . push_back ( bn ) ;
2014-12-17 01:31:57 +00:00
tokenizer - > advance ( 2 ) ;
2014-02-10 01:10:30 +00:00
} else {
SelfNode * self = alloc_node < SelfNode > ( ) ;
op - > arguments . push_back ( self ) ;
2014-12-17 01:31:57 +00:00
StringName identifier ;
2017-03-05 15:44:50 +00:00
if ( _get_completable_identifier ( COMPLETION_FUNCTION , identifier ) ) {
2014-12-17 01:31:57 +00:00
}
2017-03-05 15:44:50 +00:00
IdentifierNode * id = alloc_node < IdentifierNode > ( ) ;
id - > name = identifier ;
2014-02-10 01:10:30 +00:00
op - > arguments . push_back ( id ) ;
2014-12-17 01:31:57 +00:00
tokenizer - > advance ( 1 ) ;
2014-02-10 01:10:30 +00:00
}
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CURSOR ) {
2014-12-17 01:31:57 +00:00
_make_completable_call ( 0 ) ;
2017-03-05 15:44:50 +00:00
completion_node = op ;
2014-12-17 01:31:57 +00:00
}
2017-11-17 04:42:24 +00:00
if ( ! replaced ) {
2019-07-03 14:28:50 +00:00
if ( ! _parse_arguments ( op , op - > arguments , p_static , true , p_parsing_constant ) )
2017-11-17 04:42:24 +00:00
return NULL ;
expr = op ;
}
2017-03-31 17:28:34 +00: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 01:10:30 +00:00
//identifier (reference)
2017-03-05 15:44:50 +00:00
const ClassNode * cln = current_class ;
bool bfn = false ;
StringName identifier ;
2018-05-30 02:16:54 +00:00
int id_line = tokenizer - > get_token_line ( ) ;
2017-03-05 15:44:50 +00:00
if ( _get_completable_identifier ( COMPLETION_IDENTIFIER , identifier ) ) {
2014-12-17 01:31:57 +00:00
}
2018-05-30 02:16:54 +00:00
BlockNode * b = current_block ;
2018-07-01 16:17:40 +00:00
while ( ! bfn & & b ) {
2018-05-30 02:16:54 +00: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 01:31:57 +00:00
2018-07-01 16:17:40 +00:00
# ifdef DEBUG_ENABLED
2018-10-03 14:13:34 +00:00
LocalVarNode * lv = b - > variables [ identifier ] ;
2018-05-30 02:16:54 +00: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 16:17:40 +00: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 02:16:54 +00:00
}
2020-02-22 19:47:50 +00:00
[[fallthrough]] ;
2019-06-28 09:25:09 +00:00
}
2018-05-30 02:16:54 +00:00
case GDScriptTokenizer : : TK_OP_ASSIGN : {
lv - > assignments + = 1 ;
2018-07-01 16:17:40 +00:00
lv - > usages - - ; // Assignment is not really usage
} break ;
default : {
lv - > usages + + ;
2018-05-30 02:16:54 +00:00
}
2016-06-30 13:40:13 +00:00
}
2018-07-01 16:17:40 +00:00
# endif // DEBUG_ENABLED
2018-05-30 02:16:54 +00:00
break ;
2014-12-17 01:31:57 +00:00
}
2018-05-30 02:16:54 +00:00
b = b - > parent_block ;
}
2014-12-17 01:31:57 +00:00
2018-05-30 02:16:54 +00: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 13:40:13 +00:00
//check from constants
ConstantNode * constant = alloc_node < ConstantNode > ( ) ;
2017-03-05 15:44:50 +00:00
constant - > value = GDScriptLanguage : : get_singleton ( ) - > get_global_array ( ) [ GDScriptLanguage : : get_singleton ( ) - > get_global_map ( ) [ identifier ] ] ;
2018-05-30 02:16:54 +00:00
constant - > datatype = _type_from_variant ( constant - > value ) ;
constant - > line = id_line ;
2017-03-05 15:44:50 +00:00
expr = constant ;
2016-06-30 13:40:13 +00:00
bfn = true ;
}
2018-05-28 16:38:35 +00: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 ;
}
2019-01-17 21:17:06 +00:00
2019-03-03 19:36:42 +00:00
if ( ! dependencies_only ) {
2019-03-05 21:19:02 +00:00
if ( ! bfn & & ScriptServer : : is_global_class ( identifier ) ) {
Ref < Script > scr = ResourceLoader : : load ( ScriptServer : : get_global_class_path ( identifier ) ) ;
if ( scr . is_valid ( ) & & scr - > is_valid ( ) ) {
ConstantNode * constant = alloc_node < ConstantNode > ( ) ;
constant - > value = scr ;
expr = constant ;
bfn = true ;
}
}
2019-03-03 19:36:42 +00:00
// Check parents for the constant
2019-07-03 14:59:29 +00:00
if ( ! bfn ) {
// Using current_class instead of cln here, since cln is const*
_determine_inheritance ( current_class , false ) ;
if ( cln - > base_type . has_type & & cln - > base_type . kind = = DataType : : GDSCRIPT & & cln - > base_type . script_type - > is_valid ( ) ) {
2019-03-03 19:36:42 +00:00
Map < StringName , Variant > parent_constants ;
2019-07-03 14:59:29 +00:00
current_class - > base_type . script_type - > get_constants ( & parent_constants ) ;
2019-03-03 19:36:42 +00:00
if ( parent_constants . has ( identifier ) ) {
ConstantNode * constant = alloc_node < ConstantNode > ( ) ;
constant - > value = parent_constants [ identifier ] ;
expr = constant ;
bfn = true ;
}
2019-01-17 21:17:06 +00:00
}
}
}
2016-06-30 01:17:55 +00:00
}
2017-03-05 15:44:50 +00:00
if ( ! bfn ) {
2018-07-01 16:17:40 +00:00
# ifdef DEBUG_ENABLED
if ( current_function ) {
int arg_idx = current_function - > arguments . find ( identifier ) ;
if ( arg_idx ! = - 1 ) {
2019-10-19 13:59:57 +00: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 :
case GDScriptTokenizer : : TK_OP_ASSIGN : {
// Assignment is not really usage
} break ;
default : {
current_function - > arguments_usage . write [ arg_idx ] = current_function - > arguments_usage [ arg_idx ] + 1 ;
}
2018-07-01 16:17:40 +00:00
}
}
}
# endif // DEBUG_ENABLED
2014-12-17 01:31:57 +00:00
IdentifierNode * id = alloc_node < IdentifierNode > ( ) ;
id - > name = identifier ;
2018-05-30 02:16:54 +00:00
id - > line = id_line ;
2014-12-17 01:31:57 +00:00
expr = id ;
}
2014-02-10 01:10:30 +00:00
2017-11-16 17:38:18 +00: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 01:10:30 +00:00
2016-10-12 21:43:59 +00:00
//single prefix operators like !expr +expr -expr ++expr --expr
2016-07-09 01:12:50 +00:00
alloc_node < OperatorNode > ( ) ;
2014-02-10 01:10:30 +00:00
Expression e ;
2017-03-05 15:44:50 +00:00
e . is_op = true ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
switch ( tokenizer - > get_token ( ) ) {
2017-11-16 17:38:18 +00: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 ;
2019-04-09 15:08:36 +00:00
default : {
}
2014-02-10 01:10:30 +00:00
}
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2014-02-10 01:10:30 +00:00
2017-11-16 17:38:18 +00:00
if ( e . op ! = OperatorNode : : OP_NOT & & tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_OP_NOT ) {
2014-02-10 01:10:30 +00: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 16:31:23 +00: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
2019-08-09 04:52:27 +00:00
if ( ! expr ) {
_set_error ( " Expected identifier before 'is' operator " ) ;
return NULL ;
}
2018-08-26 16:31:23 +00:00
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 17:38:18 +00:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_BRACKET_OPEN ) {
2014-02-10 01:10:30 +00:00
// array
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2014-02-10 01:10:30 +00:00
ArrayNode * arr = alloc_node < ArrayNode > ( ) ;
2017-03-05 15:44:50 +00:00
bool expecting_comma = false ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
while ( true ) {
2014-02-10 01:10:30 +00:00
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_EOF ) {
2014-02-10 01:10:30 +00:00
_set_error ( " Unterminated array " ) ;
return NULL ;
2017-11-16 17:38:18 +00:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_BRACKET_CLOSE ) {
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2014-02-10 01:10:30 +00:00
break ;
2017-11-16 17:38:18 +00:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_NEWLINE ) {
2014-02-10 01:10:30 +00:00
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ; //ignore newline
2017-11-16 17:38:18 +00:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COMMA ) {
2014-02-10 01:10:30 +00:00
if ( ! expecting_comma ) {
_set_error ( " expression or ']' expected " ) ;
return NULL ;
}
2017-03-05 15:44:50 +00:00
expecting_comma = false ;
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ; //ignore newline
2014-02-10 01:10:30 +00:00
} else {
//parse expression
if ( expecting_comma ) {
_set_error ( " ',' or ']' expected " ) ;
return NULL ;
}
2017-03-05 15:44:50 +00:00
Node * n = _parse_expression ( arr , p_static , p_allow_assign , p_parsing_constant ) ;
2014-02-10 01:10:30 +00:00
if ( ! n )
return NULL ;
arr - > elements . push_back ( n ) ;
2017-03-05 15:44:50 +00:00
expecting_comma = true ;
2014-02-10 01:10:30 +00:00
}
}
2017-03-05 15:44:50 +00:00
expr = arr ;
2017-11-16 17:38:18 +00:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CURLY_BRACKET_OPEN ) {
2014-02-10 01:10:30 +00:00
// array
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2014-02-10 01:10:30 +00:00
DictionaryNode * dict = alloc_node < DictionaryNode > ( ) ;
enum DictExpect {
DICT_EXPECT_KEY ,
DICT_EXPECT_COLON ,
DICT_EXPECT_VALUE ,
DICT_EXPECT_COMMA
} ;
2017-03-05 15:44:50 +00:00
Node * key = NULL ;
2016-11-26 12:40:13 +00:00
Set < Variant > keys ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
DictExpect expecting = DICT_EXPECT_KEY ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
while ( true ) {
2014-02-10 01:10:30 +00:00
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_EOF ) {
2014-02-10 01:10:30 +00:00
_set_error ( " Unterminated dictionary " ) ;
return NULL ;
2017-11-16 17:38:18 +00:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CURLY_BRACKET_CLOSE ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
if ( expecting = = DICT_EXPECT_COLON ) {
2014-02-10 01:10:30 +00:00
_set_error ( " ':' expected " ) ;
return NULL ;
}
2017-03-05 15:44:50 +00:00
if ( expecting = = DICT_EXPECT_VALUE ) {
2014-02-10 01:10:30 +00:00
_set_error ( " value expected " ) ;
return NULL ;
}
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2014-02-10 01:10:30 +00:00
break ;
2017-11-16 17:38:18 +00:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_NEWLINE ) {
2014-02-10 01:10:30 +00:00
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ; //ignore newline
2017-11-16 17:38:18 +00:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COMMA ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
if ( expecting = = DICT_EXPECT_KEY ) {
2014-02-10 01:10:30 +00:00
_set_error ( " key or '}' expected " ) ;
return NULL ;
}
2017-03-05 15:44:50 +00:00
if ( expecting = = DICT_EXPECT_VALUE ) {
2014-02-10 01:10:30 +00:00
_set_error ( " value expected " ) ;
return NULL ;
}
2017-03-05 15:44:50 +00:00
if ( expecting = = DICT_EXPECT_COLON ) {
2014-02-10 01:10:30 +00:00
_set_error ( " ':' expected " ) ;
return NULL ;
}
2017-03-05 15:44:50 +00:00
expecting = DICT_EXPECT_KEY ;
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ; //ignore newline
2014-02-10 01:10:30 +00:00
2017-11-16 17:38:18 +00:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COLON ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
if ( expecting = = DICT_EXPECT_KEY ) {
2014-02-10 01:10:30 +00:00
_set_error ( " key or '}' expected " ) ;
return NULL ;
}
2017-03-05 15:44:50 +00:00
if ( expecting = = DICT_EXPECT_VALUE ) {
2014-02-10 01:10:30 +00:00
_set_error ( " value expected " ) ;
return NULL ;
}
2017-03-05 15:44:50 +00:00
if ( expecting = = DICT_EXPECT_COMMA ) {
2014-02-10 01:10:30 +00:00
_set_error ( " ',' or '}' expected " ) ;
return NULL ;
}
2017-03-05 15:44:50 +00:00
expecting = DICT_EXPECT_VALUE ;
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ; //ignore newline
2014-02-10 01:10:30 +00:00
} else {
2017-03-05 15:44:50 +00:00
if ( expecting = = DICT_EXPECT_COMMA ) {
2014-02-10 01:10:30 +00:00
_set_error ( " ',' or '}' expected " ) ;
return NULL ;
}
2017-03-05 15:44:50 +00:00
if ( expecting = = DICT_EXPECT_COLON ) {
2014-02-10 01:10:30 +00:00
_set_error ( " ':' expected " ) ;
return NULL ;
}
2017-03-05 15:44:50 +00:00
if ( expecting = = DICT_EXPECT_KEY ) {
2014-02-10 01:10:30 +00:00
2017-11-16 17:38:18 +00:00
if ( tokenizer - > is_token_literal ( ) & & tokenizer - > get_token ( 1 ) = = GDScriptTokenizer : : TK_OP_ASSIGN ) {
2017-03-31 17:28:34 +00:00
// We check with is_token_literal, as this allows us to use match/sync/etc. as a name
2014-02-10 01:10:30 +00:00
//lua style identifier, easier to write
ConstantNode * cn = alloc_node < ConstantNode > ( ) ;
2017-03-31 17:28:34 +00:00
cn - > value = tokenizer - > get_token_literal ( ) ;
2018-05-30 02:16:54 +00:00
cn - > datatype = _type_from_variant ( cn - > value ) ;
2014-02-10 01:10:30 +00:00
key = cn ;
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( 2 ) ;
2017-03-05 15:44:50 +00:00
expecting = DICT_EXPECT_VALUE ;
2014-02-10 01:10:30 +00:00
} else {
//python/js style more flexible
2017-03-05 15:44:50 +00:00
key = _parse_expression ( dict , p_static , p_allow_assign , p_parsing_constant ) ;
2014-02-10 01:10:30 +00:00
if ( ! key )
return NULL ;
2017-03-05 15:44:50 +00:00
expecting = DICT_EXPECT_COLON ;
2014-02-10 01:10:30 +00:00
}
}
2017-03-05 15:44:50 +00:00
if ( expecting = = DICT_EXPECT_VALUE ) {
Node * value = _parse_expression ( dict , p_static , p_allow_assign , p_parsing_constant ) ;
2014-02-10 01:10:30 +00:00
if ( ! value )
return NULL ;
2017-03-05 15:44:50 +00:00
expecting = DICT_EXPECT_COMMA ;
2014-02-10 01:10:30 +00:00
2017-11-16 17:38:18 +00:00
if ( key - > type = = GDScriptParser : : Node : : TYPE_CONSTANT ) {
Variant const & keyName = static_cast < const GDScriptParser : : ConstantNode * > ( key ) - > value ;
2016-11-26 12:40:13 +00:00
if ( keys . has ( keyName ) ) {
_set_error ( " Duplicate key found in Dictionary literal " ) ;
return NULL ;
}
keys . insert ( keyName ) ;
}
2014-02-10 01:10:30 +00:00
DictionaryNode : : Pair pair ;
2017-03-05 15:44:50 +00:00
pair . key = key ;
pair . value = value ;
2014-02-10 01:10:30 +00:00
dict - > elements . push_back ( pair ) ;
2017-03-05 15:44:50 +00:00
key = NULL ;
2014-02-10 01:10:30 +00:00
}
}
}
2017-03-05 15:44:50 +00:00
expr = dict ;
2014-02-10 01:10:30 +00:00
2018-06-21 01:41:26 +00:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PERIOD & & ( tokenizer - > is_token_literal ( 1 ) | | tokenizer - > get_token ( 1 ) = = GDScriptTokenizer : : TK_CURSOR ) ) {
2017-03-31 17:28:34 +00:00
// We check with is_token_literal, as this allows us to use match/sync/etc. as a name
2014-02-10 01:10:30 +00:00
// parent call
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ; //goto identifier
2014-02-10 01:10:30 +00:00
OperatorNode * op = alloc_node < OperatorNode > ( ) ;
2017-03-05 15:44:50 +00:00
op - > op = OperatorNode : : OP_PARENT_CALL ;
2014-02-10 01:10:30 +00:00
/*SelfNode *self = alloc_node<SelfNode>();
op - > arguments . push_back ( self ) ;
forbidden for now */
2014-12-17 01:31:57 +00:00
StringName identifier ;
2018-06-21 01:41:26 +00:00
bool is_completion = _get_completable_identifier ( COMPLETION_PARENT_FUNCTION , identifier ) & & for_completion ;
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
IdentifierNode * id = alloc_node < IdentifierNode > ( ) ;
2017-03-05 15:44:50 +00:00
id - > name = identifier ;
2014-02-10 01:10:30 +00:00
op - > arguments . push_back ( id ) ;
2018-06-21 01:41:26 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PARENTHESIS_OPEN ) {
if ( ! is_completion ) {
_set_error ( " Expected '(' for parent function call. " ) ;
return NULL ;
}
} else {
tokenizer - > advance ( ) ;
2019-07-03 14:28:50 +00:00
if ( ! _parse_arguments ( op , op - > arguments , p_static , false , p_parsing_constant ) ) {
2018-06-21 01:41:26 +00:00
return NULL ;
}
}
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
expr = op ;
2014-02-10 01:10:30 +00:00
2018-08-26 16:31:23 +00: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 01:10:30 +00:00
} else {
//find list [ or find dictionary {
2017-03-05 15:44:50 +00:00
_set_error ( " Error parsing expression, misplaced: " + String ( tokenizer - > get_token_name ( tokenizer - > get_token ( ) ) ) ) ;
return NULL ; //nothing
2014-02-10 01:10:30 +00:00
}
2019-08-09 04:49:33 +00:00
ERR_FAIL_COND_V_MSG ( ! expr , NULL , " GDScriptParser bug, couldn't figure out what expression is. " ) ;
2014-02-10 01:10:30 +00:00
/******************/
/* Parse Indexing */
/******************/
while ( true ) {
//expressions can be indexed any number of times
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PERIOD ) {
2014-02-10 01:10:30 +00:00
//indexing using "."
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( 1 ) ! = GDScriptTokenizer : : TK_CURSOR & & ! tokenizer - > is_token_literal ( 1 ) ) {
2017-03-31 17:28:34 +00:00
// We check with is_token_literal, as this allows us to use match/sync/etc. as a name
2014-02-10 01:10:30 +00:00
_set_error ( " Expected identifier as member " ) ;
return NULL ;
2017-11-16 17:38:18 +00:00
} else if ( tokenizer - > get_token ( 2 ) = = GDScriptTokenizer : : TK_PARENTHESIS_OPEN ) {
2014-02-10 01:10:30 +00:00
//call!!
2017-03-05 15:44:50 +00:00
OperatorNode * op = alloc_node < OperatorNode > ( ) ;
op - > op = OperatorNode : : OP_CALL ;
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
tokenizer - > advance ( ) ;
2017-03-05 15:44:50 +00:00
IdentifierNode * id = alloc_node < IdentifierNode > ( ) ;
2019-07-03 14:28:50 +00:00
StringName identifier ;
if ( _get_completable_identifier ( COMPLETION_METHOD , identifier ) ) {
completion_node = op ;
//indexing stuff
2014-02-16 00:16:33 +00:00
}
2014-02-10 01:10:30 +00:00
2019-07-03 14:28:50 +00:00
id - > name = identifier ;
2014-02-10 01:10:30 +00:00
op - > arguments . push_back ( expr ) ; // call what
op - > arguments . push_back ( id ) ; // call func
//get arguments
2014-12-17 01:31:57 +00:00
tokenizer - > advance ( 1 ) ;
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CURSOR ) {
2014-12-17 01:31:57 +00:00
_make_completable_call ( 0 ) ;
2017-03-05 15:44:50 +00:00
completion_node = op ;
2014-12-17 01:31:57 +00:00
}
2019-07-03 14:28:50 +00:00
if ( ! _parse_arguments ( op , op - > arguments , p_static , true , p_parsing_constant ) )
2014-02-10 01:10:30 +00:00
return NULL ;
2017-03-05 15:44:50 +00:00
expr = op ;
2014-02-10 01:10:30 +00:00
} else {
//simple indexing!
2014-12-17 01:31:57 +00:00
2017-03-05 15:44:50 +00:00
OperatorNode * op = alloc_node < OperatorNode > ( ) ;
op - > op = OperatorNode : : OP_INDEX_NAMED ;
2014-12-17 01:31:57 +00:00
tokenizer - > advance ( ) ;
StringName identifier ;
2017-03-05 15:44:50 +00:00
if ( _get_completable_identifier ( COMPLETION_INDEX , identifier ) ) {
2014-12-17 01:31:57 +00:00
2017-03-05 15:44:50 +00:00
if ( identifier = = StringName ( ) ) {
Fix misc. source comment typos
Found using `codespell -q 3 -S ./thirdparty,*.po -L ang,ba,cas,dof,doubleclick,fave,hist,leapyear,lod,nd,numer,ois,paket,seeked,sinc,switchs,te,uint -D ~/Projects/codespell/codespell_lib/data/dictionary.txt `
2019-09-19 18:36:39 +00:00
identifier = " @temp " ; //so it parses alright
2014-12-17 01:31:57 +00:00
}
2017-03-05 15:44:50 +00:00
completion_node = op ;
2014-12-17 01:31:57 +00:00
//indexing stuff
}
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
IdentifierNode * id = alloc_node < IdentifierNode > ( ) ;
id - > name = identifier ;
2014-02-10 01:10:30 +00:00
op - > arguments . push_back ( expr ) ;
op - > arguments . push_back ( id ) ;
2017-03-05 15:44:50 +00:00
expr = op ;
2014-02-10 01:10:30 +00:00
}
2017-11-16 17:38:18 +00:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_BRACKET_OPEN ) {
2014-02-10 01:10:30 +00:00
//indexing using "[]"
2017-03-05 15:44:50 +00:00
OperatorNode * op = alloc_node < OperatorNode > ( ) ;
op - > op = OperatorNode : : OP_INDEX ;
2014-02-10 01:10:30 +00:00
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( 1 ) ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
Node * subexpr = _parse_expression ( op , p_static , p_allow_assign , p_parsing_constant ) ;
2014-02-10 01:10:30 +00:00
if ( ! subexpr ) {
return NULL ;
}
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_BRACKET_CLOSE ) {
2014-02-10 01:10:30 +00:00
_set_error ( " Expected ']' " ) ;
return NULL ;
}
op - > arguments . push_back ( expr ) ;
op - > arguments . push_back ( subexpr ) ;
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( 1 ) ;
2017-03-05 15:44:50 +00:00
expr = op ;
2014-02-10 01:10:30 +00:00
} else
break ;
}
2018-05-30 02:16:51 +00: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 02:16:53 +00:00
if ( ! _parse_type ( cn - > cast_type ) ) {
2018-05-30 02:16:51 +00:00
_set_error ( " Expected type after 'as'. " ) ;
return NULL ;
}
has_casting = true ;
cn - > source_node = expr ;
expr = cn ;
}
2014-02-10 01:10:30 +00:00
/******************/
/* Parse Operator */
/******************/
2017-03-05 15:44:50 +00:00
if ( parenthesis > 0 ) {
2014-04-05 21:50:09 +00:00
//remove empty space (only allowed if inside parenthesis
2017-11-16 17:38:18 +00:00
while ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_NEWLINE ) {
2014-04-05 21:50:09 +00:00
tokenizer - > advance ( ) ;
}
}
2014-02-10 01:10:30 +00:00
Expression e ;
2017-03-05 15:44:50 +00:00
e . is_op = false ;
e . node = expr ;
2014-02-10 01:10:30 +00:00
expression . push_back ( e ) ;
// determine which operator is next
OperatorNode : : Operator op ;
2017-03-05 15:44:50 +00:00
bool valid = true ;
2014-02-10 01:10:30 +00:00
2017-03-24 20:45:31 +00:00
//assign, if allowed is only allowed on the first operator
2017-03-05 15:44:50 +00:00
# define _VALIDATE_ASSIGN \
2018-05-30 02:16:51 +00:00
if ( ! p_allow_assign | | has_casting ) { \
2017-03-05 15:44:50 +00:00
_set_error ( " Unexpected assign. " ) ; \
return NULL ; \
} \
p_allow_assign = false ;
2017-08-24 03:06:56 +00:00
2017-03-05 15:44:50 +00:00
switch ( tokenizer - > get_token ( ) ) { //see operator
2017-11-16 17:38:18 +00: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 15:44:50 +00:00
op = OperatorNode : : OP_MOD ;
break ;
2017-11-16 17:38:18 +00: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 03:06:56 +00:00
_VALIDATE_ASSIGN op = OperatorNode : : OP_ASSIGN ;
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( 1 ) = = GDScriptTokenizer : : TK_CURSOR ) {
2017-08-24 03:06:56 +00: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 17:38:18 +00: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 15:44:50 +00:00
default : valid = false ; break ;
2014-02-10 01:10:30 +00:00
}
2016-03-08 23:00:52 +00:00
if ( valid ) {
2017-03-05 15:44:50 +00:00
e . is_op = true ;
e . op = op ;
2014-02-10 01:10:30 +00:00
expression . push_back ( e ) ;
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2014-02-10 01:10:30 +00:00
} else {
break ;
}
}
/* Reduce the set set of expressions and place them in an operator tree, respecting precedence */
2017-03-05 15:44:50 +00:00
while ( expression . size ( ) > 1 ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
int next_op = - 1 ;
int min_priority = 0xFFFFF ;
bool is_unary = false ;
bool is_ternary = false ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
for ( int i = 0 ; i < expression . size ( ) ; i + + ) {
2014-02-10 01:10:30 +00:00
if ( ! expression [ i ] . is_op ) {
continue ;
}
int priority ;
2017-03-05 15:44:50 +00:00
bool unary = false ;
bool ternary = false ;
bool error = false ;
2017-12-11 13:36:32 +00:00
bool right_to_left = false ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
switch ( expression [ i ] . op ) {
2014-02-10 01:10:30 +00:00
2017-05-26 17:45:39 +00:00
case OperatorNode : : OP_IS :
2018-08-26 16:31:23 +00:00
case OperatorNode : : OP_IS_BUILTIN :
2017-03-05 15:44:50 +00:00
priority = - 1 ;
break ; //before anything
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
case OperatorNode : : OP_BIT_INVERT :
priority = 0 ;
unary = true ;
break ;
case OperatorNode : : OP_NEG :
case OperatorNode : : OP_POS :
priority = 1 ;
unary = true ;
break ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00: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 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
case OperatorNode : : OP_ADD : priority = 3 ; break ;
case OperatorNode : : OP_SUB : priority = 3 ; break ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
case OperatorNode : : OP_SHIFT_LEFT : priority = 4 ; break ;
case OperatorNode : : OP_SHIFT_RIGHT : priority = 4 ; break ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00: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 01:10:30 +00:00
2017-03-05 15:44:50 +00: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 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
case OperatorNode : : OP_EQUAL : priority = 8 ; break ;
case OperatorNode : : OP_NOT_EQUAL : priority = 8 ; break ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
case OperatorNode : : OP_IN : priority = 10 ; break ;
2016-08-25 18:18:35 +00:00
2017-03-05 15:44:50 +00: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 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
case OperatorNode : : OP_TERNARY_IF :
priority = 14 ;
ternary = true ;
2017-12-11 13:36:32 +00:00
right_to_left = true ;
2017-03-05 15:44:50 +00:00
break ;
case OperatorNode : : OP_TERNARY_ELSE :
priority = 14 ;
error = true ;
2017-12-11 13:36:32 +00:00
// Rigth-to-left should be false in this case, otherwise it would always error.
break ;
2017-03-05 15:44:50 +00: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 01:10:30 +00:00
default : {
2017-11-16 17:38:18 +00:00
_set_error ( " GDScriptParser bug, invalid operator in expression: " + itos ( expression [ i ] . op ) ) ;
2014-02-10 01:10:30 +00:00
return NULL ;
}
}
2017-12-11 13:36:32 +00: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 15:44:50 +00:00
if ( error ) {
2016-08-25 18:18:35 +00:00
_set_error ( " Unexpected operator " ) ;
return NULL ;
}
2017-03-05 15:44:50 +00:00
next_op = i ;
min_priority = priority ;
is_unary = unary ;
is_ternary = ternary ;
2014-02-10 01:10:30 +00:00
}
}
2017-03-05 15:44:50 +00:00
if ( next_op = = - 1 ) {
2014-02-10 01:10:30 +00:00
_set_error ( " Yet another parser bug.... " ) ;
2019-06-26 13:08:25 +00:00
ERR_FAIL_V ( NULL ) ;
2014-02-10 01:10:30 +00:00
}
// OK! create operator..
if ( is_unary ) {
2017-03-05 15:44:50 +00:00
int expr_pos = next_op ;
while ( expression [ expr_pos ] . is_op ) {
2014-02-10 01:10:30 +00:00
expr_pos + + ;
2017-03-05 15:44:50 +00:00
if ( expr_pos = = expression . size ( ) ) {
2014-02-10 01:10:30 +00:00
//can happen..
2018-04-22 17:36:01 +00:00
_set_error ( " Unexpected end of expression... " ) ;
2014-02-10 01:10:30 +00:00
return NULL ;
}
}
//consecutively do unary opeators
2017-03-05 15:44:50 +00:00
for ( int i = expr_pos - 1 ; i > = next_op ; i - - ) {
2014-02-10 01:10:30 +00:00
OperatorNode * op = alloc_node < OperatorNode > ( ) ;
2017-03-05 15:44:50 +00: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 01:11:03 +00:00
expression . write [ i ] . is_op = false ;
expression . write [ i ] . node = op ;
2017-03-05 15:44:50 +00:00
expression . remove ( i + 1 ) ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
} else if ( is_ternary ) {
if ( next_op < 1 | | next_op > = ( expression . size ( ) - 1 ) ) {
2018-04-22 17:36:01 +00:00
_set_error ( " Parser bug... " ) ;
2016-08-25 18:18:35 +00:00
ERR_FAIL_V ( NULL ) ;
}
2017-03-05 15:44:50 +00:00
if ( next_op > = ( expression . size ( ) - 2 ) | | expression [ next_op + 2 ] . op ! = OperatorNode : : OP_TERNARY_ELSE ) {
2016-08-25 18:18:35 +00:00
_set_error ( " Expected else after ternary if. " ) ;
2018-05-30 02:16:54 +00:00
return NULL ;
2016-08-25 18:18:35 +00:00
}
2017-03-05 15:44:50 +00:00
if ( next_op > = ( expression . size ( ) - 3 ) ) {
2016-08-25 18:18:35 +00:00
_set_error ( " Expected value after ternary else. " ) ;
2018-05-30 02:16:54 +00:00
return NULL ;
2016-08-25 18:18:35 +00:00
}
OperatorNode * op = alloc_node < OperatorNode > ( ) ;
2017-03-05 15:44:50 +00:00
op - > op = expression [ next_op ] . op ;
op - > line = op_line ; //line might have been changed from a \n
2016-08-25 18:18:35 +00:00
2017-03-05 15:44:50 +00:00
if ( expression [ next_op - 1 ] . is_op ) {
2016-08-25 18:18:35 +00:00
2018-04-22 17:36:01 +00:00
_set_error ( " Parser bug... " ) ;
2016-08-25 18:18:35 +00:00
ERR_FAIL_V ( NULL ) ;
}
2017-03-05 15:44:50 +00:00
if ( expression [ next_op + 1 ] . is_op ) {
2016-08-25 18:18:35 +00:00
// this is not invalid and can really appear
// but it becomes invalid anyway because no binary op
2017-09-02 14:19:06 +00:00
// can be followed by a unary op in a valid combination,
2017-03-24 20:45:31 +00:00
// due to how precedence works, unaries will always disappear first
2016-08-25 18:18:35 +00:00
_set_error ( " Unexpected two consecutive operators after ternary if. " ) ;
return NULL ;
}
2017-03-05 15:44:50 +00:00
if ( expression [ next_op + 3 ] . is_op ) {
2016-08-25 18:18:35 +00:00
// this is not invalid and can really appear
// but it becomes invalid anyway because no binary op
2017-09-02 14:19:06 +00:00
// can be followed by a unary op in a valid combination,
2017-03-24 20:45:31 +00:00
// due to how precedence works, unaries will always disappear first
2016-08-25 18:18:35 +00:00
_set_error ( " Unexpected two consecutive operators after ternary else. " ) ;
return NULL ;
}
2017-03-05 15:44:50 +00: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 18:18:35 +00:00
//replace all 3 nodes by this operator and make it an expression
2018-07-25 01:11:03 +00:00
expression . write [ next_op - 1 ] . node = op ;
2016-08-25 18:18:35 +00:00
expression . remove ( next_op ) ;
expression . remove ( next_op ) ;
expression . remove ( next_op ) ;
expression . remove ( next_op ) ;
2014-02-10 01:10:30 +00:00
} else {
2017-03-05 15:44:50 +00:00
if ( next_op < 1 | | next_op > = ( expression . size ( ) - 1 ) ) {
2018-04-22 17:36:01 +00:00
_set_error ( " Parser bug... " ) ;
2014-02-10 01:10:30 +00:00
ERR_FAIL_V ( NULL ) ;
}
OperatorNode * op = alloc_node < OperatorNode > ( ) ;
2017-03-05 15:44:50 +00:00
op - > op = expression [ next_op ] . op ;
op - > line = op_line ; //line might have been changed from a \n
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
if ( expression [ next_op - 1 ] . is_op ) {
2014-02-10 01:10:30 +00:00
2018-04-22 17:36:01 +00:00
_set_error ( " Parser bug... " ) ;
2014-02-10 01:10:30 +00:00
ERR_FAIL_V ( NULL ) ;
}
2017-03-05 15:44:50 +00:00
if ( expression [ next_op + 1 ] . is_op ) {
2014-02-10 01:10:30 +00:00
// this is not invalid and can really appear
// but it becomes invalid anyway because no binary op
2017-09-02 14:19:06 +00:00
// can be followed by a unary op in a valid combination,
2017-03-24 20:45:31 +00:00
// due to how precedence works, unaries will always disappear first
2014-02-10 01:10:30 +00:00
2016-06-24 13:30:36 +00:00
_set_error ( " Unexpected two consecutive operators. " ) ;
return NULL ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00: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 01:10:30 +00:00
//replace all 3 nodes by this operator and make it an expression
2018-07-25 01:11:03 +00:00
expression . write [ next_op - 1 ] . node = op ;
2014-02-10 01:10:30 +00:00
expression . remove ( next_op ) ;
expression . remove ( next_op ) ;
}
}
return expression [ 0 ] . node ;
}
2017-11-16 17:38:18 +00:00
GDScriptParser : : Node * GDScriptParser : : _reduce_expression ( Node * p_node , bool p_to_const ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
switch ( p_node - > type ) {
2014-02-10 01:10:30 +00:00
case Node : : TYPE_BUILT_IN_FUNCTION : {
//many may probably be optimizable
return p_node ;
} break ;
case Node : : TYPE_ARRAY : {
2017-03-05 15:44:50 +00:00
ArrayNode * an = static_cast < ArrayNode * > ( p_node ) ;
bool all_constants = true ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
for ( int i = 0 ; i < an - > elements . size ( ) ; i + + ) {
2014-02-10 01:10:30 +00:00
2018-07-25 01:11:03 +00:00
an - > elements . write [ i ] = _reduce_expression ( an - > elements [ i ] , p_to_const ) ;
2017-03-05 15:44:50 +00:00
if ( an - > elements [ i ] - > type ! = Node : : TYPE_CONSTANT )
all_constants = false ;
2014-02-10 01:10:30 +00:00
}
if ( all_constants & & p_to_const ) {
//reduce constant array expression
ConstantNode * cn = alloc_node < ConstantNode > ( ) ;
2017-01-11 11:53:31 +00:00
Array arr ;
2014-02-10 01:10:30 +00:00
arr . resize ( an - > elements . size ( ) ) ;
2017-03-05 15:44:50 +00: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 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
cn - > value = arr ;
2018-05-30 02:16:54 +00:00
cn - > datatype = _type_from_variant ( cn - > value ) ;
2014-02-10 01:10:30 +00:00
return cn ;
}
return an ;
} break ;
case Node : : TYPE_DICTIONARY : {
2017-03-05 15:44:50 +00:00
DictionaryNode * dn = static_cast < DictionaryNode * > ( p_node ) ;
bool all_constants = true ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
for ( int i = 0 ; i < dn - > elements . size ( ) ; i + + ) {
2014-02-10 01:10:30 +00:00
2018-07-25 01:11:03 +00:00
dn - > elements . write [ i ] . key = _reduce_expression ( dn - > elements [ i ] . key , p_to_const ) ;
2017-03-05 15:44:50 +00:00
if ( dn - > elements [ i ] . key - > type ! = Node : : TYPE_CONSTANT )
all_constants = false ;
2018-07-25 01:11:03 +00:00
dn - > elements . write [ i ] . value = _reduce_expression ( dn - > elements [ i ] . value , p_to_const ) ;
2017-03-05 15:44:50 +00:00
if ( dn - > elements [ i ] . value - > type ! = Node : : TYPE_CONSTANT )
all_constants = false ;
2014-02-10 01:10:30 +00:00
}
if ( all_constants & & p_to_const ) {
//reduce constant array expression
ConstantNode * cn = alloc_node < ConstantNode > ( ) ;
2017-01-11 11:53:31 +00:00
Dictionary dict ;
2017-03-05 15:44:50 +00: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 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
dict [ key_c - > value ] = value_c - > value ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
cn - > value = dict ;
2018-05-30 02:16:54 +00:00
cn - > datatype = _type_from_variant ( cn - > value ) ;
2014-02-10 01:10:30 +00:00
return cn ;
}
return dn ;
} break ;
case Node : : TYPE_OPERATOR : {
2017-03-05 15:44:50 +00:00
OperatorNode * op = static_cast < OperatorNode * > ( p_node ) ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
bool all_constants = true ;
int last_not_constant = - 1 ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
for ( int i = 0 ; i < op - > arguments . size ( ) ; i + + ) {
2014-02-10 01:10:30 +00:00
2018-07-25 01:11:03 +00:00
op - > arguments . write [ i ] = _reduce_expression ( op - > arguments [ i ] , p_to_const ) ;
2017-03-05 15:44:50 +00:00
if ( op - > arguments [ i ] - > type ! = Node : : TYPE_CONSTANT ) {
all_constants = false ;
last_not_constant = i ;
2014-02-10 01:10:30 +00:00
}
}
2017-05-26 17:45:39 +00:00
if ( op - > op = = OperatorNode : : OP_IS ) {
2014-02-10 01:10:30 +00:00
//nothing much
return op ;
2017-03-05 15:44:50 +00:00
}
if ( op - > op = = OperatorNode : : OP_PARENT_CALL ) {
2014-02-10 01:10:30 +00:00
//nothing much
return op ;
2017-03-05 15:44:50 +00:00
} else if ( op - > op = = OperatorNode : : OP_CALL ) {
2014-02-10 01:10:30 +00:00
//can reduce base type constructors
2017-11-16 17:38:18 +00: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 01:10:30 +00:00
//native type constructor or intrinsic function
2017-03-05 15:44:50 +00:00
const Variant * * vptr = NULL ;
Vector < Variant * > ptrs ;
if ( op - > arguments . size ( ) > 1 ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
ptrs . resize ( op - > arguments . size ( ) - 1 ) ;
for ( int i = 0 ; i < ptrs . size ( ) ; i + + ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
ConstantNode * cn = static_cast < ConstantNode * > ( op - > arguments [ i + 1 ] ) ;
2018-07-25 01:11:03 +00:00
ptrs . write [ i ] = & cn - > value ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
vptr = ( const Variant * * ) & ptrs [ 0 ] ;
2014-02-10 01:10:30 +00:00
}
2020-02-19 19:27:19 +00:00
Callable : : CallError ce ;
2014-02-10 01:10:30 +00:00
Variant v ;
2017-03-05 15:44:50 +00: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 01:10:30 +00:00
} else {
2017-11-16 17:38:18 +00:00
GDScriptFunctions : : Function func = static_cast < BuiltInFunctionNode * > ( op - > arguments [ 0 ] ) - > function ;
GDScriptFunctions : : call ( func , vptr , ptrs . size ( ) , v , ce ) ;
2014-02-10 01:10:30 +00:00
}
2020-02-19 19:27:19 +00:00
if ( ce . error ! = Callable : : CallError : : CALL_OK ) {
2014-02-10 01:10:30 +00:00
String errwhere ;
2017-03-05 15:44:50 +00:00
if ( op - > arguments [ 0 ] - > type = = Node : : TYPE_TYPE ) {
TypeNode * tn = static_cast < TypeNode * > ( op - > arguments [ 0 ] ) ;
2018-01-30 03:32:08 +00:00
errwhere = " ' " + Variant : : get_type_name ( tn - > vtype ) + " ' constructor " ;
2014-02-10 01:10:30 +00:00
} else {
2017-11-16 17:38:18 +00:00
GDScriptFunctions : : Function func = static_cast < BuiltInFunctionNode * > ( op - > arguments [ 0 ] ) - > function ;
2018-01-30 03:32:08 +00:00
errwhere = String ( " ' " ) + GDScriptFunctions : : get_func_name ( func ) + " ' intrinsic function " ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
switch ( ce . error ) {
2014-02-10 01:10:30 +00:00
2020-02-19 19:27:19 +00:00
case Callable : : CallError : : CALL_ERROR_INVALID_ARGUMENT : {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
_set_error ( " Invalid argument (# " + itos ( ce . argument + 1 ) + " ) for " + errwhere + " . " ) ;
2014-02-10 01:10:30 +00:00
} break ;
2020-02-19 19:27:19 +00:00
case Callable : : CallError : : CALL_ERROR_TOO_MANY_ARGUMENTS : {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
_set_error ( " Too many arguments for " + errwhere + " . " ) ;
2014-02-10 01:10:30 +00:00
} break ;
2020-02-19 19:27:19 +00:00
case Callable : : CallError : : CALL_ERROR_TOO_FEW_ARGUMENTS : {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
_set_error ( " Too few arguments for " + errwhere + " . " ) ;
2014-02-10 01:10:30 +00:00
} break ;
default : {
2017-03-05 15:44:50 +00:00
_set_error ( " Invalid arguments for " + errwhere + " . " ) ;
2014-02-10 01:10:30 +00:00
} break ;
}
2017-03-05 15:44:50 +00:00
error_line = op - > line ;
2016-07-22 12:22:34 +00:00
2014-02-10 01:10:30 +00:00
return p_node ;
}
ConstantNode * cn = alloc_node < ConstantNode > ( ) ;
2017-03-05 15:44:50 +00:00
cn - > value = v ;
2018-05-30 02:16:54 +00:00
cn - > datatype = _type_from_variant ( v ) ;
2014-02-10 01:10:30 +00:00
return cn ;
}
return op ; //don't reduce yet
2014-09-15 14:33:30 +00:00
2017-03-05 15:44:50 +00:00
} else if ( op - > op = = OperatorNode : : OP_YIELD ) {
2014-09-15 14:33:30 +00:00
return op ;
2017-03-05 15:44:50 +00:00
} else if ( op - > op = = OperatorNode : : OP_INDEX ) {
2014-02-10 01:10:30 +00:00
//can reduce indices into constant arrays or dictionaries
if ( all_constants ) {
2017-03-05 15:44:50 +00:00
ConstantNode * ca = static_cast < ConstantNode * > ( op - > arguments [ 0 ] ) ;
ConstantNode * cb = static_cast < ConstantNode * > ( op - > arguments [ 1 ] ) ;
2014-02-10 01:10:30 +00:00
bool valid ;
2017-03-05 15:44:50 +00:00
Variant v = ca - > value . get ( cb - > value , & valid ) ;
2014-02-10 01:10:30 +00:00
if ( ! valid ) {
_set_error ( " invalid index in constant expression " ) ;
2017-03-05 15:44:50 +00:00
error_line = op - > line ;
2014-02-10 01:10:30 +00:00
return op ;
}
ConstantNode * cn = alloc_node < ConstantNode > ( ) ;
2017-03-05 15:44:50 +00:00
cn - > value = v ;
2018-05-30 02:16:54 +00:00
cn - > datatype = _type_from_variant ( v ) ;
2014-02-10 01:10:30 +00:00
return cn ;
2018-05-30 02:16:54 +00:00
}
2014-02-10 01:10:30 +00:00
2014-11-06 00:20:42 +00:00
return op ;
2017-03-05 15:44:50 +00:00
} else if ( op - > op = = OperatorNode : : OP_INDEX_NAMED ) {
2014-11-06 00:20:42 +00:00
2017-03-05 15:44:50 +00:00
if ( op - > arguments [ 0 ] - > type = = Node : : TYPE_CONSTANT & & op - > arguments [ 1 ] - > type = = Node : : TYPE_IDENTIFIER ) {
2014-11-06 00:20:42 +00:00
2017-03-05 15:44:50 +00:00
ConstantNode * ca = static_cast < ConstantNode * > ( op - > arguments [ 0 ] ) ;
IdentifierNode * ib = static_cast < IdentifierNode * > ( op - > arguments [ 1 ] ) ;
2014-11-06 00:20:42 +00:00
bool valid ;
2017-03-05 15:44:50 +00:00
Variant v = ca - > value . get_named ( ib - > name , & valid ) ;
2014-11-06 00:20:42 +00:00
if ( ! valid ) {
2017-03-05 15:44:50 +00:00
_set_error ( " invalid index ' " + String ( ib - > name ) + " ' in constant expression " ) ;
error_line = op - > line ;
2014-11-06 00:20:42 +00:00
return op ;
}
ConstantNode * cn = alloc_node < ConstantNode > ( ) ;
2017-03-05 15:44:50 +00:00
cn - > value = v ;
2018-05-30 02:16:54 +00:00
cn - > datatype = _type_from_variant ( v ) ;
2014-11-06 00:20:42 +00:00
return cn ;
2014-02-10 01:10:30 +00:00
}
return op ;
}
2018-05-30 02:16:54 +00:00
//validate assignment (don't assign to constant expression
2017-03-05 15:44:50 +00:00
switch ( op - > op ) {
2014-02-10 01:10:30 +00: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 15:44:50 +00: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 01:10:30 +00:00
return op ;
2020-01-21 19:57:22 +00:00
} else if ( op - > arguments [ 0 ] - > type = = Node : : TYPE_SELF ) {
_set_error ( " Can't assign to self. " , op - > line ) ;
error_line = op - > line ;
return op ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
if ( op - > arguments [ 0 ] - > type = = Node : : TYPE_OPERATOR ) {
OperatorNode * on = static_cast < OperatorNode * > ( op - > arguments [ 0 ] ) ;
2016-12-29 10:31:19 +00:00
if ( on - > op ! = OperatorNode : : OP_INDEX & & on - > op ! = OperatorNode : : OP_INDEX_NAMED ) {
2017-03-05 15:44:50 +00:00
_set_error ( " Can't assign to an expression " , tokenizer - > get_token_line ( ) - 1 ) ;
error_line = op - > line ;
2016-12-29 10:31:19 +00:00
return op ;
}
}
2014-02-10 01:10:30 +00:00
} break ;
2019-04-09 15:08:36 +00:00
default : {
break ;
}
2014-02-10 01:10:30 +00:00
}
//now se if all are constants
if ( ! all_constants )
return op ; //nothing to reduce from here on
2017-03-05 15:44:50 +00: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 02:16:54 +00:00
cn - > datatype = _type_from_variant ( res ) ; \
2014-02-10 01:10:30 +00:00
return cn ;
2017-03-05 15:44:50 +00: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 02:16:54 +00:00
cn - > datatype = _type_from_variant ( res ) ; \
2014-02-10 01:10:30 +00:00
return cn ;
2017-03-05 15:44:50 +00:00
switch ( op - > op ) {
2014-02-10 01:10:30 +00:00
//unary operators
2017-03-05 15:44:50 +00: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 01:10:30 +00:00
//binary operators (in precedence order)
2017-03-05 15:44:50 +00: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 00:32:05 +00:00
_REDUCE_BINARY ( Variant : : OP_SUBTRACT ) ;
2017-03-05 15:44:50 +00: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 02:16:54 +00: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 ;
2019-04-09 15:08:36 +00:00
default : {
ERR_FAIL_V ( op ) ;
}
2014-02-10 01:10:30 +00:00
}
} break ;
default : {
return p_node ;
} break ;
}
}
2017-11-16 17:38:18 +00: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 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
Node * expr = _parse_expression ( p_parent , p_static , p_allow_assign , p_reduce_const ) ;
2014-02-10 01:10:30 +00:00
if ( ! expr | | error_set )
return NULL ;
2017-03-05 15:44:50 +00:00
expr = _reduce_expression ( expr , p_reduce_const ) ;
2014-02-10 01:10:30 +00:00
if ( ! expr | | error_set )
return NULL ;
return expr ;
}
2017-11-16 17:38:18 +00:00
bool GDScriptParser : : _recover_from_completion ( ) {
2015-08-30 14:50:10 +00:00
if ( ! completion_found ) {
return false ; //can't recover if no completion
}
//skip stuff until newline
2017-11-16 17:38:18 +00:00
while ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_NEWLINE & & tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_EOF & & tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_ERROR ) {
2015-08-30 14:50:10 +00:00
tokenizer - > advance ( ) ;
}
2017-03-05 15:44:50 +00:00
completion_found = false ;
error_set = false ;
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_ERROR ) {
2015-09-02 03:56:51 +00:00
error_set = true ;
}
2015-08-30 14:50:10 +00:00
return true ;
}
2017-11-16 17:38:18 +00:00
GDScriptParser : : PatternNode * GDScriptParser : : _parse_pattern ( bool p_static ) {
2017-03-05 15:44:50 +00:00
2016-10-05 16:48:38 +00:00
PatternNode * pattern = alloc_node < PatternNode > ( ) ;
2017-03-05 15:44:50 +00:00
2017-11-16 17:38:18 +00:00
GDScriptTokenizer : : Token token = tokenizer - > get_token ( ) ;
2016-09-30 19:40:31 +00:00
if ( error_set )
return NULL ;
2017-03-05 15:44:50 +00:00
2017-11-16 17:38:18 +00:00
if ( token = = GDScriptTokenizer : : TK_EOF ) {
2016-10-05 16:48:38 +00:00
return NULL ;
}
2017-03-05 15:44:50 +00:00
2016-09-30 19:40:31 +00:00
switch ( token ) {
2017-01-20 08:25:15 +00:00
// array
2017-11-16 17:38:18 +00:00
case GDScriptTokenizer : : TK_BRACKET_OPEN : {
2016-09-30 19:40:31 +00:00
tokenizer - > advance ( ) ;
2017-11-16 17:38:18 +00:00
pattern - > pt_type = GDScriptParser : : PatternNode : : PT_ARRAY ;
2016-09-30 19:40:31 +00:00
while ( true ) {
2017-03-05 15:44:50 +00:00
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_BRACKET_CLOSE ) {
2016-09-30 19:40:31 +00:00
tokenizer - > advance ( ) ;
break ;
}
2017-03-05 15:44:50 +00:00
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PERIOD & & tokenizer - > get_token ( 1 ) = = GDScriptTokenizer : : TK_PERIOD ) {
2016-09-30 19:40:31 +00:00
// match everything
tokenizer - > advance ( 2 ) ;
2016-10-05 16:48:38 +00:00
PatternNode * sub_pattern = alloc_node < PatternNode > ( ) ;
2017-11-16 17:38:18 +00:00
sub_pattern - > pt_type = GDScriptParser : : PatternNode : : PT_IGNORE_REST ;
2016-10-05 16:48:38 +00:00
pattern - > array . push_back ( sub_pattern ) ;
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COMMA & & tokenizer - > get_token ( 1 ) = = GDScriptTokenizer : : TK_BRACKET_CLOSE ) {
2016-09-30 19:40:31 +00:00
tokenizer - > advance ( 2 ) ;
break ;
2017-11-16 17:38:18 +00:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_BRACKET_CLOSE ) {
2016-09-30 19:40:31 +00:00
tokenizer - > advance ( 1 ) ;
break ;
} else {
_set_error ( " '..' pattern only allowed at the end of an array pattern " ) ;
return NULL ;
}
}
2017-03-05 15:44:50 +00:00
2016-09-30 19:40:31 +00:00
PatternNode * sub_pattern = _parse_pattern ( p_static ) ;
if ( ! sub_pattern ) {
return NULL ;
}
2017-03-05 15:44:50 +00:00
2016-09-30 19:40:31 +00:00
pattern - > array . push_back ( sub_pattern ) ;
2017-03-05 15:44:50 +00:00
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COMMA ) {
2016-09-30 19:40:31 +00:00
tokenizer - > advance ( ) ;
continue ;
2017-11-16 17:38:18 +00:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_BRACKET_CLOSE ) {
2016-09-30 19:40:31 +00:00
tokenizer - > advance ( ) ;
break ;
} else {
_set_error ( " Not a valid pattern " ) ;
return NULL ;
}
}
} break ;
2016-10-16 11:20:28 +00:00
// bind
2017-11-16 17:38:18 +00:00
case GDScriptTokenizer : : TK_PR_VAR : {
2016-10-05 16:48:38 +00:00
tokenizer - > advance ( ) ;
2018-08-21 16:38:18 +00:00
if ( ! tokenizer - > is_token_literal ( ) ) {
_set_error ( " Expected identifier for binding variable name. " ) ;
return NULL ;
}
2017-11-16 17:38:18 +00:00
pattern - > pt_type = GDScriptParser : : PatternNode : : PT_BIND ;
2019-03-03 10:36:27 +00:00
pattern - > bind = tokenizer - > get_token_literal ( ) ;
2018-08-21 16:38:18 +00: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 02:16:54 +00: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 19:40:31 +00:00
tokenizer - > advance ( ) ;
} break ;
2017-01-20 08:25:15 +00:00
// dictionary
2017-11-16 17:38:18 +00:00
case GDScriptTokenizer : : TK_CURLY_BRACKET_OPEN : {
2016-09-30 19:40:31 +00:00
tokenizer - > advance ( ) ;
2017-11-16 17:38:18 +00:00
pattern - > pt_type = GDScriptParser : : PatternNode : : PT_DICTIONARY ;
2016-09-30 19:40:31 +00:00
while ( true ) {
2017-03-05 15:44:50 +00:00
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CURLY_BRACKET_CLOSE ) {
2016-09-30 19:40:31 +00:00
tokenizer - > advance ( ) ;
break ;
}
2017-03-05 15:44:50 +00:00
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PERIOD & & tokenizer - > get_token ( 1 ) = = GDScriptTokenizer : : TK_PERIOD ) {
2016-09-30 19:40:31 +00:00
// match everything
tokenizer - > advance ( 2 ) ;
2016-10-05 16:48:38 +00:00
PatternNode * sub_pattern = alloc_node < PatternNode > ( ) ;
sub_pattern - > pt_type = PatternNode : : PT_IGNORE_REST ;
pattern - > array . push_back ( sub_pattern ) ;
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COMMA & & tokenizer - > get_token ( 1 ) = = GDScriptTokenizer : : TK_CURLY_BRACKET_CLOSE ) {
2016-09-30 19:40:31 +00:00
tokenizer - > advance ( 2 ) ;
break ;
2017-11-16 17:38:18 +00:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CURLY_BRACKET_CLOSE ) {
2016-09-30 19:40:31 +00:00
tokenizer - > advance ( 1 ) ;
break ;
} else {
2017-09-02 14:19:06 +00:00
_set_error ( " '..' pattern only allowed at the end of a dictionary pattern " ) ;
2016-09-30 19:40:31 +00:00
return NULL ;
}
}
2017-03-05 15:44:50 +00:00
2016-09-30 19:40:31 +00: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 15:44:50 +00:00
2017-11-16 17:38:18 +00:00
if ( key - > type ! = GDScriptParser : : Node : : TYPE_CONSTANT ) {
2016-09-30 19:40:31 +00:00
_set_error ( " Not a constant expression as key " ) ;
return NULL ;
}
2017-03-05 15:44:50 +00:00
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COLON ) {
2016-09-30 19:40:31 +00:00
tokenizer - > advance ( ) ;
2017-03-05 15:44:50 +00:00
2016-09-30 19:40:31 +00:00
PatternNode * value = _parse_pattern ( p_static ) ;
if ( ! value ) {
_set_error ( " Expected pattern in dictionary value " ) ;
return NULL ;
}
2017-03-05 15:44:50 +00:00
pattern - > dictionary . insert ( static_cast < ConstantNode * > ( key ) , value ) ;
2016-09-30 19:40:31 +00:00
} else {
2017-03-05 15:44:50 +00:00
pattern - > dictionary . insert ( static_cast < ConstantNode * > ( key ) , NULL ) ;
2016-09-30 19:40:31 +00:00
}
2017-03-05 15:44:50 +00:00
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COMMA ) {
2016-09-30 19:40:31 +00:00
tokenizer - > advance ( ) ;
continue ;
2017-11-16 17:38:18 +00:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CURLY_BRACKET_CLOSE ) {
2016-09-30 19:40:31 +00:00
tokenizer - > advance ( ) ;
break ;
} else {
_set_error ( " Not a valid pattern " ) ;
return NULL ;
}
}
} break ;
2017-11-16 17:38:18 +00:00
case GDScriptTokenizer : : TK_WILDCARD : {
2017-01-20 08:25:15 +00:00
tokenizer - > advance ( ) ;
pattern - > pt_type = PatternNode : : PT_WILDCARD ;
} break ;
2016-10-16 11:20:28 +00:00
// all the constants like strings and numbers
2016-09-30 19:40:31 +00:00
default : {
2016-10-05 16:48:38 +00:00
Node * value = _parse_and_reduce_expression ( pattern , p_static ) ;
2017-10-24 05:07:21 +00:00
if ( ! value ) {
_set_error ( " Expect constant expression or variables in a pattern " ) ;
2016-10-05 16:48:38 +00:00
return NULL ;
}
2017-03-05 15:44:50 +00:00
2017-08-22 12:54:52 +00: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 11:20:28 +00:00
_set_error ( " Only constant expressions or variables allowed in a pattern " ) ;
return NULL ;
}
2017-03-05 15:44:50 +00:00
2016-10-05 16:48:38 +00:00
pattern - > pt_type = PatternNode : : PT_CONSTANT ;
pattern - > constant = value ;
} break ;
2016-09-30 19:40:31 +00:00
}
2017-03-05 15:44:50 +00:00
2016-09-30 19:40:31 +00:00
return pattern ;
}
2017-11-16 17:38:18 +00:00
void GDScriptParser : : _parse_pattern_block ( BlockNode * p_block , Vector < PatternBranchNode * > & p_branches , bool p_static ) {
2019-10-13 19:48:18 +00:00
IndentLevel current_level = indent_level . back ( ) - > get ( ) ;
2017-03-05 15:44:50 +00:00
2019-02-20 19:49:48 +00:00
p_block - > has_return = true ;
2019-07-13 02:15:27 +00:00
bool catch_all_appeared = false ;
2016-09-30 19:40:31 +00:00
while ( true ) {
2017-03-05 15:44:50 +00:00
2017-11-16 17:38:18 +00:00
while ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_NEWLINE & & _parse_newline ( ) )
2017-03-05 15:44:50 +00:00
;
2017-11-16 17:38:18 +00:00
// GDScriptTokenizer::Token token = tokenizer->get_token();
2016-09-30 19:40:31 +00:00
if ( error_set )
return ;
2017-03-05 15:44:50 +00:00
2019-10-13 19:48:18 +00:00
if ( current_level . indent > indent_level . back ( ) - > get ( ) . indent ) {
2019-07-13 02:15:27 +00:00
break ; // go back a level
2016-09-30 19:40:31 +00:00
}
2017-03-05 15:44:50 +00:00
2019-10-14 09:40:55 +00:00
pending_newline = - 1 ;
2017-03-05 15:44:50 +00:00
2016-10-05 16:48:38 +00:00
PatternBranchNode * branch = alloc_node < PatternBranchNode > ( ) ;
2018-05-30 02:16:54 +00: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 15:44:50 +00:00
2016-10-16 11:20:28 +00:00
branch - > patterns . push_back ( _parse_pattern ( p_static ) ) ;
if ( ! branch - > patterns [ 0 ] ) {
2019-07-13 02:15:27 +00:00
break ;
2016-09-30 19:40:31 +00:00
}
2017-03-05 15:44:50 +00:00
2018-05-30 02:16:54 +00: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 ;
2019-07-13 02:15:27 +00:00
# ifdef DEBUG_ENABLED
// Branches after a wildcard or binding are unreachable
if ( catch_all_appeared & & ! current_function - > has_unreachable_code ) {
_add_warning ( GDScriptWarning : : UNREACHABLE_CODE , - 1 , current_function - > name . operator String ( ) ) ;
current_function - > has_unreachable_code = true ;
}
# endif
2017-11-16 17:38:18 +00:00
while ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COMMA ) {
2016-10-16 11:20:28 +00:00
tokenizer - > advance ( ) ;
branch - > patterns . push_back ( _parse_pattern ( p_static ) ) ;
if ( ! branch - > patterns [ branch - > patterns . size ( ) - 1 ] ) {
return ;
}
2018-05-30 02:16:54 +00: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 11:20:28 +00:00
}
2017-03-05 15:44:50 +00:00
2019-07-13 02:15:27 +00:00
catch_all_appeared = catch_all_appeared | | catch_all ;
2017-03-05 15:44:50 +00:00
if ( ! _enter_indent_block ( ) ) {
2016-09-30 19:40:31 +00:00
_set_error ( " Expected block in pattern branch " ) ;
return ;
}
2017-03-05 15:44:50 +00:00
2016-09-30 19:40:31 +00:00
_parse_block ( branch - > body , p_static ) ;
2017-03-05 15:44:50 +00:00
2016-10-05 16:48:38 +00:00
current_block = p_block ;
2017-03-05 15:44:50 +00:00
2019-02-20 19:49:48 +00:00
if ( ! branch - > body - > has_return ) {
p_block - > has_return = false ;
2018-05-30 02:16:54 +00:00
}
2016-10-05 16:48:38 +00:00
p_branches . push_back ( branch ) ;
}
2019-07-13 02:15:27 +00:00
// Even if all branches return, there is possibility of default fallthrough
if ( ! catch_all_appeared ) {
p_block - > has_return = false ;
}
2016-10-05 16:48:38 +00:00
}
2017-11-16 17:38:18 +00: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 02:16:54 +00:00
const DataType & to_match_type = p_node_to_match - > get_datatype ( ) ;
2016-10-16 11:20:28 +00:00
switch ( p_pattern - > pt_type ) {
case PatternNode : : PT_CONSTANT : {
2017-03-05 15:44:50 +00:00
2018-05-30 02:16:54 +00: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 ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " The pattern type ( " + pattern_type . to_string ( ) + " ) isn't compatible with the type of the value to match ( " + to_match_type . to_string ( ) + " ). " ,
2018-05-30 02:16:54 +00:00
p_pattern - > line ) ;
return ;
}
} else {
// runtime typecheck
BuiltInFunctionNode * typeof_node = alloc_node < BuiltInFunctionNode > ( ) ;
typeof_node - > function = GDScriptFunctions : : TYPE_OF ;
2017-03-05 15:44:50 +00:00
2018-05-30 02:16:54 +00: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 15:44:50 +00:00
2018-05-30 02:16:54 +00: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 15:44:50 +00:00
2018-05-30 02:16:54 +00: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 15:44:50 +00:00
2018-05-30 02:16:54 +00:00
// compare the actual values
2016-10-16 11:20:28 +00: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 15:44:50 +00:00
2018-05-30 02:16:54 +00: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 15:44:50 +00:00
2018-05-30 02:16:54 +00:00
p_resulting_node = full_comparison ;
} else {
p_resulting_node = value_comp ;
}
2017-03-05 15:44:50 +00:00
2016-10-05 16:48:38 +00:00
} break ;
case PatternNode : : PT_BIND : {
2016-10-16 11:20:28 +00:00
p_bindings [ p_pattern - > bind ] = p_node_to_match ;
2017-03-05 15:44:50 +00:00
2016-10-16 11:20:28 +00:00
// a bind always matches
2016-10-05 16:48:38 +00: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 15:44:50 +00:00
2016-10-16 11:20:28 +00:00
bool open_ended = false ;
2017-03-05 15:44:50 +00:00
2016-10-16 11:20:28 +00: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 15:44:50 +00:00
2016-10-16 11:20:28 +00: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 15:44:50 +00:00
2016-10-16 11:20:28 +00:00
{
2018-05-30 02:16:54 +00: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 15:44:50 +00:00
2018-05-30 02:16:54 +00: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 15:44:50 +00:00
2018-05-30 02:16:54 +00:00
IdentifierNode * typeof_array = alloc_node < IdentifierNode > ( ) ;
typeof_array - > name = " TYPE_ARRAY " ;
2017-03-05 15:44:50 +00:00
2018-05-30 02:16:54 +00: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 15:44:50 +00:00
2016-10-16 11:20:28 +00:00
// size
ConstantNode * length = alloc_node < ConstantNode > ( ) ;
length - > value = Variant ( open_ended ? p_pattern - > array . size ( ) - 1 : p_pattern - > array . size ( ) ) ;
2017-03-05 15:44:50 +00:00
2016-10-16 11:20:28 +00:00
OperatorNode * call = alloc_node < OperatorNode > ( ) ;
call - > op = OperatorNode : : OP_CALL ;
call - > arguments . push_back ( p_node_to_match ) ;
2017-03-05 15:44:50 +00:00
2016-10-16 11:20:28 +00:00
IdentifierNode * size = alloc_node < IdentifierNode > ( ) ;
size - > name = " size " ;
call - > arguments . push_back ( size ) ;
2017-03-05 15:44:50 +00:00
2016-10-16 11:20:28 +00: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 15:44:50 +00:00
2018-05-30 02:16:54 +00: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 15:44:50 +00:00
2018-05-30 02:16:54 +00:00
p_resulting_node = type_and_length_comparison ;
} else {
p_resulting_node = length_comparison ;
}
2016-10-16 11:20:28 +00:00
}
2017-03-05 15:44:50 +00:00
2016-10-16 11:20:28 +00:00
for ( int i = 0 ; i < p_pattern - > array . size ( ) ; i + + ) {
PatternNode * pattern = p_pattern - > array [ i ] ;
2017-03-05 15:44:50 +00:00
2016-10-16 11:20:28 +00:00
Node * condition = NULL ;
2017-03-05 15:44:50 +00:00
2016-10-16 11:20:28 +00:00
ConstantNode * index = alloc_node < ConstantNode > ( ) ;
index - > value = Variant ( i ) ;
2017-03-05 15:44:50 +00:00
2016-10-16 11:20:28 +00: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 15:44:50 +00:00
2016-10-16 11:20:28 +00:00
_generate_pattern ( pattern , indexed_value , condition , p_bindings ) ;
2017-03-05 15:44:50 +00:00
2016-10-16 11:20:28 +00: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 15:44:50 +00:00
2016-10-16 11:20:28 +00:00
p_resulting_node = and_node ;
}
2017-03-05 15:44:50 +00:00
2016-10-05 16:48:38 +00:00
} break ;
case PatternNode : : PT_DICTIONARY : {
2017-03-05 15:44:50 +00:00
2016-10-16 11:20:28 +00:00
bool open_ended = false ;
2017-03-05 15:44:50 +00:00
2016-10-16 11:20:28 +00:00
if ( p_pattern - > array . size ( ) > 0 ) {
open_ended = true ;
}
2017-03-05 15:44:50 +00:00
2016-10-16 11:20:28 +00: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 15:44:50 +00:00
2016-10-16 11:20:28 +00:00
{
2018-05-30 02:16:54 +00: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 15:44:50 +00:00
2018-05-30 02:16:54 +00: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 15:44:50 +00:00
2018-05-30 02:16:54 +00:00
IdentifierNode * typeof_dictionary = alloc_node < IdentifierNode > ( ) ;
typeof_dictionary - > name = " TYPE_DICTIONARY " ;
2017-03-05 15:44:50 +00:00
2018-05-30 02:16:54 +00: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 15:44:50 +00:00
2016-10-16 11:20:28 +00:00
// size
ConstantNode * length = alloc_node < ConstantNode > ( ) ;
length - > value = Variant ( open_ended ? p_pattern - > dictionary . size ( ) - 1 : p_pattern - > dictionary . size ( ) ) ;
2017-03-05 15:44:50 +00:00
2016-10-16 11:20:28 +00:00
OperatorNode * call = alloc_node < OperatorNode > ( ) ;
call - > op = OperatorNode : : OP_CALL ;
call - > arguments . push_back ( p_node_to_match ) ;
2017-03-05 15:44:50 +00:00
2016-10-16 11:20:28 +00:00
IdentifierNode * size = alloc_node < IdentifierNode > ( ) ;
size - > name = " size " ;
call - > arguments . push_back ( size ) ;
2017-03-05 15:44:50 +00:00
2016-10-16 11:20:28 +00: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 15:44:50 +00:00
2018-05-30 02:16:54 +00: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 15:44:50 +00:00
2018-05-30 02:16:54 +00:00
p_resulting_node = type_and_length_comparison ;
} else {
p_resulting_node = length_comparison ;
}
2016-10-16 11:20:28 +00:00
}
2017-03-05 15:44:50 +00:00
for ( Map < ConstantNode * , PatternNode * > : : Element * e = p_pattern - > dictionary . front ( ) ; e ; e = e - > next ( ) ) {
2016-10-16 11:20:28 +00:00
Node * condition = NULL ;
2017-03-05 15:44:50 +00:00
2018-09-13 01:38:39 +00:00
// check for has, then for pattern
2017-03-05 15:44:50 +00:00
2016-10-16 11:20:28 +00:00
IdentifierNode * has = alloc_node < IdentifierNode > ( ) ;
has - > name = " has " ;
2017-03-05 15:44:50 +00:00
2016-10-16 11:20:28 +00: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 15:44:50 +00:00
2016-10-16 11:20:28 +00:00
if ( e - > value ( ) ) {
2017-03-05 15:44:50 +00:00
2016-10-16 11:20:28 +00: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 15:44:50 +00:00
2016-10-16 11:20:28 +00:00
_generate_pattern ( e - > value ( ) , indexed_value , condition , p_bindings ) ;
2017-03-05 15:44:50 +00:00
2016-10-16 11:20:28 +00: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 15:44:50 +00:00
2016-10-16 11:20:28 +00:00
condition = has_and_pattern ;
} else {
condition = has_call ;
}
2017-03-05 15:44:50 +00:00
2016-10-16 11:20:28 +00: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 15:44:50 +00:00
2016-10-16 11:20:28 +00:00
p_resulting_node = and_node ;
}
2017-03-05 15:44:50 +00:00
2016-10-05 16:48:38 +00:00
} break ;
2016-10-16 11:20:28 +00:00
case PatternNode : : PT_IGNORE_REST :
2016-10-05 16:48:38 +00: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 15:44:50 +00:00
2016-10-05 16:48:38 +00:00
} break ;
}
}
2018-05-30 02:16:54 +00:00
void GDScriptParser : : _transform_match_statment ( MatchNode * p_match_statement ) {
2016-10-05 16:48:38 +00:00
IdentifierNode * id = alloc_node < IdentifierNode > ( ) ;
2016-10-16 11:20:28 +00:00
id - > name = " #match_value " ;
2018-06-06 01:57:44 +00:00
id - > line = p_match_statement - > line ;
2018-05-30 02:16:54 +00:00
id - > datatype = _reduce_node_type ( p_match_statement - > val_to_match ) ;
2018-06-05 16:50:21 +00:00
if ( id - > datatype . has_type ) {
_mark_line_as_safe ( id - > line ) ;
} else {
_mark_line_as_unsafe ( id - > line ) ;
}
2018-05-30 02:16:54 +00:00
if ( error_set ) {
return ;
}
2017-03-05 15:44:50 +00:00
2016-10-05 16:48:38 +00:00
for ( int i = 0 ; i < p_match_statement - > branches . size ( ) ; i + + ) {
2017-03-05 15:44:50 +00:00
2016-10-05 16:48:38 +00:00
PatternBranchNode * branch = p_match_statement - > branches [ i ] ;
2017-03-05 15:44:50 +00:00
2016-10-16 11:20:28 +00:00
MatchNode : : CompiledPatternBranch compiled_branch ;
compiled_branch . compiled_pattern = NULL ;
2017-03-05 15:44:50 +00:00
Map < StringName , Node * > binding ;
2016-10-16 11:20:28 +00:00
for ( int j = 0 ; j < branch - > patterns . size ( ) ; j + + ) {
PatternNode * pattern = branch - > patterns [ j ] ;
2018-06-05 16:50:21 +00:00
_mark_line_as_safe ( pattern - > line ) ;
2017-03-05 15:44:50 +00:00
Map < StringName , Node * > bindings ;
2018-05-30 02:16:54 +00:00
Node * resulting_node = NULL ;
2016-10-16 11:20:28 +00:00
_generate_pattern ( pattern , id , resulting_node , bindings ) ;
2017-03-05 15:44:50 +00:00
2018-05-30 02:16:54 +00:00
if ( ! resulting_node ) {
return ;
}
2016-10-16 11:20:28 +00:00
if ( ! binding . empty ( ) & & ! bindings . empty ( ) ) {
_set_error ( " Multipatterns can't contain bindings " ) ;
return ;
} else {
binding = bindings ;
}
2017-03-05 15:44:50 +00:00
2018-05-30 02:16:54 +00: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 11:20:28 +00: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 15:44:50 +00:00
2016-10-16 11:20:28 +00:00
compiled_branch . compiled_pattern = or_node ;
} else {
// single pattern | first one
compiled_branch . compiled_pattern = resulting_node ;
}
}
2017-03-05 15:44:50 +00:00
2016-10-16 11:20:28 +00:00
// prepare the body ...hehe
2017-03-05 15:44:50 +00:00
for ( Map < StringName , Node * > : : Element * e = binding . front ( ) ; e ; e = e - > next ( ) ) {
2018-05-30 02:16:54 +00: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 11:20:28 +00:00
local_var - > assign = e - > value ( ) ;
2018-05-30 02:16:54 +00:00
local_var - > set_datatype ( local_var - > assign - > get_datatype ( ) ) ;
2016-10-16 11:20:28 +00:00
2019-02-12 20:10:08 +00:00
IdentifierNode * id2 = alloc_node < IdentifierNode > ( ) ;
id2 - > name = local_var - > name ;
id2 - > declared_block = branch - > body ;
id2 - > set_datatype ( local_var - > assign - > get_datatype ( ) ) ;
2016-10-16 11:20:28 +00:00
OperatorNode * op = alloc_node < OperatorNode > ( ) ;
2017-03-05 15:44:50 +00:00
op - > op = OperatorNode : : OP_ASSIGN ;
2019-02-12 20:10:08 +00:00
op - > arguments . push_back ( id2 ) ;
2016-10-16 11:20:28 +00:00
op - > arguments . push_back ( local_var - > assign ) ;
2020-01-08 22:28:07 +00:00
local_var - > assign_op = op ;
2017-03-05 15:44:50 +00:00
2016-10-16 11:20:28 +00:00
branch - > body - > statements . push_front ( op ) ;
branch - > body - > statements . push_front ( local_var ) ;
}
2017-03-05 15:44:50 +00:00
2016-10-16 11:20:28 +00:00
compiled_branch . body = branch - > body ;
2017-03-05 15:44:50 +00:00
2016-10-16 11:20:28 +00:00
p_match_statement - > compiled_pattern_branches . push_back ( compiled_branch ) ;
2016-10-05 16:48:38 +00:00
}
2016-09-30 19:40:31 +00:00
}
2017-11-16 17:38:18 +00:00
void GDScriptParser : : _parse_block ( BlockNode * p_block , bool p_static ) {
2014-02-10 01:10:30 +00:00
2019-10-13 19:48:18 +00:00
IndentLevel current_level = indent_level . back ( ) - > get ( ) ;
2014-02-10 01:10:30 +00:00
# ifdef DEBUG_ENABLED
NewLineNode * nl = alloc_node < NewLineNode > ( ) ;
2017-03-05 15:44:50 +00:00
nl - > line = tokenizer - > get_token_line ( ) ;
2014-02-10 01:10:30 +00:00
p_block - > statements . push_back ( nl ) ;
# endif
2017-03-23 18:07:02 +00:00
bool is_first_line = true ;
2017-03-05 15:44:50 +00:00
while ( true ) {
2019-10-13 19:48:18 +00:00
if ( ! is_first_line & & indent_level . back ( ) - > prev ( ) & & indent_level . back ( ) - > prev ( ) - > get ( ) . indent = = current_level . indent ) {
if ( indent_level . back ( ) - > prev ( ) - > get ( ) . is_mixed ( current_level ) ) {
_set_error ( " Mixed tabs and spaces in indentation. " ) ;
return ;
}
2017-03-23 18:07:02 +00:00
// pythonic single-line expression, don't parse future lines
2019-10-13 19:48:18 +00:00
indent_level . pop_back ( ) ;
2017-04-08 16:41:08 +00:00
p_block - > end_line = tokenizer - > get_token_line ( ) ;
2017-03-23 18:07:02 +00:00
return ;
}
is_first_line = false ;
2014-02-10 01:10:30 +00:00
2017-11-16 17:38:18 +00:00
GDScriptTokenizer : : Token token = tokenizer - > get_token ( ) ;
2014-02-10 01:10:30 +00:00
if ( error_set )
return ;
2019-10-13 19:48:18 +00:00
if ( current_level . indent > indent_level . back ( ) - > get ( ) . indent ) {
2017-03-05 15:44:50 +00:00
p_block - > end_line = tokenizer - > get_token_line ( ) ;
2014-02-10 01:10:30 +00:00
return ; //go back a level
}
2017-03-05 15:44:50 +00:00
if ( pending_newline ! = - 1 ) {
2014-05-24 04:35:47 +00:00
2019-02-12 20:10:08 +00:00
NewLineNode * nl2 = alloc_node < NewLineNode > ( ) ;
nl2 - > line = pending_newline ;
p_block - > statements . push_back ( nl2 ) ;
2017-03-05 15:44:50 +00:00
pending_newline = - 1 ;
2014-05-24 04:35:47 +00:00
}
2018-07-01 16:17:40 +00:00
# ifdef DEBUG_ENABLED
2017-03-05 15:44:50 +00:00
switch ( token ) {
2018-05-30 02:16:54 +00: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 16:17:40 +00: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 02:16:54 +00:00
} break ;
}
2018-07-01 16:17:40 +00:00
# endif // DEBUG_ENABLED
2018-05-30 02:16:54 +00:00
switch ( token ) {
2020-02-22 19:47:50 +00:00
case GDScriptTokenizer : : TK_EOF : {
2017-03-05 15:44:50 +00:00
p_block - > end_line = tokenizer - > get_token_line ( ) ;
2020-02-22 19:47:50 +00:00
return ; // End of file!
} break ;
2017-11-16 17:38:18 +00:00
case GDScriptTokenizer : : TK_ERROR : {
2020-02-22 19:47:50 +00:00
return ;
2014-02-10 01:10:30 +00:00
} break ;
2017-11-16 17:38:18 +00:00
case GDScriptTokenizer : : TK_NEWLINE : {
2014-02-10 01:10:30 +00:00
2019-03-14 16:38:07 +00:00
int line = tokenizer - > get_token_line ( ) ;
2014-02-10 01:10:30 +00:00
if ( ! _parse_newline ( ) ) {
if ( ! error_set ) {
2017-03-05 15:44:50 +00:00
p_block - > end_line = tokenizer - > get_token_line ( ) ;
pending_newline = p_block - > end_line ;
2014-02-10 01:10:30 +00:00
}
return ;
}
2014-05-24 04:35:47 +00:00
2019-02-12 20:10:08 +00:00
NewLineNode * nl2 = alloc_node < NewLineNode > ( ) ;
2019-03-14 16:38:07 +00:00
nl2 - > line = line ;
2019-02-12 20:10:08 +00:00
p_block - > statements . push_back ( nl2 ) ;
2014-05-24 04:35:47 +00:00
2014-02-10 01:10:30 +00:00
} break ;
2017-11-16 17:38:18 +00: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 01:10:30 +00:00
2019-08-23 18:51:42 +00:00
_set_error ( " Expected \" ; \" or a line break. " ) ;
2014-02-10 01:10:30 +00:00
return ;
}
2018-06-05 16:50:21 +00:00
_mark_line_as_safe ( tokenizer - > get_token_line ( ) ) ;
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_SEMICOLON ) {
2019-08-28 02:10:33 +00:00
// Ignore semicolon after 'pass'.
2016-07-10 15:20:53 +00:00
tokenizer - > advance ( ) ;
}
2014-02-10 01:10:30 +00:00
} break ;
2017-11-16 17:38:18 +00:00
case GDScriptTokenizer : : TK_PR_VAR : {
2019-08-28 02:10:33 +00:00
// Variable declaration and (eventual) initialization.
2014-02-10 01:10:30 +00:00
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2018-05-30 02:16:54 +00:00
int var_line = tokenizer - > get_token_line ( ) ;
2017-03-31 17:28:34 +00:00
if ( ! tokenizer - > is_token_literal ( 0 , true ) ) {
2014-02-10 01:10:30 +00:00
2019-08-23 18:51:42 +00:00
_set_error ( " Expected an identifier for the local variable name. " ) ;
2014-02-10 01:10:30 +00:00
return ;
}
2017-03-31 17:28:34 +00:00
StringName n = tokenizer - > get_token_literal ( ) ;
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2017-03-05 15:44:50 +00:00
if ( current_function ) {
for ( int i = 0 ; i < current_function - > arguments . size ( ) ; i + + ) {
if ( n = = current_function - > arguments [ i ] ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Variable \" " + String ( n ) + " \" already defined in the scope (at line " + itos ( current_function - > line ) + " ). " ) ;
2016-05-30 15:22:34 +00:00
return ;
}
}
}
BlockNode * check_block = p_block ;
2017-03-05 15:44:50 +00:00
while ( check_block ) {
2018-05-30 02:16:54 +00:00
if ( check_block - > variables . has ( n ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Variable \" " + String ( n ) + " \" already defined in the scope (at line " + itos ( check_block - > variables [ n ] - > line ) + " ). " ) ;
2018-05-30 02:16:54 +00:00
return ;
2016-05-30 15:22:34 +00:00
}
check_block = check_block - > parent_block ;
}
2014-02-10 01:10:30 +00:00
//must know when the local variable is declared
LocalVarNode * lv = alloc_node < LocalVarNode > ( ) ;
2017-03-05 15:44:50 +00:00
lv - > name = n ;
2018-05-30 02:16:54 +00:00
lv - > line = var_line ;
2014-02-10 01:10:30 +00:00
p_block - > statements . push_back ( lv ) ;
2017-03-05 15:44:50 +00:00
Node * assigned = NULL ;
2014-02-10 01:10:30 +00:00
2018-05-30 02:16:51 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COLON ) {
2018-06-19 05:55:52 +00: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 ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected a type for the variable. " ) ;
2018-05-30 02:16:51 +00:00
return ;
}
}
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_OP_ASSIGN ) {
2014-02-10 01:10:30 +00:00
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2017-08-21 19:15:36 +00:00
Node * subexpr = _parse_and_reduce_expression ( p_block , p_static ) ;
2015-08-30 14:50:10 +00:00
if ( ! subexpr ) {
if ( _recover_from_completion ( ) ) {
break ;
}
2014-02-10 01:10:30 +00:00
return ;
2015-08-30 14:50:10 +00:00
}
2018-05-30 02:16:54 +00:00
lv - > assignments + + ;
2017-03-05 15:44:50 +00:00
assigned = subexpr ;
2014-02-10 01:10:30 +00:00
} else {
2019-09-03 07:39:04 +00:00
assigned = _get_default_value_for_type ( lv - > datatype , var_line ) ;
2014-02-10 01:10:30 +00:00
}
2017-08-08 14:44:49 +00:00
//must be added later, to avoid self-referencing.
2018-05-30 02:16:54 +00:00
p_block - > variables . insert ( n , lv ) ;
2017-08-08 14:44:49 +00:00
2014-02-10 01:10:30 +00:00
IdentifierNode * id = alloc_node < IdentifierNode > ( ) ;
2017-03-05 15:44:50 +00:00
id - > name = n ;
2018-05-30 02:16:54 +00:00
id - > declared_block = p_block ;
2018-06-06 01:57:44 +00:00
id - > line = var_line ;
2014-02-10 01:10:30 +00:00
OperatorNode * op = alloc_node < OperatorNode > ( ) ;
2017-03-05 15:44:50 +00:00
op - > op = OperatorNode : : OP_ASSIGN ;
2014-02-10 01:10:30 +00:00
op - > arguments . push_back ( id ) ;
op - > arguments . push_back ( assigned ) ;
2018-06-06 01:57:44 +00:00
op - > line = var_line ;
2014-02-10 01:10:30 +00:00
p_block - > statements . push_back ( op ) ;
2018-05-30 02:16:54 +00:00
lv - > assign_op = op ;
lv - > assign = assigned ;
2014-02-10 01:10:30 +00:00
2015-06-24 16:29:23 +00:00
if ( ! _end_statement ( ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected end of statement ( \" var \" ). " ) ;
2015-06-24 16:29:23 +00:00
return ;
}
2014-02-10 01:10:30 +00:00
} break ;
2017-11-16 17:38:18 +00:00
case GDScriptTokenizer : : TK_CF_IF : {
2014-02-10 01:10:30 +00:00
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2017-03-05 15:44:50 +00:00
Node * condition = _parse_and_reduce_expression ( p_block , p_static ) ;
2015-08-30 14:50:10 +00:00
if ( ! condition ) {
if ( _recover_from_completion ( ) ) {
break ;
}
2014-02-10 01:10:30 +00:00
return ;
2015-08-30 14:50:10 +00:00
}
2014-02-10 01:10:30 +00:00
ControlFlowNode * cf_if = alloc_node < ControlFlowNode > ( ) ;
2017-03-05 15:44:50 +00:00
cf_if - > cf_type = ControlFlowNode : : CF_IF ;
2014-02-10 01:10:30 +00:00
cf_if - > arguments . push_back ( condition ) ;
cf_if - > body = alloc_node < BlockNode > ( ) ;
2017-03-05 15:44:50 +00:00
cf_if - > body - > parent_block = p_block ;
2017-08-25 03:34:32 +00:00
cf_if - > body - > if_condition = condition ; //helps code completion
2014-02-10 01:10:30 +00:00
p_block - > sub_blocks . push_back ( cf_if - > body ) ;
if ( ! _enter_indent_block ( cf_if - > body ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected an indented block after \" if \" . " ) ;
2017-03-05 15:44:50 +00:00
p_block - > end_line = tokenizer - > get_token_line ( ) ;
2014-02-10 01:10:30 +00:00
return ;
}
2017-03-05 15:44:50 +00:00
current_block = cf_if - > body ;
_parse_block ( cf_if - > body , p_static ) ;
current_block = p_block ;
2014-12-17 01:31:57 +00:00
2014-02-10 01:10:30 +00:00
if ( error_set )
return ;
p_block - > statements . push_back ( cf_if ) ;
2018-05-30 02:16:54 +00:00
bool all_have_return = cf_if - > body - > has_return ;
bool have_else = false ;
2017-03-05 15:44:50 +00:00
while ( true ) {
2014-02-10 01:10:30 +00:00
2017-11-16 17:38:18 +00:00
while ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_NEWLINE & & _parse_newline ( ) )
2017-04-08 16:41:08 +00:00
;
2014-02-10 01:10:30 +00:00
2019-10-13 19:48:18 +00:00
if ( indent_level . back ( ) - > get ( ) . indent < current_level . indent ) { //not at current indent level
2017-03-05 15:44:50 +00:00
p_block - > end_line = tokenizer - > get_token_line ( ) ;
2014-02-10 01:10:30 +00:00
return ;
}
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CF_ELIF ) {
2014-02-10 01:10:30 +00:00
2019-10-13 19:48:18 +00:00
if ( indent_level . back ( ) - > get ( ) . indent > current_level . indent ) {
2014-02-10 01:10:30 +00:00
2019-08-23 18:51:42 +00:00
_set_error ( " Invalid indentation. " ) ;
2014-02-10 01:10:30 +00:00
return ;
}
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
cf_if - > body_else = alloc_node < BlockNode > ( ) ;
cf_if - > body_else - > parent_block = p_block ;
2014-02-10 01:10:30 +00:00
p_block - > sub_blocks . push_back ( cf_if - > body_else ) ;
ControlFlowNode * cf_else = alloc_node < ControlFlowNode > ( ) ;
2017-03-05 15:44:50 +00:00
cf_else - > cf_type = ControlFlowNode : : CF_IF ;
2014-02-10 01:10:30 +00:00
//condition
2019-02-12 20:10:08 +00:00
Node * condition2 = _parse_and_reduce_expression ( p_block , p_static ) ;
if ( ! condition2 ) {
2015-08-30 14:50:10 +00:00
if ( _recover_from_completion ( ) ) {
break ;
}
2014-02-10 01:10:30 +00:00
return ;
2015-08-30 14:50:10 +00:00
}
2019-02-12 20:10:08 +00:00
cf_else - > arguments . push_back ( condition2 ) ;
2017-03-05 15:44:50 +00:00
cf_else - > cf_type = ControlFlowNode : : CF_IF ;
2014-02-10 01:10:30 +00:00
cf_if - > body_else - > statements . push_back ( cf_else ) ;
2017-03-05 15:44:50 +00:00
cf_if = cf_else ;
cf_if - > body = alloc_node < BlockNode > ( ) ;
cf_if - > body - > parent_block = p_block ;
2014-02-10 01:10:30 +00:00
p_block - > sub_blocks . push_back ( cf_if - > body ) ;
if ( ! _enter_indent_block ( cf_if - > body ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected an indented block after \" elif \" . " ) ;
2017-03-05 15:44:50 +00:00
p_block - > end_line = tokenizer - > get_token_line ( ) ;
2014-02-10 01:10:30 +00:00
return ;
}
2017-03-05 15:44:50 +00:00
current_block = cf_else - > body ;
_parse_block ( cf_else - > body , p_static ) ;
current_block = p_block ;
2014-02-10 01:10:30 +00:00
if ( error_set )
return ;
2018-05-30 02:16:54 +00:00
all_have_return = all_have_return & & cf_else - > body - > has_return ;
2017-11-16 17:38:18 +00:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CF_ELSE ) {
2014-02-10 01:10:30 +00:00
2019-10-13 19:48:18 +00:00
if ( indent_level . back ( ) - > get ( ) . indent > current_level . indent ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Invalid indentation. " ) ;
2014-02-10 01:10:30 +00:00
return ;
}
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2017-03-05 15:44:50 +00:00
cf_if - > body_else = alloc_node < BlockNode > ( ) ;
cf_if - > body_else - > parent_block = p_block ;
2014-02-10 01:10:30 +00:00
p_block - > sub_blocks . push_back ( cf_if - > body_else ) ;
if ( ! _enter_indent_block ( cf_if - > body_else ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected an indented block after \" else \" . " ) ;
2017-03-05 15:44:50 +00:00
p_block - > end_line = tokenizer - > get_token_line ( ) ;
2014-02-10 01:10:30 +00:00
return ;
}
2017-03-05 15:44:50 +00:00
current_block = cf_if - > body_else ;
_parse_block ( cf_if - > body_else , p_static ) ;
current_block = p_block ;
2014-02-10 01:10:30 +00:00
if ( error_set )
return ;
2018-05-30 02:16:54 +00:00
all_have_return = all_have_return & & cf_if - > body_else - > has_return ;
have_else = true ;
2014-02-10 01:10:30 +00:00
break ; //after else, exit
} else
break ;
}
2018-05-30 02:16:54 +00: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 01:10:30 +00:00
} break ;
2017-11-16 17:38:18 +00:00
case GDScriptTokenizer : : TK_CF_WHILE : {
2014-02-10 01:10:30 +00:00
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2019-02-12 20:10:08 +00:00
Node * condition2 = _parse_and_reduce_expression ( p_block , p_static ) ;
if ( ! condition2 ) {
2015-08-30 14:50:10 +00:00
if ( _recover_from_completion ( ) ) {
break ;
}
2014-02-10 01:10:30 +00:00
return ;
2015-08-30 14:50:10 +00:00
}
2014-02-10 01:10:30 +00:00
ControlFlowNode * cf_while = alloc_node < ControlFlowNode > ( ) ;
2017-03-05 15:44:50 +00:00
cf_while - > cf_type = ControlFlowNode : : CF_WHILE ;
2019-02-12 20:10:08 +00:00
cf_while - > arguments . push_back ( condition2 ) ;
2014-02-10 01:10:30 +00:00
cf_while - > body = alloc_node < BlockNode > ( ) ;
2017-03-05 15:44:50 +00:00
cf_while - > body - > parent_block = p_block ;
2014-02-10 01:10:30 +00:00
p_block - > sub_blocks . push_back ( cf_while - > body ) ;
if ( ! _enter_indent_block ( cf_while - > body ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected an indented block after \" while \" . " ) ;
2017-03-05 15:44:50 +00:00
p_block - > end_line = tokenizer - > get_token_line ( ) ;
2014-02-10 01:10:30 +00:00
return ;
}
2017-03-05 15:44:50 +00:00
current_block = cf_while - > body ;
_parse_block ( cf_while - > body , p_static ) ;
current_block = p_block ;
2014-02-10 01:10:30 +00:00
if ( error_set )
return ;
2018-05-30 02:16:54 +00:00
p_block - > has_return = cf_while - > body - > has_return ;
2014-02-10 01:10:30 +00:00
p_block - > statements . push_back ( cf_while ) ;
} break ;
2017-11-16 17:38:18 +00:00
case GDScriptTokenizer : : TK_CF_FOR : {
2014-02-10 01:10:30 +00:00
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2014-02-10 01:10:30 +00:00
2017-03-31 17:28:34 +00:00
if ( ! tokenizer - > is_token_literal ( 0 , true ) ) {
2014-02-10 01:10:30 +00:00
2019-08-23 18:51:42 +00:00
_set_error ( " Identifier expected after \" for \" . " ) ;
2014-02-10 01:10:30 +00:00
}
IdentifierNode * id = alloc_node < IdentifierNode > ( ) ;
2017-03-05 15:44:50 +00:00
id - > name = tokenizer - > get_token_identifier ( ) ;
2014-02-10 01:10:30 +00:00
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2014-02-10 01:10:30 +00:00
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_OP_IN ) {
2019-08-23 18:51:42 +00:00
_set_error ( " \" in \" expected after identifier. " ) ;
2014-02-10 01:10:30 +00:00
return ;
}
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
Node * container = _parse_and_reduce_expression ( p_block , p_static ) ;
2015-08-30 14:50:10 +00:00
if ( ! container ) {
if ( _recover_from_completion ( ) ) {
break ;
}
2014-02-10 01:10:30 +00:00
return ;
2015-08-30 14:50:10 +00:00
}
2014-02-10 01:10:30 +00:00
2018-05-30 02:16:54 +00:00
DataType iter_type ;
2017-03-05 15:44:50 +00:00
if ( container - > type = = Node : : TYPE_OPERATOR ) {
2017-01-11 23:09:45 +00:00
2017-03-05 15:44:50 +00:00
OperatorNode * op = static_cast < OperatorNode * > ( container ) ;
2017-11-16 17:38:18 +00: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-11 23:09:45 +00: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 15:44:50 +00:00
Vector < Node * > args ;
2017-01-11 23:09:45 +00:00
Vector < double > constants ;
2017-03-05 15:44:50 +00:00
bool constant = false ;
2017-01-11 23:09:45 +00:00
2017-03-05 15:44:50 +00:00
for ( int i = 1 ; i < op - > arguments . size ( ) ; i + + ) {
2017-01-11 23:09:45 +00:00
args . push_back ( op - > arguments [ i ] ) ;
2017-03-05 15:44:50 +00:00
if ( constant & & op - > arguments [ i ] - > type = = Node : : TYPE_CONSTANT ) {
ConstantNode * c = static_cast < ConstantNode * > ( op - > arguments [ i ] ) ;
Variant: Added 64-bit packed arrays, renamed Variant::REAL to FLOAT.
- Renames PackedIntArray to PackedInt32Array.
- Renames PackedFloatArray to PackedFloat32Array.
- Adds PackedInt64Array and PackedFloat64Array.
- Renames Variant::REAL to Variant::FLOAT for consistency.
Packed arrays are for storing large amount of data and creating stuff like
meshes, buffers. textures, etc. Forcing them to be 64 is a huge waste of
memory. That said, many users requested the ability to have 64 bits packed
arrays for their games, so this is just an optional added type.
For Variant, the float datatype is always 64 bits, and exposed as `float`.
We still have `real_t` which is the datatype that can change from 32 to 64
bits depending on a compile flag (not entirely working right now, but that's
the idea). It affects math related datatypes and code only.
Neither Variant nor PackedArray make use of real_t, which is only intended
for math precision, so the term is removed from there to keep only float.
2020-02-24 18:20:53 +00:00
if ( c - > value . get_type ( ) = = Variant : : FLOAT | | c - > value . get_type ( ) = = Variant : : INT ) {
2017-01-11 23:09:45 +00:00
constants . push_back ( c - > value ) ;
2017-03-05 15:44:50 +00:00
constant = true ;
2017-01-11 23:09:45 +00:00
}
2017-02-13 01:51:16 +00:00
} else {
2017-03-05 15:44:50 +00:00
constant = false ;
2017-01-11 23:09:45 +00:00
}
}
2017-03-05 15:44:50 +00:00
if ( args . size ( ) > 0 & & args . size ( ) < 4 ) {
2017-01-11 23:09:45 +00:00
if ( constant ) {
ConstantNode * cn = alloc_node < ConstantNode > ( ) ;
2017-03-05 15:44:50 +00:00
switch ( args . size ( ) ) {
2017-05-16 10:56:49 +00:00
case 1 : cn - > value = ( int ) constants [ 0 ] ; break ;
2017-03-05 15:44:50 +00: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-11 23:09:45 +00:00
}
2018-05-30 02:16:54 +00:00
cn - > datatype = _type_from_variant ( cn - > value ) ;
2017-03-05 15:44:50 +00:00
container = cn ;
2017-01-11 23:09:45 +00:00
} else {
OperatorNode * on = alloc_node < OperatorNode > ( ) ;
2017-03-05 15:44:50 +00:00
on - > op = OperatorNode : : OP_CALL ;
2017-01-11 23:09:45 +00:00
TypeNode * tn = alloc_node < TypeNode > ( ) ;
on - > arguments . push_back ( tn ) ;
2017-03-05 15:44:50 +00:00
switch ( args . size ( ) ) {
2017-05-16 10:56:49 +00:00
case 1 : tn - > vtype = Variant : : INT ; break ;
2017-03-05 15:44:50 +00:00
case 2 : tn - > vtype = Variant : : VECTOR2 ; break ;
case 3 : tn - > vtype = Variant : : VECTOR3 ; break ;
2017-01-11 23:09:45 +00:00
}
2017-03-05 15:44:50 +00:00
for ( int i = 0 ; i < args . size ( ) ; i + + ) {
2017-01-11 23:09:45 +00:00
on - > arguments . push_back ( args [ i ] ) ;
}
2017-03-05 15:44:50 +00:00
container = on ;
2017-01-11 23:09:45 +00:00
}
}
2018-05-30 02:16:54 +00:00
iter_type . has_type = true ;
iter_type . kind = DataType : : BUILTIN ;
iter_type . builtin_type = Variant : : INT ;
2017-01-11 23:09:45 +00:00
}
}
2014-02-10 01:10:30 +00:00
ControlFlowNode * cf_for = alloc_node < ControlFlowNode > ( ) ;
2017-03-05 15:44:50 +00:00
cf_for - > cf_type = ControlFlowNode : : CF_FOR ;
2014-02-10 01:10:30 +00:00
cf_for - > arguments . push_back ( id ) ;
cf_for - > arguments . push_back ( container ) ;
cf_for - > body = alloc_node < BlockNode > ( ) ;
2017-03-05 15:44:50 +00:00
cf_for - > body - > parent_block = p_block ;
2014-02-10 01:10:30 +00:00
p_block - > sub_blocks . push_back ( cf_for - > body ) ;
if ( ! _enter_indent_block ( cf_for - > body ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected indented block after \" for \" . " ) ;
2017-03-05 15:44:50 +00:00
p_block - > end_line = tokenizer - > get_token_line ( ) ;
2014-02-10 01:10:30 +00:00
return ;
}
2017-03-05 15:44:50 +00:00
current_block = cf_for - > body ;
2016-05-30 15:22:34 +00:00
// this is for checking variable for redefining
// inside this _parse_block
2018-05-30 02:16:54 +00: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 15:44:50 +00:00
_parse_block ( cf_for - > body , p_static ) ;
current_block = p_block ;
2014-12-17 01:31:57 +00:00
2014-02-10 01:10:30 +00:00
if ( error_set )
return ;
2018-05-30 02:16:54 +00:00
p_block - > has_return = cf_for - > body - > has_return ;
2014-02-10 01:10:30 +00:00
p_block - > statements . push_back ( cf_for ) ;
} break ;
2017-11-16 17:38:18 +00:00
case GDScriptTokenizer : : TK_CF_CONTINUE : {
2014-02-10 01:10:30 +00:00
2019-08-28 02:10:33 +00:00
_mark_line_as_safe ( tokenizer - > get_token_line ( ) ) ;
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2014-02-10 01:10:30 +00:00
ControlFlowNode * cf_continue = alloc_node < ControlFlowNode > ( ) ;
2017-03-05 15:44:50 +00:00
cf_continue - > cf_type = ControlFlowNode : : CF_CONTINUE ;
2014-02-10 01:10:30 +00:00
p_block - > statements . push_back ( cf_continue ) ;
if ( ! _end_statement ( ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected end of statement ( \" continue \" ). " ) ;
2014-02-10 01:10:30 +00:00
return ;
}
} break ;
2017-11-16 17:38:18 +00:00
case GDScriptTokenizer : : TK_CF_BREAK : {
2014-02-10 01:10:30 +00:00
2019-08-28 02:10:33 +00:00
_mark_line_as_safe ( tokenizer - > get_token_line ( ) ) ;
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2014-02-10 01:10:30 +00:00
ControlFlowNode * cf_break = alloc_node < ControlFlowNode > ( ) ;
2017-03-05 15:44:50 +00:00
cf_break - > cf_type = ControlFlowNode : : CF_BREAK ;
2014-02-10 01:10:30 +00:00
p_block - > statements . push_back ( cf_break ) ;
if ( ! _end_statement ( ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected end of statement ( \" break \" ). " ) ;
2014-02-10 01:10:30 +00:00
return ;
}
} break ;
2017-11-16 17:38:18 +00:00
case GDScriptTokenizer : : TK_CF_RETURN : {
2014-02-10 01:10:30 +00:00
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2014-02-10 01:10:30 +00:00
ControlFlowNode * cf_return = alloc_node < ControlFlowNode > ( ) ;
2017-03-05 15:44:50 +00:00
cf_return - > cf_type = ControlFlowNode : : CF_RETURN ;
2018-06-06 01:57:44 +00:00
cf_return - > line = tokenizer - > get_token_line ( - 1 ) ;
2014-02-10 01:10:30 +00:00
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_SEMICOLON | | tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_NEWLINE | | tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_EOF ) {
2014-02-10 01:10:30 +00:00
//expect end of statement
p_block - > statements . push_back ( cf_return ) ;
if ( ! _end_statement ( ) ) {
return ;
}
} else {
//expect expression
2017-03-05 15:44:50 +00:00
Node * retexpr = _parse_and_reduce_expression ( p_block , p_static ) ;
2015-08-30 14:50:10 +00:00
if ( ! retexpr ) {
if ( _recover_from_completion ( ) ) {
break ;
}
2014-02-10 01:10:30 +00:00
return ;
2015-08-30 14:50:10 +00:00
}
2014-02-10 01:10:30 +00: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 02:16:54 +00:00
p_block - > has_return = true ;
2014-02-10 01:10:30 +00:00
2016-09-30 19:40:31 +00:00
} break ;
2017-11-16 17:38:18 +00:00
case GDScriptTokenizer : : TK_CF_MATCH : {
2017-03-05 15:44:50 +00:00
2016-09-30 19:40:31 +00:00
tokenizer - > advance ( ) ;
2017-03-05 15:44:50 +00:00
2016-10-05 16:48:38 +00:00
MatchNode * match_node = alloc_node < MatchNode > ( ) ;
2017-03-05 15:44:50 +00:00
2016-09-30 19:40:31 +00:00
Node * val_to_match = _parse_and_reduce_expression ( p_block , p_static ) ;
2017-03-05 15:44:50 +00:00
2016-09-30 19:40:31 +00:00
if ( ! val_to_match ) {
if ( _recover_from_completion ( ) ) {
break ;
}
return ;
}
2017-03-05 15:44:50 +00:00
2016-10-05 16:48:38 +00:00
match_node - > val_to_match = val_to_match ;
2017-03-05 15:44:50 +00:00
2016-09-30 19:40:31 +00:00
if ( ! _enter_indent_block ( ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected indented pattern matching block after \" match \" . " ) ;
2016-09-30 19:40:31 +00:00
return ;
}
2017-03-05 15:44:50 +00:00
2016-10-05 16:48:38 +00:00
BlockNode * compiled_branches = alloc_node < BlockNode > ( ) ;
compiled_branches - > parent_block = p_block ;
compiled_branches - > parent_class = p_block - > parent_class ;
2017-03-05 15:44:50 +00:00
2016-10-05 16:48:38 +00:00
p_block - > sub_blocks . push_back ( compiled_branches ) ;
2017-03-05 15:44:50 +00:00
2016-10-05 16:48:38 +00:00
_parse_pattern_block ( compiled_branches , match_node - > branches , p_static ) ;
2017-03-05 15:44:50 +00:00
2018-05-30 02:16:54 +00:00
if ( error_set ) return ;
2017-03-05 15:44:50 +00:00
2016-10-05 16:48:38 +00: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 02:16:54 +00:00
match_cf_node - > body = compiled_branches ;
2017-03-05 15:44:50 +00:00
2018-05-30 02:16:54 +00:00
p_block - > has_return = p_block - > has_return | | compiled_branches - > has_return ;
2016-10-05 16:48:38 +00:00
p_block - > statements . push_back ( match_cf_node ) ;
2017-03-05 15:44:50 +00:00
2016-10-05 16:48:38 +00:00
_end_statement ( ) ;
2014-02-10 01:10:30 +00:00
} break ;
2017-11-16 17:38:18 +00:00
case GDScriptTokenizer : : TK_PR_ASSERT : {
2014-02-10 01:10:30 +00:00
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2019-08-06 11:28:22 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PARENTHESIS_OPEN ) {
_set_error ( " Expected '(' after assert " ) ;
2014-02-10 01:10:30 +00:00
return ;
2015-08-30 14:50:10 +00:00
}
2019-08-06 11:28:22 +00:00
tokenizer - > advance ( ) ;
Vector < Node * > args ;
const bool result = _parse_arguments ( p_block , args , p_static ) ;
if ( ! result ) {
return ;
}
if ( args . empty ( ) | | args . size ( ) > 2 ) {
_set_error ( " Wrong number of arguments, expected 1 or 2 " ) ;
return ;
}
2014-02-10 01:10:30 +00:00
AssertNode * an = alloc_node < AssertNode > ( ) ;
2019-08-06 11:28:22 +00:00
an - > condition = _reduce_expression ( args [ 0 ] , p_static ) ;
if ( args . size ( ) = = 2 ) {
an - > message = _reduce_expression ( args [ 1 ] , p_static ) ;
} else {
ConstantNode * message_node = alloc_node < ConstantNode > ( ) ;
message_node - > value = String ( ) ;
an - > message = message_node ;
}
2014-02-10 01:10:30 +00:00
p_block - > statements . push_back ( an ) ;
if ( ! _end_statement ( ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected end of statement after \" assert \" . " ) ;
2014-02-10 01:10:30 +00:00
return ;
}
} break ;
2017-11-16 17:38:18 +00:00
case GDScriptTokenizer : : TK_PR_BREAKPOINT : {
2015-12-29 15:11:21 +00:00
tokenizer - > advance ( ) ;
BreakpointNode * bn = alloc_node < BreakpointNode > ( ) ;
p_block - > statements . push_back ( bn ) ;
if ( ! _end_statement ( ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected end of statement after \" breakpoint \" . " ) ;
2015-12-29 15:11:21 +00:00
return ;
}
} break ;
2014-02-10 01:10:30 +00:00
default : {
2017-03-05 15:44:50 +00:00
Node * expression = _parse_and_reduce_expression ( p_block , p_static , false , true ) ;
2015-08-30 14:50:10 +00:00
if ( ! expression ) {
if ( _recover_from_completion ( ) ) {
break ;
}
2014-02-10 01:10:30 +00:00
return ;
2015-08-30 14:50:10 +00:00
}
2014-02-10 01:10:30 +00:00
p_block - > statements . push_back ( expression ) ;
if ( ! _end_statement ( ) ) {
2019-12-02 20:21:49 +00:00
// Attempt to guess a better error message if the user "retypes" a variable
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COLON & & tokenizer - > get_token ( 1 ) = = GDScriptTokenizer : : TK_OP_ASSIGN ) {
_set_error ( " Unexpected ':=', use '=' instead. Expected end of statement after expression. " ) ;
} else {
_set_error ( String ( ) + " Expected end of statement after expression, got " + tokenizer - > get_token_name ( tokenizer - > get_token ( ) ) + " instead " ) ;
}
2014-02-10 01:10:30 +00:00
return ;
}
} break ;
}
}
}
2017-11-16 17:38:18 +00:00
bool GDScriptParser : : _parse_newline ( ) {
2014-02-10 01:10:30 +00:00
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( 1 ) ! = GDScriptTokenizer : : TK_EOF & & tokenizer - > get_token ( 1 ) ! = GDScriptTokenizer : : TK_NEWLINE ) {
2014-02-10 01:10:30 +00:00
2019-10-13 19:48:18 +00:00
IndentLevel current_level = indent_level . back ( ) - > get ( ) ;
2014-02-25 12:31:47 +00:00
int indent = tokenizer - > get_token_line_indent ( ) ;
2019-10-13 19:48:18 +00:00
int tabs = tokenizer - > get_token_line_tab_indent ( ) ;
IndentLevel new_level ( indent , tabs ) ;
if ( new_level . is_mixed ( current_level ) ) {
_set_error ( " Mixed tabs and spaces in indentation. " ) ;
return false ;
}
2014-02-10 01:10:30 +00:00
2019-10-13 19:48:18 +00:00
if ( indent > current_level . indent ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Unexpected indentation. " ) ;
2014-02-10 01:10:30 +00:00
return false ;
}
2019-10-13 19:48:18 +00:00
if ( indent < current_level . indent ) {
2014-02-10 01:10:30 +00:00
2019-10-13 19:48:18 +00:00
while ( indent < current_level . indent ) {
2014-02-10 01:10:30 +00:00
//exit block
2019-10-13 19:48:18 +00:00
if ( indent_level . size ( ) = = 1 ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Invalid indentation. Bug? " ) ;
2014-02-10 01:10:30 +00:00
return false ;
}
2019-10-13 19:48:18 +00:00
indent_level . pop_back ( ) ;
2014-02-10 01:10:30 +00:00
2019-10-13 19:48:18 +00:00
if ( indent_level . back ( ) - > get ( ) . indent < indent ) {
2014-02-10 01:10:30 +00:00
_set_error ( " Unindent does not match any outer indentation level. " ) ;
return false ;
}
2019-10-13 19:48:18 +00:00
if ( indent_level . back ( ) - > get ( ) . is_mixed ( current_level ) ) {
_set_error ( " Mixed tabs and spaces in indentation. " ) ;
return false ;
}
current_level = indent_level . back ( ) - > get ( ) ;
2014-02-10 01:10:30 +00:00
}
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2014-02-10 01:10:30 +00:00
return false ;
}
}
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2014-02-10 01:10:30 +00:00
return true ;
}
2017-11-16 17:38:18 +00:00
void GDScriptParser : : _parse_extends ( ClassNode * p_class ) {
2014-02-10 01:10:30 +00:00
if ( p_class - > extends_used ) {
2019-08-23 18:51:42 +00:00
_set_error ( " \" extends \" can only be present once per script. " ) ;
2014-02-10 01:10:30 +00:00
return ;
}
2019-08-28 09:05:21 +00:00
if ( ! p_class - > constant_expressions . empty ( ) | | ! p_class - > subclasses . empty ( ) | | ! p_class - > functions . empty ( ) | | ! p_class - > variables . empty ( ) ) {
2014-02-10 01:10:30 +00:00
2019-08-23 18:51:42 +00:00
_set_error ( " \" extends \" must be used before anything else. " ) ;
2014-02-10 01:10:30 +00:00
return ;
}
2017-03-05 15:44:50 +00:00
p_class - > extends_used = true ;
2014-02-10 01:10:30 +00:00
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2014-02-10 01:10:30 +00:00
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_BUILT_IN_TYPE & & tokenizer - > get_token_type ( ) = = Variant : : OBJECT ) {
2015-11-12 14:50:20 +00: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 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CONSTANT ) {
2014-02-10 01:10:30 +00:00
2014-02-25 12:31:47 +00:00
Variant constant = tokenizer - > get_token_constant ( ) ;
2017-03-05 15:44:50 +00:00
if ( constant . get_type ( ) ! = Variant : : STRING ) {
2014-02-10 01:10:30 +00:00
2019-08-23 18:51:42 +00:00
_set_error ( " \" extends \" constant must be a string. " ) ;
2014-02-10 01:10:30 +00:00
return ;
}
2017-03-05 15:44:50 +00:00
p_class - > extends_file = constant ;
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2014-02-10 01:10:30 +00:00
2019-03-03 19:36:42 +00:00
// Add parent script as a dependency
String parent = constant ;
if ( parent . is_rel_path ( ) ) {
parent = base_path . plus_file ( parent ) . simplify_path ( ) ;
}
dependencies . push_back ( parent ) ;
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PERIOD ) {
2014-02-10 01:10:30 +00:00
return ;
} else
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
while ( true ) {
2014-02-10 01:10:30 +00:00
2017-10-30 11:59:11 +00:00
switch ( tokenizer - > get_token ( ) ) {
case GDScriptTokenizer : : TK_IDENTIFIER : {
2017-12-06 18:54:22 +00:00
StringName identifier = tokenizer - > get_token_identifier ( ) ;
p_class - > extends_class . push_back ( identifier ) ;
} break ;
2017-10-30 11:59:11 +00:00
case GDScriptTokenizer : : TK_PERIOD :
break ;
default : {
2014-02-10 01:10:30 +00:00
2019-08-23 18:51:42 +00:00
_set_error ( " Invalid \" extends \" syntax, expected string constant (path) and/or identifier (parent class). " ) ;
2017-10-30 11:59:11 +00:00
return ;
}
}
2014-02-10 01:10:30 +00:00
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( 1 ) ;
2017-10-30 11:59:11 +00:00
switch ( tokenizer - > get_token ( ) ) {
case GDScriptTokenizer : : TK_IDENTIFIER :
case GDScriptTokenizer : : TK_PERIOD :
continue ;
default :
return ;
}
2014-02-10 01:10:30 +00:00
}
}
2017-11-16 17:38:18 +00:00
void GDScriptParser : : _parse_class ( ClassNode * p_class ) {
2014-02-10 01:10:30 +00:00
2019-10-13 19:48:18 +00:00
IndentLevel current_level = indent_level . back ( ) - > get ( ) ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
while ( true ) {
2014-02-10 01:10:30 +00:00
2017-11-16 17:38:18 +00:00
GDScriptTokenizer : : Token token = tokenizer - > get_token ( ) ;
2014-02-10 01:10:30 +00:00
if ( error_set )
return ;
2019-10-13 19:48:18 +00:00
if ( current_level . indent > indent_level . back ( ) - > get ( ) . indent ) {
2017-03-05 15:44:50 +00:00
p_class - > end_line = tokenizer - > get_token_line ( ) ;
2014-02-10 01:10:30 +00:00
return ; //go back a level
}
2017-03-05 15:44:50 +00:00
switch ( token ) {
2014-02-10 01:10:30 +00:00
2018-07-25 16:39:05 +00:00
case GDScriptTokenizer : : TK_CURSOR : {
tokenizer - > advance ( ) ;
} break ;
2020-02-22 19:47:50 +00:00
case GDScriptTokenizer : : TK_EOF : {
2017-03-05 15:44:50 +00:00
p_class - > end_line = tokenizer - > get_token_line ( ) ;
2020-02-22 19:47:50 +00:00
return ; // End of file!
} break ;
2017-11-16 17:38:18 +00:00
case GDScriptTokenizer : : TK_ERROR : {
2020-02-22 19:47:50 +00:00
return ; // Go back.
2014-02-10 01:10:30 +00:00
} break ;
2017-11-16 17:38:18 +00:00
case GDScriptTokenizer : : TK_NEWLINE : {
2014-02-10 01:10:30 +00:00
if ( ! _parse_newline ( ) ) {
if ( ! error_set ) {
2017-03-05 15:44:50 +00:00
p_class - > end_line = tokenizer - > get_token_line ( ) ;
2014-02-10 01:10:30 +00:00
}
return ;
}
} break ;
2017-11-16 17:38:18 +00:00
case GDScriptTokenizer : : TK_PR_EXTENDS : {
2014-02-10 01:10:30 +00:00
2018-06-05 16:50:21 +00:00
_mark_line_as_safe ( tokenizer - > get_token_line ( ) ) ;
2014-02-10 01:10:30 +00:00
_parse_extends ( p_class ) ;
if ( error_set )
return ;
2015-06-24 16:29:23 +00:00
if ( ! _end_statement ( ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected end of statement after \" extends \" . " ) ;
2015-06-24 16:29:23 +00:00
return ;
}
2014-02-10 01:10:30 +00:00
2018-07-15 22:29:00 +00:00
} break ;
case GDScriptTokenizer : : TK_PR_CLASS_NAME : {
2019-08-02 18:07:54 +00:00
_mark_line_as_safe ( tokenizer - > get_token_line ( ) ) ;
2018-07-15 22:29:00 +00:00
if ( p_class - > owner ) {
2019-08-23 18:51:42 +00:00
_set_error ( " \" class_name \" is only valid for the main class namespace. " ) ;
2018-07-15 22:29:00 +00:00
return ;
}
2019-04-17 15:06:21 +00:00
if ( self_path . begins_with ( " res:// " ) & & self_path . find ( " :: " ) ! = - 1 ) {
2019-08-23 18:51:42 +00:00
_set_error ( " \" class_name \" isn't allowed in built-in scripts. " ) ;
2019-04-10 21:05:34 +00:00
return ;
}
2018-07-15 22:29:00 +00:00
if ( tokenizer - > get_token ( 1 ) ! = GDScriptTokenizer : : TK_IDENTIFIER ) {
2019-08-23 18:51:42 +00:00
_set_error ( " \" class_name \" syntax: \" class_name <UniqueName> \" " ) ;
2018-07-15 22:29:00 +00:00
return ;
}
2019-04-13 15:26:58 +00:00
if ( p_class - > classname_used ) {
2019-08-23 18:51:42 +00:00
_set_error ( " \" class_name \" can only be present once per script. " ) ;
2019-04-13 15:26:58 +00:00
return ;
}
p_class - > classname_used = true ;
2018-07-15 22:29:00 +00:00
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 ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Unique global class \" " + p_class - > name + " \" already exists at path: " + ScriptServer : : get_global_class_path ( p_class - > name ) ) ;
2018-07-15 22:29:00 +00:00
return ;
}
2018-05-30 02:16:54 +00:00
if ( ClassDB : : class_exists ( p_class - > name ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " The class \" " + p_class - > name + " \" shadows a native class. " ) ;
2018-05-30 02:16:54 +00:00
return ;
}
2019-09-03 18:23:27 +00:00
if ( p_class - > classname_used & & ProjectSettings : : get_singleton ( ) - > has_setting ( " autoload/ " + p_class - > name ) ) {
const String autoload_path = ProjectSettings : : get_singleton ( ) - > get_setting ( " autoload/ " + p_class - > name ) ;
if ( autoload_path . begins_with ( " * " ) ) {
// It's a singleton, and not just a regular AutoLoad script.
_set_error ( " The class \" " + p_class - > name + " \" conflicts with the AutoLoad singleton of the same name, and is therefore redundant. Remove the class_name declaration to fix this error. " ) ;
}
return ;
}
2018-07-15 22:29:00 +00:00
tokenizer - > advance ( 2 ) ;
2018-07-29 03:36:43 +00: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 ) ) {
2019-02-06 03:46:21 +00:00
# ifdef TOOLS_ENABLED
if ( Engine : : get_singleton ( ) - > is_editor_hint ( ) ) {
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 ;
}
2018-07-29 03:36:43 +00:00
2019-02-06 03:46:21 +00:00
p_class - > icon_path = icon_path ;
2018-07-29 03:36:43 +00:00
}
2019-02-06 03:46:21 +00:00
# endif
2018-07-29 03:36:43 +00:00
tokenizer - > advance ( ) ;
} else {
2019-08-23 18:51:42 +00:00
_set_error ( " The optional parameter after \" class_name \" must be a string constant file path to an icon. " ) ;
2018-07-29 03:36:43 +00:00
return ;
}
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CONSTANT ) {
2019-08-23 18:51:42 +00:00
_set_error ( " The class icon must be separated by a comma. " ) ;
2018-07-29 03:36:43 +00:00
return ;
}
2014-02-10 01:10:30 +00:00
} break ;
2017-11-16 17:38:18 +00:00
case GDScriptTokenizer : : TK_PR_TOOL : {
2014-02-10 01:10:30 +00:00
if ( p_class - > tool ) {
2019-08-23 18:51:42 +00:00
_set_error ( " The \" tool \" keyword can only be present once per script. " ) ;
2014-02-10 01:10:30 +00:00
return ;
}
2017-03-05 15:44:50 +00:00
p_class - > tool = true ;
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2014-02-10 01:10:30 +00:00
} break ;
2017-11-16 17:38:18 +00:00
case GDScriptTokenizer : : TK_PR_CLASS : {
2014-02-10 01:10:30 +00:00
//class inside class :D
StringName name ;
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( 1 ) ! = GDScriptTokenizer : : TK_IDENTIFIER ) {
2014-02-10 01:10:30 +00:00
2019-08-23 18:51:42 +00:00
_set_error ( " \" class \" syntax: \" class <Name>: \" or \" class <Name> extends <BaseClass>: \" " ) ;
2014-02-10 01:10:30 +00:00
return ;
}
2014-02-25 12:31:47 +00:00
name = tokenizer - > get_token_identifier ( 1 ) ;
tokenizer - > advance ( 2 ) ;
2014-02-10 01:10:30 +00:00
2018-05-30 02:16:52 +00:00
// Check if name is shadowing something else
2018-05-30 02:16:54 +00:00
if ( ClassDB : : class_exists ( name ) | | ClassDB : : class_exists ( " _ " + name . operator String ( ) ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " The class \" " + String ( name ) + " \" shadows a native class. " ) ;
2018-05-30 02:16:52 +00:00
return ;
}
2018-07-15 22:29:00 +00:00
if ( ScriptServer : : is_global_class ( name ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Can't override name of the unique global class \" " + name + " \" . It already exists at: " + ScriptServer : : get_global_class_path ( p_class - > name ) ) ;
2018-07-15 22:29:00 +00:00
return ;
}
2018-05-30 02:16:54 +00: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 ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Another class named \" " + String ( name ) + " \" already exists in this scope (at line " + itos ( outer_class - > subclasses [ i ] - > line ) + " ). " ) ;
2018-05-30 02:16:54 +00:00
return ;
}
}
if ( outer_class - > constant_expressions . has ( name ) ) {
2019-08-23 18:51:42 +00:00
_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 02:16:52 +00:00
return ;
}
2018-05-30 02:16:54 +00:00
outer_class = outer_class - > owner ;
2018-05-30 02:16:52 +00:00
}
2018-07-15 22:29:00 +00:00
2014-02-10 01:10:30 +00:00
ClassNode * newclass = alloc_node < ClassNode > ( ) ;
newclass - > initializer = alloc_node < BlockNode > ( ) ;
2017-03-05 15:44:50 +00:00
newclass - > initializer - > parent_class = newclass ;
2015-12-30 00:23:26 +00:00
newclass - > ready = alloc_node < BlockNode > ( ) ;
2017-03-05 15:44:50 +00:00
newclass - > ready - > parent_class = newclass ;
newclass - > name = name ;
newclass - > owner = p_class ;
2014-02-10 01:10:30 +00:00
p_class - > subclasses . push_back ( newclass ) ;
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PR_EXTENDS ) {
2014-02-10 01:10:30 +00:00
_parse_extends ( newclass ) ;
if ( error_set )
return ;
}
if ( ! _enter_indent_block ( ) ) {
_set_error ( " Indented block expected. " ) ;
return ;
}
2017-03-05 15:44:50 +00:00
current_class = newclass ;
2014-02-10 01:10:30 +00:00
_parse_class ( newclass ) ;
2017-03-05 15:44:50 +00:00
current_class = p_class ;
2014-02-10 01:10:30 +00:00
} break ;
/* this is for functions....
2017-11-16 17:38:18 +00:00
case GDScriptTokenizer : : TK_CF_PASS : {
2014-02-10 01:10:30 +00:00
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( 1 ) ;
2014-02-10 01:10:30 +00:00
} break ;
*/
2017-11-16 17:38:18 +00:00
case GDScriptTokenizer : : TK_PR_STATIC : {
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PR_FUNCTION ) {
2014-02-10 01:10:30 +00:00
2019-08-23 18:51:42 +00:00
_set_error ( " Expected \" func \" . " ) ;
2014-02-10 01:10:30 +00:00
return ;
}
2020-02-22 19:47:50 +00:00
[[fallthrough]] ;
2019-04-05 12:06:16 +00:00
}
2017-11-16 17:38:18 +00:00
case GDScriptTokenizer : : TK_PR_FUNCTION : {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
bool _static = false ;
pending_newline = - 1 ;
2014-02-10 01:10:30 +00:00
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( - 1 ) = = GDScriptTokenizer : : TK_PR_STATIC ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
_static = true ;
2014-02-10 01:10:30 +00:00
}
2015-01-03 16:03:13 +00:00
tokenizer - > advance ( ) ;
StringName name ;
2017-03-05 15:44:50 +00:00
if ( _get_completable_identifier ( COMPLETION_VIRTUAL_FUNC , name ) ) {
2015-01-03 16:03:13 +00:00
}
2017-03-05 15:44:50 +00:00
if ( name = = StringName ( ) ) {
2014-02-10 01:10:30 +00:00
2019-08-23 18:51:42 +00:00
_set_error ( " Expected an identifier after \" func \" (syntax: \" func <identifier>([arguments]): \" ). " ) ;
2014-02-10 01:10:30 +00:00
return ;
}
2017-03-05 15:44:50 +00:00
for ( int i = 0 ; i < p_class - > functions . size ( ) ; i + + ) {
if ( p_class - > functions [ i ] - > name = = name ) {
2019-08-23 18:51:42 +00:00
_set_error ( " The function \" " + String ( name ) + " \" already exists in this class (at line " + itos ( p_class - > functions [ i ] - > line ) + " ). " ) ;
2014-02-10 01:10:30 +00:00
}
}
2017-03-05 15:44:50 +00:00
for ( int i = 0 ; i < p_class - > static_functions . size ( ) ; i + + ) {
if ( p_class - > static_functions [ i ] - > name = = name ) {
2019-08-23 18:51:42 +00:00
_set_error ( " The function \" " + String ( name ) + " \" already exists in this class (at line " + itos ( p_class - > static_functions [ i ] - > line ) + " ). " ) ;
2014-02-10 01:10:30 +00:00
}
}
2015-01-03 16:03:13 +00:00
2018-07-01 16:17:40 +00: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 ) ;
}
}
2019-04-09 17:28:34 +00:00
for ( int i = 0 ; i < p_class - > subclasses . size ( ) ; i + + ) {
if ( p_class - > subclasses [ i ] - > name = = name ) {
_add_warning ( GDScriptWarning : : FUNCTION_CONFLICTS_CONSTANT , - 1 , name ) ;
}
}
2018-07-01 16:17:40 +00:00
# endif // DEBUG_ENABLED
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PARENTHESIS_OPEN ) {
2014-02-10 01:10:30 +00:00
2019-08-23 18:51:42 +00:00
_set_error ( " Expected \" ( \" after the identifier (syntax: \" func <identifier>([arguments]): \" ). " ) ;
2014-02-10 01:10:30 +00:00
return ;
}
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2014-02-10 01:10:30 +00:00
Vector < StringName > arguments ;
2018-05-30 02:16:53 +00:00
Vector < DataType > argument_types ;
2017-03-05 15:44:50 +00:00
Vector < Node * > default_values ;
2018-07-01 16:17:40 +00:00
# ifdef DEBUG_ENABLED
Vector < int > arguments_usage ;
# endif // DEBUG_ENABLED
2014-02-10 01:10:30 +00:00
2014-02-25 12:31:47 +00:00
int fnline = tokenizer - > get_token_line ( ) ;
2014-02-10 01:10:30 +00:00
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE ) {
2014-02-10 01:10:30 +00:00
//has arguments
2017-03-05 15:44:50 +00:00
bool defaulting = false ;
while ( true ) {
2014-02-10 01:10:30 +00:00
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_NEWLINE ) {
2016-10-03 18:18:21 +00:00
tokenizer - > advance ( ) ;
continue ;
}
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PR_VAR ) {
2014-02-10 01:10:30 +00:00
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ; //var before the identifier is allowed
2014-02-10 01:10:30 +00:00
}
2017-03-31 17:28:34 +00:00
if ( ! tokenizer - > is_token_literal ( 0 , true ) ) {
2014-02-10 01:10:30 +00:00
2019-08-23 18:51:42 +00:00
_set_error ( " Expected an identifier for an argument. " ) ;
2014-02-10 01:10:30 +00:00
return ;
}
2017-03-05 15:44:50 +00:00
StringName argname = tokenizer - > get_token_identifier ( ) ;
2014-02-10 01:10:30 +00:00
arguments . push_back ( argname ) ;
2018-07-01 16:17:40 +00:00
# ifdef DEBUG_ENABLED
arguments_usage . push_back ( 0 ) ;
# endif // DEBUG_ENABLED
2014-02-10 01:10:30 +00:00
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2014-02-10 01:10:30 +00:00
2018-05-30 02:16:53 +00:00
DataType argtype ;
2018-05-30 02:16:51 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COLON ) {
2018-07-25 23:48:22 +00:00
if ( tokenizer - > get_token ( 1 ) = = GDScriptTokenizer : : TK_OP_ASSIGN ) {
argtype . infer_type = true ;
tokenizer - > advance ( ) ;
} else if ( ! _parse_type ( argtype ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected a type for an argument. " ) ;
2018-05-30 02:16:51 +00:00
return ;
}
}
2018-05-30 02:16:53 +00:00
argument_types . push_back ( argtype ) ;
2018-05-30 02:16:51 +00:00
2017-11-16 17:38:18 +00:00
if ( defaulting & & tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_OP_ASSIGN ) {
2014-02-10 01:10:30 +00:00
_set_error ( " Default parameter expected. " ) ;
return ;
}
2014-02-25 12:31:47 +00:00
//tokenizer->advance();
2014-02-10 01:10:30 +00:00
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_OP_ASSIGN ) {
2017-03-05 15:44:50 +00:00
defaulting = true ;
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( 1 ) ;
2017-08-21 19:15:36 +00:00
Node * defval = _parse_and_reduce_expression ( p_class , _static ) ;
2014-02-10 01:10:30 +00:00
if ( ! defval | | error_set )
return ;
OperatorNode * on = alloc_node < OperatorNode > ( ) ;
2017-03-05 15:44:50 +00:00
on - > op = OperatorNode : : OP_ASSIGN ;
2018-06-06 01:57:44 +00:00
on - > line = fnline ;
2014-02-10 01:10:30 +00:00
IdentifierNode * in = alloc_node < IdentifierNode > ( ) ;
2017-03-05 15:44:50 +00:00
in - > name = argname ;
2018-06-06 01:57:44 +00:00
in - > line = fnline ;
2014-02-10 01:10:30 +00: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 17:38:18 +00:00
while ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_NEWLINE ) {
2016-10-03 18:18:21 +00:00
tokenizer - > advance ( ) ;
}
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COMMA ) {
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2014-02-10 01:10:30 +00:00
continue ;
2017-11-16 17:38:18 +00:00
} else if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE ) {
2014-02-10 01:10:30 +00:00
2019-08-23 18:51:42 +00:00
_set_error ( " Expected \" , \" or \" ) \" . " ) ;
2014-02-10 01:10:30 +00:00
return ;
}
break ;
}
}
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2014-02-10 01:10:30 +00:00
BlockNode * block = alloc_node < BlockNode > ( ) ;
2017-03-05 15:44:50 +00:00
block - > parent_class = p_class ;
2014-02-10 01:10:30 +00:00
2018-11-16 09:46:05 +00: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 15:44:50 +00:00
if ( name = = " _init " ) {
2014-02-10 01:10:30 +00:00
2018-05-30 02:16:54 +00:00
if ( _static ) {
2019-08-23 18:51:42 +00:00
_set_error ( " The constructor cannot be static. " ) ;
2018-05-30 02:16:54 +00:00
return ;
}
2014-02-10 01:10:30 +00:00
if ( p_class - > extends_used ) {
OperatorNode * cparent = alloc_node < OperatorNode > ( ) ;
2017-03-05 15:44:50 +00:00
cparent - > op = OperatorNode : : OP_PARENT_CALL ;
2014-02-10 01:10:30 +00:00
block - > statements . push_back ( cparent ) ;
IdentifierNode * id = alloc_node < IdentifierNode > ( ) ;
2017-03-05 15:44:50 +00:00
id - > name = " _init " ;
2014-02-10 01:10:30 +00:00
cparent - > arguments . push_back ( id ) ;
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PERIOD ) {
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PARENTHESIS_OPEN ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected \" ( \" for parent constructor arguments. " ) ;
2018-05-30 02:16:54 +00:00
return ;
2014-02-10 01:10:30 +00:00
}
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2014-02-10 01:10:30 +00:00
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE ) {
2014-02-10 01:10:30 +00:00
//has arguments
2017-03-05 15:44:50 +00:00
parenthesis + + ;
while ( true ) {
2014-02-10 01:10:30 +00:00
2018-11-16 09:46:05 +00:00
current_function = function ;
2017-03-05 15:44:50 +00:00
Node * arg = _parse_and_reduce_expression ( p_class , _static ) ;
2018-11-16 09:46:05 +00:00
current_function = NULL ;
2014-02-10 01:10:30 +00:00
cparent - > arguments . push_back ( arg ) ;
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COMMA ) {
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2014-02-10 01:10:30 +00:00
continue ;
2017-11-16 17:38:18 +00:00
} else if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE ) {
2014-02-10 01:10:30 +00:00
2019-08-23 18:51:42 +00:00
_set_error ( " Expected \" , \" or \" ) \" . " ) ;
2014-02-10 01:10:30 +00:00
return ;
}
break ;
}
2017-03-05 15:44:50 +00:00
parenthesis - - ;
2014-02-10 01:10:30 +00:00
}
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2014-02-10 01:10:30 +00:00
}
} else {
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PERIOD ) {
2014-02-10 01:10:30 +00:00
_set_error ( " Parent constructor call found for a class without inheritance. " ) ;
return ;
}
}
}
2018-05-30 02:16:53 +00:00
DataType return_type ;
2018-05-30 02:16:51 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_FORWARD_ARROW ) {
2018-05-30 02:16:53 +00:00
if ( ! _parse_type ( return_type , true ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected a return type for the function. " ) ;
2018-05-30 02:16:51 +00:00
return ;
}
}
2014-02-10 01:10:30 +00:00
if ( ! _enter_indent_block ( block ) ) {
_set_error ( " Indented block expected. " ) ;
return ;
}
2018-05-30 02:16:53 +00:00
function - > return_type = return_type ;
2014-02-10 01:10:30 +00:00
if ( _static )
p_class - > static_functions . push_back ( function ) ;
else
p_class - > functions . push_back ( function ) ;
2017-03-05 15:44:50 +00:00
current_function = function ;
function - > body = block ;
current_block = block ;
_parse_block ( block , _static ) ;
current_block = NULL ;
2014-12-17 01:31:57 +00:00
2014-02-10 01:10:30 +00:00
//arguments
} break ;
2017-11-16 17:38:18 +00:00
case GDScriptTokenizer : : TK_PR_SIGNAL : {
2015-06-24 16:29:23 +00:00
tokenizer - > advance ( ) ;
2017-03-31 17:28:34 +00:00
if ( ! tokenizer - > is_token_literal ( ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected an identifier after \" signal \" . " ) ;
2015-06-24 16:29:23 +00:00
return ;
}
ClassNode : : Signal sig ;
sig . name = tokenizer - > get_token_identifier ( ) ;
2018-07-01 16:17:40 +00:00
sig . emissions = 0 ;
sig . line = tokenizer - > get_token_line ( ) ;
2015-06-24 16:29:23 +00:00
tokenizer - > advance ( ) ;
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PARENTHESIS_OPEN ) {
2015-06-24 16:29:23 +00:00
tokenizer - > advance ( ) ;
2017-03-05 15:44:50 +00:00
while ( true ) {
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_NEWLINE ) {
2016-10-03 18:18:21 +00:00
tokenizer - > advance ( ) ;
continue ;
}
2015-06-24 16:29:23 +00:00
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE ) {
2015-06-24 16:29:23 +00:00
tokenizer - > advance ( ) ;
break ;
}
2017-07-25 18:01:19 +00:00
if ( ! tokenizer - > is_token_literal ( 0 , true ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected an identifier in a \" signal \" argument. " ) ;
2015-06-24 16:29:23 +00:00
return ;
}
sig . arguments . push_back ( tokenizer - > get_token_identifier ( ) ) ;
tokenizer - > advance ( ) ;
2017-11-16 17:38:18 +00:00
while ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_NEWLINE ) {
2016-10-03 18:18:21 +00:00
tokenizer - > advance ( ) ;
}
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COMMA ) {
2015-06-24 16:29:23 +00:00
tokenizer - > advance ( ) ;
2017-11-16 17:38:18 +00:00
} else if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected \" , \" or \" ) \" after a \" signal \" parameter identifier. " ) ;
2015-06-24 16:29:23 +00:00
return ;
}
}
}
p_class - > _signals . push_back ( sig ) ;
if ( ! _end_statement ( ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected end of statement ( \" signal \" ). " ) ;
2015-06-24 16:29:23 +00:00
return ;
}
} break ;
2017-11-16 17:38:18 +00:00
case GDScriptTokenizer : : TK_PR_EXPORT : {
2014-02-10 01:10:30 +00:00
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2014-02-10 01:10:30 +00:00
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PARENTHESIS_OPEN ) {
2014-02-10 01:10:30 +00:00
2020-01-02 16:03:11 +00:00
# define _ADVANCE_AND_CONSUME_NEWLINES \
do { \
tokenizer - > advance ( ) ; \
} while ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_NEWLINE )
_ADVANCE_AND_CONSUME_NEWLINES ;
parenthesis + + ;
2018-02-05 14:41:13 +00: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 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_BUILT_IN_TYPE ) {
2014-02-10 01:10:30 +00:00
2014-02-25 12:31:47 +00:00
Variant : : Type type = tokenizer - > get_token_type ( ) ;
2017-03-05 15:44:50 +00:00
if ( type = = Variant : : NIL ) {
2014-02-10 01:10:30 +00:00
_set_error ( " Can't export null type. " ) ;
return ;
}
2017-12-09 17:33:23 +00:00
if ( type = = Variant : : OBJECT ) {
_set_error ( " Can't export raw object type. " ) ;
return ;
}
2017-03-05 15:44:50 +00:00
current_export . type = type ;
current_export . usage | = PROPERTY_USAGE_SCRIPT_VARIABLE ;
2020-01-02 16:03:11 +00:00
_ADVANCE_AND_CONSUME_NEWLINES ;
2017-03-05 15:44:50 +00:00
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COMMA ) {
2014-02-10 01:10:30 +00:00
// hint expected next!
2020-01-02 16:03:11 +00:00
_ADVANCE_AND_CONSUME_NEWLINES ;
2016-10-26 11:38:41 +00:00
2017-03-05 15:44:50 +00:00
switch ( type ) {
2014-02-10 01:10:30 +00:00
case Variant : : INT : {
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_IDENTIFIER & & tokenizer - > get_token_identifier ( ) = = " FLAGS " ) {
2015-07-20 18:02:46 +00:00
2020-01-02 16:03:11 +00:00
_ADVANCE_AND_CONSUME_NEWLINES ;
2015-12-11 14:15:57 +00:00
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_COMMA ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected \" , \" in the bit flags hint. " ) ;
2015-07-20 18:02:46 +00:00
return ;
}
2015-12-11 14:15:57 +00:00
2017-03-05 15:44:50 +00:00
current_export . hint = PROPERTY_HINT_FLAGS ;
2020-01-02 16:03:11 +00:00
_ADVANCE_AND_CONSUME_NEWLINES ;
2015-12-11 14:15:57 +00:00
bool first = true ;
2017-03-05 15:44:50 +00:00
while ( true ) {
2015-12-11 14:15:57 +00:00
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_CONSTANT | | tokenizer - > get_token_constant ( ) . get_type ( ) ! = Variant : : STRING ) {
2017-03-05 15:44:50 +00:00
current_export = PropertyInfo ( ) ;
2019-08-23 18:51:42 +00:00
_set_error ( " Expected a string constant in the named bit flags hint. " ) ;
2015-12-11 14:15:57 +00:00
return ;
}
String c = tokenizer - > get_token_constant ( ) ;
if ( ! first )
2017-03-05 15:44:50 +00:00
current_export . hint_string + = " , " ;
2015-12-11 14:15:57 +00:00
else
2017-03-05 15:44:50 +00:00
first = false ;
2015-12-11 14:15:57 +00:00
2017-03-05 15:44:50 +00:00
current_export . hint_string + = c . xml_escape ( ) ;
2015-12-11 14:15:57 +00:00
2020-01-02 16:03:11 +00:00
_ADVANCE_AND_CONSUME_NEWLINES ;
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE )
2015-12-11 14:15:57 +00:00
break ;
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_COMMA ) {
2017-03-05 15:44:50 +00:00
current_export = PropertyInfo ( ) ;
2019-08-23 18:51:42 +00:00
_set_error ( " Expected \" ) \" or \" , \" in the named bit flags hint. " ) ;
2015-12-11 14:15:57 +00:00
return ;
}
2020-01-02 16:03:11 +00:00
_ADVANCE_AND_CONSUME_NEWLINES ;
2015-12-11 14:15:57 +00:00
}
2015-07-20 18:02:46 +00:00
break ;
}
2019-06-07 14:30:54 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_IDENTIFIER & & tokenizer - > get_token_identifier ( ) = = " LAYERS_2D_RENDER " ) {
2020-01-02 16:03:11 +00:00
_ADVANCE_AND_CONSUME_NEWLINES ;
2019-06-07 14:30:54 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected \" ) \" in the layers 2D render hint. " ) ;
2019-06-07 14:30:54 +00:00
return ;
}
current_export . hint = PROPERTY_HINT_LAYERS_2D_RENDER ;
break ;
}
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_IDENTIFIER & & tokenizer - > get_token_identifier ( ) = = " LAYERS_2D_PHYSICS " ) {
2020-01-02 16:03:11 +00:00
_ADVANCE_AND_CONSUME_NEWLINES ;
2019-06-07 14:30:54 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected \" ) \" in the layers 2D physics hint. " ) ;
2019-06-07 14:30:54 +00:00
return ;
}
current_export . hint = PROPERTY_HINT_LAYERS_2D_PHYSICS ;
break ;
}
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_IDENTIFIER & & tokenizer - > get_token_identifier ( ) = = " LAYERS_3D_RENDER " ) {
2020-01-02 16:03:11 +00:00
_ADVANCE_AND_CONSUME_NEWLINES ;
2019-06-07 14:30:54 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected \" ) \" in the layers 3D render hint. " ) ;
2019-06-07 14:30:54 +00:00
return ;
}
current_export . hint = PROPERTY_HINT_LAYERS_3D_RENDER ;
break ;
}
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_IDENTIFIER & & tokenizer - > get_token_identifier ( ) = = " LAYERS_3D_PHYSICS " ) {
2020-01-02 16:03:11 +00:00
_ADVANCE_AND_CONSUME_NEWLINES ;
2019-06-07 14:30:54 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected \" ) \" in the layers 3D physics hint. " ) ;
2019-06-07 14:30:54 +00:00
return ;
}
current_export . hint = PROPERTY_HINT_LAYERS_3D_PHYSICS ;
break ;
}
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CONSTANT & & tokenizer - > get_token_constant ( ) . get_type ( ) = = Variant : : STRING ) {
2014-02-10 01:10:30 +00:00
//enumeration
2017-03-05 15:44:50 +00:00
current_export . hint = PROPERTY_HINT_ENUM ;
bool first = true ;
while ( true ) {
2014-02-10 01:10:30 +00:00
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_CONSTANT | | tokenizer - > get_token_constant ( ) . get_type ( ) ! = Variant : : STRING ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
current_export = PropertyInfo ( ) ;
2019-08-23 18:51:42 +00:00
_set_error ( " Expected a string constant in the enumeration hint. " ) ;
2014-09-19 21:39:50 +00:00
return ;
2014-02-10 01:10:30 +00:00
}
2014-02-25 12:31:47 +00:00
String c = tokenizer - > get_token_constant ( ) ;
2014-02-10 01:10:30 +00:00
if ( ! first )
2017-03-05 15:44:50 +00:00
current_export . hint_string + = " , " ;
2014-02-10 01:10:30 +00:00
else
2017-03-05 15:44:50 +00:00
first = false ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
current_export . hint_string + = c . xml_escape ( ) ;
2014-02-10 01:10:30 +00:00
2020-01-02 16:03:11 +00:00
_ADVANCE_AND_CONSUME_NEWLINES ;
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE )
2014-02-10 01:10:30 +00:00
break ;
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_COMMA ) {
2017-03-05 15:44:50 +00:00
current_export = PropertyInfo ( ) ;
2019-08-23 18:51:42 +00:00
_set_error ( " Expected \" ) \" or \" , \" in the enumeration hint. " ) ;
2014-09-19 21:39:50 +00:00
return ;
2014-02-10 01:10:30 +00:00
}
2020-01-02 16:03:11 +00:00
_ADVANCE_AND_CONSUME_NEWLINES ;
2014-02-10 01:10:30 +00:00
}
break ;
}
2020-02-22 19:47:50 +00:00
[[fallthrough]] ;
2019-04-05 12:06:16 +00:00
}
Variant: Added 64-bit packed arrays, renamed Variant::REAL to FLOAT.
- Renames PackedIntArray to PackedInt32Array.
- Renames PackedFloatArray to PackedFloat32Array.
- Adds PackedInt64Array and PackedFloat64Array.
- Renames Variant::REAL to Variant::FLOAT for consistency.
Packed arrays are for storing large amount of data and creating stuff like
meshes, buffers. textures, etc. Forcing them to be 64 is a huge waste of
memory. That said, many users requested the ability to have 64 bits packed
arrays for their games, so this is just an optional added type.
For Variant, the float datatype is always 64 bits, and exposed as `float`.
We still have `real_t` which is the datatype that can change from 32 to 64
bits depending on a compile flag (not entirely working right now, but that's
the idea). It affects math related datatypes and code only.
Neither Variant nor PackedArray make use of real_t, which is only intended
for math precision, so the term is removed from there to keep only float.
2020-02-24 18:20:53 +00:00
case Variant : : FLOAT : {
2014-02-10 01:10:30 +00:00
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_IDENTIFIER & & tokenizer - > get_token_identifier ( ) = = " EASE " ) {
2017-03-05 15:44:50 +00:00
current_export . hint = PROPERTY_HINT_EXP_EASING ;
2020-01-02 16:03:11 +00:00
_ADVANCE_AND_CONSUME_NEWLINES ;
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected \" ) \" in the hint. " ) ;
2015-10-16 18:37:13 +00:00
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 05:45:03 +00:00
// range
2017-11-16 17:38:18 +00: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 05:45:03 +00:00
2017-03-05 15:44:50 +00:00
current_export . hint = PROPERTY_HINT_EXP_RANGE ;
2020-01-02 16:03:11 +00:00
_ADVANCE_AND_CONSUME_NEWLINES ;
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 05:45:03 +00:00
2017-11-16 17:38:18 +00: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 05:45:03 +00:00
break ;
2017-11-16 17:38:18 +00:00
else if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_COMMA ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected \" ) \" or \" , \" in the exponential range hint. " ) ;
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 05:45:03 +00:00
return ;
}
2020-01-02 16:03:11 +00:00
_ADVANCE_AND_CONSUME_NEWLINES ;
2017-03-05 15:44:50 +00: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 05:45:03 +00:00
2017-03-05 15:44:50 +00:00
float sign = 1.0 ;
2014-09-21 04:43:42 +00:00
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_OP_SUB ) {
2017-03-05 15:44:50 +00:00
sign = - 1 ;
2020-01-02 16:03:11 +00:00
_ADVANCE_AND_CONSUME_NEWLINES ;
2014-09-21 04:43:42 +00:00
}
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_CONSTANT | | ! tokenizer - > get_token_constant ( ) . is_num ( ) ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
current_export = PropertyInfo ( ) ;
2019-08-23 18:51:42 +00:00
_set_error ( " Expected a range in the numeric hint. " ) ;
2014-09-19 21:39:50 +00:00
return ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
current_export . hint_string = rtos ( sign * double ( tokenizer - > get_token_constant ( ) ) ) ;
2020-01-02 16:03:11 +00:00
_ADVANCE_AND_CONSUME_NEWLINES ;
2014-02-10 01:10:30 +00:00
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE ) {
2017-03-05 15:44:50 +00:00
current_export . hint_string = " 0, " + current_export . hint_string ;
2014-02-10 01:10:30 +00:00
break ;
}
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_COMMA ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
current_export = PropertyInfo ( ) ;
2019-08-23 18:51:42 +00:00
_set_error ( " Expected \" , \" or \" ) \" in the numeric range hint. " ) ;
2014-09-19 21:39:50 +00:00
return ;
2014-02-10 01:10:30 +00:00
}
2020-01-02 16:03:11 +00:00
_ADVANCE_AND_CONSUME_NEWLINES ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
sign = 1.0 ;
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_OP_SUB ) {
2017-03-05 15:44:50 +00:00
sign = - 1 ;
2020-01-02 16:03:11 +00:00
_ADVANCE_AND_CONSUME_NEWLINES ;
2014-09-21 04:43:42 +00:00
}
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_CONSTANT | | ! tokenizer - > get_token_constant ( ) . is_num ( ) ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
current_export = PropertyInfo ( ) ;
2019-08-23 18:51:42 +00:00
_set_error ( " Expected a number as upper bound in the numeric range hint. " ) ;
2014-09-19 21:39:50 +00:00
return ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
current_export . hint_string + = " , " + rtos ( sign * double ( tokenizer - > get_token_constant ( ) ) ) ;
2020-01-02 16:03:11 +00:00
_ADVANCE_AND_CONSUME_NEWLINES ;
2014-02-10 01:10:30 +00:00
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE )
2014-02-10 01:10:30 +00:00
break ;
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_COMMA ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
current_export = PropertyInfo ( ) ;
2019-08-23 18:51:42 +00:00
_set_error ( " Expected \" , \" or \" ) \" in the numeric range hint. " ) ;
2014-09-19 21:39:50 +00:00
return ;
2014-02-10 01:10:30 +00:00
}
2020-01-02 16:03:11 +00:00
_ADVANCE_AND_CONSUME_NEWLINES ;
2017-03-05 15:44:50 +00:00
sign = 1.0 ;
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_OP_SUB ) {
2017-03-05 15:44:50 +00:00
sign = - 1 ;
2020-01-02 16:03:11 +00:00
_ADVANCE_AND_CONSUME_NEWLINES ;
2014-09-21 04:43:42 +00:00
}
2014-02-10 01:10:30 +00:00
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_CONSTANT | | ! tokenizer - > get_token_constant ( ) . is_num ( ) ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
current_export = PropertyInfo ( ) ;
2019-08-23 18:51:42 +00:00
_set_error ( " Expected a number as step in the numeric range hint. " ) ;
2014-09-19 21:39:50 +00:00
return ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
current_export . hint_string + = " , " + rtos ( sign * double ( tokenizer - > get_token_constant ( ) ) ) ;
2020-01-02 16:03:11 +00:00
_ADVANCE_AND_CONSUME_NEWLINES ;
2014-02-10 01:10:30 +00:00
} break ;
case Variant : : STRING : {
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CONSTANT & & tokenizer - > get_token_constant ( ) . get_type ( ) = = Variant : : STRING ) {
2014-02-10 01:10:30 +00:00
//enumeration
2017-03-05 15:44:50 +00:00
current_export . hint = PROPERTY_HINT_ENUM ;
bool first = true ;
while ( true ) {
2014-02-10 01:10:30 +00:00
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_CONSTANT | | tokenizer - > get_token_constant ( ) . get_type ( ) ! = Variant : : STRING ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
current_export = PropertyInfo ( ) ;
2019-08-23 18:51:42 +00:00
_set_error ( " Expected a string constant in the enumeration hint. " ) ;
2014-09-19 21:39:50 +00:00
return ;
2014-02-10 01:10:30 +00:00
}
2014-02-25 12:31:47 +00:00
String c = tokenizer - > get_token_constant ( ) ;
2014-02-10 01:10:30 +00:00
if ( ! first )
2017-03-05 15:44:50 +00:00
current_export . hint_string + = " , " ;
2014-02-10 01:10:30 +00:00
else
2017-03-05 15:44:50 +00:00
first = false ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
current_export . hint_string + = c . xml_escape ( ) ;
2020-01-02 16:03:11 +00:00
_ADVANCE_AND_CONSUME_NEWLINES ;
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE )
2014-02-10 01:10:30 +00:00
break ;
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_COMMA ) {
2017-03-05 15:44:50 +00:00
current_export = PropertyInfo ( ) ;
2019-08-23 18:51:42 +00:00
_set_error ( " Expected \" ) \" or \" , \" in the enumeration hint. " ) ;
2014-02-10 01:10:30 +00:00
return ;
}
2020-01-02 16:03:11 +00:00
_ADVANCE_AND_CONSUME_NEWLINES ;
2014-02-10 01:10:30 +00:00
}
break ;
}
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_IDENTIFIER & & tokenizer - > get_token_identifier ( ) = = " DIR " ) {
2014-02-10 01:10:30 +00:00
2020-01-02 16:03:11 +00:00
_ADVANCE_AND_CONSUME_NEWLINES ;
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 05:45:03 +00:00
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE )
2017-03-05 15:44:50 +00:00
current_export . hint = PROPERTY_HINT_DIR ;
2017-11-16 17:38:18 +00: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 05:45:03 +00:00
2020-01-02 16:03:11 +00:00
_ADVANCE_AND_CONSUME_NEWLINES ;
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 05:45:03 +00:00
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_IDENTIFIER | | ! ( tokenizer - > get_token_identifier ( ) = = " GLOBAL " ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected \" GLOBAL \" after comma in the directory hint. " ) ;
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 05:45:03 +00:00
return ;
}
if ( ! p_class - > tool ) {
_set_error ( " Global filesystem hints may only be used in tool scripts. " ) ;
return ;
}
2017-03-05 15:44:50 +00:00
current_export . hint = PROPERTY_HINT_GLOBAL_DIR ;
2020-01-02 16:03:11 +00:00
_ADVANCE_AND_CONSUME_NEWLINES ;
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 05:45:03 +00:00
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected \" ) \" in the hint. " ) ;
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 05:45:03 +00:00
return ;
}
2017-03-05 15:44:50 +00:00
} else {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected \" ) \" or \" , \" in the hint. " ) ;
2014-02-10 01:10:30 +00:00
return ;
}
break ;
}
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_IDENTIFIER & & tokenizer - > get_token_identifier ( ) = = " FILE " ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
current_export . hint = PROPERTY_HINT_FILE ;
2020-01-02 16:03:11 +00:00
_ADVANCE_AND_CONSUME_NEWLINES ;
2014-02-10 01:10:30 +00:00
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COMMA ) {
2014-02-10 01:10:30 +00:00
2020-01-02 16:03:11 +00:00
_ADVANCE_AND_CONSUME_NEWLINES ;
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 05:45:03 +00:00
2017-11-16 17:38:18 +00: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 05:45:03 +00:00
if ( ! p_class - > tool ) {
_set_error ( " Global filesystem hints may only be used in tool scripts. " ) ;
return ;
}
2017-03-05 15:44:50 +00:00
current_export . hint = PROPERTY_HINT_GLOBAL_FILE ;
2020-01-02 16:03:11 +00:00
_ADVANCE_AND_CONSUME_NEWLINES ;
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 05:45:03 +00:00
2017-11-16 17:38:18 +00: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 05:45:03 +00:00
break ;
2017-11-16 17:38:18 +00:00
else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COMMA )
2020-01-02 16:03:11 +00:00
_ADVANCE_AND_CONSUME_NEWLINES ;
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 05:45:03 +00:00
else {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected \" ) \" or \" , \" in the hint. " ) ;
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 05:45:03 +00:00
return ;
}
}
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_CONSTANT | | tokenizer - > get_token_constant ( ) . get_type ( ) ! = Variant : : STRING ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
if ( current_export . hint = = PROPERTY_HINT_GLOBAL_FILE )
2019-08-23 18:51:42 +00:00
_set_error ( " Expected string constant with filter. " ) ;
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 05:45:03 +00:00
else
2019-08-23 18:51:42 +00:00
_set_error ( " Expected \" GLOBAL \" or string constant with filter. " ) ;
2014-02-10 01:10:30 +00:00
return ;
}
2017-03-05 15:44:50 +00:00
current_export . hint_string = tokenizer - > get_token_constant ( ) ;
2020-01-02 16:03:11 +00:00
_ADVANCE_AND_CONSUME_NEWLINES ;
2014-02-10 01:10:30 +00:00
}
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected \" ) \" in the hint. " ) ;
2014-02-10 01:10:30 +00:00
return ;
}
break ;
}
2015-10-16 14:39:59 +00:00
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_IDENTIFIER & & tokenizer - > get_token_identifier ( ) = = " MULTILINE " ) {
2015-10-16 14:39:59 +00:00
2017-03-05 15:44:50 +00:00
current_export . hint = PROPERTY_HINT_MULTILINE_TEXT ;
2020-01-02 16:03:11 +00:00
_ADVANCE_AND_CONSUME_NEWLINES ;
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected \" ) \" in the hint. " ) ;
2015-10-16 14:39:59 +00:00
return ;
}
break ;
}
2014-02-10 01:10:30 +00:00
} break ;
case Variant : : COLOR : {
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_IDENTIFIER ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
current_export = PropertyInfo ( ) ;
2019-08-23 18:51:42 +00:00
_set_error ( " Color type hint expects RGB or RGBA as hints. " ) ;
2014-02-10 01:10:30 +00:00
return ;
}
2014-02-25 12:31:47 +00:00
String identifier = tokenizer - > get_token_identifier ( ) ;
2017-03-05 15:44:50 +00:00
if ( identifier = = " RGB " ) {
current_export . hint = PROPERTY_HINT_COLOR_NO_ALPHA ;
} else if ( identifier = = " RGBA " ) {
2014-02-10 01:10:30 +00:00
//none
} else {
2017-03-05 15:44:50 +00:00
current_export = PropertyInfo ( ) ;
2019-08-23 18:51:42 +00:00
_set_error ( " Color type hint expects RGB or RGBA as hints. " ) ;
2014-02-10 01:10:30 +00:00
return ;
}
2020-01-02 16:03:11 +00:00
_ADVANCE_AND_CONSUME_NEWLINES ;
2014-02-10 01:10:30 +00:00
} break ;
default : {
2017-03-05 15:44:50 +00:00
current_export = PropertyInfo ( ) ;
2019-08-23 18:51:42 +00:00
_set_error ( " Type \" " + Variant : : get_type_name ( type ) + " \" can't take hints. " ) ;
2014-02-10 01:10:30 +00:00
return ;
} break ;
}
2016-10-26 11:38:41 +00:00
}
2014-02-10 01:10:30 +00:00
2017-11-15 18:50:37 +00:00
} else {
2014-02-10 01:10:30 +00:00
2017-11-15 18:50:37 +00:00
parenthesis + + ;
Node * subexpr = _parse_and_reduce_expression ( p_class , true , true ) ;
if ( ! subexpr ) {
if ( _recover_from_completion ( ) ) {
break ;
}
return ;
}
parenthesis - - ;
2015-10-16 14:18:46 +00:00
2017-11-15 18:50:37 +00:00
if ( subexpr - > type ! = Node : : TYPE_CONSTANT ) {
2017-03-05 15:44:50 +00:00
current_export = PropertyInfo ( ) ;
2017-11-15 18:50:37 +00:00
_set_error ( " Expected a constant expression. " ) ;
2014-02-10 01:10:30 +00:00
}
2015-10-16 14:18:46 +00:00
2017-11-15 18:50:37 +00:00
Variant constant = static_cast < ConstantNode * > ( subexpr ) - > value ;
2016-06-11 21:31:22 +00:00
2017-11-15 18:50:37 +00:00
if ( constant . get_type ( ) = = Variant : : OBJECT ) {
GDScriptNativeClass * native_class = Object : : cast_to < GDScriptNativeClass > ( constant ) ;
2015-10-16 14:18:46 +00:00
2017-11-15 18:50:37 +00: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 16:01:26 +00:00
current_export . class_name = native_class - > get_name ( ) ;
2017-11-15 18:50:37 +00:00
} else {
current_export = PropertyInfo ( ) ;
2019-08-23 18:51:42 +00:00
_set_error ( " The export hint isn't a resource type. " ) ;
2017-11-15 18:50:37 +00:00
}
} else if ( constant . get_type ( ) = = Variant : : DICTIONARY ) {
// Enumeration
bool is_flags = false ;
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COMMA ) {
2020-01-02 16:03:11 +00:00
_ADVANCE_AND_CONSUME_NEWLINES ;
2017-11-15 18:50:37 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_IDENTIFIER & & tokenizer - > get_token_identifier ( ) = = " FLAGS " ) {
is_flags = true ;
2020-01-02 16:03:11 +00:00
_ADVANCE_AND_CONSUME_NEWLINES ;
2017-11-15 18:50:37 +00:00
} else {
current_export = PropertyInfo ( ) ;
2019-08-23 18:51:42 +00:00
_set_error ( " Expected \" FLAGS \" after comma. " ) ;
2017-11-15 18:50:37 +00:00
}
}
current_export . type = Variant : : INT ;
current_export . hint = is_flags ? PROPERTY_HINT_FLAGS : PROPERTY_HINT_ENUM ;
2018-11-06 08:53:53 +00:00
current_export . usage | = PROPERTY_USAGE_SCRIPT_VARIABLE ;
2017-11-15 18:50:37 +00: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 01:10:30 +00:00
}
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PARENTHESIS_CLOSE ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
current_export = PropertyInfo ( ) ;
2019-08-23 18:51:42 +00:00
_set_error ( " Expected \" ) \" or \" , \" after the export hint. " ) ;
2014-02-10 01:10:30 +00:00
return ;
}
2020-01-02 16:03:11 +00:00
tokenizer - > advance ( ) ;
parenthesis - - ;
2018-02-05 14:41:13 +00: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 ;
}
2020-01-02 16:03:11 +00:00
# undef _ADVANCE_AND_CONSUME_NEWLINES
2014-02-10 01:10:30 +00:00
}
2020-02-12 11:01:30 +00: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_REMOTESYNC & & tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PR_MASTERSYNC & & tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PR_PUPPETSYNC ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
current_export = PropertyInfo ( ) ;
2020-02-12 11:01:30 +00:00
_set_error ( " Expected \" var \" , \" onready \" , \" remote \" , \" master \" , \" puppet \" , \" remotesync \" , \" mastersync \" , \" puppetsync \" . " ) ;
2014-02-10 01:10:30 +00:00
return ;
}
2016-08-19 19:48:08 +00:00
continue ;
} break ;
2017-11-16 17:38:18 +00:00
case GDScriptTokenizer : : TK_PR_ONREADY : {
2015-12-28 22:31:52 +00:00
2016-08-19 19:48:08 +00:00
//may be fallthrough from export, ignore if so
tokenizer - > advance ( ) ;
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PR_VAR ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected \" var \" . " ) ;
2016-08-19 19:48:08 +00:00
return ;
}
continue ;
} break ;
2017-11-16 17:38:18 +00:00
case GDScriptTokenizer : : TK_PR_REMOTE : {
2016-08-19 19:48:08 +00:00
//may be fallthrough from export, ignore if so
tokenizer - > advance ( ) ;
2017-03-05 15:44:50 +00:00
if ( current_export . type ) {
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PR_VAR ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected \" var \" . " ) ;
2015-12-28 22:31:52 +00:00
return ;
}
2016-08-19 19:48:08 +00:00
} else {
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PR_VAR & & tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PR_FUNCTION ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected \" var \" or \" func \" . " ) ;
2016-08-19 19:48:08 +00:00
return ;
}
2015-12-28 22:31:52 +00:00
}
2018-05-13 05:07:56 +00:00
rpc_mode = MultiplayerAPI : : RPC_MODE_REMOTE ;
2016-08-19 19:48:08 +00:00
continue ;
} break ;
2017-11-16 17:38:18 +00:00
case GDScriptTokenizer : : TK_PR_MASTER : {
2016-08-19 19:48:08 +00:00
//may be fallthrough from export, ignore if so
tokenizer - > advance ( ) ;
2017-03-05 15:44:50 +00:00
if ( current_export . type ) {
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PR_VAR ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected \" var \" . " ) ;
2016-08-19 19:48:08 +00:00
return ;
}
} else {
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PR_VAR & & tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PR_FUNCTION ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected \" var \" or \" func \" . " ) ;
2016-08-19 19:48:08 +00:00
return ;
}
}
2018-05-13 05:07:56 +00:00
rpc_mode = MultiplayerAPI : : RPC_MODE_MASTER ;
2016-08-19 19:48:08 +00:00
continue ;
} break ;
2018-09-14 19:59:47 +00:00
case GDScriptTokenizer : : TK_PR_PUPPET : {
2016-08-19 19:48:08 +00:00
//may be fallthrough from export, ignore if so
tokenizer - > advance ( ) ;
2017-03-05 15:44:50 +00:00
if ( current_export . type ) {
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PR_VAR ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected \" var \" . " ) ;
2016-08-19 19:48:08 +00:00
return ;
}
} else {
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PR_VAR & & tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PR_FUNCTION ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected \" var \" or \" func \" . " ) ;
2016-08-19 19:48:08 +00:00
return ;
}
}
2018-09-14 19:59:47 +00:00
rpc_mode = MultiplayerAPI : : RPC_MODE_PUPPET ;
2016-08-19 19:48:08 +00:00
continue ;
} break ;
2020-02-12 11:01:30 +00:00
case GDScriptTokenizer : : TK_PR_REMOTESYNC : {
2016-08-19 19:48:08 +00:00
//may be fallthrough from export, ignore if so
tokenizer - > advance ( ) ;
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PR_VAR & & tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_PR_FUNCTION ) {
2016-08-19 19:48:08 +00:00
if ( current_export . type )
2019-08-23 18:51:42 +00:00
_set_error ( " Expected \" var \" . " ) ;
2016-08-19 19:48:08 +00:00
else
2019-08-23 18:51:42 +00:00
_set_error ( " Expected \" var \" or \" func \" . " ) ;
2016-08-19 19:48:08 +00:00
return ;
}
2018-09-14 21:10:12 +00:00
rpc_mode = MultiplayerAPI : : RPC_MODE_REMOTESYNC ;
2016-08-19 19:48:08 +00:00
continue ;
} break ;
2018-05-26 08:30:36 +00: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 )
2019-08-23 18:51:42 +00:00
_set_error ( " Expected \" var \" . " ) ;
2018-05-26 08:30:36 +00:00
else
2019-08-23 18:51:42 +00:00
_set_error ( " Expected \" var \" or \" func \" . " ) ;
2018-05-26 08:30:36 +00:00
return ;
}
rpc_mode = MultiplayerAPI : : RPC_MODE_MASTERSYNC ;
continue ;
} break ;
2018-09-14 19:59:47 +00:00
case GDScriptTokenizer : : TK_PR_PUPPETSYNC : {
2018-05-26 08:30:36 +00: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 )
2019-08-23 18:51:42 +00:00
_set_error ( " Expected \" var \" . " ) ;
2018-05-26 08:30:36 +00:00
else
2019-08-23 18:51:42 +00:00
_set_error ( " Expected \" var \" or \" func \" . " ) ;
2018-05-26 08:30:36 +00:00
return ;
}
2018-09-14 19:59:47 +00:00
rpc_mode = MultiplayerAPI : : RPC_MODE_PUPPETSYNC ;
2018-05-26 08:30:36 +00:00
continue ;
} break ;
2017-11-16 17:38:18 +00:00
case GDScriptTokenizer : : TK_PR_VAR : {
2019-04-05 12:06:16 +00:00
// variable declaration and (eventual) initialization
2014-02-10 01:10:30 +00:00
ClassNode : : Member member ;
2019-01-30 01:12:41 +00:00
2017-11-16 17:38:18 +00:00
bool autoexport = tokenizer - > get_token ( - 1 ) = = GDScriptTokenizer : : TK_PR_EXPORT ;
2017-03-05 15:44:50 +00:00
if ( current_export . type ! = Variant : : NIL ) {
member . _export = current_export ;
current_export = PropertyInfo ( ) ;
2014-02-10 01:10:30 +00:00
}
2017-11-16 17:38:18 +00:00
bool onready = tokenizer - > get_token ( - 1 ) = = GDScriptTokenizer : : TK_PR_ONREADY ;
2015-12-28 22:31:52 +00:00
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2017-03-31 17:28:34 +00:00
if ( ! tokenizer - > is_token_literal ( 0 , true ) ) {
2014-02-10 01:10:30 +00:00
2019-08-23 18:51:42 +00:00
_set_error ( " Expected an identifier for the member variable name. " ) ;
2014-02-10 01:10:30 +00:00
return ;
}
2017-03-31 17:28:34 +00:00
member . identifier = tokenizer - > get_token_literal ( ) ;
2017-03-05 15:44:50 +00:00
member . expression = NULL ;
member . _export . name = member . identifier ;
member . line = tokenizer - > get_token_line ( ) ;
2018-07-01 16:17:40 +00:00
member . usages = 0 ;
2017-03-05 15:44:50 +00:00
member . rpc_mode = rpc_mode ;
2016-08-19 19:48:08 +00:00
2018-05-30 02:16:54 +00:00
if ( current_class - > constant_expressions . has ( member . identifier ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " A constant named \" " + String ( member . identifier ) + " \" already exists in this class (at line: " +
2018-05-30 02:16:54 +00: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 ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Variable \" " + String ( member . identifier ) + " \" already exists in this class (at line: " +
2018-05-30 02:16:54 +00:00
itos ( current_class - > variables [ i ] . line ) + " ). " ) ;
return ;
}
}
2019-04-09 17:28:34 +00:00
for ( int i = 0 ; i < current_class - > subclasses . size ( ) ; i + + ) {
if ( current_class - > subclasses [ i ] - > name = = member . identifier ) {
2019-08-23 18:51:42 +00:00
_set_error ( " A class named \" " + String ( member . identifier ) + " \" already exists in this class (at line " + itos ( current_class - > subclasses [ i ] - > line ) + " ). " ) ;
2019-04-09 17:28:34 +00:00
return ;
}
}
2018-07-01 16:17:40 +00: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 12:31:47 +00:00
tokenizer - > advance ( ) ;
2014-02-10 01:10:30 +00:00
2018-05-13 05:07:56 +00:00
rpc_mode = MultiplayerAPI : : RPC_MODE_DISABLED ;
2016-08-19 19:48:08 +00:00
2018-05-30 02:16:51 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COLON ) {
2018-06-19 05:55:52 +00: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 ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected a type for the class variable. " ) ;
2018-05-30 02:16:51 +00:00
return ;
}
}
2020-01-16 12:50:29 +00: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 ;
member . _export . hint_string = member . data_type . native_type ;
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 ;
}
}
# ifdef TOOLS_ENABLED
2020-02-19 19:27:19 +00:00
Callable : : CallError ce ;
2020-01-16 12:50:29 +00:00
member . default_value = Variant : : construct ( member . _export . type , NULL , 0 , ce ) ;
# endif
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_OP_ASSIGN ) {
2014-02-10 01:10:30 +00:00
2014-10-28 01:54:32 +00:00
# ifdef DEBUG_ENABLED
int line = tokenizer - > get_token_line ( ) ;
# endif
tokenizer - > advance ( ) ;
2014-02-10 01:10:30 +00:00
2018-06-18 19:24:31 +00:00
Node * subexpr = _parse_and_reduce_expression ( p_class , false , autoexport | | member . _export . type ! = Variant : : NIL ) ;
2015-08-30 14:50:10 +00:00
if ( ! subexpr ) {
if ( _recover_from_completion ( ) ) {
break ;
}
2014-02-10 01:10:30 +00:00
return ;
2015-08-30 14:50:10 +00:00
}
2014-02-10 01:10:30 +00:00
2015-12-28 22:31:52 +00:00
//discourage common error
2017-03-05 15:44:50 +00:00
if ( ! onready & & subexpr - > type = = Node : : TYPE_OPERATOR ) {
2015-12-28 22:31:52 +00:00
2017-03-05 15:44:50 +00: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 22:31:52 +00:00
2019-08-23 18:51:42 +00:00
_set_error ( " Use \" onready var " + String ( member . identifier ) + " = get_node(...) \" instead. " ) ;
2015-12-28 22:31:52 +00:00
return ;
}
}
}
2017-03-05 15:44:50 +00:00
member . expression = subexpr ;
2014-12-17 01:31:57 +00:00
2018-05-30 02:16:54 +00:00
if ( autoexport & & ! member . data_type . has_type ) {
2014-02-10 01:10:30 +00:00
2018-05-30 02:16:54 +00:00
if ( subexpr - > type ! = Node : : TYPE_CONSTANT ) {
2014-02-10 01:10:30 +00:00
2018-05-30 02:16:54 +00:00
_set_error ( " Type-less export needs a constant expression assigned to infer type. " ) ;
return ;
}
2014-02-10 01:10:30 +00:00
2018-05-30 02:16:54 +00:00
ConstantNode * cn = static_cast < ConstantNode * > ( subexpr ) ;
if ( cn - > value . get_type ( ) = = Variant : : NIL ) {
2014-02-10 01:10:30 +00:00
2018-05-30 02:16:54 +00: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 ) {
2019-08-23 18:51:42 +00:00
_set_error ( " The exported constant isn't a type or resource. " ) ;
2014-10-28 01:54:32 +00:00
return ;
}
2018-05-30 02:16:54 +00:00
member . _export . hint = PROPERTY_HINT_RESOURCE_TYPE ;
member . _export . hint_string = res - > get_class ( ) ;
2014-02-10 01:10:30 +00:00
}
2014-10-28 01:54:32 +00:00
}
# ifdef TOOLS_ENABLED
2019-01-23 20:06:58 +00:00
if ( subexpr - > type = = Node : : TYPE_CONSTANT & & ( member . _export . type ! = Variant : : NIL | | member . data_type . has_type ) ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
ConstantNode * cn = static_cast < ConstantNode * > ( subexpr ) ;
if ( cn - > value . get_type ( ) ! = Variant : : NIL ) {
2019-02-26 12:58:39 +00:00
if ( member . _export . type ! = Variant : : NIL & & cn - > value . get_type ( ) ! = member . _export . type ) {
if ( Variant : : can_convert ( cn - > value . get_type ( ) , member . _export . type ) ) {
2020-02-19 19:27:19 +00:00
Callable : : CallError err ;
2019-02-26 12:58:39 +00:00
const Variant * args = & cn - > value ;
cn - > value = Variant : : construct ( member . _export . type , & args , 1 , err ) ;
} else {
2019-08-23 18:51:42 +00:00
_set_error ( " Can't convert the provided value to the export type. " ) ;
2019-02-26 12:58:39 +00:00
return ;
}
}
2017-03-05 15:44:50 +00:00
member . default_value = cn - > value ;
2014-02-10 01:10:30 +00:00
}
}
2014-10-28 01:54:32 +00:00
# endif
IdentifierNode * id = alloc_node < IdentifierNode > ( ) ;
2017-03-05 15:44:50 +00:00
id - > name = member . identifier ;
2014-10-28 01:54:32 +00:00
OperatorNode * op = alloc_node < OperatorNode > ( ) ;
2017-03-05 15:44:50 +00:00
op - > op = OperatorNode : : OP_INIT_ASSIGN ;
2014-10-28 01:54:32 +00:00
op - > arguments . push_back ( id ) ;
op - > arguments . push_back ( subexpr ) ;
# ifdef DEBUG_ENABLED
2019-02-12 20:10:08 +00:00
NewLineNode * nl2 = alloc_node < NewLineNode > ( ) ;
nl2 - > line = line ;
2015-12-28 22:31:52 +00:00
if ( onready )
2019-02-12 20:10:08 +00:00
p_class - > ready - > statements . push_back ( nl2 ) ;
2015-12-28 22:31:52 +00:00
else
2019-02-12 20:10:08 +00:00
p_class - > initializer - > statements . push_back ( nl2 ) ;
2014-10-28 01:54:32 +00:00
# endif
2015-12-28 22:31:52 +00:00
if ( onready )
p_class - > ready - > statements . push_back ( op ) ;
else
p_class - > initializer - > statements . push_back ( op ) ;
2014-02-10 01:10:30 +00:00
2018-05-30 02:16:54 +00:00
member . initial_assignment = op ;
2014-10-28 01:54:32 +00:00
2018-05-30 02:16:54 +00:00
} else {
2014-10-28 01:54:32 +00:00
2018-05-30 02:16:54 +00:00
if ( autoexport & & ! member . data_type . has_type ) {
2014-10-28 01:54:32 +00:00
_set_error ( " Type-less export needs a constant expression assigned to infer type. " ) ;
return ;
2014-02-10 01:10:30 +00:00
}
2019-02-01 16:09:41 +00:00
2019-09-03 07:39:04 +00:00
Node * expr ;
2019-02-01 16:09:41 +00:00
2019-09-03 07:39:04 +00:00
if ( member . data_type . has_type ) {
expr = _get_default_value_for_type ( member . data_type ) ;
} else {
DataType exported_type ;
exported_type . has_type = true ;
exported_type . kind = DataType : : BUILTIN ;
exported_type . builtin_type = member . _export . type ;
expr = _get_default_value_for_type ( exported_type ) ;
}
2019-02-01 16:09:41 +00:00
2019-09-03 07:39:04 +00:00
IdentifierNode * id = alloc_node < IdentifierNode > ( ) ;
id - > name = member . identifier ;
2019-02-01 16:09:41 +00:00
2019-09-03 07:39:04 +00:00
OperatorNode * op = alloc_node < OperatorNode > ( ) ;
op - > op = OperatorNode : : OP_INIT_ASSIGN ;
op - > arguments . push_back ( id ) ;
op - > arguments . push_back ( expr ) ;
2019-02-01 16:09:41 +00:00
2019-09-03 07:39:04 +00:00
p_class - > initializer - > statements . push_back ( op ) ;
2019-02-01 16:09:41 +00:00
2019-09-03 07:39:04 +00:00
member . initial_assignment = op ;
2014-02-10 01:10:30 +00:00
}
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_PR_SETGET ) {
2014-02-10 01:10:30 +00:00
2014-10-28 01:54:32 +00:00
tokenizer - > advance ( ) ;
2014-02-10 01:10:30 +00:00
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_COMMA ) {
2014-10-28 01:54:32 +00:00
//just comma means using only getter
2017-03-31 17:28:34 +00:00
if ( ! tokenizer - > is_token_literal ( ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected an identifier for the setter function after \" setget \" . " ) ;
2014-10-28 01:54:32 +00:00
}
2014-02-10 01:10:30 +00:00
2017-03-31 17:28:34 +00:00
member . setter = tokenizer - > get_token_literal ( ) ;
2014-10-28 01:54:32 +00:00
tokenizer - > advance ( ) ;
}
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COMMA ) {
2014-10-28 01:54:32 +00:00
//there is a getter
tokenizer - > advance ( ) ;
2017-03-31 17:28:34 +00:00
if ( ! tokenizer - > is_token_literal ( ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected an identifier for the getter function after \" , \" . " ) ;
2014-10-28 01:54:32 +00:00
}
2017-03-31 17:28:34 +00:00
member . getter = tokenizer - > get_token_literal ( ) ;
2014-10-28 01:54:32 +00:00
tokenizer - > advance ( ) ;
}
}
p_class - > variables . push_back ( member ) ;
2014-02-10 01:10:30 +00:00
2015-06-24 16:29:23 +00:00
if ( ! _end_statement ( ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected end of statement ( \" continue \" ). " ) ;
2015-06-24 16:29:23 +00:00
return ;
}
2014-02-10 01:10:30 +00:00
} break ;
2017-11-16 17:38:18 +00:00
case GDScriptTokenizer : : TK_PR_CONST : {
2018-05-30 02:16:54 +00:00
// constant declaration and initialization
2014-02-10 01:10:30 +00:00
ClassNode : : Constant constant ;
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2017-03-31 17:28:34 +00:00
if ( ! tokenizer - > is_token_literal ( 0 , true ) ) {
2014-02-10 01:10:30 +00:00
2019-08-23 18:51:42 +00:00
_set_error ( " Expected an identifier for the constant. " ) ;
2014-02-10 01:10:30 +00:00
return ;
}
2018-05-30 02:16:54 +00:00
StringName const_id = tokenizer - > get_token_literal ( ) ;
2018-06-06 01:57:44 +00:00
int line = tokenizer - > get_token_line ( ) ;
2018-05-30 02:16:54 +00:00
if ( current_class - > constant_expressions . has ( const_id ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Constant \" " + String ( const_id ) + " \" already exists in this class (at line " +
2018-05-30 02:16:54 +00: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 ) {
2019-08-23 18:51:42 +00:00
_set_error ( " A variable named \" " + String ( const_id ) + " \" already exists in this class (at line " +
2018-05-30 02:16:54 +00:00
itos ( current_class - > variables [ i ] . line ) + " ). " ) ;
return ;
}
}
2019-04-09 17:28:34 +00:00
for ( int i = 0 ; i < current_class - > subclasses . size ( ) ; i + + ) {
if ( current_class - > subclasses [ i ] - > name = = const_id ) {
2019-08-23 18:51:42 +00:00
_set_error ( " A class named \" " + String ( const_id ) + " \" already exists in this class (at line " + itos ( current_class - > subclasses [ i ] - > line ) + " ). " ) ;
2019-04-09 17:28:34 +00:00
return ;
}
}
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2014-02-10 01:10:30 +00:00
2018-05-30 02:16:51 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COLON ) {
2018-06-19 05:55:52 +00: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 ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected a type for the class constant. " ) ;
2018-05-30 02:16:51 +00:00
return ;
}
}
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_OP_ASSIGN ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Constants must be assigned immediately. " ) ;
2014-02-10 01:10:30 +00:00
return ;
}
2014-02-25 12:31:47 +00:00
tokenizer - > advance ( ) ;
2014-02-10 01:10:30 +00:00
2017-08-21 19:15:36 +00:00
Node * subexpr = _parse_and_reduce_expression ( p_class , true , true ) ;
2015-08-30 14:50:10 +00:00
if ( ! subexpr ) {
if ( _recover_from_completion ( ) ) {
break ;
}
2014-02-10 01:10:30 +00:00
return ;
2015-08-30 14:50:10 +00:00
}
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
if ( subexpr - > type ! = Node : : TYPE_CONSTANT ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected a constant expression. " , line ) ;
2018-05-30 02:16:52 +00:00
return ;
2014-02-10 01:10:30 +00:00
}
2018-06-06 01:57:44 +00:00
subexpr - > line = line ;
2017-03-05 15:44:50 +00:00
constant . expression = subexpr ;
2014-02-10 01:10:30 +00:00
2018-05-30 02:16:54 +00:00
p_class - > constant_expressions . insert ( const_id , constant ) ;
2014-02-10 01:10:30 +00:00
2015-06-24 16:29:23 +00:00
if ( ! _end_statement ( ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected end of statement (constant). " , line ) ;
2015-06-24 16:29:23 +00:00
return ;
}
2014-02-10 01:10:30 +00:00
2016-08-26 11:15:45 +00:00
} break ;
2017-11-16 17:38:18 +00:00
case GDScriptTokenizer : : TK_PR_ENUM : {
2018-02-21 16:30:55 +00:00
//multiple constant declarations..
2016-08-26 11:15:45 +00:00
2018-09-13 01:38:39 +00:00
int last_assign = - 1 ; // Incremented by 1 right before the assignment.
2016-08-27 12:56:51 +00:00
String enum_name ;
Dictionary enum_dict ;
2016-08-26 11:15:45 +00:00
tokenizer - > advance ( ) ;
2017-03-31 17:28:34 +00:00
if ( tokenizer - > is_token_literal ( 0 , true ) ) {
enum_name = tokenizer - > get_token_literal ( ) ;
2018-11-11 03:42:20 +00:00
if ( current_class - > constant_expressions . has ( enum_name ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " A constant named \" " + String ( enum_name ) + " \" already exists in this class (at line " +
2018-11-11 03:42:20 +00:00
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 ) {
2019-08-23 18:51:42 +00:00
_set_error ( " A variable named \" " + String ( enum_name ) + " \" already exists in this class (at line " +
2018-11-11 03:42:20 +00:00
itos ( current_class - > variables [ i ] . line ) + " ). " ) ;
return ;
}
}
2019-04-09 17:28:34 +00:00
for ( int i = 0 ; i < current_class - > subclasses . size ( ) ; i + + ) {
if ( current_class - > subclasses [ i ] - > name = = enum_name ) {
2019-08-23 18:51:42 +00:00
_set_error ( " A class named \" " + String ( enum_name ) + " \" already exists in this class (at line " + itos ( current_class - > subclasses [ i ] - > line ) + " ). " ) ;
2019-04-09 17:28:34 +00:00
return ;
}
}
2016-08-27 12:56:51 +00:00
tokenizer - > advance ( ) ;
}
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) ! = GDScriptTokenizer : : TK_CURLY_BRACKET_OPEN ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected \" { \" in the enum declaration. " ) ;
2016-08-26 11:15:45 +00:00
return ;
}
tokenizer - > advance ( ) ;
2017-03-05 15:44:50 +00:00
while ( true ) {
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_NEWLINE ) {
2017-03-05 15:44:50 +00:00
2016-08-26 11:15:45 +00:00
tokenizer - > advance ( ) ; // Ignore newlines
2017-11-16 17:38:18 +00:00
} else if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_CURLY_BRACKET_CLOSE ) {
2017-03-05 15:44:50 +00:00
2016-08-26 11:15:45 +00:00
tokenizer - > advance ( ) ;
break ; // End of enum
2017-03-31 17:28:34 +00:00
} else if ( ! tokenizer - > is_token_literal ( 0 , true ) ) {
2017-03-05 15:44:50 +00:00
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_EOF ) {
2016-08-26 11:15:45 +00:00
_set_error ( " Unexpected end of file. " ) ;
} else {
2019-08-23 18:51:42 +00:00
_set_error ( String ( " Unexpected " ) + GDScriptTokenizer : : get_token_name ( tokenizer - > get_token ( ) ) + " , expected an identifier. " ) ;
2016-08-26 11:15:45 +00:00
}
2017-03-05 15:44:50 +00:00
2016-08-26 11:15:45 +00:00
return ;
2017-03-31 17:28:34 +00:00
} else { // tokenizer->is_token_literal(0, true)
2018-05-30 02:16:54 +00:00
StringName const_id = tokenizer - > get_token_literal ( ) ;
2017-03-05 15:44:50 +00:00
2016-08-26 11:15:45 +00:00
tokenizer - > advance ( ) ;
2017-03-05 15:44:50 +00:00
2018-11-11 03:42:20 +00:00
ConstantNode * enum_value_expr ;
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_OP_ASSIGN ) {
2016-08-26 11:15:45 +00:00
tokenizer - > advance ( ) ;
2017-08-21 19:15:36 +00:00
Node * subexpr = _parse_and_reduce_expression ( p_class , true , true ) ;
2016-08-26 11:15:45 +00:00
if ( ! subexpr ) {
if ( _recover_from_completion ( ) ) {
break ;
}
return ;
}
2017-03-05 15:44:50 +00:00
if ( subexpr - > type ! = Node : : TYPE_CONSTANT ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected a constant expression. " ) ;
2018-05-30 02:16:52 +00:00
return ;
2016-08-26 11:15:45 +00:00
}
2017-03-05 15:44:50 +00:00
2018-11-11 03:42:20 +00:00
enum_value_expr = static_cast < ConstantNode * > ( subexpr ) ;
2017-03-05 15:44:50 +00:00
2018-11-11 03:42:20 +00:00
if ( enum_value_expr - > value . get_type ( ) ! = Variant : : INT ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected an integer value for \" enum \" . " ) ;
2018-05-30 02:16:52 +00:00
return ;
2016-08-26 11:15:45 +00:00
}
2017-03-05 15:44:50 +00:00
2018-11-11 03:42:20 +00:00
last_assign = enum_value_expr - > value ;
2016-08-26 11:15:45 +00:00
} else {
last_assign = last_assign + 1 ;
2018-11-11 03:42:20 +00: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 11:15:45 +00:00
}
2017-03-05 15:44:50 +00:00
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_COMMA ) {
2016-08-26 11:15:45 +00:00
tokenizer - > advance ( ) ;
2019-07-05 18:40:40 +00:00
} else if ( tokenizer - > is_token_literal ( 0 , true ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Unexpected identifier. " ) ;
2019-07-05 18:40:40 +00:00
return ;
2016-08-26 11:15:45 +00:00
}
2017-03-05 15:44:50 +00:00
if ( enum_name ! = " " ) {
2018-11-11 03:42:20 +00:00
enum_dict [ const_id ] = enum_value_expr - > value ;
} else {
if ( current_class - > constant_expressions . has ( const_id ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " A constant named \" " + String ( const_id ) + " \" already exists in this class (at line " +
2018-11-11 03:42:20 +00:00
itos ( current_class - > constant_expressions [ const_id ] . expression - > line ) + " ). " ) ;
return ;
}
2016-08-27 12:56:51 +00:00
2018-11-11 03:42:20 +00:00
for ( int i = 0 ; i < current_class - > variables . size ( ) ; i + + ) {
if ( current_class - > variables [ i ] . identifier = = const_id ) {
2019-08-23 18:51:42 +00:00
_set_error ( " A variable named \" " + String ( const_id ) + " \" already exists in this class (at line " +
2018-11-11 03:42:20 +00:00
itos ( current_class - > variables [ i ] . line ) + " ). " ) ;
return ;
}
}
2019-04-09 17:28:34 +00:00
for ( int i = 0 ; i < current_class - > subclasses . size ( ) ; i + + ) {
if ( current_class - > subclasses [ i ] - > name = = const_id ) {
2019-08-23 18:51:42 +00:00
_set_error ( " A class named \" " + String ( const_id ) + " \" already exists in this class (at line " + itos ( current_class - > subclasses [ i ] - > line ) + " ). " ) ;
2019-04-09 17:28:34 +00:00
return ;
}
}
2018-11-11 03:42:20 +00:00
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 11:15:45 +00:00
}
}
2017-03-05 15:44:50 +00:00
if ( enum_name ! = " " ) {
2016-08-27 12:56:51 +00:00
ClassNode : : Constant enum_constant ;
ConstantNode * cn = alloc_node < ConstantNode > ( ) ;
cn - > value = enum_dict ;
2018-05-30 02:16:54 +00:00
cn - > datatype = _type_from_variant ( cn - > value ) ;
2017-03-05 15:44:50 +00:00
enum_constant . expression = cn ;
2018-05-30 02:16:53 +00:00
enum_constant . type = cn - > datatype ;
2018-05-30 02:16:54 +00:00
p_class - > constant_expressions . insert ( enum_name , enum_constant ) ;
2016-08-27 12:56:51 +00:00
}
2016-08-26 11:15:45 +00:00
if ( ! _end_statement ( ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected end of statement ( \" enum \" ). " ) ;
2016-08-26 11:15:45 +00:00
return ;
}
2014-02-10 01:10:30 +00:00
} break ;
2017-03-05 15:44:50 +00:00
2017-11-16 17:38:18 +00:00
case GDScriptTokenizer : : TK_CONSTANT : {
2017-03-05 15:44:50 +00:00
if ( tokenizer - > get_token_constant ( ) . get_type ( ) = = Variant : : STRING ) {
2016-11-03 10:26:38 +00:00
tokenizer - > advance ( ) ;
// Ignore
} else {
2017-03-05 15:44:50 +00:00
_set_error ( String ( ) + " Unexpected constant of type: " + Variant : : get_type_name ( tokenizer - > get_token_constant ( ) . get_type ( ) ) ) ;
2016-11-03 10:26:38 +00:00
return ;
}
} break ;
2014-02-10 01:10:30 +00:00
2020-03-03 14:07:59 +00:00
case GDScriptTokenizer : : TK_CF_PASS : {
tokenizer - > advance ( ) ;
} break ;
2014-02-10 01:10:30 +00:00
default : {
2017-03-05 15:44:50 +00:00
_set_error ( String ( ) + " Unexpected token: " + tokenizer - > get_token_name ( tokenizer - > get_token ( ) ) + " : " + tokenizer - > get_token_identifier ( ) ) ;
2014-02-10 01:10:30 +00:00
return ;
} break ;
}
}
}
2019-07-03 14:59:29 +00:00
void GDScriptParser : : _determine_inheritance ( ClassNode * p_class , bool p_recursive ) {
2018-05-30 02:16:52 +00:00
2019-07-03 14:59:29 +00:00
if ( p_class - > base_type . has_type ) {
// Already determined
} else if ( p_class - > extends_used ) {
2018-05-30 02:16:52 +00:00
//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 01:41:26 +00:00
String base = base_path ;
2018-05-30 02:16:52 +00:00
if ( base = = " " | | base . is_rel_path ( ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Couldn't resolve relative path for the parent class: " + path , p_class - > line ) ;
2018-05-30 02:16:52 +00:00
return ;
}
2018-06-21 01:41:26 +00:00
path = base . plus_file ( path ) . simplify_path ( ) ;
2018-05-30 02:16:52 +00:00
}
script = ResourceLoader : : load ( path ) ;
if ( script . is_null ( ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Couldn't load the base class: " + path , p_class - > line ) ;
2018-05-30 02:16:52 +00:00
return ;
}
if ( ! script - > is_valid ( ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Script isn't fully loaded (cyclic preload?): " + path , p_class - > line ) ;
2018-05-30 02:16:52 +00:00
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 {
2019-08-23 18:51:42 +00:00
_set_error ( " Couldn't find the subclass: " + sub , p_class - > line ) ;
2018-05-30 02:16:52 +00:00
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 ( ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " The class \" " + base + " \" couldn't be fully loaded (script error or cyclic dependency). " , p_class - > line ) ;
2018-05-30 02:16:52 +00:00
return ;
}
p = NULL ;
2019-01-11 00:25:32 +00:00
} else {
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 = = base ) {
String singleton_path = ProjectSettings : : get_singleton ( ) - > get ( s ) ;
if ( singleton_path . begins_with ( " * " ) ) {
singleton_path = singleton_path . right ( 1 ) ;
}
if ( ! singleton_path . begins_with ( " res:// " ) ) {
singleton_path = " res:// " + singleton_path ;
}
base_script = ResourceLoader : : load ( singleton_path ) ;
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 ;
}
}
2018-05-30 02:16:52 +00:00
}
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 02:16:54 +00:00
if ( p - > constant_expressions . has ( base ) ) {
2018-09-27 08:43:12 +00:00
if ( p - > constant_expressions [ base ] . expression - > type ! = Node : : TYPE_CONSTANT ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Couldn't resolve the constant \" " + base + " \" . " , p_class - > line ) ;
2018-05-30 02:16:54 +00:00
return ;
}
const ConstantNode * cn = static_cast < const ConstantNode * > ( p - > constant_expressions [ base ] . expression ) ;
base_script = cn - > value ;
if ( base_script . is_null ( ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Constant isn't a class: " + base , p_class - > line ) ;
2018-05-30 02:16:54 +00:00
return ;
2018-05-30 02:16:52 +00:00
}
2018-05-30 02:16:54 +00:00
break ;
2018-05-30 02:16:52 +00:00
}
p = p - > owner ;
}
if ( base_script . is_valid ( ) ) {
String ident = base ;
2019-05-24 03:56:44 +00:00
Ref < GDScript > find_subclass = base_script ;
2018-05-30 02:16:52 +00:00
for ( int i = extend_iter ; i < p_class - > extends_class . size ( ) ; i + + ) {
String subclass = p_class - > extends_class [ i ] ;
ident + = ( " . " + subclass ) ;
2020-01-18 09:55:27 +00:00
if ( find_subclass - > get_subclasses ( ) . has ( subclass ) ) {
2018-05-30 02:16:52 +00:00
2020-01-18 09:55:27 +00:00
find_subclass = find_subclass - > get_subclasses ( ) [ subclass ] ;
} else if ( find_subclass - > get_constants ( ) . has ( subclass ) ) {
2018-05-30 02:16:52 +00:00
2020-01-18 09:55:27 +00:00
Ref < GDScript > new_base_class = find_subclass - > get_constants ( ) [ subclass ] ;
2018-05-30 02:16:52 +00:00
if ( new_base_class . is_null ( ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Constant isn't a class: " + ident , p_class - > line ) ;
2018-05-30 02:16:52 +00:00
return ;
}
2019-05-24 03:56:44 +00:00
find_subclass = new_base_class ;
2018-05-30 02:16:52 +00:00
} else {
2019-08-23 18:51:42 +00:00
_set_error ( " Couldn't find the subclass: " + ident , p_class - > line ) ;
2018-05-30 02:16:52 +00:00
return ;
}
}
2019-05-24 03:56:44 +00:00
script = find_subclass ;
2018-05-30 02:16:52 +00:00
} else if ( ! base_class ) {
if ( p_class - > extends_class . size ( ) > 1 ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Invalid inheritance (unknown class + subclasses). " , p_class - > line ) ;
2018-05-30 02:16:52 +00:00
return ;
}
//if not found, try engine classes
if ( ! GDScriptLanguage : : get_singleton ( ) - > get_global_map ( ) . has ( base ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Unknown class: \" " + base + " \" " , p_class - > line ) ;
2018-05-30 02:16:52 +00:00
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 {
2019-08-23 18:51:42 +00:00
_set_error ( " Couldn't determine inheritance. " , p_class - > line ) ;
2018-05-30 02:16:52 +00:00
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 " ;
}
2019-07-03 14:59:29 +00:00
if ( p_recursive ) {
// Recursively determine subclasses
for ( int i = 0 ; i < p_class - > subclasses . size ( ) ; i + + ) {
_determine_inheritance ( p_class - > subclasses [ i ] , p_recursive ) ;
}
2018-05-30 02:16:52 +00:00
}
}
2018-05-30 02:16:57 +00: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 ;
}
2020-02-22 19:47:50 +00:00
[[fallthrough]] ;
2019-04-05 12:06:16 +00:00
}
2018-05-30 02:16:57 +00:00
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 11:13:56 +00:00
case UNRESOLVED : {
} break ;
2018-05-30 02:16:57 +00:00
}
return " Unresolved " ;
}
2018-05-30 02:16:51 +00:00
bool GDScriptParser : : _parse_type ( DataType & r_type , bool p_can_be_void ) {
tokenizer - > advance ( ) ;
r_type . has_type = true ;
2018-05-30 02:16:54 +00: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 02:16:51 +00:00
switch ( tokenizer - > get_token ( ) ) {
case GDScriptTokenizer : : TK_PR_VOID : {
if ( ! p_can_be_void ) {
return false ;
}
2018-05-30 02:16:53 +00: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 02:16:54 +00: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 02:16:53 +00:00
} break ;
case GDScriptTokenizer : : TK_IDENTIFIER : {
2018-05-30 02:16:54 +00: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 02:16:53 +00:00
r_type . kind = DataType : : NATIVE ;
2018-05-30 02:16:54 +00:00
} else {
r_type . kind = DataType : : UNRESOLVED ;
can_index = true ;
full_name = r_type . native_type ;
2018-05-30 02:16:53 +00:00
}
2018-05-30 02:16:51 +00:00
} break ;
default : {
return false ;
}
}
tokenizer - > advance ( ) ;
2018-05-30 02:16:54 +00: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 01:10:30 +00:00
2018-05-30 02:16:54 +00:00
if ( can_index ) {
while ( ! finished ) {
switch ( tokenizer - > get_token ( ) ) {
case GDScriptTokenizer : : TK_PERIOD : {
if ( ! can_index ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Unexpected \" . \" . " ) ;
2018-05-30 02:16:54 +00:00
return false ;
}
can_index = false ;
tokenizer - > advance ( ) ;
} break ;
case GDScriptTokenizer : : TK_IDENTIFIER : {
if ( can_index ) {
_set_error ( " Unexpected identifier. " ) ;
return false ;
}
2014-02-10 01:10:30 +00:00
2018-05-30 02:16:54 +00: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 ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Expected a subclass identifier. " ) ;
2018-05-30 02:16:54 +00:00
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 ;
2019-03-04 01:53:50 +00:00
result . class_type = static_cast < ClassNode * > ( head ) ;
2018-05-30 02:16:54 +00:00
} else {
Ref < Script > script = ResourceLoader : : load ( script_path ) ;
Ref < GDScript > gds = script ;
if ( gds . is_valid ( ) ) {
if ( ! gds - > is_valid ( ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " The class \" " + id + " \" couldn't be fully loaded (script error or cyclic dependency). " , p_line ) ;
2018-05-30 02:16:54 +00:00
return DataType ( ) ;
}
result . kind = DataType : : GDSCRIPT ;
result . script_type = gds ;
} else if ( script . is_valid ( ) ) {
result . kind = DataType : : SCRIPT ;
result . script_type = script ;
} else {
2019-08-23 18:51:42 +00:00
_set_error ( " The class \" " + id + " \" was found in global scope, but its script couldn't be loaded. " , p_line ) ;
2018-05-30 02:16:54 +00:00
return DataType ( ) ;
}
}
name_part + + ;
continue ;
}
2019-01-11 00:25:32 +00:00
List < PropertyInfo > props ;
ProjectSettings : : get_singleton ( ) - > get_property_list ( & props ) ;
String singleton_path ;
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 = = id ) {
singleton_path = ProjectSettings : : get_singleton ( ) - > get ( s ) ;
if ( singleton_path . begins_with ( " * " ) ) {
singleton_path = singleton_path . right ( 1 ) ;
}
if ( ! singleton_path . begins_with ( " res:// " ) ) {
singleton_path = " res:// " + singleton_path ;
}
break ;
}
}
if ( ! singleton_path . empty ( ) ) {
Ref < Script > script = ResourceLoader : : load ( singleton_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 ( " Couldn't fully load singleton script ' " + id + " ' (possible cyclic reference or parse error). " , p_line ) ;
return DataType ( ) ;
}
name_part + + ;
continue ;
}
p = current_class ;
2018-05-30 02:16:54 +00:00
} 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 ) {
2019-01-17 18:03:15 +00:00
if ( outer_class - > name = = id ) {
found = true ;
result . kind = DataType : : CLASS ;
result . class_type = outer_class ;
break ;
}
2018-05-30 02:16:54 +00:00
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 ;
}
}
2019-09-11 05:19:20 +00:00
// Still look for class constants in parent scripts
2018-05-30 02:16:54 +00:00
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 ) ;
2019-09-11 05:19:20 +00:00
while ( scr . is_valid ( ) ) {
Map < StringName , Variant > constants ;
scr - > get_constants ( & constants ) ;
2018-05-30 02:16:54 +00:00
2019-09-11 05:19:20 +00:00
if ( constants . has ( id ) ) {
Ref < GDScript > gds = constants [ id ] ;
2018-05-30 02:16:54 +00:00
2019-09-11 05:19:20 +00:00
if ( gds . is_valid ( ) ) {
result . kind = DataType : : GDSCRIPT ;
result . script_type = gds ;
2018-05-30 02:16:54 +00:00
found = true ;
2019-09-11 05:19:20 +00:00
} else {
Ref < Script > scr2 = constants [ id ] ;
if ( scr2 . is_valid ( ) ) {
result . kind = DataType : : SCRIPT ;
result . script_type = scr2 ;
found = true ;
}
2018-05-30 02:16:54 +00:00
}
}
2019-09-11 05:19:20 +00:00
if ( found ) {
break ;
} else {
scr = scr - > get_base_script ( ) ;
}
2018-05-30 02:16:54 +00:00
}
}
if ( ! found & & ! for_completion ) {
String base ;
if ( name_part = = 0 ) {
base = " self " ;
} else {
base = result . to_string ( ) ;
}
2019-08-23 18:51:42 +00:00
_set_error ( " The identifier \" " + String ( id ) + " \" isn't a valid type (not a script or class), or couldn't be found on base \" " +
base + " \" . " ,
2018-05-30 02:16:54 +00:00
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 ) {
2019-01-30 01:12:41 +00:00
case GDScriptDataType : : UNINITIALIZED : {
2019-08-17 11:04:33 +00:00
ERR_PRINT ( " Uninitialized datatype. Please report a bug. " ) ;
2019-01-30 01:12:41 +00:00
} break ;
2018-05-30 02:16:54 +00:00
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 {
2020-02-19 19:27:19 +00:00
Callable : : CallError err ;
2018-05-30 02:16:54 +00:00
a = Variant : : construct ( a_type , NULL , 0 , err ) ;
2020-02-19 19:27:19 +00:00
if ( err . error ! = Callable : : CallError : : CALL_OK ) {
2018-05-30 02:16:54 +00:00
r_valid = false ;
return DataType ( ) ;
}
}
Variant b ;
REF b_ref ;
if ( b_type = = Variant : : OBJECT ) {
b_ref . instance ( ) ;
b = b_ref ;
} else {
2020-02-19 19:27:19 +00:00
Callable : : CallError err ;
2018-05-30 02:16:54 +00:00
b = Variant : : construct ( b_type , NULL , 0 , err ) ;
2020-02-19 19:27:19 +00:00
if ( err . error ! = Callable : : CallError : : CALL_OK ) {
2018-05-30 02:16:54 +00:00
r_valid = false ;
return DataType ( ) ;
}
}
// Avoid division by zero
Variant: Added 64-bit packed arrays, renamed Variant::REAL to FLOAT.
- Renames PackedIntArray to PackedInt32Array.
- Renames PackedFloatArray to PackedFloat32Array.
- Adds PackedInt64Array and PackedFloat64Array.
- Renames Variant::REAL to Variant::FLOAT for consistency.
Packed arrays are for storing large amount of data and creating stuff like
meshes, buffers. textures, etc. Forcing them to be 64 is a huge waste of
memory. That said, many users requested the ability to have 64 bits packed
arrays for their games, so this is just an optional added type.
For Variant, the float datatype is always 64 bits, and exposed as `float`.
We still have `real_t` which is the datatype that can change from 32 to 64
bits depending on a compile flag (not entirely working right now, but that's
the idea). It affects math related datatypes and code only.
Neither Variant nor PackedArray make use of real_t, which is only intended
for math precision, so the term is removed from there to keep only float.
2020-02-24 18:20:53 +00:00
if ( a_type = = Variant : : INT | | a_type = = Variant : : FLOAT ) {
2018-05-30 02:16:54 +00:00
Variant : : evaluate ( Variant : : OP_ADD , a , 1 , a , r_valid ) ;
}
Variant: Added 64-bit packed arrays, renamed Variant::REAL to FLOAT.
- Renames PackedIntArray to PackedInt32Array.
- Renames PackedFloatArray to PackedFloat32Array.
- Adds PackedInt64Array and PackedFloat64Array.
- Renames Variant::REAL to Variant::FLOAT for consistency.
Packed arrays are for storing large amount of data and creating stuff like
meshes, buffers. textures, etc. Forcing them to be 64 is a huge waste of
memory. That said, many users requested the ability to have 64 bits packed
arrays for their games, so this is just an optional added type.
For Variant, the float datatype is always 64 bits, and exposed as `float`.
We still have `real_t` which is the datatype that can change from 32 to 64
bits depending on a compile flag (not entirely working right now, but that's
the idea). It affects math related datatypes and code only.
Neither Variant nor PackedArray make use of real_t, which is only intended
for math precision, so the term is removed from there to keep only float.
2020-02-24 18:20:53 +00:00
if ( b_type = = Variant : : INT | | b_type = = Variant : : FLOAT ) {
2018-05-30 02:16:54 +00:00
Variant : : evaluate ( Variant : : OP_ADD , b , 1 , b , r_valid ) ;
}
2018-07-26 13:52:11 +00:00
if ( a_type = = Variant : : STRING & & b_type ! = Variant : : ARRAY ) {
2018-07-25 23:38:02 +00:00
a = " %s " ; // Work around for formatting operator (%)
}
2018-05-30 02:16:54 +00: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 ;
}
}
2019-03-04 11:25:59 +00:00
bool GDScriptParser : : _is_type_compatible ( const DataType & p_container , const DataType & p_expression , bool p_allow_implicit_conversion ) const {
2018-05-30 02:16:54 +00:00
// 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 ;
2019-03-04 11:25:59 +00:00
if ( p_allow_implicit_conversion ) {
valid = valid | | Variant : : can_convert_strict ( p_expression . builtin_type , p_container . builtin_type ) ;
}
2018-05-30 02:16:54 +00:00
return valid ;
}
2018-12-08 01:53:45 +00: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 ) {
2019-06-26 13:08:25 +00:00
return p_expression . builtin_type = = Variant : : NIL ;
2018-12-08 01:53:45 +00:00
}
// If it's not a built-in, must be an object
return true ;
}
2018-05-30 02:16:54 +00: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 11:13:56 +00:00
} break ;
case DataType : : BUILTIN : // Already handled above
case DataType : : UNRESOLVED : // Not allowed, see above
break ;
2018-05-30 02:16:54 +00:00
}
2019-10-19 16:38:50 +00:00
// Some classes are prefixed with `_` internally
if ( ! ClassDB : : class_exists ( expr_native ) ) {
expr_native = " _ " + expr_native ;
}
2018-05-30 02:16:54 +00: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 {
2019-10-19 16:38:50 +00:00
StringName container_native = ClassDB : : class_exists ( p_container . native_type ) ? p_container . native_type : StringName ( " _ " + p_container . native_type ) ;
return ClassDB : : is_parent_class ( expr_native , container_native ) ;
2018-05-30 02:16:54 +00:00
}
} 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 11:13:56 +00:00
} break ;
case DataType : : BUILTIN : // Already handled above
case DataType : : UNRESOLVED : // Not allowed, see above
break ;
2018-05-30 02:16:54 +00:00
}
return false ;
}
2019-09-03 07:39:04 +00:00
GDScriptParser : : Node * GDScriptParser : : _get_default_value_for_type ( const DataType & p_type , int p_line ) {
Node * result ;
if ( p_type . has_type & & p_type . kind = = DataType : : BUILTIN & & p_type . builtin_type ! = Variant : : NIL & & p_type . builtin_type ! = Variant : : OBJECT ) {
if ( p_type . builtin_type = = Variant : : ARRAY ) {
result = alloc_node < ArrayNode > ( ) ;
} else if ( p_type . builtin_type = = Variant : : DICTIONARY ) {
result = alloc_node < DictionaryNode > ( ) ;
} else {
ConstantNode * c = alloc_node < ConstantNode > ( ) ;
2020-02-19 19:27:19 +00:00
Callable : : CallError err ;
2019-09-03 07:39:04 +00:00
c - > value = Variant : : construct ( p_type . builtin_type , NULL , 0 , err ) ;
result = c ;
}
} else {
ConstantNode * c = alloc_node < ConstantNode > ( ) ;
c - > value = Variant ( ) ;
result = c ;
}
result - > line = p_line ;
return result ;
}
2018-05-30 02:16:54 +00:00
GDScriptParser : : DataType GDScriptParser : : _reduce_node_type ( Node * p_node ) {
2019-06-03 14:29:05 +00:00
# ifdef DEBUG_ENABLED
if ( p_node - > get_datatype ( ) . has_type & & p_node - > type ! = Node : : TYPE_ARRAY & & p_node - > type ! = Node : : TYPE_DICTIONARY ) {
# else
2018-05-30 02:16:54 +00:00
if ( p_node - > get_datatype ( ) . has_type ) {
2019-06-03 14:29:05 +00:00
# endif
2018-05-30 02:16:54 +00:00
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 16:31:23 +00: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 02:16:54 +00:00
case Node : : TYPE_ARRAY : {
node_type . has_type = true ;
node_type . kind = DataType : : BUILTIN ;
node_type . builtin_type = Variant : : ARRAY ;
2018-07-01 16:17:40 +00: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 02:16:54 +00: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 16:17:40 +00: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 02:16:54 +00:00
} break ;
case Node : : TYPE_SELF : {
node_type . has_type = true ;
node_type . kind = DataType : : CLASS ;
node_type . class_type = current_class ;
2020-01-21 19:57:22 +00:00
node_type . is_constant = true ;
2018-05-30 02:16:54 +00:00
} 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 16:17:40 +00:00
id - > declared_block - > variables [ id - > name ] - > usages + = 1 ;
2018-05-30 02:16:54 +00: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 {
2019-01-16 18:02:56 +00:00
node_type = _reduce_identifier_type ( NULL , id - > name , id - > line , false ) ;
2018-05-30 02:16:54 +00:00
}
} 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 ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Invalid cast. Cannot convert from \" " + source_type . to_string ( ) +
" \" to \" " + cn - > cast_type . to_string ( ) + " \" . " ,
2018-05-30 02:16:54 +00:00
cn - > line ) ;
return DataType ( ) ;
}
}
2018-06-05 16:50:21 +00:00
} else {
2018-07-01 16:17:40 +00:00
# ifdef DEBUG_ENABLED
_add_warning ( GDScriptWarning : : UNSAFE_CAST , cn - > line , cn - > cast_type . to_string ( ) ) ;
# endif // DEBUG_ENABLED
2018-06-05 16:50:21 +00:00
_mark_line_as_unsafe ( cn - > line ) ;
2018-05-30 02:16:54 +00: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 ) {
2019-08-23 18:51:42 +00:00
_set_error ( " The first argument of \" yield() \" must be an object. " , op - > line ) ;
2018-05-30 02:16:54 +00:00
return DataType ( ) ;
}
if ( signal_type . has_type & & ( signal_type . kind ! = DataType : : BUILTIN | | signal_type . builtin_type ! = Variant : : STRING ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " The second argument of \" yield() \" must be a string. " , op - > line ) ;
2018-05-30 02:16:54 +00:00
return DataType ( ) ;
}
}
// yield can return anything
node_type . has_type = false ;
} break ;
2018-08-26 16:31:23 +00:00
case OperatorNode : : OP_IS :
case OperatorNode : : OP_IS_BUILTIN : {
2018-05-30 02:16:54 +00: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 " ) ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Invalid \" is \" test: the right operand isn't a type (neither a native type nor a script). " , op - > line ) ;
2018-05-30 02:16:54 +00:00
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 16:31:23 +00:00
if ( op - > op = = OperatorNode : : OP_IS ) {
2019-08-23 18:51:42 +00:00
_set_error ( " A value of type \" " + value_type . to_string ( ) + " \" will never be an instance of \" " + type_type . to_string ( ) + " \" . " , op - > line ) ;
2018-08-26 16:31:23 +00:00
} else {
2019-08-23 18:51:42 +00:00
_set_error ( " A value of type \" " + value_type . to_string ( ) + " \" will never be of type \" " + type_type . to_string ( ) + " \" . " , op - > line ) ;
2018-08-26 16:31:23 +00:00
}
2018-05-30 02:16:54 +00: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 ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Invalid operand type ( \" " + argument_type . to_string ( ) +
" \" ) to unary operator \" " + Variant : : get_operator_name ( var_op ) + " \" . " ,
2018-05-30 02:16:54 +00:00
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 16:50:21 +00:00
_mark_line_as_unsafe ( op - > line ) ;
2018-05-30 02:16:54 +00: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 ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Invalid operand types ( \" " + argument_a_type . to_string ( ) + " \" and \" " +
argument_b_type . to_string ( ) + " \" ) to operator \" " + Variant : : get_operator_name ( var_op ) + " \" . " ,
2018-05-30 02:16:54 +00:00
op - > line , op - > column ) ;
return DataType ( ) ;
}
2018-07-01 16:17:40 +00:00
# ifdef DEBUG_ENABLED
2019-04-04 20:00:16 +00:00
if ( var_op = = Variant : : OP_DIVIDE & & argument_a_type . kind = = DataType : : BUILTIN & & argument_a_type . builtin_type = = Variant : : INT & &
argument_b_type . kind = = DataType : : BUILTIN & & argument_b_type . builtin_type = = Variant : : INT ) {
2018-07-01 16:17:40 +00:00
_add_warning ( GDScriptWarning : : INTEGER_DIVISION , op - > line ) ;
}
# endif // DEBUG_ENABLED
2018-05-30 02:16:54 +00:00
} break ;
// Ternary operators
case OperatorNode : : OP_TERNARY_IF : {
if ( op - > arguments . size ( ) ! = 3 ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Parser bug: ternary operation without 3 arguments. " ) ;
2018-05-30 02:16:54 +00:00
ERR_FAIL_V ( DataType ( ) ) ;
}
DataType true_type = _reduce_node_type ( op - > arguments [ 1 ] ) ;
DataType false_type = _reduce_node_type ( op - > arguments [ 2 ] ) ;
2019-11-15 00:29:18 +00:00
// Check arguments[0] errors.
_reduce_node_type ( op - > arguments [ 0 ] ) ;
2018-05-30 02:16:54 +00:00
// 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 16:17:40 +00:00
} else {
# ifdef DEBUG_ENABLED
_add_warning ( GDScriptWarning : : INCOMPATIBLE_TERNARY , op - > line ) ;
# endif // DEBUG_ENABLED
2018-05-30 02:16:54 +00: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 : {
2019-08-23 18:51:42 +00:00
_set_error ( " Assignment inside an expression isn't allowed (parser bug?). " , op - > line ) ;
2018-05-30 02:16:54 +00:00
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 : {
2020-02-19 19:27:19 +00:00
Callable : : CallError err ;
2018-05-30 02:16:54 +00:00
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 ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Can't get index \" " + String ( member_id - > name . operator String ( ) ) + " \" on base \" " +
base_type . to_string ( ) + " \" . " ,
2018-05-30 02:16:54 +00:00
op - > line ) ;
return DataType ( ) ;
}
} break ;
}
result . is_constant = false ;
node_type = result ;
} else {
2019-01-16 18:02:56 +00:00
node_type = _reduce_identifier_type ( & base_type , member_id - > name , op - > line , true ) ;
2018-07-01 16:17:40 +00: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 02:16:54 +00:00
}
2018-06-05 16:50:21 +00:00
} else {
_mark_line_as_unsafe ( op - > line ) ;
2018-05-30 02:16:54 +00: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 01:11:03 +00:00
op - > arguments . write [ 1 ] = id ;
2018-05-30 02:16:54 +00: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 16:50:21 +00:00
_mark_line_as_unsafe ( op - > line ) ;
2018-05-30 02:16:54 +00:00
break ;
}
if ( check_types & & index_type . has_type ) {
if ( base_type . kind = = DataType : : BUILTIN ) {
// Check if indexing is valid
2019-01-15 20:18:03 +00:00
bool error = index_type . kind ! = DataType : : BUILTIN & & base_type . builtin_type ! = Variant : : DICTIONARY ;
2018-05-30 02:16:54 +00:00
if ( ! error ) {
switch ( base_type . builtin_type ) {
// Expect int or real as index
2020-02-17 21:06:54 +00:00
case Variant : : PACKED_BYTE_ARRAY :
case Variant : : PACKED_COLOR_ARRAY :
Variant: Added 64-bit packed arrays, renamed Variant::REAL to FLOAT.
- Renames PackedIntArray to PackedInt32Array.
- Renames PackedFloatArray to PackedFloat32Array.
- Adds PackedInt64Array and PackedFloat64Array.
- Renames Variant::REAL to Variant::FLOAT for consistency.
Packed arrays are for storing large amount of data and creating stuff like
meshes, buffers. textures, etc. Forcing them to be 64 is a huge waste of
memory. That said, many users requested the ability to have 64 bits packed
arrays for their games, so this is just an optional added type.
For Variant, the float datatype is always 64 bits, and exposed as `float`.
We still have `real_t` which is the datatype that can change from 32 to 64
bits depending on a compile flag (not entirely working right now, but that's
the idea). It affects math related datatypes and code only.
Neither Variant nor PackedArray make use of real_t, which is only intended
for math precision, so the term is removed from there to keep only float.
2020-02-24 18:20:53 +00:00
case Variant : : PACKED_INT32_ARRAY :
case Variant : : PACKED_INT64_ARRAY :
case Variant : : PACKED_FLOAT32_ARRAY :
case Variant : : PACKED_FLOAT64_ARRAY :
2020-02-17 21:06:54 +00:00
case Variant : : PACKED_STRING_ARRAY :
case Variant : : PACKED_VECTOR2_ARRAY :
case Variant : : PACKED_VECTOR3_ARRAY :
2018-05-30 02:16:54 +00:00
case Variant : : ARRAY :
case Variant : : STRING : {
Variant: Added 64-bit packed arrays, renamed Variant::REAL to FLOAT.
- Renames PackedIntArray to PackedInt32Array.
- Renames PackedFloatArray to PackedFloat32Array.
- Adds PackedInt64Array and PackedFloat64Array.
- Renames Variant::REAL to Variant::FLOAT for consistency.
Packed arrays are for storing large amount of data and creating stuff like
meshes, buffers. textures, etc. Forcing them to be 64 is a huge waste of
memory. That said, many users requested the ability to have 64 bits packed
arrays for their games, so this is just an optional added type.
For Variant, the float datatype is always 64 bits, and exposed as `float`.
We still have `real_t` which is the datatype that can change from 32 to 64
bits depending on a compile flag (not entirely working right now, but that's
the idea). It affects math related datatypes and code only.
Neither Variant nor PackedArray make use of real_t, which is only intended
for math precision, so the term is removed from there to keep only float.
2020-02-24 18:20:53 +00:00
error = index_type . builtin_type ! = Variant : : INT & & index_type . builtin_type ! = Variant : : FLOAT ;
2018-05-30 02:16:54 +00:00
} 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 : {
Variant: Added 64-bit packed arrays, renamed Variant::REAL to FLOAT.
- Renames PackedIntArray to PackedInt32Array.
- Renames PackedFloatArray to PackedFloat32Array.
- Adds PackedInt64Array and PackedFloat64Array.
- Renames Variant::REAL to Variant::FLOAT for consistency.
Packed arrays are for storing large amount of data and creating stuff like
meshes, buffers. textures, etc. Forcing them to be 64 is a huge waste of
memory. That said, many users requested the ability to have 64 bits packed
arrays for their games, so this is just an optional added type.
For Variant, the float datatype is always 64 bits, and exposed as `float`.
We still have `real_t` which is the datatype that can change from 32 to 64
bits depending on a compile flag (not entirely working right now, but that's
the idea). It affects math related datatypes and code only.
Neither Variant nor PackedArray make use of real_t, which is only intended
for math precision, so the term is removed from there to keep only float.
2020-02-24 18:20:53 +00:00
error = index_type . builtin_type ! = Variant : : INT & & index_type . builtin_type ! = Variant : : FLOAT & &
2018-05-30 02:16:54 +00:00
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 ;
2019-04-09 15:08:36 +00:00
default : {
}
2018-05-30 02:16:54 +00:00
}
}
if ( error ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Invalid index type ( " + index_type . to_string ( ) + " ) for base \" " + base_type . to_string ( ) + " \" . " ,
2018-05-30 02:16:54 +00:00
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 :
2020-02-17 21:06:54 +00:00
case Variant : : PACKED_BYTE_ARRAY :
case Variant : : PACKED_COLOR_ARRAY :
Variant: Added 64-bit packed arrays, renamed Variant::REAL to FLOAT.
- Renames PackedIntArray to PackedInt32Array.
- Renames PackedFloatArray to PackedFloat32Array.
- Adds PackedInt64Array and PackedFloat64Array.
- Renames Variant::REAL to Variant::FLOAT for consistency.
Packed arrays are for storing large amount of data and creating stuff like
meshes, buffers. textures, etc. Forcing them to be 64 is a huge waste of
memory. That said, many users requested the ability to have 64 bits packed
arrays for their games, so this is just an optional added type.
For Variant, the float datatype is always 64 bits, and exposed as `float`.
We still have `real_t` which is the datatype that can change from 32 to 64
bits depending on a compile flag (not entirely working right now, but that's
the idea). It affects math related datatypes and code only.
Neither Variant nor PackedArray make use of real_t, which is only intended
for math precision, so the term is removed from there to keep only float.
2020-02-24 18:20:53 +00:00
case Variant : : PACKED_INT32_ARRAY :
case Variant : : PACKED_INT64_ARRAY :
case Variant : : PACKED_FLOAT32_ARRAY :
case Variant : : PACKED_FLOAT64_ARRAY :
2020-02-17 21:06:54 +00:00
case Variant : : PACKED_STRING_ARRAY :
case Variant : : PACKED_VECTOR2_ARRAY :
case Variant : : PACKED_VECTOR3_ARRAY : {
2018-05-30 02:16:54 +00:00
break ;
}
default : {
2020-02-19 19:27:19 +00:00
Callable : : CallError err ;
2018-05-30 02:16:54 +00:00
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 ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Can't get index \" " + String ( cn - > value ) + " \" on base \" " +
base_type . to_string ( ) + " \" . " ,
2018-05-30 02:16:54 +00:00
op - > line ) ;
return DataType ( ) ;
}
} break ;
}
2018-06-05 16:50:21 +00:00
} else {
_mark_line_as_unsafe ( op - > line ) ;
2018-05-30 02:16:54 +00:00
}
} else if ( ! for_completion & & ( index_type . kind ! = DataType : : BUILTIN | | index_type . builtin_type ! = Variant : : STRING ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Only strings can be used as an index in the base type \" " + base_type . to_string ( ) + " \" . " , op - > line ) ;
2018-05-30 02:16:54 +00:00
return DataType ( ) ;
}
}
2019-09-02 12:38:32 +00:00
if ( check_types & & ! node_type . has_type & & base_type . kind = = DataType : : BUILTIN ) {
2018-05-30 02:16:54 +00:00
// 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 :
Variant: Added 64-bit packed arrays, renamed Variant::REAL to FLOAT.
- Renames PackedIntArray to PackedInt32Array.
- Renames PackedFloatArray to PackedFloat32Array.
- Adds PackedInt64Array and PackedFloat64Array.
- Renames Variant::REAL to Variant::FLOAT for consistency.
Packed arrays are for storing large amount of data and creating stuff like
meshes, buffers. textures, etc. Forcing them to be 64 is a huge waste of
memory. That said, many users requested the ability to have 64 bits packed
arrays for their games, so this is just an optional added type.
For Variant, the float datatype is always 64 bits, and exposed as `float`.
We still have `real_t` which is the datatype that can change from 32 to 64
bits depending on a compile flag (not entirely working right now, but that's
the idea). It affects math related datatypes and code only.
Neither Variant nor PackedArray make use of real_t, which is only intended
for math precision, so the term is removed from there to keep only float.
2020-02-24 18:20:53 +00:00
case Variant : : FLOAT :
2018-05-30 02:16:54 +00:00
case Variant : : NODE_PATH :
case Variant : : _RID : {
2019-08-23 18:51:42 +00:00
_set_error ( " Can't index on a value of type \" " + base_type . to_string ( ) + " \" . " , op - > line ) ;
2018-05-30 02:16:54 +00:00
return DataType ( ) ;
} break ;
// Return int
2020-02-17 21:06:54 +00:00
case Variant : : PACKED_BYTE_ARRAY :
Variant: Added 64-bit packed arrays, renamed Variant::REAL to FLOAT.
- Renames PackedIntArray to PackedInt32Array.
- Renames PackedFloatArray to PackedFloat32Array.
- Adds PackedInt64Array and PackedFloat64Array.
- Renames Variant::REAL to Variant::FLOAT for consistency.
Packed arrays are for storing large amount of data and creating stuff like
meshes, buffers. textures, etc. Forcing them to be 64 is a huge waste of
memory. That said, many users requested the ability to have 64 bits packed
arrays for their games, so this is just an optional added type.
For Variant, the float datatype is always 64 bits, and exposed as `float`.
We still have `real_t` which is the datatype that can change from 32 to 64
bits depending on a compile flag (not entirely working right now, but that's
the idea). It affects math related datatypes and code only.
Neither Variant nor PackedArray make use of real_t, which is only intended
for math precision, so the term is removed from there to keep only float.
2020-02-24 18:20:53 +00:00
case Variant : : PACKED_INT32_ARRAY :
case Variant : : PACKED_INT64_ARRAY : {
2018-05-30 02:16:54 +00:00
result . builtin_type = Variant : : INT ;
} break ;
// Return real
Variant: Added 64-bit packed arrays, renamed Variant::REAL to FLOAT.
- Renames PackedIntArray to PackedInt32Array.
- Renames PackedFloatArray to PackedFloat32Array.
- Adds PackedInt64Array and PackedFloat64Array.
- Renames Variant::REAL to Variant::FLOAT for consistency.
Packed arrays are for storing large amount of data and creating stuff like
meshes, buffers. textures, etc. Forcing them to be 64 is a huge waste of
memory. That said, many users requested the ability to have 64 bits packed
arrays for their games, so this is just an optional added type.
For Variant, the float datatype is always 64 bits, and exposed as `float`.
We still have `real_t` which is the datatype that can change from 32 to 64
bits depending on a compile flag (not entirely working right now, but that's
the idea). It affects math related datatypes and code only.
Neither Variant nor PackedArray make use of real_t, which is only intended
for math precision, so the term is removed from there to keep only float.
2020-02-24 18:20:53 +00:00
case Variant : : PACKED_FLOAT32_ARRAY :
case Variant : : PACKED_FLOAT64_ARRAY :
2018-05-30 02:16:54 +00:00
case Variant : : VECTOR2 :
case Variant : : VECTOR3 :
case Variant : : QUAT : {
Variant: Added 64-bit packed arrays, renamed Variant::REAL to FLOAT.
- Renames PackedIntArray to PackedInt32Array.
- Renames PackedFloatArray to PackedFloat32Array.
- Adds PackedInt64Array and PackedFloat64Array.
- Renames Variant::REAL to Variant::FLOAT for consistency.
Packed arrays are for storing large amount of data and creating stuff like
meshes, buffers. textures, etc. Forcing them to be 64 is a huge waste of
memory. That said, many users requested the ability to have 64 bits packed
arrays for their games, so this is just an optional added type.
For Variant, the float datatype is always 64 bits, and exposed as `float`.
We still have `real_t` which is the datatype that can change from 32 to 64
bits depending on a compile flag (not entirely working right now, but that's
the idea). It affects math related datatypes and code only.
Neither Variant nor PackedArray make use of real_t, which is only intended
for math precision, so the term is removed from there to keep only float.
2020-02-24 18:20:53 +00:00
result . builtin_type = Variant : : FLOAT ;
2018-05-30 02:16:54 +00:00
} break ;
// Return color
2020-02-17 21:06:54 +00:00
case Variant : : PACKED_COLOR_ARRAY : {
2018-05-30 02:16:54 +00:00
result . builtin_type = Variant : : COLOR ;
} break ;
// Return string
2020-02-17 21:06:54 +00:00
case Variant : : PACKED_STRING_ARRAY :
2018-05-30 02:16:54 +00:00
case Variant : : STRING : {
result . builtin_type = Variant : : STRING ;
} break ;
// Return Vector2
2020-02-17 21:06:54 +00:00
case Variant : : PACKED_VECTOR2_ARRAY :
2018-05-30 02:16:54 +00:00
case Variant : : TRANSFORM2D :
case Variant : : RECT2 : {
result . builtin_type = Variant : : VECTOR2 ;
} break ;
// Return Vector3
2020-02-17 21:06:54 +00:00
case Variant : : PACKED_VECTOR3_ARRAY :
2018-05-30 02:16:54 +00:00
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 ;
2019-04-09 15:08:36 +00:00
default : {
}
2018-05-30 02:16:54 +00:00
}
2019-09-24 21:03:50 +00:00
node_type = _resolve_type ( node_type , p_node - > line ) ;
p_node - > set_datatype ( node_type ) ;
2018-05-30 02:16:54 +00:00
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 01:23:40 +00: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 02:16:54 +00: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 01:23:40 +00:00
if ( native = = StringName ( ) ) {
// Empty native class, might happen in some Script implementations
// Just ignore it
return false ;
}
2018-05-30 02:16:54 +00: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 ;
2019-08-09 04:49:33 +00:00
ERR_FAIL_V_MSG ( false , " Parser bug: Class ' " + String ( native ) + " ' not found. " ) ;
2018-05-30 02:16:54 +00:00
}
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 01:11:03 +00:00
par_types . write [ i - 1 ] = _reduce_node_type ( p_call - > arguments [ i ] ) ;
2018-05-30 02:16:54 +00:00
}
if ( error_set ) return DataType ( ) ;
2020-01-09 12:03:09 +00:00
// Special case: check copy constructor. Those are defined implicitly in Variant.
if ( par_types . size ( ) = = 1 ) {
if ( ! par_types [ 0 ] . has_type | | ( par_types [ 0 ] . kind = = DataType : : BUILTIN & & par_types [ 0 ] . builtin_type = = tn - > vtype ) ) {
DataType result ;
result . has_type = true ;
result . kind = DataType : : BUILTIN ;
result . builtin_type = tn - > vtype ;
return result ;
}
}
2018-05-30 02:16:54 +00:00
bool match = false ;
List < MethodInfo > constructors ;
Variant : : get_constructor_list ( tn - > vtype , & constructors ) ;
2019-02-12 20:10:08 +00:00
PropertyInfo return_type2 ;
2018-05-30 02:16:54 +00:00
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 ;
}
2019-03-04 11:25:59 +00:00
if ( ! _is_type_compatible ( arg_type , par_types [ i ] , true ) ) {
2018-05-30 02:16:54 +00:00
types_match = false ;
break ;
2018-07-01 16:17:40 +00:00
} else {
# ifdef DEBUG_ENABLED
Variant: Added 64-bit packed arrays, renamed Variant::REAL to FLOAT.
- Renames PackedIntArray to PackedInt32Array.
- Renames PackedFloatArray to PackedFloat32Array.
- Adds PackedInt64Array and PackedFloat64Array.
- Renames Variant::REAL to Variant::FLOAT for consistency.
Packed arrays are for storing large amount of data and creating stuff like
meshes, buffers. textures, etc. Forcing them to be 64 is a huge waste of
memory. That said, many users requested the ability to have 64 bits packed
arrays for their games, so this is just an optional added type.
For Variant, the float datatype is always 64 bits, and exposed as `float`.
We still have `real_t` which is the datatype that can change from 32 to 64
bits depending on a compile flag (not entirely working right now, but that's
the idea). It affects math related datatypes and code only.
Neither Variant nor PackedArray make use of real_t, which is only intended
for math precision, so the term is removed from there to keep only float.
2020-02-24 18:20:53 +00:00
if ( arg_type . kind = = DataType : : BUILTIN & & arg_type . builtin_type = = Variant : : INT & & par_types [ i ] . kind = = DataType : : BUILTIN & & par_types [ i ] . builtin_type = = Variant : : FLOAT ) {
2018-07-01 16:17:40 +00:00
_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 02:16:54 +00:00
}
}
if ( types_match ) {
match = true ;
2019-02-12 20:10:08 +00:00
return_type2 = mi . return_val ;
2018-05-30 02:16:54 +00:00
break ;
}
}
if ( match ) {
2019-02-12 20:10:08 +00:00
return _type_from_property ( return_type2 , false ) ;
2018-05-30 02:16:54 +00:00
} 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 16:17:40 +00:00
// Check all arguments beforehand to solve warnings
for ( int i = 1 ; i < p_call - > arguments . size ( ) ; i + + ) {
_reduce_node_type ( p_call - > arguments [ i ] ) ;
}
2018-05-30 02:16:54 +00: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 16:17:40 +00:00
// 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 ] ) ;
}
2018-05-30 02:16:54 +00: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 16:50:21 +00:00
_mark_line_as_unsafe ( p_call - > line ) ;
2018-05-30 02:16:54 +00:00
return DataType ( ) ;
}
if ( base_type . kind = = DataType : : BUILTIN ) {
2020-02-19 19:27:19 +00:00
Callable : : CallError err ;
2018-05-30 02:16:54 +00:00
Variant tmp = Variant : : construct ( base_type . builtin_type , NULL , 0 , err ) ;
if ( check_types ) {
if ( ! tmp . has_method ( callee_name ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " The method \" " + callee_name + " \" isn't declared on base \" " + base_type . to_string ( ) + " \" . " , p_call - > line ) ;
2018-05-30 02:16:54 +00: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-09 22:50:47 +00:00
bool rets = false ;
2018-05-30 02:16:54 +00:00
return_type . has_type = true ;
return_type . kind = DataType : : BUILTIN ;
2018-10-09 22:50:47 +00: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 02:16:54 +00: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 ) ;
2019-01-15 21:15:19 +00:00
return_type = original_type ;
return_type . is_meta_type = false ;
2019-02-13 08:23:29 +00:00
valid = true ; // There's always an initializer, we can assume this is true
2018-05-30 02:16:54 +00:00
}
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 05:55:52 +00:00
# ifdef DEBUG_ENABLED
2018-05-30 02:16:54 +00:00
if ( p_call - > arguments [ 0 ] - > type = = Node : : TYPE_SELF ) {
2019-08-23 18:51:42 +00:00
_set_error ( " The method \" " + callee_name + " \" isn't declared in the current class. " , p_call - > line ) ;
2018-05-30 02:16:54 +00:00
return DataType ( ) ;
}
2018-07-01 16:17:40 +00: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 02:16:54 +00:00
_mark_line_as_unsafe ( p_call - > line ) ;
2018-07-01 16:17:40 +00:00
# endif // DEBUG_ENABLED
2018-05-30 02:16:54 +00:00
return DataType ( ) ;
}
2018-06-19 05:55:52 +00:00
# ifdef DEBUG_ENABLED
2018-05-30 02:16:54 +00:00
if ( current_function & & ! for_completion & & ! is_static & & p_call - > arguments [ 0 ] - > type = = Node : : TYPE_SELF & & current_function - > _static ) {
2019-04-04 20:00:16 +00:00
_set_error ( " Can't call non-static function from a static function. " , p_call - > line ) ;
return DataType ( ) ;
2018-05-30 02:16:54 +00:00
}
if ( check_types & & ! is_static & & ! is_initializer & & base_type . is_meta_type ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Non-static function \" " + String ( callee_name ) + " \" can only be called from an instance. " , p_call - > line ) ;
2018-05-30 02:16:54 +00:00
return DataType ( ) ;
}
2018-07-01 16:17:40 +00: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 02:16:54 +00:00
} break ;
}
2019-01-15 21:15:19 +00:00
# ifdef DEBUG_ENABLED
2018-05-30 02:16:54 +00:00
if ( ! check_types ) {
return return_type ;
}
if ( arg_count < arg_types . size ( ) - default_args_count ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Too few arguments for \" " + callee_name + " () \" call. Expected at least " + itos ( arg_types . size ( ) - default_args_count ) + " . " , p_call - > line ) ;
2018-05-30 02:16:54 +00:00
return return_type ;
}
if ( ! is_vararg & & arg_count > arg_types . size ( ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Too many arguments for \" " + callee_name + " () \" call. Expected at most " + itos ( arg_types . size ( ) ) + " . " , p_call - > line ) ;
2018-05-30 02:16:54 +00:00
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 16:17:40 +00:00
DataType arg_type = arg_types [ i - arg_diff ] ;
2018-06-05 16:50:21 +00:00
if ( ! par_type . has_type ) {
_mark_line_as_unsafe ( p_call - > line ) ;
2018-07-01 16:17:40 +00:00
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 ] ) ) ) ;
}
2019-03-04 11:25:59 +00:00
} else if ( ! _is_type_compatible ( arg_types [ i - arg_diff ] , par_type , true ) ) {
2018-06-05 16:50:21 +00:00
// Supertypes are acceptable for dynamic compliance
if ( ! _is_type_compatible ( par_type , arg_types [ i - arg_diff ] ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " At \" " + callee_name + " () \" call, argument " + itos ( i - arg_diff + 1 ) + " . Assigned type ( " +
2018-06-05 16:50:21 +00:00
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 16:17:40 +00:00
} else {
Variant: Added 64-bit packed arrays, renamed Variant::REAL to FLOAT.
- Renames PackedIntArray to PackedInt32Array.
- Renames PackedFloatArray to PackedFloat32Array.
- Adds PackedInt64Array and PackedFloat64Array.
- Renames Variant::REAL to Variant::FLOAT for consistency.
Packed arrays are for storing large amount of data and creating stuff like
meshes, buffers. textures, etc. Forcing them to be 64 is a huge waste of
memory. That said, many users requested the ability to have 64 bits packed
arrays for their games, so this is just an optional added type.
For Variant, the float datatype is always 64 bits, and exposed as `float`.
We still have `real_t` which is the datatype that can change from 32 to 64
bits depending on a compile flag (not entirely working right now, but that's
the idea). It affects math related datatypes and code only.
Neither Variant nor PackedArray make use of real_t, which is only intended
for math precision, so the term is removed from there to keep only float.
2020-02-24 18:20:53 +00:00
if ( arg_type . kind = = DataType : : BUILTIN & & arg_type . builtin_type = = Variant : : INT & & par_type . kind = = DataType : : BUILTIN & & par_type . builtin_type = = Variant : : FLOAT ) {
2018-07-01 16:17:40 +00:00
_add_warning ( GDScriptWarning : : NARROWING_CONVERSION , p_call - > line , callee_name ) ;
}
2018-05-30 02:16:54 +00:00
}
}
2019-01-15 21:15:19 +00:00
# endif // DEBUG_ENABLED
2018-05-30 02:16:54 +00: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 + + ) {
2019-01-16 18:02:56 +00:00
if ( base - > variables [ i ] . identifier = = p_member ) {
r_member_type = base - > variables [ i ] . data_type ;
base - > variables . write [ i ] . usages + = 1 ;
2018-05-30 02:16:54 +00:00
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 01:23:40 +00:00
if ( gds . is_null ( ) | | ! gds - > is_valid ( ) ) {
// GDScript wasn't properly compíled, don't bother trying
return false ;
}
2018-05-30 02:16:54 +00: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 01:23:40 +00:00
if ( native = = StringName ( ) ) {
// Empty native class, might happen in some Script implementations
// Just ignore it
return false ;
}
2018-05-30 02:16:54 +00:00
// Check ClassDB
if ( ! ClassDB : : class_exists ( native ) ) {
native = " _ " + native . operator String ( ) ;
}
if ( ! ClassDB : : class_exists ( native ) ) {
if ( ! check_types ) return false ;
2019-08-23 18:51:42 +00:00
ERR_FAIL_V_MSG ( false , " Parser bug: Class \" " + String ( native ) + " \" not found. " ) ;
2018-05-30 02:16:54 +00:00
}
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 ;
}
2019-01-16 18:02:56 +00:00
GDScriptParser : : DataType GDScriptParser : : _reduce_identifier_type ( const DataType * p_base_type , const StringName & p_identifier , int p_line , bool p_is_indexing ) {
2018-05-30 02:16:54 +00:00
if ( p_base_type & & ! p_base_type - > has_type ) {
return DataType ( ) ;
}
DataType base_type ;
2019-01-16 18:02:56 +00:00
DataType member_type ;
2018-05-30 02:16:54 +00:00
if ( ! p_base_type ) {
2018-07-25 13:20:11 +00:00
base_type . has_type = true ;
base_type . is_constant = true ;
base_type . kind = DataType : : CLASS ;
2019-01-16 18:02:56 +00:00
base_type . class_type = current_class ;
2018-07-25 13:20:11 +00:00
} else {
base_type = DataType ( * p_base_type ) ;
2018-07-01 16:17:40 +00:00
}
2018-07-25 13:20:11 +00:00
if ( _get_member_type ( base_type , p_identifier , member_type ) ) {
return member_type ;
}
2019-01-16 18:02:56 +00:00
if ( p_is_indexing ) {
// Don't look for globals since this is an indexed identifier
return DataType ( ) ;
}
2018-07-25 13:20:11 +00:00
if ( ! p_base_type ) {
// Possibly this is a global, check before failing
2018-05-30 02:16:54 +00: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 21:18:53 +00:00
if ( outer_class - > constant_expressions . has ( p_identifier ) ) {
return outer_class - > constant_expressions [ p_identifier ] . type ;
}
2018-05-30 02:16:54 +00: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 ;
2019-03-05 21:19:02 +00:00
result . is_constant = true ;
2018-05-30 02:16:54 +00:00
result . is_meta_type = true ;
Ref < GDScript > gds = scr ;
if ( gds . is_valid ( ) ) {
if ( ! gds - > is_valid ( ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " The class \" " + p_identifier + " \" couldn't be fully loaded (script error or cyclic dependency). " ) ;
2018-05-30 02:16:54 +00:00
return DataType ( ) ;
}
result . kind = DataType : : GDSCRIPT ;
} else {
result . kind = DataType : : SCRIPT ;
}
return result ;
}
2019-08-23 18:51:42 +00:00
_set_error ( " The class \" " + p_identifier + " \" was found in global scope, but its script couldn't be loaded. " ) ;
2018-05-30 02:16:54 +00:00
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 ;
2019-03-05 21:19:02 +00:00
result . is_constant = true ;
2018-05-30 02:16:54 +00:00
result . script_type = singleton ;
Ref < GDScript > gds = singleton ;
if ( gds . is_valid ( ) ) {
if ( ! gds - > is_valid ( ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Couldn't fully load the singleton script \" " + p_identifier + " \" (possible cyclic reference or parse error). " , p_line ) ;
2018-05-30 02:16:54 +00:00
return DataType ( ) ;
}
result . kind = DataType : : GDSCRIPT ;
} else {
result . kind = DataType : : SCRIPT ;
}
}
}
}
2018-06-05 16:50:21 +00:00
// This means looking in the current class, which type is always known
2019-08-23 18:51:42 +00:00
_set_error ( " The identifier \" " + p_identifier . operator String ( ) + " \" isn't declared in the current scope. " , p_line ) ;
2018-05-30 02:16:54 +00:00
}
2018-06-05 16:50:21 +00:00
2018-07-01 16:17:40 +00: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 16:50:21 +00:00
_mark_line_as_unsafe ( p_line ) ;
2018-05-30 02:16:54 +00:00
return DataType ( ) ;
}
void GDScriptParser : : _check_class_level_types ( ClassNode * p_class ) {
2020-01-10 22:43:33 +00:00
// Names of internal object properties that we check to avoid overriding them.
// "__meta__" could also be in here, but since it doesn't really affect object metadata,
// it is okay to override it on script.
StringName script_name = CoreStringNames : : get_singleton ( ) - > _script ;
2018-06-05 16:50:21 +00:00
_mark_line_as_safe ( p_class - > line ) ;
2018-05-30 02:16:54 +00: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 16:50:21 +00:00
_mark_line_as_safe ( c . expression - > line ) ;
2018-05-30 02:16:54 +00:00
DataType cont = _resolve_type ( c . type , c . expression - > line ) ;
DataType expr = _resolve_type ( c . expression - > get_datatype ( ) , c . expression - > line ) ;
2019-01-23 20:45:33 +00:00
if ( check_types & & ! _is_type_compatible ( cont , expr ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " The constant value type ( " + expr . to_string ( ) + " ) isn't compatible with declared type ( " + cont . to_string ( ) + " ). " ,
2018-05-30 02:16:54 +00:00
c . expression - > line ) ;
return ;
}
expr . is_constant = true ;
c . type = expr ;
c . expression - > set_datatype ( expr ) ;
2018-09-10 14:06:03 +00:00
DataType tmp ;
2020-01-10 22:43:33 +00:00
const StringName & constant_name = E - > key ( ) ;
if ( constant_name = = script_name | | _get_member_type ( p_class - > base_type , constant_name , tmp ) ) {
_set_error ( " The member \" " + String ( constant_name ) + " \" already exists in a parent class. " , c . expression - > line ) ;
2018-09-10 14:06:03 +00:00
return ;
}
2018-05-30 02:16:54 +00: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 01:11:03 +00:00
ClassNode : : Member & v = p_class - > variables . write [ i ] ;
2018-05-30 02:16:54 +00:00
DataType tmp ;
2020-01-10 22:43:33 +00:00
if ( v . identifier = = script_name | | _get_member_type ( p_class - > base_type , v . identifier , tmp ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " The member \" " + String ( v . identifier ) + " \" already exists in a parent class. " , v . line ) ;
2018-05-30 02:16:54 +00:00
return ;
}
2018-06-05 16:50:21 +00:00
_mark_line_as_safe ( v . line ) ;
2018-05-30 02:16:54 +00:00
v . data_type = _resolve_type ( v . data_type , v . line ) ;
if ( v . expression ) {
DataType expr_type = _reduce_node_type ( v . expression ) ;
2019-01-23 20:45:33 +00:00
if ( check_types & & ! _is_type_compatible ( v . data_type , expr_type ) ) {
2018-06-05 16:50:21 +00:00
// Try supertype test
if ( _is_type_compatible ( expr_type , v . data_type ) ) {
_mark_line_as_unsafe ( v . line ) ;
2019-03-04 11:25:59 +00:00
} else {
// Try with implicit conversion
if ( v . data_type . kind ! = DataType : : BUILTIN | | ! _is_type_compatible ( v . data_type , expr_type , true ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " The assigned expression's type ( " + expr_type . to_string ( ) + " ) doesn't match the variable's type ( " +
2019-03-04 11:25:59 +00:00
v . data_type . to_string ( ) + " ). " ,
v . line ) ;
return ;
}
2019-05-19 10:34:40 +00:00
// Replace assignment with implicit conversion
2019-03-04 11:25:59 +00:00
BuiltInFunctionNode * convert = alloc_node < BuiltInFunctionNode > ( ) ;
convert - > line = v . line ;
convert - > function = GDScriptFunctions : : TYPE_CONVERT ;
ConstantNode * tgt_type = alloc_node < ConstantNode > ( ) ;
tgt_type - > line = v . line ;
tgt_type - > value = ( int ) v . data_type . builtin_type ;
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 ) ;
v . expression = convert_call ;
v . initial_assignment - > arguments . write [ 1 ] = convert_call ;
2018-06-05 16:50:21 +00:00
}
2018-05-30 02:16:54 +00:00
}
2018-06-19 05:55:52 +00:00
if ( v . data_type . infer_type ) {
if ( ! expr_type . has_type ) {
2019-08-23 18:51:42 +00:00
_set_error ( " The assigned value doesn't have a set type; the variable type can't be inferred. " , v . line ) ;
2018-06-19 05:55:52 +00:00
return ;
}
v . data_type = expr_type ;
v . data_type . is_constant = false ;
}
2018-05-30 02:16:54 +00:00
}
// Check export hint
2018-06-05 16:50:21 +00:00
if ( v . data_type . has_type & & v . _export . type ! = Variant : : NIL ) {
2018-05-30 02:16:54 +00:00
DataType export_type = _type_from_property ( v . _export ) ;
2019-03-04 11:25:59 +00:00
if ( ! _is_type_compatible ( v . data_type , export_type , true ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " The export hint's type ( " + export_type . to_string ( ) + " ) doesn't match the variable's type ( " +
2018-05-30 02:16:54 +00:00
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 ] ;
2019-01-16 03:38:37 +00:00
if ( setter - > get_required_argument_count ( ) ! = 1 & &
! ( setter - > get_required_argument_count ( ) = = 0 & & setter - > default_values . size ( ) > 0 ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " The setter function needs to receive exactly 1 argument. See \" " + setter - > name +
" () \" definition at line " + itos ( setter - > line ) + " . " ,
2018-05-30 02:16:54 +00:00
v . line ) ;
return ;
}
if ( ! _is_type_compatible ( v . data_type , setter - > argument_types [ 0 ] ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " The setter argument's 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 ) + " . " ,
2018-05-30 02:16:54 +00:00
v . line ) ;
return ;
}
continue ;
}
if ( v . getter = = p_class - > functions [ j ] - > name ) {
found_getter = true ;
FunctionNode * getter = p_class - > functions [ j ] ;
2019-01-16 03:38:37 +00:00
if ( getter - > get_required_argument_count ( ) ! = 0 ) {
2019-08-23 18:51:42 +00:00
_set_error ( " The getter function can't receive arguments. See \" " + getter - > name +
" () \" definition at line " + itos ( getter - > line ) + " . " ,
2018-05-30 02:16:54 +00:00
v . line ) ;
return ;
}
if ( ! _is_type_compatible ( v . data_type , getter - > get_datatype ( ) ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " The getter return type ( " + getter - > get_datatype ( ) . to_string ( ) +
2018-05-30 02:16:54 +00:00
" ) doesn't match the variable's type ( " + v . data_type . to_string ( ) +
2019-08-23 18:51:42 +00:00
" ). See \" " + getter - > name + " () \" definition at line " + itos ( getter - > line ) + " . " ,
2018-05-30 02:16:54 +00:00
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 ] ;
2019-08-23 18:51:42 +00:00
_set_error ( " The setter can't be a static function. See \" " + setter - > name + " () \" definition at line " + itos ( setter - > line ) + " . " , v . line ) ;
2018-05-30 02:16:54 +00:00
return ;
}
if ( v . getter = = p_class - > static_functions [ j ] - > name ) {
FunctionNode * getter = p_class - > static_functions [ j ] ;
2019-08-23 18:51:42 +00:00
_set_error ( " The getter can't be a static function. See \" " + getter - > name + " () \" definition at line " + itos ( getter - > line ) + " . " , v . line ) ;
2018-05-30 02:16:54 +00:00
return ;
}
}
if ( ! found_setter & & v . setter ! = StringName ( ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " The setter function isn't defined. " , v . line ) ;
2018-05-30 02:16:54 +00:00
return ;
}
if ( ! found_getter & & v . getter ! = StringName ( ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " The getter function isn't defined. " , v . line ) ;
2018-05-30 02:16:54 +00:00
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-25 23:48:22 +00: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 02:16:54 +00: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-25 23:48:22 +00:00
if ( p_function - > argument_types [ i ] . infer_type ) {
def_type . is_constant = false ;
p_function - > argument_types . write [ i ] = def_type ;
} else {
2020-01-09 18:42:31 +00:00
p_function - > argument_types . write [ i ] = _resolve_type ( p_function - > argument_types [ i ] , p_function - > line ) ;
2018-07-25 23:48:22 +00:00
2019-03-04 11:25:59 +00:00
if ( ! _is_type_compatible ( p_function - > argument_types [ i ] , def_type , true ) ) {
2018-07-25 23:48:22 +00:00
String arg_name = p_function - > arguments [ i ] ;
_set_error ( " Value type ( " + def_type . to_string ( ) + " ) doesn't match the type of argument ' " +
2020-01-09 18:50:06 +00:00
arg_name + " ' ( " + p_function - > argument_types [ i ] . to_string ( ) + " ). " ,
2018-07-25 23:48:22 +00:00
p_function - > line ) ;
}
2018-05-30 02:16:54 +00:00
}
}
2018-07-01 16:17:40 +00:00
# ifdef DEBUG_ENABLED
2018-08-21 23:19:35 +00:00
if ( p_function - > arguments_usage [ i ] = = 0 & & ! p_function - > arguments [ i ] . operator String ( ) . begins_with ( " _ " ) ) {
2018-07-01 16:17:40 +00:00
_add_warning ( GDScriptWarning : : UNUSED_ARGUMENT , p_function - > line , p_function - > name , p_function - > arguments [ i ] . operator String ( ) ) ;
}
2019-04-15 13:51:52 +00:00
for ( int j = 0 ; j < current_class - > variables . size ( ) ; j + + ) {
if ( current_class - > variables [ j ] . identifier = = p_function - > arguments [ i ] ) {
_add_warning ( GDScriptWarning : : SHADOWED_VARIABLE , p_function - > line , p_function - > arguments [ i ] , itos ( current_class - > variables [ j ] . line ) ) ;
}
}
2018-07-01 16:17:40 +00:00
# endif // DEBUG_ENABLED
2018-05-30 02:16:54 +00:00
}
if ( ! ( p_function - > name = = " _init " ) ) {
// Signature for the initializer may vary
2018-07-25 15:12:46 +00:00
# ifdef DEBUG_ENABLED
2018-05-30 02:16:54 +00: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 15:12:46 +00: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 02:16:54 +00: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 15:12:46 +00: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 ( ) ) {
2019-02-12 20:10:08 +00:00
int j = 0 ;
2018-07-25 15:12:46 +00:00
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 ;
2019-02-12 20:10:08 +00:00
if ( j = = arg_types . size ( ) - default_arg_count ) {
2018-07-25 15:12:46 +00:00
parent_signature + = " =default " ;
}
2019-02-12 20:10:08 +00:00
j + + ;
2018-07-25 15:12:46 +00:00
}
}
parent_signature + = " ) " ;
2019-08-23 18:51:42 +00:00
_set_error ( " The function signature doesn't match the parent. Parent signature is: \" " + parent_signature + " \" . " , p_function - > line ) ;
2018-05-30 02:16:54 +00:00
return ;
}
}
2018-07-25 15:12:46 +00:00
# endif // DEBUG_ENABLED
2018-05-30 02:16:54 +00:00
} else {
if ( p_function - > return_type . has_type & & ( p_function - > return_type . kind ! = DataType : : BUILTIN | | p_function - > return_type . builtin_type ! = Variant : : NIL ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " The constructor can't return a value. " , p_function - > line ) ;
2018-05-30 02:16:54 +00:00
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 ) {
2019-08-23 18:51:42 +00:00
_set_error ( " A non-void function must return a value in all possible paths. " , p_function - > line ) ;
2018-05-30 02:16:54 +00:00
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 16:17:40 +00:00
p_function - > return_type . may_yield = true ;
2018-05-30 02:16:54 +00:00
}
2019-04-15 13:51:52 +00:00
# ifdef DEBUG_ENABLED
for ( Map < StringName , LocalVarNode * > : : Element * E = p_function - > body - > variables . front ( ) ; E ; E = E - > next ( ) ) {
LocalVarNode * lv = E - > get ( ) ;
for ( int i = 0 ; i < current_class - > variables . size ( ) ; i + + ) {
if ( current_class - > variables [ i ] . identifier = = lv - > name ) {
_add_warning ( GDScriptWarning : : SHADOWED_VARIABLE , lv - > line , lv - > name , itos ( current_class - > variables [ i ] . line ) ) ;
}
}
}
# endif // DEBUG_ENABLED
2018-05-30 02:16:54 +00: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 16:50:21 +00:00
_mark_line_as_safe ( current_function - > line ) ;
2018-05-30 02:16:54 +00: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 16:50:21 +00:00
_mark_line_as_safe ( current_function - > line ) ;
2018-05-30 02:16:54 +00:00
_check_block_types ( current_block ) ;
current_block = NULL ;
current_function = NULL ;
if ( error_set ) return ;
}
2018-07-01 16:17:40 +00: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 02:16:54 +00: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 16:17:40 +00: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 02:16:54 +00:00
void GDScriptParser : : _check_block_types ( BlockNode * p_block ) {
2018-06-05 16:50:21 +00:00
Node * last_var_assign = NULL ;
2018-05-30 02:16:54 +00: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 16:50:21 +00:00
_mark_line_as_safe ( lv - > line ) ;
2018-05-30 02:16:54 +00:00
2018-07-01 16:17:40 +00:00
last_var_assign = lv - > assign ;
2018-05-30 02:16:54 +00:00
if ( lv - > assign ) {
2020-01-08 14:36:50 +00:00
lv - > assign_op - > arguments [ 0 ] - > set_datatype ( lv - > datatype ) ;
2018-05-30 02:16:54 +00:00
DataType assign_type = _reduce_node_type ( lv - > assign ) ;
2018-07-01 16:17:40 +00: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
2019-03-04 11:25:59 +00:00
if ( ! _is_type_compatible ( lv - > datatype , assign_type ) ) {
2018-06-05 16:50:21 +00:00
// Try supertype test
if ( _is_type_compatible ( assign_type , lv - > datatype ) ) {
_mark_line_as_unsafe ( lv - > line ) ;
} else {
2019-05-19 10:34:40 +00:00
// Try implicit conversion
2019-03-04 11:25:59 +00:00
if ( lv - > datatype . kind ! = DataType : : BUILTIN | | ! _is_type_compatible ( lv - > datatype , assign_type , true ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " The assigned value type ( " + assign_type . to_string ( ) + " ) doesn't match the variable's type ( " +
2019-03-04 11:25:59 +00:00
lv - > datatype . to_string ( ) + " ). " ,
lv - > line ) ;
return ;
}
2019-05-19 10:34:40 +00:00
// Replace assignment with implicit conversion
2019-03-04 11:25:59 +00:00
BuiltInFunctionNode * convert = alloc_node < BuiltInFunctionNode > ( ) ;
convert - > line = lv - > line ;
convert - > function = GDScriptFunctions : : TYPE_CONVERT ;
ConstantNode * tgt_type = alloc_node < ConstantNode > ( ) ;
tgt_type - > line = lv - > line ;
tgt_type - > value = ( int ) lv - > datatype . builtin_type ;
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 ) ;
lv - > assign = convert_call ;
lv - > assign_op - > arguments . write [ 1 ] = convert_call ;
# ifdef DEBUG_ENABLED
Variant: Added 64-bit packed arrays, renamed Variant::REAL to FLOAT.
- Renames PackedIntArray to PackedInt32Array.
- Renames PackedFloatArray to PackedFloat32Array.
- Adds PackedInt64Array and PackedFloat64Array.
- Renames Variant::REAL to Variant::FLOAT for consistency.
Packed arrays are for storing large amount of data and creating stuff like
meshes, buffers. textures, etc. Forcing them to be 64 is a huge waste of
memory. That said, many users requested the ability to have 64 bits packed
arrays for their games, so this is just an optional added type.
For Variant, the float datatype is always 64 bits, and exposed as `float`.
We still have `real_t` which is the datatype that can change from 32 to 64
bits depending on a compile flag (not entirely working right now, but that's
the idea). It affects math related datatypes and code only.
Neither Variant nor PackedArray make use of real_t, which is only intended
for math precision, so the term is removed from there to keep only float.
2020-02-24 18:20:53 +00:00
if ( lv - > datatype . builtin_type = = Variant : : INT & & assign_type . builtin_type = = Variant : : FLOAT ) {
2019-03-04 11:25:59 +00:00
_add_warning ( GDScriptWarning : : NARROWING_CONVERSION , lv - > line ) ;
}
# endif // DEBUG_ENABLED
2018-06-05 16:50:21 +00:00
}
}
2018-06-19 05:55:52 +00:00
if ( lv - > datatype . infer_type ) {
if ( ! assign_type . has_type ) {
2019-08-23 18:51:42 +00:00
_set_error ( " The assigned value doesn't have a set type; the variable type can't be inferred. " , lv - > line ) ;
2018-06-19 05:55:52 +00:00
return ;
}
lv - > datatype = assign_type ;
lv - > datatype . is_constant = false ;
}
2018-06-05 16:50:21 +00:00
if ( lv - > datatype . has_type & & ! assign_type . has_type ) {
_mark_line_as_unsafe ( lv - > line ) ;
2018-05-30 02:16:54 +00: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 16:50:21 +00:00
if ( op - > arguments [ 1 ] = = last_var_assign ) {
// Assignment was already checked
break ;
}
_mark_line_as_safe ( op - > line ) ;
2018-05-30 02:16:54 +00:00
DataType lh_type = _reduce_node_type ( op - > arguments [ 0 ] ) ;
if ( error_set ) {
return ;
}
2019-03-03 10:36:27 +00:00
if ( check_types ) {
if ( ! lh_type . has_type ) {
if ( op - > arguments [ 0 ] - > type = = Node : : TYPE_OPERATOR ) {
_mark_line_as_unsafe ( op - > line ) ;
}
}
if ( lh_type . is_constant ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Can't assign a new value to a constant. " , op - > line ) ;
2019-03-03 10:36:27 +00:00
return ;
2018-06-05 16:50:21 +00:00
}
2018-05-30 02:16:54 +00:00
}
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 16:50:21 +00:00
_mark_line_as_unsafe ( op - > line ) ;
2018-05-30 02:16:54 +00:00
break ;
}
Variant : : Operator oper = _get_variant_operation ( op - > op ) ;
bool valid = false ;
rh_type = _get_operation_type ( oper , lh_type , arg_type , valid ) ;
2019-01-23 20:45:33 +00:00
if ( check_types & & ! valid ) {
2019-08-23 18:51:42 +00:00
_set_error ( " Invalid operand types ( \" " + lh_type . to_string ( ) + " \" and \" " + arg_type . to_string ( ) +
" \" ) to assignment operator \" " + Variant : : get_operator_name ( oper ) + " \" . " ,
2018-05-30 02:16:54 +00:00
op - > line ) ;
return ;
}
} else {
rh_type = _reduce_node_type ( op - > arguments [ 1 ] ) ;
}
2018-07-01 16:17:40 +00: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 ] ) ) ) ;
}
2018-05-30 02:16:54 +00:00
2020-01-08 15:22:41 +00:00
# endif // DEBUG_ENABLED
2020-01-13 11:47:43 +00:00
bool type_match = lh_type . has_type & & rh_type . has_type ;
2019-01-23 20:45:33 +00:00
if ( check_types & & ! _is_type_compatible ( lh_type , rh_type ) ) {
2019-12-13 14:35:01 +00:00
type_match = false ;
2020-01-08 15:22:41 +00:00
2018-06-05 16:50:21 +00:00
// Try supertype test
if ( _is_type_compatible ( rh_type , lh_type ) ) {
_mark_line_as_unsafe ( op - > line ) ;
} else {
2019-05-19 10:34:40 +00:00
// Try implicit conversion
2019-03-04 11:25:59 +00:00
if ( lh_type . kind ! = DataType : : BUILTIN | | ! _is_type_compatible ( lh_type , rh_type , true ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " The assigned value's type ( " + rh_type . to_string ( ) + " ) doesn't match the variable's type ( " +
2019-03-04 11:25:59 +00:00
lh_type . to_string ( ) + " ). " ,
op - > line ) ;
return ;
}
2019-12-13 14:35:01 +00:00
if ( op - > op = = OperatorNode : : OP_ASSIGN ) {
// Replace assignment with implicit conversion
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 ) ;
op - > arguments . write [ 1 ] = convert_call ;
type_match = true ; // Since we are converting, the type is matching
}
2019-03-04 11:25:59 +00:00
# ifdef DEBUG_ENABLED
Variant: Added 64-bit packed arrays, renamed Variant::REAL to FLOAT.
- Renames PackedIntArray to PackedInt32Array.
- Renames PackedFloatArray to PackedFloat32Array.
- Adds PackedInt64Array and PackedFloat64Array.
- Renames Variant::REAL to Variant::FLOAT for consistency.
Packed arrays are for storing large amount of data and creating stuff like
meshes, buffers. textures, etc. Forcing them to be 64 is a huge waste of
memory. That said, many users requested the ability to have 64 bits packed
arrays for their games, so this is just an optional added type.
For Variant, the float datatype is always 64 bits, and exposed as `float`.
We still have `real_t` which is the datatype that can change from 32 to 64
bits depending on a compile flag (not entirely working right now, but that's
the idea). It affects math related datatypes and code only.
Neither Variant nor PackedArray make use of real_t, which is only intended
for math precision, so the term is removed from there to keep only float.
2020-02-24 18:20:53 +00:00
if ( lh_type . builtin_type = = Variant : : INT & & rh_type . builtin_type = = Variant : : FLOAT ) {
2019-03-04 11:25:59 +00:00
_add_warning ( GDScriptWarning : : NARROWING_CONVERSION , op - > line ) ;
}
# endif // DEBUG_ENABLED
2018-05-30 02:16:54 +00:00
}
2018-06-05 16:50:21 +00:00
}
2019-01-23 20:45:33 +00:00
# ifdef DEBUG_ENABLED
2018-06-05 16:50:21 +00: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 02:16:54 +00:00
}
2020-01-08 15:22:41 +00:00
# endif // DEBUG_ENABLED
2020-01-13 11:47:43 +00:00
op - > datatype . has_type = type_match ;
2018-05-30 02:16:54 +00:00
} break ;
case OperatorNode : : OP_CALL :
case OperatorNode : : OP_PARENT_CALL : {
2018-06-05 16:50:21 +00:00
_mark_line_as_safe ( op - > line ) ;
2018-07-01 16:17:40 +00: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 15:27:00 +00:00
func_name = " <undetected name> " ;
2018-07-01 16:17:40 +00:00
}
_add_warning ( GDScriptWarning : : RETURN_VALUE_DISCARDED , op - > line , func_name ) ;
}
# endif // DEBUG_ENABLED
2018-05-30 02:16:54 +00:00
if ( error_set ) return ;
} break ;
2018-07-01 16:17:40 +00:00
case OperatorNode : : OP_YIELD : {
_mark_line_as_safe ( op - > line ) ;
_reduce_node_type ( op ) ;
} break ;
2018-05-30 02:16:54 +00:00
default : {
2018-06-05 16:50:21 +00:00
_mark_line_as_safe ( op - > line ) ;
_reduce_node_type ( op ) ; // Test for safety anyway
2018-07-01 16:17:40 +00:00
# ifdef DEBUG_ENABLED
2020-01-09 11:30:14 +00:00
if ( op - > op = = OperatorNode : : OP_TERNARY_IF ) {
_add_warning ( GDScriptWarning : : STANDALONE_TERNARY , statement - > line ) ;
} else {
_add_warning ( GDScriptWarning : : STANDALONE_EXPRESSION , statement - > line ) ;
}
2018-07-01 16:17:40 +00:00
# endif // DEBUG_ENABLED
2018-05-30 02:16:54 +00:00
}
}
} break ;
case Node : : TYPE_CONTROL_FLOW : {
ControlFlowNode * cf = static_cast < ControlFlowNode * > ( statement ) ;
2018-06-05 16:50:21 +00:00
_mark_line_as_safe ( cf - > line ) ;
2018-05-30 02:16:54 +00:00
switch ( cf - > cf_type ) {
case ControlFlowNode : : CF_RETURN : {
DataType function_type = current_function - > get_datatype ( ) ;
2018-06-05 16:50:21 +00:00
DataType ret_type ;
if ( cf - > arguments . size ( ) > 0 ) {
ret_type = _reduce_node_type ( cf - > arguments [ 0 ] ) ;
if ( error_set ) {
return ;
}
}
2018-05-30 02:16:54 +00: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 ) {
2019-08-23 18:51:42 +00:00
_set_error ( " A void function cannot return a value. " , cf - > line , cf - > column ) ;
2018-05-30 02:16:54 +00:00
return ;
}
} else {
// Return something, cannot be empty
if ( cf - > arguments . size ( ) = = 0 ) {
2019-08-23 18:51:42 +00:00
_set_error ( " A non-void function must return a value. " , cf - > line , cf - > column ) ;
2018-05-30 02:16:54 +00:00
return ;
}
if ( ! _is_type_compatible ( function_type , ret_type ) ) {
2019-08-23 18:51:42 +00:00
_set_error ( " The returned value type ( " + ret_type . to_string ( ) + " ) doesn't match the function return type ( " +
2018-05-30 02:16:54 +00:00
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 16:50:21 +00: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 02:16:54 +00: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 ;
}
2020-02-22 19:47:50 +00:00
[[fallthrough]] ;
2019-04-05 12:06:16 +00:00
}
2018-05-30 02:16:54 +00:00
default : {
2018-06-05 16:50:21 +00:00
_mark_line_as_safe ( statement - > line ) ;
_reduce_node_type ( statement ) ; // Test for safety anyway
2018-07-01 16:17:40 +00:00
# ifdef DEBUG_ENABLED
_add_warning ( GDScriptWarning : : STANDALONE_EXPRESSION , statement - > line ) ;
# endif // DEBUG_ENABLED
2018-05-30 02:16:54 +00: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 16:17:40 +00: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-21 23:19:35 +00: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 16:17:40 +00:00
}
}
# endif // DEBUG_ENABLED
2018-05-30 02:16:54 +00: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 15:44:50 +00:00
error_set = true ;
2014-02-10 01:10:30 +00:00
}
2018-07-01 16:17:40 +00: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 ) {
2019-11-08 04:01:22 +00:00
if ( GLOBAL_GET ( " debug/gdscript/warnings/exclude_addons " ) . booleanize ( ) & & base_path . begins_with ( " res://addons/ " ) ) {
return ;
}
2018-07-01 16:17:40 +00:00
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 17:38:18 +00:00
String GDScriptParser : : get_error ( ) const {
2014-02-10 01:10:30 +00:00
return error ;
}
2017-11-16 17:38:18 +00:00
int GDScriptParser : : get_error_line ( ) const {
2014-02-10 01:10:30 +00:00
return error_line ;
}
2017-11-16 17:38:18 +00:00
int GDScriptParser : : get_error_column ( ) const {
2014-02-10 01:10:30 +00:00
return error_column ;
}
2019-06-14 14:38:54 +00:00
bool GDScriptParser : : has_error ( ) const {
return error_set ;
}
2017-11-16 17:38:18 +00:00
Error GDScriptParser : : _parse ( const String & p_base_path ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
base_path = p_base_path ;
2014-02-10 01:10:30 +00:00
//assume class
ClassNode * main_class = alloc_node < ClassNode > ( ) ;
main_class - > initializer = alloc_node < BlockNode > ( ) ;
2017-03-05 15:44:50 +00:00
main_class - > initializer - > parent_class = main_class ;
2015-12-28 22:31:52 +00:00
main_class - > ready = alloc_node < BlockNode > ( ) ;
2017-03-05 15:44:50 +00:00
main_class - > ready - > parent_class = main_class ;
current_class = main_class ;
2014-02-10 01:10:30 +00:00
_parse_class ( main_class ) ;
2017-11-16 17:38:18 +00:00
if ( tokenizer - > get_token ( ) = = GDScriptTokenizer : : TK_ERROR ) {
2017-03-05 15:44:50 +00:00
error_set = false ;
2019-08-23 18:51:42 +00:00
_set_error ( " Parse error: " + tokenizer - > get_token_error ( ) ) ;
2014-02-10 01:10:30 +00:00
}
2018-06-21 01:41:26 +00:00
if ( error_set & & ! for_completion ) {
2018-05-30 02:16:52 +00:00
return ERR_PARSE_ERROR ;
}
2014-02-10 01:10:30 +00:00
2019-03-03 19:36:42 +00:00
if ( dependencies_only ) {
return OK ;
}
2018-05-30 02:16:52 +00:00
_determine_inheritance ( main_class ) ;
if ( error_set ) {
2014-02-10 01:10:30 +00:00
return ERR_PARSE_ERROR ;
}
2018-05-30 02:16:52 +00:00
2018-05-30 02:16:54 +00:00
current_class = main_class ;
current_function = NULL ;
current_block = NULL ;
2020-01-08 15:22:41 +00:00
2018-05-30 02:16:54 +00:00
if ( for_completion ) check_types = false ;
2018-06-21 01:41:26 +00:00
// Resolve all class-level stuff before getting into function blocks
_check_class_level_types ( main_class ) ;
2018-05-30 02:16:54 +00:00
2018-06-21 01:41:26 +00:00
if ( error_set ) {
return ERR_PARSE_ERROR ;
2018-05-30 02:16:54 +00:00
}
// Resolve the function blocks
_check_class_blocks_types ( main_class ) ;
if ( error_set ) {
return ERR_PARSE_ERROR ;
}
2019-01-23 20:45:33 +00:00
# ifdef DEBUG_ENABLED
2018-07-01 16:17:40 +00:00
// 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 01:10:30 +00:00
return OK ;
}
2017-11-16 17:38:18 +00:00
Error GDScriptParser : : parse_bytecode ( const Vector < uint8_t > & p_bytecode , const String & p_base_path , const String & p_self_path ) {
2017-03-05 15:44:50 +00:00
2018-01-21 09:35:47 +00:00
clear ( ) ;
2017-03-05 15:44:50 +00:00
self_path = p_self_path ;
2017-11-16 17:38:18 +00:00
GDScriptTokenizerBuffer * tb = memnew ( GDScriptTokenizerBuffer ) ;
2014-02-25 12:31:47 +00:00
tb - > set_code_buffer ( p_bytecode ) ;
2017-03-05 15:44:50 +00:00
tokenizer = tb ;
2014-02-25 12:31:47 +00:00
Error ret = _parse ( p_base_path ) ;
memdelete ( tb ) ;
2017-03-05 15:44:50 +00:00
tokenizer = NULL ;
2014-02-25 12:31:47 +00:00
return ret ;
}
2019-03-03 19:36:42 +00: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 , bool p_dependencies_only ) {
2014-02-25 12:31:47 +00:00
2018-01-21 09:35:47 +00:00
clear ( ) ;
2014-12-17 01:31:57 +00:00
2017-03-05 15:44:50 +00:00
self_path = p_self_path ;
2017-11-16 17:38:18 +00:00
GDScriptTokenizerText * tt = memnew ( GDScriptTokenizerText ) ;
2014-02-25 12:31:47 +00:00
tt - > set_code ( p_code ) ;
2017-03-05 15:44:50 +00:00
validating = p_just_validate ;
for_completion = p_for_completion ;
2019-03-03 19:36:42 +00:00
dependencies_only = p_dependencies_only ;
2018-06-05 16:50:21 +00:00
# ifdef DEBUG_ENABLED
safe_lines = r_safe_lines ;
# endif // DEBUG_ENABLED
2017-03-05 15:44:50 +00:00
tokenizer = tt ;
2014-02-25 12:31:47 +00:00
Error ret = _parse ( p_base_path ) ;
memdelete ( tt ) ;
2017-03-05 15:44:50 +00:00
tokenizer = NULL ;
2014-02-25 12:31:47 +00:00
return ret ;
}
2017-11-16 17:38:18 +00:00
bool GDScriptParser : : is_tool_script ( ) const {
2016-01-23 18:36:03 +00:00
2017-03-05 15:44:50 +00:00
return ( head & & head - > type = = Node : : TYPE_CLASS & & static_cast < const ClassNode * > ( head ) - > tool ) ;
2016-01-23 18:36:03 +00:00
}
2017-11-16 17:38:18 +00:00
const GDScriptParser : : Node * GDScriptParser : : get_parse_tree ( ) const {
2014-02-10 01:10:30 +00:00
return head ;
}
2017-11-16 17:38:18 +00:00
void GDScriptParser : : clear ( ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
while ( list ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
Node * l = list ;
list = list - > next ;
2014-02-10 01:10:30 +00:00
memdelete ( l ) ;
}
2017-03-05 15:44:50 +00:00
head = NULL ;
list = NULL ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00: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 02:46:55 +00:00
2017-03-05 15:44:50 +00:00
completion_found = false ;
2018-05-13 05:07:56 +00:00
rpc_mode = MultiplayerAPI : : RPC_MODE_DISABLED ;
2015-08-30 14:50:10 +00:00
2017-03-05 15:44:50 +00:00
current_function = NULL ;
2014-12-17 01:31:57 +00:00
2017-03-05 15:44:50 +00:00
validating = false ;
for_completion = false ;
error_set = false ;
2019-10-13 19:48:18 +00:00
indent_level . clear ( ) ;
indent_level . push_back ( IndentLevel ( 0 , 0 ) ) ;
2017-03-05 15:44:50 +00:00
error_line = 0 ;
error_column = 0 ;
pending_newline = - 1 ;
parenthesis = 0 ;
current_export . type = Variant : : NIL ;
2018-05-30 02:16:54 +00:00
check_types = true ;
2019-03-03 19:36:42 +00:00
dependencies_only = false ;
dependencies . clear ( ) ;
2017-03-05 15:44:50 +00:00
error = " " ;
2018-06-05 16:50:21 +00:00
# ifdef DEBUG_ENABLED
safe_lines = NULL ;
# endif // DEBUG_ENABLED
2014-02-10 01:10:30 +00:00
}
2017-11-16 17:38:18 +00:00
GDScriptParser : : CompletionType GDScriptParser : : get_completion_type ( ) {
2014-12-17 01:31:57 +00:00
return completion_type ;
}
2017-11-16 17:38:18 +00:00
StringName GDScriptParser : : get_completion_cursor ( ) {
2014-12-17 01:31:57 +00:00
return completion_cursor ;
}
2017-11-16 17:38:18 +00:00
int GDScriptParser : : get_completion_line ( ) {
2014-12-17 01:31:57 +00:00
return completion_line ;
}
2017-11-16 17:38:18 +00:00
Variant : : Type GDScriptParser : : get_completion_built_in_constant ( ) {
2014-12-17 01:31:57 +00:00
return completion_built_in_constant ;
}
2017-11-16 17:38:18 +00:00
GDScriptParser : : Node * GDScriptParser : : get_completion_node ( ) {
2014-12-17 01:31:57 +00:00
return completion_node ;
}
2017-11-16 17:38:18 +00:00
GDScriptParser : : BlockNode * GDScriptParser : : get_completion_block ( ) {
2014-12-17 01:31:57 +00:00
return completion_block ;
}
2017-11-16 17:38:18 +00:00
GDScriptParser : : ClassNode * GDScriptParser : : get_completion_class ( ) {
2014-12-17 01:31:57 +00:00
return completion_class ;
}
2017-11-16 17:38:18 +00:00
GDScriptParser : : FunctionNode * GDScriptParser : : get_completion_function ( ) {
2014-12-17 01:31:57 +00:00
return completion_function ;
}
2017-11-16 17:38:18 +00:00
int GDScriptParser : : get_completion_argument_index ( ) {
2014-12-17 01:31:57 +00:00
return completion_argument ;
}
2017-11-16 17:38:18 +00:00
int GDScriptParser : : get_completion_identifier_is_function ( ) {
2016-09-12 13:52:29 +00:00
return completion_ident_is_call ;
}
2017-11-16 17:38:18 +00:00
GDScriptParser : : GDScriptParser ( ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
head = NULL ;
list = NULL ;
tokenizer = NULL ;
pending_newline = - 1 ;
2014-02-10 01:10:30 +00:00
clear ( ) ;
}
2017-11-16 17:38:18 +00:00
GDScriptParser : : ~ GDScriptParser ( ) {
2014-02-10 01:10:30 +00:00
clear ( ) ;
}