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:
@@ -79,7 +79,7 @@ void scc_lexer_init(scc_lexer_t *lexer, scc_probe_stream_t *stream) {
|
|||||||
lexer->stream = stream;
|
lexer->stream = stream;
|
||||||
lexer->pos = scc_pos_init();
|
lexer->pos = scc_pos_init();
|
||||||
// FIXME
|
// 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)
|
#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);
|
scc_lex_parse_skip_line(lexer->stream, &lexer->pos);
|
||||||
token->loc.line = n;
|
token->loc.line = n;
|
||||||
// FIXME memory leak
|
// 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);
|
scc_cstring_free(&str);
|
||||||
return;
|
return;
|
||||||
SKIP_LINE:
|
SKIP_LINE:
|
||||||
|
|||||||
@@ -5,9 +5,14 @@
|
|||||||
#include <pp_macro.h>
|
#include <pp_macro.h>
|
||||||
void scc_pp_parse_directive(scc_probe_stream_t *stream, scc_pos_t *pos,
|
void scc_pp_parse_directive(scc_probe_stream_t *stream, scc_pos_t *pos,
|
||||||
scc_pp_macro_table_t *macros);
|
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,
|
cbool scc_pp_expand_macro(scc_probe_stream_t *stream,
|
||||||
scc_pp_macro_table_t *macros,
|
scc_pp_macro_table_t *macros,
|
||||||
scc_pp_macro_table_t *expand_stack,
|
|
||||||
scc_probe_stream_t **out_stream, int depth);
|
scc_probe_stream_t **out_stream, int depth);
|
||||||
|
|
||||||
#endif /* __SCC_PP_PARSE_H__ */
|
#endif /* __SCC_PP_PARSE_H__ */
|
||||||
|
|||||||
296
libs/pprocessor/src/expand_macro.c
Normal file
296
libs/pprocessor/src/expand_macro.c
Normal 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(¯o->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(¯o->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, ¯o->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, ¯o->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, ¶ms);
|
||||||
|
Assert(ret == true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 检查到重复展开跳过
|
||||||
|
// 检查是否可以展开
|
||||||
|
if (!can_expand_macro(state, macro)) {
|
||||||
|
ORIGIN:
|
||||||
|
// 输出原始调用
|
||||||
|
scc_cstring_t original = scc_cstring_new();
|
||||||
|
scc_cstring_append(&original, ¯o->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(¶m, &out, state);
|
||||||
|
scc_vec_push(expanded_params, out);
|
||||||
|
}
|
||||||
|
ret = scc_pp_expand_function_macro(macro, ¶ms, &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;
|
||||||
|
}
|
||||||
@@ -9,7 +9,7 @@ scc_pp_macro_t *scc_pp_macro_new(const scc_cstring_t *name,
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
macro->name = scc_cstring_from_cstr(scc_cstring_as_cstr(name));
|
macro->name = scc_cstring_copy(name);
|
||||||
macro->type = type;
|
macro->type = type;
|
||||||
scc_vec_init(macro->params);
|
scc_vec_init(macro->params);
|
||||||
scc_vec_init(macro->replaces);
|
scc_vec_init(macro->replaces);
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
#include <ctype.h>
|
|
||||||
#include <lex_parser.h>
|
#include <lex_parser.h>
|
||||||
#include <pp_macro.h>
|
#include <pp_macro.h>
|
||||||
#include <pp_parse.h>
|
#include <pp_parse.h>
|
||||||
#include <pp_token.h>
|
#include <pp_token.h>
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
static const struct {
|
static const struct {
|
||||||
const char *name;
|
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) {
|
scc_pp_macro_list_t *list) {
|
||||||
Assert(stream != null && list != null);
|
Assert(stream != null && list != null);
|
||||||
scc_probe_stream_reset(stream);
|
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) {
|
scc_pp_macro_list_t *args) {
|
||||||
Assert(stream != null && args != null);
|
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 == '(') {
|
if (!has_whitespace && ch == '(') {
|
||||||
// 函数宏
|
// 函数宏
|
||||||
scc_pp_macro_list_t params;
|
scc_pp_macro_list_t params;
|
||||||
if (!parse_macro_arguments(stream, ¶ms)) {
|
if (!scc_pp_parse_macro_arguments(stream, ¶ms)) {
|
||||||
goto ERR;
|
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;
|
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, ¶ms, &replacement);
|
scc_pp_add_function_macro(macros, &name, ¶ms, &replacement);
|
||||||
} else {
|
} else {
|
||||||
// 对象宏
|
// 对象宏
|
||||||
scc_pp_macro_list_t replacement;
|
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_pp_add_object_macro(macros, &name, &replacement);
|
||||||
}
|
}
|
||||||
scc_cstring_free(&name);
|
scc_cstring_free(&name);
|
||||||
@@ -296,190 +294,3 @@ FREE:
|
|||||||
scc_cstring_free(&directive);
|
scc_cstring_free(&directive);
|
||||||
scc_cstring_free(&name);
|
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(¯o->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(¯o->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, ¯o->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, ¶ms);
|
|
||||||
Assert(ret == true);
|
|
||||||
scc_pp_expand_function_macro(macro, ¶ms, &tmp_buff);
|
|
||||||
Assert(ret == true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 已经展开的将被标记并入栈
|
|
||||||
scc_pp_macro_table_set(expand_stack,
|
|
||||||
scc_pp_macro_new(¯o->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, ¯o->name);
|
|
||||||
return true;
|
|
||||||
ERR:
|
|
||||||
*out_stream = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -9,7 +9,11 @@
|
|||||||
#include <pp_token.h>
|
#include <pp_token.h>
|
||||||
#include <pprocessor.h>
|
#include <pprocessor.h>
|
||||||
|
|
||||||
|
#ifdef TEST_MODE
|
||||||
|
#define MAX_MACRO_EXPANSION_DEPTH 16
|
||||||
|
#else
|
||||||
#define MAX_MACRO_EXPANSION_DEPTH 64 // 防止无限递归的最大展开深度
|
#define MAX_MACRO_EXPANSION_DEPTH 64 // 防止无限递归的最大展开深度
|
||||||
|
#endif
|
||||||
|
|
||||||
static int pp_stream_read_char(scc_probe_stream_t *_stream) {
|
static int pp_stream_read_char(scc_probe_stream_t *_stream) {
|
||||||
scc_pp_stream_t *stream = (scc_pp_stream_t *)_stream;
|
scc_pp_stream_t *stream = (scc_pp_stream_t *)_stream;
|
||||||
@@ -30,12 +34,9 @@ RETRY:
|
|||||||
&stream->self->macro_table);
|
&stream->self->macro_table);
|
||||||
goto RETRY;
|
goto RETRY;
|
||||||
} else if (scc_lex_parse_is_identifier_prefix(ch)) {
|
} else if (scc_lex_parse_is_identifier_prefix(ch)) {
|
||||||
scc_pp_macro_table_t tmp_table;
|
cbool ret =
|
||||||
scc_pp_marco_table_init(&tmp_table);
|
scc_pp_expand_macro(stream->input, &stream->self->macro_table,
|
||||||
cbool ret = scc_pp_expand_macro(
|
&stream->tmp_stream, MAX_MACRO_EXPANSION_DEPTH);
|
||||||
stream->input, &stream->self->macro_table, &tmp_table,
|
|
||||||
&stream->tmp_stream, MAX_MACRO_EXPANSION_DEPTH);
|
|
||||||
scc_pp_macro_table_drop(&tmp_table);
|
|
||||||
if (ret == false) {
|
if (ret == false) {
|
||||||
LOG_ERROR("macro_expand_error");
|
LOG_ERROR("macro_expand_error");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ typedef struct scc_cstring {
|
|||||||
*
|
*
|
||||||
* @return cstring_t 初始化后的对象
|
* @return cstring_t 初始化后的对象
|
||||||
*/
|
*/
|
||||||
|
// FIXME need using `init` beacuse it not malloc
|
||||||
static inline scc_cstring_t scc_cstring_new(void) {
|
static inline scc_cstring_t scc_cstring_new(void) {
|
||||||
return (scc_cstring_t){.data = null, .size = 0, .cap = 0};
|
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};
|
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 释放动态字符串占用的内存资源
|
* @brief 释放动态字符串占用的内存资源
|
||||||
*
|
*
|
||||||
@@ -182,4 +187,15 @@ static inline char *scc_cstring_as_cstr(const scc_cstring_t *str) {
|
|||||||
return str->data;
|
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__ */
|
#endif /* __SCC_CORE_STR_H__ */
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ typedef struct scc_mem_probe_stream {
|
|||||||
usize data_length;
|
usize data_length;
|
||||||
usize curr_pos; // 当前读取位置
|
usize curr_pos; // 当前读取位置
|
||||||
usize probe_pos; // 探针位置(用于peek)
|
usize probe_pos; // 探针位置(用于peek)
|
||||||
cbool owned; // 是否拥有数据(需要释放)
|
cbool owned; // 是否拥有数据(如果拥有将会自动释放)
|
||||||
} scc_mem_probe_stream_t;
|
} scc_mem_probe_stream_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -109,22 +109,22 @@ typedef struct scc_mem_probe_stream {
|
|||||||
* @param stream 流结构指针
|
* @param stream 流结构指针
|
||||||
* @param data 数据指针
|
* @param data 数据指针
|
||||||
* @param length 数据长度
|
* @param length 数据长度
|
||||||
* @param need_copy 是否需要复制数据
|
* @param owned 是否拥有数据(如果拥有将会自动释放)
|
||||||
* @return core_probe_stream_t* 成功返回流指针,失败返回NULL
|
* @return core_probe_stream_t* 成功返回流指针,失败返回NULL
|
||||||
*/
|
*/
|
||||||
scc_probe_stream_t *scc_mem_probe_stream_init(scc_mem_probe_stream_t *stream,
|
scc_probe_stream_t *scc_mem_probe_stream_init(scc_mem_probe_stream_t *stream,
|
||||||
const char *data, usize length,
|
const char *data, usize length,
|
||||||
cbool need_copy);
|
cbool owned);
|
||||||
/**
|
/**
|
||||||
* @brief 构造内存探针流(其中drop会自动释放内存)
|
* @brief 构造内存探针流(其中drop会自动释放内存)
|
||||||
*
|
*
|
||||||
* @param data
|
* @param data
|
||||||
* @param length
|
* @param length
|
||||||
* @param need_copy
|
* @param owned 是否拥有数据(如果拥有将会自动释放)
|
||||||
* @return scc_probe_stream_t*
|
* @return scc_probe_stream_t*
|
||||||
*/
|
*/
|
||||||
scc_probe_stream_t *scc_mem_probe_stream_new(const char *data, usize length,
|
scc_probe_stream_t *scc_mem_probe_stream_new(const char *data, usize length,
|
||||||
cbool need_copy);
|
cbool owned);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* __SMCC_CORE_PROBE_STREAM_H__ */
|
#endif /* __SMCC_CORE_PROBE_STREAM_H__ */
|
||||||
|
|||||||
@@ -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,
|
scc_probe_stream_t *scc_mem_probe_stream_init(scc_mem_probe_stream_t *stream,
|
||||||
const char *data, usize length,
|
const char *data, usize length,
|
||||||
cbool need_copy) {
|
cbool owned) {
|
||||||
if (stream == null || data == null) {
|
if (stream == null || data == null) {
|
||||||
LOG_ERROR("param error");
|
LOG_ERROR("param error");
|
||||||
return null;
|
return null;
|
||||||
@@ -133,22 +133,11 @@ scc_probe_stream_t *scc_mem_probe_stream_init(scc_mem_probe_stream_t *stream,
|
|||||||
|
|
||||||
if (length == 0) {
|
if (length == 0) {
|
||||||
LOG_WARN("input memory is empty");
|
LOG_WARN("input memory is empty");
|
||||||
need_copy = false;
|
owned = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
stream->owned = need_copy;
|
stream->owned = owned;
|
||||||
if (need_copy) {
|
stream->data = data;
|
||||||
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->data_length = length;
|
stream->data_length = length;
|
||||||
stream->curr_pos = 0;
|
stream->curr_pos = 0;
|
||||||
stream->probe_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,
|
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 *stream =
|
||||||
(scc_mem_probe_stream_t *)scc_malloc(sizeof(scc_mem_probe_stream_t));
|
(scc_mem_probe_stream_t *)scc_malloc(sizeof(scc_mem_probe_stream_t));
|
||||||
if (stream == null) {
|
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_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;
|
stream->stream.drop = scc_owned_mem_stream_drop;
|
||||||
Assert(ret != null);
|
Assert(ret != null);
|
||||||
return ret;
|
return ret;
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
/**
|
/**
|
||||||
* @file hashmap.h
|
* @file hashtable.h
|
||||||
* @brief 开放寻址法哈希表实现
|
* @brief 开放寻址法哈希表实现
|
||||||
*
|
*
|
||||||
* 提供基于向量容器的哈希表实现,支持动态扩容和墓碑机制
|
* 提供基于向量容器的哈希表实现,支持动态扩容和墓碑机制
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __SCC_HASHMAP_H__
|
#ifndef __SCC_HASHTABLE_H__
|
||||||
#define __SCC_HASHMAP_H__
|
#define __SCC_HASHTABLE_H__
|
||||||
|
|
||||||
#include <libcore.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 scc_hashtable_foreach(scc_hashtable_t *ht, scc_hashtable_iter_fn iter_func,
|
||||||
void *context);
|
void *context);
|
||||||
|
|
||||||
#endif /* __SCC_HASHMAP_H__ */
|
#endif /* __SCC_HASHTABLE_H__ */
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
#ifndef __SMCC_UTILS_H__
|
#ifndef __SMCC_UTILS_H__
|
||||||
#define __SMCC_UTILS_H__
|
#define __SMCC_UTILS_H__
|
||||||
|
|
||||||
#include "hashmap.h"
|
#include "hashtable.h"
|
||||||
#include "kllist.h"
|
#include "kllist.h"
|
||||||
#include "strpool.h"
|
#include "strpool.h"
|
||||||
#include <libcore.h>
|
#include <libcore.h>
|
||||||
|
|
||||||
#endif // __SMCC_UTILS_H__
|
#endif /* __SMCC_UTILS_H__ */
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
#ifndef __SCC_STRPOOL_H__
|
#ifndef __SCC_STRPOOL_H__
|
||||||
#define __SCC_STRPOOL_H__
|
#define __SCC_STRPOOL_H__
|
||||||
|
|
||||||
#include "hashmap.h"
|
#include "hashtable.h"
|
||||||
#include <libcore.h>
|
#include <libcore.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#include <hashmap.h>
|
#include <hashtable.h>
|
||||||
|
|
||||||
#ifndef SCC_INIT_HASHMAP_SIZE
|
#ifndef SCC_INIT_HASHMAP_SIZE
|
||||||
#define SCC_INIT_HASHMAP_SIZE (32)
|
#define SCC_INIT_HASHMAP_SIZE (32)
|
||||||
@@ -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
222
runtime/runtime_gdb.py
Normal 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()
|
||||||
Reference in New Issue
Block a user