From c0a50bfe270dd685adb8a0f58b8f8b34c1282231 Mon Sep 17 00:00:00 2001 From: Miguel Lechon Date: Thu, 9 Sep 2021 12:21:03 +0200 Subject: [PATCH] Discard and warn against conflicting expression operators. --- source/md.c | 238 ++++++++++++++++++++++++++------------- source/md.h | 6 +- tests/expression_tests.c | 59 +++++++--- 3 files changed, 205 insertions(+), 98 deletions(-) diff --git a/source/md.c b/source/md.c index e103a79..91baa61 100644 --- a/source/md.c +++ b/source/md.c @@ -2976,7 +2976,8 @@ MD_NodeDeepMatch(MD_Node *a, MD_Node *b, MD_MatchFlags flags) 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){ + MD_u64 precedence, MD_Node *md_node) +{ MD_ExprOperatorNode *node = MD_PushArray(arena, MD_ExprOperatorNode, 1); MD_QueuePush(list->first, list->last, node); list->count += 1; @@ -2986,66 +2987,17 @@ MD_ExprOperatorPush(MD_Arena *arena, MD_ExprOperatorList *list, node->op.md_node = md_node; } -MD_FUNCTION_IMPL MD_ExprOperatorTable -MD_ExprBakeOperatorTableFromList(MD_Arena *arena, MD_ExprOperatorList *list){ - 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); -} - -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){ +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){ + !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) { @@ -3058,17 +3010,122 @@ _MD_ExprOperatorMatch(MD_ExprOperatorTable *table, MD_ExprOperatorKind kind, MD_ return result; } -MD_FUNCTION_IMPL void _MD_CtxAdvance(_MD_ExprParseCtx *ctx){ +MD_FUNCTION_IMPL MD_ExprOperatorTable +MD_ExprBakeOperatorTableFromList(MD_Arena *arena, MD_ExprOperatorList *list) +{ + MD_ExprOperatorTable result = MD_ZERO_STRUCT; + + for(MD_ExprOperatorNode *op_node = list->first; op_node; op_node = op_node->next) + { + MD_ExprOperator op = op_node->op; + MD_String8 op_s = op.md_node->string; + + MD_String8 error_str = MD_ZERO_STRUCT; + if(op.kind != MD_ExprOperatorKind_Prefix && op.kind != MD_ExprOperatorKind_Postfix && + op.kind != MD_ExprOperatorKind_Binary && op.kind != MD_ExprOperatorKind_BinaryRightAssociative) + { + error_str = MD_S8Fmt(arena, "Invalid operator kind."); + } + else + { + for(MD_ExprOperatorNode *op_node2 = list->first; op_node2 != op_node; op_node2 = op_node2->next) + { // NOTE(mal): O(n^2) + MD_ExprOperator op2 = op_node2->op; + MD_String8 op2_s = op2.md_node->string; + if(op.precedence == op2.precedence && + ((op.kind == MD_ExprOperatorKind_Binary && op2.kind == MD_ExprOperatorKind_BinaryRightAssociative) || + (op.kind == MD_ExprOperatorKind_BinaryRightAssociative && op2.kind == MD_ExprOperatorKind_Binary))) + { + error_str = MD_S8Fmt(arena, "Ignored binary operator \"%.*s\" because another binary operator" + "has the same precedence and different associativity", MD_S8VArg(op_s)); + } + else if(MD_S8Match(op_s, op2_s, 0)) + { + if(op.kind == op2.kind) + { + error_str = MD_S8Fmt(arena, "Ignored repeat operator \"%.*s\".", MD_S8VArg(op_s)); + } + else if(op.kind != MD_ExprOperatorKind_Prefix && op2.kind != MD_ExprOperatorKind_Prefix) + { + error_str = MD_S8Fmt(arena, "Ignored conflicting repeat operator \"%.*s\". There can't be" + "more than one posfix/binary operator associated to the same token", + MD_S8VArg(op_s)); + } + } + } + } + + if(error_str.size == 0) + { + MD_ExprOperatorList *list = result.table+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; + + if(op.kind == MD_ExprOperatorKind_Postfix && MD_S8Match(op_s, MD_S8Lit("()"), 0)) + { + result.call_op = &op_node_copy->op; + } + else if(op.kind == MD_ExprOperatorKind_Binary && MD_S8Match(op_s, MD_S8Lit("[]"), 0)) + { + result.subscript_op = &op_node_copy->op; + } + } + else + { + MD_Message *error = MD_MakeNodeError(arena, op.md_node, MD_MessageKind_Warning, error_str); + MD_MessageListPush(&result.errors, error); + } + } + + return(result); +} + +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 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_ExprOperator **op_out) +{ MD_b32 result = 0; - if(!MD_NodeIsNil(ctx->first)){ + 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){ + if(op && op->precedence >= min_precedence) + { result = 1; *op_out = op; _MD_CtxAdvance(ctx); @@ -3078,7 +3135,8 @@ MD_FUNCTION_IMPL MD_b32 _MD_ExprOperatorConsumed(_MD_ExprParseCtx *ctx, MD_ExprO } MD_FUNCTION_IMPL _MD_ExprParseCtx -_MD_ExprParse_MakeContext(MD_ExprOperatorTable *op_table, MD_Node *first, MD_Node *one_past_last){ +_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; @@ -3087,35 +3145,41 @@ _MD_ExprParse_MakeContext(MD_ExprOperatorTable *op_table, MD_Node *first, MD_Nod } MD_FUNCTION_IMPL MD_ExprParseResult -_MD_ExprParse_Atom(MD_Arena *arena, _MD_ExprParseCtx *ctx){ +_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 + 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)){ + (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)){ + 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){ + 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)){ + 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); @@ -3130,26 +3194,31 @@ _MD_ExprParse_Atom(MD_Arena *arena, _MD_ExprParseCtx *ctx){ } MD_FUNCTION_IMPL MD_ExprParseResult -_MD_ExprParse_Ctx_MinPrecedence(MD_Arena *arena, _MD_ExprParseCtx *ctx, MD_u32 min_precedence){ +_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){ + 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){ + 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){ + 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{ @@ -3158,10 +3227,12 @@ _MD_ExprParse_Ctx_MinPrecedence(MD_Arena *arena, _MD_ExprParseCtx *ctx, MD_u32 m } } else if(_MD_ExprOperatorConsumed(ctx, MD_ExprOperatorKind_Binary, min_precedence, &op) || - _MD_ExprOperatorConsumed(ctx, MD_ExprOperatorKind_BinaryRightAssociative, 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){ + 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{ @@ -3170,11 +3241,13 @@ _MD_ExprParse_Ctx_MinPrecedence(MD_Arena *arena, _MD_ExprParseCtx *ctx, MD_u32 m } } 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 + 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)){ + else if(_MD_ExprOperatorConsumed(ctx, MD_ExprOperatorKind_Postfix, min_precedence, &op)) + { result.node = _MD_Expr_Make(arena, op, node, result.node, 0); } else{ @@ -3187,13 +3260,16 @@ _MD_ExprParse_Ctx_MinPrecedence(MD_Arena *arena, _MD_ExprParseCtx *ctx, MD_u32 m } MD_FUNCTION_IMPL MD_ExprParseResult -MD_ExprParse(MD_Arena *arena, MD_ExprOperatorTable *op_table, MD_Node *first, MD_Node *one_past_last){ +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){ + 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); diff --git a/source/md.h b/source/md.h index 373c7c8..d949bed 100644 --- a/source/md.h +++ b/source/md.h @@ -719,11 +719,9 @@ struct MD_ExprOperatorTable { // 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; + MD_ExprOperator *call_op; // TODO: Move elsewhere + MD_ExprOperator *subscript_op; }; typedef struct MD_ExprNode MD_ExprNode; diff --git a/tests/expression_tests.c b/tests/expression_tests.c index ed8beb3..22733e5 100644 --- a/tests/expression_tests.c +++ b/tests/expression_tests.c @@ -1,15 +1,5 @@ //$ 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" @@ -86,7 +76,6 @@ typedef enum{ } Op; #undef X - static MD_String8 node_raw_contents(MD_Node *node, MD_b32 exclude_outer){ MD_String8 result = {0}; @@ -194,7 +183,6 @@ static MD_String8 parenthesize(MD_Arena *arena, OperatorDescription *descs, MD_E return result; } - int main(void){ OperatorDescription operator_array[Op_COUNT] = {0}; #define X(name, token, kind_, prec) \ @@ -204,9 +192,54 @@ int main(void){ #undef X arena = MD_ArenaAlloc(1ull << 40); + + /* NOTE: Operator table bake errors */ { + MD_ExprOperatorList operator_list = {0}; + MD_ExprOperatorTable op_table = {0}; + + MD_String8 plus = MD_S8Lit("+"); + MD_String8 minus = MD_S8Lit("-"); + MD_Node *plus_node = MD_MakeNode(arena, MD_NodeKind_Main, plus, plus, 0); + MD_Node *minus_node = MD_MakeNode(arena, MD_NodeKind_Main, minus, minus, 0); + MD_Node *plus_node_bis = MD_MakeNode(arena, MD_NodeKind_Main, plus, plus, 0); + + // NOTE: Wrong operator kind + operator_list = (MD_ExprOperatorList){0}; + MD_ExprOperatorPush(arena, &operator_list, Op_Addition, MD_ExprOperatorKind_Null, 1, plus_node); + op_table = MD_ExprBakeOperatorTableFromList(arena, &operator_list); + MD_Assert(op_table.errors.max_message_kind = MD_MessageKind_Warning && op_table.errors.node_count == 1 && + op_table.errors.first->node == plus_node); + + // NOTE: Repeat operator + operator_list = (MD_ExprOperatorList){0}; + MD_ExprOperatorPush(arena, &operator_list, Op_Addition, MD_ExprOperatorKind_Binary, 1, plus_node); + MD_ExprOperatorPush(arena, &operator_list, Op_Addition, MD_ExprOperatorKind_Binary, 1, plus_node_bis); + op_table = MD_ExprBakeOperatorTableFromList(arena, &operator_list); + MD_Assert(op_table.errors.max_message_kind = MD_MessageKind_Warning && op_table.errors.node_count == 1 && + op_table.errors.first->node == plus_node_bis); + + operator_list = (MD_ExprOperatorList){0}; + // NOTE: Binary-postfix operator conflict + MD_ExprOperatorPush(arena, &operator_list, Op_Addition, MD_ExprOperatorKind_Binary, 1, plus_node); + MD_ExprOperatorPush(arena, &operator_list, Op_Addition, MD_ExprOperatorKind_Postfix, 1, plus_node_bis); + op_table = MD_ExprBakeOperatorTableFromList(arena, &operator_list); + MD_Assert(op_table.errors.max_message_kind = MD_MessageKind_Warning && op_table.errors.node_count == 1 && + op_table.errors.first->node == plus_node_bis); + + operator_list = (MD_ExprOperatorList){0}; + // NOTE: Same precedence difference associativity conflict + MD_ExprOperatorPush(arena, &operator_list, Op_Addition, MD_ExprOperatorKind_Binary, 1, plus_node); + MD_ExprOperatorPush(arena, &operator_list, Op_Addition, MD_ExprOperatorKind_BinaryRightAssociative, + 1, minus_node); + op_table = MD_ExprBakeOperatorTableFromList(arena, &operator_list); + MD_Assert(op_table.errors.max_message_kind = MD_MessageKind_Warning && op_table.errors.node_count == 1 && + op_table.errors.first->node == minus_node); + + } + MD_ExprOperatorList operator_list = {0}; - for(Op op= Op_Null+1; op < Op_COUNT; ++op){ + 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);