Skip to content

Commit

Permalink
Merge pull request #395 from MrAnno/filterx-null-assign
Browse files Browse the repository at this point in the history
FilterX null coalescing assignments
  • Loading branch information
alltilla authored Nov 29, 2024
2 parents b8f7853 + 3847726 commit 32386f5
Show file tree
Hide file tree
Showing 11 changed files with 305 additions and 70 deletions.
2 changes: 1 addition & 1 deletion lib/cfg-grammar.y
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ main_location_print (FILE *yyo, YYLTYPE const * const yylocp)
%left ';'

/* operators in the filter language, the order of this determines precedence */
%right KW_ASSIGN 9000, KW_PLUS_ASSIGN 9001
%right KW_ASSIGN 9000, KW_PLUS_ASSIGN 9001, KW_NULLV_ASSIGN 9002
%right '?' ':'
%right KW_NULL_COALESCING
%left KW_OR 9010
Expand Down
1 change: 1 addition & 0 deletions lib/cfg-lex.l
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,7 @@ filterx_word [^ \#'"/\(\)\{\}\[\]\\;\r\n\t,|\.@:]
<filterx>=~ { return KW_REGEXP_MATCH; }
<filterx>!~ { return KW_REGEXP_NOMATCH; }
<filterx>\?\? { return KW_NULL_COALESCING; }
<filterx>=\?\? { return KW_NULLV_ASSIGN; }
<INITIAL,filterx>(-|\+)?{digit}+\.{digit}+ { yylval->fnum = strtod(yytext, NULL); return LL_FLOAT; }
<INITIAL,filterx>0x{xdigit}+ {
Expand Down
70 changes: 60 additions & 10 deletions lib/filterx/expr-assign.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,15 @@
#include "filterx/object-primitive.h"
#include "filterx/filterx-ref.h"
#include "filterx/object-json.h"
#include "filterx/filterx-object-istype.h"
#include "filterx/filterx-eval.h"
#include "filterx/object-null.h"
#include "filterx/object-message-value.h"
#include "scratch-buffers.h"

static FilterXObject *
_eval(FilterXExpr *s)
static inline FilterXObject *
_assign(FilterXBinaryOp *self, FilterXObject *value)
{
FilterXBinaryOp *self = (FilterXBinaryOp *) s;

FilterXObject *value = filterx_expr_eval(self->rhs);

if (!value)
return NULL;

/* TODO: create ref unconditionally after implementing hierarchical CoW for JSON types
* (or after creating our own dict/list repr) */
if (!value->weak_referenced)
Expand All @@ -52,14 +49,67 @@ _eval(FilterXExpr *s)
return value;
}

static inline FilterXObject *
_suppress_error(void)
{
msg_debug("FILTERX null coalesce assignment supressing error", filterx_format_last_error());
filterx_eval_clear_errors();

return filterx_null_new();
}

static FilterXObject *
_nullv_assign_eval(FilterXExpr *s)
{
FilterXBinaryOp *self = (FilterXBinaryOp *) s;

FilterXObject *value = filterx_expr_eval(self->rhs);

if (!value || filterx_object_is_type(value, &FILTERX_TYPE_NAME(null))
|| (filterx_object_is_type(value, &FILTERX_TYPE_NAME(message_value))
&& filterx_message_value_get_type(value) == LM_VT_NULL))
{
if (!value)
return _suppress_error();

return value;
}

return _assign(self, value);
}

static FilterXObject *
_assign_eval(FilterXExpr *s)
{
FilterXBinaryOp *self = (FilterXBinaryOp *) s;

FilterXObject *value = filterx_expr_eval(self->rhs);

if (!value)
return NULL;

return _assign(self, value);
}

FilterXExpr *
filterx_nullv_assign_new(FilterXExpr *lhs, FilterXExpr *rhs)
{
FilterXBinaryOp *self = g_new0(FilterXBinaryOp, 1);

filterx_binary_op_init_instance(self, lhs, rhs);
self->super.eval = _nullv_assign_eval;
self->super.ignore_falsy_result = TRUE;
return &self->super;
}

/* NOTE: takes the object reference */
FilterXExpr *
filterx_assign_new(FilterXExpr *lhs, FilterXExpr *rhs)
{
FilterXBinaryOp *self = g_new0(FilterXBinaryOp, 1);

filterx_binary_op_init_instance(self, lhs, rhs);
self->super.eval = _eval;
self->super.eval = _assign_eval;
self->super.ignore_falsy_result = TRUE;
return &self->super;
}
2 changes: 1 addition & 1 deletion lib/filterx/expr-assign.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@
#include "filterx/filterx-expr.h"

FilterXExpr *filterx_assign_new(FilterXExpr *lhs, FilterXExpr *rhs);

FilterXExpr *filterx_nullv_assign_new(FilterXExpr *lhs, FilterXExpr *rhs);

#endif
134 changes: 102 additions & 32 deletions lib/filterx/expr-set-subscript.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@
#include "filterx/object-primitive.h"
#include "filterx/filterx-eval.h"
#include "filterx/filterx-ref.h"
#include "filterx/filterx-object-istype.h"
#include "filterx/filterx-eval.h"
#include "filterx/object-null.h"
#include "filterx/object-message-value.h"
#include "scratch-buffers.h"

typedef struct _FilterXSetSubscript
Expand All @@ -34,60 +38,109 @@ typedef struct _FilterXSetSubscript
FilterXExpr *new_value;
} FilterXSetSubscript;

static inline FilterXObject *
_set_subscript(FilterXSetSubscript *self, FilterXObject *object, FilterXObject *key, FilterXObject **new_value)
{
if (object->readonly)
{
filterx_eval_push_error("Object set-subscript failed, object is readonly", &self->super, key);
return NULL;
}

/* TODO: create ref unconditionally after implementing hierarchical CoW for JSON types
* (or after creating our own dict/list repr) */
if (!(*new_value)->weak_referenced)
{
*new_value = filterx_ref_new(*new_value);
}

FilterXObject *cloned = filterx_object_clone(*new_value);
filterx_object_unref(*new_value);
*new_value = NULL;

if (!filterx_object_set_subscript(object, key, &cloned))
{
filterx_eval_push_error("Object set-subscript failed", &self->super, key);
filterx_object_unref(cloned);
return NULL;
}

return cloned;
}

static inline FilterXObject *
_suppress_error(void)
{
msg_debug("FILTERX null coalesce assignment supressing error", filterx_format_last_error());
filterx_eval_clear_errors();

return filterx_null_new();
}

static FilterXObject *
_eval(FilterXExpr *s)
_nullv_set_subscript_eval(FilterXExpr *s)
{
FilterXSetSubscript *self = (FilterXSetSubscript *) s;
FilterXObject *result = NULL;
FilterXObject *new_value = NULL, *key = NULL, *object = NULL;
FilterXObject *key = NULL;

FilterXObject *new_value = filterx_expr_eval(self->new_value);
if (!new_value || filterx_object_is_type(new_value, &FILTERX_TYPE_NAME(null))
|| (filterx_object_is_type(new_value, &FILTERX_TYPE_NAME(message_value))
&& filterx_message_value_get_type(new_value) == LM_VT_NULL))
{
if (!new_value)
return _suppress_error();

object = filterx_expr_eval_typed(self->object);
return new_value;
}

FilterXObject *object = filterx_expr_eval_typed(self->object);
if (!object)
return NULL;
goto exit;

if (self->key)
{
key = filterx_expr_eval(self->key);
if (!key)
goto exit;
}
else
{
/* append */
key = NULL;
}

if (object->readonly)
{
filterx_eval_push_error("Object set-subscript failed, object is readonly", s, key);
goto exit;
}
result = _set_subscript(self, object, key, &new_value);

new_value = filterx_expr_eval(self->new_value);
exit:
filterx_object_unref(new_value);
filterx_object_unref(key);
filterx_object_unref(object);
return result;
}

static FilterXObject *
_set_subscript_eval(FilterXExpr *s)
{
FilterXSetSubscript *self = (FilterXSetSubscript *) s;
FilterXObject *result = NULL;
FilterXObject *key = NULL;

FilterXObject *new_value = filterx_expr_eval(self->new_value);
if (!new_value)
return NULL;

FilterXObject *object = filterx_expr_eval_typed(self->object);
if (!object)
goto exit;

/* TODO: create ref unconditionally after implementing hierarchical CoW for JSON types
* (or after creating our own dict/list repr) */
if (!new_value->weak_referenced)
if (self->key)
{
new_value = filterx_ref_new(new_value);
key = filterx_expr_eval(self->key);
if (!key)
goto exit;
}

FilterXObject *cloned = filterx_object_clone(new_value);
filterx_object_unref(new_value);

if (!filterx_object_set_subscript(object, key, &cloned))
{
filterx_eval_push_error("Object set-subscript failed", s, key);
filterx_object_unref(cloned);
}
else
{
result = cloned;
}
result = _set_subscript(self, object, key, &new_value);

exit:
filterx_object_unref(new_value);
filterx_object_unref(key);
filterx_object_unref(object);
return result;
Expand Down Expand Up @@ -139,13 +192,30 @@ _free(FilterXExpr *s)
filterx_expr_free_method(s);
}

FilterXExpr *
filterx_nullv_set_subscript_new(FilterXExpr *object, FilterXExpr *key, FilterXExpr *new_value)
{
FilterXSetSubscript *self = g_new0(FilterXSetSubscript, 1);

filterx_expr_init_instance(&self->super);
self->super.eval = _nullv_set_subscript_eval;
self->super.init = _init;
self->super.deinit = _deinit;
self->super.free_fn = _free;
self->object = object;
self->key = key;
self->new_value = new_value;
self->super.ignore_falsy_result = TRUE;
return &self->super;
}

FilterXExpr *
filterx_set_subscript_new(FilterXExpr *object, FilterXExpr *key, FilterXExpr *new_value)
{
FilterXSetSubscript *self = g_new0(FilterXSetSubscript, 1);

filterx_expr_init_instance(&self->super);
self->super.eval = _eval;
self->super.eval = _set_subscript_eval;
self->super.init = _init;
self->super.deinit = _deinit;
self->super.free_fn = _free;
Expand Down
2 changes: 1 addition & 1 deletion lib/filterx/expr-set-subscript.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@
#include "filterx/filterx-expr.h"

FilterXExpr *filterx_set_subscript_new(FilterXExpr *object, FilterXExpr *key, FilterXExpr *new_value);

FilterXExpr *filterx_nullv_set_subscript_new(FilterXExpr *object, FilterXExpr *key, FilterXExpr *new_value);

#endif
Loading

0 comments on commit 32386f5

Please sign in to comment.