Files
scc/libs/parser/src/parse_stmt.c
zzy c99f64708e feat(parser): 改进解析器错误处理和表达式解析逻辑
- 在初始化解析中添加缺失的赋值操作符检查
- 改进后缀表达式解析逻辑,处理嵌套情况
- 添加数组下标初始化的赋值操作符验证
- 修复主表达式解析中的返回语句处理

refactor(pproc): 优化预处理器宏展开和位置追踪

- 添加token复制函数来保持原始位置信息
- 重构宏展开函数参数传递方式
- 修复字符串化参数的位置信息处理
- 改进可变参数宏的处理逻辑

test(parser): 增加标签语句和字符串字面量测试用例

- 添加返回语句with复合字面量的测试
- 增加标签继续语句的测试用例
- 添加字符串连接的解析测试

test(pproc): 添加预处理器位置追踪测试

- 增加双重宏定义位置追踪测试
- 添加带参数宏定义位置追踪测试
- 增加字符串化操作位置追踪测试

docs: 更新代码中的宏定义和注释

- 修正未定义标识符的拼写错误
- 添加必要的头文件包含
- 改进错误消息提示文本
2026-03-13 13:48:55 +08:00

457 lines
13 KiB
C

/*
A.2.3 Statements
(6.8)
statement:
labeled-statement
compound-statement
expression-statement
selection-statement
iteration-statement
jump-statement
(6.8.1)
labeled-statement:
identifier : statement
case constant-expression : statement
default : statement
(6.8.2)
compound-statement:
{ block-item-list(opt) }
(6.8.2)
block-item-list:
block-item
block-item-list block-item
(6.8.2)
block-item:
declaration
statement
(6.8.3)
expression-statement:
expression(opt) ;
(6.8.4)
selection-statement:
if ( expression ) statement
if ( expression ) statement else statement
switch ( expression ) statement
(6.8.5)
iteration-statement:
while ( expression ) statement
do statement while ( expression );
for ( expression(opt) ; expression(opt) ; expression(opt) ) statement
for ( declaration expression(opt) ; expression(opt) ) statement
(6.8.6)
jump-statement:
goto identifier ;
continue ;
break ;
return expression(opt) ;
*/
#include <parser_utils.h>
#include <scc_parser.h>
static inline scc_ast_stmt_t *ast_stmt_alloc() {
scc_ast_stmt_t *stmt = (scc_ast_stmt_t *)scc_malloc(sizeof(scc_ast_stmt_t));
if (stmt == null) {
LOG_FATAL("Out of memory");
}
return stmt;
}
static inline scc_ast_expr_t *ast_parse_paren_expression(scc_parser_t *parser) {
if (!scc_parser_consume_if(parser, SCC_TOK_L_PAREN)) {
SCC_ERROR(scc_parser_got_current_pos(parser),
"Expected '(' before like `( expression )` .");
}
scc_ast_expr_t *ret = scc_parse_expression(parser);
if (!scc_parser_consume_if(parser, SCC_TOK_R_PAREN)) {
SCC_ERROR(scc_parser_got_current_pos(parser),
"Expected ')' before like `( expression )` .");
}
return ret;
}
static scc_ast_stmt_t *parse_label_statement(scc_parser_t *parser) {
scc_lexer_tok_t tok = {0};
if (!scc_parser_next_consume(parser, &tok)) {
return null;
}
if (!scc_parser_consume_if(parser, SCC_TOK_COLON)) {
SCC_ERROR(scc_parser_got_current_pos(parser),
"Expected identifier before like `identifier : statement` .");
}
scc_ast_stmt_t *statement = scc_parse_statement(parser);
if (statement == null) {
Panic("expect stmt");
}
scc_ast_stmt_t *stmt = ast_stmt_alloc();
Assert(stmt != null);
scc_ast_stmt_label_init(stmt, scc_cstring_as_cstr(&tok.lexeme), statement);
return stmt;
}
static scc_ast_stmt_t *parse_case_statement(scc_parser_t *parser) {
if (!scc_parser_consume_if(parser, SCC_TOK_CASE)) {
return null;
}
scc_ast_expr_t *expr = null;
expr = scc_parser_constant_expression(parser);
if (expr == null) {
SCC_ERROR(scc_parser_got_current_pos(parser),
"Expected constant expression after case.");
return null;
}
if (!scc_parser_consume_if(parser, SCC_TOK_COLON)) {
SCC_ERROR(scc_parser_got_current_pos(parser),
"Expected `:` after case.");
return null;
}
scc_ast_stmt_t *statement = scc_parse_statement(parser);
if (statement == null) {
Panic("expect stmt");
}
scc_ast_stmt_t *stmt = ast_stmt_alloc();
scc_ast_stmt_case_init(stmt, expr, statement);
return stmt;
}
static scc_ast_stmt_t *parse_default_statement(scc_parser_t *parser) {
if (!scc_parser_consume_if(parser, SCC_TOK_DEFAULT)) {
return null;
}
if (!scc_parser_consume_if(parser, SCC_TOK_COLON)) {
SCC_ERROR(scc_parser_got_current_pos(parser),
"Expected constant expression after case.");
return null;
}
scc_ast_stmt_t *statement = scc_parse_statement(parser);
if (statement == null) {
Panic("expect stmt");
}
scc_ast_stmt_t *stmt = ast_stmt_alloc();
scc_ast_stmt_default_init(stmt, statement);
return stmt;
}
static scc_ast_stmt_t *parse_compound_statement(scc_parser_t *parser) {
if (!scc_parser_consume_if(parser, SCC_TOK_L_BRACE)) {
return null;
}
scc_ast_block_item_vec_t block_items;
scc_vec_init(block_items);
while (!scc_parser_consume_if(parser, SCC_TOK_R_BRACE)) {
/// TODO
// scc_parse_is_decl();
scc_ast_node_t *ret = null;
ret = (scc_ast_node_t *)scc_parse_declaration(parser);
if (ret == null) {
ret = (scc_ast_node_t *)scc_parse_statement(parser);
}
if (ret == null) {
SCC_ERROR(scc_parser_got_current_pos(parser), "Invalid statement");
// TODO free
parser->errcode = 1;
return null;
}
scc_vec_push(block_items, ret);
}
scc_ast_stmt_t *stmt = ast_stmt_alloc();
scc_ast_stmt_compound_init(stmt, &block_items);
return stmt;
}
static scc_ast_stmt_t *parse_if_statement(scc_parser_t *parser) {
if (!scc_parser_consume_if(parser, SCC_TOK_IF)) {
return null;
}
scc_ast_expr_t *expression = ast_parse_paren_expression(parser);
scc_ast_stmt_t *statement = scc_parse_statement(parser);
scc_ast_stmt_t *opt_else = null;
if (scc_parser_consume_if(parser, SCC_TOK_ELSE)) {
opt_else = scc_parse_statement(parser);
} else {
opt_else = null;
}
scc_ast_stmt_t *stmt = ast_stmt_alloc();
scc_ast_stmt_if_init(stmt, expression, statement, opt_else);
return stmt;
}
static scc_ast_stmt_t *parse_switch_statement(scc_parser_t *parser) {
if (!scc_parser_consume_if(parser, SCC_TOK_SWITCH)) {
return null;
}
scc_ast_expr_t *expression = ast_parse_paren_expression(parser);
scc_ast_stmt_t *statement = scc_parse_statement(parser);
scc_ast_stmt_t *stmt = ast_stmt_alloc();
scc_ast_stmt_switch_init(stmt, expression, statement);
return stmt;
}
static scc_ast_stmt_t *parse_while_statement(scc_parser_t *parser) {
if (!scc_parser_consume_if(parser, SCC_TOK_WHILE)) {
return null;
}
scc_ast_expr_t *expression = ast_parse_paren_expression(parser);
scc_ast_stmt_t *statement = scc_parse_statement(parser);
scc_ast_stmt_t *stmt = ast_stmt_alloc();
scc_ast_stmt_while_init(stmt, expression, statement);
return stmt;
}
static scc_ast_stmt_t *parse_do_while_statement(scc_parser_t *parser) {
if (!scc_parser_consume_if(parser, SCC_TOK_DO)) {
return null;
}
scc_ast_stmt_t *statement = scc_parse_statement(parser);
if (!scc_parser_consume_if(parser, SCC_TOK_WHILE)) {
SCC_ERROR(scc_parser_got_current_pos(parser),
"Expected 'while' after do.");
// TODO 使用更好的错误处理,未来应当采用更好的内存管理器
scc_free(statement);
return null;
}
scc_ast_expr_t *expression = ast_parse_paren_expression(parser);
scc_ast_stmt_t *stmt = ast_stmt_alloc();
scc_ast_stmt_do_while_init(stmt, expression, statement);
return stmt;
}
static scc_ast_stmt_t *parse_for_statement(scc_parser_t *parser) {
if (!scc_parser_consume_if(parser, SCC_TOK_FOR)) {
return null;
}
/*
for ( expression(opt) ; expression(opt) ; expression(opt) ) statement
for ( declaration expression(opt) ; expression(opt) ) statement
*/
if (!scc_parser_consume_if(parser, SCC_TOK_L_PAREN)) {
SCC_ERROR(scc_parser_got_current_pos(parser),
"Expected '(' before like `( expression )` .");
}
scc_ast_type_t *init = null;
scc_ast_expr_t *cond = null;
scc_ast_expr_t *incr = null;
scc_ast_stmt_t *body = null;
// TODO use decl or expr
init = (scc_ast_type_t *)scc_parse_declaration(parser);
if (init == null) {
init = (scc_ast_type_t *)scc_parse_expression(parser);
if (!scc_parser_consume_if(parser, SCC_TOK_SEMICOLON)) {
SCC_ERROR(scc_parser_got_current_pos(parser),
"Expected semicolon in for statement.");
}
}
cond = scc_parse_expression(parser);
if (!scc_parser_consume_if(parser, SCC_TOK_SEMICOLON)) {
SCC_ERROR(scc_parser_got_current_pos(parser),
"Expected semicolon in for statement.");
}
incr = scc_parse_expression(parser);
if (!scc_parser_consume_if(parser, SCC_TOK_R_PAREN)) {
SCC_ERROR(scc_parser_got_current_pos(parser),
"Expected ')' after like `( expression )` .");
}
body = scc_parse_statement(parser);
scc_ast_stmt_t *stmt = ast_stmt_alloc();
scc_ast_stmt_for_init(stmt, init, cond, incr, body);
return stmt;
}
static scc_ast_stmt_t *parse_jump_statement(scc_parser_t *parser) {
scc_ast_stmt_t *stmt = ast_stmt_alloc();
if (scc_parser_consume_if(parser, SCC_TOK_GOTO)) {
scc_lexer_tok_t tok = {0};
if (scc_parser_next_consume(parser, &tok)) {
scc_ast_stmt_goto_init(stmt, scc_cstring_as_cstr(&tok.lexeme));
} else {
SCC_ERROR(scc_parser_got_current_pos(parser),
"Expected label after goto.");
}
} else if (scc_parser_consume_if(parser, SCC_TOK_CONTINUE)) {
scc_ast_stmt_continue_init(stmt);
} else if (scc_parser_consume_if(parser, SCC_TOK_BREAK)) {
scc_ast_stmt_break_init(stmt);
} else if (scc_parser_consume_if(parser, SCC_TOK_RETURN)) {
scc_ast_stmt_return_init(stmt, scc_parse_expression(parser));
} else {
UNREACHABLE();
}
if (!scc_parser_consume_if(parser, SCC_TOK_SEMICOLON)) {
SCC_ERROR(scc_parser_got_current_pos(parser),
"Expected semicolon after jump statement.");
}
return stmt;
}
static scc_ast_stmt_t *parse_expression_statement(scc_parser_t *parser) {
if (scc_parser_consume_if(parser, SCC_TOK_SEMICOLON)) {
scc_ast_stmt_t *stmt = ast_stmt_alloc();
scc_ast_stmt_expr_init(stmt, null);
return stmt;
}
scc_ast_expr_t *expr = scc_parse_expression(parser);
if (expr == null) {
return null;
}
scc_ast_stmt_t *stmt = ast_stmt_alloc();
scc_ast_stmt_expr_init(stmt, expr);
if (!scc_parser_consume_if(parser, SCC_TOK_SEMICOLON)) {
SCC_ERROR(scc_parser_got_current_pos(parser),
"Expected semicolon after expression.");
}
return stmt;
}
scc_ast_stmt_t *scc_parse_statement(scc_parser_t *parser) {
scc_ast_stmt_t *stmt;
const scc_lexer_tok_t *tok_ref;
tok_ref = scc_parser_peek(parser);
if (!tok_ref) {
return null;
}
switch (tok_ref->type) {
/*
(6.8.1)
labeled-statement:
identifier : statement
case constant-expression : statement
default : statement
*/
case SCC_TOK_IDENT:
scc_parser_next(parser);
tok_ref = scc_parser_next(parser);
if (tok_ref == null || tok_ref->type != SCC_TOK_COLON) {
scc_parser_reset(parser);
break;
}
scc_parser_reset(parser);
stmt = parse_label_statement(parser);
goto RETURN;
case SCC_TOK_CASE: {
stmt = parse_case_statement(parser);
goto RETURN;
}
case SCC_TOK_DEFAULT:
stmt = parse_default_statement(parser);
goto RETURN;
/*
(6.8.2)
compound-statement:
{ block-item-list(opt) }
(6.8.2)
block-item-list:
block-item
block-item-list block-item
(6.8.2)
block-item:
declaration
statement
*/
case SCC_TOK_L_BRACE:
stmt = parse_compound_statement(parser);
goto RETURN;
/*
(6.8.4)
selection-statement:
if ( expression ) statement
if ( expression ) statement else statement
switch ( expression ) statement
*/
case SCC_TOK_IF:
stmt = parse_if_statement(parser);
goto RETURN;
case SCC_TOK_SWITCH:
stmt = parse_switch_statement(parser);
goto RETURN;
/*
(6.8.5)
iteration-statement:
while ( expression ) statement
do statement while ( expression );
for ( expression(opt) ; expression(opt) ; expression(opt) )
statement
for ( declaration expression(opt) ; expression(opt) )
statement
*/
case SCC_TOK_WHILE:
stmt = parse_while_statement(parser);
goto RETURN;
case SCC_TOK_DO:
stmt = parse_do_while_statement(parser);
goto RETURN;
case SCC_TOK_FOR:
stmt = parse_for_statement(parser);
goto RETURN;
/*
(6.8.6)
jump-statement:
goto identifier ;
continue ;
break ;
return expression(opt) ;
*/
case SCC_TOK_GOTO:
case SCC_TOK_CONTINUE:
case SCC_TOK_BREAK:
case SCC_TOK_RETURN:
stmt = parse_jump_statement(parser);
goto RETURN;
default:
break;
}
/*
(6.8.3)
expression-statement:
expression(opt) ;
*/
stmt = parse_expression_statement(parser);
RETURN:
scc_parser_reset(parser);
if (stmt) {
parser->sema_callbacks.on_stmt(parser->sema_callbacks.context,
stmt->base.type, stmt);
}
return stmt;
}