[template-glib] expr-parser.y: allow calling anonymous functions



commit 8993d1594c2963c6c184a3ef578541b61918e3d5
Author: Christian Hergert <chergert redhat com>
Date:   Wed May 4 17:47:38 2022 -0700

    expr-parser.y: allow calling anonymous functions

 src/tmpl-expr-eval.c    | 90 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/tmpl-expr-parser.y  |  6 ++++
 src/tmpl-expr-private.h |  9 +++++
 src/tmpl-expr-types.h   |  1 +
 src/tmpl-expr.c         | 21 ++++++++++++
 src/tmpl-expr.h         |  3 ++
 tests/test1.script      |  4 +++
 7 files changed, 134 insertions(+)
---
diff --git a/src/tmpl-expr-eval.c b/src/tmpl-expr-eval.c
index 98081ab..e5ea6f4 100644
--- a/src/tmpl-expr-eval.c
+++ b/src/tmpl-expr-eval.c
@@ -1089,6 +1089,93 @@ cleanup:
   return ret;
 }
 
+static gboolean
+tmpl_expr_anon_fn_call_eval (TmplExprAnonFnCall  *node,
+                             TmplScope           *scope,
+                             GValue              *return_value,
+                             GError             **error)
+{
+  char **args;
+  TmplExpr *params = NULL;
+  TmplScope *local_scope = NULL;
+  gboolean ret = FALSE;
+  gint n_args = 0;
+
+  g_assert (node != NULL);
+  g_assert (scope != NULL);
+  g_assert (return_value != NULL);
+
+  if (node->anon->any.type != TMPL_EXPR_FUNC)
+    {
+      g_set_error_literal (error,
+                           TMPL_ERROR,
+                           TMPL_ERROR_NOT_A_FUNCTION,
+                           "Not a function to evaluate");
+      return FALSE;
+    }
+
+  args = node->anon->func.symlist;
+  n_args = args ? g_strv_length (args) : 0;
+  local_scope = tmpl_scope_new_with_parent (scope);
+  params = node->params;
+
+  for (guint i = 0; i < n_args; i++)
+    {
+      const gchar *arg = args[i];
+      GValue value = G_VALUE_INIT;
+
+      if (params == NULL)
+        {
+          g_set_error (error,
+                       TMPL_ERROR,
+                       TMPL_ERROR_SYNTAX_ERROR,
+                       "Function takes %d arguments, got %d",
+                       n_args, i);
+          return FALSE;
+        }
+
+      if (params->any.type == TMPL_EXPR_ARGS)
+        {
+          TmplExprSimple *simple = (TmplExprSimple *)params;
+
+          if (!tmpl_expr_eval_internal (simple->left, local_scope, &value, error))
+            goto cleanup;
+
+          params = simple->right;
+        }
+      else
+        {
+          if (!tmpl_expr_eval_internal (params, local_scope, &value, error))
+            goto cleanup;
+
+          params = NULL;
+        }
+
+      tmpl_scope_set_value (local_scope, arg, &value);
+      TMPL_CLEAR_VALUE (&value);
+    }
+
+  if (params != NULL)
+    {
+      g_set_error (error,
+                   TMPL_ERROR,
+                   TMPL_ERROR_SYNTAX_ERROR,
+                   "Function takes %d params",
+                   n_args);
+      goto cleanup;
+    }
+
+  if (!tmpl_expr_eval_internal (node->anon, local_scope, return_value, error))
+    goto cleanup;
+
+  ret = TRUE;
+
+cleanup:
+  g_clear_pointer (&local_scope, tmpl_scope_unref);
+
+  return ret;
+}
+
 static gboolean
 tmpl_expr_user_fn_call_eval (TmplExprUserFnCall  *node,
                              TmplScope           *scope,
@@ -1325,6 +1412,9 @@ tmpl_expr_eval_internal (TmplExpr   *node,
     case TMPL_EXPR_FN_CALL:
       return tmpl_expr_fn_call_eval ((TmplExprFnCall *)node, scope, return_value, error);
 
+    case TMPL_EXPR_ANON_FN_CALL:
+      return tmpl_expr_anon_fn_call_eval ((TmplExprAnonFnCall *)node, scope, return_value, error);
+
     case TMPL_EXPR_USER_FN_CALL:
       return tmpl_expr_user_fn_call_eval ((TmplExprUserFnCall *)node, scope, return_value, error);
 
diff --git a/src/tmpl-expr-parser.y b/src/tmpl-expr-parser.y
index d8f47af..a10ebf7 100644
--- a/src/tmpl-expr-parser.y
+++ b/src/tmpl-expr-parser.y
@@ -285,6 +285,12 @@ exp: exp CMP exp {
     $$ = tmpl_expr_new_user_fn_call ($1, NULL);
     g_free ($1);
   }
+  | exp '(' ')' {
+    $$ = tmpl_expr_new_anon_call ($1, NULL);
+  }
+  | exp '(' explist ')' {
+    $$ = tmpl_expr_new_anon_call ($1, $3);
+  }
   | '!' exp {
     $$ = tmpl_expr_new_invert_boolean ($2);
   }
diff --git a/src/tmpl-expr-private.h b/src/tmpl-expr-private.h
index 4d7a19a..d9d733e 100644
--- a/src/tmpl-expr-private.h
+++ b/src/tmpl-expr-private.h
@@ -60,6 +60,14 @@ typedef struct
   TmplExpr      *params;
 } TmplExprUserFnCall;
 
+typedef struct
+{
+  TmplExprType   type;
+  volatile gint  ref_count;
+  TmplExpr      *anon;
+  TmplExpr      *params;
+} TmplExprAnonFnCall;
+
 typedef struct
 {
   TmplExprType   type;
@@ -158,6 +166,7 @@ union _TmplExpr
   TmplExprSimple       simple;
   TmplExprGiCall       gi_call;
   TmplExprFnCall       fn_call;
+  TmplExprAnonFnCall   anon_fn_call;
   TmplExprUserFnCall   user_fn_call;
   TmplExprFlow         flow;
   TmplExprNumber       number;
diff --git a/src/tmpl-expr-types.h b/src/tmpl-expr-types.h
index 6862ff2..4e4ad97 100644
--- a/src/tmpl-expr-types.h
+++ b/src/tmpl-expr-types.h
@@ -67,6 +67,7 @@ typedef enum
   TMPL_EXPR_SYMBOL_REF,
   TMPL_EXPR_SYMBOL_ASSIGN,
   TMPL_EXPR_FN_CALL,
+  TMPL_EXPR_ANON_FN_CALL,
   TMPL_EXPR_USER_FN_CALL,
   TMPL_EXPR_GETATTR,
   TMPL_EXPR_SETATTR,
diff --git a/src/tmpl-expr.c b/src/tmpl-expr.c
index d5a1422..d040f81 100644
--- a/src/tmpl-expr.c
+++ b/src/tmpl-expr.c
@@ -92,6 +92,11 @@ tmpl_expr_destroy (TmplExpr *self)
       g_clear_pointer (&self->user_fn_call.params, tmpl_expr_unref);
       break;
 
+    case TMPL_EXPR_ANON_FN_CALL:
+      g_clear_pointer (&self->anon_fn_call.anon, tmpl_expr_unref);
+      g_clear_pointer (&self->user_fn_call.params, tmpl_expr_unref);
+      break;
+
     case TMPL_EXPR_GETATTR:
       g_clear_pointer (&self->getattr.attr, g_free);
       g_clear_pointer (&self->getattr.left, tmpl_expr_unref);
@@ -412,6 +417,22 @@ tmpl_expr_new_func (char      *name,
   return (TmplExpr *)ret;
 }
 
+TmplExpr *
+tmpl_expr_new_anon_call (TmplExpr *func,
+                         TmplExpr *params)
+{
+  TmplExprAnonFnCall *ret;
+
+  g_return_val_if_fail (func != NULL, NULL);
+  g_return_val_if_fail (func->any.type == TMPL_EXPR_FUNC, NULL);
+
+  ret = tmpl_expr_new (TMPL_EXPR_ANON_FN_CALL);
+  ret->anon = func;
+  ret->params = params;
+
+  return (TmplExpr *)ret;
+}
+
 TmplExpr *
 tmpl_expr_new_nop (void)
 {
diff --git a/src/tmpl-expr.h b/src/tmpl-expr.h
index a57b0b4..b9951e6 100644
--- a/src/tmpl-expr.h
+++ b/src/tmpl-expr.h
@@ -91,6 +91,9 @@ TmplExpr *tmpl_expr_new_func          (char             *name,
                                        char            **symlist,
                                        TmplExpr         *list);
 TMPL_AVAILABLE_IN_3_36
+TmplExpr *tmpl_expr_new_anon_call     (TmplExpr         *func,
+                                       TmplExpr         *params);
+TMPL_AVAILABLE_IN_3_36
 TmplExpr *tmpl_expr_new_nop           (void);
 
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (TmplExpr, tmpl_expr_unref)
diff --git a/tests/test1.script b/tests/test1.script
index a8f256f..7ceab69 100644
--- a/tests/test1.script
+++ b/tests/test1.script
@@ -49,4 +49,8 @@ order3(1,2,3)
 def weird1(a) assert(a==1)
 end
 
+# anonymous functions can have a single statement
+(func() 123)()
+(func(a,b) assert((a==1)&&(b==2)))(1,2)
+
 1234;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]