plug-ins: new named argument syntax in Script-Fu.

This syntax is now the official syntax for non-core PDB procedures.

I.e. that while core procedures will still use ordered arguments (e.g.:
`(gimp-image-get-layers 1)`), plug-in PDB procedures called from
Script-Fu will have named arguments in any order.

Say for instance that you want to call python-fu-foggify from Script-Fu.
Whereas we used to run:

> (python-fu-foggify RUN-NONINTERACTIVE 1 (car (gimp-image-get-layers 1)) "Clouds" '(50 4 4) 1.0 50.0)

Now we should call:

> (python-fu-foggify #:image 1 #:drawables (car (gimp-image-get-layers 1)) #:opacity 50.0 #:color '(50 4 4))

Now we can note:

* Every argument is preceded by a #:label which is the argument name.
  This makes these calls much more readable (some plug-in procedures can
  have dozen of arguments and these end up as list of integers, floats
  and strings, which are hard to read and hard to debug) and semantic.
* Order doesn't matter anymore. For instance here, I put #:opacity
  before #:color.
* As a direct consequence, we can drop any argument which we wish to
  keep with default value. E.g. in the old style, we had to put the
  #:run-mode, #:name ("Clouds") and #:turbulence (1.0) because we were
  changing the last argument #:opacity (50.0). Now we can drop all 3
  default arguments.

Having non-ordered argument is in fact the starter of this feature,
because it is already the case for calling PDB procedures in the libgimp
API (and therefore in all GIR bindings). By saying that the order of PDB
procedures is not part of the API, we actually allow to add arguments
and even to reorder them in the future without breaking existing scripts
in the 3.0 series.

This became even more serious when I was considering to make the generic
metadata arguments public. Since they are appended to the end, after all
plug-in-specific arguments, if I do this, adding any argument in an
export plug-in would break order. It won't matter anymore!

Note that it doesn't matter for core PDB procedures (where this syntax
is not used) because these are also C functions and therefore order and
number of arguments matter anyway. Also these don't have dozens of
arguments.

As a helper for Script-Fu developer, in particular as we already
released 2 RCs and therefore some people already started to port their
scripts, the old syntax will still work yet will produce a warning
showing how to call the same thing with the new syntax. For instance,
with the above first call, the warning will be:

> (script-fu:2059912): scriptfu-WARNING **: 22:54:47.507: Calling Plug-In PDB procedures with arguments as an ordered list is deprecated.
> Please use named arguments: (python-fu-foggify #:run-mode 1 #:image 1 #:drawables (2) #:name "Clouds" #:color '(50 4 4) #:turbulence 1.000000 #:opacity 50.000000)

Note that the argument name syntax is coming from the Racket scheme
variant: https://docs.racket-lang.org/arguments/index.html

Common Lisp has a similar syntax, either with just a colon, or also a
sharp + colon (it's unclear to me the difference, something about
interned vs. uninterned symbols).
After discussion with Liam on IRC, we decided to go with the #: syntax
of Racket.
This commit is contained in:
Jehan 2025-01-19 22:21:54 +01:00
parent 751665a7bd
commit f92f7d425c
6 changed files with 448 additions and 105 deletions

View file

@ -79,11 +79,13 @@ marshal_ID_to_item (scheme *sc,
* Requires ID's of instances of GimpItem * Requires ID's of instances of GimpItem
*/ */
pointer pointer
marshal_vector_to_item_array (scheme *sc, marshal_vector_to_item_array (scheme *sc,
pointer vector, pointer vector,
GValue *value) GValue *value,
gchar **strvalue)
{ {
GObject **object_array; GObject **object_array;
GString *v = NULL;
gint id; gint id;
pointer error; pointer error;
@ -92,6 +94,8 @@ marshal_vector_to_item_array (scheme *sc,
/* empty vector will produce empty GimpObjectArray */ /* empty vector will produce empty GimpObjectArray */
object_array = g_new0 (GObject *, num_elements + 1); object_array = g_new0 (GObject *, num_elements + 1);
if (strvalue)
v = g_string_new ("");
for (int j = 0; j < num_elements; ++j) for (int j = 0; j < num_elements; ++j)
{ {
@ -100,6 +104,8 @@ marshal_vector_to_item_array (scheme *sc,
if (!sc->vptr->is_number (element)) if (!sc->vptr->is_number (element))
{ {
g_free (object_array); g_free (object_array);
if (v)
g_string_free (v, TRUE);
return script_error (sc, "Expected numeric in vector of ID", vector); return script_error (sc, "Expected numeric in vector of ID", vector);
/* FUTURE more detailed error msg: /* FUTURE more detailed error msg:
* return script_type_error_in_container (sc, "numeric", i, j, proc_name, vector); * return script_type_error_in_container (sc, "numeric", i, j, proc_name, vector);
@ -111,8 +117,13 @@ marshal_vector_to_item_array (scheme *sc,
if (error) if (error)
{ {
g_free (object_array); g_free (object_array);
if (v)
g_string_free (v, TRUE);
return error; return error;
} }
if (v)
g_string_append_printf (v, "%s%d", j == 0 ? "" : " ", id);
} }
/* Shallow copy. /* Shallow copy.
@ -122,6 +133,11 @@ marshal_vector_to_item_array (scheme *sc,
* and fails if we default to G_TYPE_INVALID but passes if ITEM. * and fails if we default to G_TYPE_INVALID but passes if ITEM.
*/ */
g_value_set_boxed (value, (gpointer) object_array); g_value_set_boxed (value, (gpointer) object_array);
if (v)
{
*strvalue = g_strdup_printf ("(%s)", v->str);
g_string_free (v, TRUE);
}
g_free (object_array); g_free (object_array);
@ -258,12 +274,18 @@ get_item_from_ID_in_script (scheme *sc,
* A list longer than expected (>4) is not an error, but extra list is not used. * A list longer than expected (>4) is not an error, but extra list is not used.
*/ */
static void static void
marshal_list_of_numeric_to_rgba (scheme *sc, marshal_list_of_numeric_to_rgba (scheme *sc,
pointer color_list, pointer color_list,
guchar (*rgba)[4], /* OUT */ guchar (*rgba)[4], /* OUT */
guint *length) /* OUT */ guint *length, /* OUT */
gchar **strvalue)
{ {
GString *v = NULL;
*length = 0; *length = 0;
if (strvalue)
v = g_string_new ("");
for (guint i=0; i<4; i++) for (guint i=0; i<4; i++)
{ {
if (sc->vptr->is_number (sc->vptr->pair_car (color_list))) if (sc->vptr->is_number (sc->vptr->pair_car (color_list)))
@ -272,14 +294,31 @@ marshal_list_of_numeric_to_rgba (scheme *sc,
0, 255); 0, 255);
*length = *length + 1; *length = *length + 1;
color_list = sc->vptr->pair_cdr (color_list); color_list = sc->vptr->pair_cdr (color_list);
if (v)
g_string_append_printf (v, "%s%d", i == 0 ? "" : " ", (*rgba)[i]);
} }
else else
{ {
/* Reached end of list or non-numeric. */ /* Reached end of list or non-numeric. */
if (v)
{
if (*length != 0)
*strvalue = g_strdup_printf ("'(%s)", v->str);
g_string_free (v, TRUE);
}
return; return;
} }
} }
/* *length is in [0,4] and *rgba is filled in with same count. */ /* *length is in [0,4] and *rgba is filled in with same count. */
if (v)
{
*strvalue = g_strdup_printf ("'(%s)", v->str);
g_string_free (v, TRUE);
}
} }
/* Walk a C array of bytes (rgba) creating Scheme list of numeric. /* Walk a C array of bytes (rgba) creating Scheme list of numeric.
@ -313,16 +352,15 @@ marshal_rgba_to_list_of_numeric (scheme *sc,
* - list elements not numbers. * - list elements not numbers.
*/ */
GeglColor * GeglColor *
marshal_component_list_to_color (scheme *sc, marshal_component_list_to_color (scheme *sc,
pointer color_list) pointer color_list,
gchar **strvalue)
{ {
GeglColor *color_result; GeglColor *color_result;
guchar rgba[4]; guchar rgba[4];
guint list_length; guint list_length;
marshal_list_of_numeric_to_rgba (sc, color_list, &rgba, &list_length, strvalue);
marshal_list_of_numeric_to_rgba (sc, color_list, &rgba, &list_length);
/* list_length is the count of numerics used. */ /* list_length is the count of numerics used. */
/* Dispatch on list length and create different format colors */ /* Dispatch on list length and create different format colors */

View file

@ -25,7 +25,8 @@ pointer marshal_ID_to_item (scheme *sc,
pointer marshal_vector_to_item_array (scheme *sc, pointer marshal_vector_to_item_array (scheme *sc,
pointer a, pointer a,
GValue *value); GValue *value,
gchar **strvalue);
void marshal_path_string_to_gfile (scheme *sc, void marshal_path_string_to_gfile (scheme *sc,
pointer a, pointer a,
@ -38,7 +39,8 @@ pointer marshal_returned_object_array_to_vector (scheme *sc,
gchar* marshal_returned_gfile_to_string (GValue *value); gchar* marshal_returned_gfile_to_string (GValue *value);
GeglColor* marshal_component_list_to_color (scheme *sc, GeglColor* marshal_component_list_to_color (scheme *sc,
pointer list); pointer list,
gchar **strvalue);
pointer marshal_color_to_component_list (scheme *sc, pointer marshal_color_to_component_list (scheme *sc,
GeglColor *color); GeglColor *color);

View file

@ -70,7 +70,8 @@ static pointer script_fu_marshal_arg_to_value (scheme
const gchar *proc_name, const gchar *proc_name,
gint arg_index, gint arg_index,
GParamSpec *arg_spec, GParamSpec *arg_spec,
GValue *value); GValue *value,
gchar **strvalue);
static pointer script_fu_marshal_procedure_call (scheme *sc, static pointer script_fu_marshal_procedure_call (scheme *sc,
pointer a, pointer a,
@ -730,12 +731,13 @@ ts_load_file (const gchar *dirname,
/* Returns pointer to sc->NIL (normal) or pointer to error. */ /* Returns pointer to sc->NIL (normal) or pointer to error. */
static pointer static pointer
script_fu_marshal_arg_to_value (scheme *sc, script_fu_marshal_arg_to_value (scheme *sc,
pointer a, pointer a,
const gchar *proc_name, const gchar *proc_name,
gint arg_index, gint arg_index,
GParamSpec *arg_spec, GParamSpec *arg_spec,
GValue *value) GValue *value,
gchar **strvalue)
{ {
pointer arg_val; pointer arg_val;
pointer vector; pointer vector;
@ -763,6 +765,8 @@ script_fu_marshal_arg_to_value (scheme *sc,
return script_int_range_error (sc, arg_index, proc_name, ispec->minimum, ispec->maximum, v); return script_int_range_error (sc, arg_index, proc_name, ispec->minimum, ispec->maximum, v);
g_value_set_int (value, v); g_value_set_int (value, v);
if (strvalue)
*strvalue = g_strdup_printf ("%d", v);
} }
} }
else if (G_VALUE_HOLDS_UINT (value)) else if (G_VALUE_HOLDS_UINT (value))
@ -780,6 +784,8 @@ script_fu_marshal_arg_to_value (scheme *sc,
return script_int_range_error (sc, arg_index, proc_name, ispec->minimum, ispec->maximum, v); return script_int_range_error (sc, arg_index, proc_name, ispec->minimum, ispec->maximum, v);
g_value_set_uint (value, v); g_value_set_uint (value, v);
if (strvalue)
*strvalue = g_strdup_printf ("%u", v);
} }
} }
else if (G_VALUE_HOLDS_UCHAR (value)) else if (G_VALUE_HOLDS_UCHAR (value))
@ -797,6 +803,8 @@ script_fu_marshal_arg_to_value (scheme *sc,
return script_int_range_error (sc, arg_index, proc_name, cspec->minimum, cspec->maximum, c); return script_int_range_error (sc, arg_index, proc_name, cspec->minimum, cspec->maximum, c);
g_value_set_uchar (value, c); g_value_set_uchar (value, c);
if (strvalue)
*strvalue = g_strdup_printf ("%d", c);
} }
} }
else if (G_VALUE_HOLDS_DOUBLE (value)) else if (G_VALUE_HOLDS_DOUBLE (value))
@ -814,26 +822,38 @@ script_fu_marshal_arg_to_value (scheme *sc,
return script_float_range_error (sc, arg_index, proc_name, dspec->minimum, dspec->maximum, d); return script_float_range_error (sc, arg_index, proc_name, dspec->minimum, dspec->maximum, d);
g_value_set_double (value, d); g_value_set_double (value, d);
if (strvalue)
*strvalue = g_strdup_printf ("%f", d);
} }
} }
else if (G_VALUE_HOLDS_ENUM (value)) else if (G_VALUE_HOLDS_ENUM (value))
{ {
if (! sc->vptr->is_number (arg_val)) if (! sc->vptr->is_number (arg_val))
return script_type_error (sc, "numeric", arg_index, proc_name); {
return script_type_error (sc, "numeric", arg_index, proc_name);
}
else else
g_value_set_enum (value, {
sc->vptr->ivalue (arg_val)); gint e = sc->vptr->ivalue (arg_val);
g_value_set_enum (value, e);
if (strvalue)
*strvalue = g_strdup_printf ("%d", e);
}
} }
else if (G_VALUE_HOLDS_BOOLEAN (value)) else if (G_VALUE_HOLDS_BOOLEAN (value))
{ {
if (sc->vptr->is_number (arg_val)) if (sc->vptr->is_number (arg_val))
{ {
gboolean b = sc->vptr->ivalue (arg_val);
/* Bind according to C idiom: 0 is false, other numeric values true. /* Bind according to C idiom: 0 is false, other numeric values true.
* This is not strict Scheme: 0 is truthy in Scheme. * This is not strict Scheme: 0 is truthy in Scheme.
* This lets FALSE still work, where FALSE is a deprecated symbol for 0. * This lets FALSE still work, where FALSE is a deprecated symbol for 0.
*/ */
g_value_set_boolean (value, g_value_set_boolean (value, b);
sc->vptr->ivalue (arg_val)); if (strvalue)
*strvalue = g_strdup_printf ("%s", b ? "TRUE" : "FALSE");
} }
else else
{ {
@ -847,6 +867,8 @@ script_fu_marshal_arg_to_value (scheme *sc,
*/ */
gboolean truth_value = ! (arg_val == sc->F); gboolean truth_value = ! (arg_val == sc->F);
g_value_set_boolean (value, truth_value); g_value_set_boolean (value, truth_value);
if (strvalue)
*strvalue = g_strdup_printf ("%s", truth_value ? "TRUE" : "FALSE");
} }
else else
{ {
@ -858,10 +880,21 @@ script_fu_marshal_arg_to_value (scheme *sc,
else if (G_VALUE_HOLDS_STRING (value)) else if (G_VALUE_HOLDS_STRING (value))
{ {
if (! sc->vptr->is_string (arg_val)) if (! sc->vptr->is_string (arg_val))
return script_type_error (sc, "string", arg_index, proc_name); {
return script_type_error (sc, "string", arg_index, proc_name);
}
else else
g_value_set_string (value, {
sc->vptr->string_value (arg_val)); const gchar *s = sc->vptr->string_value (arg_val);
g_value_set_string (value, s);
if (strvalue)
{
gchar *escaped = g_strescape (s, NULL);
*strvalue = g_strdup_printf ("\"%s\"", escaped);
g_free (escaped);
}
}
} }
else if (G_VALUE_HOLDS (value, G_TYPE_STRV)) else if (G_VALUE_HOLDS (value, G_TYPE_STRV))
{ {
@ -872,11 +905,14 @@ script_fu_marshal_arg_to_value (scheme *sc,
} }
else else
{ {
gchar **array; gchar **array;
GString *v = NULL;
n_elements = sc->vptr->list_length (sc, vector); n_elements = sc->vptr->list_length (sc, vector);
array = g_new0 (gchar *, n_elements + 1); array = g_new0 (gchar *, n_elements + 1);
if (strvalue)
v = g_string_new ("");
for (j = 0; j < n_elements; j++) for (j = 0; j < n_elements; j++)
{ {
@ -888,10 +924,18 @@ script_fu_marshal_arg_to_value (scheme *sc,
"Item %d in vector is not a string (argument %d for function %s)", "Item %d in vector is not a string (argument %d for function %s)",
j+1, arg_index+1, proc_name); j+1, arg_index+1, proc_name);
g_strfreev (array); g_strfreev (array);
if (v)
g_string_free (v, TRUE);
return foreign_error (sc, error_str, vector); return foreign_error (sc, error_str, vector);
} }
array[j] = g_strdup (sc->vptr->string_value (v_element)); array[j] = g_strdup (sc->vptr->string_value (v_element));
if (v)
{
gchar *escaped = g_strescape (array[j], NULL);
g_string_append_printf (v, "%s\"%s\"", j == 0 ? "" : " ", escaped);
g_free (escaped);
}
vector = sc->vptr->pair_cdr (vector); vector = sc->vptr->pair_cdr (vector);
} }
@ -911,6 +955,12 @@ script_fu_marshal_arg_to_value (scheme *sc,
} }
} }
#endif #endif
if (v)
{
*strvalue = g_strdup_printf ("#(%s)", v->str);
g_string_free (v, TRUE);
}
} }
} }
else if (GIMP_VALUE_HOLDS_DISPLAY (value)) else if (GIMP_VALUE_HOLDS_DISPLAY (value))
@ -921,10 +971,15 @@ script_fu_marshal_arg_to_value (scheme *sc,
} }
else else
{ {
GimpDisplay *display = GimpDisplay *display;
gimp_display_get_by_id (sc->vptr->ivalue (arg_val)); gint id = sc->vptr->ivalue (arg_val);
display = gimp_display_get_by_id (id);
g_value_set_object (value, display); g_value_set_object (value, display);
if (strvalue)
*strvalue = g_strdup_printf ("%d", id);
} }
} }
else if (GIMP_VALUE_HOLDS_IMAGE (value)) else if (GIMP_VALUE_HOLDS_IMAGE (value))
@ -935,10 +990,15 @@ script_fu_marshal_arg_to_value (scheme *sc,
} }
else else
{ {
GimpImage *image = GimpImage *image;
gimp_image_get_by_id (sc->vptr->ivalue (arg_val)); gint id = sc->vptr->ivalue (arg_val);
image = gimp_image_get_by_id (id);
g_value_set_object (value, image); g_value_set_object (value, image);
if (strvalue)
*strvalue = g_strdup_printf ("%d", id);
} }
} }
else if (GIMP_VALUE_HOLDS_LAYER (value)) else if (GIMP_VALUE_HOLDS_LAYER (value))
@ -949,10 +1009,15 @@ script_fu_marshal_arg_to_value (scheme *sc,
} }
else else
{ {
GimpLayer *layer = GimpLayer *layer;
gimp_layer_get_by_id (sc->vptr->ivalue (arg_val)); gint id = sc->vptr->ivalue (arg_val);
layer = gimp_layer_get_by_id (id);
g_value_set_object (value, layer); g_value_set_object (value, layer);
if (strvalue)
*strvalue = g_strdup_printf ("%d", id);
} }
} }
else if (GIMP_VALUE_HOLDS_LAYER_MASK (value)) else if (GIMP_VALUE_HOLDS_LAYER_MASK (value))
@ -963,10 +1028,15 @@ script_fu_marshal_arg_to_value (scheme *sc,
} }
else else
{ {
GimpLayerMask *layer_mask = GimpLayerMask *layer_mask;
gimp_layer_mask_get_by_id (sc->vptr->ivalue (arg_val)); gint id = sc->vptr->ivalue (arg_val);
layer_mask = gimp_layer_mask_get_by_id (id);
g_value_set_object (value, layer_mask); g_value_set_object (value, layer_mask);
if (strvalue)
*strvalue = g_strdup_printf ("%d", id);
} }
} }
else if (GIMP_VALUE_HOLDS_CHANNEL (value)) else if (GIMP_VALUE_HOLDS_CHANNEL (value))
@ -977,10 +1047,15 @@ script_fu_marshal_arg_to_value (scheme *sc,
} }
else else
{ {
GimpChannel *channel = GimpChannel *channel;
gimp_channel_get_by_id (sc->vptr->ivalue (arg_val)); gint id = sc->vptr->ivalue (arg_val);
channel = gimp_channel_get_by_id (id);
g_value_set_object (value, channel); g_value_set_object (value, channel);
if (strvalue)
*strvalue = g_strdup_printf ("%d", id);
} }
} }
else if (GIMP_VALUE_HOLDS_DRAWABLE (value)) else if (GIMP_VALUE_HOLDS_DRAWABLE (value))
@ -996,6 +1071,9 @@ script_fu_marshal_arg_to_value (scheme *sc,
pointer error = marshal_ID_to_item (sc, a, id, value); pointer error = marshal_ID_to_item (sc, a, id, value);
if (error) if (error)
return error; return error;
if (strvalue)
*strvalue = g_strdup_printf ("%d", id);
} }
} }
else if (GIMP_VALUE_HOLDS_PATH (value)) else if (GIMP_VALUE_HOLDS_PATH (value))
@ -1006,10 +1084,15 @@ script_fu_marshal_arg_to_value (scheme *sc,
} }
else else
{ {
GimpPath *path = GimpPath *path;
gimp_path_get_by_id (sc->vptr->ivalue (arg_val)); gint id = sc->vptr->ivalue (arg_val);
path = gimp_path_get_by_id (id);
g_value_set_object (value, path); g_value_set_object (value, path);
if (strvalue)
*strvalue = g_strdup_printf ("%d", id);
} }
} }
else if (GIMP_VALUE_HOLDS_ITEM (value)) else if (GIMP_VALUE_HOLDS_ITEM (value))
@ -1020,12 +1103,11 @@ script_fu_marshal_arg_to_value (scheme *sc,
} }
else else
{ {
gint item_ID; gint id = sc->vptr->ivalue (arg_val);
item_ID = sc->vptr->ivalue (arg_val);
if (gimp_item_id_is_valid (item_ID)) if (gimp_item_id_is_valid (id))
{ {
GimpItem *item = gimp_item_get_by_id (item_ID); GimpItem *item = gimp_item_get_by_id (id);
g_value_set_object (value, item); g_value_set_object (value, item);
} }
else else
@ -1035,6 +1117,9 @@ script_fu_marshal_arg_to_value (scheme *sc,
*/ */
g_value_set_object (value, NULL); g_value_set_object (value, NULL);
} }
if (strvalue)
*strvalue = g_strdup_printf ("%d", id);
} }
} }
else if (GIMP_VALUE_HOLDS_DRAWABLE_FILTER (value)) else if (GIMP_VALUE_HOLDS_DRAWABLE_FILTER (value))
@ -1045,9 +1130,7 @@ script_fu_marshal_arg_to_value (scheme *sc,
} }
else else
{ {
gint id; gint id = sc->vptr->ivalue (arg_val);
id = sc->vptr->ivalue (arg_val);
if (gimp_drawable_filter_id_is_valid (id)) if (gimp_drawable_filter_id_is_valid (id))
{ {
@ -1062,6 +1145,9 @@ script_fu_marshal_arg_to_value (scheme *sc,
*/ */
g_value_set_object (value, NULL); g_value_set_object (value, NULL);
} }
if (strvalue)
*strvalue = g_strdup_printf ("%d", id);
} }
} }
else if (GIMP_VALUE_HOLDS_INT32_ARRAY (value)) else if (GIMP_VALUE_HOLDS_INT32_ARRAY (value))
@ -1092,11 +1178,15 @@ script_fu_marshal_arg_to_value (scheme *sc,
* FUTURE: List must be *exactly* n_elements long. * FUTURE: List must be *exactly* n_elements long.
* n_elements != sc->vptr->list_length (sc, vector)) * n_elements != sc->vptr->list_length (sc, vector))
*/ */
gint32 *array; gint32 *array;
GString *v = NULL;
n_elements = sc->vptr->vector_length (vector); n_elements = sc->vptr->vector_length (vector);
array = g_new0 (gint32, n_elements); array = g_new0 (gint32, n_elements);
if (strvalue)
v = g_string_new ("");
for (j = 0; j < n_elements; j++) for (j = 0; j < n_elements; j++)
{ {
pointer v_element = sc->vptr->vector_elem (vector, j); pointer v_element = sc->vptr->vector_elem (vector, j);
@ -1105,13 +1195,22 @@ script_fu_marshal_arg_to_value (scheme *sc,
if (! sc->vptr->is_number (v_element)) if (! sc->vptr->is_number (v_element))
{ {
g_free (array); g_free (array);
if (v)
g_string_free (v, TRUE);
return script_type_error_in_container (sc, "numeric", arg_index, j, proc_name, vector); return script_type_error_in_container (sc, "numeric", arg_index, j, proc_name, vector);
} }
array[j] = (gint32) sc->vptr->ivalue (v_element); array[j] = (gint32) sc->vptr->ivalue (v_element);
if (v)
g_string_append_printf (v, "%s%d", j == 0 ? "" : " ", array[j]);
} }
gimp_value_take_int32_array (value, array, n_elements); gimp_value_take_int32_array (value, array, n_elements);
if (v)
{
*strvalue = g_strdup_printf ("#(%s)", v->str);
g_string_free (v, TRUE);
}
debug_vector (sc, vector, "%ld"); debug_vector (sc, vector, "%ld");
} }
@ -1125,11 +1224,14 @@ script_fu_marshal_arg_to_value (scheme *sc,
} }
else else
{ {
guint8 *array; guint8 *array;
GString *v = NULL;
n_elements = sc->vptr->vector_length (vector); n_elements = sc->vptr->vector_length (vector);
array = g_new0 (guint8, n_elements); array = g_new0 (guint8, n_elements);
if (strvalue)
v = g_string_new ("");
for (j = 0; j < n_elements; j++) for (j = 0; j < n_elements; j++)
{ {
@ -1138,13 +1240,22 @@ script_fu_marshal_arg_to_value (scheme *sc,
if (!sc->vptr->is_number (v_element)) if (!sc->vptr->is_number (v_element))
{ {
g_free (array); g_free (array);
if (v)
g_string_free (v, TRUE);
return script_type_error_in_container (sc, "numeric", arg_index, j, proc_name, vector); return script_type_error_in_container (sc, "numeric", arg_index, j, proc_name, vector);
} }
array[j] = (guint8) sc->vptr->ivalue (v_element); array[j] = (guint8) sc->vptr->ivalue (v_element);
if (v)
g_string_append_printf (v, "%s%d", j == 0 ? "" : " ", array[j]);
} }
g_value_take_boxed (value, g_bytes_new_take (array, n_elements)); g_value_take_boxed (value, g_bytes_new_take (array, n_elements));
if (v)
{
*strvalue = g_strdup_printf ("#(%s)", v->str);
g_string_free (v, TRUE);
}
debug_vector (sc, vector, "%ld"); debug_vector (sc, vector, "%ld");
} }
@ -1159,9 +1270,12 @@ script_fu_marshal_arg_to_value (scheme *sc,
else else
{ {
gdouble *array; gdouble *array;
GString *v = NULL;
n_elements = sc->vptr->vector_length (vector); n_elements = sc->vptr->vector_length (vector);
array = g_new0 (gdouble, n_elements); array = g_new0 (gdouble, n_elements);
if (strvalue)
v = g_string_new ("");
for (j = 0; j < n_elements; j++) for (j = 0; j < n_elements; j++)
{ {
@ -1170,13 +1284,22 @@ script_fu_marshal_arg_to_value (scheme *sc,
if (!sc->vptr->is_number (v_element)) if (!sc->vptr->is_number (v_element))
{ {
g_free (array); g_free (array);
if (v)
g_string_free (v, TRUE);
return script_type_error_in_container (sc, "numeric", arg_index, j, proc_name, vector); return script_type_error_in_container (sc, "numeric", arg_index, j, proc_name, vector);
} }
array[j] = (gdouble) sc->vptr->rvalue (v_element); array[j] = (gdouble) sc->vptr->rvalue (v_element);
if (v)
g_string_append_printf (v, "%s%f", j == 0 ? "" : " ", array[j]);
} }
gimp_value_take_double_array (value, array, n_elements); gimp_value_take_double_array (value, array, n_elements);
if (v)
{
*strvalue = g_strdup_printf ("#(%s)", v->str);
g_string_free (v, TRUE);
}
debug_vector (sc, vector, "%f"); debug_vector (sc, vector, "%f");
} }
@ -1191,12 +1314,15 @@ script_fu_marshal_arg_to_value (scheme *sc,
if (! (color = sf_color_get_color_from_name (color_string))) if (! (color = sf_color_get_color_from_name (color_string)))
return script_type_error (sc, "color string", arg_index, proc_name); return script_type_error (sc, "color string", arg_index, proc_name);
if (strvalue)
*strvalue = g_strdup_printf ("\"%s\"", color_string);
} }
else if (sc->vptr->is_list (sc, arg_val)) else if (sc->vptr->is_list (sc, arg_val))
{ {
pointer color_list = arg_val; pointer color_list = arg_val;
if (! (color = marshal_component_list_to_color (sc, color_list))) if (! (color = marshal_component_list_to_color (sc, color_list, strvalue)))
return script_type_error (sc, "color list of numeric components", arg_index, proc_name); return script_type_error (sc, "color list of numeric components", arg_index, proc_name);
} }
else else
@ -1217,10 +1343,13 @@ script_fu_marshal_arg_to_value (scheme *sc,
else else
{ {
GeglColor **colors; GeglColor **colors;
GString *v = NULL;
n_elements = sc->vptr->vector_length (vector); n_elements = sc->vptr->vector_length (vector);
colors = g_new0 (GeglColor *, n_elements + 1); colors = g_new0 (GeglColor *, n_elements + 1);
if (strvalue)
v = g_string_new ("");
for (j = 0; j < n_elements; j++) for (j = 0; j < n_elements; j++)
{ {
@ -1238,6 +1367,8 @@ script_fu_marshal_arg_to_value (scheme *sc,
"Item %d in vector is not a color " "Item %d in vector is not a color "
"(argument %d for function %s)", "(argument %d for function %s)",
j+1, arg_index+1, proc_name); j+1, arg_index+1, proc_name);
if (v)
g_string_free (v, TRUE);
return script_error (sc, error_str, 0); return script_error (sc, error_str, 0);
} }
@ -1253,9 +1384,16 @@ script_fu_marshal_arg_to_value (scheme *sc,
colors[j] = gegl_color_new (NULL); colors[j] = gegl_color_new (NULL);
gegl_color_set_pixel (colors[j], babl_format ("R'G'B' u8"), rgb); gegl_color_set_pixel (colors[j], babl_format ("R'G'B' u8"), rgb);
if (v)
g_string_append_printf (v, " '(%d %d %d)", rgb[0], rgb[1], rgb[2]);
} }
g_value_take_boxed (value, colors); g_value_take_boxed (value, colors);
if (v)
{
*strvalue = g_strdup_printf ("#(%s)", v->str);
g_string_free (v, TRUE);
}
g_debug ("color vector has %ld elements", sc->vptr->vector_length (vector)); g_debug ("color vector has %ld elements", sc->vptr->vector_length (vector));
} }
@ -1306,6 +1444,16 @@ script_fu_marshal_arg_to_value (scheme *sc,
g_debug ("data '%s'", (char *)parasite.data); g_debug ("data '%s'", (char *)parasite.data);
g_value_set_boxed (value, &parasite); g_value_set_boxed (value, &parasite);
if (strvalue)
{
gchar *escaped_name = g_strescape (parasite.name, NULL);
gchar *escaped_data = g_strescape (parasite.data, NULL);
*strvalue = g_strdup_printf ("(\"%s\" %d \"%s\")",
escaped_name, parasite.flags, escaped_data);
g_free (escaped_name);
g_free (escaped_data);
}
} }
} }
else if (GIMP_VALUE_HOLDS_CORE_OBJECT_ARRAY (value)) else if (GIMP_VALUE_HOLDS_CORE_OBJECT_ARRAY (value))
@ -1318,7 +1466,7 @@ script_fu_marshal_arg_to_value (scheme *sc,
if (sc->vptr->is_vector (vector)) if (sc->vptr->is_vector (vector))
{ {
pointer error = marshal_vector_to_item_array (sc, vector, value); pointer error = marshal_vector_to_item_array (sc, vector, value, strvalue);
if (error) if (error)
return error; return error;
} }
@ -1332,6 +1480,9 @@ script_fu_marshal_arg_to_value (scheme *sc,
if (! sc->vptr->is_string (arg_val)) if (! sc->vptr->is_string (arg_val))
return script_type_error (sc, "string for path", arg_index, proc_name); return script_type_error (sc, "string for path", arg_index, proc_name);
marshal_path_string_to_gfile (sc, a, value); marshal_path_string_to_gfile (sc, a, value);
if (strvalue)
*strvalue = g_strdup_printf ("%s", sc->vptr->string_value (arg_val));
} }
else if (G_VALUE_TYPE (value) == GIMP_TYPE_PDB_STATUS_TYPE) else if (G_VALUE_TYPE (value) == GIMP_TYPE_PDB_STATUS_TYPE)
{ {
@ -1369,6 +1520,8 @@ script_fu_marshal_arg_to_value (scheme *sc,
return script_error (sc, "runtime: resource ID of improper subclass.", a); return script_error (sc, "runtime: resource ID of improper subclass.", a);
} }
g_value_set_object (value, resource); g_value_set_object (value, resource);
if (strvalue)
*strvalue = g_strdup_printf ("%d", resource_id);
} }
} }
else if (GIMP_VALUE_HOLDS_EXPORT_OPTIONS (value)) else if (GIMP_VALUE_HOLDS_EXPORT_OPTIONS (value))
@ -1384,6 +1537,8 @@ script_fu_marshal_arg_to_value (scheme *sc,
* create a GimpExportOptions (actually a proxy?) * create a GimpExportOptions (actually a proxy?)
*/ */
g_value_set_object (value, NULL); g_value_set_object (value, NULL);
if (strvalue)
*strvalue = g_strdup_printf ("%d", -1);
} }
else else
{ {
@ -1455,6 +1610,8 @@ script_fu_marshal_procedure_call (scheme *sc,
arg_specs = gimp_procedure_get_arguments (procedure, &n_arg_specs); arg_specs = gimp_procedure_get_arguments (procedure, &n_arg_specs);
actual_arg_count = sc->vptr->list_length (sc, a) - 1; actual_arg_count = sc->vptr->list_length (sc, a) - 1;
a = sc->vptr->pair_cdr (a);
/* Check the supplied number of arguments. /* Check the supplied number of arguments.
* This only gives messages to the console. * This only gives messages to the console.
* It does not ensure that the count of supplied args equals the count of formal args. * It does not ensure that the count of supplied args equals the count of formal args.
@ -1466,67 +1623,149 @@ script_fu_marshal_procedure_call (scheme *sc,
* Extra supplied args can be discarded. * Extra supplied args can be discarded.
* Formerly, this was a deprecated behavior depending on "permissive". * Formerly, this was a deprecated behavior depending on "permissive".
*/ */
{ if (gimp_procedure_is_core (procedure))
if (actual_arg_count > n_arg_specs) {
{ if (actual_arg_count > n_arg_specs)
/* Permit extra args. Will discard args from script, to next right paren.*/ {
g_info ("in script, permitting too many args to %s", proc_name); /* Permit extra args. Will discard args from script, to next right paren.*/
} g_info ("in script, permitting too many args to %s", proc_name);
else if (actual_arg_count < n_arg_specs) }
{ else if (actual_arg_count < n_arg_specs)
/* Permit too few args. The config carries a sane default for most types. */ {
g_info ("in script, permitting too few args to %s", proc_name); /* Permit too few args. The config carries a sane default for most types. */
} g_info ("in script, permitting too few args to %s", proc_name);
/* else equal counts of args. */ }
} /* else equal counts of args. */
}
/* Marshall the supplied arguments */ /* Marshall the supplied arguments */
for (i = 0; i < n_arg_specs; i++) if (gimp_procedure_is_core (procedure) ||
! sc->vptr->is_arg_name (sc->vptr->pair_car (a)))
{ {
GParamSpec *arg_spec = arg_specs[i]; GString *deprecation_warning = NULL;
GValue value = G_VALUE_INIT;
consumed_arg_count++; if (! gimp_procedure_is_core (procedure))
if (consumed_arg_count > actual_arg_count)
{ {
/* Exhausted supplied arguments before formal specs. */ deprecation_warning = g_string_new ("Calling Plug-In PDB procedures with arguments as an ordered list is deprecated.\n"
"Please use named arguments: (");
/* Say formal type of first missing arg. */ g_string_append (deprecation_warning, proc_name);
g_warning ("Missing arg type: %s", g_type_name (G_PARAM_SPEC_VALUE_TYPE (arg_spec)));
/* Break loop over formal specs. Continuation is to call PDB with partial args. */
break;
}
else
a = sc->vptr->pair_cdr (a); /* advance pointer to next arg in list. */
g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (arg_spec));
debug_in_arg (sc, a, i, g_type_name (G_VALUE_TYPE (&value)));
return_val = script_fu_marshal_arg_to_value (sc, a, proc_name, i, arg_spec, &value);
if (return_val != sc->NIL)
{
g_value_unset (&value);
return return_val;
} }
debug_gvalue (&value); for (i = 0; i < n_arg_specs; i++)
if (g_param_value_validate (arg_spec, &value))
{ {
gchar error_message[1024]; GParamSpec *arg_spec = arg_specs[i];
GValue value = G_VALUE_INIT;
gchar *strvalue = NULL;
g_snprintf (error_message, sizeof (error_message), consumed_arg_count++;
"Invalid value for argument %d",
i); if (consumed_arg_count > actual_arg_count)
{
/* Exhausted supplied arguments before formal specs. */
/* Say formal type of first missing arg. */
g_warning ("Missing arg type: %s", g_type_name (G_PARAM_SPEC_VALUE_TYPE (arg_spec)));
/* Break loop over formal specs. Continuation is to call PDB with partial args. */
break;
}
g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (arg_spec));
debug_in_arg (sc, a, i, g_type_name (G_VALUE_TYPE (&value)));
return_val = script_fu_marshal_arg_to_value (sc, a, proc_name, i, arg_spec, &value, &strvalue);
if (return_val != sc->NIL)
{
g_value_unset (&value);
return return_val;
}
debug_gvalue (&value);
if (g_param_value_validate (arg_spec, &value))
{
gchar error_message[1024];
g_snprintf (error_message, sizeof (error_message),
"Invalid value for argument %d",
i);
g_value_unset (&value);
return script_error (sc, error_message, 0);
}
g_object_set_property (G_OBJECT (config), arg_specs[i]->name, &value);
g_value_unset (&value); g_value_unset (&value);
return script_error (sc, error_message, 0); if (deprecation_warning != NULL)
g_string_append_printf (deprecation_warning, " #:%s %s",
arg_specs[i]->name, strvalue);
a = sc->vptr->pair_cdr (a);
g_free (strvalue);
}
if (deprecation_warning != NULL)
{
g_string_append (deprecation_warning, ")");
g_warning ("%s", deprecation_warning->str);
g_string_free (deprecation_warning, TRUE);
}
}
else
{
for (i = 0; i < actual_arg_count; i++)
{
GParamSpec *arg_spec;
gchar *arg_name;
GValue value = G_VALUE_INIT;
if (! sc->vptr->is_arg_name (sc->vptr->pair_car (a)))
{
g_snprintf (error_str, sizeof (error_str),
"Expected argument name for argument %d", i);
return script_error (sc, error_str, 0);
}
arg_name = g_strdup (sc->vptr->string_value (sc->vptr->pair_car (a)));
arg_spec = g_object_class_find_property (G_OBJECT_GET_CLASS (config), arg_name);
if (arg_spec == NULL)
{
g_snprintf (error_str, sizeof (error_str),
"Invalid argument name: %s", arg_name);
g_free (arg_name);
return script_error (sc, error_str, 0);
}
if (i == actual_arg_count - 1)
{
g_snprintf (error_str, sizeof (error_str),
"Lonely argument with no value: %s", arg_name);
g_free (arg_name);
return script_error (sc, error_str, 0);
}
else
{
a = sc->vptr->pair_cdr (a);
i++;
}
g_value_init (&value, arg_spec->value_type);
return_val = script_fu_marshal_arg_to_value (sc, a, proc_name, i, arg_spec, &value, NULL);
if (return_val != sc->NIL)
{
g_value_unset (&value);
g_free (arg_name);
return return_val;
}
g_object_set_property (G_OBJECT (config), arg_name, &value);
g_value_unset (&value);
a = sc->vptr->pair_cdr (a);
} }
g_object_set_property (G_OBJECT (config), arg_specs[i]->name, &value);
g_value_unset (&value);
} }
/* Omit refresh scripts from a script, better than crashing, see #575830. */ /* Omit refresh scripts from a script, better than crashing, see #575830. */
@ -1650,7 +1889,7 @@ script_fu_marshal_drawable_filter_configure (scheme *sc,
g_value_init (&value, arg_spec->value_type); g_value_init (&value, arg_spec->value_type);
a = sc->vptr->pair_cdr (a); a = sc->vptr->pair_cdr (a);
return_val = script_fu_marshal_arg_to_value (sc, a, proc_name, arg_index, arg_spec, &value); return_val = script_fu_marshal_arg_to_value (sc, a, proc_name, arg_index, arg_spec, &value, NULL);
if (return_val != sc->NIL) if (return_val != sc->NIL)
{ {

View file

@ -269,7 +269,7 @@ script_fu_parse_default_spec (scheme *sc,
else if (sc->vptr->is_list (sc, default_spec)) else if (sc->vptr->is_list (sc, default_spec))
{ {
/* default_spec is list of numbers. */ /* default_spec is list of numbers. */
GeglColor *color = marshal_component_list_to_color (sc, default_spec); GeglColor *color = marshal_component_list_to_color (sc, default_spec, NULL);
if (color == NULL) if (color == NULL)
{ {

View file

@ -116,6 +116,7 @@ ts_get_error_string (scheme *sc)
#define TOK_SHARP_CONST 11 #define TOK_SHARP_CONST 11
#define TOK_VEC 12 #define TOK_VEC 12
#define TOK_USCORE 13 #define TOK_USCORE 13
#define TOK_ARG 14
#define BACKQUOTE '`' #define BACKQUOTE '`'
#define DELIMITERS "()\";\f\t\v\n\r " #define DELIMITERS "()\";\f\t\v\n\r "
@ -198,7 +199,8 @@ enum scheme_types {
T_PROMISE=13, T_PROMISE=13,
T_ENVIRONMENT=14, T_ENVIRONMENT=14,
T_BYTE=15, T_BYTE=15,
T_LAST_SYSTEM_TYPE=15 T_ARG_SLOT=16,
T_LAST_SYSTEM_TYPE=16
}; };
/* ADJ is enough slack to align cells in a TYPE_BITS-bit boundary */ /* ADJ is enough slack to align cells in a TYPE_BITS-bit boundary */
@ -241,6 +243,7 @@ static num num_one;
#define type(p) (typeflag(p)&T_MASKTYPE) #define type(p) (typeflag(p)&T_MASKTYPE)
INTERFACE INLINE int is_string(pointer p) { return (type(p)==T_STRING); } INTERFACE INLINE int is_string(pointer p) { return (type(p)==T_STRING); }
INTERFACE INLINE int is_arg_name(pointer p) { return (type(p)==T_ARG_SLOT); }
#define strvalue(p) ((p)->_object._string._svalue) #define strvalue(p) ((p)->_object._string._svalue)
#define strlength(p) ((p)->_object._string._length) #define strlength(p) ((p)->_object._string._length)
@ -442,6 +445,7 @@ static gunichar inchar(scheme *sc);
static void backchar(scheme *sc, gunichar c); static void backchar(scheme *sc, gunichar c);
static char *readstr_upto(scheme *sc, char *delim); static char *readstr_upto(scheme *sc, char *delim);
static pointer readstrexp(scheme *sc); static pointer readstrexp(scheme *sc);
static pointer read_arg_name_exp(scheme *sc);
static INLINE int skipspace(scheme *sc); static INLINE int skipspace(scheme *sc);
static int token(scheme *sc); static int token(scheme *sc);
static void printslashstring(scheme *sc, char *s, int len); static void printslashstring(scheme *sc, char *s, int len);
@ -1159,6 +1163,21 @@ INTERFACE pointer mk_counted_string(scheme *sc, const char *str, int len) {
return (x); return (x);
} }
INTERFACE pointer
mk_arg_name (scheme *sc,
const char *str)
{
gint len = strlen (str);
pointer x = get_cell (sc, sc->NIL, sc->NIL);
typeflag(x) = (T_ARG_SLOT | T_ATOM);
strvalue(x) = store_string (sc, len, str, 0);
strlength(x) = len;
return (x);
}
/* len is the length for the empty string in characters */ /* len is the length for the empty string in characters */
INTERFACE pointer mk_empty_string(scheme *sc, int len, gunichar fill) { INTERFACE pointer mk_empty_string(scheme *sc, int len, gunichar fill) {
pointer x = get_cell(sc, sc->NIL, sc->NIL); pointer x = get_cell(sc, sc->NIL, sc->NIL);
@ -1989,6 +2008,35 @@ static pointer readstrexp(scheme *sc) {
} }
} }
static pointer
read_arg_name_exp (scheme *sc)
{
char *p = sc->strbuff;
gunichar c;
while (TRUE)
{
c = inchar (sc);
if (c == EOF || p - sc->strbuff > sizeof (sc->strbuff) - 1)
{
*p = 0;
return sc->F;
}
else if (g_unichar_isspace (c))
{
*p = 0;
return mk_arg_name (sc, sc->strbuff);
}
else
{
gint len;
len = g_unichar_to_utf8 (c, p);
p += len;
}
}
}
/* check c is in chars */ /* check c is in chars */
static INLINE int is_one_of(char *s, gunichar c) { static INLINE int is_one_of(char *s, gunichar c) {
if (c==EOF) if (c==EOF)
@ -2097,6 +2145,8 @@ static int token(scheme *sc) {
{ return (TOK_EOF); } { return (TOK_EOF); }
else else
{ return (token(sc));} { return (token(sc));}
} else if (c == ':') {
return (TOK_ARG);
} else { } else {
backchar(sc,c); backchar(sc,c);
if(is_one_of(" tfodxb\\",c)) { if(is_one_of(" tfodxb\\",c)) {
@ -4649,6 +4699,14 @@ static pointer opexe_5(scheme *sc, enum scheme_opcodes op) {
} else { } else {
s_return(sc,x); s_return(sc,x);
} }
case TOK_ARG: {
x = read_arg_name_exp(sc);
if (x == sc->F) {
Error_0 (sc, "Error reading argument slot name");
}
setimmutable(x);
s_return(sc,x);
}
case TOK_RPAREN: case TOK_RPAREN:
Error_1 (sc, "syntax error: unexpected right parenthesis", 0); Error_1 (sc, "syntax error: unexpected right parenthesis", 0);
case TOK_DOT: case TOK_DOT:
@ -5097,6 +5155,7 @@ static struct scheme_interface vtbl ={
mk_vector, mk_vector,
mk_foreign_func, mk_foreign_func,
mk_closure, mk_closure,
mk_arg_name,
putstr, putstr,
putcharacter, putcharacter,
@ -5145,6 +5204,8 @@ static struct scheme_interface vtbl ={
is_immutable, is_immutable,
setimmutable, setimmutable,
is_arg_name,
scheme_load_file, scheme_load_file,
scheme_load_string scheme_load_string
}; };

View file

@ -200,6 +200,7 @@ struct scheme_interface {
pointer (*mk_vector)(scheme *sc, int len); pointer (*mk_vector)(scheme *sc, int len);
pointer (*mk_foreign_func)(scheme *sc, foreign_func f); pointer (*mk_foreign_func)(scheme *sc, foreign_func f);
pointer (*mk_closure)(scheme *sc, pointer c, pointer e); pointer (*mk_closure)(scheme *sc, pointer c, pointer e);
pointer (*mk_arg_name)(scheme *sc, const char *str);
void (*putstr)(scheme *sc, const char *s); void (*putstr)(scheme *sc, const char *s);
void (*putcharacter)(scheme *sc, gunichar c); void (*putcharacter)(scheme *sc, gunichar c);
@ -250,6 +251,8 @@ struct scheme_interface {
int (*is_immutable)(pointer p); int (*is_immutable)(pointer p);
void (*setimmutable)(pointer p); void (*setimmutable)(pointer p);
int (*is_arg_name)(pointer p);
void (*load_file)(scheme *sc, FILE *fin); void (*load_file)(scheme *sc, FILE *fin);
void (*load_string)(scheme *sc, const char *input); void (*load_string)(scheme *sc, const char *input);
}; };