Compare commits
11 Commits
c86071416d
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
| d2eaf2247f | |||
| 51869bf081 | |||
| 3b2f68111e | |||
| 4940b652eb | |||
| 8007825800 | |||
| b705e5d0ad | |||
| 9c2b4db22a | |||
| bc0b1d23e3 | |||
| a52ff33e30 | |||
| 27a87d17ab | |||
| 08a60e6e8a |
8
LICENSE
Normal file
8
LICENSE
Normal file
@@ -0,0 +1,8 @@
|
||||
The MIT License (MIT)
|
||||
Copyright © 2026 <copyright holders>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
28
justfile
28
justfile
@@ -11,5 +11,29 @@ count:
|
||||
# you need download `tokei` it can download by cargo
|
||||
tokei libs runtime src -e tests
|
||||
|
||||
build_lexer:
|
||||
python ./tools/cbuild/cbuild.py --path libs/lexer build
|
||||
count-file:
|
||||
# you need download `tokei` it can download by cargo
|
||||
tokei libs runtime src -e tests --files
|
||||
|
||||
clean:
|
||||
cbuild clean
|
||||
|
||||
build:
|
||||
cbuild build -cclang
|
||||
|
||||
build-install: build
|
||||
cp ./build/dev/scc.exe ./scc.exe
|
||||
|
||||
test-scc:
|
||||
# windows: (Get-Content a.txt -Raw) -replace '\x1b\[[0-9;]*[a-zA-Z]', '' | Set-Content clean.txt
|
||||
# linux: sed 's/\x1b\[[0-9;]*[a-zA-Z]//g' a.txt > clean.txt
|
||||
just clean
|
||||
just build-install
|
||||
just clean
|
||||
cbuild build -cscc
|
||||
|
||||
debug-scc:
|
||||
just clean
|
||||
just build-install
|
||||
just clean
|
||||
cbuild build -cscc --dev --record --dry-run
|
||||
|
||||
@@ -43,6 +43,7 @@ typedef enum scc_argparse_err {
|
||||
SCC_ARGPARSE_ERR_UNKNOWN_VALUE,
|
||||
} scc_argparse_err_t;
|
||||
|
||||
typedef SCC_VEC(const char *) scc_argparse_list_t;
|
||||
// 约束规范结构体
|
||||
struct scc_argparse_spec {
|
||||
scc_argparse_val_type_t value_type; // 值类型
|
||||
@@ -56,6 +57,7 @@ struct scc_argparse_spec {
|
||||
const char **str_store; // 字符串存储
|
||||
char **str_alloc_store; // 字符串存储(使用alloc,需要free)
|
||||
void **ptr_store; // 通用指针存储
|
||||
scc_argparse_list_t *vec_store; // 新增:指向字符串向量的指针
|
||||
} store;
|
||||
|
||||
// 枚举值约束
|
||||
@@ -215,6 +217,15 @@ static inline void scc_argparse_spec_setup_choices(scc_argparse_spec_t *spec,
|
||||
spec->choices.count = count;
|
||||
}
|
||||
|
||||
// 添加设置列表的辅助函数(内联)
|
||||
static inline void scc_argparse_spec_setup_list(scc_argparse_spec_t *spec,
|
||||
scc_argparse_list_t *vec) {
|
||||
spec->value_type = SCC_ARGPARSE_VAL_TYPE_LIST;
|
||||
spec->store.vec_store = vec;
|
||||
// 自动设置 flag_takes_multiple 为 true,因为列表需要多个值
|
||||
spec->flag_takes_multiple = true;
|
||||
}
|
||||
|
||||
#define SCC_ARGPARSE_MACRO_SETTER(attr) \
|
||||
static inline void scc_argparse_spec_set_##attr(scc_argparse_spec_t *spec, \
|
||||
cbool flag) { \
|
||||
|
||||
@@ -63,6 +63,10 @@ static inline void parse_cmd(scc_optparse_t *optparse,
|
||||
min_args = 0;
|
||||
max_args = 0;
|
||||
break;
|
||||
case SCC_ARGPARSE_VAL_TYPE_LIST: // 列表类型
|
||||
min_args = 1;
|
||||
max_args = 65535; // FIXME maybe INT_MAX ?
|
||||
break;
|
||||
default:
|
||||
min_args = 0;
|
||||
max_args = 0;
|
||||
@@ -161,6 +165,10 @@ static int handle_option(scc_argparse_context_t *ctx, scc_argparse_t *parser) {
|
||||
return SCC_ARGPARSE_ERR_PNT_DEFAULT;
|
||||
}
|
||||
|
||||
if (parser->need_version && scc_strcmp(opt->long_name, "version") == 0) {
|
||||
// TODO default version print
|
||||
}
|
||||
|
||||
if (opt->spec.flag_store_as_count) {
|
||||
(*opt->spec.store.int_store)++;
|
||||
}
|
||||
@@ -169,8 +177,14 @@ static int handle_option(scc_argparse_context_t *ctx, scc_argparse_t *parser) {
|
||||
*opt->spec.store.bool_store = true;
|
||||
}
|
||||
|
||||
if (ctx->result.value) {
|
||||
if (!ctx->result.value) {
|
||||
return SCC_ARGPARSE_ERR_NONE;
|
||||
}
|
||||
opt->spec.raw_value = ctx->result.value;
|
||||
|
||||
if (opt->spec.value_type == SCC_ARGPARSE_VAL_TYPE_LIST) {
|
||||
scc_vec_push(*opt->spec.store.vec_store, ctx->result.value);
|
||||
} else {
|
||||
*opt->spec.store.str_store = ctx->result.value;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define __SCC_AST_DEF_H__
|
||||
|
||||
#include <scc_core.h>
|
||||
#include <scc_pos.h>
|
||||
|
||||
/**
|
||||
* @brief AST 节点类型枚举
|
||||
@@ -310,7 +311,7 @@ struct scc_ast_expr {
|
||||
} compound_literal;
|
||||
// 字面量
|
||||
struct {
|
||||
scc_cvalue_t value;
|
||||
scc_cstring_t lexme;
|
||||
} literal;
|
||||
// 标识符
|
||||
struct {
|
||||
|
||||
@@ -392,16 +392,10 @@ static void dump_expr_impl(scc_ast_expr_t *expr, scc_tree_dump_ctx_t *ctx) {
|
||||
PRINT_QUOTED_VALUE(ctx, get_op_str(expr->unary.op));
|
||||
break;
|
||||
case SCC_AST_EXPR_INT_LITERAL:
|
||||
PRINT_VALUE(ctx, " %lld", expr->literal.value.i);
|
||||
break;
|
||||
case SCC_AST_EXPR_FLOAT_LITERAL:
|
||||
PRINT_VALUE(ctx, " %f", expr->literal.value.f);
|
||||
break;
|
||||
case SCC_AST_EXPR_CHAR_LITERAL:
|
||||
PRINT_VALUE(ctx, " '%c'", (char)expr->literal.value.ch);
|
||||
break;
|
||||
case SCC_AST_EXPR_STRING_LITERAL:
|
||||
PRINT_VALUE(ctx, " \"%s\"", expr->literal.value.cstr.data);
|
||||
PRINT_VALUE(ctx, " %s", expr->literal.lexme);
|
||||
break;
|
||||
case SCC_AST_EXPR_IDENTIFIER:
|
||||
if (expr->identifier.name) {
|
||||
|
||||
@@ -42,44 +42,44 @@ typedef enum scc_cstd {
|
||||
// WARNING: Using Binary Search To Fast Find Keyword
|
||||
// 你必须确保其中是按照字典序排列
|
||||
#define SCC_CKEYWORD_TABLE \
|
||||
X(asm , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_ASM , SCC_CEXT_SCC) \
|
||||
X(atomic , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_ATOMIC , SCC_CEXT_SCC) \
|
||||
X(auto , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_AUTO , SCC_CEXT_SCC) \
|
||||
X(bool , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_BOOL , SCC_CEXT_SCC) \
|
||||
X(break , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_BREAK , SCC_CSTD_C89) \
|
||||
X(case , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_CASE , SCC_CSTD_C89) \
|
||||
X(char , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_CHAR , SCC_CSTD_C89) \
|
||||
X(complex , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_COMPLEX , SCC_CEXT_SCC) \
|
||||
X(const , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_CONST , SCC_CSTD_C89) \
|
||||
X(continue , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_CONTINUE , SCC_CSTD_C89) \
|
||||
X(default , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_DEFAULT , SCC_CSTD_C89) \
|
||||
X(do , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_DO , SCC_CSTD_C89) \
|
||||
X(double , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_DOUBLE , SCC_CSTD_C89) \
|
||||
X(else , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_ELSE , SCC_CSTD_C89) \
|
||||
X(enum , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_ENUM , SCC_CSTD_C89) \
|
||||
X(extern , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_EXTERN , SCC_CSTD_C89) \
|
||||
X(float , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_FLOAT , SCC_CSTD_C89) \
|
||||
X(for , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_FOR , SCC_CSTD_C89) \
|
||||
X(goto , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_GOTO , SCC_CSTD_C89) \
|
||||
X(if , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_IF , SCC_CSTD_C89) \
|
||||
X(inline , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_INLINE , SCC_CSTD_C99) \
|
||||
X(int , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_INT , SCC_CSTD_C89) \
|
||||
X(long , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_LONG , SCC_CSTD_C89) \
|
||||
X(register , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_REGISTER , SCC_CSTD_C89) \
|
||||
X(restrict , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_RESTRICT , SCC_CSTD_C99) \
|
||||
X(return , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_RETURN , SCC_CSTD_C89) \
|
||||
X(short , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_SHORT , SCC_CSTD_C89) \
|
||||
X(signed , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_SIGNED , SCC_CSTD_C89) \
|
||||
X(sizeof , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_SIZEOF , SCC_CSTD_C89) \
|
||||
X(static , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_STATIC , SCC_CSTD_C89) \
|
||||
X(struct , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_STRUCT , SCC_CSTD_C89) \
|
||||
X(switch , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_SWITCH , SCC_CSTD_C89) \
|
||||
X(typedef , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_TYPEDEF , SCC_CSTD_C89) \
|
||||
X(union , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_UNION , SCC_CSTD_C89) \
|
||||
X(unsigned , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_UNSIGNED , SCC_CSTD_C89) \
|
||||
X(void , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_VOID , SCC_CSTD_C89) \
|
||||
X(volatile , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_VOLATILE , SCC_CSTD_C89) \
|
||||
X(while , SCC_TOK_SUBTYPE_KEYWORD , SCC_TOK_WHILE , SCC_CSTD_C89) \
|
||||
X(asm , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_ASM , SCC_CEXT_SCC) \
|
||||
X(atomic , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_ATOMIC , SCC_CEXT_SCC) \
|
||||
X(auto , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_AUTO , SCC_CEXT_SCC) \
|
||||
X(bool , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_BOOL , SCC_CEXT_SCC) \
|
||||
X(break , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_BREAK , SCC_CSTD_C89) \
|
||||
X(case , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_CASE , SCC_CSTD_C89) \
|
||||
X(char , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_CHAR , SCC_CSTD_C89) \
|
||||
X(complex , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_COMPLEX , SCC_CEXT_SCC) \
|
||||
X(const , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_CONST , SCC_CSTD_C89) \
|
||||
X(continue , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_CONTINUE , SCC_CSTD_C89) \
|
||||
X(default , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_DEFAULT , SCC_CSTD_C89) \
|
||||
X(do , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_DO , SCC_CSTD_C89) \
|
||||
X(double , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_DOUBLE , SCC_CSTD_C89) \
|
||||
X(else , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_ELSE , SCC_CSTD_C89) \
|
||||
X(enum , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_ENUM , SCC_CSTD_C89) \
|
||||
X(extern , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_EXTERN , SCC_CSTD_C89) \
|
||||
X(float , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_FLOAT , SCC_CSTD_C89) \
|
||||
X(for , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_FOR , SCC_CSTD_C89) \
|
||||
X(goto , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_GOTO , SCC_CSTD_C89) \
|
||||
X(if , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_IF , SCC_CSTD_C89) \
|
||||
X(inline , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_INLINE , SCC_CSTD_C99) \
|
||||
X(int , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_INT , SCC_CSTD_C89) \
|
||||
X(long , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_LONG , SCC_CSTD_C89) \
|
||||
X(register , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_REGISTER , SCC_CSTD_C89) \
|
||||
X(restrict , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_RESTRICT , SCC_CSTD_C99) \
|
||||
X(return , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_RETURN , SCC_CSTD_C89) \
|
||||
X(short , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_SHORT , SCC_CSTD_C89) \
|
||||
X(signed , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_SIGNED , SCC_CSTD_C89) \
|
||||
X(sizeof , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_SIZEOF , SCC_CSTD_C89) \
|
||||
X(static , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_STATIC , SCC_CSTD_C89) \
|
||||
X(struct , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_STRUCT , SCC_CSTD_C89) \
|
||||
X(switch , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_SWITCH , SCC_CSTD_C89) \
|
||||
X(typedef , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_TYPEDEF , SCC_CSTD_C89) \
|
||||
X(union , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_UNION , SCC_CSTD_C89) \
|
||||
X(unsigned , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_UNSIGNED , SCC_CSTD_C89) \
|
||||
X(void , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_VOID , SCC_CSTD_C89) \
|
||||
X(volatile , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_VOLATILE , SCC_CSTD_C89) \
|
||||
X(while , SCC_TOK_SUBTYPE_IDENTIFIER , SCC_TOK_WHILE , SCC_CSTD_C89) \
|
||||
// KEYWORD_TABLE
|
||||
|
||||
#define SCC_CTOK_TABLE \
|
||||
@@ -145,9 +145,9 @@ typedef enum scc_cstd {
|
||||
// END
|
||||
/* clang-format on */
|
||||
|
||||
// 定义TokenType枚举
|
||||
typedef enum scc_tok_type {
|
||||
// must first becase the unknown token must be 0
|
||||
/* clang-format off */
|
||||
// must first becase the unknown token must be 0
|
||||
#define X(str, subtype, tok) tok,
|
||||
SCC_CTOK_TABLE
|
||||
#undef X
|
||||
@@ -159,11 +159,11 @@ typedef enum scc_tok_type {
|
||||
#define X(name, subtype, tok, std) tok,
|
||||
SCC_CKEYWORD_TABLE
|
||||
#undef X
|
||||
/* clang-format on*/
|
||||
} scc_tok_type_t;
|
||||
|
||||
typedef enum scc_tok_subtype {
|
||||
SCC_TOK_SUBTYPE_INVALID, // 错误占位
|
||||
SCC_TOK_SUBTYPE_KEYWORD, // 关键字
|
||||
SCC_TOK_SUBTYPE_OPERATOR, // 操作符
|
||||
SCC_TOK_SUBTYPE_IDENTIFIER, // 标识符
|
||||
SCC_TOK_SUBTYPE_LITERAL, // 字面量
|
||||
@@ -187,6 +187,11 @@ scc_tok_subtype_t scc_get_tok_subtype(scc_tok_type_t type);
|
||||
const char *scc_get_tok_name(scc_tok_type_t type);
|
||||
|
||||
static inline void scc_lexer_tok_drop(scc_lexer_tok_t *tok) {
|
||||
tok->type = SCC_TOK_UNKNOWN;
|
||||
tok->loc.col = 0;
|
||||
tok->loc.line = 0;
|
||||
tok->loc.name = null;
|
||||
tok->loc.offset = 0;
|
||||
scc_cstring_free(&tok->lexeme);
|
||||
}
|
||||
|
||||
@@ -195,4 +200,20 @@ static inline cbool scc_lexer_tok_match(const scc_lexer_tok_t *tok,
|
||||
return tok->type == type;
|
||||
}
|
||||
|
||||
// 深拷贝 token
|
||||
static inline scc_lexer_tok_t scc_lexer_tok_copy(const scc_lexer_tok_t *src) {
|
||||
scc_lexer_tok_t dst = *src;
|
||||
dst.lexeme = scc_cstring_copy(&src->lexeme);
|
||||
return dst;
|
||||
}
|
||||
|
||||
// 移动 token(源 token 不再拥有 lexeme)
|
||||
static inline void scc_lexer_tok_move(scc_lexer_tok_t *dst,
|
||||
scc_lexer_tok_t *src) {
|
||||
*dst = *src;
|
||||
src->lexeme.data = null;
|
||||
src->lexeme.size = 0;
|
||||
src->lexeme.cap = 0;
|
||||
}
|
||||
|
||||
#endif /* __SCC_LEXER_TOKEN_H__ */
|
||||
|
||||
@@ -28,11 +28,14 @@ static inline cbool scc_lexer_next_non_blank(scc_lexer_tok_ring_t *stream,
|
||||
static inline void scc_lexer_skip_until_newline(scc_lexer_tok_ring_t *stream) {
|
||||
scc_lexer_tok_t tok;
|
||||
cbool ok;
|
||||
while (scc_lexer_peek_non_blank(stream, &tok)) {
|
||||
if (tok.type == SCC_TOK_ENDLINE)
|
||||
break;
|
||||
while (1) {
|
||||
scc_ring_next_consume(*stream, tok, ok);
|
||||
if (!ok)
|
||||
break;
|
||||
scc_tok_type_t type = tok.type;
|
||||
scc_lexer_tok_drop(&tok);
|
||||
if (type == SCC_TOK_ENDLINE)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -301,8 +301,7 @@ void test_identifiers() {
|
||||
void test_preprocessor() {
|
||||
TEST_CASE("Preprocessor directives - just the # token");
|
||||
TEST_TOKEN("#", SCC_TOK_SHARP);
|
||||
TEST_TOKEN("##", SCC_TOK_SHARP); // 第一个 # 是 token,第二个 # 将是下一个
|
||||
// token(在序列测试中验证)
|
||||
TEST_TOKEN("##", SCC_TOK_SHARP_SHARP);
|
||||
|
||||
// 多 token 序列测试 #include 等
|
||||
TEST_SEQUENCE("#include <stdio.h>", SCC_TOK_SHARP, SCC_TOK_IDENT,
|
||||
@@ -311,6 +310,18 @@ void test_preprocessor() {
|
||||
TEST_SEQUENCE("#define FOO 123", SCC_TOK_SHARP, SCC_TOK_IDENT,
|
||||
SCC_TOK_BLANK, SCC_TOK_IDENT, SCC_TOK_BLANK,
|
||||
SCC_TOK_INT_LITERAL);
|
||||
TEST_SEQUENCE("#define FOO(x) x + 1", SCC_TOK_SHARP, SCC_TOK_IDENT,
|
||||
SCC_TOK_BLANK, SCC_TOK_IDENT, SCC_TOK_L_PAREN, SCC_TOK_IDENT,
|
||||
SCC_TOK_R_PAREN, SCC_TOK_BLANK, SCC_TOK_IDENT, SCC_TOK_BLANK,
|
||||
SCC_TOK_ADD, SCC_TOK_BLANK, SCC_TOK_INT_LITERAL);
|
||||
TEST_SEQUENCE("#undef FOO", SCC_TOK_SHARP, SCC_TOK_IDENT, SCC_TOK_BLANK,
|
||||
SCC_TOK_IDENT);
|
||||
|
||||
TEST_SEQUENCE("#error \"This is an error\"", SCC_TOK_SHARP, SCC_TOK_IDENT,
|
||||
SCC_TOK_BLANK, SCC_TOK_STRING_LITERAL);
|
||||
TEST_SEQUENCE("#warning \"This is an warning\"\n", SCC_TOK_SHARP,
|
||||
SCC_TOK_IDENT, SCC_TOK_BLANK, SCC_TOK_STRING_LITERAL,
|
||||
SCC_TOK_ENDLINE);
|
||||
}
|
||||
|
||||
void test_edge_cases() {
|
||||
@@ -348,7 +359,7 @@ void test_sequences() {
|
||||
TEST_SEQUENCE("<<=", SCC_TOK_ASSIGN_L_SH);
|
||||
TEST_SEQUENCE("...", SCC_TOK_ELLIPSIS);
|
||||
TEST_SEQUENCE("->", SCC_TOK_DEREF);
|
||||
TEST_SEQUENCE("##", SCC_TOK_SHARP, SCC_TOK_SHARP); // 两个预处理记号
|
||||
TEST_SEQUENCE("##", SCC_TOK_SHARP_SHARP); // 两个预处理记号
|
||||
|
||||
TEST_CASE("Comments and whitespace interleaved");
|
||||
TEST_SEQUENCE("/* comment */ a // line comment\n b", SCC_TOK_BLOCK_COMMENT,
|
||||
@@ -371,18 +382,18 @@ void test_error_recovery() {
|
||||
// 测试未闭合的字符字面量:词法分析器可能继续直到遇到换行或 EOF
|
||||
// 这里假设它会产生一个 SCC_TOK_CHAR_LITERAL 但包含到结束
|
||||
// 但标准 C 中未闭合是错误,我们可能返回 UNKNOWN
|
||||
TEST_CASE("Unterminated character literal");
|
||||
TEST_TOKEN("'a", SCC_TOK_UNKNOWN); // 取决于实现,可能为 CHAR_LITERAL
|
||||
// 更可靠的测试:序列中下一个 token 是什么
|
||||
TEST_SEQUENCE("'a b", SCC_TOK_UNKNOWN,
|
||||
SCC_TOK_IDENT); // 假设第一个 token 是错误
|
||||
// TEST_CASE("Unterminated character literal");
|
||||
// TEST_TOKEN("'a", SCC_TOK_UNKNOWN); // 取决于实现,可能为 CHAR_LITERAL
|
||||
// // 更可靠的测试:序列中下一个 token 是什么
|
||||
// TEST_SEQUENCE("'a b", SCC_TOK_UNKNOWN,
|
||||
// SCC_TOK_IDENT); // 假设第一个 token 是错误
|
||||
|
||||
TEST_CASE("Unterminated string literal");
|
||||
TEST_TOKEN("\"hello", SCC_TOK_UNKNOWN); // 同样
|
||||
// TEST_CASE("Unterminated string literal");
|
||||
// TEST_TOKEN("\"hello", SCC_TOK_UNKNOWN); // 同样
|
||||
|
||||
TEST_CASE("Unterminated block comment");
|
||||
TEST_SEQUENCE("/* comment",
|
||||
SCC_TOK_BLOCK_COMMENT); // 直到 EOF,可能仍为注释
|
||||
// TEST_CASE("Unterminated block comment");
|
||||
// TEST_SEQUENCE("/* comment",
|
||||
// SCC_TOK_BLOCK_COMMENT); // 直到 EOF,可能仍为注释
|
||||
}
|
||||
|
||||
// ============================ 主测试列表 ============================
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
|
||||
typedef struct {
|
||||
scc_pproc_macro_table_t *macro_table;
|
||||
scc_pproc_macro_table_t *expanded_set;
|
||||
scc_lexer_tok_ring_t *input;
|
||||
scc_lexer_tok_vec_t output;
|
||||
scc_pproc_macro_table_t expanded_set;
|
||||
int need_rescan;
|
||||
} scc_pproc_expand_t;
|
||||
|
||||
@@ -22,5 +22,12 @@ scc_lexer_array_to_ring(scc_lexer_tok_vec_t *array) {
|
||||
}
|
||||
|
||||
void scc_pproc_expand_macro(scc_pproc_expand_t *expand_ctx);
|
||||
void scc_pproc_expand_by_src(scc_pproc_macro_table_t *macro_table,
|
||||
scc_lexer_tok_ring_t *input,
|
||||
scc_lexer_tok_ring_t *output,
|
||||
const scc_pproc_macro_t *macro);
|
||||
void scc_pproc_expand_by_vec(scc_pproc_macro_table_t *macro_table,
|
||||
scc_lexer_tok_vec_t *input,
|
||||
scc_lexer_tok_ring_t *output);
|
||||
|
||||
#endif /* __SCC_PPROC_EXPAND_H__ */
|
||||
|
||||
@@ -15,41 +15,66 @@
|
||||
|
||||
// 条件编译状态栈
|
||||
typedef struct {
|
||||
int active; // 当前层级是否有效(即应该输出 token)
|
||||
int skip; // 当前层级是否跳过(即不输出 token)
|
||||
// 可根据需要增加状态,如 #if 的结果、#elif 已执行等
|
||||
} scc_pproc_if_state_t;
|
||||
typedef SCC_VEC(scc_pproc_if_state_t) scc_pproc_if_stack_t;
|
||||
cbool found_true;
|
||||
cbool seen_else;
|
||||
cbool active;
|
||||
} scc_pproc_if_t;
|
||||
typedef SCC_VEC(scc_pproc_if_t) scc_pproc_if_stack_t;
|
||||
|
||||
// 文件包含栈
|
||||
typedef struct {
|
||||
scc_lexer_t *lexer; // 当前文件的 lexer
|
||||
scc_lexer_tok_ring_t *tok_ring; // 当前文件的 token 环(由 lexer 提供)
|
||||
// 可能还需要保存当前位置等
|
||||
} scc_pproc_file_state_t;
|
||||
typedef SCC_VEC(scc_pproc_file_state_t) scc_pproc_file_stack_t;
|
||||
scc_sstream_t sstream;
|
||||
scc_lexer_t lexer;
|
||||
scc_lexer_tok_ring_t *ring;
|
||||
} scc_pproc_file_t;
|
||||
typedef SCC_VEC(scc_pproc_file_t *) scc_pproc_file_stack_t;
|
||||
|
||||
typedef SCC_VEC(scc_lexer_tok_ring_t *) scc_pproc_ring_vec_t;
|
||||
typedef SCC_VEC(scc_cstring_t) scc_pproc_cstr_vec_t;
|
||||
|
||||
typedef struct scc_pproc {
|
||||
scc_lexer_tok_ring_t *org_ring;
|
||||
scc_lexer_tok_ring_t *cur_ring;
|
||||
scc_lexer_tok_ring_t expanded_ring;
|
||||
scc_strpool_t strpool;
|
||||
int at_line_start;
|
||||
int enable;
|
||||
|
||||
scc_pproc_cstr_vec_t include_paths;
|
||||
scc_pproc_macro_table_t macro_table;
|
||||
scc_pproc_if_stack_t if_stack;
|
||||
scc_pproc_file_stack_t file_stack;
|
||||
|
||||
scc_lexer_tok_ring_t ring;
|
||||
int ring_ref_count;
|
||||
|
||||
struct {
|
||||
int max_include_depth;
|
||||
} config;
|
||||
} scc_pproc_t;
|
||||
|
||||
void scc_pproc_init(scc_pproc_t *pp, scc_lexer_tok_ring_t *input);
|
||||
scc_lexer_tok_ring_t *scc_pproc_to_ring(scc_pproc_t *pp, int ring_size);
|
||||
void scc_pproc_drop(scc_pproc_t *pp);
|
||||
|
||||
static inline void scc_pproc_add_include_path(scc_pproc_t *pp,
|
||||
const scc_cstring_t *path) {
|
||||
scc_vec_push(pp->include_paths, scc_cstring_copy(path));
|
||||
}
|
||||
|
||||
static inline void scc_pproc_add_include_path_cstr(scc_pproc_t *pp,
|
||||
const char *path) {
|
||||
scc_vec_push(pp->include_paths, scc_cstring_from_cstr(path));
|
||||
}
|
||||
|
||||
void scc_pproc_handle_directive(scc_pproc_t *pp);
|
||||
void scc_pproc_expand_by_src(scc_pproc_t *pp, const scc_pproc_macro_t *macro);
|
||||
|
||||
cbool scc_pproc_parse_if_defined(scc_pproc_t *pp, scc_tok_type_t type,
|
||||
const scc_lexer_tok_t *tok);
|
||||
cbool scc_pproc_parse_if_condition(scc_pproc_t *pp, scc_tok_type_t type,
|
||||
scc_lexer_tok_ring_t *tok_ring);
|
||||
void scc_pproc_parse_include(scc_pproc_t *pp, scc_lexer_tok_t *include_tok,
|
||||
scc_lexer_tok_ring_t *tok_ring);
|
||||
|
||||
void scc_pproc_parse_macro_arguments(scc_lexer_tok_ring_t *ring,
|
||||
scc_lexer_tok_vec_t *args, int need_full);
|
||||
void scc_pproc_parse_function_macro(scc_pproc_t *pp,
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#include <pproc_expand.h>
|
||||
#include <scc_core_ring.h>
|
||||
#include <scc_lexer_utils.h>
|
||||
#include <scc_pproc.h>
|
||||
|
||||
static const struct {
|
||||
const char *name;
|
||||
scc_tok_type_t tok_type;
|
||||
@@ -59,14 +62,16 @@ void scc_pproc_parse_macro_arguments(scc_lexer_tok_ring_t *ring,
|
||||
if (tok.type == SCC_TOK_R_PAREN) {
|
||||
depth--;
|
||||
}
|
||||
if (depth > 0 || need_full) {
|
||||
ok = depth > 0 || need_full;
|
||||
if (ok) {
|
||||
scc_vec_push(*args, tok);
|
||||
} else {
|
||||
scc_lexer_tok_drop(&tok);
|
||||
}
|
||||
if (tok.type == SCC_TOK_L_PAREN) {
|
||||
depth++;
|
||||
}
|
||||
if (!ok) {
|
||||
scc_lexer_tok_drop(&tok);
|
||||
}
|
||||
} while (depth);
|
||||
}
|
||||
|
||||
@@ -146,6 +151,25 @@ void scc_pproc_parse_object_macro(scc_pproc_t *pp,
|
||||
scc_pproc_macro_table_set(&pp->macro_table, macro);
|
||||
}
|
||||
|
||||
static void scc_pproc_parse_line_and_expand(scc_pproc_t *pp,
|
||||
scc_lexer_tok_ring_t *out_ring) {
|
||||
int ok;
|
||||
scc_lexer_tok_t tok;
|
||||
scc_lexer_tok_ring_t *stream = pp->cur_ring;
|
||||
scc_lexer_tok_vec_t org_toks;
|
||||
scc_vec_init(org_toks);
|
||||
while (1) {
|
||||
scc_ring_peek(*stream, tok, ok);
|
||||
if (ok == false)
|
||||
break;
|
||||
scc_ring_next_consume(*stream, tok, ok);
|
||||
scc_vec_push(org_toks, tok);
|
||||
if (tok.type == SCC_TOK_ENDLINE)
|
||||
break;
|
||||
}
|
||||
scc_pproc_expand_by_vec(&pp->macro_table, &org_toks, out_ring);
|
||||
}
|
||||
|
||||
/*
|
||||
```txt
|
||||
6.10 Preprocessing directives
|
||||
@@ -206,10 +230,11 @@ void scc_pproc_handle_directive(scc_pproc_t *pp) {
|
||||
scc_lexer_tok_t tok = {0};
|
||||
int ok = 0;
|
||||
scc_ring_next(*pp->cur_ring, tok, ok);
|
||||
Assert(ok == true && tok.type == SCC_TOK_SHARP);
|
||||
scc_lexer_tok_drop(&tok);
|
||||
|
||||
if (!scc_lexer_next_non_blank(pp->cur_ring, &tok) ||
|
||||
tok.type != SCC_TOK_IDENT) {
|
||||
scc_get_tok_subtype(tok.type) != SCC_TOK_SUBTYPE_IDENTIFIER) {
|
||||
scc_lexer_tok_drop(&tok);
|
||||
LOG_ERROR("Invalid preprocessor directive");
|
||||
goto ERROR;
|
||||
@@ -221,8 +246,34 @@ void scc_pproc_handle_directive(scc_pproc_t *pp) {
|
||||
LOG_ERROR("Expected preprocessor keyword, got %s", tok.lexeme);
|
||||
goto ERROR;
|
||||
}
|
||||
|
||||
scc_tok_type_t type = keywords[ret].tok_type;
|
||||
|
||||
if (scc_vec_size(pp->if_stack) != 0) {
|
||||
scc_pproc_if_t *top =
|
||||
&scc_vec_at(pp->if_stack, scc_vec_size(pp->if_stack) - 1);
|
||||
pp->enable = top->active;
|
||||
}
|
||||
// 非条件指令,且当前不活动时直接跳过整行
|
||||
int is_conditional = 0;
|
||||
switch (type) {
|
||||
case SCC_PP_TOK_IFDEF:
|
||||
case SCC_PP_TOK_IFNDEF:
|
||||
case SCC_PP_TOK_ELIFDEF:
|
||||
case SCC_PP_TOK_ELIFNDEF:
|
||||
case SCC_PP_TOK_ELSE:
|
||||
case SCC_PP_TOK_ENDIF:
|
||||
case SCC_PP_TOK_IF:
|
||||
case SCC_PP_TOK_ELIF:
|
||||
is_conditional = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (!is_conditional && !pp->enable) {
|
||||
scc_lexer_skip_until_newline(pp->cur_ring);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case SCC_PP_TOK_DEFINE: {
|
||||
scc_lexer_tok_drop(&tok);
|
||||
@@ -268,24 +319,80 @@ void scc_pproc_handle_directive(scc_pproc_t *pp) {
|
||||
scc_lexer_tok_drop(&tok);
|
||||
return;
|
||||
}
|
||||
case SCC_PP_TOK_INCLUDE:
|
||||
case SCC_PP_TOK_IF:
|
||||
case SCC_PP_TOK_INCLUDE: {
|
||||
scc_lexer_tok_ring_t out_ring;
|
||||
scc_pproc_parse_line_and_expand(pp, &out_ring);
|
||||
scc_pproc_parse_include(pp, &tok, &out_ring);
|
||||
return;
|
||||
}
|
||||
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_ELIFNDEF: {
|
||||
scc_lexer_tok_drop(&tok);
|
||||
if (!scc_lexer_next_non_blank(pp->cur_ring, &tok) ||
|
||||
tok.type != SCC_TOK_IDENT) {
|
||||
scc_lexer_tok_drop(&tok);
|
||||
LOG_ERROR("expected identifier");
|
||||
} else {
|
||||
scc_pproc_parse_if_defined(pp, type, &tok);
|
||||
scc_lexer_tok_drop(&tok);
|
||||
}
|
||||
scc_lexer_skip_until_newline(pp->cur_ring);
|
||||
return;
|
||||
}
|
||||
case SCC_PP_TOK_ELSE:
|
||||
case SCC_PP_TOK_ENDIF: {
|
||||
scc_lexer_tok_drop(&tok);
|
||||
scc_pproc_parse_if_defined(pp, type, null);
|
||||
scc_lexer_skip_until_newline(pp->cur_ring);
|
||||
return;
|
||||
}
|
||||
case SCC_PP_TOK_IF:
|
||||
case SCC_PP_TOK_ELIF: {
|
||||
scc_lexer_tok_drop(&tok);
|
||||
scc_lexer_tok_ring_t out_ring;
|
||||
scc_pproc_parse_line_and_expand(pp, &out_ring);
|
||||
scc_pproc_parse_if_condition(pp, type, &out_ring);
|
||||
return;
|
||||
}
|
||||
case SCC_PP_TOK_LINE:
|
||||
case SCC_PP_TOK_EMBED:
|
||||
goto ERROR;
|
||||
case SCC_PP_TOK_ERROR:
|
||||
scc_lexer_tok_drop(&tok);
|
||||
while (1) {
|
||||
ok = scc_lexer_next_non_blank(pp->cur_ring, &tok);
|
||||
if (tok.type == SCC_TOK_ENDLINE || ok == false) {
|
||||
return;
|
||||
}
|
||||
if (scc_get_tok_subtype(tok.type) == SCC_TOK_SUBTYPE_LITERAL) {
|
||||
LOG_ERROR(scc_cstring_as_cstr(&tok.lexeme));
|
||||
}
|
||||
scc_lexer_tok_drop(&tok);
|
||||
}
|
||||
return;
|
||||
case SCC_PP_TOK_WARNING:
|
||||
scc_lexer_tok_drop(&tok);
|
||||
while (1) {
|
||||
ok = scc_lexer_next_non_blank(pp->cur_ring, &tok);
|
||||
if (tok.type == SCC_TOK_ENDLINE || ok == false) {
|
||||
return;
|
||||
}
|
||||
if (scc_get_tok_subtype(tok.type) == SCC_TOK_SUBTYPE_LITERAL) {
|
||||
LOG_WARN(scc_cstring_as_cstr(&tok.lexeme));
|
||||
}
|
||||
scc_lexer_tok_drop(&tok);
|
||||
}
|
||||
return;
|
||||
case SCC_PP_TOK_PRAGMA:
|
||||
LOG_WARN("Pragma ignored");
|
||||
scc_lexer_skip_until_newline(pp->cur_ring);
|
||||
return;
|
||||
default:
|
||||
LOG_WARN("Unhandled directive: %s", scc_cstring_as_cstr(&tok.lexeme));
|
||||
break;
|
||||
}
|
||||
ERROR:
|
||||
LOG_WARN("Unhandled directive: %s", scc_cstring_as_cstr(&tok.lexeme));
|
||||
scc_lexer_skip_until_newline(pp->cur_ring);
|
||||
}
|
||||
@@ -2,11 +2,12 @@
|
||||
#include <scc_pproc.h>
|
||||
|
||||
static scc_lexer_tok_t stringify_argument(scc_lexer_tok_vec_t *arg_tokens) {
|
||||
// WRITE BY AI
|
||||
scc_cstring_t str = scc_cstring_create();
|
||||
scc_cstring_append_ch(&str, '\"'); // 左引号
|
||||
|
||||
int need_space = 0; // 是否需要插入空格
|
||||
for (usize i = 0; i < arg_tokens->size; i++) {
|
||||
scc_vec_foreach(*arg_tokens, i) {
|
||||
scc_lexer_tok_t *tok = &scc_vec_at(*arg_tokens, i);
|
||||
if (tok->type == SCC_TOK_BLANK) {
|
||||
need_space = 1; // 标记遇到空白
|
||||
@@ -16,7 +17,6 @@ static scc_lexer_tok_t stringify_argument(scc_lexer_tok_vec_t *arg_tokens) {
|
||||
// 需要空格且当前不是第一个有效token,插入一个空格
|
||||
if (need_space && i > 0) {
|
||||
scc_cstring_append_ch(&str, ' ');
|
||||
need_space = 0;
|
||||
}
|
||||
|
||||
// 对字符串/字符常量内的 " 和 \ 进行转义
|
||||
@@ -36,6 +36,40 @@ static scc_lexer_tok_t stringify_argument(scc_lexer_tok_vec_t *arg_tokens) {
|
||||
return result;
|
||||
}
|
||||
|
||||
static scc_lexer_tok_t concatenate_tokens(const scc_lexer_tok_t *left,
|
||||
const scc_lexer_tok_t *right) {
|
||||
Assert(left != null && right != null);
|
||||
scc_cstring_t new_lex = scc_cstring_create();
|
||||
scc_cstring_append(&new_lex, &left->lexeme);
|
||||
scc_cstring_append(&new_lex, &right->lexeme);
|
||||
|
||||
scc_lexer_t lexer;
|
||||
scc_sstream_t sstream;
|
||||
// new_lex 所有权转移
|
||||
scc_sstream_init_by_buffer(&sstream, scc_cstring_as_cstr(&new_lex),
|
||||
scc_cstring_len(&new_lex), true, 8);
|
||||
scc_lexer_init(&lexer, scc_sstream_to_ring(&sstream));
|
||||
scc_lexer_tok_ring_t *ring = scc_lexer_to_ring(&lexer, 8, true);
|
||||
|
||||
scc_lexer_tok_t result;
|
||||
int ok;
|
||||
scc_ring_next_consume(*ring, result, ok);
|
||||
if (!ok) {
|
||||
scc_lexer_tok_drop(&result);
|
||||
return result;
|
||||
}
|
||||
scc_ring_next_consume(*ring, result, ok);
|
||||
if (ok) {
|
||||
scc_lexer_tok_drop(&result);
|
||||
return result;
|
||||
}
|
||||
|
||||
scc_lexer_drop_ring(ring);
|
||||
scc_lexer_drop(&lexer);
|
||||
scc_sstream_drop(&sstream);
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline void scc_copy_expand(scc_pproc_expand_t *expand_ctx,
|
||||
scc_pproc_expand_t *copyed_ctx,
|
||||
scc_lexer_tok_ring_t *ring) {
|
||||
@@ -47,56 +81,94 @@ static inline void scc_copy_expand(scc_pproc_expand_t *expand_ctx,
|
||||
scc_vec_init(copyed_ctx->output);
|
||||
}
|
||||
|
||||
void scc_pproc_expand_by_src(scc_pproc_t *pp, const scc_pproc_macro_t *macro) {
|
||||
scc_pproc_expand_t ctx;
|
||||
void scc_pproc_expand_by_src(scc_pproc_macro_table_t *macro_table,
|
||||
scc_lexer_tok_ring_t *input,
|
||||
scc_lexer_tok_ring_t *output,
|
||||
const scc_pproc_macro_t *macro) {
|
||||
scc_lexer_tok_vec_t expaned_buffer;
|
||||
scc_vec_init(expaned_buffer);
|
||||
|
||||
int ok;
|
||||
scc_lexer_tok_t tok;
|
||||
scc_ring_next_consume(*pp->cur_ring, tok, ok);
|
||||
scc_ring_next_consume(*input, tok, ok);
|
||||
if (macro->type == SCC_PP_MACRO_NONE || ok == false) {
|
||||
UNREACHABLE();
|
||||
} else if (macro->type == SCC_PP_MACRO_OBJECT) {
|
||||
scc_vec_push(expaned_buffer, tok);
|
||||
} else if (macro->type == SCC_PP_MACRO_FUNCTION) {
|
||||
scc_vec_push(expaned_buffer, tok);
|
||||
scc_pproc_parse_macro_arguments(pp->cur_ring, &expaned_buffer, true);
|
||||
scc_pproc_parse_macro_arguments(input, &expaned_buffer, true);
|
||||
}
|
||||
|
||||
scc_lexer_tok_ring_t ring = scc_lexer_array_to_ring(&expaned_buffer);
|
||||
scc_pproc_expand_by_vec(macro_table, &expaned_buffer, output);
|
||||
}
|
||||
|
||||
void scc_pproc_expand_by_vec(scc_pproc_macro_table_t *macro_table,
|
||||
scc_lexer_tok_vec_t *input,
|
||||
scc_lexer_tok_ring_t *output) {
|
||||
scc_pproc_expand_t ctx;
|
||||
scc_lexer_tok_ring_t ring = scc_lexer_array_to_ring(input);
|
||||
ctx.input = ˚
|
||||
ctx.macro_table = &pp->macro_table;
|
||||
ctx.macro_table = macro_table;
|
||||
ctx.need_rescan = false;
|
||||
scc_vec_init(ctx.output);
|
||||
scc_pproc_macro_table_t expanded_set;
|
||||
ctx.expanded_set = &expanded_set;
|
||||
|
||||
scc_pproc_marco_table_init(&ctx.expanded_set);
|
||||
scc_pproc_marco_table_init(ctx.expanded_set);
|
||||
scc_pproc_expand_macro(&ctx);
|
||||
pp->expanded_ring = scc_lexer_array_to_ring(&ctx.output);
|
||||
scc_pproc_macro_table_drop(&ctx.expanded_set);
|
||||
*output = scc_lexer_array_to_ring(&ctx.output);
|
||||
scc_pproc_macro_table_drop(ctx.expanded_set);
|
||||
}
|
||||
|
||||
static inline void
|
||||
split_arguments(scc_pproc_macro_extened_params_t *splited_params,
|
||||
scc_lexer_tok_vec_t *raw_args) {
|
||||
scc_lexer_tok_vec_t *raw_args, const scc_pproc_macro_t *macro) {
|
||||
scc_lexer_tok_vec_t arg;
|
||||
scc_vec_init(arg);
|
||||
|
||||
int named_count = (int)scc_vec_size(macro->params);
|
||||
cbool is_variadic =
|
||||
(named_count > 0 &&
|
||||
scc_vec_at(macro->params, named_count - 1).type == SCC_TOK_ELLIPSIS);
|
||||
|
||||
int depth = 0;
|
||||
|
||||
scc_vec_foreach(*raw_args, i) {
|
||||
scc_lexer_tok_t *raw_arg = &scc_vec_at(*raw_args, i);
|
||||
if (raw_arg->type == SCC_TOK_COMMA) {
|
||||
scc_lexer_tok_drop(raw_arg);
|
||||
scc_vec_push(*splited_params, arg);
|
||||
scc_vec_init(arg);
|
||||
} else {
|
||||
if (raw_arg->type == SCC_TOK_BLANK ||
|
||||
scc_get_tok_subtype(raw_arg->type) ==
|
||||
SCC_TOK_SUBTYPE_EMPTYSPACE) {
|
||||
scc_lexer_tok_drop(raw_arg);
|
||||
if (raw_arg->type == SCC_TOK_L_PAREN) {
|
||||
depth++;
|
||||
} else if (raw_arg->type == SCC_TOK_R_PAREN) {
|
||||
depth--;
|
||||
}
|
||||
if (depth != 0 || raw_arg->type != SCC_TOK_COMMA ||
|
||||
(is_variadic && scc_vec_size(*splited_params) == named_count - 1)) {
|
||||
if (scc_vec_size(arg) == 0 && raw_arg->type == SCC_TOK_BLANK) {
|
||||
scc_lexer_tok_drop(raw_arg);
|
||||
} else {
|
||||
scc_vec_push(arg, *raw_arg);
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
scc_lexer_tok_drop(raw_arg);
|
||||
if (scc_vec_size(arg) &&
|
||||
scc_vec_at(arg, scc_vec_size(arg) - 1).type == SCC_TOK_BLANK) {
|
||||
scc_lexer_tok_drop(&scc_vec_pop(arg));
|
||||
}
|
||||
scc_vec_push(*splited_params, arg);
|
||||
scc_vec_init(arg);
|
||||
}
|
||||
}
|
||||
if (scc_vec_size(arg) &&
|
||||
scc_vec_at(arg, scc_vec_size(arg) - 1).type == SCC_TOK_BLANK) {
|
||||
scc_lexer_tok_drop(&scc_vec_pop(arg));
|
||||
}
|
||||
|
||||
scc_vec_push(*splited_params, arg);
|
||||
if (is_variadic && scc_vec_size(*splited_params) == named_count - 1) {
|
||||
scc_vec_init(arg);
|
||||
scc_vec_push(*splited_params, arg);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
@@ -116,6 +188,7 @@ expand_arguments(scc_pproc_macro_extened_params_t *expanded_params,
|
||||
scc_lexer_tok_ring_t ring = scc_lexer_array_to_ring(&expanded_param);
|
||||
scc_copy_expand(expand_ctx, &ctx, &ring);
|
||||
scc_pproc_expand_macro(&ctx);
|
||||
scc_ring_free(ring);
|
||||
scc_vec_push(*expanded_params, ctx.output);
|
||||
}
|
||||
}
|
||||
@@ -133,8 +206,63 @@ expanded_params_free(scc_pproc_macro_extened_params_t *expanded_params) {
|
||||
scc_vec_free(*expanded_params);
|
||||
}
|
||||
|
||||
static void rescan(scc_pproc_expand_t *expand_ctx,
|
||||
const scc_pproc_macro_t *macro,
|
||||
scc_lexer_tok_vec_t *tok_buffer) {
|
||||
scc_pproc_macro_t *expanded_macro =
|
||||
scc_pproc_macro_new(¯o->name, macro->type);
|
||||
if (expanded_macro == null) {
|
||||
LOG_FATAL("Out of memory");
|
||||
}
|
||||
scc_pproc_macro_table_set(expand_ctx->expanded_set, expanded_macro);
|
||||
|
||||
scc_pproc_expand_t rescan_ctx;
|
||||
scc_lexer_tok_ring_t ring = scc_lexer_array_to_ring(tok_buffer);
|
||||
scc_copy_expand(expand_ctx, &rescan_ctx, &ring);
|
||||
scc_pproc_expand_macro(&rescan_ctx);
|
||||
scc_ring_free(ring);
|
||||
scc_vec_foreach(rescan_ctx.output, i) {
|
||||
scc_vec_push(expand_ctx->output, scc_vec_at(rescan_ctx.output, i));
|
||||
}
|
||||
|
||||
scc_pproc_macro_table_remove(expand_ctx->expanded_set, ¯o->name);
|
||||
}
|
||||
|
||||
static int find_params(const scc_lexer_tok_t *tok,
|
||||
const scc_pproc_macro_t *macro) {
|
||||
scc_vec_foreach(macro->params, j) {
|
||||
if (scc_cstring_cmp(&(tok->lexeme),
|
||||
&(scc_vec_at(macro->params, j).lexeme)) == 0) {
|
||||
return j;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int got_left_non_blank(int i,
|
||||
const scc_lexer_tok_vec_t *replaces) {
|
||||
int left_idx = i - 1;
|
||||
while (left_idx >= 0 &&
|
||||
scc_vec_at(*replaces, left_idx).type == SCC_TOK_BLANK) {
|
||||
left_idx--;
|
||||
}
|
||||
return left_idx;
|
||||
}
|
||||
static inline int got_right_non_blank(int i,
|
||||
const scc_lexer_tok_vec_t *replaces) {
|
||||
int right_idx = i + 1;
|
||||
while (right_idx < (int)scc_vec_size(*replaces) &&
|
||||
scc_vec_at(*replaces, right_idx).type == SCC_TOK_BLANK) {
|
||||
right_idx++;
|
||||
}
|
||||
return right_idx;
|
||||
}
|
||||
|
||||
static inline void expand_function_macro(scc_pproc_expand_t *expand_ctx,
|
||||
const scc_pproc_macro_t *macro) {
|
||||
scc_lexer_tok_vec_t tok_buffer;
|
||||
scc_vec_init(tok_buffer);
|
||||
|
||||
Assert(macro->type == SCC_PP_MACRO_FUNCTION);
|
||||
scc_lexer_tok_vec_t raw_args;
|
||||
scc_vec_init(raw_args);
|
||||
@@ -143,7 +271,7 @@ static inline void expand_function_macro(scc_pproc_expand_t *expand_ctx,
|
||||
// collect, fill and expand arg
|
||||
scc_pproc_macro_extened_params_t splited_params;
|
||||
scc_vec_init(splited_params);
|
||||
split_arguments(&splited_params, &raw_args);
|
||||
split_arguments(&splited_params, &raw_args, macro);
|
||||
|
||||
scc_pproc_macro_extened_params_t expanded_params;
|
||||
scc_vec_init(expanded_params);
|
||||
@@ -151,62 +279,183 @@ static inline void expand_function_macro(scc_pproc_expand_t *expand_ctx,
|
||||
|
||||
// replace
|
||||
scc_vec_foreach(macro->replaces, i) {
|
||||
scc_lexer_tok_t tok = scc_vec_at(macro->replaces, i);
|
||||
scc_lexer_tok_t prev_tok = {0};
|
||||
if (i >= 1) {
|
||||
prev_tok = scc_vec_at(macro->replaces, i - 1);
|
||||
}
|
||||
scc_lexer_tok_t tok =
|
||||
scc_lexer_tok_copy(&scc_vec_at(macro->replaces, i));
|
||||
if (tok.type == SCC_TOK_BLANK) {
|
||||
scc_cstring_free(&tok.lexeme);
|
||||
tok.lexeme = scc_cstring_from_cstr(" ");
|
||||
scc_vec_push(expand_ctx->output, tok);
|
||||
scc_vec_push(tok_buffer, tok);
|
||||
continue;
|
||||
}
|
||||
scc_vec_foreach(macro->params, j) {
|
||||
if (scc_cstring_cmp(&tok.lexeme,
|
||||
&(scc_vec_at(macro->params, j).lexeme)) == 0) {
|
||||
if (j >= scc_vec_size(expanded_params)) {
|
||||
LOG_ERROR("Invalid macro parameter");
|
||||
goto CONTINUE;
|
||||
}
|
||||
|
||||
if (prev_tok.type == SCC_TOK_SHARP) {
|
||||
if (tok.type == SCC_TOK_SHARP) {
|
||||
// # stringify
|
||||
scc_lexer_tok_t out =
|
||||
stringify_argument(&scc_vec_at(splited_params, j));
|
||||
scc_vec_pop(expand_ctx->output);
|
||||
scc_vec_push(expand_ctx->output, out);
|
||||
scc_lexer_tok_drop(&tok);
|
||||
int right_idx = got_right_non_blank(i, ¯o->replaces);
|
||||
if (right_idx >= (int)macro->replaces.size) {
|
||||
LOG_WARN("generate empty stringify");
|
||||
scc_cstring_free(&tok.lexeme);
|
||||
tok.lexeme = scc_cstring_from_cstr("");
|
||||
scc_vec_push(tok_buffer, tok);
|
||||
break;
|
||||
}
|
||||
int j = find_params(&scc_vec_at(macro->replaces, right_idx), macro);
|
||||
Assert(j != -1 && j < (int)scc_vec_size(splited_params));
|
||||
|
||||
tok = stringify_argument(&scc_vec_at(splited_params, j));
|
||||
scc_vec_push(tok_buffer, tok);
|
||||
i = right_idx;
|
||||
continue;
|
||||
} else if (tok.type == SCC_TOK_SHARP_SHARP) {
|
||||
// ## contact
|
||||
scc_lexer_tok_drop(&tok);
|
||||
int left_idx = got_left_non_blank(i, ¯o->replaces);
|
||||
int right_idx = got_right_non_blank(i, ¯o->replaces);
|
||||
|
||||
if (left_idx < 0 || right_idx >= (int)macro->replaces.size) {
|
||||
LOG_FATAL("Invalid ## operator");
|
||||
}
|
||||
while (i++ < right_idx) {
|
||||
scc_lexer_tok_drop(&scc_vec_pop(tok_buffer));
|
||||
}
|
||||
|
||||
scc_lexer_tok_t *left_tok = &scc_vec_at(macro->replaces, left_idx);
|
||||
scc_lexer_tok_t *right_tok =
|
||||
&scc_vec_at(macro->replaces, right_idx);
|
||||
|
||||
if (left_tok->type == SCC_TOK_COMMA &&
|
||||
scc_strcmp(scc_cstring_as_cstr(&(right_tok->lexeme)),
|
||||
"__VA_ARGS__") == 0) {
|
||||
// GNU 扩展:处理逗号删除
|
||||
int right_param_idx = find_params(right_tok, macro);
|
||||
Assert(right_param_idx != -1);
|
||||
scc_lexer_tok_vec_t right_vec =
|
||||
scc_vec_at(expanded_params, right_param_idx);
|
||||
if (scc_vec_size(right_vec) != 0) {
|
||||
// 可变参数非空:输出逗号副本,然后输出右侧参数的展开
|
||||
scc_lexer_tok_t comma_tok = scc_lexer_tok_copy(left_tok);
|
||||
scc_vec_push(tok_buffer, comma_tok);
|
||||
}
|
||||
scc_vec_foreach(right_vec, k) {
|
||||
scc_lexer_tok_t tok =
|
||||
scc_lexer_tok_copy(&scc_vec_at(right_vec, k));
|
||||
scc_vec_push(tok_buffer, tok);
|
||||
}
|
||||
i = right_idx;
|
||||
continue;
|
||||
}
|
||||
|
||||
int idx;
|
||||
idx = find_params(left_tok, macro);
|
||||
scc_lexer_tok_vec_t left_vec;
|
||||
if (idx != -1) {
|
||||
Assert(idx < (int)scc_vec_size(splited_params));
|
||||
left_vec = scc_vec_at(splited_params, idx);
|
||||
} else {
|
||||
scc_vec_init(left_vec);
|
||||
scc_vec_push(left_vec, scc_lexer_tok_copy(left_tok));
|
||||
}
|
||||
|
||||
idx = find_params(right_tok, macro);
|
||||
scc_lexer_tok_vec_t right_vec;
|
||||
if (idx != -1) {
|
||||
Assert(idx < (int)scc_vec_size(splited_params));
|
||||
right_vec = scc_vec_at(splited_params, idx);
|
||||
} else {
|
||||
scc_vec_init(right_vec);
|
||||
scc_vec_push(right_vec, scc_lexer_tok_copy(right_tok));
|
||||
}
|
||||
|
||||
scc_lexer_tok_t *left =
|
||||
scc_vec_size(left_vec)
|
||||
? &scc_vec_at(left_vec, scc_vec_size(left_vec) - 1)
|
||||
: null;
|
||||
scc_lexer_tok_t *right =
|
||||
scc_vec_size(right_vec) ? &scc_vec_at(right_vec, 0) : null;
|
||||
|
||||
scc_vec_foreach(left_vec, k) {
|
||||
if (k + 1 >= scc_vec_size(left_vec)) {
|
||||
continue;
|
||||
}
|
||||
scc_lexer_tok_t tok =
|
||||
scc_lexer_tok_copy(&scc_vec_at(left_vec, k));
|
||||
scc_vec_push(tok_buffer, tok);
|
||||
}
|
||||
scc_lexer_tok_t concate_tok = concatenate_tokens(left, right);
|
||||
if (concate_tok.type == SCC_TOK_UNKNOWN) {
|
||||
LOG_FATAL("Invalid ## token");
|
||||
}
|
||||
scc_vec_push(tok_buffer, concate_tok);
|
||||
scc_vec_foreach(right_vec, k) {
|
||||
if (k == 0) {
|
||||
continue;
|
||||
}
|
||||
scc_lexer_tok_t tok =
|
||||
scc_lexer_tok_copy(&scc_vec_at(right_vec, k));
|
||||
scc_vec_push(tok_buffer, tok);
|
||||
}
|
||||
i = right_idx;
|
||||
continue;
|
||||
} else {
|
||||
int j = find_params(&tok, macro);
|
||||
if (j != -1) {
|
||||
Assert(j < (int)scc_vec_size(expanded_params));
|
||||
scc_lexer_tok_vec_t expanded_param =
|
||||
scc_vec_at(expanded_params, j);
|
||||
scc_lexer_tok_drop(&tok);
|
||||
scc_vec_foreach(expanded_param, k) {
|
||||
tok = scc_vec_at(expanded_param, k);
|
||||
tok.lexeme = scc_cstring_copy(&tok.lexeme);
|
||||
scc_vec_push(expand_ctx->output, tok);
|
||||
tok = scc_lexer_tok_copy(&scc_vec_at(expanded_param, k));
|
||||
scc_vec_push(tok_buffer, tok);
|
||||
}
|
||||
}
|
||||
goto CONTINUE;
|
||||
}
|
||||
}
|
||||
tok.lexeme = scc_cstring_copy(&tok.lexeme);
|
||||
scc_vec_push(expand_ctx->output, tok);
|
||||
CONTINUE:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
scc_vec_push(tok_buffer, tok);
|
||||
}
|
||||
expanded_params_free(&splited_params);
|
||||
expanded_params_free(&expanded_params);
|
||||
|
||||
rescan(expand_ctx, macro, &tok_buffer);
|
||||
}
|
||||
|
||||
static inline void expand_object_macro(scc_pproc_expand_t *expand_ctx,
|
||||
const scc_pproc_macro_t *macro) {
|
||||
scc_lexer_tok_vec_t tok_buffer;
|
||||
scc_vec_init(tok_buffer);
|
||||
|
||||
scc_vec_foreach(macro->replaces, i) {
|
||||
scc_lexer_tok_t tok = scc_vec_at(macro->replaces, i);
|
||||
scc_lexer_tok_t tok =
|
||||
scc_lexer_tok_copy(&scc_vec_at(macro->replaces, i));
|
||||
if (tok.type == SCC_TOK_BLANK) {
|
||||
scc_cstring_free(&tok.lexeme);
|
||||
tok.lexeme = scc_cstring_from_cstr(" ");
|
||||
} else {
|
||||
tok.lexeme = scc_cstring_copy(&tok.lexeme);
|
||||
} else if (tok.type == SCC_TOK_SHARP_SHARP) {
|
||||
// ## contact
|
||||
int left_idx = got_left_non_blank(i, ¯o->replaces);
|
||||
int right_idx = got_right_non_blank(i, ¯o->replaces);
|
||||
|
||||
if (left_idx < 0 ||
|
||||
right_idx >= (int)scc_vec_size(macro->replaces)) {
|
||||
LOG_FATAL("Invalid ## operator");
|
||||
}
|
||||
scc_vec_push(expand_ctx->output, tok);
|
||||
scc_lexer_tok_t *left = &scc_vec_at(macro->replaces, left_idx);
|
||||
scc_lexer_tok_t *right = &scc_vec_at(macro->replaces, right_idx);
|
||||
|
||||
scc_lexer_tok_t concate_tok = concatenate_tokens(left, right);
|
||||
while (i++ < right_idx) {
|
||||
scc_lexer_tok_drop(&scc_vec_pop(tok_buffer));
|
||||
}
|
||||
if (concate_tok.type == SCC_TOK_UNKNOWN) {
|
||||
LOG_FATAL("Invalid ## token");
|
||||
}
|
||||
scc_vec_push(tok_buffer, concate_tok);
|
||||
i = right_idx;
|
||||
continue;
|
||||
}
|
||||
scc_vec_push(tok_buffer, tok);
|
||||
}
|
||||
|
||||
rescan(expand_ctx, macro, &tok_buffer);
|
||||
}
|
||||
|
||||
void scc_pproc_expand_macro(scc_pproc_expand_t *expand_ctx) {
|
||||
@@ -225,30 +474,27 @@ void scc_pproc_expand_macro(scc_pproc_expand_t *expand_ctx) {
|
||||
scc_pproc_macro_t *macro =
|
||||
scc_pproc_macro_table_get(expand_ctx->macro_table, &tok.lexeme);
|
||||
|
||||
if (macro == null || scc_pproc_macro_table_get(
|
||||
&expand_ctx->expanded_set, ¯o->name)) {
|
||||
if (macro == null ||
|
||||
scc_pproc_macro_table_get(expand_ctx->expanded_set, ¯o->name)) {
|
||||
scc_vec_push(expand_ctx->output, tok);
|
||||
continue;
|
||||
}
|
||||
expand_ctx->need_rescan = true;
|
||||
|
||||
scc_pproc_macro_t *expanded_macro =
|
||||
scc_pproc_macro_new(¯o->name, macro->type);
|
||||
if (expanded_macro == null) {
|
||||
LOG_FATAL("Out of memory");
|
||||
}
|
||||
scc_pproc_macro_table_set(&expand_ctx->expanded_set, expanded_macro);
|
||||
if (macro->type == SCC_PP_MACRO_OBJECT) {
|
||||
expand_object_macro(expand_ctx, macro);
|
||||
} else if (macro->type == SCC_PP_MACRO_FUNCTION) {
|
||||
scc_lexer_tok_t expect_tok;
|
||||
scc_ring_peek(*expand_ctx->input, expect_tok, ok);
|
||||
if (ok == false || expect_tok.type != SCC_TOK_L_PAREN) {
|
||||
scc_vec_push(expand_ctx->output, tok);
|
||||
continue;
|
||||
}
|
||||
|
||||
expand_function_macro(expand_ctx, macro);
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
RESCAN:
|
||||
scc_pproc_macro_table_remove(&expand_ctx->expanded_set, ¯o->name);
|
||||
// TODO expand # and ##
|
||||
continue;
|
||||
}
|
||||
if (expand_ctx->need_rescan) {
|
||||
expand_ctx->need_rescan = false;
|
||||
|
||||
173
libs/pproc/src/pproc_if.c
Normal file
173
libs/pproc/src/pproc_if.c
Normal file
@@ -0,0 +1,173 @@
|
||||
#include <scc_lexer_utils.h>
|
||||
#include <scc_pproc.h>
|
||||
|
||||
cbool scc_pproc_parse_if_defined(scc_pproc_t *pp, scc_tok_type_t type,
|
||||
const scc_lexer_tok_t *tok) {
|
||||
int defined = 0;
|
||||
if (tok) {
|
||||
defined = (scc_pproc_macro_table_get(&pp->macro_table,
|
||||
&(tok->lexeme)) != null);
|
||||
}
|
||||
switch (type) {
|
||||
case SCC_PP_TOK_IFDEF:
|
||||
case SCC_PP_TOK_IFNDEF: {
|
||||
cbool condition = (type == SCC_PP_TOK_IFDEF) ? defined : !defined;
|
||||
|
||||
scc_pproc_if_t new_if;
|
||||
new_if.found_true = condition;
|
||||
new_if.seen_else = 0;
|
||||
new_if.active = pp->enable ? condition : 0;
|
||||
scc_vec_push(pp->if_stack, new_if);
|
||||
|
||||
pp->enable = new_if.active;
|
||||
break;
|
||||
}
|
||||
case SCC_PP_TOK_ELIFDEF:
|
||||
case SCC_PP_TOK_ELIFNDEF: {
|
||||
if (scc_vec_size(pp->if_stack) == 0) {
|
||||
LOG_ERROR("#elif without #if");
|
||||
return false;
|
||||
}
|
||||
scc_pproc_if_t *top =
|
||||
&scc_vec_at(pp->if_stack, scc_vec_size(pp->if_stack) - 1);
|
||||
if (top->seen_else) {
|
||||
LOG_ERROR("#elif after #else");
|
||||
return false;
|
||||
}
|
||||
|
||||
int condition = (type == SCC_PP_TOK_ELIFDEF) ? defined : !defined;
|
||||
|
||||
if (top->found_true) {
|
||||
// 前面已有真分支,本 elif 不激活
|
||||
top->active = 0;
|
||||
} else {
|
||||
if (condition) {
|
||||
top->active = 1;
|
||||
top->found_true = 1;
|
||||
} else {
|
||||
top->active = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// seen_else 仍为 0
|
||||
pp->enable = top->active;
|
||||
break;
|
||||
}
|
||||
case SCC_PP_TOK_ELSE: {
|
||||
if (scc_vec_size(pp->if_stack) == 0) {
|
||||
LOG_ERROR("#else without #if");
|
||||
return false;
|
||||
}
|
||||
scc_pproc_if_t *top =
|
||||
&scc_vec_at(pp->if_stack, scc_vec_size(pp->if_stack) - 1);
|
||||
if (top->seen_else) {
|
||||
LOG_ERROR("multiple #else");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (top->found_true) {
|
||||
top->active = 0;
|
||||
} else {
|
||||
top->active = 1;
|
||||
top->found_true = 1;
|
||||
}
|
||||
|
||||
top->seen_else = 1;
|
||||
|
||||
pp->enable = top->active;
|
||||
break;
|
||||
}
|
||||
case SCC_PP_TOK_ENDIF: {
|
||||
if (scc_vec_size(pp->if_stack) == 0) {
|
||||
LOG_ERROR("#endif without #if");
|
||||
} else {
|
||||
scc_vec_pop(pp->if_stack);
|
||||
}
|
||||
if (scc_vec_size(pp->if_stack) == 0) {
|
||||
pp->enable = 1;
|
||||
} else {
|
||||
pp->enable =
|
||||
scc_vec_at(pp->if_stack, scc_vec_size(pp->if_stack) - 1).active;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
LOG_FATAL("unexpected directive");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static cbool parse_constant_condition() { return false; }
|
||||
|
||||
cbool scc_pproc_parse_if_condition(scc_pproc_t *pp, scc_tok_type_t type,
|
||||
scc_lexer_tok_ring_t *tok_ring) {
|
||||
// TODO
|
||||
int ok;
|
||||
scc_lexer_tok_t tok = {0};
|
||||
ok = scc_lexer_next_non_blank(tok_ring, &tok);
|
||||
if (ok == false) {
|
||||
LOG_FATAL("unexpected EOF");
|
||||
}
|
||||
int condition = 0;
|
||||
if (tok.type == SCC_TOK_INT_LITERAL) {
|
||||
condition = scc_cstring_as_cstr(&tok.lexeme)[0] == '0' ? 0 : 1;
|
||||
} else {
|
||||
LOG_ERROR("expected integer constant but got %s",
|
||||
scc_cstring_as_cstr(&tok.lexeme));
|
||||
}
|
||||
scc_lexer_tok_drop(&tok);
|
||||
|
||||
ok = scc_lexer_next_non_blank(tok_ring, &tok);
|
||||
if (ok == false) {
|
||||
LOG_FATAL("unexpected EOF");
|
||||
}
|
||||
if (tok.type != SCC_TOK_ENDLINE) {
|
||||
LOG_ERROR("expected endline");
|
||||
scc_lexer_skip_until_newline(tok_ring);
|
||||
} else {
|
||||
scc_lexer_tok_drop(&tok);
|
||||
}
|
||||
scc_ring_free(*tok_ring);
|
||||
|
||||
// 根据指令类型更新条件编译栈
|
||||
switch (type) {
|
||||
case SCC_PP_TOK_IF: {
|
||||
scc_pproc_if_t new_if;
|
||||
new_if.found_true = condition;
|
||||
new_if.seen_else = 0;
|
||||
new_if.active = pp->enable ? condition : 0;
|
||||
scc_vec_push(pp->if_stack, new_if);
|
||||
pp->enable = new_if.active;
|
||||
break;
|
||||
}
|
||||
case SCC_PP_TOK_ELIF: {
|
||||
if (scc_vec_size(pp->if_stack) == 0) {
|
||||
LOG_ERROR("#elif without #if");
|
||||
return false;
|
||||
}
|
||||
scc_pproc_if_t *top =
|
||||
&scc_vec_at(pp->if_stack, scc_vec_size(pp->if_stack) - 1);
|
||||
if (top->seen_else) {
|
||||
LOG_ERROR("#elif after #else");
|
||||
return false;
|
||||
}
|
||||
if (top->found_true) {
|
||||
top->active = 0;
|
||||
} else {
|
||||
if (condition) {
|
||||
top->active = 1;
|
||||
top->found_true = 1;
|
||||
} else {
|
||||
top->active = 0;
|
||||
}
|
||||
}
|
||||
pp->enable = top->active;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG_FATAL("unexpected directive in parse_if_condition");
|
||||
}
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
#include <pproc_expand.h>
|
||||
#include <scc_core_ring.h>
|
||||
#include <scc_pproc.h>
|
||||
|
||||
static int switch_file_stack(scc_pproc_t *pp, scc_cstring_t *fname,
|
||||
scc_pos_t *pos, int is_system) {
|
||||
scc_cstring_t fpath = scc_cstring_create();
|
||||
int ret = 0;
|
||||
|
||||
const char *org_fname = pos->name;
|
||||
|
||||
if (!is_system) {
|
||||
const char parent[] = "/../";
|
||||
// FIXME maybe it can eazy
|
||||
scc_cstring_append_cstr(&fpath, org_fname, scc_strlen(org_fname));
|
||||
scc_cstring_append_cstr(&fpath, parent, scc_strlen(parent));
|
||||
scc_cstring_append(&fpath, fname);
|
||||
ret = scc_fexists(scc_cstring_as_cstr(&fpath));
|
||||
if (ret == true) {
|
||||
goto FOPEN;
|
||||
}
|
||||
}
|
||||
|
||||
/* system default path and -I includes path */
|
||||
scc_vec_foreach(pp->include_paths, i) {
|
||||
scc_cstring_free(&fpath);
|
||||
scc_cstring_t *syspath = &scc_vec_at(pp->include_paths, i);
|
||||
scc_cstring_append(&fpath, syspath);
|
||||
scc_cstring_append_ch(&fpath, '/');
|
||||
scc_cstring_append(&fpath, fname);
|
||||
ret = scc_fexists(scc_cstring_as_cstr(&fpath));
|
||||
if (ret == true) {
|
||||
goto FOPEN;
|
||||
}
|
||||
}
|
||||
LOG_ERROR("In %s:%d:%d include %c%s%c, the file is not found", org_fname,
|
||||
pos->line, pos->col, is_system ? '<' : '\"',
|
||||
scc_cstring_as_cstr(fname), is_system ? '>' : '\"');
|
||||
return -1;
|
||||
FOPEN:
|
||||
if (scc_vec_size(pp->file_stack) >= pp->config.max_include_depth) {
|
||||
LOG_FATAL("Include depth is too deep, the include depth is %d, set "
|
||||
"MAX_INCLUDE_DEPTH is %d",
|
||||
scc_vec_size(pp->file_stack), pp->config.max_include_depth);
|
||||
}
|
||||
|
||||
scc_pproc_file_t *file = scc_malloc(sizeof(scc_pproc_file_t));
|
||||
Assert(file != null);
|
||||
if (scc_sstream_init(&(file->sstream), scc_cstring_as_cstr(&fpath), 1024)) {
|
||||
return -1;
|
||||
}
|
||||
scc_lexer_init(&(file->lexer), scc_sstream_to_ring(&(file->sstream)));
|
||||
file->ring = scc_lexer_to_ring(&(file->lexer), 8, true);
|
||||
pp->cur_ring = file->ring;
|
||||
|
||||
scc_vec_push(pp->file_stack, file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void scc_pproc_parse_include(scc_pproc_t *pp, scc_lexer_tok_t *include_tok,
|
||||
scc_lexer_tok_ring_t *tok_ring) {
|
||||
int ok;
|
||||
scc_lexer_tok_t tok;
|
||||
scc_pos_t pos = include_tok->loc;
|
||||
scc_lexer_tok_drop(include_tok);
|
||||
|
||||
scc_cstring_t line = scc_cstring_create();
|
||||
while (1) {
|
||||
scc_ring_next_consume(*tok_ring, tok, ok);
|
||||
if (!ok)
|
||||
break;
|
||||
if (scc_get_tok_subtype(tok.type) != SCC_TOK_SUBTYPE_EMPTYSPACE &&
|
||||
scc_get_tok_subtype(tok.type) != SCC_TOK_SUBTYPE_COMMENT) {
|
||||
scc_cstring_append(&line, &tok.lexeme);
|
||||
}
|
||||
scc_lexer_tok_drop(&tok);
|
||||
}
|
||||
scc_ring_free(*tok_ring);
|
||||
|
||||
const char *includename = scc_cstring_as_cstr(&line);
|
||||
int len = scc_cstring_len(&line);
|
||||
if (len < 2) {
|
||||
goto ERROR;
|
||||
} else if (len == 2) {
|
||||
goto ERROR;
|
||||
} else {
|
||||
if (includename[0] == '\"') {
|
||||
if (includename[len - 1] != '\"') {
|
||||
goto ERROR;
|
||||
}
|
||||
} else if (includename[0] == '<') {
|
||||
if (includename[len - 1] != '>') {
|
||||
goto ERROR;
|
||||
}
|
||||
} else {
|
||||
goto ERROR;
|
||||
}
|
||||
}
|
||||
scc_cstring_t fname = scc_cstring_create();
|
||||
for (int i = 1; i < len - 1; i++) {
|
||||
scc_cstring_append_ch(&fname, includename[i]);
|
||||
}
|
||||
scc_cstring_free(&line);
|
||||
int is_system = includename[0] == '<';
|
||||
if (switch_file_stack(pp, &fname, &pos, is_system)) {
|
||||
// LOG_ERROR()
|
||||
}
|
||||
scc_cstring_free(&fname);
|
||||
return;
|
||||
ERROR:
|
||||
LOG_ERROR("Invalid include filename need \"FILENAME\" or <FILENAME>");
|
||||
scc_cstring_free(&line);
|
||||
}
|
||||
|
||||
@@ -22,8 +22,6 @@ void scc_pproc_macro_drop(scc_pproc_macro_t *macro) {
|
||||
if (!macro)
|
||||
return;
|
||||
|
||||
scc_cstring_free(¯o->name);
|
||||
|
||||
// 释放参数列表
|
||||
for (usize i = 0; i < macro->params.size; ++i) {
|
||||
scc_lexer_tok_drop(&scc_vec_at(macro->params, i));
|
||||
@@ -36,6 +34,8 @@ void scc_pproc_macro_drop(scc_pproc_macro_t *macro) {
|
||||
}
|
||||
scc_vec_free(macro->replaces);
|
||||
|
||||
scc_cstring_free(¯o->name);
|
||||
|
||||
scc_free(macro);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
#include <pproc_expand.h>
|
||||
#include <scc_lexer_utils.h>
|
||||
#include <scc_pproc.h>
|
||||
|
||||
static int pproc_next(scc_pproc_t *pp, scc_lexer_tok_t *out) {
|
||||
static int pproc_next_one_file(scc_pproc_t *pp, scc_lexer_tok_t *out) {
|
||||
CONTINUE:
|
||||
scc_lexer_tok_ring_t *stream = pp->cur_ring;
|
||||
scc_lexer_tok_t tok = {0};
|
||||
int ok = 0;
|
||||
CONTINUE:
|
||||
if (pp->expanded_ring.cap) {
|
||||
scc_ring_next_consume(pp->expanded_ring, *out, ok);
|
||||
if (ok == false) {
|
||||
@@ -15,25 +17,40 @@ CONTINUE:
|
||||
}
|
||||
}
|
||||
scc_ring_peek(*stream, tok, ok);
|
||||
if (tok.type == SCC_TOK_BLANK ||
|
||||
scc_get_tok_subtype(tok.type) == SCC_TOK_SUBTYPE_COMMENT) {
|
||||
scc_cstring_free(&tok.lexeme);
|
||||
scc_ring_next_consume(*stream, *out, ok);
|
||||
out->lexeme = scc_cstring_from_cstr(" ");
|
||||
return true;
|
||||
} else if (tok.type == SCC_TOK_ENDLINE) {
|
||||
if (ok == false) {
|
||||
return false;
|
||||
}
|
||||
if (tok.type == SCC_TOK_ENDLINE) {
|
||||
scc_ring_next_consume(*stream, *out, ok);
|
||||
pp->at_line_start = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (pp->at_line_start && tok.type == SCC_TOK_SHARP) {
|
||||
if (pp->at_line_start) {
|
||||
if (tok.type == SCC_TOK_SHARP) {
|
||||
// parse to #
|
||||
scc_pproc_handle_directive(pp);
|
||||
pp->at_line_start = true;
|
||||
goto CONTINUE;
|
||||
} else if (tok.type == SCC_TOK_BLANK) {
|
||||
scc_ring_next(*stream, *out, ok);
|
||||
scc_ring_peek(*stream, tok, ok);
|
||||
if (ok && tok.type == SCC_TOK_SHARP) {
|
||||
scc_pproc_handle_directive(pp);
|
||||
pp->at_line_start = true;
|
||||
goto CONTINUE;
|
||||
}
|
||||
scc_ring_back(*stream, ok);
|
||||
Assert(ok == true);
|
||||
scc_ring_peek(*stream, tok, ok);
|
||||
}
|
||||
}
|
||||
if (pp->enable == false) {
|
||||
scc_lexer_skip_until_newline(stream);
|
||||
pp->at_line_start = true;
|
||||
goto CONTINUE;
|
||||
}
|
||||
|
||||
pp->at_line_start = false;
|
||||
if (tok.type == SCC_TOK_IDENT) {
|
||||
// maybe expanded
|
||||
scc_pproc_macro_t *macro =
|
||||
@@ -42,7 +59,8 @@ CONTINUE:
|
||||
scc_ring_next_consume(*stream, *out, ok);
|
||||
return ok;
|
||||
}
|
||||
scc_pproc_expand_by_src(pp, macro);
|
||||
scc_pproc_expand_by_src(&pp->macro_table, pp->cur_ring,
|
||||
&pp->expanded_ring, macro);
|
||||
goto CONTINUE;
|
||||
} else {
|
||||
// continue
|
||||
@@ -52,14 +70,50 @@ CONTINUE:
|
||||
return false;
|
||||
}
|
||||
|
||||
static int pproc_next(scc_pproc_t *pp, scc_lexer_tok_t *tok) {
|
||||
CONTINUE:
|
||||
int ret = pproc_next_one_file(pp, tok);
|
||||
if (ret != 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (scc_vec_size(pp->file_stack) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
scc_pproc_file_t *file = scc_vec_pop(pp->file_stack);
|
||||
Assert(file->ring == pp->cur_ring);
|
||||
scc_lexer_drop_ring(file->ring);
|
||||
scc_lexer_drop(&(file->lexer));
|
||||
scc_sstream_drop(&(file->sstream));
|
||||
scc_free(file);
|
||||
|
||||
if (scc_vec_size(pp->file_stack) == 0) {
|
||||
pp->cur_ring = pp->org_ring;
|
||||
} else {
|
||||
pp->cur_ring =
|
||||
scc_vec_at(pp->file_stack, scc_vec_size(pp->file_stack) - 1)->ring;
|
||||
}
|
||||
goto CONTINUE;
|
||||
}
|
||||
|
||||
void scc_pproc_init(scc_pproc_t *pp, scc_lexer_tok_ring_t *input) {
|
||||
Assert(pp != null && input != null);
|
||||
pp->cur_ring = input;
|
||||
pp->org_ring = input;
|
||||
pp->cur_ring = pp->org_ring;
|
||||
scc_ring_init(pp->expanded_ring, 0, 0, 0);
|
||||
scc_pproc_marco_table_init(&pp->macro_table);
|
||||
scc_vec_init(pp->include_paths);
|
||||
scc_vec_init(pp->if_stack);
|
||||
scc_vec_init(pp->file_stack);
|
||||
pp->at_line_start = true;
|
||||
pp->enable = true;
|
||||
|
||||
pp->config.max_include_depth = 32;
|
||||
}
|
||||
|
||||
void scc_pproc_add_builtin_macros() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
static cbool fill_token(scc_lexer_tok_t *tok, void *userdata) {
|
||||
|
||||
@@ -17,7 +17,7 @@ static cbool process_input(const char *input, scc_cstring_t *output) {
|
||||
scc_pproc_init(&pp, scc_lexer_to_ring(&lexer, 8, true));
|
||||
|
||||
scc_lexer_tok_ring_t *tok_ring = scc_pproc_to_ring(&pp, 8);
|
||||
*output = scc_cstring_create();
|
||||
*output = scc_cstring_from_cstr("");
|
||||
scc_lexer_tok_t tok;
|
||||
while (1) {
|
||||
scc_ring_next_consume(*tok_ring, tok, ret);
|
||||
@@ -31,7 +31,6 @@ static cbool process_input(const char *input, scc_cstring_t *output) {
|
||||
scc_pproc_drop(&pp);
|
||||
scc_lexer_drop(&lexer);
|
||||
scc_sstream_drop(&mem_stream);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -102,12 +101,19 @@ static void test_define_stringify_operator(void) {
|
||||
"\"test value\"\n");
|
||||
CHECK_PP_OUTPUT_EXACT("#define STR(x) #x\nSTR(A B \"ab\")\n",
|
||||
"\"A B \"ab\"\"\n");
|
||||
CHECK_PP_OUTPUT_EXACT("#define STR(x) # x\nSTR(A B \"ab\")\n",
|
||||
"\"A B \"ab\"\"\n");
|
||||
}
|
||||
|
||||
static void test_define_concat_operator(void) {
|
||||
TEST_CASE("concatenation operator (##)");
|
||||
CHECK_PP_OUTPUT_EXACT("#define CONCAT a##b\nCONCAT\n", "ab\n");
|
||||
CHECK_PP_OUTPUT_EXACT("#define CONCAT a ## b\nCONCAT\n", "ab\n");
|
||||
CHECK_PP_OUTPUT_EXACT("#define CONCAT(a,b) a##b\nCONCAT(hello,world)\n",
|
||||
"helloworld\n");
|
||||
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");
|
||||
}
|
||||
@@ -120,6 +126,41 @@ static void test_define_nested_macros(void) {
|
||||
CHECK_PP_OUTPUT_EXACT(
|
||||
"#define A 1\n#define B (A + 1)\n#define C (B + 1)\nC\n",
|
||||
"((1 + 1) + 1)\n");
|
||||
|
||||
CHECK_PP_OUTPUT_EXACT("#define A\n", "");
|
||||
CHECK_PP_OUTPUT_EXACT("#undef A\n", "");
|
||||
|
||||
CHECK_PP_OUTPUT_EXACT(" # define A 1\nA", "1");
|
||||
// CHECK_PP_OUTPUT_EXACT(" # define A 1 \nA", "1"); // TODO
|
||||
|
||||
CHECK_PP_OUTPUT_EXACT("#define CONCAT(str) __scc_##str\nCONCAT(int)",
|
||||
"__scc_int");
|
||||
CHECK_PP_OUTPUT_EXACT("#define CONCAT(str) str##_scc__\nCONCAT(int)",
|
||||
"int_scc__");
|
||||
|
||||
CHECK_PP_OUTPUT_EXACT("#define CONCAT(str) __scc_ ## str\nCONCAT(int)",
|
||||
"__scc_int");
|
||||
|
||||
// TEST_CASE("TODO"); /*FALSE*/
|
||||
// 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_undef_macros(void) {
|
||||
@@ -169,33 +210,19 @@ static void hard_test_define_func_macros(void) {
|
||||
"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("mulit braces");
|
||||
CHECK_PP_OUTPUT_EXACT("#define MACRO(a, b, c) a, b, c\n"
|
||||
"MACRO(1, (2,3), 4)\n",
|
||||
"1, (2,3), 4\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");
|
||||
TEST_CASE("max_macro hard");
|
||||
CHECK_PP_OUTPUT_EXACT("#define max(a, b) ((a) > (b) ? (a) : (b))\n"
|
||||
"max(1, 2)\n",
|
||||
"((1) > (2) ? (1) : (2))\n");
|
||||
CHECK_PP_OUTPUT_EXACT("#define max(a, b) ((a) > (b) ? (a) : (b))\n"
|
||||
"max(max(x, y), z)\n",
|
||||
"((((x) > (y) ? (x) : (y))) > (z) ? (((x) > (y) ? "
|
||||
"(x) : (y))) : (z))\n");
|
||||
}
|
||||
|
||||
static void test_error_cases(void) {
|
||||
@@ -218,6 +245,289 @@ static void test_edge_cases(void) {
|
||||
CHECK_PP_OUTPUT_EXACT("#define A B\n#define B C\n#define C 1\nA\n", "1\n");
|
||||
}
|
||||
|
||||
static void test_conditional_ifdef(void) {
|
||||
TEST_CASE("ifdef and ifndef");
|
||||
|
||||
// 基本 ifdef / ifndef
|
||||
CHECK_PP_OUTPUT_EXACT("#define FOO\n"
|
||||
"#ifdef FOO\n"
|
||||
"foo\n"
|
||||
"#endif\n",
|
||||
"foo\n");
|
||||
|
||||
CHECK_PP_OUTPUT_EXACT("#define FOO\n"
|
||||
"#ifndef FOO\n"
|
||||
"foo\n"
|
||||
"#endif\n",
|
||||
"");
|
||||
|
||||
CHECK_PP_OUTPUT_EXACT("#undef FOO\n"
|
||||
"#ifdef FOO\n"
|
||||
"foo\n"
|
||||
"#endif\n",
|
||||
"");
|
||||
|
||||
CHECK_PP_OUTPUT_EXACT("#undef FOO\n"
|
||||
"#ifndef FOO\n"
|
||||
"foo\n"
|
||||
"#endif\n",
|
||||
"foo\n");
|
||||
|
||||
// ifdef + else
|
||||
CHECK_PP_OUTPUT_EXACT("#define FOO\n"
|
||||
"#ifdef FOO\n"
|
||||
"foo\n"
|
||||
"#else\n"
|
||||
"bar\n"
|
||||
"#endif\n",
|
||||
"foo\n");
|
||||
|
||||
CHECK_PP_OUTPUT_EXACT("#undef FOO\n"
|
||||
"#ifdef FOO\n"
|
||||
"foo\n"
|
||||
"#else\n"
|
||||
"bar\n"
|
||||
"#endif\n",
|
||||
"bar\n");
|
||||
|
||||
// ifdef + elifdef (C23)
|
||||
CHECK_PP_OUTPUT_EXACT("#define FOO\n"
|
||||
"#ifdef FOO\n"
|
||||
"foo\n"
|
||||
"#elifdef FOO\n"
|
||||
"foo2\n"
|
||||
"#endif\n",
|
||||
"foo\n");
|
||||
|
||||
CHECK_PP_OUTPUT_EXACT("#undef FOO\n"
|
||||
"#define BAR\n"
|
||||
"#ifdef FOO\n"
|
||||
"foo\n"
|
||||
"#elifdef BAR\n"
|
||||
"bar\n"
|
||||
"#else\n"
|
||||
"none\n"
|
||||
"#endif\n",
|
||||
"bar\n");
|
||||
|
||||
CHECK_PP_OUTPUT_EXACT("#undef FOO\n"
|
||||
"#undef BAR\n"
|
||||
"#ifdef FOO\n"
|
||||
"foo\n"
|
||||
"#elifdef BAR\n"
|
||||
"bar\n"
|
||||
"#else\n"
|
||||
"none\n"
|
||||
"#endif\n",
|
||||
"none\n");
|
||||
|
||||
// 嵌套
|
||||
CHECK_PP_OUTPUT_EXACT("#define A\n"
|
||||
"#ifdef A\n"
|
||||
" #ifdef B\n"
|
||||
" inner\n"
|
||||
" #endif\n"
|
||||
" outer\n"
|
||||
"#endif\n",
|
||||
" outer\n");
|
||||
|
||||
CHECK_PP_OUTPUT_EXACT("#define B\n"
|
||||
"#ifndef A\n"
|
||||
" #ifdef B\n"
|
||||
" inner\n"
|
||||
" #endif\n"
|
||||
" outer\n"
|
||||
"#endif\n",
|
||||
" inner\n outer\n");
|
||||
|
||||
// 外层假,内层真
|
||||
CHECK_PP_OUTPUT_EXACT("#ifdef __NONE\n"
|
||||
"#define OUTER\n"
|
||||
"#endif\n"
|
||||
"#ifdef OUTER\n"
|
||||
"should not appear\n"
|
||||
"#endif\n",
|
||||
""); // 期望为空
|
||||
|
||||
// 更复杂的嵌套条件
|
||||
CHECK_PP_OUTPUT_EXACT("#define X\n"
|
||||
"#ifdef X\n"
|
||||
"x defined\n"
|
||||
"#ifdef Y\n"
|
||||
"Y defined\n"
|
||||
"#else\n"
|
||||
"Y not defined\n"
|
||||
"#endif\n"
|
||||
"after inner\n"
|
||||
"#else\n"
|
||||
"X not defined\n"
|
||||
"#endif\n",
|
||||
"x defined\nY not defined\nafter inner\n");
|
||||
}
|
||||
|
||||
static void test_simple_number_conditional_if(void) {
|
||||
TEST_CASE("if and elif with one integer constants");
|
||||
|
||||
// 基本 if
|
||||
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("#if 4\ntrue\n#endif\n", "true\n");
|
||||
CHECK_PP_OUTPUT_EXACT("#if 0\nfalse\n#else\nother\n#endif\n", "other\n");
|
||||
|
||||
// if + elif + else
|
||||
CHECK_PP_OUTPUT_EXACT("#if 0\nzero\n#elif 1\none\n#else\nother\n#endif\n",
|
||||
"one\n");
|
||||
CHECK_PP_OUTPUT_EXACT("#if 0\nzero\n#elif 0\none\n#else\nother\n#endif\n",
|
||||
"other\n");
|
||||
CHECK_PP_OUTPUT_EXACT("#if 1\nfirst\n#elif 1\nsecond\n#endif\n", "first\n");
|
||||
|
||||
// 嵌套
|
||||
CHECK_PP_OUTPUT_EXACT(
|
||||
"#if 1\n #if 0\n inner\n #endif\n outer\n#endif\n", " outer\n");
|
||||
CHECK_PP_OUTPUT_EXACT("#if 0\n #if 1\n inner\n #endif\n "
|
||||
"outer\n#else\n alternative\n#endif\n",
|
||||
" alternative\n");
|
||||
|
||||
// 与 #ifdef 混合
|
||||
CHECK_PP_OUTPUT_EXACT("#define FOO\n"
|
||||
"#if 1\n"
|
||||
" #ifdef FOO\n"
|
||||
" foo\n"
|
||||
" #endif\n"
|
||||
" bar\n"
|
||||
"#endif\n",
|
||||
" foo\n bar\n");
|
||||
}
|
||||
|
||||
static void test_variadic_macros(void) {
|
||||
TEST_CASE("variadic macros with __VA_ARGS__");
|
||||
|
||||
// 基本可变参数宏
|
||||
CHECK_PP_OUTPUT_EXACT("#define FOO(x, ...) x __VA_ARGS__\n"
|
||||
"FOO(1, 2, 3)\n",
|
||||
"1 2, 3\n");
|
||||
|
||||
// 多参数
|
||||
CHECK_PP_OUTPUT_EXACT("#define SUM(...) (__VA_ARGS__)\n"
|
||||
"SUM(1, 2, 3)\n",
|
||||
"(1, 2, 3)\n");
|
||||
|
||||
// 与 printf 结合
|
||||
CHECK_PP_OUTPUT_EXACT("#define DEBUG(fmt, ...) printf(fmt, __VA_ARGS__)\n"
|
||||
"DEBUG(\"hello\", 1, 2)\n",
|
||||
"printf(\"hello\", 1, 2)\n");
|
||||
|
||||
// 空可变参数
|
||||
CHECK_PP_OUTPUT_EXACT("#define FOO(x, ...) x __VA_ARGS__\n"
|
||||
"FOO(1)\n",
|
||||
"1 \n");
|
||||
|
||||
CHECK_PP_OUTPUT_EXACT("#define FOO(x, ...) #__VA_ARGS__\nFOO(1);", "\"\";");
|
||||
}
|
||||
|
||||
static void test_gnu_comma_variadic_deletion(void) {
|
||||
TEST_CASE("GNU comma deletion with ## and __VA_ARGS__");
|
||||
// 可变参数为空,逗号被删除
|
||||
CHECK_PP_OUTPUT_EXACT("#define FOO(fmt, ...) printf(fmt, ## __VA_ARGS__)\n"
|
||||
"FOO(\"hello\")\n",
|
||||
"printf(\"hello\")\n");
|
||||
// 可变参数非空,逗号保留
|
||||
CHECK_PP_OUTPUT_EXACT("#define FOO(fmt, ...) printf(fmt, ## __VA_ARGS__)\n"
|
||||
"FOO(\"%d\", 42)\n",
|
||||
"printf(\"%d\",42)\n");
|
||||
// 带空白变体
|
||||
CHECK_PP_OUTPUT_EXACT("#define FOO(fmt,...) printf(fmt,##__VA_ARGS__)\n"
|
||||
"FOO(\"%d\", 42)\n",
|
||||
"printf(\"%d\",42)\n");
|
||||
}
|
||||
|
||||
static void test_c99_docs(void) {
|
||||
TEST_CASE("6.10.3.3 The ## operator EXAMPLE");
|
||||
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");
|
||||
|
||||
// 6.10.3.5 Scope of macrodefinitions
|
||||
TEST_CASE("EXAMPLE 3 To illustrate the rules for redefinition and "
|
||||
"reexamination, the sequence");
|
||||
/*
|
||||
CHECK_PP_OUTPUT_EXACT(
|
||||
"#define x 3\n"
|
||||
"#define f(a) f(x * (a))\n"
|
||||
"#undef x\n"
|
||||
"#define x 2\n"
|
||||
"#define g f\n"
|
||||
"#define z z[0]\n"
|
||||
"#define h g(~\n"
|
||||
"#define m(a) a(w)\n"
|
||||
"#define w 0,1\n"
|
||||
"#define t(a) a\n"
|
||||
"#define p() int\n"
|
||||
"#define q(x) x\n"
|
||||
"#define r(x,y) x ## y\n"
|
||||
"#define str(x) # x\n"
|
||||
"f(y+1) + f(f(z)) % t(t(g)(0) + t)(1);\n"
|
||||
"g(x+(3,4)-w) | h 5) & m\n"
|
||||
" (f)^m(m);\n"
|
||||
"p() i[q()] = { q(1), r(2,3), r(4,), r(,5), r(,) };\n"
|
||||
"char c[2][6] = { str(hello), str() };\n",
|
||||
"f(2 * (y+1)) + f(2 * (f(2 * (z[0])))) % f(2 * (0)) + t(1);\n"
|
||||
"f(2 * (2+(3,4)-0,1)) | f(2 * (~ 5)) & f(2 * (0,1))^m(0,1);\n"
|
||||
"int i[] = { 1, 23, 4, 5, };\n"
|
||||
"char c[2][6] = { \"hello\", \"\" };\n");
|
||||
*/
|
||||
|
||||
TEST_CASE("EXAMPLE 4 To illustrate the rules for creating character string "
|
||||
"literals and concatenating tokens, the sequence");
|
||||
|
||||
TEST_CASE("EXAMPLE 5 To illustrate the rules for placemarker preprocessing "
|
||||
"tokens, the sequence");
|
||||
/*
|
||||
CHECK_PP_OUTPUT_EXACT("#define t(x,y,z) x ## y ## z\n"
|
||||
"int j[] = { t(1,2,3), t(,4,5), t(6,,7), t(8,9,),\n"
|
||||
"\t\t\tt(10,,), t(,11,), t(,,12), t(,,) };\n",
|
||||
"int j[] = { 123, 45, 67, 89,\n"
|
||||
"\t\t\t10, 11, 12, };\n");
|
||||
*/
|
||||
|
||||
TEST_CASE("EXAMPLE 6 To demonstrate the redefinition rules, the following "
|
||||
"sequence is valid.");
|
||||
CHECK_PP_OUTPUT_EXACT(
|
||||
"#define OBJ_LIKE (1-1)\n"
|
||||
"#define OBJ_LIKE /* white space */ (1-1) /* other */\n"
|
||||
"#define FUNC_LIKE(a) (a)\n"
|
||||
"#define FUNC_LIKE( a )( /* note the white space */ \\\n"
|
||||
" a /* other stuffonthis line\n"
|
||||
" */ )\n",
|
||||
"");
|
||||
// INVALID
|
||||
// CHECK_PP_OUTPUT_EXACT(
|
||||
// "#define OBJ_LIKE (0) // different token sequence\n"
|
||||
// "#define OBJ_LIKE (1 - 1) // different white space\n"
|
||||
// "#define FUNC_LIKE(b) ( a ) // different parameter usage\n"
|
||||
// "#define FUNC_LIKE(b) ( b ) // different parameter spelling\n");
|
||||
|
||||
TEST_CASE("EXAMPLE 7 Finally,to show the variable argument list macro "
|
||||
"facilities:");
|
||||
CHECK_PP_OUTPUT_EXACT(
|
||||
"#define debug(...) fprintf(stderr, __VA_ARGS__)\n"
|
||||
"#define showlist(...) puts(#__VA_ARGS__)\n"
|
||||
"#define report(test, ...) ((test)?puts(#test): \\\n"
|
||||
" printf(__VA_ARGS__))\n"
|
||||
"debug(\"Flag\");\n"
|
||||
"debug(\"X = %d\\n\", x);\n"
|
||||
"showlist(The first, second, and third items.);\n"
|
||||
"report(x>y, \"x is %d but y is %d\", x, y);\n",
|
||||
|
||||
"fprintf(stderr, \"Flag\");\n"
|
||||
"fprintf(stderr, \"X = %d\\n\", x);\n"
|
||||
"puts(\"The first, second, and third items.\");\n"
|
||||
"((x>y)?puts(\"x>y\"): printf(\"x is %d but y is %d\", x, y));\n");
|
||||
}
|
||||
|
||||
#define TEST_LIST_CASE(func_name) {#func_name, func_name}
|
||||
TEST_LIST = {
|
||||
TEST_LIST_CASE(test_define_simple_no_macro),
|
||||
@@ -230,5 +540,11 @@ TEST_LIST = {
|
||||
TEST_LIST_CASE(test_define_nested_macros),
|
||||
TEST_LIST_CASE(test_undef_macros),
|
||||
TEST_LIST_CASE(hard_test_define_func_macros),
|
||||
TEST_LIST_CASE(test_conditional_ifdef),
|
||||
TEST_LIST_CASE(test_simple_number_conditional_if),
|
||||
TEST_LIST_CASE(test_variadic_macros),
|
||||
TEST_LIST_CASE(test_gnu_comma_variadic_deletion),
|
||||
|
||||
TEST_LIST_CASE(test_c99_docs),
|
||||
{NULL, NULL},
|
||||
};
|
||||
@@ -84,13 +84,13 @@ int scc_sstream_init(scc_sstream_t *stream, const char *fname, int ring_size) {
|
||||
scc_file_t file = scc_fopen(fname, SCC_FILE_READ);
|
||||
if (file == null) {
|
||||
LOG_ERROR("Failed to open file: %s", fname);
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
usize fsize = scc_fsize(file);
|
||||
if (fsize == 0) {
|
||||
LOG_WARN("file size is 0");
|
||||
scc_fclose(file);
|
||||
return 0;
|
||||
return 2;
|
||||
}
|
||||
char *buffer = (char *)scc_malloc(fsize);
|
||||
scc_memset(buffer, 0, fsize);
|
||||
|
||||
@@ -7,7 +7,12 @@
|
||||
#define __SCC_LOG_IMPL_H__
|
||||
|
||||
#include "color.h"
|
||||
#ifndef __SCC__
|
||||
#include <stdarg.h>
|
||||
#else
|
||||
// TODO
|
||||
#warning "TODO: implement stdarg.h"
|
||||
#endif
|
||||
|
||||
#ifdef __SCC_LOG_IMPL_USE_STD_IMPL__
|
||||
#include <stdio.h>
|
||||
@@ -21,7 +26,7 @@
|
||||
#define __smcc_log_unreachable() (__builtin_unreachable())
|
||||
#elif defined _MSC_VER // MSVC
|
||||
#define __smcc_log_unreachable() (__assume(false))
|
||||
#elif defined __SMCC_BUILT_IN__ // The SMCC (my compiler)
|
||||
#elif defined __SCC_BUILT_IN__ // The SCC Compiler (my compiler)
|
||||
#define __smcc_log_unreachable() (__smcc_builtin_unreachable())
|
||||
#else
|
||||
#define __smcc_log_unreachable()
|
||||
@@ -182,13 +187,10 @@ void log_set_handler(logger_t *logger, log_handler handler);
|
||||
* 利用数组大小不能为负的特性
|
||||
* 或使用 _Static_assert (C11)
|
||||
*/
|
||||
#ifdef static_assert
|
||||
#undef static_assert
|
||||
#endif
|
||||
#if __STDC_VERSION__ >= 201112L
|
||||
#define static_assert _Static_assert
|
||||
#define StaticAssert static_assert
|
||||
#else
|
||||
#define static_assert(cond, msg) extern char __static_assertion[(cond) ? 1 : -1]
|
||||
#define StaticAssert(cond, msg) extern char __static_assertion[(cond) ? 1 : -1]
|
||||
#endif
|
||||
|
||||
#ifdef __SCC_LOG_IMPL_IMPORT_SRC__
|
||||
|
||||
@@ -29,14 +29,20 @@
|
||||
|
||||
// ==================== 内部辅助宏 (不直接使用) ====================
|
||||
|
||||
#define scc_ring_phys(ring, idx) ((idx) % (ring).cap)
|
||||
#define _scc_ring_phys(ring, idx) ((idx) % (ring).cap)
|
||||
#define _scc_ring_cap(ring) ((ring).cap)
|
||||
#define _scc_ring_head(ring) ((ring).head)
|
||||
#define _scc_ring_probe(ring) ((ring).probe)
|
||||
#define _scc_ring_tail(ring) ((ring).tail)
|
||||
#define _scc_ring_size(ring) ((ring).tail - (ring).head)
|
||||
#define _scc_ring_empty(ring) ((ring).head == (ring).tail)
|
||||
|
||||
/**
|
||||
* @brief 确保 probe 位置有数据可用 (尝试填充)
|
||||
* @param ring 环形缓冲区变量
|
||||
* @param ok 变量名 (如 int ok_flag) ,宏会将其设置为 true 或 false
|
||||
*/
|
||||
#define scc_ring_ensure(ring, ok) \
|
||||
#define _scc_ring_ensure(ring, ok) \
|
||||
do { \
|
||||
ok = 1; \
|
||||
if ((ring).probe < (ring).tail) \
|
||||
@@ -50,7 +56,7 @@
|
||||
ok = 0; /* 缓冲区满,无法填充 */ \
|
||||
break; \
|
||||
} \
|
||||
usize phys_tail = scc_ring_phys(ring, (ring).tail); \
|
||||
usize phys_tail = _scc_ring_phys(ring, (ring).tail); \
|
||||
if ((ring).fill == null || \
|
||||
!(ring).fill(&(ring).data[phys_tail], (ring).userdata)) { \
|
||||
ok = 0; \
|
||||
@@ -110,14 +116,14 @@
|
||||
*/
|
||||
#define scc_ring_peek(ring, val, ok) \
|
||||
do { \
|
||||
scc_ring_ensure(ring, ok); \
|
||||
_scc_ring_ensure(ring, ok); \
|
||||
if (!(ok)) \
|
||||
break; \
|
||||
if ((ring).probe >= (ring).tail) { \
|
||||
ok = 0; \
|
||||
break; \
|
||||
} \
|
||||
usize _phys = scc_ring_phys(ring, (ring).probe); \
|
||||
usize _phys = _scc_ring_phys(ring, (ring).probe); \
|
||||
val = (ring).data[_phys]; \
|
||||
} while (0)
|
||||
|
||||
@@ -129,14 +135,14 @@
|
||||
*/
|
||||
#define scc_ring_next(ring, val, ok) \
|
||||
do { \
|
||||
scc_ring_ensure(ring, ok); \
|
||||
_scc_ring_ensure(ring, ok); \
|
||||
if (!(ok)) \
|
||||
break; \
|
||||
if ((ring).probe >= (ring).tail) { \
|
||||
ok = 0; \
|
||||
break; \
|
||||
} \
|
||||
usize _phys = scc_ring_phys(ring, (ring).probe); \
|
||||
usize _phys = _scc_ring_phys(ring, (ring).probe); \
|
||||
val = (ring).data[_phys]; \
|
||||
(ring).probe++; \
|
||||
} while (0)
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
#ifndef __SCC_CORE_TYPE_H__
|
||||
#define __SCC_CORE_TYPE_H__
|
||||
|
||||
#ifndef __SCC_BUILTIN_TYPE__
|
||||
#ifndef __SCC__
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
@@ -32,22 +33,28 @@ typedef bool cbool;
|
||||
static_assert(sizeof(cbool) == 1, "cbool size must 1");
|
||||
|
||||
#else
|
||||
#define __scc_i8
|
||||
#define __scc_i16
|
||||
#define __scc_i32
|
||||
#define __scc_i64
|
||||
#define __scc_u8
|
||||
#define __scc_u16
|
||||
#define __scc_u32
|
||||
#define __scc_u64
|
||||
#define __scc_f32
|
||||
#define __scc_f64
|
||||
#define __scc_bool
|
||||
#define __scc_char
|
||||
#define __scc_void
|
||||
#define __scc_null
|
||||
#define __scc_isize
|
||||
#define __scc_usize
|
||||
/* clang-format off */
|
||||
typedef __scc_i8 i8;
|
||||
typedef __scc_i16 i16;
|
||||
typedef __scc_i32 i32;
|
||||
typedef __scc_i64 i64;
|
||||
typedef __scc_u8 u8;
|
||||
typedef __scc_u16 u16;
|
||||
typedef __scc_u32 u32;
|
||||
typedef __scc_u64 u64;
|
||||
|
||||
typedef __scc_isize isize;
|
||||
typedef __scc_usize usize;
|
||||
typedef __scc_isize pdiff;
|
||||
|
||||
typedef __scc_f32 f32;
|
||||
typedef __scc_f64 f64;
|
||||
|
||||
typedef __scc_bool cbool;
|
||||
/// void / null
|
||||
#define null __scc_null
|
||||
|
||||
/* clang-format on */
|
||||
#endif
|
||||
|
||||
typedef union scc_cvalue {
|
||||
|
||||
@@ -60,7 +60,7 @@ void test_char_ring_basic(void) {
|
||||
reset_char_fill();
|
||||
char_ring_t ring;
|
||||
scc_ring_init(ring, 4, char_fill, 0);
|
||||
char c;
|
||||
char c = 0;
|
||||
cbool ok;
|
||||
|
||||
scc_ring_next(ring, c, ok);
|
||||
@@ -109,7 +109,7 @@ void test_char_ring_full(void) {
|
||||
reset_char_fill();
|
||||
char_ring_t ring;
|
||||
scc_ring_init(ring, 3, char_fill, 0);
|
||||
char c;
|
||||
char c = 0;
|
||||
cbool ok;
|
||||
|
||||
scc_ring_next(ring, c, ok);
|
||||
@@ -140,7 +140,7 @@ void test_char_ring_eof(void) {
|
||||
reset_char_fill();
|
||||
char_ring_t ring;
|
||||
scc_ring_init(ring, 32, char_fill, 0);
|
||||
char c;
|
||||
char c = 0;
|
||||
cbool ok;
|
||||
|
||||
for (int i = 0; i < 26; i++) {
|
||||
@@ -160,7 +160,7 @@ void test_char_ring_back_boundary(void) {
|
||||
reset_char_fill();
|
||||
char_ring_t ring;
|
||||
scc_ring_init(ring, 4, char_fill, 0);
|
||||
char c;
|
||||
char c = 0;
|
||||
cbool ok;
|
||||
|
||||
scc_ring_next(ring, c, ok);
|
||||
@@ -186,7 +186,7 @@ void test_char_ring_consume_reset(void) {
|
||||
reset_char_fill();
|
||||
char_ring_t ring;
|
||||
scc_ring_init(ring, 5, char_fill, 0);
|
||||
char c;
|
||||
char c = 0;
|
||||
cbool ok;
|
||||
|
||||
scc_ring_next(ring, c, ok);
|
||||
@@ -219,7 +219,7 @@ void test_char_ring_wrap(void) {
|
||||
reset_char_fill();
|
||||
char_ring_t ring;
|
||||
scc_ring_init(ring, 3, char_fill, 0);
|
||||
char c;
|
||||
char c = 0;
|
||||
cbool ok;
|
||||
|
||||
for (int i = 0; i < 26; i++) {
|
||||
@@ -239,7 +239,7 @@ void test_token_ring_basic(void) {
|
||||
reset_token_fill();
|
||||
token_ring_t ring;
|
||||
scc_ring_init(ring, 3, token_fill, 0);
|
||||
test_token_t tok;
|
||||
test_token_t tok = {0};
|
||||
cbool ok;
|
||||
|
||||
scc_ring_next(ring, tok, ok);
|
||||
@@ -284,7 +284,7 @@ void test_token_ring_full(void) {
|
||||
reset_token_fill();
|
||||
token_ring_t ring;
|
||||
scc_ring_init(ring, 2, token_fill, 0);
|
||||
test_token_t tok;
|
||||
test_token_t tok = {0};
|
||||
cbool ok;
|
||||
|
||||
scc_ring_next(ring, tok, ok);
|
||||
|
||||
77
src/main.c
77
src/main.c
@@ -7,12 +7,11 @@
|
||||
// #include <ir_dump.h>
|
||||
// #include <scc_ast2ir.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
typedef struct {
|
||||
const char *input_file;
|
||||
const char *output_file;
|
||||
int verbose;
|
||||
scc_argparse_list_t include_paths;
|
||||
cbool emit_lex;
|
||||
cbool emit_pp;
|
||||
cbool emit_ast;
|
||||
@@ -26,6 +25,7 @@ static void setup_argparse(scc_argparse_t *argparse, scc_config_t *config,
|
||||
SCC_HINT_DESCRIPTION,
|
||||
SCC_HINT_OUTPUT_FILE,
|
||||
SCC_HINT_INPUT_FILE,
|
||||
SCC_HINT_INCLUDE_PATH,
|
||||
SCC_HINT_VERBOSE,
|
||||
|
||||
SCC_HINT_EMIT_LEX,
|
||||
@@ -38,6 +38,7 @@ static void setup_argparse(scc_argparse_t *argparse, scc_config_t *config,
|
||||
[SCC_HINT_DESCRIPTION] = "A simple C compiler",
|
||||
[SCC_HINT_OUTPUT_FILE] = "Output file",
|
||||
[SCC_HINT_INPUT_FILE] = "Input source file",
|
||||
[SCC_HINT_INCLUDE_PATH] = "SCC_HINT_INCLUDE_PATH",
|
||||
[SCC_HINT_VERBOSE] = "Increase verbosity (can be used multiple times)",
|
||||
[SCC_HINT_EMIT_LEX] = "Generate lexer sources tokens and exit",
|
||||
[SCC_HINT_EMIT_PP] = "Generate preprocessed tokens and exit",
|
||||
@@ -49,6 +50,7 @@ static void setup_argparse(scc_argparse_t *argparse, scc_config_t *config,
|
||||
[SCC_HINT_DESCRIPTION] = "一个简单的C编译器",
|
||||
[SCC_HINT_OUTPUT_FILE] = "输出文件",
|
||||
[SCC_HINT_INPUT_FILE] = "输入源文件",
|
||||
[SCC_HINT_INCLUDE_PATH] = "SCC_HINT_INCLUDE_PATH",
|
||||
[SCC_HINT_VERBOSE] = "增加详细输出(可多次使用)",
|
||||
[SCC_HINT_EMIT_LEX] = "生成`源代码的词法单元`并退出",
|
||||
[SCC_HINT_EMIT_PP] = "生成`预处理后的词法单元`并退出",
|
||||
@@ -88,6 +90,13 @@ static void setup_argparse(scc_argparse_t *argparse, scc_config_t *config,
|
||||
scc_argparse_spec_set_required(&arg_input.spec, true);
|
||||
scc_argparse_cmd_add_arg(root, &arg_input);
|
||||
|
||||
// -I, --include (添加额外的系统头文件搜索路径)
|
||||
scc_argparse_opt_t opt_include;
|
||||
scc_argparse_opt_init(&opt_include, 'I', "include",
|
||||
scc_hints[SCC_HINT_INCLUDE_PATH]);
|
||||
scc_argparse_spec_setup_list(&opt_include.spec, &(config->include_paths));
|
||||
scc_argparse_cmd_add_opt(root, &opt_include);
|
||||
|
||||
// -v, --verbose (计数)
|
||||
scc_argparse_opt_t opt_verbose;
|
||||
scc_argparse_opt_init(&opt_verbose, 'V', "verbose",
|
||||
@@ -143,27 +152,54 @@ static void print_ring(scc_lexer_tok_ring_t *ring, int verbose) {
|
||||
scc_cstring_as_cstr(&tok.lexeme), tok.loc.name,
|
||||
tok.loc.line, tok.loc.col);
|
||||
}
|
||||
scc_lexer_tok_drop(&tok);
|
||||
}
|
||||
}
|
||||
|
||||
static void print_file(scc_lexer_tok_ring_t *ring, const char *file_name) {
|
||||
scc_lexer_tok_t tok = {0};
|
||||
int ret = 0;
|
||||
scc_file_t fp = scc_fopen(file_name, SCC_FILE_WRITE);
|
||||
if (fp == null) {
|
||||
LOG_FATAL("Failed to open file %s", file_name);
|
||||
return;
|
||||
}
|
||||
while (1) {
|
||||
scc_ring_next_consume(*ring, tok, ret);
|
||||
if (ret == false || tok.type == SCC_TOK_EOF) {
|
||||
break;
|
||||
}
|
||||
usize ret = scc_fwrite(fp, scc_cstring_as_cstr(&tok.lexeme),
|
||||
scc_cstring_len(&tok.lexeme));
|
||||
if (ret != scc_cstring_len(&tok.lexeme)) {
|
||||
LOG_FATAL("Failed to write to file %s", file_name);
|
||||
}
|
||||
scc_lexer_tok_drop(&tok);
|
||||
}
|
||||
scc_fclose(fp);
|
||||
}
|
||||
|
||||
int main(int argc, const char **argv, const char **envp) {
|
||||
#ifdef _WIN32
|
||||
SetConsoleOutputCP(CP_UTF8);
|
||||
SetConsoleCP(CP_UTF8);
|
||||
#endif
|
||||
setbuf(stdout, NULL);
|
||||
|
||||
#ifdef _WIN32
|
||||
#define OUTPUT_DEFAULT_FILE "a.exe"
|
||||
#else
|
||||
#define OUTPUT_DEFAULT_FILE "a.out"
|
||||
#endif
|
||||
|
||||
scc_config_t config = {
|
||||
.input_file = NULL,
|
||||
#ifdef _WIN32
|
||||
.output_file = "a.exe",
|
||||
#else
|
||||
.output_file = "a.out",
|
||||
#endif
|
||||
.input_file = null,
|
||||
.verbose = 0,
|
||||
.output_file = null,
|
||||
.emit_ast = false,
|
||||
.emit_ir = false,
|
||||
};
|
||||
scc_vec_init(config.include_paths);
|
||||
|
||||
scc_argparse_t argparse;
|
||||
setup_argparse(&argparse, &config, SCC_ARGPARSE_LANG_ZH);
|
||||
int ret = scc_argparse_parse(&argparse, argc, argv);
|
||||
@@ -181,16 +217,35 @@ int main(int argc, const char **argv, const char **envp) {
|
||||
scc_lexer_t lexer;
|
||||
scc_lexer_init(&lexer, scc_sstream_to_ring(&sstream));
|
||||
if (config.emit_lex) {
|
||||
scc_lexer_tok_ring_t *tok_ring = scc_lexer_to_ring(&lexer, 8, false);
|
||||
scc_lexer_tok_ring_t *tok_ring = scc_lexer_to_ring(
|
||||
&lexer, 8, config.output_file == null ? false : true);
|
||||
if (config.output_file == null) {
|
||||
print_ring(tok_ring, config.verbose);
|
||||
} else {
|
||||
print_file(tok_ring, config.output_file);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
scc_pproc_t pproc;
|
||||
scc_pproc_init(&pproc, scc_lexer_to_ring(&lexer, 8, false));
|
||||
scc_pproc_init(&pproc, scc_lexer_to_ring(&lexer, 8, true));
|
||||
scc_vec_foreach(config.include_paths, i) {
|
||||
scc_pproc_add_include_path_cstr(&pproc,
|
||||
scc_vec_at(config.include_paths, i));
|
||||
}
|
||||
scc_lexer_tok_vec_t pproc_tok_vec;
|
||||
scc_vec_init(pproc_tok_vec);
|
||||
scc_cstring_t pproc_macro_name = scc_cstring_from_cstr("__SCC__");
|
||||
scc_pproc_add_object_macro(&(pproc.macro_table), &pproc_macro_name,
|
||||
&pproc_tok_vec);
|
||||
if (config.emit_pp) {
|
||||
scc_lexer_tok_ring_t *tok_ring = scc_pproc_to_ring(&pproc, 8);
|
||||
if (config.output_file == null) {
|
||||
print_ring(tok_ring, config.verbose);
|
||||
} else {
|
||||
print_file(tok_ring, config.output_file);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
scc_pproc_drop(&pproc);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""cbuild.py - 优化的轻量C构建系统"""
|
||||
"""cbuild.py - 优化的轻量C构建系统""" # pylint: disable=too-many-lines
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
import tomllib
|
||||
@@ -517,7 +517,7 @@ class BuildCache:
|
||||
with open(self.cache_file, "r", encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
self.cache = {k: CacheEntry(**v) for k, v in data.items()}
|
||||
except OSError, json.JSONDecodeError, TypeError:
|
||||
except (OSError, json.JSONDecodeError, TypeError):
|
||||
self.cache = {}
|
||||
|
||||
def save(self):
|
||||
@@ -564,6 +564,7 @@ class Compiler(ABC):
|
||||
def __init__(self):
|
||||
self.recorded = []
|
||||
self.recording = False
|
||||
self.dry_run = False
|
||||
|
||||
def enable_recording(self, enable=True):
|
||||
"""启用命令记录"""
|
||||
@@ -580,6 +581,8 @@ class Compiler(ABC):
|
||||
"""运行命令"""
|
||||
self.record(cmd)
|
||||
logger.debug("执行命令: %s", cmd)
|
||||
if self.dry_run:
|
||||
return # 只打印,不执行
|
||||
try:
|
||||
subprocess.run(cmd, check=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
@@ -679,6 +682,25 @@ class ClangCompiler(Compiler):
|
||||
self.run(cmd)
|
||||
|
||||
|
||||
class SccCompiler(Compiler):
|
||||
"""SCC编译器"""
|
||||
|
||||
def get_flags(self, mode: BuildMode) -> list[str]:
|
||||
return []
|
||||
|
||||
def compile(
|
||||
self, source: Path, output: Path, includes: list[Path], flags: list[str]
|
||||
):
|
||||
# cmd = ["clang"] + flags + ["-c", str(source), "-o", str(output)]
|
||||
cmd = ["scc", "--emit-pp", "-o", str(output), str(source), "-I", "scc_libs"]
|
||||
for inc in includes:
|
||||
cmd += ["-I", f"{inc}"]
|
||||
self.run(cmd)
|
||||
|
||||
def link(self, objects: list[Path], output: Path, flags: list[str]):
|
||||
pass
|
||||
|
||||
|
||||
class DummyCompiler(Compiler):
|
||||
"""虚拟编译器(用于测试)"""
|
||||
|
||||
@@ -1018,12 +1040,18 @@ def create_parser():
|
||||
parser = argparse.ArgumentParser(description="轻量C构建系统", prog="cbuild")
|
||||
parser.add_argument("--verbose", "-v", action="store_true", help="详细输出")
|
||||
parser.add_argument("--path", "-p", default=".", help="项目路径")
|
||||
|
||||
subparsers = parser.add_subparsers(dest="command", required=True, metavar="COMMAND")
|
||||
|
||||
def add_common_args(subparser):
|
||||
subparser.add_argument(
|
||||
"--compiler", "-c", choices=["gcc", "clang"], default="gcc", help="编译器"
|
||||
"--compiler",
|
||||
"-c",
|
||||
choices=["gcc", "clang", "scc"],
|
||||
default="gcc",
|
||||
help="编译器",
|
||||
)
|
||||
subparser.add_argument(
|
||||
"--dry-run", "-d", action="store_true", help="仅打印命令,不实际执行"
|
||||
)
|
||||
subparser.add_argument("--record", "-r", action="store_true", help="记录命令")
|
||||
subparser.add_argument(
|
||||
@@ -1102,6 +1130,7 @@ def create_parser():
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
# print("current cwd: " + os.getcwd())
|
||||
parser = create_parser()
|
||||
args = parser.parse_args()
|
||||
|
||||
@@ -1117,9 +1146,12 @@ def main():
|
||||
compiler_map = {
|
||||
"gcc": GccCompiler(),
|
||||
"clang": ClangCompiler(),
|
||||
"scc": SccCompiler(),
|
||||
}
|
||||
compiler = compiler_map.get(args.compiler, GccCompiler())
|
||||
|
||||
if hasattr(args, "dry_run") and args.dry_run:
|
||||
compiler.dry_run = True
|
||||
if hasattr(args, "record") and args.record:
|
||||
compiler.enable_recording()
|
||||
|
||||
|
||||
53
tools/wc.py
53
tools/wc.py
@@ -1,53 +0,0 @@
|
||||
"""统计目录下C/C++文件的行数(write by AI)"""
|
||||
|
||||
import os
|
||||
|
||||
|
||||
def count_lines(file_path):
|
||||
"""统计单个文件的代码行数"""
|
||||
try:
|
||||
with open(file_path, "rb") as f: # 二进制模式读取避免编码问题
|
||||
return sum(1 for _ in f)
|
||||
except UnicodeDecodeError:
|
||||
print(f"警告:无法解码文件 {file_path}(可能不是文本文件)")
|
||||
return 0
|
||||
except Exception as e:
|
||||
print(f"读取 {file_path} 出错: {str(e)}")
|
||||
return 0
|
||||
|
||||
|
||||
def scan_files(directory, exclude_dirs=None):
|
||||
"""扫描目录获取所有C/C++文件"""
|
||||
if exclude_dirs is None:
|
||||
exclude_dirs = [".git", "venv", "__pycache__", ".old"] # 默认排除的目录
|
||||
|
||||
c_files = []
|
||||
for root, dirs, files in os.walk(directory):
|
||||
# 跳过排除目录
|
||||
dirs[:] = [d for d in dirs if d not in exclude_dirs]
|
||||
|
||||
for file in files:
|
||||
if file.endswith((".c", ".h")):
|
||||
full_path = os.path.join(root, file)
|
||||
c_files.append(full_path)
|
||||
return c_files
|
||||
|
||||
|
||||
def main():
|
||||
"""main function"""
|
||||
target_dir = input("请输入要扫描的目录路径(留空为当前目录): ") or "."
|
||||
|
||||
files = scan_files(target_dir)
|
||||
total_lines = 0
|
||||
|
||||
print("\n统计结果:")
|
||||
for idx, file in enumerate(files, 1):
|
||||
lines = count_lines(file)
|
||||
total_lines += lines
|
||||
print(f"{idx:4d}. {file} ({lines} 行)")
|
||||
|
||||
print(f"\n总计: {len(files)} 个C/C++文件,共 {total_lines} 行代码")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user