Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FilterX null coalescing assignments #395

Merged
merged 10 commits into from
Nov 29, 2024
Merged
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