The Sappho Scripting Language

The purpose of this page is not so much to teach you how to use the Sappo scripting language used in SWAT as to show you some useful ideas incorporated in its design, ideas that I hope you will stea… uh, recycle. Here’s a sample Sappho script. It doesn’t contain all the features of the language, but it gives you an idea of how it works.

Colors
The most obvious oddity of Sappho is how colorful it is. Wowie-zowie, look at all those pretty colors! Doesn’t this look like a lot more fun than all those boring black-and-white languages? Even better, those colors actually MEAN something! Utility and garish aesthetics merge in a single scripting language!

The colors indicate data types. You programmers know all about data types; you even make up your own data types in your high-falutin’ languages. Sappho, however, has fixed data types corresponding to the data types in story worlds.

The colors used are as follows:

BoundedNumber
Actor
Boolean
Trait
Prop
Stage (This orange is difficult to see against the orange backward). 

For artists, not programmers
A fundamental requirement for Sappho is that it be accessible to artists. This is not a programming language for programmers; in fact, programmers will hate it because it deliberately restricts their power. What programmers call ‘power’, I call ‘capacity for screwing up’. Really powerful programming languages provide you with a million ways to blast your computer into a smoking crater of digital rubble. Sappho is draped with safety rails, protective walls, and other schemes to insure that you can’t do anything wrong. It is also simplified and does not offer the full computational power of a full-featured programming language. Hell, you can’t even compute the value of π to a thousand decimal places!

No Syntax Errors
One of the worst flaws of conventional programming languages is the frequency of syntax errors. Everything has to be exactly correct or the damn thing will spit a syntax error in your face. Did you miss a required comma? Forgot to balance your parenthesis or curly braces? Used a colon instead of a semicolon? We’ve got a million ways to trip you up, and every time you make the tiniest error, we nail you with another syntax error. Language designers are sadists. 

Sappho has a simple solution to the problem of syntax errors: an inverse parser. Programming languages use parsers to figure out what your code means. First you write your code, then you submit it to the Almighty Parser, who sits in judgement of every dot and tittle of your code. The parser applies all the complex logical requirements of the language to your code, and rejects any failure with a contemptuous error message. Stupid programmer! You do not live up to the high standards of this glorious language! Fail!

This shell game is a waste of programmer’s time. Instead of demanding that the programmer guess which shell the pea is hiding under, the computer should be doing all the work. It should figure out in advance everything that is logically acceptable at each stage of the process and then offer those options to the programmer. This is implemented in something I cooked up decades ago: an inverse parser. Here is my explanation of the first inverse parser I built over 30 years ago. 

Basically, an inverse parser works through menus. The user selects something to edit and the inverse parser recalculates all the menus in the system to include only menu items that are logically permissible at that location. The user selects a menu item and it is placed at the selected location. Here are some side-by-side examples of the same script with different terms selected, showing how the same menu shows different menu items depending upon the type of term selected:

In the left image, the term “PBad_Good” is selected, and only red (BNumber) items in the menu “ThisEvent” (on the right side of that image) are shown. In the middle image, the term “ReactingActor” is selected, and only blue (Actor) items are shown. In the right image, the boolean term “OR3” is selected and only black (Boolean) terms are shown in the menu. 

No “Undefined Element” errors
Another stupid requirement of many languages demands that the programmer must do some “X” before some other “Y” is legal. Usually the “Y” is what the programmer actually wants to do, but the “X” is a stupid statement that must precede “Y”. A typical example of this is variable declaration. If you put a variable into a formula, like this:

newTime = oldTime + 5;

This statement is legal ONLY if you have previously declared what type of variable newTime is. The correct form might look like this:

int newTime;
newTime = oldTime + 5;

This is usually just a minor headache, but why should humans have to do all the work? That’s what computers are for! 

A more common instance of this kind of problem comes when you try to call a subroutine, function or method. Suppose that you have written the following function:

float calculateDeviation(float oldValue, float newValue, float range)

Then you go back into your main code to use your function and you write this:

x = calculateDeviation(y, z);

This will of course generate an error; the function calculateDeviation requires THREE input parameters, and you entered only two. Shame on you!

Why should the programmer be required to remember all these petty details? That’s stupid-work that is exactly the kind of thing that computers do well and people do poorly. The solution should be obvious: the minute that the programmer creates calculateDeviation, the computer should automatically insert the parameters. Programming language designers have finally figured this out, and many editors now include “autocomplete” features that automatically insert necessary parameters. It took long enough! Sappho has handled this automatically for years. In most cases, Sappho will automatically insert usable values into the scripts for you, but in some cases, Sappho will instead insert a token value that reminds you of what kind of thing belongs in that slot. If you don’t fill in the slot, then you’ll get same old “Undefined Element” error message when it attempts to execute the script. Sorry about that, there just wasn’t any other good way to handle the problem. 

Implicit Loops
Loops are essential to programming; a program without a loop is almost always useless. However, all the loops in a storyworld, it turns out, are meant to select the best or worst something-or-other. For example, suppose that a young woman wakes up some morning and says to herself, “I think I’ll get married today!” All she has to do is find the right guy to marry. In a normal programming language, this would be handled by a loop structured something like this:

bestCandidate = -1;
highestDesirability = 0;
for all the actors in the storyworld {
   if thisActor is acceptable {
      x = how desirable thisActor is
      if (x > highestDesirability) {
         highestDesirability = x
         bestCandidate = thisActor
      }
   }
}

I decided to collapse all this froofaroo into a single built-in function: PickBestActor(acceptable, desirable).

Acceptable and Desirable
One of the ideas in Sappo that keeps popping up over and over is the twin concept of acceptable and desirable. Acceptable is a boolean function: it determines that something either is or is not acceptable. Desirable is the term I have been using for many years, although I now believe that Preferable captures the idea better. The central idea is that, given some set of possibilities, Sappho will pare down that set to those things that are Acceptable, and then select the one with the highest value of Desirable. 

This is worthwhile ONLY because Acceptable and Desirable are calculated from an algorithm that takes into account the circumstances of the moment. In the example above in which the woman decides to get married, and obvious acceptability function is:

Acceptable = (candidate is male) AND (candidate is not already married)

A simple desirability algorithm might be this:

Desirable = BSum(wealth of candidate, power of candidate)

Of course, you might prefer:

Desirable = Blend(handsomeness of candidate, kindness of candidate, 0)

or 

Desirable = BSum(sensitivity of candidate, even-temperedness of candidate)

or perhaps something like this:

Desirable = BSum6(wealth, power, handsome, kind, sensitive, even-tempered)

(where BSum6 is just the six parameters added together boundedly)

It’s all a matter of artistic taste. That’s what makes this art.

No Runtime errors
Some kinds of errors just can’t be anticipated before the program executes. The standard example of this is the divide-by-zero error. Your code looks like this:

argleBargle = fooFrum/poofy;

This works just fine until the day comes when poofy = 0; then the computer can’t handle the situation and generates a run-time error, terminating execution. I came up with a better solution that I call “poison”. In the Sappho language, everything, every line of code, every calculation, is a component of a larger whole. If a runtime error strikes, Sappho marks that calculation as “poisoned” and ignores everything consequent to it. It nevertheless continues the storyworld execution. The result is not craziness; the result is merely a pared-down response. 

For example, suppose that our young lady above has waited too long and there aren’t any acceptable men left; they’re all either married or dead. Sappho prevents a runtime error by poisoning the option “get married” when PickBestActor comes back empty-handed. Since the option “get married” is poisoned, it is eliminated from the list of possibilities and another option verb is selected. The story isn’t ruined; it suffers only narrowed options. She has to pick another option, like “adopt a dog”. Who knows, it might turn out better than way. 😜 

Poison is applied to anything that happens in a storyworld; in fact, if ALL of our young lady's options are poisoned, she simply does nothing. 

One term per line
You will notice that Sappho permits only one term on each line of the script. This might seem wasteful; after all, ‘real’ programming languages allow you to cram lots and lots of terms into a single line. But this device offers a number of benefits. First, the little triangles on the left side of each term permit “folding”: a term’s parameters can be hidden so that you can see the structure of the script more clearly. Second, the vertical lines and the indentation make absolutely clear what belongs to what. You don’t have to count parentheses like you must in ‘real’ programming languages.

The vertical structure of Sappho scripts permits an amazing analytical tool we call “Scriptalyzer”:

You can wiggle around the input values using the scroll bars and see how the results of the calculation change in response to your wiggling. It’s a live simulation of your algorithm, allowing you to immediately see how each term effects the final result. This would not be possible with a conventional language.

With the notions of Acceptable and Desirable under your belt, you are now ready to understand how Options work.