/** * Called when a statement was just closed and the pse_tos was just * decremented. * * - if the TOS is now VBRACE, insert a CT_VBRACE_CLOSE and recurse. * - if the TOS is a complex statement, call handle_complex_close() * * @return true - done with this chunk, false - keep processing */ bool close_statement(struct parse_frame *frm, chunk_t *pc) { chunk_t *vbc = pc; LOG_FMT(LTOK, "%s:%d] %s '%s' type %s stage %d\n", __func__, pc->orig_line, get_token_name(pc->type), pc->str.c_str(), get_token_name(frm->pse[frm->pse_tos].type), frm->pse[frm->pse_tos].stage); if (cpd.consumed) { frm->stmt_count = 0; frm->expr_count = 0; LOG_FMT(LSTMT, "%s: %d> reset2 stmt on %s\n", __func__, pc->orig_line, pc->str.c_str()); } /** * If we are in a virtual brace and we are not ON a CT_VBRACE_CLOSE add one */ if (frm->pse[frm->pse_tos].type == CT_VBRACE_OPEN) { /* If the current token has already been consumed, then add after it */ if (cpd.consumed) { insert_vbrace_close_after(pc, frm); } else { /* otherwise, add before it and consume the vbrace */ vbc = chunk_get_prev_ncnl(pc); vbc = insert_vbrace_close_after(vbc, frm); vbc->parent_type = frm->pse[frm->pse_tos].parent; frm->level--; frm->brace_level--; frm->pse_tos--; /* Update the token level */ pc->level = frm->level; pc->brace_level = frm->brace_level; print_stack(LBCSPOP, "-CS VB ", frm, pc); /* And repeat the close */ close_statement(frm, pc); return(true); } } /* See if we are done with a complex statement */ if (frm->pse[frm->pse_tos].stage != BS_NONE) { if (handle_complex_close(frm, vbc)) { return(true); } } return(false); }
/** * At the heart of this algorithm are two stacks. * There is the Paren Stack (PS) and the Frame stack. * * The PS (pse in the code) keeps track of braces, parens, * if/else/switch/do/while/etc items -- anything that is nestable. * Complex statements go through stages. * Take this simple if statement as an example: * if ( x ) { x--; } * * The stack would change like so: 'token' stack afterwards * 'if' [IF - 1] * '(' [IF - 1] [PAREN OPEN] * 'x' [IF - 1] [PAREN OPEN] * ')' [IF - 2] <- note that the state was incremented * '{' [IF - 2] [BRACE OPEN] * 'x' [IF - 2] [BRACE OPEN] * '--' [IF - 2] [BRACE OPEN] * ';' [IF - 2] [BRACE OPEN] * '}' [IF - 3] * <- lack of else kills the IF, closes statement * * Virtual braces example: * if ( x ) x--; else x++; * * 'if' [IF - 1] * '(' [IF - 1] [PAREN OPEN] * 'x' [IF - 1] [PAREN OPEN] * ')' [IF - 2] * 'x' [IF - 2] [VBRACE OPEN] <- VBrace open inserted before because '{' was not next * '--' [IF - 2] [VBRACE OPEN] * ';' [IF - 3] <- VBrace close inserted after semicolon * 'else' [ELSE - 0] <- IF changed into ELSE * 'x' [ELSE - 0] [VBRACE OPEN] <- lack of '{' -> VBrace * '++' [ELSE - 0] [VBRACE OPEN] * ';' [ELSE - 0] <- VBrace close inserted after semicolon * <- ELSE removed after statement close * * The pse stack is kept on a frame stack. * The frame stack is need for languages that support preprocessors (C, C++, C#) * that can arbitrarily change code flow. It also isolates #define macros so * that they are indented independently and do not affect the rest of the program. * * When an #if is hit, a copy of the current frame is push on the frame stack. * When an #else/#elif is hit, a copy of the current stack is pushed under the * #if frame and the original (pre-#if) frame is copied to the current frame. * When #endif is hit, the top frame is popped. * This has the following effects: * - a simple #if / #endif does not affect program flow * - #if / #else /#endif - continues from the #if clause * * When a #define is entered, the current frame is pushed and cleared. * When a #define is exited, the frame is popped. */ static void parse_cleanup(parse_frame_t *frm, chunk_t *pc) { LOG_FUNC_ENTRY(); LOG_FMT(LTOK, "%s(%d): orig_line is %zu, type is %s, tos is %zu, TOS.type is %s, TOS.stage is %u\n", __func__, __LINE__, pc->orig_line, get_token_name(pc->type), frm->pse_tos, get_token_name(frm->pse[frm->pse_tos].type), (unsigned int)frm->pse[frm->pse_tos].stage); // Mark statement starts if ( (frm->stmt_count == 0 || frm->expr_count == 0) && !chunk_is_semicolon(pc) && pc->type != CT_BRACE_CLOSE && pc->type != CT_VBRACE_CLOSE && !chunk_is_str(pc, ")", 1) && !chunk_is_str(pc, "]", 1)) { chunk_flags_set(pc, PCF_EXPR_START | ((frm->stmt_count == 0) ? PCF_STMT_START : 0)); LOG_FMT(LSTMT, "%s(%d): orig_line is %zu, 1.marked '%s' as %s, start stmt_count is %d, expr_count is %d\n", __func__, __LINE__, pc->orig_line, pc->text(), (pc->flags & PCF_STMT_START) ? "stmt" : "expr", frm->stmt_count, frm->expr_count); } frm->stmt_count++; frm->expr_count++; if (frm->sparen_count > 0) { chunk_flags_set(pc, PCF_IN_SPAREN); // Mark everything in the for statement for (int tmp = frm->pse_tos - 1; tmp >= 0; tmp--) { if (frm->pse[tmp].type == CT_FOR) { chunk_flags_set(pc, PCF_IN_FOR); break; } } // Mark the parent on semicolons in for() statements if ( pc->type == CT_SEMICOLON && frm->pse_tos > 1 && (frm->pse[frm->pse_tos - 1].type == CT_FOR)) { set_chunk_parent(pc, CT_FOR); } } // Check the progression of complex statements if (frm->pse[frm->pse_tos].stage != brace_stage_e::NONE) { if (check_complex_statements(frm, pc)) { return; } } /* * Check for a virtual brace statement close due to a semicolon. * The virtual brace will get handled the next time through. * The semicolon isn't handled at all. * TODO: may need to float VBRACE past comments until newline? */ if (frm->pse[frm->pse_tos].type == CT_VBRACE_OPEN) { if (chunk_is_semicolon(pc)) { cpd.consumed = true; close_statement(frm, pc); } else if (cpd.lang_flags & LANG_PAWN) { if (pc->type == CT_BRACE_CLOSE) { close_statement(frm, pc); } } } // Handle close parenthesis, vbrace, brace, and square if ( pc->type == CT_PAREN_CLOSE || pc->type == CT_BRACE_CLOSE || pc->type == CT_VBRACE_CLOSE || pc->type == CT_ANGLE_CLOSE || pc->type == CT_MACRO_CLOSE || pc->type == CT_SQUARE_CLOSE) { // Change CT_PAREN_CLOSE into CT_SPAREN_CLOSE or CT_FPAREN_CLOSE if ( pc->type == CT_PAREN_CLOSE && ( (frm->pse[frm->pse_tos].type == CT_FPAREN_OPEN) || (frm->pse[frm->pse_tos].type == CT_SPAREN_OPEN))) { set_chunk_type(pc, (c_token_t)(frm->pse[frm->pse_tos].type + 1)); if (pc->type == CT_SPAREN_CLOSE) { frm->sparen_count--; chunk_flags_clr(pc, PCF_IN_SPAREN); } } // Make sure the open / close match if (pc->type != (frm->pse[frm->pse_tos].type + 1)) { if ( (frm->pse[frm->pse_tos].type != CT_NONE) && (frm->pse[frm->pse_tos].type != CT_PP_DEFINE)) { LOG_FMT(LWARN, "%s(%d): %s, orig_line is %zu, Error: Unexpected '%s' for '%s', which was on line %zu\n", __func__, __LINE__, cpd.filename, pc->orig_line, pc->text(), get_token_name(frm->pse[frm->pse_tos].pc->type), frm->pse[frm->pse_tos].pc->orig_line); print_stack(LBCSPOP, "=Error ", frm, pc); cpd.error_count++; } } else { cpd.consumed = true; // Copy the parent, update the parenthesis/brace levels set_chunk_parent(pc, frm->pse[frm->pse_tos].parent); frm->level--; if ( pc->type == CT_BRACE_CLOSE || pc->type == CT_VBRACE_CLOSE || pc->type == CT_MACRO_CLOSE) { frm->brace_level--; } pc->level = frm->level; pc->brace_level = frm->brace_level; // Pop the entry frm->pse_tos--; print_stack(LBCSPOP, "-Close ", frm, pc); // See if we are in a complex statement if (frm->pse[frm->pse_tos].stage != brace_stage_e::NONE) { handle_complex_close(frm, pc); } } } /* * In this state, we expect a semicolon, but we'll also hit the closing * sparen, so we need to check cpd.consumed to see if the close sparen was * aleady handled. */ if (frm->pse[frm->pse_tos].stage == brace_stage_e::WOD_SEMI) { chunk_t *tmp = pc; if (cpd.consumed) { /* * If consumed, then we are on the close sparen. * PAWN: Check the next chunk for a semicolon. If it isn't, then * add a virtual semicolon, which will get handled on the next pass. */ if (cpd.lang_flags & LANG_PAWN) { tmp = chunk_get_next_ncnl(pc); if (tmp->type != CT_SEMICOLON && tmp->type != CT_VSEMICOLON) { pawn_add_vsemi_after(pc); } } } else { // Complain if this ISN'T a semicolon, but close out WHILE_OF_DO anyway if (pc->type == CT_SEMICOLON || pc->type == CT_VSEMICOLON) { cpd.consumed = true; set_chunk_parent(pc, CT_WHILE_OF_DO); } else { LOG_FMT(LWARN, "%s: %s(%d): %zu: Error: Expected a semicolon for WHILE_OF_DO, but got '%s'\n", cpd.filename, __func__, __LINE__, pc->orig_line, get_token_name(pc->type)); cpd.error_count++; } handle_complex_close(frm, pc); } } // Get the parent type for brace and parenthesis open c_token_t parent = pc->parent_type; if ( pc->type == CT_PAREN_OPEN || pc->type == CT_FPAREN_OPEN || pc->type == CT_SPAREN_OPEN || pc->type == CT_BRACE_OPEN) { chunk_t *prev = chunk_get_prev_ncnl(pc); if (prev != nullptr) { if ( pc->type == CT_PAREN_OPEN || pc->type == CT_FPAREN_OPEN || pc->type == CT_SPAREN_OPEN) { // Set the parent for parenthesis and change parenthesis type if (frm->pse[frm->pse_tos].stage != brace_stage_e::NONE) { set_chunk_type(pc, CT_SPAREN_OPEN); parent = frm->pse[frm->pse_tos].type; frm->sparen_count++; } else if (prev->type == CT_FUNCTION) { set_chunk_type(pc, CT_FPAREN_OPEN); parent = CT_FUNCTION; } // NS_ENUM and NS_OPTIONS are followed by a (type, name) pair else if (prev->type == CT_ENUM && (cpd.lang_flags & LANG_OC)) { // Treat both as CT_ENUM since the syntax is identical set_chunk_type(pc, CT_FPAREN_OPEN); parent = CT_ENUM; } else { // no need to set parent } } else // must be CT_BRACE_OPEN { // Set the parent for open braces if (frm->pse[frm->pse_tos].stage != brace_stage_e::NONE) { parent = frm->pse[frm->pse_tos].type; } else if (prev->type == CT_ASSIGN && (prev->str[0] == '=')) { parent = CT_ASSIGN; } // Carry through CT_ENUM parent in NS_ENUM (type, name) { else if ( prev->type == CT_FPAREN_CLOSE && (cpd.lang_flags & LANG_OC) && prev->parent_type == CT_ENUM) { parent = CT_ENUM; } else if (prev->type == CT_FPAREN_CLOSE) { parent = CT_FUNCTION; } else { // no need to set parent } } } } /* * Adjust the level for opens & create a stack entry * Note that CT_VBRACE_OPEN has already been handled. */ if ( pc->type == CT_BRACE_OPEN || pc->type == CT_PAREN_OPEN || pc->type == CT_FPAREN_OPEN || pc->type == CT_SPAREN_OPEN || pc->type == CT_ANGLE_OPEN || pc->type == CT_MACRO_OPEN || pc->type == CT_SQUARE_OPEN) { frm->level++; if (pc->type == CT_BRACE_OPEN || pc->type == CT_MACRO_OPEN) { frm->brace_level++; } push_fmr_pse(frm, pc, brace_stage_e::NONE, "+Open "); frm->pse[frm->pse_tos].parent = parent; set_chunk_parent(pc, parent); } pattern_class_e patcls = get_token_pattern_class(pc->type); /* * Create a stack entry for complex statements: * if, elseif, switch, for, while, synchronized, using, lock, with, * version, CT_D_SCOPE_IF */ if (patcls == pattern_class_e::BRACED) { push_fmr_pse(frm, pc, (pc->type == CT_DO) ? brace_stage_e::BRACE_DO : brace_stage_e::BRACE2, "+ComplexBraced"); } else if (patcls == pattern_class_e::PBRACED) { brace_stage_e bs = brace_stage_e::PAREN1; if (pc->type == CT_WHILE && maybe_while_of_do(pc)) { set_chunk_type(pc, CT_WHILE_OF_DO); bs = brace_stage_e::WOD_PAREN; } push_fmr_pse(frm, pc, bs, "+ComplexParenBraced"); } else if (patcls == pattern_class_e::OPBRACED) { push_fmr_pse(frm, pc, brace_stage_e::OP_PAREN1, "+ComplexOpParenBraced"); } else if (patcls == pattern_class_e::ELSE) { push_fmr_pse(frm, pc, brace_stage_e::ELSEIF, "+ComplexElse"); } /* * Mark simple statement/expression starts * - after { or } * - after ';', but not if the paren stack top is a paren * - after '(' that has a parent type of CT_FOR */ if ( pc->type == CT_SQUARE_OPEN || (pc->type == CT_BRACE_OPEN && pc->parent_type != CT_ASSIGN) || pc->type == CT_BRACE_CLOSE || pc->type == CT_VBRACE_CLOSE || (pc->type == CT_SPAREN_OPEN && pc->parent_type == CT_FOR) || pc->type == CT_COLON || pc->type == CT_OC_END || ( chunk_is_semicolon(pc) && frm->pse[frm->pse_tos].type != CT_PAREN_OPEN && frm->pse[frm->pse_tos].type != CT_FPAREN_OPEN && frm->pse[frm->pse_tos].type != CT_SPAREN_OPEN)) { LOG_FMT(LSTMT, "%s(%d): orig_line is %zu, reset1 stmt on '%s'\n", __func__, __LINE__, pc->orig_line, pc->text()); frm->stmt_count = 0; frm->expr_count = 0; } // Mark expression starts chunk_t *tmp = chunk_get_next_ncnl(pc); if ( pc->type == CT_ARITH || pc->type == CT_ASSIGN || pc->type == CT_CASE || pc->type == CT_COMPARE || ( pc->type == CT_STAR && tmp != nullptr && tmp->type != CT_STAR) || pc->type == CT_BOOL || pc->type == CT_MINUS || pc->type == CT_PLUS || pc->type == CT_CARET || pc->type == CT_ANGLE_OPEN || pc->type == CT_ANGLE_CLOSE || pc->type == CT_RETURN || pc->type == CT_THROW || pc->type == CT_GOTO || pc->type == CT_CONTINUE || pc->type == CT_PAREN_OPEN || pc->type == CT_FPAREN_OPEN || pc->type == CT_SPAREN_OPEN || pc->type == CT_BRACE_OPEN || chunk_is_semicolon(pc) || pc->type == CT_COMMA || pc->type == CT_NOT || pc->type == CT_INV || pc->type == CT_COLON || pc->type == CT_QUESTION) { frm->expr_count = 0; LOG_FMT(LSTMT, "%s(%d): orig_line is %zu, reset expr on '%s'\n", __func__, __LINE__, pc->orig_line, pc->text()); } else if (pc->type == CT_BRACE_CLOSE) { if (!cpd.consumed) { size_t file_pp_level = ifdef_over_whole_file() ? 1 : 0; if (!cpd.unc_off_used && pc->pp_level == file_pp_level) { // fatal error char *outputMessage; if (cpd.settings[UO_tok_split_gte].b) { outputMessage = make_message("Unmatched BRACE_CLOSE\nat orig_line=%zu, orig_col=%zu\n", pc->orig_line, pc->orig_col); } else { outputMessage = make_message("Unmatched BRACE_CLOSE\nat orig_line=%zu, orig_col=%zu\nTry the option 'tok_split_gte = true'\n", pc->orig_line, pc->orig_col); } fprintf(stderr, "%s", outputMessage); free(outputMessage); log_flush(true); exit(EXIT_FAILURE); } } } } // parse_cleanup
/** * At the heart of this algorithm are two stacks. * There is the Paren Stack (PS) and the Frame stack. * * The PS (pse in the code) keeps track of braces, parens, * if/else/switch/do/while/etc items -- anything that is nestable. * Complex statements go through stages. * Take this simple if statement as an example: * if ( x ) { x--; } * * The stack would change like so: 'token' stack afterwards * 'if' [IF - 1] * '(' [IF - 1] [PAREN OPEN] * 'x' [IF - 1] [PAREN OPEN] * ')' [IF - 2] <- note that the state was incremented * '{' [IF - 2] [BRACE OPEN] * 'x' [IF - 2] [BRACE OPEN] * '--' [IF - 2] [BRACE OPEN] * ';' [IF - 2] [BRACE OPEN] * '}' [IF - 3] * <- lack of else kills the IF, closes statement * * Virtual braces example: * if ( x ) x--; else x++; * * 'if' [IF - 1] * '(' [IF - 1] [PAREN OPEN] * 'x' [IF - 1] [PAREN OPEN] * ')' [IF - 2] * 'x' [IF - 2] [VBRACE OPEN] <- VBrace open inserted before because '{' was not next * '--' [IF - 2] [VBRACE OPEN] * ';' [IF - 3] <- VBrace close inserted after semicolon * 'else' [ELSE - 0] <- IF changed into ELSE * 'x' [ELSE - 0] [VBRACE OPEN] <- lack of '{' -> VBrace * '++' [ELSE - 0] [VBRACE OPEN] * ';' [ELSE - 0] <- VBrace close inserted after semicolon * <- ELSE removed after statement close * * The pse stack is kept on a frame stack. * The frame stack is need for languages that support preprocessors (C, C++, C#) * that can arbitrarily change code flow. It also isolates #define macros so * that they are indented independently and do not affect the rest of the program. * * When an #if is hit, a copy of the current frame is push on the frame stack. * When an #else/#elif is hit, a copy of the current stack is pushed under the * #if frame and the original (pre-#if) frame is copied to the current frame. * When #endif is hit, the top frame is popped. * This has the following effects: * - a simple #if / #endif does not affect program flow * - #if / #else /#endif - continues from the #if clause * * When a #define is entered, the current frame is pushed and cleared. * When a #define is exited, the frame is popped. */ static void parse_cleanup(struct parse_frame *frm, chunk_t *pc) { c_token_t parent = CT_NONE; chunk_t *prev; LOG_FMT(LTOK, "%s:%d] %16s - tos:%d/%16s stg:%d\n", __func__, pc->orig_line, get_token_name(pc->type), frm->pse_tos, get_token_name(frm->pse[frm->pse_tos].type), frm->pse[frm->pse_tos].stage); /* Mark statement starts */ if (((frm->stmt_count == 0) || (frm->expr_count == 0)) && !chunk_is_semicolon(pc) && (pc->type != CT_BRACE_CLOSE) && (pc->type != CT_VBRACE_CLOSE) && !chunk_is_str(pc, ")", 1) && !chunk_is_str(pc, "]", 1)) { pc->flags |= PCF_EXPR_START; pc->flags |= (frm->stmt_count == 0) ? PCF_STMT_START : 0; LOG_FMT(LSTMT, "%d] 1.marked %s as %s start st:%d ex:%d\n", pc->orig_line, pc->str.c_str(), (pc->flags &PCF_STMT_START) ? "stmt" : "expr", frm->stmt_count, frm->expr_count); } frm->stmt_count++; frm->expr_count++; if (frm->sparen_count > 0) { int tmp; pc->flags |= PCF_IN_SPAREN; /* Mark everything in the a for statement */ for (tmp = frm->pse_tos - 1; tmp >= 0; tmp--) { if (frm->pse[tmp].type == CT_FOR) { pc->flags |= PCF_IN_FOR; break; } } /* Mark the parent on semicolons in for() stmts */ if ((pc->type == CT_SEMICOLON) && (frm->pse_tos > 1) && (frm->pse[frm->pse_tos - 1].type == CT_FOR)) { pc->parent_type = CT_FOR; } } /* Check the progression of complex statements */ if (frm->pse[frm->pse_tos].stage != BS_NONE) { if (check_complex_statements(frm, pc)) { return; } } /** * Check for a virtual brace statement close due to a semicolon. * The virtual brace will get handled the next time through. * The semicolon isn't handled at all. * TODO: may need to float VBRACE past comments until newline? */ if (frm->pse[frm->pse_tos].type == CT_VBRACE_OPEN) { if (chunk_is_semicolon(pc)) { cpd.consumed = true; close_statement(frm, pc); } else if ((cpd.lang_flags & LANG_PAWN) != 0) { if (pc->type == CT_BRACE_CLOSE) { close_statement(frm, pc); } } } /* Handle close paren, vbrace, brace, and square */ if ((pc->type == CT_PAREN_CLOSE) || (pc->type == CT_BRACE_CLOSE) || (pc->type == CT_VBRACE_CLOSE) || (pc->type == CT_ANGLE_CLOSE) || (pc->type == CT_MACRO_CLOSE) || (pc->type == CT_SQUARE_CLOSE)) { /* Change CT_PAREN_CLOSE into CT_SPAREN_CLOSE or CT_FPAREN_CLOSE */ if ((pc->type == CT_PAREN_CLOSE) && ((frm->pse[frm->pse_tos].type == CT_FPAREN_OPEN) || (frm->pse[frm->pse_tos].type == CT_SPAREN_OPEN))) { pc->type = (c_token_t)(frm->pse[frm->pse_tos].type + 1); if (pc->type == CT_SPAREN_CLOSE) { frm->sparen_count--; pc->flags &= ~PCF_IN_SPAREN; } } /* Make sure the open / close match */ if (pc->type != (frm->pse[frm->pse_tos].type + 1)) { if ((frm->pse[frm->pse_tos].type != CT_NONE) && (frm->pse[frm->pse_tos].type != CT_PP_DEFINE)) { LOG_FMT(LWARN, "%s:%d Error: Unexpected '%s' for '%s', which was on line %d\n", cpd.filename, pc->orig_line, pc->str.c_str(), get_token_name(frm->pse[frm->pse_tos].pc->type), frm->pse[frm->pse_tos].pc->orig_line); print_stack(LBCSPOP, "=Error ", frm, pc); cpd.error_count++; } } else { cpd.consumed = true; /* Copy the parent, update the paren/brace levels */ pc->parent_type = frm->pse[frm->pse_tos].parent; frm->level--; if ((pc->type == CT_BRACE_CLOSE) || (pc->type == CT_VBRACE_CLOSE) || (pc->type == CT_MACRO_CLOSE)) { frm->brace_level--; } pc->level = frm->level; pc->brace_level = frm->brace_level; /* Pop the entry */ frm->pse_tos--; print_stack(LBCSPOP, "-Close ", frm, pc); /* See if we are in a complex statement */ if (frm->pse[frm->pse_tos].stage != BS_NONE) { handle_complex_close(frm, pc); } } } /* In this state, we expect a semicolon, but we'll also hit the closing * sparen, so we need to check cpd.consumed to see if the close sparen was * aleady handled. */ if (frm->pse[frm->pse_tos].stage == BS_WOD_SEMI) { chunk_t *tmp = pc; if (cpd.consumed) { /* If consumed, then we are on the close sparen. * PAWN: Check the next chunk for a semicolon. If it isn't, then * add a virtual semicolon, which will get handled on the next pass. */ if (cpd.lang_flags & LANG_PAWN) { tmp = chunk_get_next_ncnl(pc); if ((tmp->type != CT_SEMICOLON) && (tmp->type != CT_VSEMICOLON)) { pawn_add_vsemi_after(pc); } } } else { /* Complain if this ISN'T a semicolon, but close out WHILE_OF_DO anyway */ if ((pc->type == CT_SEMICOLON) || (pc->type == CT_VSEMICOLON)) { cpd.consumed = true; pc->parent_type = CT_WHILE_OF_DO; } else { LOG_FMT(LWARN, "%s:%d: Error: Expected a semicolon for WHILE_OF_DO, but got '%s'\n", cpd.filename, pc->orig_line, get_token_name(pc->type)); cpd.error_count++; } handle_complex_close(frm, pc); } } /* Get the parent type for brace and paren open */ parent = pc->parent_type; if ((pc->type == CT_PAREN_OPEN) || (pc->type == CT_FPAREN_OPEN) || (pc->type == CT_SPAREN_OPEN) || (pc->type == CT_BRACE_OPEN)) { prev = chunk_get_prev_ncnl(pc); if (prev != NULL) { if ((pc->type == CT_PAREN_OPEN) || (pc->type == CT_FPAREN_OPEN) || (pc->type == CT_SPAREN_OPEN)) { /* Set the parent for parens and change paren type */ if (frm->pse[frm->pse_tos].stage != BS_NONE) { pc->type = CT_SPAREN_OPEN; parent = frm->pse[frm->pse_tos].type; frm->sparen_count++; } else if (prev->type == CT_FUNCTION) { pc->type = CT_FPAREN_OPEN; parent = CT_FUNCTION; } else { /* no need to set parent */ } } else /* must be CT_BRACE_OPEN */ { /* Set the parent for open braces */ if (frm->pse[frm->pse_tos].stage != BS_NONE) { parent = frm->pse[frm->pse_tos].type; } else if ((prev->type == CT_ASSIGN) && (prev->str[0] == '=')) { parent = CT_ASSIGN; } else if (prev->type == CT_FPAREN_CLOSE) { parent = CT_FUNCTION; } else { /* no need to set parent */ } } } } /** * Adjust the level for opens & create a stack entry * Note that CT_VBRACE_OPEN has already been handled. */ if ((pc->type == CT_BRACE_OPEN) || (pc->type == CT_PAREN_OPEN) || (pc->type == CT_FPAREN_OPEN) || (pc->type == CT_SPAREN_OPEN) || (pc->type == CT_ANGLE_OPEN) || (pc->type == CT_MACRO_OPEN) || (pc->type == CT_SQUARE_OPEN)) { frm->level++; if ((pc->type == CT_BRACE_OPEN) || (pc->type == CT_MACRO_OPEN)) { frm->brace_level++; } push_fmr_pse(frm, pc, BS_NONE, "+Open "); frm->pse[frm->pse_tos].parent = parent; pc->parent_type = parent; } pattern_class patcls = get_token_pattern_class(pc->type); /** Create a stack entry for complex statements IF/DO/FOR/WHILE/SWITCH */ if (patcls == PATCLS_BRACED) { push_fmr_pse(frm, pc, (pc->type == CT_DO) ? BS_BRACE_DO : BS_BRACE2, "+ComplexBraced"); } else if (patcls == PATCLS_PBRACED) { brstage_e bs = BS_PAREN1; if ((pc->type == CT_WHILE) && maybe_while_of_do(pc)) { pc->type = CT_WHILE_OF_DO; bs = BS_WOD_PAREN; } push_fmr_pse(frm, pc, bs, "+ComplexParenBraced"); } else if (patcls == PATCLS_OPBRACED) { push_fmr_pse(frm, pc, BS_OP_PAREN1, "+ComplexOpParenBraced"); } else if (patcls == PATCLS_ELSE) { push_fmr_pse(frm, pc, BS_ELSEIF, "+ComplexElse"); } /* Mark simple statement/expression starts * - after { or } * - after ';', but not if the paren stack top is a paren * - after '(' that has a parent type of CT_FOR */ if ((pc->type == CT_SQUARE_OPEN) || ((pc->type == CT_BRACE_OPEN) && (pc->parent_type != CT_ASSIGN)) || (pc->type == CT_BRACE_CLOSE) || (pc->type == CT_VBRACE_CLOSE) || ((pc->type == CT_SPAREN_OPEN) && (pc->parent_type == CT_FOR)) || (chunk_is_semicolon(pc) && (frm->pse[frm->pse_tos].type != CT_PAREN_OPEN) && (frm->pse[frm->pse_tos].type != CT_FPAREN_OPEN) && (frm->pse[frm->pse_tos].type != CT_SPAREN_OPEN))) { LOG_FMT(LSTMT, "%s: %d> reset1 stmt on %s\n", __func__, pc->orig_line, pc->str.c_str()); frm->stmt_count = 0; frm->expr_count = 0; } /* Mark expression starts */ chunk_t *tmp = chunk_get_next_ncnl(pc); if ((pc->type == CT_ARITH) || (pc->type == CT_ASSIGN) || (pc->type == CT_CASE) || (pc->type == CT_COMPARE) || ((pc->type == CT_STAR) && tmp && (tmp->type != CT_STAR)) || (pc->type == CT_BOOL) || (pc->type == CT_MINUS) || (pc->type == CT_PLUS) || (pc->type == CT_ANGLE_OPEN) || (pc->type == CT_ANGLE_CLOSE) || (pc->type == CT_RETURN) || (pc->type == CT_GOTO) || (pc->type == CT_CONTINUE) || (pc->type == CT_PAREN_OPEN) || (pc->type == CT_FPAREN_OPEN) || (pc->type == CT_SPAREN_OPEN) || (pc->type == CT_BRACE_OPEN) || chunk_is_semicolon(pc) || (pc->type == CT_COMMA) || (pc->type == CT_NOT) || (pc->type == CT_INV) || (pc->type == CT_COLON) || (pc->type == CT_QUESTION)) { frm->expr_count = 0; LOG_FMT(LSTMT, "%s: %d> reset expr on %s\n", __func__, pc->orig_line, pc->str.c_str()); } }
bool close_statement(parse_frame_t *frm, chunk_t *pc) { LOG_FUNC_ENTRY(); chunk_t *vbc = pc; LOG_FMT(LTOK, "%s(%d): orig_line is %zu, type is %s, '%s' type is %s, stage is %u\n", __func__, __LINE__, pc->orig_line, get_token_name(pc->type), pc->text(), get_token_name(frm->pse[frm->pse_tos].type), (unsigned int)frm->pse[frm->pse_tos].stage); if (cpd.consumed) { frm->stmt_count = 0; frm->expr_count = 0; LOG_FMT(LSTMT, "%s(%d): orig_line is %zu> reset2 stmt on '%s'\n", __func__, __LINE__, pc->orig_line, pc->text()); } /* * Insert a CT_VBRACE_CLOSE, if needed: * If we are in a virtual brace and we are not ON a CT_VBRACE_CLOSE add one */ if (frm->pse[frm->pse_tos].type == CT_VBRACE_OPEN) { // If the current token has already been consumed, then add after it if (cpd.consumed) { insert_vbrace_close_after(pc, frm); } else { // otherwise, add before it and consume the vbrace vbc = chunk_get_prev_ncnl(pc); vbc = insert_vbrace_close_after(vbc, frm); set_chunk_parent(vbc, frm->pse[frm->pse_tos].parent); frm->level--; frm->brace_level--; frm->pse_tos--; // Update the token level pc->level = frm->level; pc->brace_level = frm->brace_level; print_stack(LBCSPOP, "-CS VB ", frm, pc); // And repeat the close close_statement(frm, pc); return(true); } } // See if we are done with a complex statement if (frm->pse[frm->pse_tos].stage != brace_stage_e::NONE) { if (handle_complex_close(frm, vbc)) { return(true); } } return(false); } // close_statement