refactor(lexer): 重构词法分析器头文件结构并优化缓冲区管理

移除了旧的lexer_stream.c实现,引入新的环形缓冲区机制来替代原有的
动态数组缓冲区。更新了词法分析器的核心数据结构,修改了token获取
相关函数的实现以支持新的缓冲区管理方式。

BREAKING CHANGE: 移除了scc_lexer_stream_t相关的API,替换为基于
环形缓冲区的新接口scc_lexer_to_ring和相关函数。

feat(lexer_token): 添加词法分析结果内存泄漏警告注释

docs: 移除预处理器模块的测试文件和相关配置
This commit is contained in:
zzy
2026-02-16 21:21:23 +08:00
parent 0e7dec202a
commit b4929be6b8
72 changed files with 119 additions and 2474 deletions

View File

@@ -1,100 +0,0 @@
/**
* @file lexer.h
* @brief C语言词法分析器核心数据结构与接口
*/
#ifndef __SCC_LEXER_H__
#define __SCC_LEXER_H__
#include "lexer_token.h"
#include <scc_core.h>
#include <scc_sstream.h>
/**
* @brief 词法分析器核心结构体
*
* 封装词法分析所需的状态信息和缓冲区管理
*/
typedef struct scc_lexer {
scc_sstream_ring_t stream_ref;
int jump_macro;
} scc_lexer_t;
void scc_lexer_init(scc_lexer_t *lexer, scc_sstream_ring_t *stream_ref);
/**
* @brief 获取原始token
* @param[in] lexer 词法分析器实例
* @param[out] token 输出token存储位置
*
* 此函数会返回所有类型的token包括空白符等无效token
*/
void scc_lexer_get_token(scc_lexer_t *lexer, scc_lexer_tok_t *token);
/**
* @brief 获取有效token
* @param[in] lexer 词法分析器实例
* @param[out] token 输出token存储位置
*
* 此函数会自动跳过空白符等无效token返回对语法分析有意义的token
*/
void scc_lexer_get_valid_token(scc_lexer_t *lexer, scc_lexer_tok_t *token);
typedef SCC_VEC(scc_lexer_tok_t) scc_lexer_tok_vec_t;
typedef struct scc_lexer_stream scc_lexer_stream_t;
struct scc_lexer_stream {
scc_lexer_t *lexer;
scc_lexer_tok_vec_t toks; // 循环缓冲区
usize curr_pos; // 当前读取位置(逻辑位置)
usize probe_pos; // 已填充位置(逻辑位置)
cbool need_comment;
/// @brief 向前读取n个token
const scc_lexer_tok_t *(*peek)(scc_lexer_stream_t *stream, usize n);
/// @brief 指针推进到offset
void (*advance)(scc_lexer_stream_t *stream, usize offset);
/// @brief 销毁并释放资源
void (*drop)(scc_lexer_stream_t *stream);
};
/**
* @brief 将词法分析器转换成流式输出(自带缓冲区)
* @param[in] lexer 已经词法分析器实例
* @param[out] stream 输出流对象指针
* @param[in] need_comment 输出时是否需要注释
*/
void scc_lexer_to_stream(scc_lexer_t *lexer, scc_lexer_stream_t *stream,
cbool need_comment);
static inline const scc_lexer_tok_t *
scc_lexer_stream_current(scc_lexer_stream_t *stream) {
Assert(stream != null);
return stream->peek(stream, 0);
}
static inline const scc_lexer_tok_t *
scc_lexer_stream_peek(scc_lexer_stream_t *stream, usize n) {
Assert(stream != null);
return stream->peek(stream, n);
}
static inline void scc_lexer_stream_consume(scc_lexer_stream_t *stream) {
Assert(stream != null);
return stream->advance(stream, 1);
}
static inline void scc_lexer_stream_advance(scc_lexer_stream_t *stream,
usize n) {
Assert(stream != null);
return stream->advance(stream, n);
}
static inline void scc_lexer_stream_drop(scc_lexer_stream_t *stream) {
Assert(stream != null);
return stream->drop(stream);
}
#endif /* __SCC_LEXER_H__ */

View File

@@ -144,6 +144,10 @@ typedef enum scc_tok_subtype {
scc_tok_subtype_t scc_get_tok_subtype(scc_tok_type_t type); scc_tok_subtype_t scc_get_tok_subtype(scc_tok_type_t type);
const char *scc_get_tok_name(scc_tok_type_t type); const char *scc_get_tok_name(scc_tok_type_t type);
/**
* @brief 词法分析结果
* @warning 需要手动释放lexeme否则会出现内存泄漏
*/
typedef struct scc_lexer_token { typedef struct scc_lexer_token {
scc_tok_type_t type; scc_tok_type_t type;
scc_cstring_t lexeme; scc_cstring_t lexeme;

View File

@@ -0,0 +1,54 @@
/**
* @file lexer.h
* @brief C语言词法分析器核心数据结构与接口
*/
#ifndef __SCC_LEXER_H__
#define __SCC_LEXER_H__
#include "lexer_token.h"
#include <scc_core.h>
#include <scc_core_ring.h>
#include <scc_sstream.h>
typedef SCC_RING(scc_lexer_tok_t) scc_lexer_tok_ring_t;
typedef SCC_VEC(scc_lexer_tok_t) scc_lexer_tok_vec_t;
/**
* @brief 词法分析器核心结构体
*
* 封装词法分析所需的状态信息和缓冲区管理
*/
typedef struct scc_lexer {
scc_sstream_ring_t *stream_ref;
scc_lexer_tok_ring_t ring;
int ring_ref_count;
int jump_macro;
} scc_lexer_t;
void scc_lexer_init(scc_lexer_t *lexer, scc_sstream_ring_t *stream_ref);
/**
* @brief 获取原始token
* @param[in] lexer 词法分析器实例
* @param[out] token 输出token存储位置
*
* 此函数会返回所有类型的token包括空白符等无效token
*/
void scc_lexer_get_token(scc_lexer_t *lexer, scc_lexer_tok_t *token);
/**
* @brief 获取有效token
* @param[in] lexer 词法分析器实例
* @param[out] token 输出token存储位置
*
* 此函数会自动跳过空白符等无效token返回对语法分析有意义的token
*/
void scc_lexer_get_valid_token(scc_lexer_t *lexer, scc_lexer_tok_t *token);
scc_lexer_tok_ring_t *scc_lexer_to_ring(scc_lexer_t *lexer, int ring_size,
cbool need_comment);
void scc_lexer_drop_ring(scc_lexer_tok_ring_t *ring_ref);
void scc_lexer_drop(scc_lexer_t *lexer);
#endif /* __SCC_LEXER_H__ */

View File

@@ -1,5 +1,6 @@
#include <lexer.h> #include "scc_lexer.h"
#include <lexer_log.h> #include <lexer_log.h>
#include <scc_lexer.h>
static const struct { static const struct {
const char *name; const char *name;
@@ -41,7 +42,8 @@ static int keyword_cmp(const char *name, int len) {
} }
void scc_lexer_init(scc_lexer_t *lexer, scc_sstream_ring_t *stream_ref) { void scc_lexer_init(scc_lexer_t *lexer, scc_sstream_ring_t *stream_ref) {
lexer->stream_ref = *stream_ref; lexer->stream_ref = stream_ref;
lexer->ring_ref_count = 0;
lexer->jump_macro = false; lexer->jump_macro = false;
} }
@@ -68,7 +70,7 @@ static inline cbool is_hex_digit(int ch) {
/* 从环形缓冲区预览一个字符带EOF检测 */ /* 从环形缓冲区预览一个字符带EOF检测 */
static inline cbool peek_char(scc_lexer_t *lexer, scc_sstream_char_t *out) { static inline cbool peek_char(scc_lexer_t *lexer, scc_sstream_char_t *out) {
cbool ok; cbool ok;
scc_ring_peek(lexer->stream_ref, *out, ok); scc_ring_peek(*lexer->stream_ref, *out, ok);
return ok; return ok;
} }
@@ -76,7 +78,7 @@ static inline cbool peek_char(scc_lexer_t *lexer, scc_sstream_char_t *out) {
static inline cbool next_char(scc_lexer_t *lexer, scc_cstring_t *lexeme, static inline cbool next_char(scc_lexer_t *lexer, scc_cstring_t *lexeme,
scc_sstream_char_t *out) { scc_sstream_char_t *out) {
cbool ok; cbool ok;
scc_ring_next(lexer->stream_ref, *out, ok); scc_ring_next(*lexer->stream_ref, *out, ok);
if (!ok) if (!ok)
return false; return false;
scc_cstring_append_ch(lexeme, out->character); scc_cstring_append_ch(lexeme, out->character);
@@ -132,7 +134,7 @@ void scc_lexer_get_token(scc_lexer_t *lexer, scc_lexer_tok_t *token) {
next_char(lexer, &lex, &cur); // 消费 '/' next_char(lexer, &lex, &cur); // 消费 '/'
while (peek_char(lexer, &cur) && !is_newline(cur.character)) { while (peek_char(lexer, &cur) && !is_newline(cur.character)) {
next_char(lexer, &lex, &cur); next_char(lexer, &lex, &cur);
scc_ring_consume(lexer->stream_ref); scc_ring_consume(*lexer->stream_ref);
} }
// 注释结束不包含换行符换行符单独成token // 注释结束不包含换行符换行符单独成token
} else if (next.character == '*') { } else if (next.character == '*') {
@@ -150,7 +152,7 @@ void scc_lexer_get_token(scc_lexer_t *lexer, scc_lexer_tok_t *token) {
next_char(lexer, &lex, &cur); // 消费 '/' next_char(lexer, &lex, &cur); // 消费 '/'
break; break;
} }
scc_ring_consume(lexer->stream_ref); scc_ring_consume(*lexer->stream_ref);
} }
} else { } else {
// 只是除号 / // 只是除号 /
@@ -161,7 +163,7 @@ void scc_lexer_get_token(scc_lexer_t *lexer, scc_lexer_tok_t *token) {
token->type = SCC_TOK_IDENT; // 暂定 token->type = SCC_TOK_IDENT; // 暂定
while (peek_char(lexer, &cur) && is_identifier_part(cur.character)) { while (peek_char(lexer, &cur) && is_identifier_part(cur.character)) {
next_char(lexer, &lex, &cur); next_char(lexer, &lex, &cur);
scc_ring_consume(lexer->stream_ref); scc_ring_consume(*lexer->stream_ref);
} }
// 检查是否为关键字 // 检查是否为关键字
int idx = keyword_cmp(scc_cstring_as_cstr(&lex), scc_cstring_len(&lex)); int idx = keyword_cmp(scc_cstring_as_cstr(&lex), scc_cstring_len(&lex));
@@ -241,7 +243,7 @@ void scc_lexer_get_token(scc_lexer_t *lexer, scc_lexer_tok_t *token) {
} else { } else {
next_char(lexer, &lex, &cur); next_char(lexer, &lex, &cur);
} }
scc_ring_consume(lexer->stream_ref); scc_ring_consume(*lexer->stream_ref);
} }
} else { } else {
scc_sstream_char_t next = {0}; scc_sstream_char_t next = {0};
@@ -447,7 +449,7 @@ void scc_lexer_get_token(scc_lexer_t *lexer, scc_lexer_tok_t *token) {
} }
// 设置token // 设置token
scc_ring_consume(lexer->stream_ref); scc_ring_consume(*lexer->stream_ref);
token->type = token->type; // 上面已设 token->type = token->type; // 上面已设
token->loc = start_loc; token->loc = start_loc;
token->lexeme = lex; // 转移所有权 token->lexeme = lex; // 转移所有权
@@ -469,3 +471,42 @@ void scc_lexer_get_valid_token(scc_lexer_t *lexer, scc_lexer_tok_t *token) {
} while (subtype == SCC_TOK_SUBTYPE_EMPTYSPACE || } while (subtype == SCC_TOK_SUBTYPE_EMPTYSPACE ||
subtype == SCC_TOK_SUBTYPE_COMMENT); subtype == SCC_TOK_SUBTYPE_COMMENT);
} }
static int fill_token(scc_lexer_tok_t *out, void *userdata) {
scc_lexer_t *lexer = userdata;
scc_lexer_get_token(lexer, out);
return 0;
}
static int fill_valid_token(scc_lexer_tok_t *out, void *userdata) {
scc_lexer_t *lexer = userdata;
scc_lexer_get_valid_token(lexer, out);
return 0;
}
scc_lexer_tok_ring_t *scc_lexer_to_ring(scc_lexer_t *lexer, int ring_size,
cbool need_comment) {
scc_ring_init(lexer->ring, ring_size,
need_comment ? fill_token : fill_valid_token, lexer);
lexer->ring_ref_count++;
return &lexer->ring;
}
void scc_lexer_drop_ring(scc_lexer_tok_ring_t *ring_ref) {
scc_lexer_t *lexer = ring_ref->userdata;
if (lexer->ring_ref_count > 0) {
lexer->ring_ref_count--;
} else {
LOG_WARN("double drop sstream ring");
}
}
void scc_lexer_drop(scc_lexer_t *lexer) {
Assert(lexer != null);
if (lexer->ring_ref_count) {
LOG_FATAL("drop sstream must be drop ring before ref [%d]",
lexer->ring_ref_count);
}
scc_ring_free(lexer->ring);
scc_sstream_drop_ring(lexer->stream_ref);
}

View File

@@ -1,139 +0,0 @@
#include <lexer.h>
static void lexer_stream_extend(scc_lexer_stream_t *stream, usize n) {
Assert(stream != null);
// 检查是否需要扩容
if ((stream->probe_pos - stream->curr_pos + n) >= stream->toks.cap) {
// 需要扩容 - 创建新缓冲区
usize new_cap = stream->toks.cap * 2;
if (new_cap < stream->probe_pos - stream->curr_pos + n + 1) {
new_cap = stream->probe_pos - stream->curr_pos + n + 1;
}
scc_lexer_tok_t *new_data =
scc_realloc(null, new_cap * sizeof(scc_lexer_tok_t));
if (!new_data) {
LOG_FATAL("lexer_stream_extend: realloc failed\n");
return;
}
// 将旧缓冲区中的数据拷贝到新缓冲区,保持顺序
usize data_count = stream->probe_pos - stream->curr_pos;
for (usize i = 0; i < data_count; ++i) {
usize old_idx = (stream->curr_pos + i) % stream->toks.cap;
new_data[i] = stream->toks.data[old_idx];
}
// 释放旧缓冲区
if (stream->toks.data) {
scc_free(stream->toks.data);
}
// 更新结构体
stream->toks.data = new_data;
stream->toks.cap = new_cap;
stream->curr_pos = 0;
stream->probe_pos = data_count;
}
// 填充新token
for (usize i = 0; i < n; ++i) {
usize idx = (stream->probe_pos + i) % stream->toks.cap;
if (stream->need_comment)
scc_lexer_get_token(stream->lexer, &stream->toks.data[idx]);
else
scc_lexer_get_valid_token(stream->lexer, &stream->toks.data[idx]);
}
stream->probe_pos += n;
}
static const scc_lexer_tok_t *lexer_stream_peek(scc_lexer_stream_t *stream,
usize n) {
Assert(stream != null);
// 计算需要的前看token数量
usize available = stream->probe_pos - stream->curr_pos;
if (n >= available) {
// 需要扩展缓冲区
usize need = n - available + 1;
lexer_stream_extend(stream, need);
}
// 计算实际缓冲区中的位置
usize idx = (stream->curr_pos + n) % stream->toks.cap;
return &stream->toks.data[idx];
}
static void lexer_stream_advance(scc_lexer_stream_t *stream, usize offset) {
Assert(stream != null);
if (stream->curr_pos + offset > stream->probe_pos) {
// 尝试填充更多token
usize need = stream->curr_pos + offset - stream->probe_pos;
lexer_stream_extend(stream, need);
}
stream->curr_pos += offset;
// 可选当已消费的token过多时压缩缓冲区
if (stream->curr_pos > stream->toks.cap * 3 / 4) {
// 压缩缓冲区:将有效数据移动到前面
usize data_count = stream->probe_pos - stream->curr_pos;
scc_lexer_tok_t *temp =
scc_realloc(null, data_count * sizeof(scc_lexer_tok_t));
if (!temp)
return; // 压缩失败也没关系
for (usize i = 0; i < data_count; ++i) {
usize old_idx = (stream->curr_pos + i) % stream->toks.cap;
temp[i] = stream->toks.data[old_idx];
}
scc_free(stream->toks.data);
stream->toks.data = temp;
stream->toks.cap = data_count;
stream->curr_pos = 0;
stream->probe_pos = data_count;
}
}
static void lexer_stream_drop(scc_lexer_stream_t *stream) {
Assert(stream != null);
// 清理所有token如果有需要清理的内部资源
for (usize i = 0; i < stream->toks.cap; ++i) {
// 这里假设scc_lexer_tok_t可能包含需要释放的资源
// 如果有,需要调用相应的清理函数
// 例如: if (stream->toks.data[i].needs_free)
// scc_free(stream->toks.data[i].ptr);
}
scc_vec_free(stream->toks);
stream->lexer = null;
stream->curr_pos = 0;
stream->probe_pos = 0;
stream->need_comment = false;
stream->peek = null;
stream->advance = null;
stream->drop = null;
}
void scc_lexer_to_stream(scc_lexer_t *lexer, scc_lexer_stream_t *stream,
cbool need_comment) {
Assert(lexer != null && stream != null);
stream->lexer = lexer;
stream->curr_pos = 0;
stream->probe_pos = 0;
stream->need_comment = need_comment;
// 初始化循环缓冲区
scc_vec_init(stream->toks);
scc_vec_realloc(stream->toks, 8); // 初始容量为8
stream->peek = lexer_stream_peek;
stream->advance = lexer_stream_advance;
stream->drop = lexer_stream_drop;
}

View File

@@ -1,5 +1,5 @@
#include <lexer.h>
#include <lexer_log.h> #include <lexer_log.h>
#include <scc_lexer.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@@ -53,10 +53,7 @@ int main(int argc, char *argv[]) {
scc_get_tok_name(token.type), scc_get_tok_name(token.type),
scc_cstring_as_cstr(&token.lexeme), token.loc.name, scc_cstring_as_cstr(&token.lexeme), token.loc.name,
token.loc.line, token.loc.col); token.loc.line, token.loc.col);
// LOG_DEBUG("%s", token.val.str); scc_cstring_free(&token.lexeme);
// printf("line: %d, column: %d, type: %3d, typename: %s\n",
// lexer.line, lexer.index, token.type,
// scc_get_tok_name(token.type));
} }
scc_sstream_drop_ring(ref); scc_sstream_drop_ring(ref);
scc_sstream_drop(&stream); scc_sstream_drop(&stream);

View File

@@ -1,5 +1,5 @@
// test_lexer.c // test_lexer.c
#include <lexer.h> #include <scc_lexer.h>
#include <string.h> #include <string.h>
#include <utest/acutest.h> #include <utest/acutest.h>

View File

@@ -1,4 +0,0 @@
tests/pp 复制了 TinyCC 的 tests/pp 测试
详情见 [README](tests/pp/README)

View File

@@ -1,8 +0,0 @@
[package]
name = "scc_pprocesser"
dependencies = [
{ name = "scc_core", path = "../../runtime/scc_core" },
{ name = "scc_utils", path = "../../runtime/scc_utils" },
{ name = "lex_parser", path = "../lex_parser" },
]

View File

@@ -1,96 +0,0 @@
#ifndef __SCC_PP_MACRO_H__
#define __SCC_PP_MACRO_H__
#include <scc_core.h>
#include <scc_utils.h>
// 宏定义类型
typedef enum {
SCC_PP_MACRO_OBJECT, // 对象宏
SCC_PP_MACRO_FUNCTION, // 函数宏
SCC_PP_MACRO_NONE, // 不是宏
} scc_pp_macro_type_t;
typedef SCC_VEC(scc_cstring_t) scc_pp_macro_list_t;
// 宏定义结构
typedef struct scc_macro {
scc_cstring_t name; // 宏名称
scc_pp_macro_type_t type; // 宏类型
scc_pp_macro_list_t replaces; // 替换列表
scc_pp_macro_list_t params; // 参数列表(仅函数宏)
} scc_pp_macro_t;
typedef struct scc_macro_table {
scc_hashtable_t table; // 宏定义表
} scc_pp_macro_table_t;
/**
* @brief 创建宏对象
* @param name 宏名称
* @param type 宏类型
* @return 创建的宏对象指针失败返回NULL
*/
scc_pp_macro_t *scc_pp_macro_new(const scc_cstring_t *name,
scc_pp_macro_type_t type);
/**
* @brief 销毁宏对象
* @param macro 要销毁的宏对象
*/
void scc_pp_macro_drop(scc_pp_macro_t *macro);
/**
* @brief 添加对象宏
* @param pp 预处理器实例
* @param name 宏名称
* @param replacement 替换文本列表
* @return 成功返回true失败返回false
*/
cbool scc_pp_add_object_macro(scc_pp_macro_table_t *pp,
const scc_cstring_t *name,
const scc_pp_macro_list_t *replacement);
/**
* @brief 添加函数宏
* @param pp 预处理器实例
* @param name 宏名称
* @param params 参数列表
* @param replacement 替换文本列表
* @return 成功返回true失败返回false
*/
cbool scc_pp_add_function_macro(scc_pp_macro_table_t *pp,
const scc_cstring_t *name,
const scc_pp_macro_list_t *params,
const scc_pp_macro_list_t *replacement);
/**
* @brief
*
* @param pp
* @param macro
* @return scc_pp_macro_t*
*/
scc_pp_macro_t *scc_pp_macro_table_set(scc_pp_macro_table_t *pp,
scc_pp_macro_t *macro);
/**
* @brief 查找宏定义
* @param pp 预处理器实例
* @param name 宏名称
* @return 找到的宏对象指针未找到返回NULL
*/
scc_pp_macro_t *scc_pp_macro_table_get(scc_pp_macro_table_t *pp,
const scc_cstring_t *name);
/**
* @brief 从预处理器中删除宏
* @param pp 预处理器实例
* @param name 宏名称
* @return 成功删除返回true未找到返回false
*/
cbool scc_pp_macro_table_remove(scc_pp_macro_table_t *pp,
const scc_cstring_t *name);
void scc_pp_marco_table_init(scc_pp_macro_table_t *macros);
void scc_pp_macro_table_drop(scc_pp_macro_table_t *macros);
#endif /* __SCC_PP_MACRO_H__ */

View File

@@ -1,18 +0,0 @@
#ifndef __SCC_PP_PARSE_H__
#define __SCC_PP_PARSE_H__
#include <pp_macro.h>
#include <scc_core.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_probe_stream_t **out_stream, int depth);
#endif /* __SCC_PP_PARSE_H__ */

View File

@@ -1,30 +0,0 @@
#ifndef __SCC_PP_TOKEN_H__
#define __SCC_PP_TOKEN_H__
/* clang-format off */
/// https://cppreference.cn/w/c/preprocessor
#define SCC_PP_INST_TOKEN \
X(define , SCC_PP_STD, SCC_PP_TOK_DEFINE ) \
X(elif , SCC_PP_STD, SCC_PP_TOK_ELIF ) \
X(elifdef , SCC_PP_STD, SCC_PP_TOK_ELIFDEF ) \
X(elifndef , SCC_PP_STD, SCC_PP_TOK_ELIFNDEF ) \
X(else , SCC_PP_STD, SCC_PP_TOK_ELSE ) \
X(embed , SCC_PP_STD, SCC_PP_TOK_EMBED ) \
X(endif , SCC_PP_STD, SCC_PP_TOK_ENDIF ) \
X(error , SCC_PP_STD, SCC_PP_TOK_ERROR ) \
X(if , SCC_PP_STD, SCC_PP_TOK_IF ) \
X(ifdef , SCC_PP_C23, SCC_PP_TOK_IFDEF ) \
X(ifndef , SCC_PP_STD, SCC_PP_TOK_IFNDEF ) \
X(include , SCC_PP_STD, SCC_PP_TOK_INCLUDE ) \
X(line , SCC_PP_C23, SCC_PP_TOK_LINE ) \
X(pragma , SCC_PP_STD, SCC_PP_TOK_PRAGMA ) \
X(undef , SCC_PP_C23, SCC_PP_TOK_UNDEF ) \
X(warning , SCC_PP_STD, SCC_PP_TOK_WARNING ) \
// END
/* clang-format on */
#define X(name, type, tok) tok,
typedef enum scc_pp_token { SCC_PP_INST_TOKEN } scc_pp_token_t;
#undef X
#endif /* __SCC_PP_TOKEN_H__ */

View File

@@ -1,62 +0,0 @@
/**
* @file pprocessor.h
* @brief C语言预处理器核心数据结构与接口
*/
#ifndef __SCC_PP_H__
#define __SCC_PP_H__
#include <pp_macro.h>
#include <scc_core.h>
#include <scc_utils.h>
// 条件编译状态
typedef enum {
IFState_NONE, // 不在条件编译中
IFState_TRUE, // 条件为真
IFState_FALSE, // 条件为假
IFState_ELSE // 已经执行过else分支
} if_state_t;
// 条件编译栈项
typedef struct if_stack_item {
if_state_t state;
int skip; // 是否跳过当前段
} if_stack_item_t;
// 预处理器状态结构
typedef struct scc_pproc {
scc_probe_stream_t *stream; // 输出流
scc_strpool_t strpool; // 字符串池
scc_pp_macro_table_t macro_table;
SCC_VEC(if_stack_item_t) if_stack; // 条件编译栈
} scc_pproc_t;
/**
* @brief 初始化预处理器
* @param[out] pp 要初始化的预处理器实例
* @param[in] input 输入流对象指针
* @return output 输出流对象指针
*/
// TODO 内存释放问题
scc_probe_stream_t *scc_pproc_init(scc_pproc_t *pp, scc_probe_stream_t *input);
/**
* @brief 销毁预处理器
* @param[in] pp 预处理器实例
*/
void scc_pproc_drop(scc_pproc_t *pp);
/// inner private struct
typedef SCC_VEC(u8) scc_pp_buffer_t;
typedef struct pp_stream {
scc_probe_stream_t stream;
scc_probe_stream_t *input;
scc_pproc_t *self;
scc_pos_t pos;
scc_probe_stream_t *tmp_stream;
} scc_pp_stream_t;
#endif /* __SCC_PP_H__ */

View File

@@ -1,412 +0,0 @@
#include <lex_parser.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_create();
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(const 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_ERROR("# need a valid literator");
break;
}
}
scc_cstring_append(out_buff, &scc_vec_at(*list, i));
}
return true;
}
// 展开对象宏
cbool scc_pp_expand_object_macro(const 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(const scc_pp_macro_t *macro,
const 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 || params->size == 0);
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) {
continue;
}
if (params->size == 0) {
scc_vec_push(list, scc_cstring_from_cstr(""));
goto END;
}
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;
} scc_expansion_ctx_t;
// 进入宏展开
static void enter_macro_expansion(scc_expansion_ctx_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(scc_expansion_ctx_t *state,
scc_pp_macro_t *macro) {
// 从活动宏移除,添加到禁用宏
scc_pp_macro_table_remove(&state->painted_blue, &macro->name);
}
// 检查是否可以展开
static cbool can_expand_macro(scc_expansion_ctx_t *state,
scc_pp_macro_t *macro) {
return scc_pp_macro_table_get(&state->painted_blue, &macro->name) == null;
}
typedef struct {
scc_cstring_t identifier;
scc_pp_macro_list_t args;
} scc_expand_unit_t;
void scc_pp_parse_expand_macro(scc_probe_stream_t *stream, scc_pos_t *pos,
scc_expand_unit_t *unit) {
Assert(stream != null && pos != null && unit != null);
// TODO Assert empty unit
scc_cstring_init(&unit->identifier);
scc_vec_init(unit->args);
cbool ret = scc_lex_parse_identifier(stream, pos, &unit->identifier);
Assert(ret == true);
scc_probe_stream_sync(stream);
do {
if (scc_lex_parse_is_whitespace(scc_probe_stream_peek(stream))) {
scc_lex_parse_skip_whitespace(stream, pos);
}
if (scc_probe_stream_peek(stream) != '(') {
// TODO maybe error
// 因为没能正确表述空值
break;
}
// TODO maybe like f()() => maybe twice expand
ret = scc_pp_parse_macro_arguments(stream, &unit->args);
scc_probe_stream_sync(stream);
Assert(ret == true);
} while (0);
}
static cbool expand_buffer(const scc_cstring_t *in, scc_cstring_t *out,
scc_expansion_ctx_t *state);
static inline void args2str_append(const scc_pp_macro_list_t *args,
scc_cstring_t *out) {
Assert(args != null);
if (args->size != 0) {
scc_cstring_append_ch(out, '(');
for (usize i = 0; i < args->size; ++i) {
scc_cstring_append(out, &scc_vec_at(*args, i));
if (i != args->size - 1) {
scc_cstring_append_ch(out, ',');
scc_cstring_append_ch(out, ' ');
}
}
scc_cstring_append_ch(out, ')');
}
}
static cbool expand_macro(const scc_expand_unit_t *unit, scc_cstring_t *out,
scc_expansion_ctx_t *state) {
scc_pp_macro_t *macro =
scc_pp_macro_table_get(state->macros, &unit->identifier);
if (macro == null) {
scc_cstring_append(out, &unit->identifier);
args2str_append(&unit->args, out);
return true;
}
cbool ret;
if (macro->type == SCC_PP_MACRO_OBJECT) {
if (can_expand_macro(state, macro)) {
ret = scc_pp_expand_object_macro(macro, out);
Assert(ret == true);
args2str_append(&unit->args, out);
} else {
scc_cstring_append(out, &macro->name);
args2str_append(&unit->args, out);
return true;
}
} else if (macro->type == SCC_PP_MACRO_FUNCTION) {
/**
* 1. 参数先展开
* 2. 替换后重扫描
* 3. 蓝色集合中不展开
* 4. #, ## 不展开
* 5. 最后的括号要检查
*/
scc_pp_macro_list_t expanded_params;
scc_vec_init(expanded_params);
// expand params fisrt and recursive
for (usize i = 0; i < unit->args.size; ++i) {
scc_cstring_t param = scc_vec_at(unit->args, i);
scc_cstring_t out = scc_cstring_create();
expand_buffer(&param, &out, state);
scc_vec_push(expanded_params, out);
}
if (can_expand_macro(state, macro)) {
ret = scc_pp_expand_function_macro(macro, &unit->args,
&expanded_params, out);
Assert(ret == true);
} else {
scc_cstring_append(out, &macro->name);
args2str_append(&expanded_params, out);
return true;
}
// TODO memory leak
} else {
UNREACHABLE();
}
// 重新扫描展开结果
scc_cstring_t rescanned = scc_cstring_create();
enter_macro_expansion(state, macro);
expand_buffer(out, &rescanned, state);
leave_macro_expansion(state, macro);
scc_cstring_free(out);
*out = rescanned;
return true;
}
static cbool expand_buffer(const scc_cstring_t *in, scc_cstring_t *out,
scc_expansion_ctx_t *state) {
scc_probe_stream_t *in_stream = scc_mem_probe_stream_alloc(
scc_cstring_as_cstr(in), scc_cstring_len(in), false);
int ch;
scc_pos_t pos;
scc_expand_unit_t unit;
while ((ch = scc_probe_stream_peek(in_stream)) != scc_stream_eof) {
if (scc_lex_parse_is_identifier_prefix(ch)) {
// 递归检查
scc_cstring_t expanded_buffer = scc_cstring_create();
scc_pp_parse_expand_macro(in_stream, &pos, &unit);
scc_probe_stream_reset(in_stream);
if (expand_macro(&unit, &expanded_buffer, state) == false) {
scc_cstring_free(&expanded_buffer);
return false;
} else {
scc_cstring_append(out, &expanded_buffer);
scc_cstring_free(&expanded_buffer);
}
} else {
scc_cstring_append_ch(out, scc_probe_stream_consume(in_stream));
}
}
scc_probe_stream_drop(in_stream);
return true;
}
// static cbool scc_pp_expand_macro_impl(scc_probe_stream_t *stream,
// scc_expansion_ctx_t *ctx, scc_pos_t
// *pos, scc_cstring_t *output) {
// // TODO self position and it maybe is a stack on #include ?
// // 递归扫描
// if (ctx->depth-- <= 0) {
// scc_cstring_free(output);
// return false;
// }
// cbool ret;
// scc_pp_macro_table_t *macros = ctx->macros;
// scc_cstring_t identifier = scc_cstring_create();
// 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_alloc(
// scc_cstring_move_cstr(&identifier), length, true);
// return true;
// } else {
// scc_cstring_free(&identifier);
// }
// // 收集参数(如果是函数宏)
// scc_pp_macro_list_t params;
// scc_pp_macro_list_t expanded_params;
// scc_vec_init(params);
// scc_vec_init(expanded_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);
// // 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_create();
// expand_buffer(&param, &out, ctx);
// scc_vec_push(expanded_params, out);
// }
// }
// // 2. 检查到重复展开跳过
// // 检查是否可以展开
// if (!can_expand_macro(ctx, macro)) {
// ORIGIN:
// // 输出原始调用
// scc_cstring_t original = scc_cstring_create();
// scc_cstring_append(&original, &macro->name);
// if (macro->type == SCC_PP_MACRO_FUNCTION && expanded_params.size !=
// 0) {
// scc_cstring_append_ch(&original, '(');
// for (usize i = 0; i < expanded_params.size; ++i) {
// scc_cstring_append(&original, &scc_vec_at(expanded_params,
// i)); if (i != expanded_params.size - 1) {
// scc_cstring_append_ch(&original, ',');
// scc_cstring_append_ch(&original, ' ');
// }
// }
// scc_cstring_append_ch(&original, ')');
// }
// *out_stream = scc_mem_probe_stream_alloc(
// scc_cstring_as_cstr(&original), scc_cstring_len(&original),
// true);
// scc_vec_free(params);
// scc_vec_free(expanded_params);
// return true;
// }
// // 开始展开
// scc_cstring_t expanded = scc_cstring_create();
// if (macro->type == SCC_PP_MACRO_OBJECT) {
// ret = scc_pp_expand_object_macro(macro, &expanded);
// Assert(ret == true);
// } else if (macro->type == SCC_PP_MACRO_FUNCTION) {
// ret = scc_pp_expand_function_macro(macro, &params, &expanded_params,
// &expanded);
// Assert(ret == true);
// } else {
// UNREACHABLE();
// }
// // 重新扫描展开结果
// // 将展开内容变换成stream并递归展开
// scc_cstring_t rescanned = scc_cstring_create();
// enter_macro_expansion(ctx, macro);
// expand_buffer(&expanded, &rescanned, ctx);
// leave_macro_expansion(ctx, macro);
// scc_cstring_free(&expanded);
// // TODO memory leak
// *out_stream = scc_mem_probe_stream_alloc(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);
scc_expansion_ctx_t state;
state.depth = depth;
scc_pp_marco_table_init(&state.painted_blue);
state.macros = macros;
scc_cstring_t buffer = scc_cstring_create();
scc_pos_t pos;
// cbool ret = scc_pp_expand_macro_impl(stream, &state, &pos, &buffer);
// expand_buffer(stream, &buffer, &state);
scc_expand_unit_t unit;
scc_pp_parse_expand_macro(stream, &pos, &unit);
scc_probe_stream_reset(stream);
expand_macro(&unit, &buffer, &state);
*out_stream = scc_mem_probe_stream_alloc(scc_cstring_as_cstr(&buffer),
scc_cstring_len(&buffer), true);
scc_pp_macro_table_drop(&state.painted_blue);
return true;
}

View File

@@ -1,155 +0,0 @@
#include <pp_macro.h>
// 创建宏对象
scc_pp_macro_t *scc_pp_macro_new(const scc_cstring_t *name,
scc_pp_macro_type_t type) {
scc_pp_macro_t *macro = scc_malloc(sizeof(scc_pp_macro_t));
if (!macro) {
LOG_ERROR("Failed to allocate memory for macro");
return null;
}
macro->name = scc_cstring_copy(name);
macro->type = type;
scc_vec_init(macro->params);
scc_vec_init(macro->replaces);
return macro;
}
// 销毁宏对象
void scc_pp_macro_drop(scc_pp_macro_t *macro) {
if (!macro)
return;
scc_cstring_free(&macro->name);
// 释放参数列表
for (usize i = 0; i < macro->params.size; ++i) {
scc_cstring_free(&scc_vec_at(macro->params, i));
}
scc_vec_free(macro->params);
// 释放替换列表
for (usize i = 0; i < macro->replaces.size; ++i) {
scc_cstring_free(&scc_vec_at(macro->replaces, i));
}
scc_vec_free(macro->replaces);
scc_free(macro);
}
// 添加对象宏
cbool scc_pp_add_object_macro(scc_pp_macro_table_t *macros,
const scc_cstring_t *name,
const scc_pp_macro_list_t *replacement) {
if (!macros || !name || !replacement)
return false;
scc_pp_macro_t *macro = scc_pp_macro_new(name, SCC_PP_MACRO_OBJECT);
if (!macro)
return false;
macro->replaces = *replacement;
// 检查是否已存在同名宏
scc_pp_macro_t *existing = scc_hashtable_get(&macros->table, &macro->name);
if (existing) {
LOG_WARN("Redefining macro: %s", scc_cstring_as_cstr(&macro->name));
scc_pp_macro_drop(existing);
}
scc_hashtable_set(&macros->table, &macro->name, macro);
return true;
}
// 添加函数宏
cbool scc_pp_add_function_macro(scc_pp_macro_table_t *macros,
const scc_cstring_t *name,
const scc_pp_macro_list_t *params,
const scc_pp_macro_list_t *replacement) {
if (!macros || !name || !params || !replacement)
return false;
scc_pp_macro_t *macro = scc_pp_macro_new(name, SCC_PP_MACRO_FUNCTION);
if (!macro)
return false;
// 复制参数列表
macro->params = *params;
macro->replaces = *replacement;
// 检查是否已存在同名宏
scc_pp_macro_t *existing = scc_hashtable_get(&macros->table, &macro->name);
if (existing) {
LOG_WARN("Redefining macro: %s", scc_cstring_as_cstr(&macro->name));
scc_pp_macro_drop(existing);
}
scc_hashtable_set(&macros->table, &macro->name, macro);
return true;
}
/// marco_table
scc_pp_macro_t *scc_pp_macro_table_set(scc_pp_macro_table_t *pp,
scc_pp_macro_t *macro) {
Assert(pp != null && macro != null);
return scc_hashtable_set(&pp->table, &macro->name, macro);
}
// 查找宏定义
scc_pp_macro_t *scc_pp_macro_table_get(scc_pp_macro_table_t *pp,
const scc_cstring_t *name) {
return scc_hashtable_get(&pp->table, name);
}
// 从预处理器中删除宏
cbool scc_pp_macro_table_remove(scc_pp_macro_table_t *pp,
const scc_cstring_t *name) {
if (!pp || !name)
return false;
scc_pp_macro_t *macro = scc_hashtable_get(&pp->table, name);
if (!macro)
return false;
scc_hashtable_del(&pp->table, name);
scc_pp_macro_drop(macro);
return true;
}
static u32 hash_func(const void *key) {
const scc_cstring_t *string = (const scc_cstring_t *)key;
return scc_strhash32(scc_cstring_as_cstr(string));
}
static int hash_cmp(const void *key1, const void *key2) {
const scc_cstring_t *str1 = (const scc_cstring_t *)key1;
const scc_cstring_t *str2 = (const scc_cstring_t *)key2;
if (str1->size != str2->size) {
return str1->size - str2->size;
}
return scc_strcmp(scc_cstring_as_cstr(str1), scc_cstring_as_cstr(str2));
}
void scc_pp_marco_table_init(scc_pp_macro_table_t *macros) {
Assert(macros != null);
macros->table.hash_func = hash_func;
macros->table.key_cmp = hash_cmp;
scc_hashtable_init(&macros->table);
}
static int macro_free(const void *key, void *value, void *context) {
(void)key;
(void)context;
scc_pp_macro_drop(value);
return 0;
}
void scc_pp_macro_table_drop(scc_pp_macro_table_t *macros) {
Assert(macros != null);
scc_hashtable_foreach(&macros->table, macro_free, null);
scc_hashtable_drop(&macros->table);
}

View File

@@ -1,295 +0,0 @@
#include <lex_parser.h>
#include <pp_macro.h>
#include <pp_parse.h>
#include <pp_token.h>
static const struct {
const char *name;
scc_pp_token_t tok;
} keywords[] = {
#define X(name, type, tok) {#name, tok},
SCC_PP_INST_TOKEN
#undef X
};
// 使用二分查找查找关键字
static inline int keyword_cmp(const char *name, int len) {
int low = 0;
int high = sizeof(keywords) / sizeof(keywords[0]) - 1;
while (low <= high) {
int mid = (low + high) / 2;
const char *key = keywords[mid].name;
int cmp = 0;
// 自定义字符串比较逻辑
for (int i = 0; i < len; i++) {
if (name[i] != key[i]) {
cmp = (unsigned char)name[i] - (unsigned char)key[i];
break;
}
if (name[i] == '\0')
break; // 遇到终止符提前结束
}
if (cmp == 0) {
// 完全匹配检查(长度相同)
if (key[len] == '\0')
return mid;
cmp = -1; // 当前关键词比输入长
}
if (cmp < 0) {
high = mid - 1;
} else {
low = mid + 1;
}
}
return -1; // Not a keyword.
}
static inline void try_to_cut_list(scc_pp_macro_list_t *list,
scc_cstring_t *buff) {
if (scc_cstring_len(buff) != 0) {
scc_vec_push(*list, *buff);
*buff = scc_cstring_create();
}
}
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);
scc_vec_init(*list);
scc_cstring_t replacement = scc_cstring_create();
int ch;
scc_pos_t pos = scc_pos_create();
while ((ch = scc_probe_stream_peek(stream)) != scc_stream_eof) {
if (scc_lex_parse_is_endline(ch)) {
break;
}
if (scc_lex_parse_is_identifier_prefix(ch)) {
try_to_cut_list(list, &replacement);
cbool ret = scc_lex_parse_identifier(stream, &pos, &replacement);
Assert(ret == true);
try_to_cut_list(list, &replacement);
} else if (ch == '#') {
// 处理 # 和 ## 操作符
scc_probe_stream_next(stream);
try_to_cut_list(list, &replacement);
scc_cstring_append_ch(&replacement, '#');
if (scc_probe_stream_peek(stream) == '#') {
// ## 连接操作符
scc_probe_stream_next(stream);
scc_cstring_append_ch(&replacement, '#');
}
// 我需要尽可能防止空白字符干扰解析
scc_lex_parse_skip_whitespace(stream, &pos);
try_to_cut_list(list, &replacement);
} else if (scc_lex_parse_is_whitespace(ch)) {
try_to_cut_list(list, &replacement);
scc_lex_parse_skip_whitespace(stream, &pos);
scc_cstring_append_ch(&replacement, ' ');
try_to_cut_list(list, &replacement);
} else {
scc_probe_stream_next(stream);
scc_cstring_append_ch(&replacement, (char)ch);
}
}
if (scc_cstring_len(&replacement) != 0) {
scc_vec_push(*list, replacement);
replacement = scc_cstring_create();
}
// for (usize i = 0; i < list->size; ++i) {
// LOG_DEBUG("list %d: %s", (int)i,
// scc_cstring_as_cstr(&scc_vec_at(*list, i)));
// }
return true;
}
// 解析宏参数列表
cbool scc_pp_parse_macro_arguments(scc_probe_stream_t *stream,
scc_pp_macro_list_t *args) {
Assert(stream != null && args != null);
scc_vec_init(*args);
int ch;
// scc_probe_stream_reset(stream);
// 跳过 '('
ch = scc_probe_stream_peek(stream);
if (ch != '(') {
return false;
}
scc_probe_stream_next(stream); // 消费 '('
int paren_depth = 1;
scc_cstring_t current_arg = scc_cstring_create();
scc_pos_t pos = scc_pos_create();
while (paren_depth > 0) {
ch = scc_probe_stream_peek(stream);
if (ch == scc_stream_eof) {
scc_cstring_free(&current_arg);
scc_cstring_free(&pos.name);
return false;
}
if (ch == '(') {
paren_depth++;
scc_cstring_append_ch(&current_arg, (char)ch);
scc_probe_stream_next(stream);
} else if (ch == ')') {
paren_depth--;
if (paren_depth > 0) {
scc_cstring_append_ch(&current_arg, (char)ch);
}
scc_probe_stream_next(stream);
} else if (ch == ',' && paren_depth == 1) {
// 参数分隔符
scc_vec_push(*args, current_arg);
current_arg = scc_cstring_create();
scc_probe_stream_next(stream);
// 跳过参数后的空白
scc_lex_parse_skip_whitespace(stream, &pos);
} else {
scc_cstring_append_ch(&current_arg, (char)ch);
scc_probe_stream_next(stream);
}
}
// 添加最后一个参数
if (!scc_cstring_is_empty(&current_arg)) {
scc_vec_push(*args, current_arg);
} else {
scc_cstring_free(&current_arg);
}
scc_cstring_free(&pos.name);
return true;
}
static cbool safe_skip_backspace_if_endline(scc_probe_stream_t *stream,
scc_pos_t *pos) {
// scc_probe_stream_reset(stream);
int ch = scc_probe_stream_peek(stream);
// FIXME maybe it not correct
while (ch == '\r' || ch == '\n' || ch == ' ' || ch == '\t') {
if (scc_lex_parse_is_endline(ch)) {
scc_lex_parse_skip_endline(stream, pos);
return true;
}
scc_probe_stream_next(stream);
ch = scc_probe_stream_peek(stream);
}
// scc_probe_stream_reset(stream);
return false;
}
void scc_pp_parse_directive(scc_probe_stream_t *stream, scc_pos_t *pos,
scc_pp_macro_table_t *macros) {
Assert(stream != null);
// scc_probe_stream_reset(stream);
// 跳过 '#' 和后续空白
if (scc_probe_stream_peek(stream) != '#') {
LOG_WARN("Invalid directive");
return;
}
scc_pos_next(pos);
scc_probe_stream_next(stream);
if (safe_skip_backspace_if_endline(stream, pos))
return;
// 解析指令名称
scc_cstring_t directive = scc_cstring_create();
if (!scc_lex_parse_identifier(stream, pos, &directive)) {
goto ERR;
}
if (safe_skip_backspace_if_endline(stream, pos))
goto FREE;
scc_pp_token_t token = keyword_cmp(scc_cstring_as_cstr(&directive),
scc_cstring_len(&directive));
scc_cstring_t name = scc_cstring_create();
switch (token) {
case SCC_PP_TOK_DEFINE: {
if (!scc_lex_parse_identifier(stream, pos, &name)) {
goto ERR;
}
// 检查是否是函数宏:宏名后是否直接跟着 '('(没有空白字符)
// scc_probe_stream_reset(stream);
int ch = scc_probe_stream_peek(stream);
cbool has_whitespace = scc_lex_parse_is_whitespace(ch);
if (has_whitespace && safe_skip_backspace_if_endline(stream, pos)) {
goto FREE;
}
if (!has_whitespace && ch == '(') {
// 函数宏
scc_pp_macro_list_t params;
if (!scc_pp_parse_macro_arguments(stream, &params)) {
goto ERR;
}
ch = scc_probe_stream_peek(stream);
if (ch == ')') {
scc_probe_stream_next(stream); // 消费 ')'
}
if (safe_skip_backspace_if_endline(stream, pos)) {
goto FREE;
}
scc_pp_macro_list_t 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;
scc_pp_parse_macro_replace_list(stream, &replacement);
scc_pp_add_object_macro(macros, &name, &replacement);
}
break;
}
case SCC_PP_TOK_UNDEF: {
if (scc_lex_parse_identifier(stream, pos, &name)) {
cbool ret = scc_pp_macro_table_remove(macros, &name);
Assert(ret == true);
}
break;
}
case SCC_PP_TOK_INCLUDE:
case SCC_PP_TOK_IF:
case SCC_PP_TOK_IFDEF:
case SCC_PP_TOK_IFNDEF:
case SCC_PP_TOK_ELSE:
case SCC_PP_TOK_ELIF:
case SCC_PP_TOK_ELIFDEF:
case SCC_PP_TOK_ELIFNDEF:
case SCC_PP_TOK_ENDIF:
case SCC_PP_TOK_LINE:
case SCC_PP_TOK_EMBED:
case SCC_PP_TOK_ERROR:
case SCC_PP_TOK_WARNING:
case SCC_PP_TOK_PRAGMA:
// 暂时跳过这一行
scc_lex_parse_skip_line(stream, pos);
TODO();
break;
default:
LOG_WARN("Unknown preprocessor directive: %s",
scc_cstring_as_cstr(&directive));
scc_lex_parse_skip_line(stream, pos);
}
ERR:
scc_lex_parse_skip_line(stream, pos);
FREE:
scc_cstring_free(&directive);
scc_cstring_free(&name);
}

View File

@@ -1,132 +0,0 @@
/**
* @file pprocessor.c
* @brief C语言预处理器实现
*/
#include <lex_parser.h>
#include <pp_macro.h>
#include <pp_parse.h>
#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;
Assert(stream != null);
int ch;
READ_BUF:
if (stream->tmp_stream != null &&
(ch = scc_probe_stream_consume(stream->tmp_stream)) != scc_stream_eof) {
return ch;
}
RETRY:
scc_probe_stream_reset(stream->input);
ch = scc_probe_stream_peek(stream->input);
if (ch == '#') {
scc_pp_parse_directive(stream->input, &stream->pos,
&stream->self->macro_table);
scc_probe_stream_sync(stream->input);
goto RETRY;
} else if (scc_lex_parse_is_identifier_prefix(ch)) {
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");
}
goto READ_BUF;
} else if (scc_probe_stream_next(stream->input) == '/') {
ch = scc_probe_stream_peek(stream->input);
if (ch == '/') {
scc_probe_stream_reset(stream->input);
scc_lex_parse_skip_line(stream->input, &stream->pos);
scc_probe_stream_sync(stream->input);
} else if (ch == '*') {
scc_probe_stream_reset(stream->input);
scc_lex_parse_skip_block_comment(stream->input, &stream->pos);
scc_probe_stream_sync(stream->input);
}
}
// 非标识符字符,直接消费并返回
return scc_probe_stream_consume(stream->input);
}
static void pp_stream_drop(scc_probe_stream_t *_stream) {
scc_pp_stream_t *stream = (scc_pp_stream_t *)_stream;
Assert(stream != null);
scc_cstring_free(&stream->stream.name);
if (stream->tmp_stream) {
scc_probe_stream_drop(stream->tmp_stream);
}
scc_free(_stream);
}
static scc_probe_stream_t *pp_stream_init(scc_pproc_t *self,
scc_probe_stream_t *input) {
scc_pp_stream_t *stream = scc_malloc(sizeof(scc_pp_stream_t));
if (stream == null) {
LOG_FATAL("Failed to allocate memory for output stream");
return null;
}
if (self == null) {
scc_free(stream);
return null;
}
stream->self = self;
stream->input = input;
stream->tmp_stream = null;
stream->pos = scc_pos_create();
stream->stream.name = scc_cstring_from_cstr("pp_stream");
stream->stream.consume = pp_stream_read_char;
stream->stream.peek = null;
stream->stream.next = null;
stream->stream.sync = null;
stream->stream.reset = null;
stream->stream.back = null;
stream->stream.read_buf = null;
stream->stream.is_at_end = null;
stream->stream.drop = pp_stream_drop;
return (scc_probe_stream_t *)stream;
}
scc_probe_stream_t *scc_pproc_init(scc_pproc_t *pp, scc_probe_stream_t *input) {
if (pp == null || input == null) {
return null;
}
pp->stream = pp_stream_init(pp, input);
if (pp->stream == null) {
return null;
}
scc_pp_marco_table_init(&pp->macro_table);
return pp->stream;
}
// 销毁预处理器
void scc_pproc_drop(scc_pproc_t *pp) {
if (pp == null)
return;
scc_pp_macro_table_drop(&pp->macro_table);
// 清理流
if (pp->stream) {
scc_probe_stream_drop(pp->stream);
pp->stream = null;
}
}

View File

@@ -1,6 +0,0 @@
#define hash_hash # ## #
#define mkstr(a) # a
#define in_between(a) mkstr(a)
#define join(c, d) in_between(c hash_hash d)
char p[] = join(x, y);
// char p[] = "x ## y";

View File

@@ -1 +0,0 @@
char p[] = "x ## y";

View File

@@ -1,28 +0,0 @@
#define x 3
#define f(a) f(x * (a))
#undef x
#define x 2
#define g f
#define z z[0]
#define h g(~
#define m(a) a(w)
#define w 0,1
#define t(a) a
#define p() int
#define q(x) x
#define r(x,y) x ## y
#define str(x) # x
f(y+1) + f(f(z)) % t(t(g)(0) + t)(1);
g(x+(3,4)-w) | h 5) & m
(f)^m(m);
char c[2][6] = { str(hello), str() };
/*
* f(2 * (y+1)) + f(2 * (f(2 * (z[0])))) % f(2 * (0)) + t(1);
* f(2 * (2+(3,4)-0,1)) | f(2 * (~ 5)) & f(2 * (0,1))^m(0,1);
* char c[2][6] = { "hello", "" };
*/
#define L21 f(y+1) + f(f(z)) % t(t(g)(0) + t)(1);
#define L22 g(x+(3,4)-w) | h 5) & m\
(f)^m(m);
L21
L22

View File

@@ -1,5 +0,0 @@
f(2 * (y+1)) + f(2 * (f(2 * (z[0])))) % f(2 * (0)) + t(1);
f(2 * (2 +(3,4)-0,1)) | f(2 * (~ 5)) & f(2 * (0,1))^m(0,1);
char c[2][6] = { "hello", "" };
f(2 * (y+1)) + f(2 * (f(2 * (z[0])))) % f(2 * (0)) + t(1);
f(2 * (2 +(3,4)-0,1)) | f(2 * (~ 5)) & f(2 * (0,1))^m(0,1);

View File

@@ -1,15 +0,0 @@
#define str(s) # s
#define xstr(s) str(s)
#define debug(s, t) printf("x" # s "= %d, x" # t "= %s", \
x ## s, x ## t)
#define INCFILE(n) vers ## n
#define glue(a, b) a ## b
#define xglue(a, b) glue(a, b)
#define HIGHLOW "hello"
#define LOW LOW ", world"
debug(1, 2);
fputs(str(strncmp("abc\0d", "abc", '\4') // this goes away
== 0) str(: @\n), s);
\#include xstr(INCFILE(2).h)
glue(HIGH, LOW);
xglue(HIGH, LOW)

View File

@@ -1,5 +0,0 @@
printf("x" "1" "= %d, x" "2" "= %s", x1, x2);
fputs("strncmp(\"abc\\0d\", \"abc\", '\\4') == 0" ": @\n", s);
\#include "vers2.h"
"hello";
"hello" ", world"

View File

@@ -1,4 +0,0 @@
#define foobar 1
#define C(x,y) x##y
#define D(x) (C(x,bar))
D(foo)

View File

@@ -1 +0,0 @@
(1)

View File

@@ -1,7 +0,0 @@
#define t(x,y,z) x ## y ## z
#define xxx(s) int s[] = { t(1,2,3), t(,4,5), t(6,,7), t(8,9,), \
t(10,,), t(,11,), t(,,12), t(,,) };
int j[] = { t(1,2,3), t(,4,5), t(6,,7), t(8,9,),
t(10,,), t(,11,), t(,,12), t(,,) };
xxx(j)

View File

@@ -1,3 +0,0 @@
int j[] = { 123, 45, 67, 89,
10, 11, 12, };
int j[] = { 123, 45, 67, 89, 10, 11, 12, };

View File

@@ -1,5 +0,0 @@
#define X(a,b, \
c,d) \
foo
X(1,2,3,4)

View File

@@ -1 +0,0 @@
foo

View File

@@ -1,4 +0,0 @@
#define a() YES
#define b() a
b()
b()()

View File

@@ -1,2 +0,0 @@
a
YES

View File

@@ -1,4 +0,0 @@
// test macro expansion in arguments
#define s_pos s_s.s_pos
#define foo(x) (x)
foo(hej.s_pos)

View File

@@ -1 +0,0 @@
(hej.s_s.s_pos)

View File

@@ -1,4 +0,0 @@
#define C(a,b,c) a##b##c
#define N(x,y) C(x,_,y)
#define A_O aaaaoooo
N(A,O)

View File

@@ -1 +0,0 @@
aaaaoooo

View File

@@ -1,10 +0,0 @@
#define f(x) x
#define g(x) f(x) f(x
#define i(x) g(x)) g(x
#define h(x) i(x))) i(x
#define k(x) i(x))) i(x))))
f(x)
g(x))
i(x)))
h(x))))
k(x))))

View File

@@ -1,5 +0,0 @@
x
x x
x x x x
x x x x x x x x
x x x x x x x x))))

View File

@@ -1,36 +0,0 @@
#define D1(s, ...) s
#define D2(s, ...) s D1(__VA_ARGS__)
#define D3(s, ...) s D2(__VA_ARGS__)
#define D4(s, ...) s D3(__VA_ARGS__)
D1(a)
D2(a, b)
D3(a, b, c)
D4(a, b, c, d)
x D4(a, b, c, d) y
x D4(a, b, c) y
x D4(a, b) y
x D4(a) y
x D4() y
#define GNU_COMMA(X,Y...) X,##Y
x GNU_COMMA(A,B,C) y
x GNU_COMMA(A,B) y
x GNU_COMMA(A) y
x GNU_COMMA() y
#define __sun_attr___noreturn__ __attribute__((__noreturn__))
#define ___sun_attr_inner(__a) __sun_attr_##__a
#define __sun_attr__(__a) ___sun_attr_inner __a
#define __NORETURN __sun_attr__((__noreturn__))
__NORETURN
#define X(...)
#define Y(...) 1 __VA_ARGS__ 2
Y(X X() ())
#define DDD(A, B) D_ ## B ## _D_ ## A
#define CCC(X, ...) DDD(X,## __VA_ARGS__)
/* must be D_B_D_A (not D_B _D_A) */
CCC(A,B)

View File

@@ -1,16 +0,0 @@
a
a b
a b c
a b c d
x a b c d y
x a b c y
x a b y
x a y
x y
x A,B,C y
x A,B y
x A y
x y
__attribute__((__noreturn__))
1 2
D_B_D_A

View File

@@ -1,8 +0,0 @@
#define SRC(y...) \
9999: y; \
.section __ex_table, "a"; \
.long 9999b, 6001f ; \
// .previous
SRC(1: movw (%esi), %bx)
6001:

View File

@@ -1,2 +0,0 @@
9999: 1: movw (%esi), %bx; .section __ex_table, "a"; .long 9999b, 6001f ;
6001:

View File

@@ -1,6 +0,0 @@
# `modelist' label. Each video mode record looks like:
#ifdef AAA
# modelist' label. Each video mode record looks like:
#endif
.text
endtext:

View File

@@ -1,2 +0,0 @@
.text
endtext:

View File

@@ -1,13 +0,0 @@
#define W Z
#define Z(X) W(X,2)
#define Y(X) Z(X)
#define X Y
return X(X(1));
#define P Q
#define Q(n) P(n,2)
return P(1);
#define A (B * B)
#define B (A + A)
return A + B;

View File

@@ -1,3 +0,0 @@
return Z(Z(1,2),2);
return Q(1,2);
return ((A + A) * (A + A)) + ((B * B) + (B * B));

View File

@@ -1,18 +0,0 @@
// insert a space between two tokens if otherwise they
// would form a single token when read back
#define n(x) x
return (n(long)n(double))d;
return n(A)n(++)n(+)n(B);
return n(A)n(+)n(++)n(B);
return n(A)n(++)n(+)n(+)n(B);
// not a hex float
return n(0x1E)n(-1);
// unlike gcc but correct
// XXX: return n(x)+n(x)-n(1)+n(1)-2;
// unlike gcc, but cannot appear in valid C
// XXX: return n(x)n(x)n(1)n(2)n(x);

View File

@@ -1,5 +0,0 @@
return (long double)d;
return A+++B;
return A+ ++B;
return A+++ +B;
return 0x1E -1;

View File

@@ -1,3 +0,0 @@
/* The following should warn */
#define A ...
#define A <<=

View File

@@ -1,2 +0,0 @@
16.c:3: warning: A redefined

View File

@@ -1,14 +0,0 @@
#define STR1(u) # u
#define pass(a) a
#define __ASM_REG(reg) STR1(one##reg)
#define _ASM_DX __ASM_REG(tok)
X162 pass(__ASM_REG(tok))
X161 pass(_ASM_DX)
X163 pass(STR1(one##tok))
X170 pass(x ## y)
X171 pass(x pass(##) y)
#define Y(x) Z(x)
#define X Y
X180 return X(X(1));

View File

@@ -1,6 +0,0 @@
X162 "onetok"
X161 "onetok"
X163 "one##tok"
X170 x ## y
X171 x ## y
X180 return Z(Z(1));

View File

@@ -1,30 +0,0 @@
#define M_RETI_ARG27(x,y,z,aa, ...) aa
#define M_RET_ARG27(...) M_RETI_ARG27(__VA_ARGS__)
#define M_COMMA_P(...) M_RET_ARG27(__VA_ARGS__, 1, 1, 0, useless)
#define M_EMPTYI_DETECT(...) 0, 1,
#define M_EMPTYI_P_C1(...) M_COMMA_P(M_EMPTYI_DETECT __VA_ARGS__ () )
#define EX
#define empty(x)
#define fnlike(x) yeah x
/* If the following macro is called with empty arg (X183), the use
of 'x' between fnlike and '(' doesn't hinder the recognition of this
being a further fnlike macro invocation. */
#define usefnlike(x) fnlike x (x)
X181 M_EMPTYI_P_C1()
X182 M_EMPTYI_P_C1(x)
X183 usefnlike()
#define ABC(x) ABC : x
#define A(a,b,c) a ## b ## c
#define B(a,b,c) A(a,b,c)
#define C(a,b,c) a b c
B(
C(A,C(,,),),
C(B(,,),B,B(,,)),
C(B(,,),B(,,),C)
)/* */(a b c)
#define TEST(x) TEST : x
#define K(a,b) a ## b
#define L(a,b) K( TE a , b ST )
L(,)(t e s t)

View File

@@ -1,5 +0,0 @@
X181 1
X182 0
X183 yeah
ABC : a b c
TEST : t e s t

View File

@@ -1,101 +0,0 @@
#define M_C2I(a, ...) a ## __VA_ARGS__
#define M_C(a, ...) M_C2I(a, __VA_ARGS__)
#define M_C3I(a, b, ...) a ## b ## __VA_ARGS__
#define M_C3(a, b, ...) M_C3I(a ,b, __VA_ARGS__)
#define M_RETI_ARG2(a, b, ...) b
#define M_RET_ARG2(...) M_RETI_ARG2(__VA_ARGS__)
#define M_RETI_ARG27(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,aa, ...) aa
#define M_RET_ARG27(...) M_RETI_ARG27(__VA_ARGS__)
#define M_TOBOOLI_0 1, 0,
#define M_BOOL(x) M_RET_ARG2(M_C(M_TOBOOLI_, x), 1, useless)
#define M_IFI_0(true_macro, ...) __VA_ARGS__
#define M_IFI_1(true_macro, ...) true_macro
#define M_IF(c) M_C(M_IFI_, M_BOOL(c))
#define M_FLAT(...) __VA_ARGS__
#define M_INVI_0 1
#define M_INVI_1 0
#define M_INV(x) M_C(M_INVI_, x)
#define M_ANDI_00 0
#define M_ANDI_01 0
#define M_ANDI_10 0
#define M_ANDI_11 1
#define M_AND(x,y) M_C3(M_ANDI_, x, y)
#define M_ORI_00 0
#define M_ORI_01 1
#define M_ORI_10 1
#define M_ORI_11 1
#define M_OR(x,y) M_C3(M_ORI_, x, y)
#define M_COMMA_P(...) M_RET_ARG27(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, useless)
#define M_EMPTYI_DETECT(...) 0, 1,
#define M_EMPTYI_P_C1(...) M_COMMA_P(M_EMPTYI_DETECT __VA_ARGS__ ())
#define M_EMPTYI_P_C2(...) M_COMMA_P(M_EMPTYI_DETECT __VA_ARGS__)
#define M_EMPTYI_P_C3(...) M_COMMA_P(__VA_ARGS__ () )
#define M_EMPTY_P(...) M_AND(M_EMPTYI_P_C1(__VA_ARGS__), M_INV(M_OR(M_OR(M_EMPTYI_P_C2(__VA_ARGS__), M_COMMA_P(__VA_ARGS__)),M_EMPTYI_P_C3(__VA_ARGS__))))
#define M_APPLY_FUNC2B(func, arg1, arg2) \
M_IF(M_EMPTY_P(arg2))(,func(arg1, arg2))
#define M_MAP2B_0(func, data, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z,...) \
M_APPLY_FUNC2B(func, data, a) M_APPLY_FUNC2B(func, data, b) M_APPLY_FUNC2B(func, data, c) \
M_APPLY_FUNC2B(func, data, d) M_APPLY_FUNC2B(func, data, e) M_APPLY_FUNC2B(func, data, f) \
M_APPLY_FUNC2B(func, data, g) M_APPLY_FUNC2B(func, data, h) M_APPLY_FUNC2B(func, data, i) \
M_APPLY_FUNC2B(func, data, j) M_APPLY_FUNC2B(func, data, k) M_APPLY_FUNC2B(func, data, l) \
M_APPLY_FUNC2B(func, data, m) M_APPLY_FUNC2B(func, data, n) M_APPLY_FUNC2B(func, data, o) \
M_APPLY_FUNC2B(func, data, p) M_APPLY_FUNC2B(func, data, q) M_APPLY_FUNC2B(func, data, r) \
M_APPLY_FUNC2B(func, data, s) M_APPLY_FUNC2B(func, data, t) M_APPLY_FUNC2B(func, data, u) \
M_APPLY_FUNC2B(func, data, v) M_APPLY_FUNC2B(func, data, w) M_APPLY_FUNC2B(func, data, x) \
M_APPLY_FUNC2B(func, data, y) M_APPLY_FUNC2B(func, data, z)
#define M_MAP2B(f, ...) M_MAP2B_0(f, __VA_ARGS__, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , )
#define M_INIT_INIT(a) ,a,
#define M_GET_METHOD(method, method_default, ...) \
M_RET_ARG2 (M_MAP2B(M_C, M_C3(M_, method, _), __VA_ARGS__), method_default,)
#define M_TEST_METHOD_P(method, oplist) \
M_BOOL(M_GET_METHOD (method, 0, M_FLAT oplist))
#define TRUE 1
#define TEST1(n) \
M_IF(n)(ok,nok)
#define TEST2(op) \
M_TEST_METHOD_P(INIT, op)
#define TEST3(op) \
M_IF(M_TEST_METHOD_P(INIT, op))(ok, nok)
#define TEST4(op) \
TEST1(TEST2(op))
#define KO(a) ((void)1)
/* This checks that the various expansions that ultimately lead to
something like 'KO(arg,arg)', where 'KO' comes from a macro
expansion reducing from a large macro chain do not are regarded
as funclike macro invocation of KO. E.g. X93 and X94 expand to 'KO',
but X95 must not consume the (a,b) arguments outside the M_IF()
invocation to reduce the 'KO' macro to an invocation. Instead
X95 should reduce via M_IF(KO)(a,b) to 'a'.
The other lines here are variations on this scheme, with X1 to
X6 coming from the bug report at
http://lists.nongnu.org/archive/html/tinycc-devel/2017-07/msg00017.html */
X92 M_IF(KO)
X93 M_GET_METHOD(INIT, 0, INIT(KO))
X94 M_GET_METHOD(INIT, 0, M_FLAT (INIT(KO)))
X95 M_IF(M_GET_METHOD(INIT, 0, INIT(KO)))(a,b)
X96 M_IF(M_GET_METHOD(INIT, 0, M_FLAT (INIT(KO))))
X97 M_IF(M_GET_METHOD(INIT, 0, M_FLAT (INIT(KO))))(ok,nok)
X98 (M_TEST_METHOD_P(INIT, (INIT(KO))))(ok, nok)
X99 M_IF(M_TEST_METHOD_P(INIT, (INIT(KO))))(ok, nok)
// test begins
X1 TEST1(TRUE) // ==> expect ok, get ok
// First test with a token which is not a macro
X2 TEST2((INIT(ok))) // ==> expect 1, get 1
X3 TEST3((INIT(ok))) // ==> expect ok, get ok
// Then test with a token which is a macro, but should not be expanded.
X4 TEST2((INIT(KO))) // ==> expect 1, get 1
X5 TEST4(INIT(KO))
X6 TEST3((INIT(KO))) // ==> expect ok, get "error: macro 'KO' used with too many args"

View File

@@ -1,14 +0,0 @@
X92 M_IFI_1
X93 KO
X94 KO
X95 a
X96 M_IFI_1
X97 ok
X98 (1)(ok, nok)
X99 ok
X1 ok
X2 1
X3 ok
X4 1
X5 nok
X6 ok

View File

@@ -1,13 +0,0 @@
/* Various things I encountered while hacking the pre processor */
#define wrap(x) x
#define pr_warning(fmt, ...) printk(KERN_WARNING fmt, ##__VA_ARGS__)
#define pr_warn(x,y) pr_warning(x,y)
#define net_ratelimited_function(function, ...) function(__VA_ARGS__)
X1 net_ratelimited_function(pr_warn, "pipapo", bla);
X2 net_ratelimited_function(wrap(pr_warn), "bla", foo);
#define two m n
#define chain4(a,b,c,d) a ## b ## c ## d
X2 chain4(two,o,p,q)
X3 chain4(o,two,p,q)
X4 chain4(o,p,two,q)
X5 chain4(o,p,q,two)

View File

@@ -1,6 +0,0 @@
X1 printk(KERN_WARNING "pipapo",bla);
X2 printk(KERN_WARNING "bla",foo);
X2 twoopq
X3 otwopq
X4 optwoq
X5 opqtwo

View File

@@ -1,41 +0,0 @@
/* accept 'defined' as result of substitution */
----- 1 ------
#define AAA 2
#define BBB
#define CCC (defined ( AAA ) && AAA > 1 && !defined BBB)
#if !CCC
OK
#else
NOT OK
#endif
----- 2 ------
#undef BBB
#if CCC
OK
#else
NOT OK
#endif
----- 3 ------
#define DEFINED defined
#define DDD (DEFINED ( AAA ) && AAA > 1 && !DEFINED BBB)
#if (DDD)
OK
#else
NOT OK
#endif
----- 4 ------
#undef AAA
#if !(DDD)
OK
#else
NOT OK
#endif
----- 5 ------
line __LINE__
#define __LINE__ # ## #
line __LINE__

View File

@@ -1,11 +0,0 @@
----- 1 ------
OK
----- 2 ------
OK
----- 3 ------
OK
----- 4 ------
OK
----- 5 ------
line 39
line ##

View File

@@ -1,12 +0,0 @@
#define TRACE(a,b,c) X a X b X c X
#define rettrue(x) 1
A rettrue(bla) B
TRACE(
ARG_1,
#if rettrue(bla)
ARG_2,
#else
ARG_2_wrong,
#endif
ARG_3
);

View File

@@ -1,2 +0,0 @@
A 1 B
X ARG_1 X ARG_2 X ARG_3 X;

View File

@@ -1,55 +0,0 @@
#
# credits: 01..13.c from the pcc cpp-tests suite
#
TOP = ../..
include $(TOP)/Makefile
SRC = $(TOPSRC)/tests/pp
VPATH = $(SRC)
files = $(patsubst %.$1,%.test,$(notdir $(wildcard $(SRC)/*.$1)))
TESTS = $(call files,c) $(call files,S)
all test testspp.all: $(sort $(TESTS))
DIFF_OPTS = -Nu -b
# Filter source directory in warnings/errors (out-of-tree builds)
FILTER = 2>&1 | sed 's,$(SRC)/,,g'
%.test: %.c %.expect
@echo PPTest $* ...
-@$(TCC) -E -P $< $(FILTER) >$*.output 2>&1 ; \
diff $(DIFF_OPTS) $(SRC)/$*.expect $*.output \
&& rm -f $*.output
%.test: %.S %.expect
@echo PPTest $* ...
-@$(TCC) -E -P $< $(FILTER) >$*.output 2>&1 ; \
diff $(DIFF_OPTS) $(SRC)/$*.expect $*.output \
&& rm -f $*.output
testspp.%: %.test ;
# generate .expect file with tcc, e.g. "make testspp.17+"
testspp.%+: %.c
$(TCC) -E -P $*.[cS] -o $*.expect
# automatically generate .expect files with gcc:
%.expect: # %.c
gcc -E -P $*.[cS] >$*.expect 2>&1
# tell make not to delete
.PRECIOUS: %.expect
clean:
rm -f *.output
02.test : DIFF_OPTS += -w
# 15.test : DIFF_OPTS += -I"^XXX:"
# diff options:
# -b ighore space changes
# -w ighore all whitespace
# -B ignore blank lines
# -I <RE> ignore lines matching RE

View File

@@ -1,96 +0,0 @@
Tiny C Compiler - C Scripting Everywhere - The Smallest ANSI C compiler
-----------------------------------------------------------------------
Features:
--------
- SMALL! You can compile and execute C code everywhere, for example on
rescue disks.
- FAST! tcc generates optimized x86 code. No byte code
overhead. Compile, assemble and link about 7 times faster than 'gcc
-O0'.
- UNLIMITED! Any C dynamic library can be used directly. TCC is
heading toward full ISOC99 compliance. TCC can of course compile
itself.
- SAFE! tcc includes an optional memory and bound checker. Bound
checked code can be mixed freely with standard code.
- Compile and execute C source directly. No linking or assembly
necessary. Full C preprocessor included.
- C script supported : just add '#!/usr/local/bin/tcc -run' at the first
line of your C source, and execute it directly from the command
line.
Documentation:
-------------
1) Installation on a i386/x86_64/arm/aarch64/riscv64
Linux/macOS/FreeBSD/NetBSD/OpenBSD hosts.
./configure
make
make test
make install
Notes: For FreeBSD, NetBSD and OpenBSD, gmake should be used instead of make.
For Windows read tcc-win32.txt.
makeinfo must be installed to compile the doc. By default, tcc is
installed in /usr/local/bin. ./configure --help shows configuration
options.
2) Introduction
We assume here that you know ANSI C. Look at the example ex1.c to know
what the programs look like.
The include file <tcclib.h> can be used if you want a small basic libc
include support (especially useful for floppy disks). Of course, you
can also use standard headers, although they are slower to compile.
You can begin your C script with '#!/usr/local/bin/tcc -run' on the first
line and set its execute bits (chmod a+x your_script). Then, you can
launch the C code as a shell or perl script :-) The command line
arguments are put in 'argc' and 'argv' of the main functions, as in
ANSI C.
3) Examples
ex1.c: simplest example (hello world). Can also be launched directly
as a script: './ex1.c'.
ex2.c: more complicated example: find a number with the four
operations given a list of numbers (benchmark).
ex3.c: compute fibonacci numbers (benchmark).
ex4.c: more complicated: X11 program. Very complicated test in fact
because standard headers are being used ! As for ex1.c, can also be launched
directly as a script: './ex4.c'.
ex5.c: 'hello world' with standard glibc headers.
tcc.c: TCC can of course compile itself. Used to check the code
generator.
tcctest.c: auto test for TCC which tests many subtle possible bugs. Used
when doing 'make test'.
4) Full Documentation
Please read tcc-doc.html to have all the features of TCC.
Additional information is available for the Windows port in tcc-win32.txt.
License:
-------
TCC is distributed under the GNU Lesser General Public License (see
COPYING file).
Fabrice Bellard.

View File

@@ -1,27 +0,0 @@
X1 __COUNTER__
X2 __COUNTER__
#if __COUNTER__
X3 __COUNTER__
#endif
#define pass(x) x
#define a x __COUNTER__ y
#define a2 pass(__COUNTER__)
#define f(c) c __COUNTER__
#define apply(d) d d __COUNTER__ x2 f(d) y2 __COUNTER__
#define _paste(a,b) a ## b
#define paste(a,b) _paste(a,b)
#define _paste3(a,b,c) a ## b ## c
#define doublepaste(a,b) _paste3(a,b,b)
#define str(x) #x
X4 a
X5 f(a)
X6 f(b)
X7 f(__COUNTER__)
X8 apply(a)
X9 apply(f(a))
X10 apply(__COUNTER__)
X11 apply(a2)
X12 str(__COUNTER__)
X13 paste(x,__COUNTER__)
X14 _paste(x,__COUNTER__)
X15 doublepaste(x,__COUNTER__)

View File

@@ -1,15 +0,0 @@
X1 0
X2 1
X3 3
X4 x 4 y
X5 x 5 y 6
X6 b 7
X7 8 9
X8 x 10 y x 10 y 11 x2 x 10 y 12 y2 13
X9 x 14 y 15 x 14 y 15 16 x2 x 14 y 15 17 y2 18
X10 19 19 20 x2 19 21 y2 22
X11 23 23 24 x2 23 25 y2 26
X12 "__COUNTER__"
X13 x27
X14 x__COUNTER__
X15 x2828

View File

@@ -1,91 +0,0 @@
#include <assert.h>
#include <pprocessor.h>
#include <stdio.h>
#include <stdlib.h>
#include <utest/acutest.h>
static scc_probe_stream_t *from_file_stream(FILE *fp) {
if (fseek(fp, 0, SEEK_END) != 0) {
perror("fseek failed");
return NULL;
}
usize fsize = ftell(fp);
if (fseek(fp, 0, SEEK_SET)) {
perror("fseek failed");
return NULL;
}
char *buffer = (char *)malloc(fsize);
usize read_ret = fread(buffer, 1, fsize, fp);
fclose(fp);
scc_mem_probe_stream_t *mem_stream = malloc(sizeof(scc_mem_probe_stream_t));
scc_probe_stream_t *stream =
scc_mem_probe_stream_init(mem_stream, buffer, read_ret, true);
return stream;
}
static void test_file(const char *name) {
char src_fname[1024];
char expected_fname[1024];
snprintf(src_fname, sizeof(src_fname), __FILE__ "/../pp/%s.c", name);
snprintf(expected_fname, sizeof(expected_fname),
__FILE__ "/../pp/%s.expect", name);
FILE *fsrc = fopen(src_fname, "r");
assert(fsrc != NULL);
FILE *fexpect = fopen(expected_fname, "r");
assert(fexpect != NULL);
scc_pproc_t pp;
scc_probe_stream_t *output_stream =
scc_pproc_init(&pp, from_file_stream(fsrc));
scc_probe_stream_t *expect_stream = from_file_stream(fexpect);
fclose(fsrc);
fclose(fexpect);
TEST_CASE(src_fname);
#define BUFFER_LEN 4096
int ch;
char expect_buffer[BUFFER_LEN];
char output_buffer[BUFFER_LEN];
usize size_produced = 0, size_expected = 0;
for (usize i = 0; i < BUFFER_LEN; ++i) {
ch = scc_probe_stream_consume(expect_stream);
if (ch != scc_stream_eof) {
expect_buffer[i] = (char)ch;
} else {
size_expected = i;
break;
}
}
for (usize i = 0; i < BUFFER_LEN; ++i) {
ch = scc_probe_stream_consume(output_stream);
if (ch != scc_stream_eof) {
output_buffer[i] = (char)ch;
} else {
size_produced = i;
break;
}
}
TEST_CHECK(size_produced == size_expected &&
memcmp(output_buffer, expect_buffer, size_produced) == 0);
TEST_DUMP("Expected:", expect_buffer, size_expected);
TEST_DUMP("Produced:", output_buffer, size_produced);
scc_pproc_drop(&pp);
}
static void test_basic(void) {
char name[32];
for (int i = 1; i <= 22; ++i) {
snprintf(name, sizeof(name), "%02d", i);
test_file(name);
}
}
TEST_LIST = {
{"basic", test_basic},
{NULL, NULL},
};

View File

@@ -1,24 +0,0 @@
#include <pprocessor.h>
#include <stdio.h>
int main(void) {
scc_pproc_t pp;
scc_mem_probe_stream_t input;
scc_probe_stream_t *output;
const char buf[] = "#define A 123 \"asd\"\nA A A\n";
output = scc_pproc_init(
&pp, scc_mem_probe_stream_init(&input, buf, sizeof(buf) - 1, false));
int ch = 0;
while (1) {
ch = scc_probe_stream_consume(output);
if (ch == scc_stream_eof) {
break;
}
putc(ch, stdout);
}
return 0;
}

View File

@@ -1,215 +0,0 @@
#include <assert.h>
#include <pprocessor.h>
#include <string.h>
#include <utest/acutest.h>
static cbool process_input(const char *input, scc_cstring_t *output) {
scc_pproc_t pp;
scc_mem_probe_stream_t mem_stream;
scc_probe_stream_t *output_stream;
// 初始化预处理器
output_stream =
scc_pproc_init(&pp, scc_mem_probe_stream_init(&mem_stream, input,
strlen(input), false));
// 获取输出结果
int ch;
*output = scc_cstring_create();
while (1) {
ch = scc_probe_stream_consume(output_stream);
if (ch == scc_stream_eof) {
break;
}
scc_cstring_append_ch(output, (char)ch);
}
// 清理资源
scc_pproc_drop(&pp);
return true;
}
#define CHECK_PP_OUTPUT_EXACT(input, expect) \
do { \
scc_cstring_t output; \
process_input(input, &output); \
assert(output.data != NULL); \
TEST_CHECK(strcmp(output.data, expect) == 0); \
TEST_MSG("Expected: %s", expect); \
TEST_MSG("Produced: %s", output.data); \
} while (0)
#define CHECK_PP_OUTPUT_CONTAIN(input, expect) \
do { \
scc_cstring_t output; \
process_input(input, &output); \
assert(output.data != NULL); \
TEST_CHECK(strstr(output.data, expect) != NULL); \
TEST_MSG("Expected: %s", expect); \
TEST_MSG("Produced: %s", output.data); \
} while (0)
static void test_define_simple_object_macro(void) {
TEST_CASE("simple object-like macro");
CHECK_PP_OUTPUT_EXACT("#define MAX 100\nMAX\n", "100\n");
CHECK_PP_OUTPUT_EXACT("#define NAME test\r\nNAME\n", "test\n");
}
static void test_define_complex_object_macro(void) {
TEST_CASE("complex object-like macro");
CHECK_PP_OUTPUT_EXACT("#define VALUE (100 + 50)\nVALUE\n", "(100 + 50)\n");
CHECK_PP_OUTPUT_EXACT("#define PI 3.14159\nPI\n", "3.14159\n");
}
static void test_define_object_macro_backspace(void) {
TEST_CASE("object-like macro check backspace");
CHECK_PP_OUTPUT_EXACT("#define MAX 100\nMAX\n", "100\n");
CHECK_PP_OUTPUT_EXACT("#define NAME \ttest\r\nNAME\n", "test\n");
CHECK_PP_OUTPUT_EXACT("#define \tVALUE (100 \t+ 50)\nVALUE\n",
"(100 + 50)\n");
CHECK_PP_OUTPUT_EXACT("#define \tPI \t 3.14159\nPI\n", "3.14159\n");
}
static void test_define_function_macro(void) {
TEST_CASE("function-like macro");
CHECK_PP_OUTPUT_EXACT("#define ADD(a,b) a + b\nADD(1, 2)\n", "1 + 2\n");
CHECK_PP_OUTPUT_EXACT(
"#define MAX(a,b) ((a) > (b) ? (a) : (b))\nMAX(10, 20)\n",
"((10) > (20) ? (10) : (20))\n");
}
static void test_define_stringify_operator(void) {
TEST_CASE("stringify operator (#)");
CHECK_PP_OUTPUT_EXACT("#define STRINGIFY(x) #x\nSTRINGIFY(hello)\n",
"\"hello\"\n");
CHECK_PP_OUTPUT_EXACT("#define STR(x) #x\nSTR(test value)\n",
"\"test value\"\n");
}
static void test_define_concat_operator(void) {
TEST_CASE("concatenation operator (##)");
CHECK_PP_OUTPUT_EXACT("#define CONCAT(a,b) a##b\nCONCAT(hello,world)\n",
"helloworld\n");
CHECK_PP_OUTPUT_EXACT("#define JOIN(pre,suf) pre ## suf\nJOIN(var, 123)\n",
"var123\n");
}
static void test_define_nested_macros(void) {
TEST_CASE("nested macros");
CHECK_PP_OUTPUT_EXACT(
"#define MAX 100\n#define TWICE_MAX (MAX * 2)\nTWICE_MAX\n",
"(100 * 2)\n");
CHECK_PP_OUTPUT_EXACT(
"#define A 1\n#define B (A + 1)\n#define C (B + 1)\nC\n",
"((1 + 1) + 1)\n");
}
static void test_undef_macros(void) {
TEST_CASE("test_undef_macros");
CHECK_PP_OUTPUT_EXACT("#define x 1\n"
"x\n"
"#undef x\n"
"x\n"
"#define x 2\n"
"x\n",
"1\nx\n2\n");
}
static void hard_test_define_func_macros(void) {
TEST_CASE("func_macros_hard with pp_01");
CHECK_PP_OUTPUT_EXACT("#define hash_hash # ## #\n"
"#define mkstr(a) # a\n"
"#define in_between(a) mkstr(a)\n"
"#define join(c, d) in_between(c hash_hash d)\n"
"char p[] = join(x, y);\n",
"char p[] = \"x ## y\";\n");
TEST_CASE("func_macros_hard with recursive define");
CHECK_PP_OUTPUT_EXACT("#define M1(x) M2(x + 1)\n"
"#define M2(x) M1(x * 2)\n"
"M1(5)\n",
"M1(5 + 1 * 2)\n");
CHECK_PP_OUTPUT_EXACT("#define A B\n"
"#define B C\n"
"#define C 1\n"
"A\n",
"1\n");
TEST_CASE("func_macros_hard with self recursive call");
CHECK_PP_OUTPUT_EXACT("#define M(x) x\n"
"M(M(10))\n",
"10\n");
CHECK_PP_OUTPUT_EXACT("#define M(x) M(x)\n"
"#define N(x) x\n"
"N(M(1))\n",
"M(1)\n");
TEST_CASE("func_macros_hard with define by macro");
CHECK_PP_OUTPUT_EXACT("#define M1(x) M1(x + 1)\n"
"#define M2 M1\n"
"#define M3(x) x\n"
"M3(M3(M2)(0))\n",
"M1(0 + 1)\n");
TEST_CASE("TODO");
CHECK_PP_OUTPUT_EXACT("#define str(x) # x\n"
"str()\n",
"\"\"\n");
TEST_CASE("TODO");
CHECK_PP_OUTPUT_EXACT("#define x 1\n"
"#define f(a) f(x * (a))\n"
"f(0)\n"
"f(x)",
"f(1 * (0))\n"
"f(1 * (1))");
CHECK_PP_OUTPUT_EXACT("#define x x(0)\n"
"#define f(a) f(x * (a))\n"
"f(f(0))\n"
"f(f(x))\n"
"f(f(a))\n",
"f(x(0) * (f(x(0) * (0))))\n"
"f(x(0) * (f(x(0) * (x(0)))))\n"
"f(x(0) * (f(x(0) * (a))))\n");
}
static void test_conditional_compilation(void) {
TEST_CASE("conditional compilation");
CHECK_PP_OUTPUT_EXACT("#if 1\ntrue\n#endif\n", "true\n");
CHECK_PP_OUTPUT_EXACT("#if 0\nfalse\n#endif\n", "");
CHECK_PP_OUTPUT_EXACT("#define FLAG 1\n#if FLAG\ntrue\n#endif\n", "true\n");
}
static void test_error_cases(void) {
TEST_CASE("macro redefinition");
// 应检测到警告或错误
// CHECK_PP_OUTPUT_CONTAIN("#define A 1\n#define A 2\n", "warning");
TEST_CASE("undefined macro");
CHECK_PP_OUTPUT_EXACT("UNDEFINED_MACRO\n", "UNDEFINED_MACRO\n");
}
static void test_edge_cases(void) {
TEST_CASE("empty macro");
CHECK_PP_OUTPUT_EXACT("#define EMPTY\nEMPTY\n", "\n");
TEST_CASE("macro with only spaces");
CHECK_PP_OUTPUT_EXACT("#define SPACE \nSPACE\n", "\n");
TEST_CASE("deep nesting");
CHECK_PP_OUTPUT_EXACT("#define A B\n#define B C\n#define C 1\nA\n", "1\n");
}
#define TEST_LIST_CASE(func_name) {#func_name, func_name}
TEST_LIST = {
TEST_LIST_CASE(test_define_simple_object_macro),
TEST_LIST_CASE(test_define_complex_object_macro),
TEST_LIST_CASE(test_define_object_macro_backspace),
TEST_LIST_CASE(test_define_function_macro),
TEST_LIST_CASE(test_define_stringify_operator),
TEST_LIST_CASE(test_define_concat_operator),
TEST_LIST_CASE(test_define_nested_macros),
TEST_LIST_CASE(test_undef_macros),
TEST_LIST_CASE(hard_test_define_func_macros),
{NULL, NULL},
};

View File

@@ -26,7 +26,7 @@ typedef struct {
int scc_sstream_init(scc_sstream_t *stream, const char *fname, int ring_size); int scc_sstream_init(scc_sstream_t *stream, const char *fname, int ring_size);
int scc_sstream_init_by_buffer(scc_sstream_t *stream, const char *buffer, int scc_sstream_init_by_buffer(scc_sstream_t *stream, const char *buffer,
usize len, int owned, int ring_size); usize len, int owned, int ring_size);
scc_sstream_ring_t *scc_sstream_ref_ring(scc_sstream_t *stream); scc_sstream_ring_t *scc_sstream_to_ring(scc_sstream_t *stream);
void scc_sstream_drop_ring(scc_sstream_ring_t *ring); void scc_sstream_drop_ring(scc_sstream_ring_t *ring);
void scc_sstream_drop(scc_sstream_t *stream); void scc_sstream_drop(scc_sstream_t *stream);

View File

@@ -66,7 +66,7 @@ static int sstream_scan_at(scc_sstream_t *stream, scc_pos_t scan_pos,
} }
// 环形缓冲区填充回调(通过 userdata 获取流对象) // 环形缓冲区填充回调(通过 userdata 获取流对象)
static cbool fill_func(scc_sstream_char_t *out, void *userdata) { static int fill_func(scc_sstream_char_t *out, void *userdata) {
scc_sstream_t *stream = (scc_sstream_t *)userdata; scc_sstream_t *stream = (scc_sstream_t *)userdata;
if (stream->fill_pos.offset >= stream->len) if (stream->fill_pos.offset >= stream->len)
return false; // 已到文件尾 return false; // 已到文件尾
@@ -82,6 +82,10 @@ static cbool fill_func(scc_sstream_char_t *out, void *userdata) {
int scc_sstream_init(scc_sstream_t *stream, const char *fname, int ring_size) { int scc_sstream_init(scc_sstream_t *stream, const char *fname, int ring_size) {
Assert(stream != null && fname != null); Assert(stream != null && fname != null);
scc_file_t file = scc_fopen(fname, SCC_FILE_READ); scc_file_t file = scc_fopen(fname, SCC_FILE_READ);
if (file == null) {
LOG_ERROR("Failed to open file: %s", fname);
return 0;
}
usize fsize = scc_fsize(file); usize fsize = scc_fsize(file);
if (fsize == 0) { if (fsize == 0) {
LOG_WARN("file size is 0"); LOG_WARN("file size is 0");
@@ -115,7 +119,7 @@ int scc_sstream_init_by_buffer(scc_sstream_t *stream, const char *buffer,
return 0; return 0;
} }
scc_sstream_ring_t *scc_sstream_ref_ring(scc_sstream_t *stream) { scc_sstream_ring_t *scc_sstream_to_ring(scc_sstream_t *stream) {
Assert(stream != null); Assert(stream != null);
stream->used++; stream->used++;
return &stream->ring; return &stream->ring;

View File

@@ -23,7 +23,7 @@
usize head; \ usize head; \
usize probe; \ usize probe; \
usize tail; \ usize tail; \
cbool (*fill)(type * out, void *userdata); \ int (*fill)(type * out, void *userdata); \
void *userdata; \ void *userdata; \
} }