| 3471 | } |
| 3472 | |
| 3473 | GDScriptParser::ExpressionNode *GDScriptParser::parse_get_node(ExpressionNode *p_previous_operand, bool p_can_assign) { |
| 3474 | // We want code completion after a DOLLAR even if the current code is invalid. |
| 3475 | make_completion_context(COMPLETION_GET_NODE, nullptr, -1); |
| 3476 | |
| 3477 | if (!current.is_node_name() && !check(GDScriptTokenizer::Token::LITERAL) && !check(GDScriptTokenizer::Token::SLASH) && !check(GDScriptTokenizer::Token::PERCENT)) { |
| 3478 | push_error(vformat(R"(Expected node path as string or identifier after "%s".)", previous.get_name())); |
| 3479 | return nullptr; |
| 3480 | } |
| 3481 | |
| 3482 | if (check(GDScriptTokenizer::Token::LITERAL)) { |
| 3483 | if (current.literal.get_type() != Variant::STRING) { |
| 3484 | push_error(vformat(R"(Expected node path as string or identifier after "%s".)", previous.get_name())); |
| 3485 | return nullptr; |
| 3486 | } |
| 3487 | } |
| 3488 | |
| 3489 | GetNodeNode *get_node = alloc_node<GetNodeNode>(); |
| 3490 | |
| 3491 | // Store the last item in the path so the parser knows what to expect. |
| 3492 | // Allow allows more specific error messages. |
| 3493 | enum PathState { |
| 3494 | PATH_STATE_START, |
| 3495 | PATH_STATE_SLASH, |
| 3496 | PATH_STATE_PERCENT, |
| 3497 | PATH_STATE_NODE_NAME, |
| 3498 | } path_state = PATH_STATE_START; |
| 3499 | |
| 3500 | if (previous.type == GDScriptTokenizer::Token::DOLLAR) { |
| 3501 | // Detect initial slash, which will be handled in the loop if it matches. |
| 3502 | match(GDScriptTokenizer::Token::SLASH); |
| 3503 | } else { |
| 3504 | get_node->use_dollar = false; |
| 3505 | } |
| 3506 | |
| 3507 | int context_argument = 0; |
| 3508 | |
| 3509 | do { |
| 3510 | if (previous.type == GDScriptTokenizer::Token::PERCENT) { |
| 3511 | if (path_state != PATH_STATE_START && path_state != PATH_STATE_SLASH) { |
| 3512 | push_error(R"("%" is only valid in the beginning of a node name (either after "$" or after "/"))"); |
| 3513 | complete_extents(get_node); |
| 3514 | return nullptr; |
| 3515 | } |
| 3516 | |
| 3517 | get_node->full_path += "%"; |
| 3518 | |
| 3519 | path_state = PATH_STATE_PERCENT; |
| 3520 | } else if (previous.type == GDScriptTokenizer::Token::SLASH) { |
| 3521 | if (path_state != PATH_STATE_START && path_state != PATH_STATE_NODE_NAME) { |
| 3522 | push_error(R"("/" is only valid at the beginning of the path or after a node name.)"); |
| 3523 | complete_extents(get_node); |
| 3524 | return nullptr; |
| 3525 | } |
| 3526 | |
| 3527 | get_node->full_path += "/"; |
| 3528 | |
| 3529 | path_state = PATH_STATE_SLASH; |
| 3530 | } |
nothing calls this directly
no test coverage detected