MacDevCenter    
 Published on MacDevCenter (http://www.macdevcenter.com/)
 See this if you're having trouble printing code examples


Scripting Cocoa with F-Script

by Philippe Mougin
11/30/2001

Editor's Note -- We're seeing lots of activity in the open-source community around Mac OS X. One of the projects that has caught my eye is F-Script by Philippe Mougin. This lightweight object-oriented scripting layer provides interactive access to Cocoa frameworks and custom objects. I'm presenting this tutorial to you as part of our exploration into Cocoa and its related tools. After you've had a chance to read the text and play with the code, let us know what you think via the TalkBack function. If you find F-Script interesting, we'll publish a couple more articles in this series. Please enjoy! -- Derrick Story, managing editor

Thanks to Objective-C, Cocoa is built on a highly dynamic, reflexive, and open object model. Among other things, this makes it possible to build visual object graph editors like Interface Builder, runtime monitoring tools, object browsers, and numerous other programs making use of the capabilities of the Cocoa runtime.

One important application of Cocoa's openness is its integration with scripting languages, such as Python, JavaScript, Lua, Ruby, and Guile. There are several software packages that provide this kind of integration, both for Mac OS X and for GNUstep. Take a look at, for example, Joy from AAA+, Lua Objective-C from Steve Dekorte, RIGS from Laurent Julliard, GNUstep Guile from Richard Frith-MacDonald, and StepTalk from Stefan Urbanek. These are very useful tools because they allow for interactive use and easy glueing of Cocoa components.

Learning CocoaLearning Cocoa
By Apple Computer, Inc.
Table of Contents
Index
Sample Chapter
Full Description
Read Online -- Safari

In this article, I want to give you a taste of Cocoa scripting, and show you the level of integration you can expect. We will use F-Script, an open-source scripting language for Cocoa, to build a graphical application. Using F-Script, we'll directly program against the Application Kit, which is the Cocoa object framework for graphical user interfaces.

Note that one can build this kind of application using Interface Builder, but in this article, we will use the Application Kit programmatically, in order to illustrate Cocoa scripting.

Hey, another currency converter

Our application is a currency converter, similar to the one found in Apple tutorials (see: /Developer/Documentation/Cocoa/ObjCTutorial/CurrencyConverterTutorial.pdf).


Fig. 1. The currency converter we will build using F-Script and Cocoa.

F-Script

You can obtain F-Script at fscript.org. In this article, we use F-Script 1.1 on Mac OS X.

F-Script is a pure object-oriented scripting language with Smalltalk-like syntax and concepts. You can interact with F-Script through the "fs" application, which gives you an interactive shell into which you can type instructions.

Some pointers on the F-Script syntax:

The currency converter script, version 1

The following script can be entered into the F-Script shell using a simple copy/paste. You can also enter the instructions step by step: You'll then see the currency converter interface appearing bit by bit. Of course, you can change the values of arguments used in the script or omit some instructions in order to see what happens. You can even interact with the objects after you have built the currency converter.

F-SCRIPT CURRENCY CONVERTER (version 1)


"Instantiate and configure a window"
window := NSWindow alloc initWithContentRect:(125<>513 extent:400<>200)      
          styleMask:NSTitledWindowMask + NSClosableWindowMask
                    + NSMiniaturizableWindowMask + NSResizableWindowMask  
          backing:NSBackingStoreBuffered 
          defer:NO.

"Put the window onscreen"
window orderFront:nil.

"Give a title to the window"
window setTitle:'Currency Converter'.

"Instantiate a form object"                         
form := NSForm alloc initWithFrame:(60<>90 extent:320<>85).

"Put the form into the window"
window contentView addSubview:form.

"Configure the form"
form addEntry:'Exchange Rate per $1'.
form addEntry:'Dollars to Convert'.
form addEntry:'Amount in Other Currency'.
form setAutosizesCells:YES.

"Instantiate a decorative line and put it in the window"
line := NSBox alloc initWithFrame:(15<>70 extent:370<>2).
window contentView addSubview:line.

"Instantiate a button, put it in the window and configure it"
button := NSButton alloc initWithFrame:(250<>20 extent:90<>30).
window contentView addSubview:button.
button setBezelStyle:NSRoundedBezelStyle.
button setTitle:'Convert'. 

"Instantiate the script that will compute the currency conversion"
conversionScript := [(form cellAtIndex:2) setStringValue:(form cellAtIndex:0) floatValue 
* (form cellAtIndex:1) floatValue. form selectTextAtIndex:0].

"Make the script the target of the form"  
"The script will be evaluated when the user presses Return"
form setTarget:conversionScript.
form setAction:#value.

"Make the script the target of the button"
"The script will be evaluated when the user presses the button"     
button setTarget:conversionScript.
button setAction:#value.


We note several interesting things straight away:

The actual currency conversion is computed by an object created using the following instruction:

conversionScript := [(form cellAtIndex:2) setStringValue:(form cellAtIndex:0) floatValue 
  * (form cellAtIndex:1) floatValue. form selectTextAtIndex:0].

The [...] notation creates an object of class Block which represents a block of code that can be executed later (Block is an Objective-C class provided by the F-Script framework). In our block, we simply get the values of the fields in the user-interface objects, perform the computation (simply involves multiplication) and put the result in a UI element. Once it's done, we select the text in the first field of the form to mimic the Apple currency converter behavior.

Further on in the code, our block object becomes the target of the form and button objects. Thus, it is evaluated when the user hits Return or clicks on the button.

The currency converter script, version 2

Below is the second version of the currency converter. This script is shorter because we use some neat F-Script features that we avoided in the first version for simplicity's sake. We also removed comments (the code speaks for itself) and reorganized the program a little bit. The interface is now entirely constructed and configured before being put onscreen. Finally, we added support for the Cocoa memory management system to ensure that the various Cocoa objects we create will be destroyed when no longer in use.

F-SCRIPT CURRENCY CONVERTER (version 2)

window := NSWindow alloc initWithContentRect:(125<>513 extent:400<>200) 
                   styleMask:NSTitledWindowMask + NSClosableWindowMask 
                             + NSMiniaturizableWindowMask + NSResizableWindowMask  
                   backing:NSBackingStoreBuffered 
                   defer:NO.

conversionScript := [(form cellAtIndex:2) setStringValue:(form cellAtIndex:0) floatValue 
* (form cellAtIndex:1) floatValue. form selectTextAtIndex:0].
  
form := (NSForm alloc initWithFrame:(60<>90 extent:320<>85)) autorelease.
form addEntry:@{'Exchange Rate per $1','Dollars to Convert','Amount in Other Currency'}.
form setAutosizesCells:YES; 
setTarget:conversionScript; setAction:#value.

button := (NSButton alloc initWithFrame:(250<>20 extent:90<>30)) autorelease.
button setBezelStyle:NSRoundedBezelStyle; 
setTitle:'Convert'; setTarget:conversionScript; setAction:#value.

line := (NSBox alloc initWithFrame:(15<>70 extent:370<>2)) autorelease.
  
window contentView addSubview:@{form, button, line}. 
window setTitle:'Currency Converter'; orderFront:nil.


One of the new things we use in this version is the ; notation for cascading messages. This notation enables us to send several messages to a single receiver without having to re-specify the receiver each time.

Another interesting thing is the instruction:

form addEntry:@{'Exchange Rate per $1','Dollars to Convert','Amount in Other Currency'} 

One of the innovative features of F-Script is that it allows us to manipulate entire groups of objects at once, even with methods that have not been specifically designed to support objects collections (actually, F-Script provides a full object-query language, directly usable on Cocoa objects).

This is the case in this instruction where we add a whole list of entries to the form at once. We use the "message pattern" notation (denoted by @) that allows us to specify potentially complex groups of message sends. A message pattern generally involves single or multiple collections of objects: In our example, we use an array of string objects, denoted by { and }. At runtime, the instruction will trigger the generation of these three message sends:


form addEntry:'Exchange Rate per $1'
form addEntry:'Dollars to Convert'
form addEntry:'Amount in Other Currency'

The same pattern is also used in the instruction "window contentView addSubview:@{form, button, line}" where we put a whole set of views into the window at once.

In these examples, we use a relatively simple pattern. F-Script provides a complete syntax that makes it possible to express a broad range of message patterns. All the specific concepts of F-Script, like message patterns, can be used when scripting Cocoa.

The currency converter script, version 3

Comment on this articleNow that you've had a chance to explore F-Script, let us know what you think. Also, if you have any questions for Philippe, he'd be glad to respond.
Post your comments

As it stands, our script is not very modular: It's just a set of instructions, using global variables, that we paste into the F-Script console in order to execute them. The following version introduces modularity:

F-SCRIPT CURRENCY CONVERTER (version 3)


converter := [:title | |window conversionScript form button line|
  window := NSWindow alloc initWithContentRect:(125<>513 extent:400<>200) 
                     styleMask:NSTitledWindowMask + NSClosableWindowMask 
                               + NSMiniaturizableWindowMask + NSResizableWindowMask  
                     backing:NSBackingStoreBuffered 
                     defer:NO.

  conversionScript := [(form cellAtIndex:2) setStringValue:(form cellAtIndex:0) 
  floatValue * (form cellAtIndex:1) floatValue. form selectTextAtIndex:0].
  
  form := (NSForm alloc initWithFrame:(60<>90 extent:320<>85)) autorelease.
  form addEntry:@{'Exchange Rate per $1', 'Dollars to Convert', 'Amount in Other Currency'}.
  form setAutosizesCells:YES; setTarget:conversionScript; setAction:#value.

  button := (NSButton alloc initWithFrame:(250<>20 extent:90<>30)) autorelease.
  button setBezelStyle:NSRoundedBezelStyle; setTitle:'Convert';
setTarget:conversionScript; setAction:#value.

  line := (NSBox alloc initWithFrame:(15<>70 extent:370<>2)) autorelease.
  
  window contentView addSubview:@{form, button, line}. 
  window setTitle:title; orderFront:nil.
]

In this version, we put the instructions between [ and ] to make the F-Script interpreter generate a Block object. We also declare, on the first line of the script, the argument and the local variables. Finally, we give the name "converter" to the Block object that represents our script.

We can now invoke our script by sending it a value message without forgetting to provide the required argument. For instance:


converter value:'This converter is nice!'

Each time we invoke it, a new, fully functional currency converter is created and displayed onscreen.

Because our script is now an object of class Block, we can manipulate it like any other object: put it in a collection, pass it as an argument to methods, archive it on disk, and more. There are also several facilities specifically related to Block objects, including a graphical code editor.

Finally, because F-Script comes in the form of a framework ready to be embedded into any Cocoa application, it is easy to place our script in a standard Mac OS X executable.

Final thoughts

We have seen what scripting Cocoa with F-Script involves. Clearly, many subjects have not been tackled in this article, such as Cocoa exception handling, object archiving, Interface Builder integration, usage of custom Objective-C classes, or mapping of non-object types, but you can expect a very high level of integration on these aspects.

Philippe Mougin specializes in object-oriented technologies and enterprise systems. He is the creator of F-Script, an open-source object-oriented scripting language for Cocoa.


Return to the Mac DevCenter.

Copyright © 2009 O'Reilly Media, Inc.