. It is somewhat of a
thrown in to boot. It came to me in
yesterday afternoon.
I had the epiphany yesterday afternoon. I had the dream last night.
I present to you, the Solace scripting language, SoLISP. (Name pending.)
Actually, connoting that it's a LISP derivative, although accurate, is a
bit demeaning to it. See, it's actually a combination of LISP and sh--
No, don't go away yet! Really! I have everything worked out, from the
language to the execution model! It will^H^H^H^Hshould work! Really!
Okay. So what we have here is a cross between an imperative and a functional
language. With a sprinkling of OO thrown in. I believe that EVERYTHING can be
scripted in this, and that it can be made quite efficient (though for now I'm
just going to implement a high-overhead pure-parsing interpreter).
The actual language is simple. There is a statement, in the form of
and there is a compound statement in the form of
<STATEMENT> ; <STATEMENT>
and there is (this is what makes it LISP-like) a nesting structure, which
returns the result of executing what's inside (like sh's ` operator, except
that it's nestable), and looks like:
The execution model is simple. The thing is parsed. As it's parsed, nesting
structures are recursively executed, and the results of the parsing fill up a
C++ vector, which is then executed. Er, let's try that again. No, let's not,
I can't really explain it in English, just in C++ code, and explaining it in
code defeats the purpose of saving it for later implementation. :)
Anyway. So when the statement is ready to be executed, first it's compared
against the table of builtins. If it's not a builtin, then properties within
its /scripts/ propdir are searched for, and if it's not found there then the
parent object's are searched for, and if it's not found there then the parent
object's parent object's /scripts/ propdir, and so forth. It sounds like
there'll be a lot of overhead, but there won't; on a typical MUCK, you only
have three layers of nesting most of the time (character, room,
environment-room). There'll be four layers for a space where somone has setup
global actions for all the rooms in a group, but I think that'll be unlikely,
and all of the proptrees are going to be stored in nested hashmaps anyway, and
so the power of the STL will keep our time-complexity to a minimum.
Anyway, so as an example of a simple script: the default 'say' command could
be something like this:
echo You say, "{ARGS}". ; oecho says, "{ARGS}".
Oh, yeah. {VARNAME} means insert the value of a variable (like in sh except
without the $). Variables are passed around in an execution environment to
children (like in sh), though become separate entities (unlike in sh). There
are, of course, some implicit variables such as ARGS and NAME and the like.
Also, there will be both array-style access (i.e. {ARGS,0}), and there will be
Scheme-like builtins for treating them as lists (i.e. (head {ARGS}), (tail
{ARGS})). The insertion of the variable will NOT be evaluated; that's what the
eval builtin is for. That is, like how in MPI-based MUCKs the MPI-testing
function looks like:
in Solisp it looks like:
Oh, and of course, there's a builtin for setting variables as well.
Tentatively I'm calling it =; that is, to set the value of 'foo' to 'bar' you'd
say:
I'm doing that instead of 'set' because I'd rather use 'set' and 'get' for
values in the proptree, i.e.
or
set /forms/current (head {ARGS})
So anyway, this is both functional and imperative. I'm not going to have any
imperative looping or conditionals, because looping adds a lot more complexity
to the parser and could be much better handled as a builtin, and functional and
imperative conditionals would be almost identical and just work out to be
syntactic sugar:
if (eq {X} 0) 'This is a test' (eq {X} 1) 'only a test' 1 'thank you'
Oh, like in sh, '' and "" represent strings, with similar constraints (within
'', {}s aren't expanded, and within "", they are). And that construct above
represents the combination of 'if' and 'cond'. Its execution would look
something like this: (yeah, I know, this defines 'if' in terms of 'if,' but
if will be a builtin)
if (true {ARGS,1}) (eval {ARGS,2}) 1 (if (tail (tail {ARGS}))
Let's see, what else... strings will be passed around atomically, there'll be a
builtin 'split' which will split up a string, there'll be some sort of
primitive for dealing with proptrees as a list (so for really hairy scripts you
can put them into a proptree and do (eval (list /blah/blah/somehairything/))...
There might or might not be lambda functions (probably not, since they don't
seem all that useful to me)... Ummm, maybe I will have a simple for builtin:
for i 1 (length {ARGS}) (oecho {ARGS,i})
BTW, a bit of explanation on 'echo' vs 'oecho': 'echo' says the args to whoever
called the script, and 'oecho' says the character's name plus the args to
everyone else in the room. Basically, 'oecho' is 'emote', for you MUDders,
and 'osay' for you MPI programmers.
Oh, and I haven't mentioned how it was object-oriented, aside from the implicit
inheritance thing. Many of the builtins will have handlers which are setup in
propdirs. For example, when Foo pages Bar and Baz, the 'page' builtin sends a
message of type 'page' to Bar and Baz, and then it gets intercepted by their
page message handler, which is just a simple program which runs with SRC set to
source and DEST set to the desination(s).
While we're at it, echo and oecho go through messages and thus can have handlers. Oh, and it'll output to the client something IRC-styled, such as:
ECHO:This is only a test
OECHO:Fluffy says, "Bite me."
PAGE:[Foo] 07:23> Foo pages, "Hello!" (to you and Bar)
See, since there's still some client-side parsing going on. Also, we don't
want stuff getting to the client in plaintext; otherwise it'd be way too easy
to mess up the graphical clients by spoofing configuration changes and the
like.
I've got a whole bunch of other stuff to think about on this, though, but I
think that I've made a HUGE jump towards making ONSET a reality, at least...