Tell ya what, I'll document some thoughts on the http://fexl.com web site. In the meantime, let me just say this. It's a language which you can "execute" manually on a whiteboard very easily. So I can demonstrate the language semantics fairly simply to a non-programmer. Anyone who can follow basic instructions can follow along.

The interpreter has two things: a single stack, and a global key-value space. Both the keys and values in that space are simple strings. You can store numeric values, and the interpreter will just "do the right thing" depending on how you use them, converting to and from machine numbers as it chooses.

Here's just a taste of some of the verbiage:

: x : 4 store
: y : 3 store
? x ? y add : z store

Now the ? is a shorthand for the more general "fetch", which allows arbitrary large levels of indirect reference. These two forms are equivalent:

? x
: x fetch

But single-indirection is so common that it's nice to have the shorthand. In the rare cases you need to do multiple indirection you can do crazy things like this:

: name fetch fetch fetch

By the way if you don't like "store" and "fetch" you can use "put" and "get" instead. The concepts are more important to me than the specific vocabulary.

The really fun part is defining conditionals and looping in the language, without using labels and gotos. Here are some snippets:

# What color is the fruit? (Yeah I know I'm doing
# repeated string comparisons here even after I've
# found the right color, but whatever, at least until
# I think of something better.)

: fruit : banana store
: color : "" store
( ? fruit : banana eq : color : yellow store )
( ? fruit : orange eq : color : orange store )
( ? fruit : strawberry eq : color : red store )
( ? fruit : cherry eq : color : red store )
( ? fruit : grape eq : color : purple store )

# First 100 Fibonacci numbers. Note that with appropriate
# use of the stack I can dispense with "z", but I leave
# it this way for "clarity", such as it is. :)

: count : 100 store
: x : 1 store
: y : 1 store
(
? count : 0 >
? x print nl
: z ? x ? y add store
: x ? y store
: y ? z store
: count dec
repeat
)

The conditional tests such as "eq" and ">" work by short-circuiting the surrounding block enclosed in "(" and ")" in the case that they are false. If they're true, execution proceeds sequentially. In effect, the conditionals are guards.

Also note that in the fruit example I don't actually have to set the color variable to the value. I could just leave the value on the stack instead and let the caller grab it. But I do it with the color variable as an illustration.

Now you may ask how to define a named routine. You language purists are probably gonna shudder at this, but here goes ... you just store the routine in the key-value space, where the value is a single text string containing all the instructions. So fibonacci is:

: fibonacci ~@
... all those instructions above right inside this string
@
store

Now to call or "do" that routine, you say this:

: fibonacci do

# Note that it automatically dereferences the routine
# name so you don't have to say "? fibonacci do".

You might think it slow to read the tokens off that string every time you call the routine, but I'm not worried about it too much. First, it's plenty fast enough for most scripting purposes. Second, if and when you really care, you can do all sorts optimized representations of these tokens streams in memory, perhaps in the extreme case even compiling down to assembly code.

There are also ways to iterate through keys in forward or backward order, and ways to expand templates for producing web forms, simple ways to save and restore values inside routines, etc. etc.

Here's a little more on ordered iteration, which allows you to sort very efficiently. Some examples:

: x next

# top of stack now contains the next key in the space
# which is strictly greater than "x" (or null if no
# such thing)

: x prev

# same thing, except less than instead of greater.

The ordered iteration concept completely eliminates the need for first-class "lists" and "hashes" and such. If you want to sort a bunch of names, just jam them all into a little sub-space, prefixed with "names/" perhaps to keep them separate from other global variables, and then simply iterate over them using "next" until you reach a key that does not start with "names/". I have a verb called "follow" which matches a given prefix and gives you the stuff following that prefix, or null if it doesn't match.

Note that you cannot just jam tokens together like this:

(:x :1 store)

Instead you must keep all the tokens separated with at least one white space:

( : x : 1 store )

That makes the token reader drop-dead simple. The reader doesn't need to know anything about what any tokens "mean", and it doesn't need any rules about "special" characters such as "(" and ")". Also, any indentation or line breaks you happen to use or not use are utterly irrelevant.

In a way I'm almost agnostic on the details, since you can embed whatever verbiage and semantics you like into your surrounding interpreter, which is written in C, Perl, Ruby, Lisp, or whatever. If you want to use Swedish verbs instead of English, have fun! It's more of a technique than a specific formal specification.

The essential concepts are:

1. The use of simple space-separated tokens.

2. The use of a single stack of string values.

3. The use of a global key-value map with string keys and string values.

This basically emulates the traditional execution model of a computer, with a stack and memory area, except that instead of the values and addresses being machine words, they are strings instead.

Perhaps I should call this language "Yet Another Forth" or even "Assembler for Dummies" :)

I think it's more clear than Forth because you don't have to do major gyrations to handle complex data. In effect, the "structure" of your data is modeled in the naming scheme of your keys, and the ability to iterate over the keys gives you the ability to "walk" that structure fairly easily.