Embedding F-Script into Cocoa Applications
Pages: 1, 2, 3
Using the FSInterpreter Class
The technique described above is very easy to use and suitable for numerous situations. Nonetheless, certain cases require more control and additional functionalities.
In such cases we can look to the FSInterpreter class, which offers total control of the F-Script interpreter. Among other things, this class offers:
- Easier and more powerful error management.
- Context conservation, in other words, the possibility of executing different bits of F-Script code at different times in the same workspace.
An FSInterpreter instance represents an F-Script interpreter associated with a workspace. It is easily created in Objective-C:
FSInterpreter *myInterpreter = [[FSInterpreter alloc] init];
An interpreter enables you to add variables in the associated workspace or to modify the value of existing variables:
[myInterpreter setObject:[NSDate date] forIdentifier:@"myDate"];
An interpreter enables you to consult the value of a variable in the workspace:
BOOL found;
id result = [myInterpreter objectForIdentifier:@"myDate" found:&found];
It's also possible to retrieve the list of names of variables defined in the workspace, in the form of an array of NSStrings:
NSArray *identifiers = [myInterpreter identifiers];
To execute F-Script code, pass a character string containing this code to the execute: method of the interpreter. You will get back an FSInterpreterResult object. The following example uses the F-Script "hello world" code:
FSInterpreterResult *result = [myInterpreter execute:@"sys log:'hello world'"];
The FSInterpreterResult object offers complete control over the execution result. The isSyntaxError, isExecutionError, and isOk methods return a Boolean, which enables us to know the status of the result. In the event of an error, the errorMessage and errorRange methods enable us to obtain the error message and relevant location in the F-Script source code. The inspectBlocksInCallStack method lets us open the graphical inspector of the blocks present in the callstack corresponding to the error. If execution does not throw an error, the result method enables us to get the result of the evaluation of the F-Script code.
To illustrate this API, here's a complete program, to be compiled in Project Builder like a Foundation Tool and launched from the Project Builder or a UNIX terminal window. It's a command line interface for F-Script: the program reads a command from its standard input, executes it, prints the result on its standard output and then starts again. Note that this is a non-graphical program (does not use the application kit). It therefore does not allow the F-Script graphic functionalities to be used.
#import <stdio.h>
#import <Foundation/Foundation.h>
#import <FScript/FScript.h>
int main (int argc, char **argv, char **env)
{
FSInterpreter *interpreter;
FSInterpreterResult *execResult;
char c_command[10000];
NSString *command;
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
interpreter = [[FSInterpreter alloc] init]; // create the interpreter
[pool release];
while(1)
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
command = [NSString stringWithCString:fgets(c_command, 10000, stdin)];
execResult = [interpreter execute:command]; // execute the F-Script command
if ([execResult isOk]) // test status of the result
{
id result = [execResult result];
// print the result
if (result == nil) puts("nil");
else puts([[result printString] cString]);
if (![result isKindOfClass:[FSVoid class]]) putchar('\n');
}
else
{
// print an error message
puts([[NSString stringWithFormat:@"%@ ,
character %d\n",[execResult errorMessage],[execResult errorRange].location]
cString]);
}
[pool release];
}
return 0;
}
FSInterpreterView Component and Palette for Interface Builder
The FSInterpreterView class -- a subclass of NSView -- offers an interactive, command line interface-type F-Script graphical component. The component has its own F-Script interpreter and is ready to use. It may be instantiated and manipulated by program.
|
|
This object has methods that allow you to modify the font size, display a service message for the user, insert a command or retrieve the associated FSInterpreter object.
The following simple program opens a window containing an FSInterpreter View.
#import <Cocoa/Cocoa.h>
#import <FScript/FScript.h>
int main(int argc, const char *argv[])
{
NSApplication *NSApp = [NSApplication sharedApplication];
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Create a window
NSWindow *mainWindow = [[NSWindow alloc]
initWithContentRect:NSMakeRect(100,100,500,400) styleMask:NSClosableWindowMask |
NSTitledWindowMask backing:NSBackingStoreBuffered defer:false];
// Create an FSInterpreterView
FSInterpreterView *fscriptView = [[[FSInterpreterView alloc]
initWithFrame:NSMakeRect(0,0,0,0)]
autorelease];
// Insert the FSInterpreterView inside the window
[mainWindow setContentView:fscriptView];
[fscriptView setFontSize:16]; // We want big fonts !
[mainWindow orderFront:nil];
[pool release];
[NSApp run];
return 0;
}
A FSInterpreterView may also be manipulated directly from Interface Builder using the FScriptPalette.palette.
|
|
The palette enables an FSInterpreterView to be placed in a graphical interface using a simple drag and drop movement.
It also offers another functionality--live mode--which enables F-Script to be used directly from Interface Builder, without being in test mode. In live mode, it is possible to set up connections between interface objects and F-Script objects (for example, set up a connection between a button and a block) and to save everything in the nib file. Using this feature it is possible to build a graphical application entirely from Interface Builder by associating F-Script code blocks with interface elements.
|
|
To use live mode, first you instantiate an F-Script interpreter. To do so, simply drag and drop from the F-Script palette to any other window. The instantiated FSInterpreterView object contains the F-Script interpreter, which enables operation in live mode. To activate this mode, double-click on the instance of FSInterpreterView. An active F-Script window appears from which it will be possible to configure interface objects (and their outlets) by using F-Script instructions directly. The FSInterpreterView is used for setting up connections between the F-Script interpreter and external objects (see Figure 4).
Final Thoughts
In this article we showed how Objective-C developers can embed F-Script into their applications. But what about existing, third-party, applications? Here comes F-Script Anywhere.
This is an amazing new tool, built by Nicholas Riley, which lets you dynamically embed F-Script at runtime into any Cocoa application and lets you take control of the objects inside the application and freely play with them. You can get F-Script Anywhere from Nicholas' Web site.
Also, don't forget to drop by the F-Script site to keep up on latest releases that you can download. You might also want to read the previous articles in this series, and take a look at their TalkBacks. Enjoy!
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.
You must be logged in to the O'Reilly Network to post a talkback.
Showing messages 1 through 22 of 22.
-
example parameters and result
2002-08-07 15:32:56 psheldon [Reply | View]
-
example parameters and result
2002-08-09 10:16:23 pmougin [Reply | View]
These warning were, as you said, innocuous. But note that this was specific to this use-case with Airplane. Actually, this kind of warning is not innocuous in the general case. It signal that the compiler do not have all the type information to generate a call to some methods. The compiler then fall back to a default typing rule, which may not actually be ok, depending on your real method signature. In this case, your application will have run-time errors.
Phil -
thank you, the use of warnings
2002-08-10 08:40:18 psheldon [Reply | View]
Now I know what warnings are for! They are nice. In my early experience of programming, when I got past listings with all their error messages, I would announce, attempting to smile to the boss, I am now ready for "trial by execution". The sentence embodied an unfair legal system much as the puritan dunking stool. Maybe the phrase, "getting your feet wet" is a euphemism for the way I should have felt back then.
The boss had the luxury of irresponsibility and would sit back and see what happened. I was paid to be responsible if it didn't work. I see that bosses nowadays have also paid compiler designers to be responsible. I imagine compiler designers are left to do the trial by execution. That is what Apple tried to have the community understand when it had us users be beta testers and participate in development.
-
IBLiveMode example needed story detailing scripting
2002-08-07 15:10:18 psheldon [Reply | View]
Evidentally, project builder and interface builder files aren't sufficient to let me see the story behind what makes code work.
I didn't know what object1 etc. were. I might guess that each time I enter an F-Script into the double click LiveMode I specify the next object# source. Is that so?
I couldn't see the script lines. That would have made source and destination clearer in inspector. This confuses me and hints at an emerging understanding. In the MVC model, I believe, F-Script is being used as a controller and there are view objects being associated with scripts specifying actions and outlets.
Under the hypothesis that object# is a script object, nothing goes to the script as an action. But, this jibes with my other aha, "Objects ie. nouns aren't actions ie. verbs". Somehow, being wired from an object containing a method makes a control both able to act and respond. That is surprising and constitutes, thereby, information.
I need more detailing in a column, please. I want to do livemode but right now can't. This is fundamental stuff that will be in hindsight wonderfully obvious.
-
IBLiveMode example needed story detailing scripting
2002-08-09 10:08:25 pmougin [Reply | View]
Note that object# variables are not script objects. In fact they are outlets that allows you to connect from F-Script to “external” object in IB. When you establish a connection from the F-Script window to an other objet in IB (via the IB connection mechanism (i.e. CONTROL+Drag)) then the connection inspector let you assign the target of the connection to one of the object# outlets. Once this is done, the object# variable becomes defined in the F-Script workspace and will evaluates to the connected object (let’s say, for example, a button). THEN, since you now have a handle on the button, you can configure it, from F-Script, the way you like. For instance, supposing you have connected the object1 outlet to a button, you can assign the button a block as target by typing in the live mode F-Script window:
object1 setTarget:[sys beep]. object1 setAction:#value:
When the button will be pressed, the block will be evaluated and play a beep…
Note that understanding F-Script live mode is not trivial if you don’t master both F-Script basics and IB. I would recommend one to start familiarizing with the F-Script stand-alone application before experimenting more advanced features like live mode in IB.
Phil
-
thanks, example gave something to chew on
2002-08-10 08:26:50 psheldon [Reply | View]
object1 setTarget:[sys beep]. object1 setAction:#value:
Here, in guessing a parsing of this statement for meaning, I use my dumb in hindsight gestalt. Object is not action ie. noun is not verb.
Here # means the last statement's objects output, much as in maple computer algebra language % does. In maple I learned a writing "style with %" that helps sequential interactive developement of a compound statement in a >-promt-line of code. I suppose that I will learn a similar style in F-Script.
object1, through connection I made in IB, now stands for the button. I want the (button) control to do something. I have certain syntactic elements in F-Script to build with. I always do something in an object oriented language by sending a message to an object. Thence, I have to set a target object and say what action I want it to take.
In the line of F_script code example, in the language of IB, the button gets associated with an action. In the MVC notion of things the F-Script object is the controller while value: is the action in the controller with # abbreviating.
Yet, here is a surprise that confusing and means an emerging more powerful conceptualization.
You made the syntax of F-Script so it would be wrong to simply write :
object1 setAction: [sys beep] value:
I have some vague memory in ObjC.pdf book about what wiring really means in IB. The memory was vague because the writing was very terse, I believe to hide initially a level of detail that would scare an early reader.
So, now that I am not an early reader, perhaps you can put in words I can swallow why not my proposed abbreviation, ie. why the above syntax doesn't confuse me but would confuse the computer.
;-)
Also, I don't have a clue how to make an outlet. So, what section of your book would I read for actions and outlets. It might give some introduction as to what is going on. Maybe, if console works, the computer would complain to me when I wrote it my abbreviated statement. I'm getting that experimental itch! Good.
;-)
-
thanks, example gave something to chew on
2002-08-11 04:46:37 pmougin [Reply | View]
In order to configure the button (i.e. object1) we need to provides it with a target (i.e. an object), and the name of the message it must send to the target when clicked (this is the Cocoa target/action paradigm). The Cocoa API require for the "action" message to be a message with one and only one parameter. At run-time, when clicked, the button will send the message to its target, passing itself as the parameter (this allows the target to further interact with the button if needed).
In our case the target is an F-Script block. Our goal is to execute this block when the button is clicked. As you know, in order to execute a block, you send it a "value..." style message (the exact message depends on the number of parameters the block is expecting). Here, we configure the button with the "value:" message name, which is the one that takes one parameter (in the example, our block doesn't make use of this parameter).
Note that "#value:" represent the name of a message. What in Objective-C is called a message selector. So in fact it is (in F-Script) an object, but an object whose job is to represent the name of a message. It is a noun that describe a verb.
The target-action paradigm is described in the Apple Objective-C book, as well as some notions about outlets. I would also recommend you to get the Hillegass Cocoa book (see http://www.bignerdranch.com/Book/).
Phil
-
thanks, I believe # is F-scripts symbol and have distilled something
2002-08-12 22:12:01 psheldon [Reply | View]
My objC.pdf manual with index didn't mention a #.
I got, p.57 of old objC.pdf manual with index, that a method selector can take a runtime assigned variable as an argument, so I understand why you value implementing F-Script to take something like a literal constant. I've seen constant strings and string variables that were assigned, so I think I got the idea what this #-construct is doing and how its place could be occupied with something more general.
You wrote :
"The target-action paradigm is described in the Apple Objective-C book, as well as some notions about outlets."
I read this, but I need to struggle writing to get it in my heart.
An outlet is an object declared in a controller associated with an IB object. The IB/controller object has methods that the controller can send it as messages, such as to write some text. Now, when I set up such an outlet and its use totally in F-Script and interface builder, I imagine I must exemplify me doing something to get that object to do its method. To get an idea, I give myself an example of pushing a button. That button would run a script by my coding as in your example and in that script the outlet would be assigned to and object by the control drag mechanism. Then I would see the text print.
Am I right?
If I am right, when I am not so tired, I will give myself an exercise doing this in a simple little example program. I shall then write myself a little column, big enough for my own adventure. I know I can talk myself into believing I understand, but only when I try to work out something will I get the ultimate confidence (after some frustration).
I shall consider buying Cocoa book by Hillegass.
Thank you again.
-
Re: thanks, I believe # is F-scripts symbol and have distilled something
2002-08-14 10:54:48 pmougin [Reply | View]
Basically, what you need is to make the F-Script block a target of the button. Normally, in IB, to make an object a target of a button, you juste control-drag FROM the button TO the object. Here is a somewhat not easy thing to graps with F-Script live mode: the commonly used technique described above is not what you will use to connect a button to a F-Script block! Actually this is a two step process:
First, you let know the F-Script interpreter about the button by control-dragging FROM the F-Script interpreter object TO the button and assigning the button to one of the interpreter outlet (object1 for instance).
Now, you can manipulate the button from within F-Script. This means that you can now type "object1 setTarget:[sys log:'hello'. sys beep]. object1 setAction:#value:]" in the F-Script live mode window. This will establish the block [sys log:’hello’. sys beep] as the target of the button.
Phil
-
F-Script Anywhere and "Gift Economy"
2002-08-07 13:23:27 psheldon [Reply | View]
"Brief History of the Future" by John Naughton spoke of a paradyme shift that the internet brought that business hasn't yet caught onto when the internet got commercialized, the so called "Gift Economy". The old paradyme dogmatists haven't learned this from the noncommercial internet "geeks", but rather imagine they are going to teach the "geeks" their old trick on how to turn a profit.
The way I understood it "geeks" openly share their programming savvy and get famous to insure the market competes for them rather than that they compete for the market.
Newton personal digital assistant also used a scripting language. There was an app called ViewFrame on it that let me look inside other app's that were running. There was also a reverse compiler off in Germany somewhere long ago. A scripting language, as I vaguely recall someone writing, has a one to one correspondence between insides of programs and what I can see on the outside as a user.
The glory of a program becomes not what I can own by secrecy but rather in my being necessary to teach understanding of the savvy I have gotten by developing the concepts of that program. Where once the employer owned intellectual property, now those who create intellectual property can own themselves as an indespensible resource.
With such open source, the world might well progress beyond being mere buyers and sellers to that mysterious force called relationship...
You once again remind me of a hero of mine, Steve Weyer.
-
whoops p.9 not common reference system-confusions outside livemode use of FScriptPalette.palette
2002-08-07 13:02:15 psheldon [Reply | View]
Couldn't get wired buttons to get me print feedback in IB testmode or PB runs.
I need specific illustrations. Maybe that is what you were writing down as available on internet.
Gotta see. This scripting is hot!
-
FS not merely for print, but also read eval alone useful?
2002-08-07 12:27:58 psheldon [Reply | View]
p.9 spoke of FSInterpreterView in live mode in interface builder being useful, but I don't see output with a used to be friendly statement. Error message work, so I know the interpreter is going in both double click and test mode.
What was this used to be friendly statement?
I tried
sys log:'hello world'
to no effect of output in that window. I used this F-Script source statement to test stuff over and over successfully before.
So, I am again confused and a shift must be emerging in my understanding paradyme. But, I fear I shall remain clueless until I embarrass myself by submitting.
And so I do. -
FS not merely for print, but also read eval alone useful?
2002-08-09 09:28:45 pmougin [Reply | View]
The F-Script "log:" method write a message to the standard output (a UNIX notion) of the application into which it is executed. I can’t test it right now, but the standard output of IB is probably displayed in the Console application (which comes standard with Mac OS X, just launch Console and check for the message you are logging.
Phil
-
Eg.2, passing and retrieving
2002-08-07 08:59:44 psheldon [Reply | View]
FScript source :
a at:a location = 'CHICAGO' & (a capacity >=
200)
I think it is proper to see F-Script as a meaning underneath F-Script source syntax and cocoa source syntax.
So, I believe, this translates into obj c-code:
[a at: [a location]='CHICAGO" & [a capacity] >= 200]
It is hard for me to imagine a boolean an argument of a method at: . I try to imagine.
Does this mean return a if true and nil if not true?
Does application of a script to an array get its definition as on each element from Cocoa frameworks or FScriptFramework? My bet would be the method value: has, from the pdf file on F-Script, a great syntactic generality given meaning by F-Script.
I am starting to get an intense confusion about what it means to build a framework, way above my level or perhaps something fundamental and, in hindsight obvious and wonderful.
-
Re: Eg.2, passing and retrieving
2002-08-07 10:22:51 pmougin [Reply | View]
> So, I believe, this translates into obj c-code:
> [a at: [a location]='CHICAGO" & [a capacity] >= 200]
Not exactly. F-Script is an array language that let you manipulates arrays as a whole, taking care of iterating though the arrays’ elements automatically. This is not the case in Objective-C, where you have to explicitly code your loops.
In this particular example, the equivalent Objective-C code would looks like:
NSArray *airplanes
.
.
.
int i, nb;
NSArray *selectedAirplanes = [NSMutableArray array];
for (i=0, nb=[airplanes count]; i < nb; i++)
{
Airplane *currentAirplane = [airplanes objectAtIndex:i];
if ([currentAirplane location] isEqualToString:@"CHICAGO"] && [currentAirplane capacity] >= 200)
[selectedAirplanes addObject:currentAirplane];
}
> Does this mean return a if true and nil if not true?
No. In the example, "a" is an array of Airplanes. And the argument passed to the "at:" method is an array of booleans, with the same number of elements than in the array "a". The result will be an array of Airplaines where only the elements in "a" that correspond to a Boolean with a value of true in the bollean array will be selected.
Phil
-
thanks
2002-08-08 21:55:50 psheldon [Reply | View]
Reminds me of a long ago memory of APL. Pretty good memory when I am exhausted from working out a digital laplacian version of Mike Beam's column.
Need to figure out how to pencil around an F-Script sentence like :
"[:a| a at:a location = 'CHICAGO' & (a capacity >=200)]"
Also, might like to know if at: was short for attribute or where to go look it up, eg. examples on how to navigate your pdf book on F-Script. Maybe further columns will get up my nerve to navigate or I shall simply read the book when I get enough confidence.
-
thanks
2002-08-09 09:46:33 pmougin [Reply | View]
You’re right; F-Script is heavily inspired by APL. The "at:" method with an array of boolean argument implements the APL compression functionality.
Note that at: is not short for "attribute". It’s similar to the NSArray “objectAtIndex:” method, just more general. For more on this method, look in the F-Script guide (http://www.fscript.org/download/FScriptGuide.pdf) at section 17.
And, yes, in order to understand how the array expressions work in F-Script, you should definitely read the guide.
Questions are welcome on the F-Script mailing list or directly to me.
Phil
-
finding frameworks
2002-08-07 08:28:06 psheldon [Reply | View]
I had a happy victory over intense confusion. I put FScript.framework in home/library/ but project builder couldn't find it. After getting terrified that I would have to navigate gobs of fundamental hypertext, my brain jumped tracks to the files window wherein were frameworks. How did they get there? Could I put them there? I dragged the folder FScript.framework to the frameworks in files window and project builder was able to find the framework.
Then, I had to leave my macintosh to get ready for an appointment. When I came back to my mac, I was ready to experiment and finished reading the article in the remainder of the evening.
The first experiment that I did was to make a FSWindowController class inheriting from NSWindowController with the method expt for experiment. Interface builder had a DoIt button and that was connected to the single WindowController action expt.
Then, I saw the article prescribing writing in, to what had been for me sacrosanct, main.m.
The second of two lines of code puzzled me for awhile :
[button setTarget:printDate];
[button setAction:@selector(value:)];
and then I let them go to read on with a guilty feeling.
Then, I looked back for something else and realized that Block had a method value. I started to get something fundamental emerge from my initial puzzlement and uneasy guilt---"An object is not a procedure!" Sounds dumb now, but the fundamental can, in hindsight, be obvious. That is why it is beautiful.
-
The source code package for the examples is here...
2002-07-18 07:31:37 pmougin [Reply | View]
http://www.fscript.org/download/EmbeddingFScript.tar.gz
-
Please don't tell people they could use the System domain
2002-07-16 08:20:41 tophu [Reply | View]
You mentioned in the article that the framework should be in one of the standard locations for frameworks. One of the locations listed is in the System domain (/System). This is NOT recommended. Third party software should never put anything into the System domain.









cannot find class (factory) method (you only get those warnings the first build after a clean).
Added :
#import <FScript/Airplane.h>
and then there were no complaints after a clean.
This "@ construct" works good in headers where there are no method calls but produces innocuous, yet slightly confusing, warnings in .m files where the methods are called. Target code is produced that works, there are just warnings.
It seems, though, that when I program in the future I will get a whole lot of warnings and errors and, the more I know how to eliminate, the more I will be able to ferrit out the ones that count!
So, I am happy to have cleaned out the warnings. This shows that all my work in following columns is paying off in my losing my confusions.