expression parser; first pass.

This commit is contained in:
Miguel Lechon
2021-09-07 18:16:57 +02:00
parent c0a56dbcda
commit c6c790e50d
10 changed files with 478 additions and 13 deletions
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
+1
View File
@@ -12,6 +12,7 @@ bin/bld_core.sh show_ctx
bin/bld_core.sh unit sanity_tests tests/sanity_tests.c
bin/bld_core.sh unit unicode_test tests/unicode_test.c
bin/bld_core.sh unit cpp_build_test tests/cpp_build_test.cpp
bin/bld_core.sh unit expression_tests tests/expression_tests.c
echo
Regular → Executable
View File
Regular → Executable
+1
View File
@@ -10,6 +10,7 @@ mkdir -p build
cd build
./sanity_tests.exe
./unicode_test.exe
./expression_tests.exe
###### Restore Path ###########################################################
cd $og_path
+212 -9
View File
@@ -2973,7 +2973,7 @@ MD_NodeDeepMatch(MD_Node *a, MD_Node *b, MD_MatchFlags flags)
//~ Expression Parsing
MD_FUNCTION void
MD_FUNCTION_IMPL void
MD_ExprOperatorPush(MD_Arena *arena, MD_ExprOperatorList *list,
MD_u32 op_id, MD_ExprOperatorKind kind,
MD_u64 precedence, MD_Node *md_node){
@@ -2986,18 +2986,221 @@ MD_ExprOperatorPush(MD_Arena *arena, MD_ExprOperatorList *list,
node->op.md_node = md_node;
}
MD_FUNCTION MD_ExprOperatorTable
MD_FUNCTION_IMPL MD_ExprOperatorTable
MD_ExprBakeOperatorTableFromList(MD_Arena *arena, MD_ExprOperatorList *list){
MD_ExprOperatorTable result = {0};
// TODO(allen): @expr_parser
MD_ExprOperatorTable result = MD_ZERO_STRUCT;
for(MD_ExprOperatorNode *op_node = list->first; op_node; op_node = op_node->next){
MD_ExprOperatorList *list = result.table+op_node->op.kind;
MD_ExprOperatorNode *op_node_copy = MD_PushArray(arena, MD_ExprOperatorNode, 1);
MD_QueuePush(list->first, list->last, op_node_copy);
list->count += 1;
op_node_copy->op = op_node->op;
if(op_node->op.kind == MD_ExprOperatorKind_Postfix &&
MD_S8Match(op_node->op.md_node->string, MD_S8Lit("()"), 0)){
result.call_op = &op_node_copy->op;
}
else if(op_node->op.kind == MD_ExprOperatorKind_Binary &&
MD_S8Match(op_node->op.md_node->string, MD_S8Lit("[]"), 0)){
result.subscript_op = &op_node_copy->op;
}
// TODO(mal): Check: non-repeat operators, same precedence binary ops should share associativity
}
return(result);
}
MD_FUNCTION MD_ExprParseResult
MD_ExprParse(MD_Arena *arena, MD_ExprOperatorTable *op_table,
MD_Node *first, MD_Node *one_past_last){
MD_ExprParseResult result = {0};
// TODO(allen): @expr_parser
typedef struct _MD_ExprParseCtx _MD_ExprParseCtx;
struct _MD_ExprParseCtx{
MD_ExprOperatorTable *op_table;
MD_Node *first;
MD_Node *one_past_last;
};
MD_FUNCTION_IMPL MD_ExprParseResult
_MD_ExprParse_Ctx_MinPrecedence(MD_Arena *arena, _MD_ExprParseCtx *ctx, MD_u32 min_precedence);
MD_FUNCTION_IMPL MD_ExprNode * _MD_Expr_Make(MD_Arena *arena, MD_ExprOperator *op, MD_Node *op_node,
MD_ExprNode *left, MD_ExprNode *right){
MD_ExprNode *result = MD_PushArrayZero(arena, MD_ExprNode, 1);
result->is_op = 1;
result->op_id = op->op_id;
result->md_op_node = op->md_node;
result->md_node = op_node;
result->left = left;
result->right = right;
result->left->parent = result;
if(result->right){ // TODO(mal): Introduce Nil expr node?
result->right->parent = result;
}
return result;
}
MD_FUNCTION_IMPL MD_ExprOperator *
_MD_ExprOperatorMatch(MD_ExprOperatorTable *table, MD_ExprOperatorKind kind, MD_String8 s){
// NOTE(mal): Look for operator on one or all (kind == MD_ExprOperatorKind_Null) tables
MD_ExprOperator *result = 0;
for(MD_ExprOperatorKind cur_kind = (MD_ExprOperatorKind)(MD_ExprOperatorKind_Null+1);
!result && cur_kind < MD_ExprOperatorKind_COUNT;
cur_kind = (MD_ExprOperatorKind)(cur_kind+1)){
if(kind == MD_ExprOperatorKind_Null || kind == cur_kind){
MD_ExprOperatorList *op_list = table->table+cur_kind;
for(MD_ExprOperatorNode *op_node = op_list->first; !result && op_node; op_node = op_node->next)
{
if(MD_S8Match(op_node->op.md_node->string, s, 0)){
result = &op_node->op;
}
}
}
}
return result;
}
MD_FUNCTION_IMPL void _MD_CtxAdvance(_MD_ExprParseCtx *ctx){
ctx->first = ctx->first->next;
}
MD_FUNCTION_IMPL MD_b32 _MD_ExprOperatorConsumed(_MD_ExprParseCtx *ctx, MD_ExprOperatorKind kind, MD_u32 min_precedence,
MD_ExprOperator **op_out){
MD_b32 result = 0;
if(!MD_NodeIsNil(ctx->first)){
MD_Node *node = ctx->first;
MD_ExprOperator *op = _MD_ExprOperatorMatch(ctx->op_table, kind, node->string);
if(op && op->precedence >= min_precedence){
result = 1;
*op_out = op;
_MD_CtxAdvance(ctx);
}
}
return result;
}
MD_FUNCTION_IMPL _MD_ExprParseCtx
_MD_ExprParse_MakeContext(MD_ExprOperatorTable *op_table, MD_Node *first, MD_Node *one_past_last){
_MD_ExprParseCtx result = MD_ZERO_STRUCT;
result.op_table = op_table;
result.first = first;
result.one_past_last = one_past_last;
return result;
}
MD_FUNCTION_IMPL MD_ExprParseResult
_MD_ExprParse_Atom(MD_Arena *arena, _MD_ExprParseCtx *ctx){
MD_ExprParseResult result = MD_ZERO_STRUCT;
MD_Node *node = ctx->first;
MD_ExprOperator *op = 0;
if(node->flags & MD_NodeFlag_HasParenLeft && node->flags & MD_NodeFlag_HasParenRight){ // NOTE(mal): Parens
_MD_CtxAdvance(ctx);
_MD_ExprParseCtx sub_ctx = _MD_ExprParse_MakeContext(ctx->op_table, node->first_child, node->last_child->next);
result = _MD_ExprParse_Ctx_MinPrecedence(arena, &sub_ctx, 0);
}
else if((node->flags & MD_NodeFlag_HasBracketLeft && node->flags & MD_NodeFlag_HasBracketRight) ||
(node->flags & MD_NodeFlag_HasBraceLeft && node->flags & MD_NodeFlag_HasBraceRight)){
_MD_CtxAdvance(ctx);
// NOTE(mal): Unparsed leaf sets ({ ... }, [ ... ])
result.node = MD_PushArrayZero(arena, MD_ExprNode, 1);
result.node->md_node = node;
}
else if(_MD_ExprOperatorConsumed(ctx, MD_ExprOperatorKind_Prefix, 1, &op)){
MD_u32 min_precedence = op->precedence + 1;
MD_ExprParseResult sub_parse_result = _MD_ExprParse_Ctx_MinPrecedence(arena, ctx, min_precedence);
if(sub_parse_result.errors.max_message_kind == MD_MessageKind_Null){
result.node = _MD_Expr_Make(arena, op, node, sub_parse_result.node, 0);
}
else{
result.errors = sub_parse_result.errors;
}
}
else if(_MD_ExprOperatorConsumed(ctx, MD_ExprOperatorKind_Null, 1, &op)){
MD_String8 error_str = MD_S8Fmt(arena, "Expected leaf. Got operator \"%.*s\".", MD_S8VArg(node->string));
MD_Message *error = MD_MakeNodeError(arena, node, MD_MessageKind_Error, error_str);
MD_MessageListPush(&result.errors, error);
}
else{ // NOTE(mal): leaf
_MD_CtxAdvance(ctx);
result.node = MD_PushArrayZero(arena, MD_ExprNode, 1);
result.node->md_node = node;
}
return result;
}
MD_FUNCTION_IMPL MD_ExprParseResult
_MD_ExprParse_Ctx_MinPrecedence(MD_Arena *arena, _MD_ExprParseCtx *ctx, MD_u32 min_precedence){
MD_ExprParseResult result = MD_ZERO_STRUCT;
MD_ExprOperator *subscript_op = ctx->op_table->subscript_op;
result = _MD_ExprParse_Atom(arena, ctx);
if(result.errors.max_message_kind == MD_MessageKind_Null){
while(!MD_NodeIsNil(ctx->first) && ctx->first != ctx->one_past_last){
MD_Node *node = ctx->first;
MD_ExprOperator *op;
if(subscript_op && subscript_op->precedence >= min_precedence &&
node->flags & MD_NodeFlag_HasBracketLeft && node->flags & MD_NodeFlag_HasBracketRight){
_MD_CtxAdvance(ctx);
// NOTE(mal): Array subscript
_MD_ExprParseCtx sub_ctx = _MD_ExprParse_MakeContext(ctx->op_table,
node->first_child, node->last_child->next);
MD_ExprParseResult sub_parse_result = _MD_ExprParse_Ctx_MinPrecedence(arena, &sub_ctx, 0);
if(sub_parse_result.errors.max_message_kind == MD_MessageKind_Null){
result.node = _MD_Expr_Make(arena, subscript_op, node, result.node, sub_parse_result.node);
}
else{
result.errors = sub_parse_result.errors;
break;
}
}
else if(_MD_ExprOperatorConsumed(ctx, MD_ExprOperatorKind_Binary, min_precedence, &op) ||
_MD_ExprOperatorConsumed(ctx, MD_ExprOperatorKind_BinaryRightAssociative, min_precedence, &op)){
MD_u32 next_min_precedence = op->precedence + (op->kind == MD_ExprOperatorKind_Binary);
MD_ExprParseResult sub_parse_result = _MD_ExprParse_Ctx_MinPrecedence(arena, ctx, next_min_precedence);
if(sub_parse_result.errors.max_message_kind == MD_MessageKind_Null){
result.node = _MD_Expr_Make(arena, op, node, result.node, sub_parse_result.node);
}
else{
result.errors = sub_parse_result.errors;
break;
}
}
else if(ctx->op_table->call_op && ctx->op_table->call_op->precedence >= min_precedence &&
node->flags & MD_NodeFlag_HasParenLeft && node->flags & MD_NodeFlag_HasParenRight){ // NOTE: call
_MD_CtxAdvance(ctx);
result.node = _MD_Expr_Make(arena, ctx->op_table->call_op, node, result.node, 0);
}
else if(_MD_ExprOperatorConsumed(ctx, MD_ExprOperatorKind_Postfix, min_precedence, &op)){
result.node = _MD_Expr_Make(arena, op, node, result.node, 0);
}
else{
break; // NOTE: Due to lack of progress
}
}
}
return result;
}
MD_FUNCTION_IMPL MD_ExprParseResult
MD_ExprParse(MD_Arena *arena, MD_ExprOperatorTable *op_table, MD_Node *first, MD_Node *one_past_last){
MD_ExprParseResult result = MD_ZERO_STRUCT;
_MD_ExprParseCtx ctx = _MD_ExprParse_MakeContext(op_table, first, one_past_last);
result = _MD_ExprParse_Ctx_MinPrecedence(arena, &ctx, 0);
if(result.errors.max_message_kind == MD_MessageKind_Null){
if(ctx.first != ctx.one_past_last){
MD_String8 error_str =
MD_S8Fmt(arena, "Partial parse. Expected binary or unary postfix operator."); // TODO(mal): More detail?
MD_Message *error = MD_MakeNodeError(arena, ctx.first, MD_MessageKind_Error, error_str);
MD_MessageListPush(&result.errors, error);
}
}
return(result);
}
+10 -4
View File
@@ -681,11 +681,13 @@ struct MD_ParseResult
typedef enum MD_ExprOperatorKind
{
// TODO(mal): We could improve this naming scheme
MD_ExprOperatorKind_Null,
// TODO(mal): Improve this naming scheme ?
MD_ExprOperatorKind_Prefix,
MD_ExprOperatorKind_Postfix,
MD_ExprOperatorKind_Binary,
MD_ExprOperatorKind_BinaryRightAssociative,
MD_ExprOperatorKind_COUNT,
} MD_ExprOperatorKind;
typedef struct MD_ExprOperator MD_ExprOperator;
@@ -715,8 +717,12 @@ struct MD_ExprOperatorList
typedef struct MD_ExprOperatorTable MD_ExprOperatorTable;
struct MD_ExprOperatorTable
{
// TODO(allen): @expr_parser fill this in however needed
int foo;
// TODO(mal): Something faster; arrays indexed by op kind or hash table...
MD_ExprOperatorList table[MD_ExprOperatorKind_COUNT];
MD_ExprOperator *call_op;
MD_ExprOperator *subscript_op;
MD_MessageList errors;
};
@@ -826,7 +832,7 @@ struct MD_FileIter
((l)->next=(n),(l)=(n),zset((n)->next)))
#define MD_QueuePop_NZ(f,l,next,zset) ((f)==(l)?\
(zset(f),zset(l)):\
(f)=(f)->next)
((f)=(f)->next))
#define MD_StackPush_N(f,n,next) ((n)->next=(f),(f)=(n))
#define MD_StackPop_NZ(f,next,zchk) (zchk(f)?0:(f)=(f)->next)
+254
View File
@@ -0,0 +1,254 @@
//$ exe //
#if 0
#define ASSERT(expr) {if(!(expr)){fprintf(stderr, "Assertion failed: %s, line %d (%s)\n", __FILE__, __LINE__, #expr); __builtin_debugtrap();}}
#define HARD_ASSERT(expr) {if(!(expr)){fprintf(stderr, "Assertion failed: %s, line %d (%s)\n", __FILE__, __LINE__, #expr); __builtin_debugtrap();}}
#define BP ASSERT(!"Break point");
#define NotTested ASSERT(!"Not tested");
#define STATIC_ASSERT(expr) U8 MACRO_CONCAT(static_assert_, __COUNTER__)[(expr)?(1):(-1)]
#define NotImplemented ASSERT(!"Not implemented");
#endif
#include "md.h"
#include "md.c"
MD_Arena *arena = 0;
typedef struct Expression_QA Expression_QA;
struct Expression_QA
{
char *q;
char *a;
};
typedef struct OperatorDescription OperatorDescription;
struct OperatorDescription{
MD_String8 s;
MD_ExprOperator op;
};
// NOTE(mal): Based on https://en.wikipedia.org/wiki/Operators_in_C_and_C%2B%2B#Operator_precedence 2021-09-05
// Precedences are flipped. The ones listed here are 20-x where x is the precedence on the wikipedia table
#define OPERATORS \
X(PostFixIncrement, "++", Postfix, 18) \
X(PostFixDecrement, "--", Postfix, 18) \
X(Call, "()", Postfix, 18) \
X(ArraySubscript, "[]", Binary, 18) \
X(Member, ".", Binary, 18) \
X(PointerMember, "->", Binary, 18) \
X(PreFixIncrement, "++", Prefix, 17) \
X(PreFixDecrement, "--", Prefix, 17) \
X(UnaryPlus, "+", Prefix, 17) \
X(UnaryMinus, "-", Prefix, 17) \
X(LogicalNot, "!", Prefix, 17) \
X(BitwiseNot, "~", Prefix, 17) \
X(Dereference, "*", Prefix, 17) \
X(AddressOf, "&", Prefix, 17) \
X(SizeOf, "sizeof", Prefix, 17) /* NOTE(mal); Just for fun*/ \
X(Multiplication, "*", Binary, 15) \
X(Division, "/", Binary, 15) \
X(Modulo, "%", Binary, 15) \
X(Addition, "+", Binary, 14) \
X(Subtraction, "-", Binary, 14) \
X(LeftShift, "<<", Binary, 13) \
X(RightShift, ">>", Binary, 13) \
X(LessThan, "<", Binary, 11) \
X(LessThanOrEqual, "<=", Binary, 11) \
X(GreaterThan, ">", Binary, 11) \
X(GreaterThanOrEqual, ">=", Binary, 11) \
X(Equal, "==", Binary, 10) \
X(NotEqual, "!=", Binary, 10) \
X(BitwiseAnd, "&", Binary, 9) \
X(BitwiseXor, "^", Binary, 8) \
X(BitwiseOr, "|", Binary, 7) \
X(LogicalAnd, "&&", Binary, 6) \
X(LogicalOr, "||", Binary, 5) \
X(Assign, "=", BinaryRightAssociative, 4) \
X(AssignAddition, "+=", BinaryRightAssociative, 4) \
X(AssignSubtraction, "-=", BinaryRightAssociative, 4) \
X(AssignMultiplication,"*=", BinaryRightAssociative, 4) \
X(AssignDivision, "/=", BinaryRightAssociative, 4) \
X(AssignModulo, "%=", BinaryRightAssociative, 4) \
X(AssignLeftShift, "<<=", BinaryRightAssociative, 4) \
X(AssignRightShift, ">>=", BinaryRightAssociative, 4) \
X(AssignBitwiseAnd, "&=", BinaryRightAssociative, 4) \
X(AssignBitwiseXor, "^=", BinaryRightAssociative, 4) \
X(AssignBitwiseOr, "|=", BinaryRightAssociative, 4)
// X(Cast "()", Prefix, 17)
// X(Comma, ",", Binary, 3)
#define X(name, token, kind, prec) Op_##name,
typedef enum{
Op_Null,
OPERATORS
Op_COUNT
} Op;
#undef X
static MD_String8 node_raw_contents(MD_Node *node, MD_b32 exclude_outer){
MD_String8 result = {0};
MD_u64 beg = node->offset;
if(exclude_outer && !MD_NodeIsNil(node->first_child)){
beg = node->first_child->offset;
}
MD_u64 end = beg; {
MD_Node *last_descendant = node;
while(!MD_NodeIsNil(last_descendant->last_child)){
last_descendant = last_descendant->last_child;
}
end = last_descendant->offset + last_descendant->raw_string.size;
}
MD_Node *root = node;
while(!MD_NodeIsNil(root->parent)){
root = root->parent;
}
result = MD_S8Substring(root->raw_string, beg, end);
return result;
}
static void parenthesize_exclude_outer(MD_Arena *arena, OperatorDescription *descs, MD_String8List *l,
MD_ExprNode *node, MD_b32 exclude_outer_parens){
if(node->is_op){
if(!exclude_outer_parens){
MD_S8ListPush(arena, l, MD_S8Lit("("));
}
MD_ExprOperator *op = &descs[node->op_id].op;
if(op->kind == MD_ExprOperatorKind_Binary || op->kind == MD_ExprOperatorKind_BinaryRightAssociative){
parenthesize_exclude_outer(arena, descs, l, node->left, 0);
MD_b32 is_subscript = (node->op_id == Op_ArraySubscript);
if(is_subscript){
MD_S8ListPush(arena, l, MD_S8Lit("["));
parenthesize_exclude_outer(arena, descs, l, node->right, 1);
MD_S8ListPush(arena, l, MD_S8Lit("]"));
}
else{
MD_S8ListPush(arena, l, MD_S8Lit(" "));
MD_S8ListPush(arena, l, node->md_node->string);
MD_S8ListPush(arena, l, MD_S8Lit(" "));
parenthesize_exclude_outer(arena, descs, l, node->right, 0);
}
}
else if(op->kind == MD_ExprOperatorKind_Prefix){
MD_S8ListPush(arena, l, node->md_node->string);
MD_u8 last_op_c = MD_S8Suffix(node->md_node->string, 1).str[0];
if(MD_CharIsAlpha(last_op_c) || MD_CharIsDigit(last_op_c)){ // NOTE: Keyword prefix operator (e.g. sizeof)
MD_S8ListPush(arena, l, MD_S8Lit(" "));
}
parenthesize_exclude_outer(arena, descs, l, node->left, 0);
}
else if(op->kind == MD_ExprOperatorKind_Postfix){
parenthesize_exclude_outer(arena, descs, l, node->left, 0);
MD_b32 is_call = (node->op_id == Op_Call);
if(is_call){
MD_S8ListPush(arena, l, MD_S8Lit("(...)"));
}
else{
MD_S8ListPush(arena, l, node->md_node->string);
}
}
else{
MD_S8ListPush(arena, l, MD_S8Lit("--- Can't print expression ---"));
}
if(!exclude_outer_parens){
MD_S8ListPush(arena, l, MD_S8Lit(")"));
}
}
else{
if(MD_NodeIsNil(node->md_node->first_child)){
MD_S8ListPush(arena, l, node_raw_contents(node->md_node, 0));
}
else{
if(node->md_node->flags & MD_NodeFlag_HasBracketLeft &&
node->md_node->flags & MD_NodeFlag_HasBracketRight){
MD_S8ListPush(arena, l, MD_S8Lit("[...]"));
}
else if(node->md_node->flags & MD_NodeFlag_HasBraceLeft &&
node->md_node->flags & MD_NodeFlag_HasBraceRight){
MD_S8ListPush(arena, l, MD_S8Lit("{...}"));
}
else{
MD_S8ListPush(arena, l, MD_S8Lit("???"));
}
}
}
}
static MD_String8 parenthesize(MD_Arena *arena, OperatorDescription *descs, MD_ExprNode *node){
MD_String8 result = {0};
MD_String8List l = {0};
parenthesize_exclude_outer(arena, descs, &l, node, 1);
result = MD_S8ListJoin(arena, l, 0);
return result;
}
int main(void){
OperatorDescription operator_array[Op_COUNT] = {0};
#define X(name, token, kind_, prec) \
operator_array[Op_##name].s = MD_S8Lit(token); \
operator_array[Op_##name].op = (MD_ExprOperator){ .op_id = Op_##name, .kind = MD_ExprOperatorKind_##kind_, .precedence = prec };
OPERATORS
#undef X
arena = MD_ArenaAlloc(1ull << 40);
MD_ExprOperatorList operator_list = {0};
for(Op op= Op_Null+1; op < Op_COUNT; ++op){
OperatorDescription *desc = operator_array + op;
MD_Node *node = MD_MakeNode(arena, MD_NodeKind_Main, desc->s, desc->s, 0);
MD_ExprOperatorPush(arena, &operator_list, op, desc->op.kind, desc->op.precedence, node);
}
MD_ExprOperatorTable op_table = MD_ExprBakeOperatorTableFromList(arena, &operator_list);
Expression_QA tests[] = {
{ .q = "a + b + c", .a = "(a + b) + c" },
{ .q = "a + (b + c)", .a = "a + (b + c)" },
{ .q = "-a * b + c", .a = "((-a) * b) + c" },
{ .q = "a * (b + c)", .a = "a * (b + c)" },
{ .q = "a = b + c", .a = "a = (b + c)" },
{ .q = "a(b,c)", .a = "a(...)" },
{ .q = "(a + b)()", .a = "(a + b)(...)" },
{ .q = "sizeof a + b", .a = "(sizeof a) + b" },
{ .q = "[1, 100] * n", .a = "[...] * n" },
{ .q = "a[b+c]", .a = "a[b + c]" },
{ .q = "a++ + b", .a = "(a++) + b" },
};
for(MD_u32 i_test = 0; i_test < MD_ArrayCount(tests); i_test+=1){
Expression_QA test = tests[i_test];
MD_String8 q = MD_S8CString(test.q);
MD_String8 a = MD_S8CString(test.a);
MD_ParseResult parse = MD_ParseWholeString(arena, MD_S8Lit("test"), q);
MD_Assert(parse.errors.max_message_kind == MD_MessageKind_Null);
MD_ExprParseResult expr_parse = MD_ExprParse(arena, &op_table, parse.node->first_child, MD_NilNode());
if(expr_parse.errors.max_message_kind){
printf("Example %d : \"%.*s\". Got error \"%.*s\"\n", i_test, MD_S8VArg(q),
MD_S8VArg(expr_parse.errors.first[0].string));
}
else{
MD_String8 parser_answer = parenthesize(arena, operator_array, expr_parse.node);
if(expr_parse.errors.max_message_kind || !MD_S8Match(parser_answer, a, 0)){
printf("Example %d : Expected answer for %.*s is %.*s. Got %.*s\n",
i_test, MD_S8VArg(q), MD_S8VArg(a), MD_S8VArg(parser_answer));
}
}
}
return 0;
}