diff --git a/content/resource/script-fu/_index.md b/content/resource/script-fu/_index.md index 9795d99fa2605a46dbb330e64d3f573b8eb2e6af..b96e1b2e9d2a734ec75941590e2b7044773bfe79 100644 --- a/content/resource/script-fu/_index.md +++ b/content/resource/script-fu/_index.md @@ -20,5 +20,6 @@ Changes in ScriptFu 3 * [Changes in ScriptFu version 3](script-fu-changes-v3/) * [Adapting scripts to PDB version 3](porting_scriptfu_scripts/) +* [Using the version 3 dialect for values to and from the PDB](using-v3-binding) diff --git a/content/resource/script-fu/using-v3-binding.md b/content/resource/script-fu/using-v3-binding.md new file mode 100644 index 0000000000000000000000000000000000000000..467f88076f48f96f6f9e755c3e95fc84be091105 --- /dev/null +++ b/content/resource/script-fu/using-v3-binding.md @@ -0,0 +1,373 @@ ++++ +title = "Using the version 3 dialect for PDB values" +description = "An optional new binding of values to and from the PDB" +date = "2024-10-24" +weight = 3 +author = "Lloyd Konneker" ++++ + + +## About + +This describes a new dialect of the ScriptFu language. +ScriptFu interprets the dialect after a script calls: (script-fu-use-v3). +The new dialect is more like Scheme and makes scripts shorter. +The dialect only affects calls to the PDB. +ScriptFu interprets the old version 2 dialect for PDB binding by default, +unless a script calls script-fu-use-v3. +In other words, it is optional to use the dialect. + +Note we say "ScriptFu version 3" to mean: all the changes in ScriptFu, +the ScriptFu system and the ScriptFu language, +accompanying GIMP version3. +We say "the v3 dialect of ScriptFu" to mean the optional dialect. + +The audience is script authors and other developers. + +This describes the new dialect and how to port old scripts to the new dialect. + + +## Quick Start + +ScriptFu inteprets the old version 2 by default, +unless a script calls script-fu-use-v3. +After a call to script-fu-use-v3, +ScriptFu interprets the new dialect until the script finishes, +or until the script calls script-fu-use-v2. +A script can switch back and forth between dialects at runtime. + +A script that calls: +``` +(script-fu-use-v3) +``` +binds to certain PDB calls differently: + +1. PDB procedures that return single values (atoms) return just that single value, +*not wrapped in a list.* +Formerly, every PDB call returned a list (possibly nesting more lists.) + +2. You can use #t and #f as arguments to PDB calls taking a boolean. + +3. PDB calls returning a boolean return #t or #f, not TRUE or FALSE (1 or 0.) + +## Script-Fu Console + +The Script-Fu Console always starts in the v2 dialect. + +You can call *script-fu-use-v3* in the console. +Then, the console interprets the v3 dialect. +It continues to interpret the v3 dialect in that session, +until you call *script-fu-use-v2.* + +## Where to call *script-fu-use-v3* in scripts + +Call *script-fu-use-v3* at the beginning of the run function. +This sets the dialect version for the remaining interpretation +of the script. + +!!! Do not call *script-fu-use-v3* at the top level of a script. +This has no effect, since it is only executed in the query phase +of a plugin. +The interpreter starts at the run function during the run phase. + +*The interpreter always starts interpreting each script in the v2 dialect. +This is true even for extension-script-fu, the long-running interpreter.* +There is no need to call *script-fu-use-v2* before returning from a script, +to ensure that the next script is interpreted in v2 dialect. + +Example: + +``` +(define (script-fu-testv3 img drawables ) + (script-fu-use-v3) ; <<< + (let* ( + ... +``` +### Scope + +The dialect version has "execution scope" versus "lexical scope." +Setting the dialect version is effective even for +other functions defined in the same script but lexically +outside the function where the dialect is set. +You only need to call script-fu-use-v3 once, +not in every defined function. + + +## Don't call v2 scripts from v3 scripts + +When using the v3 dialect, +you cannot call plugin Scheme scripts or other library scripts that are in the v2 dialect. +And vice versa. +(When a script calls a PDB procedure that is a Scheme script, +a new interpreter process is *NOT* started.) + +For example, a new plugin script should not call the PDB procedure +script-fu-add-bevel because it is a Scheme script in v2 dialect +and for example has: + +``` +(width (car (gimp-drawable-get-width pic-layer))) +``` +which is v2 dialect and would throw an error such as: +``` +Error: car requires a pair. +``` + +*It is rare that a script calls another plugin script.* +A script usually calls the PDB, +but rarely calls a plugin script of the PDB. + +There are very few, obscure library scripts that call the PDB using v2 dialect. +These are in scripts/script-fu-util.scm. + +## Pure Scheme is unaffected + +The dialect only affects calls the to PDB. + +This means you can usually call most library scripts when using v3, +since most library scripts are pure Scheme, +that is, with no calls to the GIMP PDB. + +## TRUE and FALSE + +TRUE and FALSE are still in v3 dialect and are still numbers 1 and 0. +But we recommend not using them. + +You can still pass them as arguments to PDB calls taking a boolean, +and they are still converted to the C notion of boolean truth. + +FALSE which is 0 is truthy in Scheme!!! +It converts to the C notion of false only in a call the the PDB. +In the ScriptFu Console: +``` +>(equal? FALSE #t) +#t +``` + +TRUE and FALSE symbols may become obsolete in the future. + +## An argument of type SF-TOGGLE is FALSE or TRUE, not #f or #t + +The v3 dialect does not affect the binding of arguments to a script. +So the value of an argument of type SF-TOGGLE is zero or one, +not #f or #t. + +You must continue to check boolean arguments to a script like this: + +``` +(define script-fu-my-plugin (should-invert) + (if (= should-invert TRUE) + ( + ; do invert + ))) + +(script-fu-register-procedure "script-fu-my-plugin" + "My plugin..." + ... + SF-TOGGLE "Invert?" FALSE +) +``` +This may change when in the future we obsolete v2 dialect +and the symbols TRUE and FALSE. + +## Plans for the future + +This dialect is shorter and more natural for Scheme programmers. + +The long-term goal is for the v3 dialect to be the only dialect of ScriptFu. +For the short term, for backward compatibility, +the default dialect of ScriptFu is the v2 dialect. + +You should write any new scripts in the v3 dialect, +and call *script-fu-use-v3*. + +You should plan on porting existing scripts to the v3 dialect, +since eventually ScriptFu may obolete the v2 dialect. + +## Example conversions from v2 to v3 dialects + +### An example script using v3 dialect + +``` +; !!! Usually not call (script-fu-use-v3) here in global scope + +(define script-fu-my-plugin (should-invert) + ; the body switches to the v3 dialect + (script-fu-use-v3) + + (let* ( + ; don't need a car, unlike v2 + (angle gimp-context-get-brush-angle))) + + ; call PDB returning boolean + ; don't need (= (car (gimp-context-get-feather)) TRUE) + (if (gimp-context-get-feather) + ; do feather + ) + + ; boolean arg to script is still C notion of truth + (if (= should-invert TRUE) + ( + ; do invert + )) + + ; calling a v2 plugin, temporarily switch to v2 dialect + (script-fu-use-v2) + (script-fu-add-bevel ...) + + ; rest of script in v3 dialect + (script-fu-use-v3) + ... +) + +(script-fu-register-procedure "script-fu-my-plugin" + "My plugin..." + ... + SF-TOGGLE "Invert?" FALSE +) +``` + + +### A call to a PDB procedure returning a single value + +``` +(set! (width (car (gimp-drawable-get-width pic-layer)))) +``` +*must* become +``` +(set! (width (gimp-drawable-get-width pic-layer))) +``` +The PDB call returns a single integer, +so no *car* is needed. + +### A call to a PDB procedure returning boolean +``` +(if (= (gimp-image-is-rgb image) TRUE) ... +``` +*must* become: +``` +(if (gimp-image-is-rgb image) ... +``` +The PDB procedure returns a boolean, +which is bound to #t or #f, +the usual symbols for Scheme truth. + +### A call to a PDB procedure taking a boolean + +``` +(gimp-context-set-antialias TRUE) +``` +*should* become +``` +(gimp-context-set-antialias #t) +``` +This doesn't *require* conversion because TRUE is 1 and is truthy in Scheme. + +!!! But FALSE is 0 and 0 is truthy in Scheme +``` +(gimp-context-set-antialias FALSE) +``` +This *should* be converted, for clarity, but doesn't *require* conversion. +For now, ScriptFu still binds Scheme 0 to C 0. +So it still does what the script intends. + +### A call to a PDB procedure returning a list + +``` +(set! brushes (car (gimp-brushes-get-list))) +``` +*must* become: +``` +(set! brushes (gimp-brushes-get-list)) +``` +Formerly, the PDB procedure returned a list wrapped in a list, +i.e. ((...)) +In the v3 dialect, it returns just a list, i.e. (...) +which is a single value, a single container. + +### A call to a PDB procedure returning a list, getting the first element + +``` +(set! first-brush (caar (gimp-brushes-get-list))) +``` +Gets the first brush in GIMP. +This *must* become: +``` +(set! first-brush (car (gimp-brushes-get-list))) +``` +The call to *caar* is consecutive calls to *car*, +and you must eliminate the first call to *car*. + + +## Knowing what constructs need conversion + +You *should* (but are not required to) +eliminate most uses of symbols TRUE and FALSE from a script +using v3 dialect. + +You should eliminate many, *but not all*, uses of the *car* function wrapping a call to the PDB, +where the PDB procedure returns a single value. +There are several hundred such PDB procedures in the PDB. +Examine the signature of a PDB procedure using the PDB Browser. + +For example, gimp-brush-get-angle: +``` +Return Values + angle gdouble .... +Additional Information +``` +This returns a single value so no need to use car. + +For example, gimp-brushes-get-list: +``` +Return Values + brush-list GStrv +Additional Information +``` +This also returns a single value. +But the single value is a list i.e. container, of strings. +In the v2 dialect, this returned a list wrapped in a list, +for example (("foo" "bar")). + +See "Example conversions from v2 to v3" + +### Does not require changes to calls to PDB procedures returning void + +You should not need to change scripts on calls to PDB procedures returning C void because a script should not be examining the result. + +Some scripts might examine the result of a call to a void PDB procedure, thinking that (#t) is the success of the call, +but that is a misconception and should be fixed. + +Calls to PDB procedures that return C void return (#t) in v2 and the empty list () i.e. nil in v3 dialect. + +## Details of the implementation + +There are new functions in the dialect: +``` +script-fu-use-v3 +script-fu-use-v2 +``` +These functions have side-effects on the state of the interpreter. +They always return the empty list (). + +The effect is immediate. +The interpreter interprets a dialect from then on, +until the script returns, +or until the script changes the dialect again. + +A call to *script-fu-use-v3* sets a flag in the state of the interpreter. +When the flag is set, the interpreter binds arguments to PDB calls slightly differently, as described. +Binding of args to PDB calls is done at run time. + +Similarly, a call to *script-fu-use-v2* clears the flag +and restores the interpreter state to binding using the v2 dialect. + +When in the v3 state, +any PDB call constructs using v2 binding will yield errors, +and vice versa. + +Note that the difference in interpretation is only in binding, but both to and from the PDB: + +1. Single return values *from* the PDB are not wrapped in lists. +2. Boolean return values *from* the PDB are bound to #t and #f. +3. Boolean values *to* the PDB can be #t and #f.