在 C 中实现解析器组合器也是我感兴趣的一个话题,最近,我用 C 写了一个解析器组合器:https ://github.com/petercommand/cparc
以下是我的代码中的一个测试用例,它尝试将逗号分隔的数字解析为数字列表。我使用解析器列表(并通过在代码中调用 parser_chain 从“解析器列表”生成解析器)来模仿 Haskell 中的“do notation”,尽管没有那么优雅。
parser_dp_return test_parser7_rest_dp(dynamic_parser_closure* ctx, input_t input) {
parser_dp_return dp_ret;
dp_ret.obj = ctx->objs[1]->obj;
dp_ret.status = PARSER_NORMAL;
dp_ret.i = input;
dp_ret.discard_obj_callback = NULL;
return dp_ret;
}
parser_dp_return test_parser7_full_dp(dynamic_parser_closure* ctx, input_t input) {
parser_dp_return dp_ret;
list* result = list_new();
list_push_back(result, ctx->objs[0]->obj);//num
if(ctx->objs[1] && ctx->objs[1]->obj) {
list_append(result, ctx->objs[1]->obj);
}
dp_ret.status = PARSER_NORMAL;
dp_ret.i = input;
dp_ret.discard_obj_callback = (void (*)(void *))&list_delete;
dp_ret.obj = result;
return dp_ret;
}
bool test_parser7() {//comma separated values
parser* number = num();
parser* comma = symbol(',');
parser* rest_parser = parser_chain_final(test_parser7_rest_dp);
list* parser_chain_list = list_new();
list_push_back(parser_chain_list, comma);//ctx 0
list_push_back(parser_chain_list, number);//ctx 1
list_push_back(parser_chain_list, rest_parser);
parser* rest = parser_chain(parser_chain_list);
list_delete(parser_chain_list);
parser* many_rest = many(rest);
list* parser_chain_full = list_new();
list_push_back(parser_chain_full, number);//ctx 0
list_push_back(parser_chain_full, many_rest);//ctx 1
parser* full_parser = parser_chain_final(test_parser7_full_dp);
list_push_back(parser_chain_full, full_parser);
parser* final = parser_chain(parser_chain_full);
const char* input = "1,20,300,4000,50000";
input_t i;
input_init(&i, input);
parser_dp_return dp_ret = parse(final, i);
parser_delete(number);
parser_delete(comma);
parser_delete(rest_parser);
parser_delete(rest);
parser_delete(many_rest);
parser_delete(full_parser);
parser_delete(final);
bool result = true;
test_true(&result, dp_ret.status == PARSER_NORMAL);
list* l = dp_ret.obj;
list_item* li = l->head;
test_true(&result, ptr_to_int(li->item) == 1);
li = li->next;
test_true(&result, ptr_to_int(li->item) == 20);
li = li->next;
test_true(&result, ptr_to_int(li->item) == 300);
li = li->next;
test_true(&result, ptr_to_int(li->item) == 4000);
li = li->next;
test_true(&result, ptr_to_int(li->item) == 50000);
return result;
}