feat(lexer, preprocessor): replace cstring conversion with copy and refactor macro expansion

- Replace `scc_cstring_from_cstr(scc_cstring_as_cstr(...))` with `scc_cstring_copy()` in lexer to fix memory leaks
- Extract macro expansion logic into separate `expand_macro.c` file
- Remove `expand_stack` parameter from `scc_pp_expand_macro()` function
- Add new parsing functions for macro replacement lists and arguments
- Add string utility functions for whitespace trimming and string joining
- Update memory stream documentation for clarity
This commit is contained in:
zzy
2025-12-15 20:24:39 +08:00
parent 73d74f5e13
commit 07f5d9331b
15 changed files with 574 additions and 346 deletions

View File

@@ -79,7 +79,7 @@ void scc_lexer_init(scc_lexer_t *lexer, scc_probe_stream_t *stream) {
lexer->stream = stream;
lexer->pos = scc_pos_init();
// FIXME
lexer->pos.name = scc_cstring_from_cstr(scc_cstring_as_cstr(&stream->name));
lexer->pos.name = scc_cstring_copy(&stream->name);
}
#define set_err_token(token) ((token)->type = SCC_TOK_UNKNOWN)
@@ -135,7 +135,7 @@ static void parse_line(scc_lexer_t *lexer, scc_lexer_tok_t *token) {
scc_lex_parse_skip_line(lexer->stream, &lexer->pos);
token->loc.line = n;
// FIXME memory leak
token->loc.name = scc_cstring_from_cstr(scc_cstring_as_cstr(&str));
token->loc.name = scc_cstring_copy(&str);
scc_cstring_free(&str);
return;
SKIP_LINE:

View File

@@ -5,9 +5,14 @@
#include <pp_macro.h>
void scc_pp_parse_directive(scc_probe_stream_t *stream, scc_pos_t *pos,
scc_pp_macro_table_t *macros);
cbool scc_pp_parse_macro_replace_list(scc_probe_stream_t *stream,
scc_pp_macro_list_t *list);
cbool scc_pp_parse_macro_arguments(scc_probe_stream_t *stream,
scc_pp_macro_list_t *args);
// expand
cbool scc_pp_expand_macro(scc_probe_stream_t *stream,
scc_pp_macro_table_t *macros,
scc_pp_macro_table_t *expand_stack,
scc_probe_stream_t **out_stream, int depth);
#endif /* __SCC_PP_PARSE_H__ */

View File

@@ -0,0 +1,296 @@
#include <lex_parser.h>
#include <libutils.h>
#include <pp_macro.h>
#include <pp_parse.h>
static inline void scc_generate_cstr(scc_cstring_t *buff) {
scc_cstring_t out_buff = scc_cstring_new();
scc_cstring_append_ch(&out_buff, '\"');
// TODO it is too simple
scc_cstring_append(&out_buff, buff);
scc_cstring_append_ch(&out_buff, '\"');
// FIXME 可能有着更好的解决方案
scc_cstring_clear(buff);
scc_cstring_append(buff, &out_buff);
scc_cstring_free(&out_buff);
}
#define SCC_PP_IS_LIST_BLANK(i) \
((i) < list->size && scc_vec_at(*list, (i)).data[0] == ' ' && \
scc_vec_at(*list, (i)).data[1] == '\0')
#define SCC_PP_IS_LIST_TO_STRING(i) \
((i) < list->size && scc_vec_at(*list, (i)).data[0] == '#' && \
scc_vec_at(*list, (i)).data[1] == '\0')
#define SCC_PP_IS_LIST_CONNECT(i) \
((i) < list->size && scc_vec_at(*list, (i)).data[0] == '#' && \
scc_vec_at(*list, (i)).data[1] == '#' && \
scc_vec_at(*list, (i)).data[2] == '\0')
#define SCC_PP_USE_CONNECT(font, rear) \
if (rear < list->size) { \
scc_cstring_append(out_buff, &scc_vec_at(*list, font)); \
scc_cstring_append(out_buff, &scc_vec_at(*list, rear)); \
} else { \
scc_cstring_append(out_buff, &scc_vec_at(*list, font)); \
}
// for # ## to generator string
static inline cbool scc_pp_expand_string_unsafe(scc_pp_macro_list_t *list,
scc_cstring_t *out_buff) {
for (usize i = 0; i < list->size; ++i) {
if (SCC_PP_IS_LIST_BLANK(i + 1)) {
if (SCC_PP_IS_LIST_CONNECT(i + 2)) {
SCC_PP_USE_CONNECT(i, i + 3);
i += 3;
continue;
}
} else if (SCC_PP_IS_LIST_CONNECT(i + 1)) {
SCC_PP_USE_CONNECT(i, i + 2);
i += 2;
continue;
} else if (SCC_PP_IS_LIST_TO_STRING(i)) {
i += 1;
if (i < list->size) {
scc_generate_cstr(&scc_vec_at(*list, i));
} else {
LOG_WARN("# need a valid literator");
break;
}
}
scc_cstring_append(out_buff, &scc_vec_at(*list, i));
}
return true;
}
// 展开对象宏
cbool scc_pp_expand_object_macro(scc_pp_macro_t *macro,
scc_cstring_t *out_buff) {
Assert(macro->type == SCC_PP_MACRO_OBJECT && macro->params.size == 0);
Assert(scc_cstring_is_empty(out_buff) == true);
// 对象宏输出替换文本并进行递归展开
scc_pp_expand_string_unsafe(&macro->replaces, out_buff);
return true;
}
// 展开函数宏
cbool scc_pp_expand_function_macro(scc_pp_macro_t *macro,
scc_pp_macro_list_t *origin_params,
scc_pp_macro_list_t *params,
scc_cstring_t *out_buff) {
Assert(macro->type == SCC_PP_MACRO_FUNCTION);
Assert(out_buff != null);
Assert(scc_cstring_is_empty(out_buff) == true);
Assert(params->size == macro->params.size);
scc_pp_macro_list_t list;
scc_vec_init(list);
for (usize i = 0; i < macro->replaces.size; ++i) {
// TODO ... __VA_ARGS__
for (usize j = 0; j < macro->params.size; ++j) {
if (scc_strcmp(
scc_cstring_as_cstr(&scc_vec_at(macro->replaces, i)),
scc_cstring_as_cstr(&scc_vec_at(macro->params, j))) == 0) {
Assert(&scc_vec_at(*params, j) != null);
scc_vec_push(list, scc_cstring_copy(&scc_vec_at(*params, j)));
goto END;
}
}
scc_vec_push(list, scc_cstring_copy(&scc_vec_at(macro->replaces, i)));
END:;
}
scc_pp_expand_string_unsafe(&list, out_buff);
for (usize i = 0; i < list.size; ++i) {
scc_cstring_free(&scc_vec_at(list, i));
}
scc_vec_free(list);
return true;
}
// 状态管理结构
typedef struct {
scc_pp_macro_table_t *macros;
scc_pp_macro_table_t painted_blue; // 正在展开的宏
int depth;
} macro_expansion_state_t;
// 进入宏展开
static void enter_macro_expansion(macro_expansion_state_t *state,
scc_pp_macro_t *macro) {
// 添加到活动宏集合
scc_pp_macro_table_set(&state->painted_blue,
scc_pp_macro_new(&macro->name, macro->type));
}
// 离开宏展开(开始重新扫描)
static void leave_macro_expansion(macro_expansion_state_t *state,
scc_pp_macro_t *macro) {
// 从活动宏移除,添加到禁用宏
scc_pp_macro_table_remove(&state->painted_blue, &macro->name);
}
// 检查是否可以展开
static cbool can_expand_macro(macro_expansion_state_t *state,
scc_pp_macro_t *macro) {
return scc_pp_macro_table_get(&state->painted_blue, &macro->name) == null;
}
static cbool _scc_pp_expand_macro(scc_probe_stream_t *stream,
macro_expansion_state_t *state,
scc_probe_stream_t **out_stream);
static cbool expanded_buffer(const scc_cstring_t *in, scc_cstring_t *out,
macro_expansion_state_t *state) {
scc_probe_stream_t *in_stream = scc_mem_probe_stream_new(
scc_cstring_as_cstr(in), scc_cstring_len(in), false);
// rescanning
int ch;
while ((ch = scc_probe_stream_peek(in_stream)) != scc_stream_eof) {
if (scc_lex_parse_is_identifier_prefix(ch)) {
// 递归检查
scc_probe_stream_t *out_stream;
if (_scc_pp_expand_macro(in_stream, state, &out_stream) == false) {
scc_cstring_free(out);
return false;
}
Assert(out_stream != null);
while (scc_probe_stream_peek(out_stream) != scc_stream_eof) {
scc_cstring_append_ch(out,
scc_probe_stream_consume(out_stream));
}
Assert(out_stream != null && out_stream->drop != null);
scc_probe_stream_drop(out_stream);
} else {
scc_cstring_append_ch(out, scc_probe_stream_consume(in_stream));
}
}
scc_probe_stream_drop(in_stream);
return true;
}
/**
* 1. 参数先展开
* 2. 替换后重扫描
* 3. 蓝色集合中不展开
* 4. #, ## 不展开
* 5. 最后的括号要检查
*/
static cbool _scc_pp_expand_macro(scc_probe_stream_t *stream,
macro_expansion_state_t *state,
scc_probe_stream_t **out_stream) {
// TODO self position and it maybe is a stack on #include ?
// 递归扫描
if (state->depth-- <= 0) {
*out_stream = null;
return false;
}
scc_pp_macro_table_t *macros = state->macros;
scc_cstring_t identifier = scc_cstring_new();
scc_pos_t pos = scc_pos_init();
cbool ret;
ret = scc_lex_parse_identifier(stream, &pos, &identifier);
Assert(ret == true);
scc_pp_macro_t *macro = scc_pp_macro_table_get(macros, &identifier);
// 1. 不是宏,直接输出标识符
if (macro == null) {
// 不是宏,直接输出
usize length = scc_cstring_len(&identifier);
*out_stream = scc_mem_probe_stream_new(
scc_cstring_move_cstr(&identifier), length, true);
return true;
} else {
scc_cstring_free(&identifier);
}
// 收集参数(如果是函数宏)
scc_pp_macro_list_t params;
scc_vec_init(params);
if (macro->type == SCC_PP_MACRO_FUNCTION) {
// TODO when expand need check another func with () at the end
scc_lex_parse_skip_whitespace(stream, &pos);
if (scc_probe_stream_peek(stream) != '(') {
goto ORIGIN;
}
ret = scc_pp_parse_macro_arguments(stream, &params);
Assert(ret == true);
}
// 2. 检查到重复展开跳过
// 检查是否可以展开
if (!can_expand_macro(state, macro)) {
ORIGIN:
// 输出原始调用
scc_cstring_t original = scc_cstring_new();
scc_cstring_append(&original, &macro->name);
if (macro->type == SCC_PP_MACRO_FUNCTION && params.size != 0) {
scc_cstring_append_ch(&original, '(');
for (usize i = 0; i < params.size; ++i) {
scc_cstring_append(&original, &scc_vec_at(params, i));
if (i != params.size - 1) {
scc_cstring_append_ch(&original, ',');
scc_cstring_append_ch(&original, ' ');
}
}
scc_cstring_append_ch(&original, ')');
}
*out_stream = scc_mem_probe_stream_new(
scc_cstring_as_cstr(&original), scc_cstring_len(&original), true);
scc_vec_free(params);
return true;
}
// 开始展开
scc_cstring_t expanded = scc_cstring_new();
if (macro->type == SCC_PP_MACRO_OBJECT) {
ret = scc_pp_expand_object_macro(macro, &expanded);
Assert(ret == true);
goto RESCANNING;
} else if (macro->type != SCC_PP_MACRO_FUNCTION) {
TODO();
}
Assert(macro->type == SCC_PP_MACRO_FUNCTION);
scc_pp_macro_list_t expanded_params;
scc_vec_init(expanded_params);
// expand params fisrt and recursive
for (usize i = 0; i < params.size; ++i) {
scc_cstring_t param = scc_vec_at(params, i);
scc_cstring_t out = scc_cstring_new();
expanded_buffer(&param, &out, state);
scc_vec_push(expanded_params, out);
}
ret = scc_pp_expand_function_macro(macro, &params, &expanded_params,
&expanded);
Assert(ret == true);
RESCANNING:
// 重新扫描展开结果
// 将展开内容变换成stream并递归展开
scc_cstring_t rescanned = scc_cstring_new();
enter_macro_expansion(state, macro);
expanded_buffer(&expanded, &rescanned, state);
leave_macro_expansion(state, macro);
scc_cstring_free(&expanded);
*out_stream = scc_mem_probe_stream_new(scc_cstring_as_cstr(&rescanned),
scc_cstring_len(&rescanned), true);
return true;
}
cbool scc_pp_expand_macro(scc_probe_stream_t *stream,
scc_pp_macro_table_t *macros,
scc_probe_stream_t **out_stream, int depth) {
Assert(depth > 0 && stream != null && macros != null && out_stream != null);
macro_expansion_state_t state;
state.depth = depth;
scc_pp_marco_table_init(&state.painted_blue);
state.macros = macros;
cbool ret = _scc_pp_expand_macro(stream, &state, out_stream);
scc_pp_macro_table_drop(&state.painted_blue);
return ret;
}

View File

@@ -9,7 +9,7 @@ scc_pp_macro_t *scc_pp_macro_new(const scc_cstring_t *name,
return null;
}
macro->name = scc_cstring_from_cstr(scc_cstring_as_cstr(name));
macro->name = scc_cstring_copy(name);
macro->type = type;
scc_vec_init(macro->params);
scc_vec_init(macro->replaces);

View File

@@ -1,9 +1,7 @@
#include <ctype.h>
#include <lex_parser.h>
#include <pp_macro.h>
#include <pp_parse.h>
#include <pp_token.h>
#include <string.h>
static const struct {
const char *name;
@@ -57,7 +55,7 @@ static inline void try_to_cut_list(scc_pp_macro_list_t *list,
}
}
static cbool parse_macro_replace_list(scc_probe_stream_t *stream,
cbool scc_pp_parse_macro_replace_list(scc_probe_stream_t *stream,
scc_pp_macro_list_t *list) {
Assert(stream != null && list != null);
scc_probe_stream_reset(stream);
@@ -114,7 +112,7 @@ static cbool parse_macro_replace_list(scc_probe_stream_t *stream,
}
// 解析宏参数列表
static cbool parse_macro_arguments(scc_probe_stream_t *stream,
cbool scc_pp_parse_macro_arguments(scc_probe_stream_t *stream,
scc_pp_macro_list_t *args) {
Assert(stream != null && args != null);
@@ -237,7 +235,7 @@ void scc_pp_parse_directive(scc_probe_stream_t *stream, scc_pos_t *pos,
if (!has_whitespace && ch == '(') {
// 函数宏
scc_pp_macro_list_t params;
if (!parse_macro_arguments(stream, &params)) {
if (!scc_pp_parse_macro_arguments(stream, &params)) {
goto ERR;
}
@@ -250,12 +248,12 @@ void scc_pp_parse_directive(scc_probe_stream_t *stream, scc_pos_t *pos,
}
scc_pp_macro_list_t replacement;
parse_macro_replace_list(stream, &replacement);
scc_pp_parse_macro_replace_list(stream, &replacement);
scc_pp_add_function_macro(macros, &name, &params, &replacement);
} else {
// 对象宏
scc_pp_macro_list_t replacement;
parse_macro_replace_list(stream, &replacement);
scc_pp_parse_macro_replace_list(stream, &replacement);
scc_pp_add_object_macro(macros, &name, &replacement);
}
scc_cstring_free(&name);
@@ -296,190 +294,3 @@ FREE:
scc_cstring_free(&directive);
scc_cstring_free(&name);
}
static inline void scc_generate_cstr(scc_cstring_t *buff) {
scc_cstring_t out_buff = scc_cstring_new();
scc_cstring_append_ch(&out_buff, '\"');
// TODO it is too simple
scc_cstring_append(&out_buff, buff);
scc_cstring_append_ch(&out_buff, '\"');
// FIXME 可能有着更好的解决方案
scc_cstring_clear(buff);
scc_cstring_append(buff, &out_buff);
scc_cstring_free(&out_buff);
}
#define SCC_PP_IS_LIST_BLANK(i) \
((i) < list->size && scc_vec_at(*list, (i)).data[0] == ' ' && \
scc_vec_at(*list, (i)).data[1] == '\0')
#define SCC_PP_IS_LIST_TO_STRING(i) \
((i) < list->size && scc_vec_at(*list, (i)).data[0] == '#' && \
scc_vec_at(*list, (i)).data[1] == '\0')
#define SCC_PP_IS_LIST_CONNECT(i) \
((i) < list->size && scc_vec_at(*list, (i)).data[0] == '#' && \
scc_vec_at(*list, (i)).data[1] == '#' && \
scc_vec_at(*list, (i)).data[2] == '\0')
#define SCC_PP_USE_CONNECT(font, rear) \
if (rear < list->size) { \
scc_cstring_append(out_buff, &scc_vec_at(*list, font)); \
scc_cstring_append(out_buff, &scc_vec_at(*list, rear)); \
} else { \
scc_cstring_append(out_buff, &scc_vec_at(*list, font)); \
}
// for # ## to generator string
static inline cbool scc_pp_expand_string_unsafe(scc_pp_macro_list_t *list,
scc_cstring_t *out_buff) {
for (usize i = 0; i < list->size; ++i) {
if (SCC_PP_IS_LIST_BLANK(i + 1)) {
if (SCC_PP_IS_LIST_CONNECT(i + 2)) {
SCC_PP_USE_CONNECT(i, i + 3);
i += 3;
continue;
}
} else if (SCC_PP_IS_LIST_CONNECT(i + 1)) {
SCC_PP_USE_CONNECT(i, i + 2);
i += 2;
continue;
} else if (SCC_PP_IS_LIST_TO_STRING(i)) {
i += 1;
if (i < list->size) {
scc_generate_cstr(&scc_vec_at(*list, i));
} else {
LOG_WARN("# need a valid literator");
break;
}
}
scc_cstring_append(out_buff, &scc_vec_at(*list, i));
}
return true;
}
// 展开对象宏
cbool scc_pp_expand_object_macro(scc_pp_macro_t *macro,
scc_cstring_t *out_buff) {
Assert(macro->type == SCC_PP_MACRO_OBJECT && macro->params.size == 0);
Assert(scc_cstring_is_empty(out_buff) == true);
// 对象宏输出替换文本并进行递归展开
scc_pp_expand_string_unsafe(&macro->replaces, out_buff);
return true;
}
// 展开函数宏
cbool scc_pp_expand_function_macro(scc_pp_macro_t *macro,
scc_pp_macro_list_t *params,
scc_cstring_t *out_buff) {
Assert(macro->type == SCC_PP_MACRO_FUNCTION);
Assert(out_buff != null);
Assert(scc_cstring_is_empty(out_buff) == true);
for (usize i = 0; i < macro->replaces.size; ++i) {
// TODO ... __VA_ARGS__
for (usize j = 0; j < macro->params.size; ++j) {
if (scc_strcmp(
scc_cstring_as_cstr(&scc_vec_at(macro->replaces, i)),
scc_cstring_as_cstr(&scc_vec_at(macro->params, j))) == 0) {
scc_cstring_free(&scc_vec_at(macro->replaces, i));
scc_cstring_append(&scc_vec_at(macro->replaces, i),
&scc_vec_at(*params, j));
continue;
}
}
}
scc_pp_expand_string_unsafe(&macro->replaces, out_buff);
return true;
}
cbool scc_pp_expand_macro(scc_probe_stream_t *stream,
scc_pp_macro_table_t *macros,
scc_pp_macro_table_t *expand_stack,
scc_probe_stream_t **out_stream, int depth) {
// TODO self position and it maybe is a stack on #include ?
// 递归扫描
if (depth <= 0) {
*out_stream = null;
return false;
}
Assert(stream != null && macros != null && out_stream != null);
scc_cstring_t identifier = scc_cstring_new();
scc_pos_t pos = scc_pos_init();
cbool ret;
ret = scc_lex_parse_identifier(stream, &pos, &identifier);
Assert(ret == true);
scc_pp_macro_t *macro = scc_pp_macro_table_get(macros, &identifier);
// 1. 不是宏,直接输出标识符
// 2. 检查到重复展开跳过
if (macro == null ||
scc_pp_macro_table_get(expand_stack, &macro->name) != null) {
*out_stream =
scc_mem_probe_stream_new(scc_cstring_as_cstr(&identifier),
scc_cstring_len(&identifier), false);
return true;
} else {
scc_cstring_free(&identifier);
}
// 根据宏类型展开
scc_cstring_t tmp_buff = scc_cstring_new();
if (macro->type == SCC_PP_MACRO_OBJECT) {
cbool ret = scc_pp_expand_object_macro(macro, &tmp_buff);
Assert(ret == true);
} else if (macro->type == SCC_PP_MACRO_FUNCTION) {
// FIXME 是否需要忽略空白字符?
scc_lex_parse_skip_whitespace(stream, &pos);
if (scc_probe_stream_peek(stream) != '(') {
LOG_ERROR("Not a function and skip it");
goto ERR;
}
scc_pp_macro_list_t params;
ret = parse_macro_arguments(stream, &params);
Assert(ret == true);
scc_pp_expand_function_macro(macro, &params, &tmp_buff);
Assert(ret == true);
}
// 已经展开的将被标记并入栈
scc_pp_macro_table_set(expand_stack,
scc_pp_macro_new(&macro->name, macro->type));
// 将展开内容变换成stream
scc_probe_stream_t *tmp_stream = scc_mem_probe_stream_new(
scc_cstring_as_cstr(&tmp_buff), scc_cstring_len(&tmp_buff), false);
int ch;
scc_cstring_t real_buff = scc_cstring_new();
while ((ch = scc_probe_stream_peek(tmp_stream)) != scc_stream_eof) {
if (scc_lex_parse_is_identifier_prefix(ch)) {
// 递归检查
scc_probe_stream_t *tmp_out_stream;
if (scc_pp_expand_macro(tmp_stream, macros, expand_stack,
&tmp_out_stream, depth - 1) == false) {
return false;
}
// scc_cstring_append_cstr();
Assert(tmp_out_stream != null);
while (scc_probe_stream_peek(tmp_out_stream) != scc_stream_eof) {
scc_cstring_append_ch(&real_buff,
scc_probe_stream_consume(tmp_out_stream));
}
Assert(tmp_out_stream != null && tmp_out_stream->drop != null);
scc_probe_stream_drop(tmp_out_stream);
} else {
scc_cstring_append_ch(&real_buff,
scc_probe_stream_consume(tmp_stream));
}
}
scc_cstring_free(&tmp_buff);
scc_probe_stream_drop(tmp_stream);
*out_stream = scc_mem_probe_stream_new(scc_cstring_as_cstr(&real_buff),
scc_cstring_len(&real_buff), false);
// 已经展开的将被标记并出栈
scc_pp_macro_table_remove(expand_stack, &macro->name);
return true;
ERR:
*out_stream = null;
return false;
}

View File

@@ -9,7 +9,11 @@
#include <pp_token.h>
#include <pprocessor.h>
#ifdef TEST_MODE
#define MAX_MACRO_EXPANSION_DEPTH 16
#else
#define MAX_MACRO_EXPANSION_DEPTH 64 // 防止无限递归的最大展开深度
#endif
static int pp_stream_read_char(scc_probe_stream_t *_stream) {
scc_pp_stream_t *stream = (scc_pp_stream_t *)_stream;
@@ -30,12 +34,9 @@ RETRY:
&stream->self->macro_table);
goto RETRY;
} else if (scc_lex_parse_is_identifier_prefix(ch)) {
scc_pp_macro_table_t tmp_table;
scc_pp_marco_table_init(&tmp_table);
cbool ret = scc_pp_expand_macro(
stream->input, &stream->self->macro_table, &tmp_table,
&stream->tmp_stream, MAX_MACRO_EXPANSION_DEPTH);
scc_pp_macro_table_drop(&tmp_table);
cbool ret =
scc_pp_expand_macro(stream->input, &stream->self->macro_table,
&stream->tmp_stream, MAX_MACRO_EXPANSION_DEPTH);
if (ret == false) {
LOG_ERROR("macro_expand_error");
}

View File

@@ -20,6 +20,7 @@ typedef struct scc_cstring {
*
* @return cstring_t 初始化后的对象
*/
// FIXME need using `init` beacuse it not malloc
static inline scc_cstring_t scc_cstring_new(void) {
return (scc_cstring_t){.data = null, .size = 0, .cap = 0};
}
@@ -48,6 +49,10 @@ static inline scc_cstring_t scc_cstring_from_cstr(const char *s) {
return (scc_cstring_t){.size = len + 1, .cap = len + 1, .data = data};
}
static inline scc_cstring_t scc_cstring_copy(const scc_cstring_t *s) {
return scc_cstring_from_cstr(s->data);
}
/**
* @brief 释放动态字符串占用的内存资源
*
@@ -182,4 +187,15 @@ static inline char *scc_cstring_as_cstr(const scc_cstring_t *str) {
return str->data;
}
static inline char *scc_cstring_move_cstr(scc_cstring_t *str) {
if (str == null || str->data == null) {
return "";
}
char *ret = str->data;
str->data = null;
str->cap = 0;
str->size = 0;
return ret;
}
#endif /* __SCC_CORE_STR_H__ */

View File

@@ -100,7 +100,7 @@ typedef struct scc_mem_probe_stream {
usize data_length;
usize curr_pos; // 当前读取位置
usize probe_pos; // 探针位置用于peek
cbool owned; // 是否拥有数据(需要释放)
cbool owned; // 是否拥有数据(如果拥有将会自动释放)
} scc_mem_probe_stream_t;
/**
@@ -109,22 +109,22 @@ typedef struct scc_mem_probe_stream {
* @param stream 流结构指针
* @param data 数据指针
* @param length 数据长度
* @param need_copy 是否需要复制数据
* @param owned 是否拥有数据(如果拥有将会自动释放)
* @return core_probe_stream_t* 成功返回流指针失败返回NULL
*/
scc_probe_stream_t *scc_mem_probe_stream_init(scc_mem_probe_stream_t *stream,
const char *data, usize length,
cbool need_copy);
cbool owned);
/**
* @brief 构造内存探针流(其中drop会自动释放内存)
*
* @param data
* @param length
* @param need_copy
* @param owned 是否拥有数据(如果拥有将会自动释放)
* @return scc_probe_stream_t*
*/
scc_probe_stream_t *scc_mem_probe_stream_new(const char *data, usize length,
cbool need_copy);
cbool owned);
#endif
#endif /* __SMCC_CORE_PROBE_STREAM_H__ */

View File

@@ -125,7 +125,7 @@ static void mem_probe_stream_drop(scc_probe_stream_t *_stream) {
scc_probe_stream_t *scc_mem_probe_stream_init(scc_mem_probe_stream_t *stream,
const char *data, usize length,
cbool need_copy) {
cbool owned) {
if (stream == null || data == null) {
LOG_ERROR("param error");
return null;
@@ -133,22 +133,11 @@ scc_probe_stream_t *scc_mem_probe_stream_init(scc_mem_probe_stream_t *stream,
if (length == 0) {
LOG_WARN("input memory is empty");
need_copy = false;
owned = false;
}
stream->owned = need_copy;
if (need_copy) {
char *buf = (char *)scc_malloc(length);
if (buf == null) {
LOG_ERROR("malloc error");
return null;
}
scc_memcpy(buf, data, length);
stream->data = buf;
} else {
stream->data = data;
}
stream->owned = owned;
stream->data = data;
stream->data_length = length;
stream->curr_pos = 0;
stream->probe_pos = 0;
@@ -176,7 +165,7 @@ static void scc_owned_mem_stream_drop(scc_probe_stream_t *_stream) {
}
scc_probe_stream_t *scc_mem_probe_stream_new(const char *data, usize length,
cbool need_copy) {
cbool owned) {
scc_mem_probe_stream_t *stream =
(scc_mem_probe_stream_t *)scc_malloc(sizeof(scc_mem_probe_stream_t));
if (stream == null) {
@@ -184,7 +173,7 @@ scc_probe_stream_t *scc_mem_probe_stream_new(const char *data, usize length,
}
scc_probe_stream_t *ret =
scc_mem_probe_stream_init(stream, data, length, need_copy);
scc_mem_probe_stream_init(stream, data, length, owned);
stream->stream.drop = scc_owned_mem_stream_drop;
Assert(ret != null);
return ret;

View File

@@ -1,12 +1,12 @@
/**
* @file hashmap.h
* @file hashtable.h
* @brief
*
*
*/
#ifndef __SCC_HASHMAP_H__
#define __SCC_HASHMAP_H__
#ifndef __SCC_HASHTABLE_H__
#define __SCC_HASHTABLE_H__
#include <libcore.h>
@@ -121,4 +121,4 @@ typedef int (*scc_hashtable_iter_fn)(const void *key, void *value,
void scc_hashtable_foreach(scc_hashtable_t *ht, scc_hashtable_iter_fn iter_func,
void *context);
#endif /* __SCC_HASHMAP_H__ */
#endif /* __SCC_HASHTABLE_H__ */

View File

@@ -1,9 +1,9 @@
#ifndef __SMCC_UTILS_H__
#define __SMCC_UTILS_H__
#include "hashmap.h"
#include "hashtable.h"
#include "kllist.h"
#include "strpool.h"
#include <libcore.h>
#endif // __SMCC_UTILS_H__
#endif /* __SMCC_UTILS_H__ */

View File

@@ -8,7 +8,7 @@
#ifndef __SCC_STRPOOL_H__
#define __SCC_STRPOOL_H__
#include "hashmap.h"
#include "hashtable.h"
#include <libcore.h>
/**

View File

@@ -1,4 +1,4 @@
#include <hashmap.h>
#include <hashtable.h>
#ifndef SCC_INIT_HASHMAP_SIZE
#define SCC_INIT_HASHMAP_SIZE (32)

View File

@@ -1,112 +0,0 @@
# vector_gdb.py
import gdb # type: ignore
from gdb.printing import PrettyPrinter # type: ignore
class VectorPrinter:
"""兼容新旧注册方式的最终方案"""
def __init__(self, val: gdb.Value):
self.val:gdb.Value = val
def check_type(self) -> bool:
"""类型检查(兼容匿名结构体)"""
try:
if self.val.type.code != gdb.TYPE_CODE_STRUCT:
return False
fields = self.val.type.fields()
if not fields:
return False
exp = ['size', 'cap', 'data']
for t in fields:
if t.name in exp:
exp.remove(t.name)
else:
return False
return True
except gdb.error:
return False
def to_string(self):
if not self.check_type():
return "Not a vector"
return "vector({} size={}, cap={})".format(
self.val.address,
self.val['size'],
self.val['cap'],
)
def display_hint(self):
return 'array'
def children(self):
"""生成数组元素(关键改进点)"""
if not self.check_type():
return []
size = int(self.val['size'])
cap = int(self.val['cap'])
data_ptr = self.val['data']
if cap == 0 or data_ptr == 0:
return []
# 使用 GDB 内置数组转换
array = data_ptr.dereference()
array = array.cast(data_ptr.type.target().array(cap - 1))
for i in range(size):
# state = "<used>" if i < size else "<unused>"
try:
value = array[i]
yield (f"[{i}] {value.type} {value.address}", value)
except gdb.MemoryError:
yield (f"[{i}]", "<invalid>")
# 注册方式一传统append方法您之前有效的方式self
def append_printer():
gdb.pretty_printers.append(
lambda val: VectorPrinter(val) if VectorPrinter(val).check_type() else None
)
# 注册方式二:新版注册方法(备用方案)
def register_new_printer():
class VectorPrinterLocator(PrettyPrinter):
def __init__(self):
super().__init__("vector_printer")
def __call__(self, val):
ret = VectorPrinter(val).check_type()
print(f"ret {ret}, type {val.type}, {[(i.name, i.type) for i in val.type.fields()]}")
return None
gdb.printing.register_pretty_printer(
gdb.current_objfile(),
VectorPrinterLocator()
)
# 双重注册保证兼容性
append_printer() # 保留您原来有效的方式
# register_new_printer() # 添加新版注册
class VectorInfoCommand(gdb.Command):
"""保持原有命令不变"""
def __init__(self):
super().__init__("vector_info", gdb.COMMAND_USER)
def invoke(self, argument, from_tty):
val = gdb.parse_and_eval(argument)
printer = VectorPrinter(val)
if not printer.check_type():
print("Invalid vector")
return
print("=== Vector Details ===")
print("Size:", val['size'])
print("Capacity:", val['cap'])
print("Elements:")
for name, value in printer.children():
print(f" {name}: {value}")
VectorInfoCommand()

222
runtime/runtime_gdb.py Normal file
View File

@@ -0,0 +1,222 @@
"https://sourceware.org/gdb/current/onlinedocs/gdb.html/Python-API.html#Python-API"
import gdb # type: ignore
class VectorPrinter(gdb.ValuePrinter):
"""兼容新旧注册方式的最终方案"""
def __init__(self, val: gdb.Value):
self.val: gdb.Value = val
@staticmethod
def check_type(val: gdb.Value) -> bool:
"""类型检查(兼容匿名结构体)"""
try:
if val.type.code not in (gdb.TYPE_CODE_STRUCT, gdb.TYPE_CODE_TYPEDEF):
return False
if val.type.name == "scc_cstring_t":
return False
fields = val.type.fields()
if not fields:
return False
exp = ["size", "cap", "data"]
for t in fields:
if t.name in exp:
exp.remove(t.name)
else:
return False
return True
except gdb.error:
return False
except ValueError:
return False
except TypeError:
return False
except Exception as e:
print(f"[DEBUG] Unknown exception type: {type(e).__name__}")
print(f"[DEBUG] Exception details: {e}")
print(
f"[DEBUG] val type: {val.type if hasattr(val, 'type') else 'no type attr'}"
)
return False
def to_string(self):
"""
GDB will call this method to display the string representation
of the value passed to the object's constructor.
This is a basic method, and is optional.
When printing from the CLI, if the to_string method exists,
then GDB will prepend its result to the values returned by children.
Exactly how this formatting is done is dependent on the display hint,
and may change as more hints are added. Also, depending on the print settings
(see Print Settings), the CLI may print just the result of to_string
in a stack trace, omitting the result of children.
If this method returns a string, it is printed verbatim.
Otherwise, if this method returns an instance of gdb.Value,
then GDB prints this value. This may result in a call to another pretty-printer.
If instead the method returns a Python value which is convertible to a gdb.Value,
then GDB performs the conversion and prints the resulting value. Again,
this may result in a call to another pretty-printer. Python scalars
(integers, floats, and booleans) and strings are convertible to gdb.Value;
other types are not.
Finally, if this method returns None then no further operations are performed
in this method and nothing is printed.
If the result is not one of these types, an exception is raised.
https://sourceware.org/gdb/current/onlinedocs/gdb.html/Pretty-Printing-API.html#Pretty-Printing-API
"""
return (
f"vector({self.val.address} size={self.val['size']}, cap={self.val['cap']})"
)
def display_hint(self):
"""
The CLI may call this method and use its result to change the formatting of a value.
The result will also be supplied to an MI consumer as a 'displayhint'
attribute of the variable being printed.
This is a basic method, and is optional. If it does exist,
this method must return a string or the special value None.
Some display hints are predefined by GDB:
'array'
Indicate that the object being printed is “array-like”.
The CLI uses this to respect parameters such as set print elements and set print array.
'map'
Indicate that the object being printed is “map-like”,
and that the children of this value can be assumed to alternate between keys and values.
'string'
Indicate that the object being printed is “string-like”.
If the printer's to_string method returns a Python string of some kind,
then GDB will call its internal language-specific string-printing function
to format the string. For the CLI this means adding quotation marks, possibly
escaping some characters, respecting set print elements, and the like.
The special value None causes GDB to apply the default display rules.
https://sourceware.org/gdb/current/onlinedocs/gdb.html/Pretty-Printing-API.html#Pretty-Printing-API
"""
return "array"
def num_children(self):
"""
This is not a basic method, so GDB will only ever call it for objects
derived from gdb.ValuePrinter.
If available, this method should return the number of children.
None may be returned if the number can't readily be computed.
https://sourceware.org/gdb/current/onlinedocs/gdb.html/Pretty-Printing-API.html#Pretty-Printing-API
"""
return int(self.val["size"])
def children(self):
"""
This is not a basic method, so GDB will only ever call it for objects
derived from gdb.ValuePrinter.
If available, this method should return the child item (that is,
a tuple holding the name and value of this child) indicated by n.
Indices start at zero.
GDB provides a function which can be used to look up the default
pretty-printer for a gdb.Value:
https://sourceware.org/gdb/current/onlinedocs/gdb.html/Pretty-Printing-API.html#Pretty-Printing-API
"""
size = int(self.val["size"])
cap = int(self.val["cap"])
data_ptr = self.val["data"]
if cap == 0 or data_ptr == 0:
return []
# 使用 GDB 内置数组转换
array = data_ptr.dereference()
array = array.cast(data_ptr.type.target().array(cap - 1))
for i in range(size):
# state = "<used>" if i < size else "<unused>"
try:
value = array[i]
yield (f"[{i}] {value.type} {value.address}", value)
except gdb.MemoryError:
yield (f"[{i}]", "<invalid>")
class HashTablePrinter(gdb.ValuePrinter):
def __init__(self, val: gdb.Value):
self.val: gdb.Value = val
@staticmethod
def check_type(val: gdb.Value) -> bool:
if val.type.name in ["scc_hashtable_t", "scc_hashtable"]:
return True
return False
def append_printer():
"注册方式一传统append方法您之前有效的方式self"
gdb.pretty_printers.append(
lambda val: VectorPrinter(val) if VectorPrinter.check_type(val) else None
)
def register_new_printer():
"注册方式二:新版注册方法(备用方案)"
def str_lookup_function(val):
if VectorPrinter.check_type(val) is False:
return None
ret = VectorPrinter(val)
# print(
# f"ret {ret}, type {val.type.name}, {[(i.name, i.type) for i in val.type.fields()]}"
# )
return ret
gdb.printing.register_pretty_printer(gdb.current_objfile(), str_lookup_function)
# if gdb.current_progspace() is not None:
# pts = gdb.current_progspace().pretty_printers
# print(pts, len(pts))
# pts.append(str_lookup_function)
class VectorInfoCommand(gdb.Command):
"""保持原有命令不变"""
def __init__(self):
super().__init__("vector_info", gdb.COMMAND_USER)
def invoke(self, argument, from_tty):
val = gdb.parse_and_eval(argument)
if not VectorPrinter.check_type(val):
print("Invalid vector")
return
printer = VectorPrinter(val)
print("=== Vector Details ===")
print("Size:", val["size"])
print("Capacity:", val["cap"])
print("Elements:")
for name, value in printer.children():
print(f" {name}: {value}")
if __name__ == "__main__":
# 双重注册保证兼容性
# append_printer() # 保留您原来有效的方式
register_new_printer() # 添加新版注册
VectorInfoCommand()