refactor(ast): 移除内置类型头文件并优化类型初始化
移除了独立的ast_builtin.h头文件,将内置类型定义整合到现有结构中, 同时修复了类型初始化函数命名不一致的问题。 BREAKING CHANGE: 原有的ast_builtin.h头文件已被移除, 相关内置类型声明已重构为新的接口形式。 fix(parser): 修复表达式解析中的位置信息处理 确保条件表达式和逗号表达式的解析正确获取并传递位置信息, 避免在语法树构建过程中丢失源码位置。 refactor(ir): 修复IR转储中的类型转换问题 添加必要的类型转换以防止整数溢出,并优化代码格式以提高可读性。 feat(parser): 添加表达式解析测试套件 引入全面的表达式解析测试框架,覆盖从基本表达式到复杂嵌套表达式 的各种场景,确保解析器功能的正确性和稳定性。
This commit is contained in:
@@ -427,8 +427,8 @@ static scc_ast_expr_t *parse_conditional_expression(scc_parser_t *parser) {
|
||||
return null;
|
||||
|
||||
const scc_lexer_tok_t *tok_ptr = scc_parser_peek(parser);
|
||||
scc_pos_t pos = tok_ptr->loc;
|
||||
if (tok_ptr && tok_ptr->type == SCC_TOK_COND) {
|
||||
scc_pos_t pos = tok_ptr->loc;
|
||||
// 消耗 '?'
|
||||
scc_lexer_tok_t q_tok;
|
||||
if (!scc_parser_next_consume(parser, &q_tok))
|
||||
@@ -867,6 +867,9 @@ scc_ast_expr_t *scc_parse_expression(scc_parser_t *parser) {
|
||||
if (tok_ptr == null || tok_ptr->type != SCC_TOK_COMMA) {
|
||||
break;
|
||||
}
|
||||
scc_pos_t pos = tok_ptr->loc;
|
||||
scc_parser_next_consume(parser, null);
|
||||
|
||||
scc_ast_expr_t *right = scc_parse_assignment_expression(parser);
|
||||
if (!right) {
|
||||
parser_sync(parser);
|
||||
@@ -874,8 +877,7 @@ scc_ast_expr_t *scc_parse_expression(scc_parser_t *parser) {
|
||||
}
|
||||
scc_ast_expr_t *expr = scc_malloc(sizeof(scc_ast_expr_t));
|
||||
Assert(expr != null);
|
||||
scc_ast_expr_binary_init(expr, SCC_AST_OP_COMMA, left, right,
|
||||
tok_ptr->loc);
|
||||
scc_ast_expr_binary_init(expr, SCC_AST_OP_COMMA, left, right, pos);
|
||||
left = expr;
|
||||
}
|
||||
return left;
|
||||
|
||||
@@ -529,7 +529,7 @@ static scc_ast_type_t *build_type_from_info(type_spec_info_t *info,
|
||||
}
|
||||
|
||||
scc_ast_type_t *type = ast_type_alloc();
|
||||
_scc_ast_type_builtin_init(type, builtin, pos);
|
||||
scc_ast_type_builtin_init(type, builtin, pos);
|
||||
// 注意:限定符(const, volatile)不应在此处处理,应由上层函数负责
|
||||
return type;
|
||||
}
|
||||
@@ -930,8 +930,12 @@ static void parse_parameter_type_list(scc_parser_t *parser,
|
||||
param = scc_malloc(sizeof(scc_ast_decl_t));
|
||||
Assert(param != null);
|
||||
// FIXME
|
||||
scc_ast_decl_param_init(param, &scc_ast_builtin_type_va_list, null,
|
||||
tok_ptr->loc);
|
||||
type = scc_malloc(sizeof(scc_ast_type_t));
|
||||
Assert(type != null);
|
||||
scc_ast_type_builtin_init(type, SCC_AST_BUILTIN_TYPE_VA_LIST,
|
||||
tok_ptr->loc);
|
||||
scc_ast_decl_param_init(param, type, null, tok_ptr->loc);
|
||||
scc_parser_next_consume(parser, null);
|
||||
scc_vec_push(*params, param);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -117,12 +117,13 @@ void scc_sema_init(scc_sema_callbacks_t *callbacks) {
|
||||
callbacks->got_type = got_type_callback;
|
||||
|
||||
scc_sema_symtab_init(sema_symtab);
|
||||
|
||||
// FIXME memory leak
|
||||
scc_ast_type_t *type = scc_malloc(sizeof(scc_ast_type_t));
|
||||
scc_ast_type_builtin_init(type, SCC_AST_BUILTIN_TYPE_VA_LIST,
|
||||
scc_pos_create());
|
||||
scc_sema_symtab_add_symbol(sema_symtab, "__scc_builtin_va_list",
|
||||
&scc_ast_builtin_type_va_list.base);
|
||||
scc_sema_symtab_add_symbol(sema_symtab, "__scc_builtin_size_t",
|
||||
&scc_ast_builtin_type_long_long.base);
|
||||
scc_sema_symtab_add_symbol(sema_symtab, "__scc_builtin_ptrdiff_t",
|
||||
&scc_ast_builtin_type_long_long.base);
|
||||
&type->base);
|
||||
}
|
||||
|
||||
void scc_sema_drop(scc_sema_callbacks_t *callbacks) {}
|
||||
|
||||
0
libs/parser/tests/gen_ast.h
Normal file
0
libs/parser/tests/gen_ast.h
Normal file
95
libs/parser/tests/parser_test.h
Normal file
95
libs/parser/tests/parser_test.h
Normal file
@@ -0,0 +1,95 @@
|
||||
#include <scc_lexer.h>
|
||||
#include <scc_parser.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <utest/acutest.h>
|
||||
|
||||
typedef scc_ast_node_t *(*scc_parse_node_func)(scc_parser_t *parser);
|
||||
|
||||
static scc_ast_node_t *process_input(const char *input,
|
||||
scc_parse_node_func parse_func,
|
||||
cbool need_sema) {
|
||||
int res = 0;
|
||||
scc_sstream_t mem_stream;
|
||||
res = scc_sstream_init_by_buffer(&mem_stream, input, strlen(input), false,
|
||||
16);
|
||||
Assert(res == 0);
|
||||
|
||||
scc_lexer_t lexer;
|
||||
scc_lexer_init(&lexer, scc_sstream_to_ring(&mem_stream));
|
||||
|
||||
scc_lexer_tok_ring_t *tok_ring = scc_lexer_to_ring(&lexer, 64, false);
|
||||
|
||||
scc_parser_t parser;
|
||||
if (need_sema) {
|
||||
scc_sema_callbacks_t sema_callbacks;
|
||||
scc_sema_init(&sema_callbacks);
|
||||
scc_parser_init(&parser, tok_ring, &sema_callbacks);
|
||||
} else {
|
||||
scc_parser_init(&parser, tok_ring, null);
|
||||
}
|
||||
|
||||
scc_ast_node_t *ret = parse_func(&parser);
|
||||
|
||||
cbool not_eof = false;
|
||||
scc_ring_not_eof(*parser.ring, not_eof);
|
||||
if (not_eof == true) {
|
||||
// FIXME MAYBE free
|
||||
LOG_ERROR("Didn't consume all tokens");
|
||||
return null;
|
||||
}
|
||||
|
||||
scc_lexer_drop_ring(parser.ring);
|
||||
scc_parser_drop(&parser);
|
||||
scc_lexer_drop(&lexer);
|
||||
scc_sstream_drop(&mem_stream);
|
||||
return ret;
|
||||
}
|
||||
|
||||
typedef void (*scc_tree_dump_output_t)(void *userdata, const char *fmt, ...);
|
||||
|
||||
#define BUFFER_SIZE (4096)
|
||||
char expect_buffer[BUFFER_SIZE];
|
||||
char output_buffer[BUFFER_SIZE];
|
||||
|
||||
static void dump2buffer(void *_buffer, const char *fmt, ...) {
|
||||
char *buffer = _buffer;
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
int res = scc_vsnprintf(buffer + strlen(buffer),
|
||||
BUFFER_SIZE - strlen(buffer) - 1, fmt, args);
|
||||
Assert(res > 0);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
static void _scc_check_ast(scc_ast_node_t *expect_node_ptr, const char *str,
|
||||
scc_parse_node_func parse_func, cbool need_sema) {
|
||||
scc_ast_node_t *output_node_ptr = process_input(str, parse_func, need_sema);
|
||||
scc_tree_dump_ctx_t ctx;
|
||||
expect_buffer[0] = '\n', expect_buffer[1] = '\0';
|
||||
scc_tree_dump_ctx_init(&ctx, true, dump2buffer, expect_buffer);
|
||||
scc_ast_dump_node(&ctx, expect_node_ptr);
|
||||
scc_tree_dump_ctx_drop(&ctx);
|
||||
output_buffer[0] = '\n', output_buffer[1] = '\0';
|
||||
scc_tree_dump_ctx_init(&ctx, true, dump2buffer, output_buffer);
|
||||
scc_ast_dump_node(&ctx, output_node_ptr);
|
||||
scc_tree_dump_ctx_drop(&ctx);
|
||||
}
|
||||
|
||||
#define SCC_CHECK_AST_WITH_SEMA(expect_node_ptr, str, parse_func) \
|
||||
do { \
|
||||
_scc_check_ast(expect_node_ptr, str, (scc_parse_node_func)parse_func, \
|
||||
true); \
|
||||
TEST_CHECK(strcmp(output_buffer, expect_buffer) == 0); \
|
||||
TEST_MSG("Expected: %s", expect_buffer); \
|
||||
TEST_MSG("Produced: %s", output_buffer); \
|
||||
} while (0);
|
||||
|
||||
#define SCC_CHECK_AST(expect_node_ptr, str, parse_func) \
|
||||
do { \
|
||||
_scc_check_ast(expect_node_ptr, str, (scc_parse_node_func)parse_func, \
|
||||
false); \
|
||||
TEST_CHECK(strcmp(output_buffer, expect_buffer) == 0); \
|
||||
TEST_MSG("Expected: %s", expect_buffer); \
|
||||
TEST_MSG("Produced: %s", output_buffer); \
|
||||
} while (0);
|
||||
395
libs/parser/tests/test_parse_expr.c
Normal file
395
libs/parser/tests/test_parse_expr.c
Normal file
@@ -0,0 +1,395 @@
|
||||
void init_func(void);
|
||||
#define TEST_INIT init_func()
|
||||
|
||||
#include "parser_test.h"
|
||||
|
||||
static scc_ast_type_t int_type;
|
||||
void init_func(void) {
|
||||
scc_ast_type_builtin_init(&int_type, SCC_AST_BUILTIN_TYPE_INT,
|
||||
scc_pos_create());
|
||||
}
|
||||
|
||||
#define LOC scc_pos_create()
|
||||
|
||||
static void test_primary_expr(void) {
|
||||
TEST_CASE("identifier");
|
||||
{
|
||||
scc_ast_expr_t ident;
|
||||
scc_ast_expr_identifier_init(&ident, "x", LOC);
|
||||
SCC_CHECK_AST(&ident.base, "x", scc_parse_expression);
|
||||
}
|
||||
|
||||
TEST_CASE("integer literal");
|
||||
{
|
||||
scc_ast_expr_t int_lit;
|
||||
scc_ast_expr_literal_int_init(&int_lit, "42", false, LOC);
|
||||
SCC_CHECK_AST(&int_lit.base, "42", scc_parse_expression);
|
||||
}
|
||||
|
||||
TEST_CASE("string literal");
|
||||
{
|
||||
scc_ast_expr_t str_lit;
|
||||
scc_ast_expr_literal_string_init(&str_lit, "\"hello\"", false, LOC);
|
||||
SCC_CHECK_AST(&str_lit.base, "\"hello\"", scc_parse_expression);
|
||||
}
|
||||
|
||||
TEST_CASE("parenthesized expression");
|
||||
{
|
||||
scc_ast_expr_t ident;
|
||||
scc_ast_expr_identifier_init(&ident, "y", LOC);
|
||||
// 解析器应直接返回内部表达式,因此期望结果与 "y" 相同
|
||||
SCC_CHECK_AST(&ident.base, "(y)", scc_parse_expression);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_postfix_expr(void) {
|
||||
TEST_CASE("array subscript");
|
||||
{
|
||||
scc_ast_expr_t a, index, subscript;
|
||||
scc_ast_expr_identifier_init(&a, "a", LOC);
|
||||
scc_ast_expr_literal_int_init(&index, "10", false, LOC);
|
||||
scc_ast_expr_array_subscript_init(&subscript, &a, &index, LOC);
|
||||
SCC_CHECK_AST(&subscript.base, "a[10]", scc_parse_expression);
|
||||
}
|
||||
|
||||
TEST_CASE("function call (no arguments)");
|
||||
{
|
||||
scc_ast_expr_t callee, call;
|
||||
scc_ast_expr_identifier_init(&callee, "f", LOC);
|
||||
scc_ast_expr_vec_t args;
|
||||
scc_vec_init(args);
|
||||
scc_ast_expr_call_init(&call, &callee, &args, LOC);
|
||||
SCC_CHECK_AST(&call.base, "f()", scc_parse_expression);
|
||||
}
|
||||
|
||||
TEST_CASE("function call (with arguments)");
|
||||
{
|
||||
scc_ast_expr_t callee, arg1, arg2, call;
|
||||
scc_ast_expr_identifier_init(&callee, "f", LOC);
|
||||
scc_ast_expr_literal_int_init(&arg1, "1", false, LOC);
|
||||
scc_ast_expr_identifier_init(&arg2, "x", LOC);
|
||||
|
||||
scc_ast_expr_vec_t args;
|
||||
scc_vec_init(args);
|
||||
scc_vec_push(args, &arg1);
|
||||
scc_vec_push(args, &arg2);
|
||||
|
||||
scc_ast_expr_call_init(&call, &callee, &args, LOC);
|
||||
SCC_CHECK_AST(&call.base, "f(1, x)", scc_parse_expression);
|
||||
}
|
||||
|
||||
TEST_CASE("member access (.)");
|
||||
{
|
||||
scc_ast_expr_t obj, member;
|
||||
scc_ast_expr_identifier_init(&obj, "s", LOC);
|
||||
scc_ast_expr_member_init(&member, &obj, "field", LOC);
|
||||
SCC_CHECK_AST(&member.base, "s.field", scc_parse_expression);
|
||||
}
|
||||
|
||||
TEST_CASE("pointer member access (->)");
|
||||
{
|
||||
scc_ast_expr_t ptr, member;
|
||||
scc_ast_expr_identifier_init(&ptr, "p", LOC);
|
||||
scc_ast_expr_ptr_member_init(&member, &ptr, "field", LOC);
|
||||
SCC_CHECK_AST(&member.base, "p->field", scc_parse_expression);
|
||||
}
|
||||
|
||||
TEST_CASE("postfix increment/decrement");
|
||||
{
|
||||
scc_ast_expr_t x, post_inc, post_dec;
|
||||
scc_ast_expr_identifier_init(&x, "x", LOC);
|
||||
scc_ast_expr_unary_init(&post_inc, SCC_AST_OP_POSTFIX_INCREMENT, &x,
|
||||
LOC);
|
||||
scc_ast_expr_unary_init(&post_dec, SCC_AST_OP_POSTFIX_DECREMENT, &x,
|
||||
LOC);
|
||||
SCC_CHECK_AST(&post_inc.base, "x++", scc_parse_expression);
|
||||
SCC_CHECK_AST(&post_dec.base, "x--", scc_parse_expression);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_unary_expr(void) {
|
||||
scc_ast_expr_t x;
|
||||
scc_ast_expr_identifier_init(&x, "x", LOC);
|
||||
|
||||
TEST_CASE("prefix increment");
|
||||
{
|
||||
scc_ast_expr_t pre_inc;
|
||||
scc_ast_expr_unary_init(&pre_inc, SCC_AST_OP_PREFIX_INCREMENT, &x, LOC);
|
||||
SCC_CHECK_AST(&pre_inc.base, "++x", scc_parse_expression);
|
||||
}
|
||||
|
||||
TEST_CASE("prefix decrement");
|
||||
{
|
||||
scc_ast_expr_t pre_dec;
|
||||
scc_ast_expr_unary_init(&pre_dec, SCC_AST_OP_PREFIX_DECREMENT, &x, LOC);
|
||||
SCC_CHECK_AST(&pre_dec.base, "--x", scc_parse_expression);
|
||||
}
|
||||
|
||||
TEST_CASE("address-of");
|
||||
{
|
||||
scc_ast_expr_t addr;
|
||||
scc_ast_expr_unary_init(&addr, SCC_AST_OP_ADDRESS_OF, &x, LOC);
|
||||
SCC_CHECK_AST(&addr.base, "&x", scc_parse_expression);
|
||||
}
|
||||
|
||||
TEST_CASE("indirection (dereference)");
|
||||
{
|
||||
scc_ast_expr_t deref;
|
||||
scc_ast_expr_unary_init(&deref, SCC_AST_OP_INDIRECTION, &x, LOC);
|
||||
SCC_CHECK_AST(&deref.base, "*x", scc_parse_expression);
|
||||
}
|
||||
|
||||
TEST_CASE("unary plus");
|
||||
{
|
||||
scc_ast_expr_t plus;
|
||||
scc_ast_expr_unary_init(&plus, SCC_AST_OP_UNARY_PLUS, &x, LOC);
|
||||
SCC_CHECK_AST(&plus.base, "+x", scc_parse_expression);
|
||||
}
|
||||
|
||||
TEST_CASE("unary minus");
|
||||
{
|
||||
scc_ast_expr_t minus;
|
||||
scc_ast_expr_unary_init(&minus, SCC_AST_OP_UNARY_MINUS, &x, LOC);
|
||||
SCC_CHECK_AST(&minus.base, "-x", scc_parse_expression);
|
||||
}
|
||||
|
||||
TEST_CASE("bitwise NOT");
|
||||
{
|
||||
scc_ast_expr_t bit_not;
|
||||
scc_ast_expr_unary_init(&bit_not, SCC_AST_OP_BITWISE_NOT, &x, LOC);
|
||||
SCC_CHECK_AST(&bit_not.base, "~x", scc_parse_expression);
|
||||
}
|
||||
|
||||
TEST_CASE("logical NOT");
|
||||
{
|
||||
scc_ast_expr_t log_not;
|
||||
scc_ast_expr_unary_init(&log_not, SCC_AST_OP_LOGICAL_NOT, &x, LOC);
|
||||
SCC_CHECK_AST(&log_not.base, "!x", scc_parse_expression);
|
||||
}
|
||||
|
||||
TEST_CASE("sizeof expression");
|
||||
{
|
||||
scc_ast_expr_t sizeof_expr;
|
||||
scc_ast_expr_sizeof_init(&sizeof_expr, NULL, &x, LOC);
|
||||
SCC_CHECK_AST(&sizeof_expr.base, "sizeof(x)", scc_parse_expression);
|
||||
}
|
||||
|
||||
TEST_CASE("sizeof type");
|
||||
{
|
||||
scc_ast_expr_t sizeof_type;
|
||||
scc_ast_expr_sizeof_init(&sizeof_type, &int_type, NULL, LOC);
|
||||
SCC_CHECK_AST(&sizeof_type.base, "sizeof(int)", scc_parse_expression);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_cast_expr(void) {
|
||||
TEST_CASE("cast");
|
||||
{
|
||||
scc_ast_expr_t x, cast;
|
||||
scc_ast_expr_identifier_init(&x, "x", LOC);
|
||||
scc_ast_expr_cast_init(&cast, &int_type, &x, LOC);
|
||||
SCC_CHECK_AST(&cast.base, "(int)x", scc_parse_expression);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_binary_expr(void) {
|
||||
scc_ast_expr_t a, b, c, d;
|
||||
scc_ast_expr_identifier_init(&a, "a", LOC);
|
||||
scc_ast_expr_identifier_init(&b, "b", LOC);
|
||||
scc_ast_expr_identifier_init(&c, "c", LOC);
|
||||
scc_ast_expr_identifier_init(&d, "d", LOC);
|
||||
|
||||
TEST_CASE("multiplication and addition (priority)");
|
||||
{
|
||||
// a * b + c
|
||||
scc_ast_expr_t mul, add;
|
||||
scc_ast_expr_binary_init(&mul, SCC_AST_OP_MUL, &a, &b, LOC);
|
||||
scc_ast_expr_binary_init(&add, SCC_AST_OP_ADD, &mul, &c, LOC);
|
||||
SCC_CHECK_AST(&add.base, "a * b + c", scc_parse_expression);
|
||||
}
|
||||
|
||||
TEST_CASE("subtraction (left associativity)");
|
||||
{
|
||||
// a - b - c => (a - b) - c
|
||||
scc_ast_expr_t sub1, sub2;
|
||||
scc_ast_expr_binary_init(&sub1, SCC_AST_OP_SUB, &a, &b, LOC);
|
||||
scc_ast_expr_binary_init(&sub2, SCC_AST_OP_SUB, &sub1, &c, LOC);
|
||||
SCC_CHECK_AST(&sub2.base, "a - b - c", scc_parse_expression);
|
||||
}
|
||||
|
||||
TEST_CASE("shift");
|
||||
{
|
||||
// a << b
|
||||
scc_ast_expr_t shift;
|
||||
scc_ast_expr_binary_init(&shift, SCC_AST_OP_LEFT_SHIFT, &a, &b, LOC);
|
||||
SCC_CHECK_AST(&shift.base, "a << b", scc_parse_expression);
|
||||
}
|
||||
|
||||
TEST_CASE("relational");
|
||||
{
|
||||
// a < b
|
||||
scc_ast_expr_t lt;
|
||||
scc_ast_expr_binary_init(<, SCC_AST_OP_LESS, &a, &b, LOC);
|
||||
SCC_CHECK_AST(<.base, "a < b", scc_parse_expression);
|
||||
}
|
||||
|
||||
TEST_CASE("equality");
|
||||
{
|
||||
// a == b
|
||||
scc_ast_expr_t eq;
|
||||
scc_ast_expr_binary_init(&eq, SCC_AST_OP_EQUAL, &a, &b, LOC);
|
||||
SCC_CHECK_AST(&eq.base, "a == b", scc_parse_expression);
|
||||
}
|
||||
|
||||
TEST_CASE("bitwise operators (priority)");
|
||||
{
|
||||
// a & b ^ c | d => ((a & b) ^ c) | d
|
||||
scc_ast_expr_t bitand, bitxor, bitor;
|
||||
scc_ast_expr_binary_init(&bitand, SCC_AST_OP_BITWISE_AND, &a, &b, LOC);
|
||||
scc_ast_expr_binary_init(&bitxor, SCC_AST_OP_BITWISE_XOR, &bitand, &c,
|
||||
LOC);
|
||||
scc_ast_expr_binary_init(&bitor, SCC_AST_OP_BITWISE_OR, &bitxor, &d,
|
||||
LOC);
|
||||
SCC_CHECK_AST(&bitor.base, "a & b ^ c | d", scc_parse_expression);
|
||||
}
|
||||
|
||||
TEST_CASE("logical operators (priority)");
|
||||
{
|
||||
// a && b || c => (a && b) || c
|
||||
scc_ast_expr_t logand, logor;
|
||||
scc_ast_expr_binary_init(&logand, SCC_AST_OP_LOGICAL_AND, &a, &b, LOC);
|
||||
scc_ast_expr_binary_init(&logor, SCC_AST_OP_LOGICAL_OR, &logand, &c,
|
||||
LOC);
|
||||
SCC_CHECK_AST(&logor.base, "a && b || c", scc_parse_expression);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_conditional_expr(void) {
|
||||
scc_ast_expr_t a, b, c;
|
||||
scc_ast_expr_identifier_init(&a, "a", LOC);
|
||||
scc_ast_expr_identifier_init(&b, "b", LOC);
|
||||
scc_ast_expr_identifier_init(&c, "c", LOC);
|
||||
|
||||
TEST_CASE("simple conditional");
|
||||
{
|
||||
scc_ast_expr_t cond;
|
||||
scc_ast_expr_cond_init(&cond, &a, &b, &c, LOC);
|
||||
SCC_CHECK_AST(&cond.base, "a ? b : c", scc_parse_expression);
|
||||
}
|
||||
|
||||
TEST_CASE("nested conditional (right associative)");
|
||||
{
|
||||
// a ? b : c ? d : e => a ? b : (c ? d : e)
|
||||
scc_ast_expr_t d, e, inner, outer;
|
||||
scc_ast_expr_identifier_init(&d, "d", LOC);
|
||||
scc_ast_expr_identifier_init(&e, "e", LOC);
|
||||
scc_ast_expr_cond_init(&inner, &c, &d, &e, LOC);
|
||||
scc_ast_expr_cond_init(&outer, &a, &b, &inner, LOC);
|
||||
SCC_CHECK_AST(&outer.base, "a ? b : c ? d : e", scc_parse_expression);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_assignment_expr(void) {
|
||||
scc_ast_expr_t a, b, c;
|
||||
scc_ast_expr_identifier_init(&a, "a", LOC);
|
||||
scc_ast_expr_identifier_init(&b, "b", LOC);
|
||||
scc_ast_expr_identifier_init(&c, "c", LOC);
|
||||
scc_ast_expr_t lit42;
|
||||
scc_ast_expr_literal_int_init(&lit42, "42", false, LOC);
|
||||
|
||||
TEST_CASE("simple assignment");
|
||||
{
|
||||
// a = 42
|
||||
scc_ast_expr_t assign;
|
||||
scc_ast_expr_binary_init(&assign, SCC_AST_OP_ASSIGN, &a, &lit42, LOC);
|
||||
SCC_CHECK_AST(&assign.base, "a = 42", scc_parse_expression);
|
||||
}
|
||||
|
||||
TEST_CASE("chained assignment (right associative)");
|
||||
{
|
||||
// a = b = c
|
||||
scc_ast_expr_t inner, outer;
|
||||
scc_ast_expr_binary_init(&inner, SCC_AST_OP_ASSIGN, &b, &c, LOC);
|
||||
scc_ast_expr_binary_init(&outer, SCC_AST_OP_ASSIGN, &a, &inner, LOC);
|
||||
SCC_CHECK_AST(&outer.base, "a = b = c", scc_parse_expression);
|
||||
}
|
||||
|
||||
TEST_CASE("compound assignment");
|
||||
{
|
||||
// a += b
|
||||
scc_ast_expr_t add_assign;
|
||||
scc_ast_expr_binary_init(&add_assign, SCC_AST_OP_ASSIGN_ADD, &a, &b,
|
||||
LOC);
|
||||
SCC_CHECK_AST(&add_assign.base, "a += b", scc_parse_expression);
|
||||
}
|
||||
|
||||
TEST_CASE("assignment with expression");
|
||||
{
|
||||
// a = a - b + 42
|
||||
scc_ast_expr_t sub, add, assign;
|
||||
scc_ast_expr_binary_init(&sub, SCC_AST_OP_SUB, &a, &b, LOC);
|
||||
scc_ast_expr_binary_init(&add, SCC_AST_OP_ADD, &sub, &lit42, LOC);
|
||||
scc_ast_expr_binary_init(&assign, SCC_AST_OP_ASSIGN, &a, &add, LOC);
|
||||
SCC_CHECK_AST(&assign.base, "a = a - b + 42", scc_parse_expression);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_comma_expr(void) {
|
||||
TEST_CASE("comma operator");
|
||||
{
|
||||
scc_ast_expr_t a, b, comma;
|
||||
scc_ast_expr_identifier_init(&a, "a", LOC);
|
||||
scc_ast_expr_identifier_init(&b, "b", LOC);
|
||||
scc_ast_expr_binary_init(&comma, SCC_AST_OP_COMMA, &a, &b, LOC);
|
||||
SCC_CHECK_AST(&comma.base, "a, b", scc_parse_expression);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_complex_expr(void) {
|
||||
TEST_CASE("mixed operators with precedence");
|
||||
{
|
||||
scc_ast_expr_t a, b, c, d;
|
||||
scc_ast_expr_identifier_init(&a, "a", LOC);
|
||||
scc_ast_expr_identifier_init(&b, "b", LOC);
|
||||
scc_ast_expr_identifier_init(&c, "c", LOC);
|
||||
scc_ast_expr_identifier_init(&d, "d", LOC);
|
||||
|
||||
// a + b * c - d
|
||||
scc_ast_expr_t mul, add, sub;
|
||||
scc_ast_expr_binary_init(&mul, SCC_AST_OP_MUL, &b, &c, LOC);
|
||||
scc_ast_expr_binary_init(&add, SCC_AST_OP_ADD, &a, &mul, LOC);
|
||||
scc_ast_expr_binary_init(&sub, SCC_AST_OP_SUB, &add, &d, LOC);
|
||||
SCC_CHECK_AST(&sub.base, "a + b * c - d", scc_parse_expression);
|
||||
}
|
||||
|
||||
TEST_CASE("postfix and unary combination");
|
||||
{
|
||||
scc_ast_expr_t p, post_inc, deref;
|
||||
scc_ast_expr_identifier_init(&p, "p", LOC);
|
||||
scc_ast_expr_unary_init(&post_inc, SCC_AST_OP_POSTFIX_INCREMENT, &p,
|
||||
LOC);
|
||||
scc_ast_expr_unary_init(&deref, SCC_AST_OP_INDIRECTION, &post_inc, LOC);
|
||||
SCC_CHECK_AST(&deref.base, "*p++", scc_parse_expression);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_detail_expr(void) {
|
||||
TEST_CASE("multi string literal connection");
|
||||
scc_ast_expr_t str;
|
||||
scc_ast_expr_literal_string_init(&str, "\"ab\"", false, scc_pos_create());
|
||||
SCC_CHECK_AST(&str.base, "\"a\" \"b\"", scc_parse_expression);
|
||||
}
|
||||
|
||||
TEST_LIST = {
|
||||
{"test_primary_expr", test_primary_expr},
|
||||
{"test_postfix_expr", test_postfix_expr},
|
||||
{"test_unary_expr", test_unary_expr},
|
||||
{"test_cast_expr", test_cast_expr},
|
||||
{"test_binary_expr", test_binary_expr},
|
||||
{"test_conditional_expr", test_conditional_expr},
|
||||
{"test_assignment_expr", test_assignment_expr},
|
||||
{"test_comma_expr", test_comma_expr},
|
||||
{"test_complex_expr", test_complex_expr},
|
||||
{"test_detail_expr", test_detail_expr},
|
||||
{NULL, NULL},
|
||||
};
|
||||
583
libs/parser/tests/test_parse_type.c
Normal file
583
libs/parser/tests/test_parse_type.c
Normal file
@@ -0,0 +1,583 @@
|
||||
void init_func(void);
|
||||
#define TEST_INIT init_func()
|
||||
|
||||
#include "parser_test.h"
|
||||
|
||||
static scc_ast_type_t int_type;
|
||||
static scc_ast_type_t char_type;
|
||||
static scc_ast_type_t void_type;
|
||||
static scc_ast_type_t pointer_int_type;
|
||||
static scc_ast_type_t pointer_char_type;
|
||||
static scc_ast_type_t pointer_void_type;
|
||||
static scc_ast_type_t pointer_pointer_int_type;
|
||||
|
||||
void init_func(void) {
|
||||
scc_ast_type_builtin_init(&int_type, SCC_AST_BUILTIN_TYPE_INT,
|
||||
scc_pos_create());
|
||||
scc_ast_type_builtin_init(&char_type, SCC_AST_BUILTIN_TYPE_CHAR,
|
||||
scc_pos_create());
|
||||
scc_ast_type_builtin_init(&void_type, SCC_AST_BUILTIN_TYPE_VOID,
|
||||
scc_pos_create());
|
||||
scc_ast_type_pointer_init(&pointer_int_type, &int_type, scc_pos_create());
|
||||
scc_ast_type_pointer_init(&pointer_char_type, &char_type, scc_pos_create());
|
||||
scc_ast_type_pointer_init(&pointer_void_type, &void_type, scc_pos_create());
|
||||
scc_ast_type_pointer_init(&pointer_pointer_int_type, &pointer_int_type,
|
||||
scc_pos_create());
|
||||
}
|
||||
|
||||
static void test_builtin_type(void) {
|
||||
#define CHECK_BUILTIN_TYPE(type, str) \
|
||||
do { \
|
||||
scc_ast_type_t builtin_type; \
|
||||
scc_ast_type_builtin_init(&builtin_type, type, scc_pos_create()); \
|
||||
SCC_CHECK_AST(&builtin_type.base, str, scc_parse_type_name); \
|
||||
} while (0)
|
||||
|
||||
// CHECK_BUILTIN_TYPE(SCC_AST_BUILTIN_TYPE_VA_LIST, "...");
|
||||
CHECK_BUILTIN_TYPE(SCC_AST_BUILTIN_TYPE_VOID, "void");
|
||||
CHECK_BUILTIN_TYPE(SCC_AST_BUILTIN_TYPE_BOOL, "bool");
|
||||
CHECK_BUILTIN_TYPE(SCC_AST_BUILTIN_TYPE_CHAR, "char");
|
||||
CHECK_BUILTIN_TYPE(SCC_AST_BUILTIN_TYPE_SHORT, "short");
|
||||
CHECK_BUILTIN_TYPE(SCC_AST_BUILTIN_TYPE_INT, "int");
|
||||
CHECK_BUILTIN_TYPE(SCC_AST_BUILTIN_TYPE_LONG, "long");
|
||||
CHECK_BUILTIN_TYPE(SCC_AST_BUILTIN_TYPE_LONG, "long int");
|
||||
CHECK_BUILTIN_TYPE(SCC_AST_BUILTIN_TYPE_LONG_LONG, "long long");
|
||||
CHECK_BUILTIN_TYPE(SCC_AST_BUILTIN_TYPE_LONG_LONG, "long long int");
|
||||
CHECK_BUILTIN_TYPE(SCC_AST_BUILTIN_TYPE_UNSIGNED_CHAR, "unsigned char");
|
||||
CHECK_BUILTIN_TYPE(SCC_AST_BUILTIN_TYPE_UNSIGNED_SHORT, "unsigned short");
|
||||
CHECK_BUILTIN_TYPE(SCC_AST_BUILTIN_TYPE_UNSIGNED_INT, "unsigned int");
|
||||
CHECK_BUILTIN_TYPE(SCC_AST_BUILTIN_TYPE_UNSIGNED_LONG, "unsigned long");
|
||||
CHECK_BUILTIN_TYPE(SCC_AST_BUILTIN_TYPE_UNSIGNED_LONG, "unsigned long int");
|
||||
CHECK_BUILTIN_TYPE(SCC_AST_BUILTIN_TYPE_UNSIGNED_LONG_LONG,
|
||||
"unsigned long long");
|
||||
CHECK_BUILTIN_TYPE(SCC_AST_BUILTIN_TYPE_UNSIGNED_LONG_LONG,
|
||||
"unsigned long long int");
|
||||
CHECK_BUILTIN_TYPE(SCC_AST_BUILTIN_TYPE_SIGNED_CHAR, "signed char");
|
||||
CHECK_BUILTIN_TYPE(SCC_AST_BUILTIN_TYPE_SIGNED_SHORT, "signed short");
|
||||
CHECK_BUILTIN_TYPE(SCC_AST_BUILTIN_TYPE_SIGNED_INT, "signed int");
|
||||
CHECK_BUILTIN_TYPE(SCC_AST_BUILTIN_TYPE_SIGNED_LONG, "signed long");
|
||||
CHECK_BUILTIN_TYPE(SCC_AST_BUILTIN_TYPE_SIGNED_LONG, "signed long int");
|
||||
CHECK_BUILTIN_TYPE(SCC_AST_BUILTIN_TYPE_SIGNED_LONG_LONG,
|
||||
"signed long long");
|
||||
CHECK_BUILTIN_TYPE(SCC_AST_BUILTIN_TYPE_SIGNED_LONG_LONG,
|
||||
"signed long long int");
|
||||
CHECK_BUILTIN_TYPE(SCC_AST_BUILTIN_TYPE_FLOAT, "float");
|
||||
CHECK_BUILTIN_TYPE(SCC_AST_BUILTIN_TYPE_DOUBLE, "double");
|
||||
CHECK_BUILTIN_TYPE(SCC_AST_BUILTIN_TYPE_LONG_DOUBLE, "long double");
|
||||
CHECK_BUILTIN_TYPE(SCC_AST_BUILTIN_TYPE_COMPLEX_FLOAT, "complex float");
|
||||
CHECK_BUILTIN_TYPE(SCC_AST_BUILTIN_TYPE_COMPLEX_DOUBLE, "complex double");
|
||||
CHECK_BUILTIN_TYPE(SCC_AST_BUILTIN_TYPE_COMPLEX_LONG_DOUBLE,
|
||||
"complex long double");
|
||||
#undef CHECK_BUILTIN_TYPE
|
||||
}
|
||||
|
||||
static void test_pointer_type(void) {
|
||||
TEST_CASE("simple pointer");
|
||||
// int *
|
||||
SCC_CHECK_AST(&pointer_int_type.base, "int *", scc_parse_type_name);
|
||||
// int **
|
||||
SCC_CHECK_AST(&pointer_pointer_int_type.base, "int **",
|
||||
scc_parse_type_name);
|
||||
// int *** (需要临时构建)
|
||||
{
|
||||
scc_ast_type_t ptr_ptr_ptr_int;
|
||||
scc_ast_type_pointer_init(&ptr_ptr_ptr_int, &pointer_pointer_int_type,
|
||||
scc_pos_create());
|
||||
SCC_CHECK_AST(&ptr_ptr_ptr_int.base, "int ***", scc_parse_type_name);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_array_type(void) {
|
||||
scc_ast_expr_t size_5, size_3;
|
||||
|
||||
TEST_CASE("fixed size array");
|
||||
{
|
||||
// int [5]
|
||||
scc_ast_expr_literal_int_init(&size_5, "5", false, scc_pos_create());
|
||||
scc_ast_type_t array_5_int;
|
||||
scc_ast_type_array_init(&array_5_int, &int_type, &size_5,
|
||||
scc_pos_create());
|
||||
SCC_CHECK_AST(&array_5_int.base, "int [5]", scc_parse_type_name);
|
||||
}
|
||||
|
||||
TEST_CASE("incomplete array");
|
||||
{
|
||||
// int []
|
||||
scc_ast_type_t array_unknown_int;
|
||||
scc_ast_type_array_init(&array_unknown_int, &int_type, NULL,
|
||||
scc_pos_create());
|
||||
SCC_CHECK_AST(&array_unknown_int.base, "int []", scc_parse_type_name);
|
||||
}
|
||||
|
||||
TEST_CASE("multidimensional array");
|
||||
{
|
||||
// int [5][3]
|
||||
scc_ast_expr_literal_int_init(&size_5, "5", false, scc_pos_create());
|
||||
scc_ast_expr_literal_int_init(&size_3, "3", false, scc_pos_create());
|
||||
|
||||
scc_ast_type_t inner_array;
|
||||
scc_ast_type_array_init(&inner_array, &int_type, &size_3,
|
||||
scc_pos_create());
|
||||
|
||||
scc_ast_type_t outer_array;
|
||||
scc_ast_type_array_init(&outer_array, &inner_array, &size_5,
|
||||
scc_pos_create());
|
||||
SCC_CHECK_AST(&outer_array.base, "int [5][3]", scc_parse_type_name);
|
||||
}
|
||||
|
||||
TEST_CASE("pointer to array");
|
||||
{
|
||||
// int (*)[5]
|
||||
scc_ast_expr_literal_int_init(&size_5, "5", false, scc_pos_create());
|
||||
|
||||
scc_ast_type_t array_5_int;
|
||||
scc_ast_type_array_init(&array_5_int, &int_type, &size_5,
|
||||
scc_pos_create());
|
||||
|
||||
scc_ast_type_t ptr_to_array;
|
||||
scc_ast_type_pointer_init(&ptr_to_array, &array_5_int,
|
||||
scc_pos_create());
|
||||
SCC_CHECK_AST(&ptr_to_array.base, "int (*)[5]", scc_parse_type_name);
|
||||
}
|
||||
|
||||
TEST_CASE("array of pointers");
|
||||
{
|
||||
// int *[5]
|
||||
scc_ast_expr_literal_int_init(&size_5, "5", false, scc_pos_create());
|
||||
|
||||
scc_ast_type_t array_of_ptr;
|
||||
scc_ast_type_array_init(&array_of_ptr, &pointer_int_type, &size_5,
|
||||
scc_pos_create());
|
||||
SCC_CHECK_AST(&array_of_ptr.base, "int *[5]", scc_parse_type_name);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_function_type(void) {
|
||||
TEST_CASE("function with no parameters (old-style)");
|
||||
{
|
||||
// int ()
|
||||
scc_ast_type_t func_type;
|
||||
scc_ast_type_function_init(&func_type, &int_type, NULL,
|
||||
scc_pos_create());
|
||||
SCC_CHECK_AST(&func_type.base, "int ()", scc_parse_type_name);
|
||||
}
|
||||
|
||||
TEST_CASE("function with void parameter");
|
||||
{
|
||||
// int (void)
|
||||
scc_ast_decl_t void_param;
|
||||
scc_ast_decl_param_init(&void_param, &void_type, NULL,
|
||||
scc_pos_create());
|
||||
|
||||
scc_ast_decl_vec_t params;
|
||||
scc_vec_init(params);
|
||||
scc_vec_push(params, &void_param);
|
||||
|
||||
scc_ast_type_t func_type;
|
||||
scc_ast_type_function_init(&func_type, &int_type, ¶ms,
|
||||
scc_pos_create());
|
||||
SCC_CHECK_AST(&func_type.base, "int (void)", scc_parse_type_name);
|
||||
}
|
||||
|
||||
TEST_CASE("function with parameters");
|
||||
{
|
||||
// int (int, float)
|
||||
scc_ast_type_t float_type;
|
||||
scc_ast_type_builtin_init(&float_type, SCC_AST_BUILTIN_TYPE_FLOAT,
|
||||
scc_pos_create());
|
||||
|
||||
scc_ast_decl_t param_int, param_float;
|
||||
scc_ast_decl_param_init(¶m_int, &int_type, NULL, scc_pos_create());
|
||||
scc_ast_decl_param_init(¶m_float, &float_type, NULL,
|
||||
scc_pos_create());
|
||||
|
||||
scc_ast_decl_vec_t params;
|
||||
scc_vec_init(params);
|
||||
scc_vec_push(params, ¶m_int);
|
||||
scc_vec_push(params, ¶m_float);
|
||||
|
||||
scc_ast_type_t func_type;
|
||||
scc_ast_type_function_init(&func_type, &int_type, ¶ms,
|
||||
scc_pos_create());
|
||||
SCC_CHECK_AST(&func_type.base, "int (int, float)", scc_parse_type_name);
|
||||
}
|
||||
|
||||
TEST_CASE("function with variadic parameters");
|
||||
{
|
||||
// int (int, ...)
|
||||
scc_ast_type_t va_list_type;
|
||||
scc_ast_type_builtin_init(&va_list_type, SCC_AST_BUILTIN_TYPE_VA_LIST,
|
||||
scc_pos_create());
|
||||
|
||||
scc_ast_decl_t param_int, param_var;
|
||||
scc_ast_decl_param_init(¶m_int, &int_type, NULL, scc_pos_create());
|
||||
scc_ast_decl_param_init(¶m_var, &va_list_type, "...",
|
||||
scc_pos_create());
|
||||
|
||||
scc_ast_decl_vec_t params;
|
||||
scc_vec_init(params);
|
||||
scc_vec_push(params, ¶m_int);
|
||||
scc_vec_push(params, ¶m_var);
|
||||
|
||||
scc_ast_type_t func_type;
|
||||
scc_ast_type_function_init(&func_type, &int_type, ¶ms,
|
||||
scc_pos_create());
|
||||
SCC_CHECK_AST(&func_type.base, "int (int, ...)", scc_parse_type_name);
|
||||
}
|
||||
|
||||
TEST_CASE("function returning pointer");
|
||||
{
|
||||
// int *()
|
||||
scc_ast_type_t func_type;
|
||||
scc_ast_type_function_init(&func_type, &pointer_int_type, NULL,
|
||||
scc_pos_create());
|
||||
SCC_CHECK_AST(&func_type.base, "int *()", scc_parse_type_name);
|
||||
}
|
||||
|
||||
TEST_CASE("pointer to function");
|
||||
{
|
||||
// int (*)(void)
|
||||
scc_ast_decl_t void_param;
|
||||
scc_ast_decl_param_init(&void_param, &void_type, NULL,
|
||||
scc_pos_create());
|
||||
|
||||
scc_ast_decl_vec_t params;
|
||||
scc_vec_init(params);
|
||||
scc_vec_push(params, &void_param);
|
||||
|
||||
scc_ast_type_t func_type;
|
||||
scc_ast_type_function_init(&func_type, &int_type, ¶ms,
|
||||
scc_pos_create());
|
||||
|
||||
scc_ast_type_t ptr_to_func;
|
||||
scc_ast_type_pointer_init(&ptr_to_func, &func_type, scc_pos_create());
|
||||
SCC_CHECK_AST(&ptr_to_func.base, "int (*)(void)", scc_parse_type_name);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_struct_union_type(void) {
|
||||
TEST_CASE("struct tag (incomplete)");
|
||||
{
|
||||
// struct S
|
||||
scc_ast_type_t struct_type;
|
||||
scc_ast_type_struct_init(&struct_type, "S", NULL, scc_pos_create());
|
||||
SCC_CHECK_AST(&struct_type.base, "struct S", scc_parse_type_name);
|
||||
}
|
||||
|
||||
TEST_CASE("union tag (incomplete)");
|
||||
{
|
||||
// union U
|
||||
scc_ast_type_t union_type;
|
||||
scc_ast_type_union_init(&union_type, "U", NULL, scc_pos_create());
|
||||
SCC_CHECK_AST(&union_type.base, "union U", scc_parse_type_name);
|
||||
}
|
||||
|
||||
TEST_CASE("anonymous struct definition");
|
||||
{
|
||||
// struct { int x; }
|
||||
scc_ast_decl_t field_x;
|
||||
scc_ast_decl_val_init(&field_x, &int_type, "x", NULL, scc_pos_create());
|
||||
|
||||
scc_ast_decl_vec_t fields;
|
||||
scc_vec_init(fields);
|
||||
scc_vec_push(fields, &field_x);
|
||||
|
||||
scc_ast_decl_t struct_decl;
|
||||
scc_ast_decl_struct_init(&struct_decl, NULL, &fields, scc_pos_create());
|
||||
|
||||
scc_ast_type_t struct_type;
|
||||
scc_ast_type_struct_init(&struct_type, NULL, &struct_decl,
|
||||
scc_pos_create());
|
||||
SCC_CHECK_AST(&struct_type.base, "struct { int x; }",
|
||||
scc_parse_type_name);
|
||||
}
|
||||
|
||||
TEST_CASE("named struct definition");
|
||||
{
|
||||
// struct S { int x; }
|
||||
scc_ast_decl_t field_x;
|
||||
scc_ast_decl_val_init(&field_x, &int_type, "x", NULL, scc_pos_create());
|
||||
|
||||
scc_ast_decl_vec_t fields;
|
||||
scc_vec_init(fields);
|
||||
scc_vec_push(fields, &field_x);
|
||||
|
||||
scc_ast_decl_t struct_decl;
|
||||
scc_ast_decl_struct_init(&struct_decl, "S", &fields, scc_pos_create());
|
||||
|
||||
scc_ast_type_t struct_type;
|
||||
scc_ast_type_struct_init(&struct_type, "S", &struct_decl,
|
||||
scc_pos_create());
|
||||
SCC_CHECK_AST(&struct_type.base, "struct S { int x; }",
|
||||
scc_parse_type_name);
|
||||
}
|
||||
|
||||
TEST_CASE("union definition");
|
||||
{
|
||||
// union { int a; float b; }
|
||||
scc_ast_type_t float_type;
|
||||
scc_ast_type_builtin_init(&float_type, SCC_AST_BUILTIN_TYPE_FLOAT,
|
||||
scc_pos_create());
|
||||
|
||||
scc_ast_decl_t field_a, field_b;
|
||||
scc_ast_decl_val_init(&field_a, &int_type, "a", NULL, scc_pos_create());
|
||||
scc_ast_decl_val_init(&field_b, &float_type, "b", NULL,
|
||||
scc_pos_create());
|
||||
|
||||
scc_ast_decl_vec_t fields;
|
||||
scc_vec_init(fields);
|
||||
scc_vec_push(fields, &field_a);
|
||||
scc_vec_push(fields, &field_b);
|
||||
|
||||
scc_ast_decl_t union_decl;
|
||||
scc_ast_decl_union_init(&union_decl, NULL, &fields, scc_pos_create());
|
||||
|
||||
scc_ast_type_t union_type;
|
||||
scc_ast_type_union_init(&union_type, NULL, &union_decl,
|
||||
scc_pos_create());
|
||||
SCC_CHECK_AST(&union_type.base, "union { int a; float b; }",
|
||||
scc_parse_type_name);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_enum_type(void) {
|
||||
TEST_CASE("enum tag (incomplete)");
|
||||
{
|
||||
// enum E
|
||||
scc_ast_type_t enum_type;
|
||||
scc_ast_type_enum_init(&enum_type, "E", NULL, scc_pos_create());
|
||||
SCC_CHECK_AST(&enum_type.base, "enum E", scc_parse_type_name);
|
||||
}
|
||||
|
||||
TEST_CASE("anonymous enum definition");
|
||||
{
|
||||
// enum { RED, GREEN, BLUE }
|
||||
scc_ast_expr_t red, green, blue;
|
||||
scc_ast_expr_identifier_init(&red, "RED", scc_pos_create());
|
||||
scc_ast_expr_identifier_init(&green, "GREEN", scc_pos_create());
|
||||
scc_ast_expr_identifier_init(&blue, "BLUE", scc_pos_create());
|
||||
|
||||
scc_ast_expr_vec_t enumerators;
|
||||
scc_vec_init(enumerators);
|
||||
scc_vec_push(enumerators, &red);
|
||||
scc_vec_push(enumerators, &green);
|
||||
scc_vec_push(enumerators, &blue);
|
||||
|
||||
scc_ast_decl_t enum_decl;
|
||||
scc_ast_decl_enum_init(&enum_decl, NULL, &enumerators,
|
||||
scc_pos_create());
|
||||
|
||||
scc_ast_type_t enum_type;
|
||||
scc_ast_type_enum_init(&enum_type, NULL, &enum_decl, scc_pos_create());
|
||||
SCC_CHECK_AST(&enum_type.base, "enum { RED, GREEN, BLUE }",
|
||||
scc_parse_type_name);
|
||||
}
|
||||
|
||||
TEST_CASE("named enum definition");
|
||||
{
|
||||
// enum E { RED, GREEN, BLUE }
|
||||
scc_ast_expr_t red, green, blue;
|
||||
scc_ast_expr_identifier_init(&red, "RED", scc_pos_create());
|
||||
scc_ast_expr_identifier_init(&green, "GREEN", scc_pos_create());
|
||||
scc_ast_expr_identifier_init(&blue, "BLUE", scc_pos_create());
|
||||
|
||||
scc_ast_expr_vec_t enumerators;
|
||||
scc_vec_init(enumerators);
|
||||
scc_vec_push(enumerators, &red);
|
||||
scc_vec_push(enumerators, &green);
|
||||
scc_vec_push(enumerators, &blue);
|
||||
|
||||
scc_ast_decl_t enum_decl;
|
||||
scc_ast_decl_enum_init(&enum_decl, "E", &enumerators, scc_pos_create());
|
||||
|
||||
scc_ast_type_t enum_type;
|
||||
scc_ast_type_enum_init(&enum_type, "E", &enum_decl, scc_pos_create());
|
||||
SCC_CHECK_AST(&enum_type.base, "enum E { RED, GREEN, BLUE }",
|
||||
scc_parse_type_name);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_specifier_type(void) {
|
||||
TEST_CASE("const/volatile on builtin types");
|
||||
{
|
||||
// const int
|
||||
scc_ast_type_t const_int = int_type;
|
||||
const_int.quals.is_const = true;
|
||||
SCC_CHECK_AST(&const_int.base, "const int", scc_parse_type_name);
|
||||
|
||||
// volatile int
|
||||
scc_ast_type_t volatile_int = int_type;
|
||||
volatile_int.quals.is_volatile = true;
|
||||
SCC_CHECK_AST(&volatile_int.base, "volatile int", scc_parse_type_name);
|
||||
|
||||
// const volatile int
|
||||
scc_ast_type_t const_volatile_int = int_type;
|
||||
const_volatile_int.quals.is_const = true;
|
||||
const_volatile_int.quals.is_volatile = true;
|
||||
SCC_CHECK_AST(&const_volatile_int.base, "const volatile int",
|
||||
scc_parse_type_name);
|
||||
}
|
||||
|
||||
TEST_CASE("const/volatile on pointers");
|
||||
{
|
||||
// int * const (const pointer to int)
|
||||
scc_ast_type_t const_ptr_to_int = pointer_int_type;
|
||||
const_ptr_to_int.quals.is_const = true;
|
||||
SCC_CHECK_AST(&const_ptr_to_int.base, "int * const",
|
||||
scc_parse_type_name);
|
||||
|
||||
// const int * (pointer to const int)
|
||||
scc_ast_type_t const_int = int_type;
|
||||
const_int.quals.is_const = true;
|
||||
scc_ast_type_t ptr_to_const_int;
|
||||
scc_ast_type_pointer_init(&ptr_to_const_int, &const_int,
|
||||
scc_pos_create());
|
||||
SCC_CHECK_AST(&ptr_to_const_int.base, "const int *",
|
||||
scc_parse_type_name);
|
||||
|
||||
// const int * const (const pointer to const int)
|
||||
scc_ast_type_t const_ptr_to_const_int;
|
||||
scc_ast_type_pointer_init(&const_ptr_to_const_int, &const_int,
|
||||
scc_pos_create());
|
||||
const_ptr_to_const_int.quals.is_const = true;
|
||||
SCC_CHECK_AST(&const_ptr_to_const_int.base, "const int * const",
|
||||
scc_parse_type_name);
|
||||
|
||||
// volatile int * restrict (restrict pointer to volatile int)
|
||||
scc_ast_type_t volatile_int = int_type;
|
||||
volatile_int.quals.is_volatile = true;
|
||||
scc_ast_type_t restrict_ptr_to_volatile_int;
|
||||
scc_ast_type_pointer_init(&restrict_ptr_to_volatile_int, &volatile_int,
|
||||
scc_pos_create());
|
||||
restrict_ptr_to_volatile_int.quals.is_restrict = true;
|
||||
SCC_CHECK_AST(&restrict_ptr_to_volatile_int.base,
|
||||
"volatile int * restrict", scc_parse_type_name);
|
||||
}
|
||||
|
||||
TEST_CASE("const on array element type");
|
||||
{
|
||||
// const int [5]
|
||||
scc_ast_expr_t size_5;
|
||||
scc_ast_expr_literal_int_init(&size_5, "5", false, scc_pos_create());
|
||||
|
||||
scc_ast_type_t const_int = int_type;
|
||||
const_int.quals.is_const = true;
|
||||
|
||||
scc_ast_type_t const_array;
|
||||
scc_ast_type_array_init(&const_array, &const_int, &size_5,
|
||||
scc_pos_create());
|
||||
SCC_CHECK_AST(&const_array.base, "const int [5]", scc_parse_type_name);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_hard_type(void) {
|
||||
TEST_CASE("pointer to array of pointers to function");
|
||||
// int (*(*)[5])(void)
|
||||
{
|
||||
// 1) 函数类型 int (void)
|
||||
scc_ast_decl_t void_param;
|
||||
scc_ast_decl_param_init(&void_param, &void_type, NULL,
|
||||
scc_pos_create());
|
||||
scc_ast_decl_vec_t params;
|
||||
scc_vec_init(params);
|
||||
scc_vec_push(params, &void_param);
|
||||
|
||||
scc_ast_type_t func_type;
|
||||
scc_ast_type_function_init(&func_type, &int_type, ¶ms,
|
||||
scc_pos_create());
|
||||
|
||||
// 2) 指向函数的指针
|
||||
scc_ast_type_t ptr_to_func;
|
||||
scc_ast_type_pointer_init(&ptr_to_func, &func_type, scc_pos_create());
|
||||
|
||||
// 3) 数组,元素为上述指针,大小5
|
||||
scc_ast_expr_t size_5;
|
||||
scc_ast_expr_literal_int_init(&size_5, "5", false, scc_pos_create());
|
||||
|
||||
scc_ast_type_t array_of_ptr;
|
||||
scc_ast_type_array_init(&array_of_ptr, &ptr_to_func, &size_5,
|
||||
scc_pos_create());
|
||||
|
||||
// 4) 指向数组的指针
|
||||
scc_ast_type_t ptr_to_array;
|
||||
scc_ast_type_pointer_init(&ptr_to_array, &array_of_ptr,
|
||||
scc_pos_create());
|
||||
|
||||
SCC_CHECK_AST(&ptr_to_array.base, "int (*(*)[5])(void)",
|
||||
scc_parse_type_name);
|
||||
}
|
||||
|
||||
TEST_CASE("pointer to function returning pointer to array");
|
||||
// int (*(*)(void))[5]
|
||||
{
|
||||
// 1) 数组类型 int [5]
|
||||
scc_ast_expr_t size_5;
|
||||
scc_ast_expr_literal_int_init(&size_5, "5", false, scc_pos_create());
|
||||
|
||||
scc_ast_type_t array_type;
|
||||
scc_ast_type_array_init(&array_type, &int_type, &size_5,
|
||||
scc_pos_create());
|
||||
|
||||
// 2) 指向数组的指针
|
||||
scc_ast_type_t ptr_to_array;
|
||||
scc_ast_type_pointer_init(&ptr_to_array, &array_type, scc_pos_create());
|
||||
|
||||
// 3) 函数类型,返回上述指针,无参数
|
||||
scc_ast_decl_t void_param;
|
||||
scc_ast_decl_param_init(&void_param, &void_type, NULL,
|
||||
scc_pos_create());
|
||||
scc_ast_decl_vec_t params;
|
||||
scc_vec_init(params);
|
||||
scc_vec_push(params, &void_param);
|
||||
|
||||
scc_ast_type_t func_type;
|
||||
scc_ast_type_function_init(&func_type, &ptr_to_array, ¶ms,
|
||||
scc_pos_create());
|
||||
|
||||
// 4) 指向函数的指针
|
||||
scc_ast_type_t ptr_to_func;
|
||||
scc_ast_type_pointer_init(&ptr_to_func, &func_type, scc_pos_create());
|
||||
|
||||
SCC_CHECK_AST(&ptr_to_func.base, "int (*(*)(void))[5]",
|
||||
scc_parse_type_name);
|
||||
}
|
||||
|
||||
TEST_CASE("function returning pointer to function");
|
||||
// int (*())(void)
|
||||
{
|
||||
// 1) 函数类型 int (void)
|
||||
scc_ast_decl_t void_param;
|
||||
scc_ast_decl_param_init(&void_param, &void_type, NULL,
|
||||
scc_pos_create());
|
||||
scc_ast_decl_vec_t params;
|
||||
scc_vec_init(params);
|
||||
scc_vec_push(params, &void_param);
|
||||
|
||||
scc_ast_type_t func_type;
|
||||
scc_ast_type_function_init(&func_type, &int_type, ¶ms,
|
||||
scc_pos_create());
|
||||
|
||||
// 2) 指向该函数的指针
|
||||
scc_ast_type_t ptr_to_func;
|
||||
scc_ast_type_pointer_init(&ptr_to_func, &func_type, scc_pos_create());
|
||||
|
||||
// 3) 外部函数类型,返回上述指针,无参数
|
||||
scc_ast_type_t outer_func;
|
||||
scc_ast_type_function_init(&outer_func, &ptr_to_func, NULL,
|
||||
scc_pos_create());
|
||||
|
||||
SCC_CHECK_AST(&outer_func.base, "int (*())(void)", scc_parse_type_name);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_LIST = {
|
||||
{"test_builtin_type", test_builtin_type},
|
||||
{"test_pointer_type", test_pointer_type},
|
||||
{"test_array_type", test_array_type},
|
||||
{"test_function_type", test_function_type},
|
||||
{"test_struct_union_type", test_struct_union_type},
|
||||
{"test_enum_type", test_enum_type},
|
||||
{"test_specifier_type", test_specifier_type},
|
||||
{"test_hard_type", test_hard_type},
|
||||
{NULL, NULL},
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user