mirror of
https://github.com/Ed94/metadesk.git
synced 2026-06-12 23:51:37 -07:00
476 lines
21 KiB
C
476 lines
21 KiB
C
//$ exe //
|
|
|
|
#include "md.h"
|
|
#include "md.c"
|
|
|
|
MD_Arena *g_arena = 0;
|
|
|
|
typedef enum{
|
|
ExpressionErrorKind_Null,
|
|
ExpressionErrorKind_MD,
|
|
ExpressionErrorKind_Expr,
|
|
} ExpressionErrorKind;
|
|
|
|
typedef struct Expression_QA Expression_QA;
|
|
struct Expression_QA
|
|
{
|
|
char *q;
|
|
char *a;
|
|
ExpressionErrorKind error_kind;
|
|
MD_u32 error_offset;
|
|
};
|
|
|
|
typedef struct OperatorDescription OperatorDescription;
|
|
struct OperatorDescription{
|
|
MD_String8 s;
|
|
MD_ExprOpr 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, "[]", Postfix, 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, 3) \
|
|
X(AssignAddition, "+=", BinaryRightAssociative, 3) \
|
|
X(AssignSubtraction, "-=", BinaryRightAssociative, 3) \
|
|
X(AssignMultiplication,"*=", BinaryRightAssociative, 3) \
|
|
X(AssignDivision, "/=", BinaryRightAssociative, 3) \
|
|
X(AssignModulo, "%=", BinaryRightAssociative, 3) \
|
|
X(AssignLeftShift, "<<=", BinaryRightAssociative, 3) \
|
|
X(AssignRightShift, ">>=", BinaryRightAssociative, 3) \
|
|
X(AssignBitwiseAnd, "&=", BinaryRightAssociative, 3) \
|
|
X(AssignBitwiseXor, "^=", BinaryRightAssociative, 3) \
|
|
X(AssignBitwiseOr, "|=", BinaryRightAssociative, 3) \
|
|
/* NOTE(mal): These are not in C */ \
|
|
X(PostfixBraceBrace, "{}", Postfix, 18) \
|
|
X(PostfixBracketParen, "[)", Postfix, 18) \
|
|
X(PostfixParenBracket, "(]", Postfix, 18) \
|
|
|
|
#define X(name, token, kind, prec) Op_##name,
|
|
typedef enum{
|
|
Op_Null,
|
|
OPERATORS
|
|
Op_COUNT
|
|
} Op;
|
|
#undef X
|
|
|
|
static MD_String8 node_raw_contents(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;
|
|
{
|
|
Node *last_descendant = node;
|
|
for(;!MD_NodeIsNil(last_descendant->last_child);)
|
|
{
|
|
last_descendant = last_descendant->last_child;
|
|
}
|
|
end = last_descendant->offset + last_descendant->raw_string.size;
|
|
}
|
|
|
|
Node *root = node;
|
|
for(;!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_Expr *node, MD_b32 exclude_outer_parens)
|
|
{
|
|
if(node->op != 0)
|
|
{
|
|
if(!exclude_outer_parens)
|
|
{
|
|
MD_S8ListPush(arena, l, MD_S8Lit("("));
|
|
}
|
|
|
|
MD_ExprOpr *op = node->op;
|
|
if(op->kind == MD_ExprOprKind_Binary || op->kind == MD_ExprOprKind_BinaryRightAssociative)
|
|
{
|
|
parenthesize_exclude_outer(arena, descs, l, node->left, 0);
|
|
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_ExprOprKind_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_ExprOprKind_Postfix)
|
|
{
|
|
parenthesize_exclude_outer(arena, descs, l, node->left, 0);
|
|
|
|
MD_String8 op_s = descs[op->op_id].s;
|
|
if(MD_S8Match(op_s, MD_S8Lit("()"), 0) || MD_S8Match(op_s, MD_S8Lit("[]"), 0) ||
|
|
MD_S8Match(op_s, MD_S8Lit("{}"), 0) || MD_S8Match(op_s, MD_S8Lit("[)"), 0) ||
|
|
MD_S8Match(op_s, MD_S8Lit("(]"), 0))
|
|
{
|
|
MD_u8 buf[5];
|
|
buf[0] = op_s.str[0];
|
|
buf[1] = buf[2] = buf[3] = '.';
|
|
buf[4] = op_s.str[1];
|
|
MD_S8ListPush(arena, l, MD_S8(buf, sizeof(buf)));
|
|
}
|
|
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 & NodeFlag_HasParenLeft)
|
|
{
|
|
MD_S8ListPush(arena, l, MD_S8Lit("("));
|
|
}
|
|
else if(node->md_node->flags & NodeFlag_HasBraceLeft)
|
|
{
|
|
MD_S8ListPush(arena, l, MD_S8Lit("{"));
|
|
}
|
|
else if(node->md_node->flags & NodeFlag_HasBracketLeft)
|
|
{
|
|
MD_S8ListPush(arena, l, MD_S8Lit("["));
|
|
}
|
|
|
|
MD_S8ListPush(arena, l, MD_S8Lit("..."));
|
|
|
|
if(node->md_node->flags & NodeFlag_HasParenRight)
|
|
{
|
|
MD_S8ListPush(arena, l, MD_S8Lit(")"));
|
|
}
|
|
else if(node->md_node->flags & NodeFlag_HasBraceRight){
|
|
MD_S8ListPush(arena, l, MD_S8Lit("}"));
|
|
}
|
|
else if(node->md_node->flags & NodeFlag_HasBracketRight){
|
|
MD_S8ListPush(arena, l, MD_S8Lit("]"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static MD_String8 parenthesize(MD_Arena *arena, OperatorDescription *descs, MD_Expr *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;
|
|
}
|
|
|
|
static MD_MessageList global_message_list = {0};
|
|
static MD_MessageKind global_max_message_kind = MD_MessageKind_Null;
|
|
static void reset_global_messages()
|
|
{
|
|
global_message_list = (MD_MessageList){0};
|
|
global_max_message_kind = MD_MessageKind_Null;
|
|
}
|
|
static void bake_operator_error_handler(MD_MessageKind kind, MD_String8 s)
|
|
{
|
|
// NOTE: Append error to global error list
|
|
MD_Message *message = MD_PushArrayZero(g_arena, MD_Message, 1);
|
|
message->kind = kind;
|
|
message->string = s;
|
|
MD_MessageListPush(&global_message_list, message);
|
|
global_max_message_kind = global_max_message_kind >= kind ? global_max_message_kind: kind;
|
|
}
|
|
|
|
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_ExprOpr){ .op_id = Op_##name, .kind = MD_ExprOprKind_##kind_, .precedence = prec };
|
|
OPERATORS
|
|
#undef X
|
|
|
|
g_arena = MD_ArenaAlloc();
|
|
|
|
/* NOTE: Operator table bake errors */
|
|
{
|
|
MD_ExprOprList operator_list = {0};
|
|
MD_ExprOprTable op_table = {0};
|
|
|
|
MD_String8 plus = MD_S8Lit("+");
|
|
MD_String8 minus = MD_S8Lit("-");
|
|
MD_String8 cast = MD_S8Lit("()");
|
|
Node *plus_node = MD_MakeNode(g_arena, NodeKind_Main, plus, plus, 0);
|
|
Node *cast_node = MD_MakeNode(g_arena, NodeKind_Main, cast, cast, 0);
|
|
Node *minus_node = MD_MakeNode(g_arena, NodeKind_Main, minus, minus, 0);
|
|
Node *plus_node_bis = MD_MakeNode(g_arena, NodeKind_Main, plus, plus, 0);
|
|
|
|
MD_ExprSetBakeOperatorErrorHandler(bake_operator_error_handler);
|
|
|
|
// NOTE: Wrong operator kind
|
|
reset_global_messages();
|
|
operator_list = (MD_ExprOprList){0};
|
|
MD_ExprOprPush(g_arena, &operator_list, MD_ExprOprKind_Null, 1, MD_S8Lit("+"),
|
|
Op_Addition, plus_node);
|
|
op_table = MD_ExprBakeOprTableFromList(g_arena, &operator_list);
|
|
MD_Assert(global_max_message_kind == MD_MessageKind_Warning &&
|
|
global_message_list.node_count == 1);
|
|
|
|
// NOTE: () not as unary postfix
|
|
operator_list = (MD_ExprOprList){0};
|
|
reset_global_messages();
|
|
MD_ExprOprPush(g_arena, &operator_list, MD_ExprOprKind_Prefix, 1, MD_S8Lit("()"),
|
|
23 /* arbitrary MD_ExprOprKind */, cast_node);
|
|
op_table = MD_ExprBakeOprTableFromList(g_arena, &operator_list);
|
|
MD_Assert(global_max_message_kind == MD_MessageKind_Warning &&
|
|
global_message_list.node_count == 1);
|
|
|
|
// NOTE: Repeat operator
|
|
operator_list = (MD_ExprOprList){0};
|
|
reset_global_messages();
|
|
MD_ExprOprPush(g_arena, &operator_list, MD_ExprOprKind_Binary, 1, MD_S8Lit("+"),
|
|
Op_Addition, plus_node);
|
|
MD_ExprOprPush(g_arena, &operator_list, MD_ExprOprKind_Binary, 1, MD_S8Lit("+"),
|
|
Op_Addition, plus_node_bis);
|
|
op_table = MD_ExprBakeOprTableFromList(g_arena, &operator_list);
|
|
MD_Assert(global_max_message_kind == MD_MessageKind_Warning &&
|
|
global_message_list.node_count == 1);
|
|
|
|
operator_list = (MD_ExprOprList){0};
|
|
reset_global_messages();
|
|
// NOTE: Binary-postfix operator conflict
|
|
MD_ExprOprPush(g_arena, &operator_list, MD_ExprOprKind_Binary, 1, MD_S8Lit("+"),
|
|
Op_Addition, plus_node);
|
|
MD_ExprOprPush(g_arena, &operator_list, MD_ExprOprKind_Postfix, 1, MD_S8Lit("+"),
|
|
Op_Addition, plus_node_bis);
|
|
op_table = MD_ExprBakeOprTableFromList(g_arena, &operator_list);
|
|
MD_Assert(global_max_message_kind == MD_MessageKind_Warning
|
|
&& global_message_list.node_count == 1);
|
|
|
|
operator_list = (MD_ExprOprList){0};
|
|
reset_global_messages();
|
|
// NOTE: Same precedence difference associativity conflict
|
|
MD_ExprOprPush(g_arena, &operator_list, MD_ExprOprKind_Binary, 1, MD_S8Lit("+"),
|
|
Op_Addition, plus_node);
|
|
MD_ExprOprPush(g_arena, &operator_list, MD_ExprOprKind_BinaryRightAssociative, 1, MD_S8Lit("-"),
|
|
Op_Addition, minus_node);
|
|
op_table = MD_ExprBakeOprTableFromList(g_arena, &operator_list);
|
|
MD_Assert(global_max_message_kind == MD_MessageKind_Warning &&
|
|
global_message_list.node_count == 1);
|
|
|
|
// NOTE: Multitoken operator
|
|
reset_global_messages();
|
|
operator_list = (MD_ExprOprList){0};
|
|
MD_ExprOprPush(g_arena, &operator_list, MD_ExprOprKind_Prefix, 1, MD_S8Lit("+ +"),
|
|
23 /* arbitrary MD_ExprOprKind */, plus_node);
|
|
op_table = MD_ExprBakeOprTableFromList(g_arena, &operator_list);
|
|
MD_Assert(global_max_message_kind == MD_MessageKind_Warning &&
|
|
global_message_list.node_count == 1);
|
|
|
|
// NOTE: Wrong token kind operator
|
|
reset_global_messages();
|
|
operator_list = (MD_ExprOprList){0};
|
|
MD_ExprOprPush(g_arena, &operator_list, MD_ExprOprKind_Prefix, 1, MD_S8Lit("123"),
|
|
23 /* arbitrary MD_ExprOprKind */, plus_node);
|
|
op_table = MD_ExprBakeOprTableFromList(g_arena, &operator_list);
|
|
MD_Assert(global_max_message_kind == MD_MessageKind_Warning &&
|
|
global_message_list.node_count == 1);
|
|
}
|
|
|
|
MD_ExprOprList operator_list = {0};
|
|
|
|
for(Op op = Op_Null+1; op < Op_COUNT; ++op)
|
|
{
|
|
OperatorDescription *desc = operator_array + op;
|
|
Node *node = MD_MakeNode(g_arena, NodeKind_Main, desc->s, desc->s, 0);
|
|
MD_ExprOprPush(g_arena, &operator_list, desc->op.kind, desc->op.precedence, desc->s,
|
|
op, node);
|
|
}
|
|
|
|
MD_ExprOprTable op_table = MD_ExprBakeOprTableFromList(g_arena, &operator_list);
|
|
|
|
// NOTE(mal): I'm trying something different for expression parser tests. Normally one would take the
|
|
// output of MD_ExprParse and compare it against the expected output expression tree.
|
|
// If instead of that we take the output of MD_ExprParse and translate it back to text while
|
|
// adding some extra parens, we can then compare it to the expected parenthisation of the
|
|
// original input string. We get most of the topological comparison with a simpler test interface.
|
|
Expression_QA tests[] = {
|
|
{ .q = "a + b + c", .a = "(a + b) + c" },
|
|
{ .q = "a + (b + c)", .a = "a + (b + c)" },
|
|
{ .q = "a + b + c + d", .a = "((a + b) + c) + d" },
|
|
{ .q = "-a - -a", .a = "(-a) - (-a)" },
|
|
{ .q = "-(a + -b)", .a = "-(a + (-b))" },
|
|
{ .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] * [1)",.a = "[...] * [...)" },
|
|
{ .q = "a[b+c]", .a = "a[...]" },
|
|
{ .q = "a[a+]", .a = "a[...]", }, // NOTE(mal): No error because "a+" remains unparsed
|
|
{ .q = "a + b[c[d]+e]", .a = "a + (b[...])" },
|
|
{ .q = "a++ + b", .a = "(a++) + b" },
|
|
{ .q = "a.b(c)", .a = "(a . b)(...)" },
|
|
{ .q = "a*b.x+c", .a = "(a * (b . x)) + c" },
|
|
{ .q = "a*(b.x)+c", .a = "(a * (b . x)) + c" },
|
|
{ .q = "a.b*c+d", .a = "((a . b) * c) + d" },
|
|
{ .q = "a.b+c*d", .a = "(a . b) + (c * d)" },
|
|
|
|
{ .q = "a + +b", .a = "a + (+b)" },
|
|
{ .q = "a + + +b", .a = "a + (+(+b))" },
|
|
{ .q = "+ a - -b", .a = "(+a) - (-b)" },
|
|
|
|
{ .q = "!a", .a = "!a" },
|
|
{ .q = "!a + b", .a = "(!a) + b" },
|
|
{ .q = "~a", .a = "~a" },
|
|
|
|
{ .q = "*a+b", .a = "(*a) + b" },
|
|
{ .q = "* + +a", .a = "*(+(+a))" },
|
|
{ .q = "+ + *a", .a = "+(+(*a))" },
|
|
{ .q = "!a", .a = "!a" },
|
|
{ .q = "*f()[10]", .a = "*((f(...))[...])" },
|
|
|
|
{ .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", .a = "a & (&b)" },
|
|
|
|
{ .q = "\"a\" + b + c", .a = "(\"a\" + b) + c" },
|
|
{ .q = "a{b+c}", .a = "a{...}" }, // NOTE(mal): Non-standard postfix set-like operators
|
|
{ .q = "a[b+c)", .a = "a[...)" },
|
|
|
|
{ .q = "(a", .a = "", ExpressionErrorKind_MD, 0},
|
|
{ .q = "a)", .a = "", ExpressionErrorKind_MD, 1},
|
|
{ .q = "/a", .a = "", ExpressionErrorKind_Expr, 0},
|
|
{ .q = "+ /a", .a = "", ExpressionErrorKind_Expr, 2},
|
|
{ .q = "a+", .a = "", ExpressionErrorKind_Expr, 2},
|
|
{ .q = "a 1", .a = "", ExpressionErrorKind_Expr, 2},
|
|
{ .q = "a + (a+)", .a = "", ExpressionErrorKind_Expr, 7},
|
|
};
|
|
|
|
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);
|
|
|
|
ParseResult parse = MD_ParseWholeString(g_arena, MD_S8Lit("test"), q);
|
|
if(parse.errors.max_message_kind == MD_MessageKind_Null)
|
|
{
|
|
MD_ExprParseResult expr_parse = MD_ExprParse(g_arena, &op_table, parse.node->first_child, MD_NilNode());
|
|
if(expr_parse.errors.max_message_kind == MD_MessageKind_Null)
|
|
{
|
|
MD_String8 parser_answer = parenthesize(g_arena, operator_array, expr_parse.expr);
|
|
if(!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));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(test.error_kind == ExpressionErrorKind_Expr)
|
|
{
|
|
for(MD_Message *message = expr_parse.errors.first; message; message = message->next)
|
|
{
|
|
if(message->node->offset != test.error_offset)
|
|
{
|
|
printf("Example %d : \"%.*s\". Expected error on character %d; got character %ld instead\n",
|
|
i_test, MD_S8VArg(q), test.error_offset, (long)message->node->offset);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MD_Message *message = expr_parse.errors.first;
|
|
printf("Example %d : \"%.*s\". Unexpected Expr parsing error: \"%.*s\"\n", i_test, MD_S8VArg(q),
|
|
MD_S8VArg(message->string));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(test.error_kind == ExpressionErrorKind_MD)
|
|
{
|
|
for(MD_Message *message = parse.errors.first; message; message = message->next)
|
|
{
|
|
if(message->node->offset != test.error_offset)
|
|
{
|
|
printf("Example %d : \"%.*s\". Expected error on character %d; got character %ld instead\n",
|
|
i_test, MD_S8VArg(q), test.error_offset, (long)message->node->offset);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
printf("Example %d : \"%.*s\". Unexpected MD parsing error\n", i_test, MD_S8VArg(q));
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|