Script-Fu In GIMP 2.4

Since version 1.0 of GIMP, it has included a powerful scripting language which permits extending the program's capabilities and simplifying repetitive tasks. This scripting language, called “Script-Fu”, was based upon the Scheme programming language and implemented the SIOD interpreter written by George J. Carrette while he was a professor at Boston University in the late 80s.

This Script-Fu interpreter based upon Carrette's SIOD has served GIMP extremely well over the last decade – thousands of scripts have been written and shared by GIMP users – but it is starting to show its age and therefore the GIMP development team has decided to replace it with a more modern Scheme interpreter called TinyScheme. One of the main reasons for this changeover is to support international languages and fonts, for which SIOD offered no provision. There are other benefits as well, but lack of international support was the most significant.

Though this switch has required an extensive effort on the part of GIMP developers (particularly Kevin Cozens) and some significant changes to the internals of the GIMP code, there should be very little visible change to GIMP users. GIMP's scripting extension is still called “Script-Fu” and the vast majority of the scripts already available will still function using the new TinyScheme-based interpreter.

Despite the desire to keep the impact of this change to GIMP internals to a minimum, there are some differences between the SIOD-based interpreter and the TinyScheme-based Script-Fu which may crop up when trying to use older scripts with GIMP 2.4. What follows is a description of some of the problems which may be encountered and what steps need to be taken to correct them.

Setting an undeclared variable

By far, the most common problem that can be expected if using an older script is that it might assign a value to a variable without first declaring the variable. SIOD-based Script-Fu would permit a statement such as (set! x 4) even if 'x' had not been declared – 'x' would be defined automatically to be a global variable. The new Script-Fu protects against this situation and the programmer must declare the variable first. The offending script would result in an error message stating, “Error: set!: unbound variable: x”.

The use of global variables is generally discouraged because another function (written by a different author) may have chosen to use the same name and the two functions would interfere with each other. For this reason, the correct method of declaring 'x' in the preceding example is to use the let or let* statement:

(let* ( (x 4) )
  ...
  ...
  ...
)

Variables in a let block must have an initial value

When the Scheme interpreter in GIMP was changed from SIOD to TinyScheme, many scripts broke due to some variables being used before they had been declared. In some scripts the problem was “fixed” by adding the variable name to the let block as shown below.

(let* ( (x) )
  ...
)

The above is not valid Scheme syntax and was only accepted due to a bug in TinyScheme. The bug was fixed in the 2.5.1 development release of GIMP. All variables appearing in the bindings section of a let block *must* include an initial value. It doesn't matter what value is used in the binding. The easiest way to fix the problem is to add a 0 (or -1 for image and drawable IDs) to the declarations for numeric variables or ”” for string variables. Any value included in the declaration will work but it helps to use a value of the same type as the variable would typically hold.

(let* ( (x 0) )
  ...
)

NOTE: The change in TinyScheme affects the let, let*, and letrec binding constructs.

Using the empty list in conditionals

SIOD treated the empty list to be FALSE when it appeared in a conditional test (if, while, cond, not, =, etc) whereas the Scheme standard specifies that it should evaluate to TRUE. Programmers have been aware of this difference since the beginning and it is unlikely that scripts will be encountered which rely upon SIOD's nonstandard behavior but it is possible. A simple solution is to use the 'pair?' function to test the list. For example, replace (while lis …) with (while (pair? lis) …). Alternately, (not (null? lis)) could be used instead of (pair? lis).

Accessing the first element of an empty list

In SIOD, taking the 'car' of an empty list returned an empty list; in TinyScheme this is not permissible and will generate an error message (“Error: car: argument 1 must be: pair”). Like the case for conditionals, programmers have been aware of SIOD's nonstandard behavior and encountering this problem should be rare. Correcting such a problem, if encountered, should consist of testing whether a list is empty before accessing it.

Accessing beyond the last element of a list

Similar to the preceding problem, SIOD would permit you to access beyond the last element in a list, returning an empty list as a result. For example, taking the 'cdr' of an empty list or the 'cddr' of a one-element list. In GIMP 2.4, Script-Fu will not allow this and it will result in an error message (“Error: cdr: argument 1 must be: pair”). Again, SIOD's behavior has long been realized to be non-standard and this problem's occurance should be rare. Correcting such a problem, if encountered, should consist of more precise testing when accessing a list.

Constructing a pair

The Scheme cons function expects two arguments which are combined into a pair. In SIOD, if only one argument was provided then the second argument was assumed to be an empty list. In GIMP 2.4, if the second argument is not present than an error occurs (“Error: cons: needs 2 argument(s)”). The solution, should this problem be encountered, is explicitly include an empty list as the second argument.

Deprecated functions

The items listed below are functions and constants which were defined in the SIOD Scheme interpreter but are not part of the TinyScheme interpreter. GIMP 2.4 ships with a file (script-fu-compat.scm) which contains a number of SIOD compatability functions and constants. This file is automatically loaded when the Script-Fu plug-in starts.

The list below are the functions and constants available in the SIOD compatability file and the TinyScheme equivalent (if any). The items listed are not recommended for use in new scripts.

  • aset - replaced by vector-set!
  • aref - replaced by vector-ref
  • butlast
  • cons-array - replaced by make-vector (except for string arrays)
  • fmod
  • fopen - replaced by open-input-file
  • fread
  • last
  • mapcar - replaced by map
  • nil - replaced by '()
  • nreverse - replaced by reverse
  • (nth k list) - replaced by (list-ref list k)
  • *pi* - It is defined as (* 4 (atan 1.0)) in the SIOD compatability routines
  • pow - replaced by expt
  • prin1 - replaced by write
  • print - replaced by write followed by newline
  • prog1
  • strcat - replaced by string-append
  • strcmp
  • string-lessp - replaced by string<?
  • symbol-bound? - replaced by defined? (This appears to be outside of the R5RS but the interpreter has it so lets use it)
  • the-environment - replaced by current-environment
  • trunc - replaced by truncate
  • verbose

Incompatible functions and features

  • A call to 'fread' which passes a buffer is not supported.
  • There is no direct equivalent for SIOD's 'length' function. In TinyScheme it returns the length of a list.
  • number→string can take up to four parameters in SIOD. You can only use the first two in TinyScheme.
  • rand and random do not have a direct equivalent in TinyScheme. A compatible function for 'random' is defined in the SIOD compatability routines.
  • string-append allows you to append a character to a string in SIOD. To do this in TinyScheme you need to use make-string.

Additional differences

There are a number of other differences between SIOD and TinyScheme that can not be handled by compatibility functions. Some of these are optional changes as they would prevent a script from running in older versions of GIMP.

  • prog1 - replaced with begin (with order of arguments reversed).
  • Convert 'while' blocks to 'do' blocks with the loop termination test negated (ie. >= becomes <).
  • Replace use of SIOD array routines with the new array routines (?)
    • Change '(cons-array count type)' to '(make-array count type)' and change the value for type.
    • Change 'long to “int32”, 'short to “int16”, 'byte to “int8”, 'double to “float”, and 'string to “string”.
    • Change '(aref var index)' to '(array-ref var index)'
    • Change '(aset var index value)' to '(array-set! var index value)'

Conclusion

There are some other differences between the original Script-Fu and the Script-Fu of GIMP 2.4 but they should have little or no impact on existing scripts because of their rarity. These include the syntax for the catch/throw statements (which trap errors) and the bytes-append function (which does not seem to appear in any published Script-Fu). If you encounter scripts containing such problems, please post on the GIMP developers mailing list outlining your problems.

— Original text by Saul Goode 2007/10/04. Updates and additions by Kevin Cozens 2007/11/05.

1) Add information about use of booleans in 'if' statement conditional.
Add information about privatization of functions defined in script files.
software/sf/updating-scripts.txt · Last modified: 2012/09/21 14:08 (external edit)
 
Except where otherwise noted, content on this wiki is licensed under the following license: CC Attribution-Noncommercial-Share Alike 3.0 Unported
Valid XHTML 1.1 Valid CSS! Best viewed with any browser Ohloh profile for Kevin Cozens Driven by DokuWiki Recent changes RSS feed