summaryrefslogtreecommitdiffstats
path: root/src/libutil/expression.c
diff options
context:
space:
mode:
authorVsevolod Stakhov <vsevolod@highsecure.ru>2015-03-27 12:51:37 +0000
committerVsevolod Stakhov <vsevolod@highsecure.ru>2015-03-27 12:51:37 +0000
commit2fe9547e9636058a8863fc149c97b2f38903b824 (patch)
treeddb6c0ec5583dffc7cb895f16530005ad52a56d8 /src/libutil/expression.c
parent3728212eeea755b5a71e38a9e8dbd832ba3b4b19 (diff)
downloadrspamd-2fe9547e9636058a8863fc149c97b2f38903b824.tar.gz
rspamd-2fe9547e9636058a8863fc149c97b2f38903b824.zip
Add AST processing routines.
Diffstat (limited to 'src/libutil/expression.c')
-rw-r--r--src/libutil/expression.c158
1 files changed, 158 insertions, 0 deletions
diff --git a/src/libutil/expression.c b/src/libutil/expression.c
index c18783d79..8046a706a 100644
--- a/src/libutil/expression.c
+++ b/src/libutil/expression.c
@@ -702,6 +702,164 @@ err:
return FALSE;
}
+static gboolean
+rspamd_ast_node_done (struct rspamd_expression_elt *elt,
+ struct rspamd_expression_elt *parelt, gint acc, gint lim)
+{
+ gboolean ret = FALSE;
+
+ g_assert (elt->type == ELT_OP);
+
+ switch (elt->p.op) {
+ case OP_NOT:
+ ret = TRUE;
+ break;
+ case OP_PLUS:
+ if (parelt && lim > 0) {
+ g_assert (parelt->type == ELT_OP);
+
+ switch (parelt->p.op) {
+ case OP_GE:
+ ret = acc >= lim;
+ break;
+ case OP_GT:
+ ret = acc > lim;
+ break;
+ case OP_LE:
+ ret = acc <= lim;
+ break;
+ case OP_LT:
+ ret = acc < lim;
+ break;
+ default:
+ ret = FALSE;
+ break;
+ }
+ }
+ break;
+ case OP_GE:
+ ret = acc >= lim;
+ break;
+ case OP_GT:
+ ret = acc > lim;
+ break;
+ case OP_LE:
+ ret = acc <= lim;
+ break;
+ case OP_LT:
+ ret = acc < lim;
+ break;
+ case OP_MULT:
+ case OP_AND:
+ ret = !acc;
+ break;
+ case OP_OR:
+ ret = !!acc;
+ break;
+ default:
+ g_assert (0);
+ break;
+ }
+
+ return ret;
+}
+
+static gint
+rspamd_ast_do_op (struct rspamd_expression_elt *elt, gint val, gint acc)
+{
+ gint ret = val;
+
+ g_assert (elt->type == ELT_OP);
+
+ switch (elt->p.op) {
+ case OP_NOT:
+ ret = !val;
+ break;
+ case OP_PLUS:
+ ret = acc + val;
+ break;
+ case OP_GE:
+ ret = acc >= val;
+ break;
+ case OP_GT:
+ ret = acc > val;
+ break;
+ case OP_LE:
+ ret = acc <= val;
+ break;
+ case OP_LT:
+ ret = acc < val;
+ break;
+ case OP_MULT:
+ case OP_AND:
+ ret = acc && val;
+ break;
+ case OP_OR:
+ ret = acc || val;
+ break;
+ default:
+ g_assert (0);
+ break;
+ }
+
+ return ret;
+}
+
+static gint
+rspamd_ast_process_node (struct rspamd_expression *expr, GNode *node,
+ gpointer data)
+{
+ struct rspamd_expression_elt *elt, *celt, *parelt;
+ GNode *cld;
+ gint acc = 0, lim = G_MININT, val;
+
+ elt = node->data;
+
+ switch (elt->type) {
+ case ELT_ATOM:
+ if (elt->flags & RSPAMD_EXPR_FLAG_PROCESSED) {
+ return elt->value;
+ }
+ else {
+ elt->value = expr->subr->process (data, elt->p.atom);
+ elt->flags |= RSPAMD_EXPR_FLAG_PROCESSED;
+ }
+ break;
+ case ELT_LIMIT:
+ return elt->p.lim.val;
+ break;
+ case ELT_OP:
+ g_assert (node->children != NULL);
+ cld = node->children;
+
+ /* Try to find limit at the parent node */
+ if (node->parent) {
+ parelt = node->parent->data;
+ celt = node->parent->children->data;
+
+ if (celt->type == ELT_LIMIT) {
+ lim = celt->value;
+ }
+ }
+
+ DL_FOREACH (node->children, cld) {
+ celt = cld->data;
+
+ /* Save limit if we've found it */
+ val = rspamd_ast_process_node (expr, cld, data);
+
+ acc = rspamd_ast_do_op (elt, val, acc);
+
+ if (rspamd_ast_node_done (elt, parelt, acc, lim)) {
+ return acc;
+ }
+ }
+ break;
+ }
+
+ return acc;
+}
+
#define CHOSE_OPERAND(e1, e2) ((e1)->flags & RSPAMD_EXPR_FLAG_PROCESSED ? (e1) : \
((e2)->flags & RSPAMD_EXPR_FLAG_PROCESSED) ? (e2) : \
((e1)->p.atom->priority >= (e2)->p.atom->priority) ? \