blob: 5144cbd5b1d5ef004ac20676af36ee3575a3ffcf [file] [log] [blame]
/**
* @file brace_cleanup.cpp
* Determines the brace level and paren level.
* Inserts virtual braces as needed.
* Handles all that preprocessor crap.
*
* @author Ben Gardner
* @license GPL v2+
*/
#include "uncrustify_types.h"
#include "char_table.h"
#include "prototypes.h"
#include "chunk_list.h"
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cerrno>
#include "unc_ctype.h"
static chunk_t *insert_vbrace(chunk_t *pc, bool after,
struct parse_frame *frm);
#define insert_vbrace_close_after(pc, frm) insert_vbrace(pc, true, frm)
#define insert_vbrace_open_before(pc, frm) insert_vbrace(pc, false, frm)
static void parse_cleanup(struct parse_frame *frm, chunk_t *pc);
static bool close_statement(struct parse_frame *frm, chunk_t *pc);
static bool check_complex_statements(struct parse_frame *frm, chunk_t *pc);
static bool handle_complex_close(struct parse_frame *frm, chunk_t *pc);
static int preproc_start(struct parse_frame *frm, chunk_t *pc)
{
LOG_FUNC_ENTRY();
chunk_t *next;
int pp_level = cpd.pp_level;
/* Get the type of preprocessor and handle it */
next = chunk_get_next_ncnl(pc);
if (next != NULL)
{
cpd.in_preproc = next->type;
/**
* If we are in a define, push the frame stack.
*/
if (cpd.in_preproc == CT_PP_DEFINE)
{
pf_push(frm);
/* a preproc body starts a new, blank frame */
memset(frm, 0, sizeof(*frm));
frm->level = 1;
frm->brace_level = 1;
/*TODO: not sure about the next 3 lines */
frm->pse_tos = 1;
frm->pse[frm->pse_tos].type = CT_PP_DEFINE;
frm->pse[frm->pse_tos].stage = BS_NONE;
}
else
{
/* Check for #if, #else, #endif, etc */
pp_level = pf_check(frm, pc);
}
}
return(pp_level);
}
static void print_stack(log_sev_t logsev, const char *str,
struct parse_frame *frm, chunk_t *pc)
{
LOG_FUNC_ENTRY();
if (log_sev_on(logsev))
{
int idx;
log_fmt(logsev, "%8.8s", str);
for (idx = 1; idx <= frm->pse_tos; idx++)
{
if (frm->pse[idx].stage != BS_NONE)
{
LOG_FMT(logsev, " [%s - %d]", get_token_name(frm->pse[idx].type),
frm->pse[idx].stage);
}
else
{
LOG_FMT(logsev, " [%s]", get_token_name(frm->pse[idx].type));
}
}
log_fmt(logsev, "\n");
}
}
/**
* Scans through the whole list and does stuff.
* It has to do some tricks to parse preprocessors.
*
* TODO: This can be cleaned up and simplified - we can look both forward and backward!
*/
void brace_cleanup(void)
{
LOG_FUNC_ENTRY();
chunk_t *pc;
struct parse_frame frm;
int pp_level;
memset(&frm, 0, sizeof(frm));
cpd.frame_count = 0;
cpd.in_preproc = CT_NONE;
cpd.pp_level = 0;
pc = chunk_get_head();
while (pc != NULL)
{
/* Check for leaving a #define body */
if ((cpd.in_preproc != CT_NONE) && ((pc->flags & PCF_IN_PREPROC) == 0))
{
if (cpd.in_preproc == CT_PP_DEFINE)
{
/* out of the #define body, restore the frame */
pf_pop(&frm);
}
cpd.in_preproc = CT_NONE;
}
/* Check for a preprocessor start */
pp_level = cpd.pp_level;
if (pc->type == CT_PREPROC)
{
pp_level = preproc_start(&frm, pc);
}
/* Do before assigning stuff from the frame */
if ((cpd.lang_flags & LANG_PAWN) != 0)
{
if ((frm.pse[frm.pse_tos].type == CT_VBRACE_OPEN) &&
(pc->type == CT_NEWLINE))
{
pc = pawn_check_vsemicolon(pc);
}
}
/* Assume the level won't change */
pc->level = frm.level;
pc->brace_level = frm.brace_level;
pc->pp_level = pp_level;
/**
* #define bodies get the full formatting treatment
* Also need to pass in the initial '#' to close out any virtual braces.
*/
if (!chunk_is_comment(pc) && !chunk_is_newline(pc) &&
((cpd.in_preproc == CT_PP_DEFINE) ||
(cpd.in_preproc == CT_NONE)))
{
cpd.consumed = false;
parse_cleanup(&frm, pc);
print_stack(LBCSAFTER, (pc->type == CT_VBRACE_CLOSE) ? "Virt-}" : pc->str, &frm, pc);
}
pc = chunk_get_next(pc);
}
}
/**
* pc is a CT_WHILE.
* Scan backwards to see if we find a brace/vbrace with the parent set to CT_DO
*/
static bool maybe_while_of_do(chunk_t *pc)
{
LOG_FUNC_ENTRY();
chunk_t *prev;
prev = chunk_get_prev_ncnl(pc);
if ((prev == NULL) || !(prev->flags & PCF_IN_PREPROC))
{
return(false);
}
/* Find the chunk before the preprocessor */
while ((prev != NULL) && (prev->flags & PCF_IN_PREPROC))
{
prev = chunk_get_prev_ncnl(prev);
}
if ((prev != NULL) &&
(prev->parent_type == CT_DO) &&
((prev->type == CT_VBRACE_CLOSE) ||
(prev->type == CT_BRACE_CLOSE)))
{
return(true);
}
return(false);
}
static void push_fmr_pse(struct parse_frame *frm, chunk_t *pc,
brstage_e stage, const char *logtext)
{
LOG_FUNC_ENTRY();
if (frm->pse_tos < ((int)ARRAY_SIZE(frm->pse) - 1))
{
frm->pse_tos++;
frm->pse[frm->pse_tos].type = pc->type;
frm->pse[frm->pse_tos].stage = stage;
frm->pse[frm->pse_tos].pc = pc;
print_stack(LBCSPUSH, logtext, frm, pc);
}
else
{
LOG_FMT(LWARN, "%s:%d Error: Frame stack overflow, Unable to properly process this file.\n",
cpd.filename, cpd.line_number);
cpd.error_count++;
}
}
/**
* 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)
{
LOG_FUNC_ENTRY();
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))
{
set_chunk_parent(pc, 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)))
{
set_chunk_type(pc, (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 */
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 != 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;
set_chunk_parent(pc, 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)
{
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 != BS_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, BS_NONE, "+Open ");
frm->pse[frm->pse_tos].parent = parent;
set_chunk_parent(pc, 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))
{
set_chunk_type(pc, 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)) ||
(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> 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_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> reset expr on %s\n",
__func__, pc->orig_line, pc->str.c_str());
}
}
/**
* Checks the progression of complex statements.
* - checks for else after if
* - checks for if after else
* - checks for while after do
* - checks for open brace in BRACE2 and BRACE_DO stages, inserts open VBRACE
* - checks for open paren in PAREN1 and PAREN2 stages, complains
*
* @param frm The parse frame
* @param pc The current chunk
* @return true - done with this chunk, false - keep processing
*/
static bool check_complex_statements(struct parse_frame *frm, chunk_t *pc)
{
LOG_FUNC_ENTRY();
c_token_t parent;
chunk_t *vbrace;
/* Turn an optional paren into either a real paren or a brace */
if (frm->pse[frm->pse_tos].stage == BS_OP_PAREN1)
{
frm->pse[frm->pse_tos].stage = (pc->type != CT_PAREN_OPEN) ? BS_BRACE2 : BS_PAREN1;
}
/* Check for CT_ELSE after CT_IF */
while (frm->pse[frm->pse_tos].stage == BS_ELSE)
{
if (pc->type == CT_ELSE)
{
/* Replace CT_IF with CT_ELSE on the stack & we are done */
frm->pse[frm->pse_tos].type = CT_ELSE;
frm->pse[frm->pse_tos].stage = BS_ELSEIF;
print_stack(LBCSSWAP, "=Swap ", frm, pc);
return(true);
}
/* Remove the CT_IF and close the statement */
frm->pse_tos--;
print_stack(LBCSPOP, "-IF-CCS ", frm, pc);
if (close_statement(frm, pc))
{
return(true);
}
}
/* Check for CT_IF after CT_ELSE */
if (frm->pse[frm->pse_tos].stage == BS_ELSEIF)
{
if (pc->type == CT_IF)
{
if (!cpd.settings[UO_indent_else_if].b ||
!chunk_is_newline(chunk_get_prev_nc(pc)))
{
/* Replace CT_ELSE with CT_IF */
set_chunk_type(pc, CT_ELSEIF);
frm->pse[frm->pse_tos].type = CT_ELSEIF;
frm->pse[frm->pse_tos].stage = BS_PAREN1;
return(true);
}
}
/* Jump to the 'expecting brace' stage */
frm->pse[frm->pse_tos].stage = BS_BRACE2;
}
/* Check for CT_CATCH or CT_FINALLY after CT_TRY or CT_CATCH */
while (frm->pse[frm->pse_tos].stage == BS_CATCH)
{
if ((pc->type == CT_CATCH) || (pc->type == CT_FINALLY))
{
/* Replace CT_TRY with CT_CATCH on the stack & we are done */
frm->pse[frm->pse_tos].type = pc->type;
frm->pse[frm->pse_tos].stage = (pc->type == CT_CATCH) ? BS_OP_PAREN1 : BS_BRACE2;
print_stack(LBCSSWAP, "=Swap ", frm, pc);
return(true);
}
/* Remove the CT_TRY and close the statement */
frm->pse_tos--;
print_stack(LBCSPOP, "-TRY-CCS ", frm, pc);
if (close_statement(frm, pc))
{
return(true);
}
}
/* Check for CT_WHILE after the CT_DO */
if (frm->pse[frm->pse_tos].stage == BS_WHILE)
{
if (pc->type == CT_WHILE)
{
set_chunk_type(pc, CT_WHILE_OF_DO);
frm->pse[frm->pse_tos].type = CT_WHILE_OF_DO; //CT_WHILE;
frm->pse[frm->pse_tos].stage = BS_WOD_PAREN;
return(true);
}
LOG_FMT(LWARN, "%s:%d Error: Expected 'while', got '%s'\n",
cpd.filename, pc->orig_line, pc->str.c_str());
frm->pse_tos--;
print_stack(LBCSPOP, "-Error ", frm, pc);
cpd.error_count++;
}
/* Insert a CT_VBRACE_OPEN, if needed */
if ((pc->type != CT_BRACE_OPEN) &&
((frm->pse[frm->pse_tos].stage == BS_BRACE2) ||
(frm->pse[frm->pse_tos].stage == BS_BRACE_DO)))
{
parent = frm->pse[frm->pse_tos].type;
vbrace = insert_vbrace_open_before(pc, frm);
set_chunk_parent(vbrace, parent);
frm->level++;
frm->brace_level++;
push_fmr_pse(frm, vbrace, BS_NONE, "+VBrace ");
frm->pse[frm->pse_tos].parent = parent;
/* update the level of pc */
pc->level = frm->level;
pc->brace_level = frm->brace_level;
/* Mark as a start of a statement */
frm->stmt_count = 0;
frm->expr_count = 0;
pc->flags |= PCF_STMT_START | PCF_EXPR_START;
frm->stmt_count = 1;
frm->expr_count = 1;
LOG_FMT(LSTMT, "%d] 2.marked %s as stmt start\n", pc->orig_line, pc->str.c_str());
}
/* Verify open paren in complex statement */
if ((pc->type != CT_PAREN_OPEN) &&
((frm->pse[frm->pse_tos].stage == BS_PAREN1) ||
(frm->pse[frm->pse_tos].stage == BS_WOD_PAREN)))
{
LOG_FMT(LWARN, "%s:%d Error: Expected '(', got '%s' for '%s'\n",
cpd.filename, pc->orig_line, pc->str.c_str(),
get_token_name(frm->pse[frm->pse_tos].type));
/* Throw out the complex statement */
frm->pse_tos--;
print_stack(LBCSPOP, "-Error ", frm, pc);
cpd.error_count++;
}
return(false);
}
/**
* Handles a close paren or brace - just progress the stage, if the end
* of the statement is hit, call close_statement()
*
* @param frm The parse frame
* @param pc The current chunk
* @return true - done with this chunk, false - keep processing
*/
static bool handle_complex_close(struct parse_frame *frm, chunk_t *pc)
{
LOG_FUNC_ENTRY();
chunk_t *next;
if (frm->pse[frm->pse_tos].stage == BS_PAREN1)
{
/* PAREN1 always => BRACE2 */
frm->pse[frm->pse_tos].stage = BS_BRACE2;
}
else if (frm->pse[frm->pse_tos].stage == BS_BRACE2)
{
/* BRACE2: IF => ELSE, anyting else => close */
if ((frm->pse[frm->pse_tos].type == CT_IF) ||
(frm->pse[frm->pse_tos].type == CT_ELSEIF))
{
frm->pse[frm->pse_tos].stage = BS_ELSE;
/* If the next chunk isn't CT_ELSE, close the statement */
next = chunk_get_next_ncnl(pc);
if ((next != NULL) && (next->type != CT_ELSE))
{
frm->pse_tos--;
print_stack(LBCSPOP, "-IF-HCS ", frm, pc);
if (close_statement(frm, pc))
{
return(true);
}
}
}
else if ((frm->pse[frm->pse_tos].type == CT_TRY) ||
(frm->pse[frm->pse_tos].type == CT_CATCH))
{
frm->pse[frm->pse_tos].stage = BS_CATCH;
/* If the next chunk isn't CT_CATCH or CT_FINALLY, close the statement */
next = chunk_get_next_ncnl(pc);
if ((next != NULL) &&
(next->type != CT_CATCH) &&
(next->type != CT_FINALLY))
{
frm->pse_tos--;
print_stack(LBCSPOP, "-TRY-HCS ", frm, pc);
if (close_statement(frm, pc))
{
return(true);
}
}
}
else
{
LOG_FMT(LNOTE, "%s: close_statement on %s BS_BRACE2\n", __func__,
get_token_name(frm->pse[frm->pse_tos].type));
frm->pse_tos--;
print_stack(LBCSPOP, "-HCC B2 ", frm, pc);
if (close_statement(frm, pc))
{
return(true);
}
}
}
else if (frm->pse[frm->pse_tos].stage == BS_BRACE_DO)
{
frm->pse[frm->pse_tos].stage = BS_WHILE;
}
else if (frm->pse[frm->pse_tos].stage == BS_WOD_PAREN)
{
LOG_FMT(LNOTE, "%s: close_statement on %s BS_WOD_PAREN\n", __func__,
get_token_name(frm->pse[frm->pse_tos].type));
frm->pse[frm->pse_tos].stage = BS_WOD_SEMI;
print_stack(LBCSPOP, "-HCC WoDP ", frm, pc);
}
else if (frm->pse[frm->pse_tos].stage == BS_WOD_SEMI)
{
LOG_FMT(LNOTE, "%s: close_statement on %s BS_WOD_SEMI\n", __func__,
get_token_name(frm->pse[frm->pse_tos].type));
frm->pse_tos--;
print_stack(LBCSPOP, "-HCC WoDS ", frm, pc);
if (close_statement(frm, pc))
{
return(true);
}
}
else
{
/* PROBLEM */
LOG_FMT(LWARN, "%s:%d Error: TOS.type='%s' TOS.stage=%d\n",
cpd.filename, pc->orig_line,
get_token_name(frm->pse[frm->pse_tos].type),
frm->pse[frm->pse_tos].stage);
cpd.error_count++;
}
return(false);
}
static chunk_t *insert_vbrace(chunk_t *pc, bool after,
struct parse_frame *frm)
{
LOG_FUNC_ENTRY();
chunk_t chunk;
chunk_t *rv;
chunk_t *ref;
chunk.orig_line = pc->orig_line;
chunk.parent_type = frm->pse[frm->pse_tos].type;
chunk.level = frm->level;
chunk.brace_level = frm->brace_level;
chunk.flags = pc->flags & PCF_COPY_FLAGS;
chunk.str = "";
if (after)
{
chunk.type = CT_VBRACE_CLOSE;
rv = chunk_add_after(&chunk, pc);
}
else
{
ref = chunk_get_prev(pc);
if ((ref->flags & PCF_IN_PREPROC) == 0)
{
chunk.flags &= ~PCF_IN_PREPROC;
}
while (chunk_is_newline(ref) || chunk_is_comment(ref))
{
ref->level++;
ref->brace_level++;
ref = chunk_get_prev(ref);
}
/* Don't back into a preprocessor */
if (((pc->flags & PCF_IN_PREPROC) == 0) &&
((ref->flags & PCF_IN_PREPROC) != 0))
{
if (ref->type == CT_PREPROC_BODY)
{
do
{
ref = chunk_get_prev(ref);
} while ((ref != NULL) && ((ref->flags & PCF_IN_PREPROC) != 0));
}
else
{
ref = chunk_get_next(ref);
}
}
chunk.orig_line = ref->orig_line;
chunk.column = ref->column + ref->len() + 1;
chunk.type = CT_VBRACE_OPEN;
rv = chunk_add_after(&chunk, ref);
}
return(rv);
}
/**
* 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)
{
LOG_FUNC_ENTRY();
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);
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 != BS_NONE)
{
if (handle_complex_close(frm, vbc))
{
return(true);
}
}
return(false);
}