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:
@@ -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__ */
|
||||
@@ -144,6 +144,10 @@ typedef enum scc_tok_subtype {
|
||||
scc_tok_subtype_t scc_get_tok_subtype(scc_tok_type_t type);
|
||||
const char *scc_get_tok_name(scc_tok_type_t type);
|
||||
|
||||
/**
|
||||
* @brief 词法分析结果
|
||||
* @warning 需要手动释放lexeme否则会出现内存泄漏
|
||||
*/
|
||||
typedef struct scc_lexer_token {
|
||||
scc_tok_type_t type;
|
||||
scc_cstring_t lexeme;
|
||||
|
||||
54
libs/lexer/include/scc_lexer.h
Normal file
54
libs/lexer/include/scc_lexer.h
Normal 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__ */
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <lexer.h>
|
||||
#include "scc_lexer.h"
|
||||
#include <lexer_log.h>
|
||||
#include <scc_lexer.h>
|
||||
|
||||
static const struct {
|
||||
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) {
|
||||
lexer->stream_ref = *stream_ref;
|
||||
lexer->stream_ref = stream_ref;
|
||||
lexer->ring_ref_count = 0;
|
||||
lexer->jump_macro = false;
|
||||
}
|
||||
|
||||
@@ -68,7 +70,7 @@ static inline cbool is_hex_digit(int ch) {
|
||||
/* 从环形缓冲区预览一个字符(带EOF检测) */
|
||||
static inline cbool peek_char(scc_lexer_t *lexer, scc_sstream_char_t *out) {
|
||||
cbool ok;
|
||||
scc_ring_peek(lexer->stream_ref, *out, ok);
|
||||
scc_ring_peek(*lexer->stream_ref, *out, 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,
|
||||
scc_sstream_char_t *out) {
|
||||
cbool ok;
|
||||
scc_ring_next(lexer->stream_ref, *out, ok);
|
||||
scc_ring_next(*lexer->stream_ref, *out, ok);
|
||||
if (!ok)
|
||||
return false;
|
||||
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); // 消费 '/'
|
||||
while (peek_char(lexer, &cur) && !is_newline(cur.character)) {
|
||||
next_char(lexer, &lex, &cur);
|
||||
scc_ring_consume(lexer->stream_ref);
|
||||
scc_ring_consume(*lexer->stream_ref);
|
||||
}
|
||||
// 注释结束,不包含换行符(换行符单独成token)
|
||||
} 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); // 消费 '/'
|
||||
break;
|
||||
}
|
||||
scc_ring_consume(lexer->stream_ref);
|
||||
scc_ring_consume(*lexer->stream_ref);
|
||||
}
|
||||
} else {
|
||||
// 只是除号 /
|
||||
@@ -161,7 +163,7 @@ void scc_lexer_get_token(scc_lexer_t *lexer, scc_lexer_tok_t *token) {
|
||||
token->type = SCC_TOK_IDENT; // 暂定
|
||||
while (peek_char(lexer, &cur) && is_identifier_part(cur.character)) {
|
||||
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));
|
||||
@@ -241,7 +243,7 @@ void scc_lexer_get_token(scc_lexer_t *lexer, scc_lexer_tok_t *token) {
|
||||
} else {
|
||||
next_char(lexer, &lex, &cur);
|
||||
}
|
||||
scc_ring_consume(lexer->stream_ref);
|
||||
scc_ring_consume(*lexer->stream_ref);
|
||||
}
|
||||
} else {
|
||||
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
|
||||
scc_ring_consume(lexer->stream_ref);
|
||||
scc_ring_consume(*lexer->stream_ref);
|
||||
token->type = token->type; // 上面已设
|
||||
token->loc = start_loc;
|
||||
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 ||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
#include <lexer.h>
|
||||
#include <lexer_log.h>
|
||||
#include <scc_lexer.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -53,10 +53,7 @@ int main(int argc, char *argv[]) {
|
||||
scc_get_tok_name(token.type),
|
||||
scc_cstring_as_cstr(&token.lexeme), token.loc.name,
|
||||
token.loc.line, token.loc.col);
|
||||
// LOG_DEBUG("%s", token.val.str);
|
||||
// printf("line: %d, column: %d, type: %3d, typename: %s\n",
|
||||
// lexer.line, lexer.index, token.type,
|
||||
// scc_get_tok_name(token.type));
|
||||
scc_cstring_free(&token.lexeme);
|
||||
}
|
||||
scc_sstream_drop_ring(ref);
|
||||
scc_sstream_drop(&stream);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// test_lexer.c
|
||||
#include <lexer.h>
|
||||
#include <scc_lexer.h>
|
||||
#include <string.h>
|
||||
#include <utest/acutest.h>
|
||||
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
|
||||
tests/pp 复制了 TinyCC 的 tests/pp 测试
|
||||
|
||||
详情见 [README](tests/pp/README)
|
||||
@@ -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" },
|
||||
]
|
||||
@@ -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__ */
|
||||
@@ -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__ */
|
||||
@@ -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__ */
|
||||
@@ -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__ */
|
||||
@@ -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(¯o->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(¯o->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, ¯o->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, ¯o->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, ¯o->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(¶m, &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, ¯o->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, ¶ms);
|
||||
// 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(¶m, &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, ¯o->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, ¶ms, &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;
|
||||
}
|
||||
@@ -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(¯o->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(¯os->table, ¯o->name);
|
||||
if (existing) {
|
||||
LOG_WARN("Redefining macro: %s", scc_cstring_as_cstr(¯o->name));
|
||||
scc_pp_macro_drop(existing);
|
||||
}
|
||||
|
||||
scc_hashtable_set(¯os->table, ¯o->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(¯os->table, ¯o->name);
|
||||
if (existing) {
|
||||
LOG_WARN("Redefining macro: %s", scc_cstring_as_cstr(¯o->name));
|
||||
scc_pp_macro_drop(existing);
|
||||
}
|
||||
|
||||
scc_hashtable_set(¯os->table, ¯o->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, ¯o->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(¯os->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(¯os->table, macro_free, null);
|
||||
scc_hashtable_drop(¯os->table);
|
||||
}
|
||||
@@ -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(¤t_arg);
|
||||
scc_cstring_free(&pos.name);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ch == '(') {
|
||||
paren_depth++;
|
||||
scc_cstring_append_ch(¤t_arg, (char)ch);
|
||||
scc_probe_stream_next(stream);
|
||||
} else if (ch == ')') {
|
||||
paren_depth--;
|
||||
if (paren_depth > 0) {
|
||||
scc_cstring_append_ch(¤t_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(¤t_arg, (char)ch);
|
||||
scc_probe_stream_next(stream);
|
||||
}
|
||||
}
|
||||
|
||||
// 添加最后一个参数
|
||||
if (!scc_cstring_is_empty(¤t_arg)) {
|
||||
scc_vec_push(*args, current_arg);
|
||||
} else {
|
||||
scc_cstring_free(¤t_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, ¶ms)) {
|
||||
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, ¶ms, &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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
@@ -1 +0,0 @@
|
||||
char p[] = "x ## y";
|
||||
@@ -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
|
||||
@@ -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);
|
||||
@@ -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)
|
||||
@@ -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"
|
||||
@@ -1,4 +0,0 @@
|
||||
#define foobar 1
|
||||
#define C(x,y) x##y
|
||||
#define D(x) (C(x,bar))
|
||||
D(foo)
|
||||
@@ -1 +0,0 @@
|
||||
(1)
|
||||
@@ -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)
|
||||
@@ -1,3 +0,0 @@
|
||||
int j[] = { 123, 45, 67, 89,
|
||||
10, 11, 12, };
|
||||
int j[] = { 123, 45, 67, 89, 10, 11, 12, };
|
||||
@@ -1,5 +0,0 @@
|
||||
#define X(a,b, \
|
||||
c,d) \
|
||||
foo
|
||||
|
||||
X(1,2,3,4)
|
||||
@@ -1 +0,0 @@
|
||||
foo
|
||||
@@ -1,4 +0,0 @@
|
||||
#define a() YES
|
||||
#define b() a
|
||||
b()
|
||||
b()()
|
||||
@@ -1,2 +0,0 @@
|
||||
a
|
||||
YES
|
||||
@@ -1,4 +0,0 @@
|
||||
// test macro expansion in arguments
|
||||
#define s_pos s_s.s_pos
|
||||
#define foo(x) (x)
|
||||
foo(hej.s_pos)
|
||||
@@ -1 +0,0 @@
|
||||
(hej.s_s.s_pos)
|
||||
@@ -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)
|
||||
@@ -1 +0,0 @@
|
||||
aaaaoooo
|
||||
@@ -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))))
|
||||
@@ -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))))
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -1,8 +0,0 @@
|
||||
#define SRC(y...) \
|
||||
9999: y; \
|
||||
.section __ex_table, "a"; \
|
||||
.long 9999b, 6001f ; \
|
||||
// .previous
|
||||
|
||||
SRC(1: movw (%esi), %bx)
|
||||
6001:
|
||||
@@ -1,2 +0,0 @@
|
||||
9999: 1: movw (%esi), %bx; .section __ex_table, "a"; .long 9999b, 6001f ;
|
||||
6001:
|
||||
@@ -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:
|
||||
@@ -1,2 +0,0 @@
|
||||
.text
|
||||
endtext:
|
||||
@@ -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;
|
||||
@@ -1,3 +0,0 @@
|
||||
return Z(Z(1,2),2);
|
||||
return Q(1,2);
|
||||
return ((A + A) * (A + A)) + ((B * B) + (B * B));
|
||||
@@ -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);
|
||||
@@ -1,5 +0,0 @@
|
||||
return (long double)d;
|
||||
return A+++B;
|
||||
return A+ ++B;
|
||||
return A+++ +B;
|
||||
return 0x1E -1;
|
||||
@@ -1,3 +0,0 @@
|
||||
/* The following should warn */
|
||||
#define A ...
|
||||
#define A <<=
|
||||
@@ -1,2 +0,0 @@
|
||||
|
||||
16.c:3: warning: A redefined
|
||||
@@ -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));
|
||||
@@ -1,6 +0,0 @@
|
||||
X162 "onetok"
|
||||
X161 "onetok"
|
||||
X163 "one##tok"
|
||||
X170 x ## y
|
||||
X171 x ## y
|
||||
X180 return Z(Z(1));
|
||||
@@ -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)
|
||||
@@ -1,5 +0,0 @@
|
||||
X181 1
|
||||
X182 0
|
||||
X183 yeah
|
||||
ABC : a b c
|
||||
TEST : t e s t
|
||||
@@ -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"
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -1,6 +0,0 @@
|
||||
X1 printk(KERN_WARNING "pipapo",bla);
|
||||
X2 printk(KERN_WARNING "bla",foo);
|
||||
X2 twoopq
|
||||
X3 otwopq
|
||||
X4 optwoq
|
||||
X5 opqtwo
|
||||
@@ -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__
|
||||
@@ -1,11 +0,0 @@
|
||||
----- 1 ------
|
||||
OK
|
||||
----- 2 ------
|
||||
OK
|
||||
----- 3 ------
|
||||
OK
|
||||
----- 4 ------
|
||||
OK
|
||||
----- 5 ------
|
||||
line 39
|
||||
line ##
|
||||
@@ -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
|
||||
);
|
||||
@@ -1,2 +0,0 @@
|
||||
A 1 B
|
||||
X ARG_1 X ARG_2 X ARG_3 X;
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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__)
|
||||
@@ -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
|
||||
@@ -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},
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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},
|
||||
};
|
||||
@@ -26,7 +26,7 @@ typedef struct {
|
||||
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,
|
||||
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(scc_sstream_t *stream);
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ static int sstream_scan_at(scc_sstream_t *stream, scc_pos_t scan_pos,
|
||||
}
|
||||
|
||||
// 环形缓冲区填充回调(通过 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;
|
||||
if (stream->fill_pos.offset >= stream->len)
|
||||
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) {
|
||||
Assert(stream != null && fname != null);
|
||||
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);
|
||||
if (fsize == 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;
|
||||
}
|
||||
|
||||
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);
|
||||
stream->used++;
|
||||
return &stream->ring;
|
||||
|
||||
Reference in New Issue
Block a user