Page 1 of 2

Scripting: request for comments

Posted: Tue Mar 13, 2007 10:55 am
by JensAyton
This post is technical, but understanding it is probably worth your while if you do any OXP scripting. :-)

The work on integrating JavaScript has got me refactoring the scripting system, and effectively rewriting the plist script parser in the process. I’m currently implementing conditional statements, so now seems a good time to look over that functionality.

As it stands, and as I understand it (I haven’t done any serious OXP scripting; feel free to correct me), a conditional clause consists of an array of conditions. Each condition has one of these forms:
<testable_variable> "undefined"
<testable_variable> "one_of" <number_list> (I don’t think that one’s documented; it’s something dajt added)
<testable_variable> <logical_operator> <constant_expression>

A testable_variable is a string of the form local_foo or mission_foo, or a method selector (property accessor) of the form foo_string, foo_number or foo_bool.

A constant_expression is a testable_variable or a literal (e.g., 42.3, YES or STATUS_DOCKED).

A logical_operator is one of: equal, lessthan, greaterThan or morethan (the last two being synonyms).

Some very easy extensions to this would be:
  • Removing the syntactic distinction between testable_variable and constant_expression. This probably wouldn’t be useful, but would slightly simplify the implementation and documentation.
  • New operators: defined (inverse of undefined), notoneof (inverse of oneof), notequal, lessthanorequal, greaterthanorequal, or, and, andnot, ornot, xor (exclusive or, i.e. one or the other, but not both).
  • Mixed-case operators (greaterThanOrEqual as a synonym for greaterthanorequal).
  • Symbolic operators: =, !=, <, >, <=, >=, &, |, &!, |!, ? (for defined), !? (for undefined).
A revised grammar would then be:
<condition_term> <unary_operator>
<condition_term> <binary_operator> <condition_term>

A unary_operator being one of:
  • defined, ?
  • undefined, unDefined, !?
A binary_operator being one of:
  • equal, =
  • notequal, notEqual, !=
  • lessthan, lessThan, <
  • greaterthan, greaterThan, morethan, moreThan, >
  • lessthanorequal, lessThanOrEqual, <=
  • greaterthanorequal, greaterThanOrEqual, morethanorequal, moreThanOrEqual, >=
  • and, &
  • or, |
  • andnot, andNot, &!
  • ornot, orNot, |!
  • oneof, oneOf
  • notoneof, notOneOf
I’d like to see suggestions, but remember, these are changes that are easy to implement. I’m not going to implement full nestable parenthesised expressions, because JavaScript has that.

Posted: Tue Mar 13, 2007 11:43 am
by Commander McLane
Great idea to improve this! :D

A few comments:

- oneof (that's the current syntax) is documented in the wiki, in the script.plist - document. And it doesn't need to be a <number_list>. Also "status_string oneof STATUS_IN_FLIGHT, STATUS_DOCKED" is a valid condition.

- The distinction between <testable_variable> and <constant_expression> already is somehow fluent. E.g. "mission_mymission_counter lessthan [mission_mymission_anothercounter]", comparing two variables to one another is valid code, provided the second variable is put in square brackets.

- Some of the additional logical operators would be useful, especially notoneof (or better noneof?) and notequal. The other ones are not that necessary. E.g. "lessthanorequal 2" in most cases can easily be substituted by "lessthan 3".

- The point of the other operators AND, OR, ANDNOT, ORNOT(?) and XOR would not be here, within a condition, but in combining conditions. I explain that:

As for now you can have more then one condition in each condition clause, and they all are combined with AND. Example from the script of Equilibrium.oxp (not yet finished):

Code: Select all

				{
					conditions = (
						"dockedAtMainStation_bool equal YES",
						"mission_thargplans equal MISSION_COMPLETE",
						"mission_equilibrium undefined",
						"mission_equilibrium_sponsoring undefined",
						"planet_number equal 6"
					);
					do = (
						"setMissionImage: none",
						setGuiToMissionScreen,
						"addMissionText: MIT_Briefing_1a",
						resetMissionChoice,
						"setMissionChoices: equilibrium_Briefing_cont",
						"set: mission_equilibrium_sponsoring MIT_BRIEFING_1a"
					);
				},
What we have here is an array of conditions that has to be fulfilled in order for the new mission to be triggered and a mission screen to be displayed. So all the conditions are combined by a logical AND (syntax-wise it's the comma at the end of each line).

Now I could easily think of situations where I would like to combine my conditions in another way, let's say with an ANDNOT:

Code: Select all

					conditions = (
						"dockedAtMainStation_bool equal YES"
						AND
						"mission_thargplans equal MISSION_COMPLETE"
						AND
						"mission_equilibrium undefined"
						AND
						"mission_equilibrium_sponsoring undefined"
						ANDNOT
						"planet_number equal 6"
					);
Also notequal would do the job here, but it's not there yet. The only workaround for now would be to have two seperate sets of conditions, on with "lessthan 6" and another with "morethan 6". Or in other situations to extensively use "else"-clauses.

So IMO this is where AND, OR, ANDNOT, ORNOT and XOR would make sense, not within a condition. Right now I couldn't imagine a use for "credits_number xor 32451".

- Having symbols as an alternative to words would be fine for those of us who are used to symbols, but not necessary for all the others.

Posted: Tue Mar 13, 2007 12:08 pm
by CaptKev
Great stuff Ahruman, operator expressions like * and / would be nice.

Posted: Tue Mar 13, 2007 12:36 pm
by JensAyton
Commander McLane wrote:
- oneof (that's the current syntax) is documented in the wiki, in the script.plist - document. And it doesn't need to be a <number_list>. Also "status_string oneof STATUS_IN_FLIGHT, STATUS_DOCKED" is a valid condition.
Make that <list>, then. :-)
Commander McLane wrote:
- The distinction between <testable_variable> and <constant_expression> already is somehow fluent. E.g. "mission_mymission_counter lessthan [mission_mymission_anothercounter]", comparing two variables to one another is valid code, provided the second variable is put in square brackets.
I know, but I referred to removing the distinction completely. (This would require escaping strings that could be mistaken for selectors with, say, single quotes. This could be problematic if any such strings are used in existing scripts, though.)
Commander McLane wrote:
-- The point of the other operators AND, OR, ANDNOT, ORNOT(?) and XOR would not be here, within a condition, but in combining conditions.
I’m well aware of this, but that would be a much larger task, which is very much in ain’t-gonna-happen territory what with the introduction of JavaScript.
Commander McLane wrote:
Right now I couldn't imagine a use for "credits_number xor 32451".
No, but how about "isDocked_bool or isInWitchspace_bool" (to make up some properties, since I don’t have the code with me to work)?

Oh, another thing... how about syntax like foo$ instead of foo_string, foo@ instead of foo_number[/b] and foo? instead of foo_bool? As a programmer I’d find that easier to work with, but others might disagree. ("isDocked? | isInWitchspace?" -- this would also involve removing ? as a shortcut for defined, since "foo?" and "foo ?" look rather similar.)

Posted: Tue Mar 13, 2007 1:48 pm
by Charlie
All good stuff :D

All changes I'd wellcome very much - esp greater variety / flexability with operators.

foo$ instead of foo_string... etc?
Fine by me - less typing :lol:

A request ( if not too far outside the remit of your plans ) would be a wider variety of testable conditions, esp inverted forms.
eg: scanFor_not_Thargoid, scanFor_not_Rock etc...
Maybe even scanFor<definedEntity>, so new classes etc could be added via .oxp
Better still have <definedEntity> as an option to most commands instead of a hardcoded object/event.

Also a direct command for adding / removing items from a specfic place / entity:
eg: you want something to happen reliably ( other than an explosion ) @ the position where your current object is. Maybe a simple generic command that points to a named section in a script.
This would allow all sorts of things from easy addition/removal of ships/sub-entities with specific consequences under specific circumstances to basic animation! ( you keep replacing the ship or texture with the next one in the script )
-Getting greedy now-

Oh - I'm sure you've thought of this one... backwards compatability?

All the best.

Posted: Tue Mar 13, 2007 3:20 pm
by JensAyton
Charlie wrote:
A request ( if not too far outside the remit of your plans ) would be a wider variety of testable conditions, esp inverted forms.
eg: scanFor_not_Thargoid, scanFor_not_Rock etc...
Maybe even scanFor<definedEntity>, so new classes etc could be added via .oxp
Better still have <definedEntity> as an option to most commands instead of a hardcoded object/event.

Also a direct command for adding / removing items from a specfic place / entity
Greater flexibility with parameters is one of the benefits you’ll (eventually) see by switching to JavaScript. ;-)
Charlie wrote:
Oh - I'm sure you've thought of this one... backwards compatability?
A primary goal will be to maintain compatibility with existing scripts. Once I think it’s working I’ll fast-track a new test release (1.68, hopefully with a more-or-less simultaneous OS X/Windows release… anyone up to making a Linux release?) with the primary goal of getting everyone to test their existing scripts. Obviously any new features won’t work with old versions of Oolite.

By the way, error messages directly related to interpreting a script will now be reported with the name of the script. (In the case of script.plist scripts, the name is the key in the dictionary of scripts.)

Posted: Tue Mar 13, 2007 9:29 pm
by JensAyton
After further thought: unifying <testable_variable> and <constant_expression> while ensuring backwards compatiblity won’t be possible. xor would be equivalent to notEqual except when comparing numbers, where it would mean “exactly one of these numbers is zero”; I’m not sure that’s particularly useful.

Posted: Wed Mar 14, 2007 7:10 am
by Commander McLane
Ahruman wrote:
Commander McLane wrote:
Right now I couldn't imagine a use for "credits_number xor 32451".
No, but how about "isDocked_bool or isInWitchspace_bool" (to make up some properties, since I don’t have the code with me to work)?
But that would basically be the same as oneof, wouldn't it? So no need to directly compare the two arguments. At least that's what I think. If I'm wrong then just ignore this remark.

Generally operators like AND, OR, XOR etc. are useful if you are operating with binary numbers, which we pretty much don't do in scripts. A rare example from Oolite (that's the engine itself, not a script) is how commodity-prices are calculated, which involves a couple of ANDs with a price- or quantity-mask. But this operation is hardcoded on the engine-side. No way to influence the formula itself in a script (e.g. by having a XOR instead of an AND).

Posted: Wed Mar 14, 2007 2:23 pm
by JensAyton
As far as I can see, oneof doesn’t evaluate the members of the list; it can only handle lists of literals, so you couldn’t use it to OR a list of method results together.

Posted: Wed Mar 14, 2007 3:59 pm
by JensAyton
I’ve thought of a semi-solution to the problem that script conditions can only be ANDed together: add some entries to the condition dictionary, namely ifSome, and ifNone. If all values are true, do is used. If some values are true and some are false, use ifSome if defined, otherwise fall back to else. If all values are false, use ifNone, falling back to else. (ifAll and ifNotAll could be introduced as synonyms to do and else for consistency. An alternative would be to allow both an ifSome/ifNone and an else/ifNotAll clause, allowing common code for the ifSome and ifNone cases to be put in the else clause.) This would essentially allow for ORing all the conditions together rather than ANDing them – not as flexible as a full set of Boolean operators with parentheses, but probably the most common case that AND-only doesn’t cover.

Posted: Wed Mar 14, 2007 5:18 pm
by JensAyton
Commander McLane wrote:
- The distinction between <testable_variable> and <constant_expression> already is somehow fluent. E.g. "mission_mymission_counter lessthan [mission_mymission_anothercounter]", comparing two variables to one another is valid code, provided the second variable is put in square brackets.
Are you sure about this? It looks to me for all the world as though what the existing code is doing for <constant_expression>s is:
  • If it ends with _bool, _number or _string, treat it as a query method.
  • Otherwise, treat it as a literal.
Variables don’t seem to be handled.

One thing I missed is that you can actually have multiple <constant_expression>s on the right hand side, in which case they’re concatenated, separated by spaces, as in "dockedStationName_string equal A Station Name" (where A, Station and Name are separate literals, and the number of spaces, line breaks etc. between them doesn’t have an effect).

Posted: Wed Mar 14, 2007 5:50 pm
by Arexack_Heretic
notEqual is something I missed, it will certainly make reading and writing scripts less complex.

how about Or and And to combine conditions?
ex:
{conditions = (
"legalStatus_number and mission_variable_independent_legal lessthan 50");
do = ("commsMessage: you are a good boy, commander");}
instead of
{conditions = ("legalStatus_number lessthan 50");
do = ( {
conditions = ("mission_variable_independent_legal lessthan 50");
do = ("commsMessage: you are a good boy, commander"); } ); }

edit: you already mentioned those. :oops:

Posted: Wed Mar 14, 2007 11:03 pm
by dajt
Why spend time and effort improving the plist scripting engine at all, unless it simplifies the code?

The JavaScript engine can already do everything the plist engine can, plus a whole lot more.

Posted: Wed Mar 14, 2007 11:24 pm
by JensAyton
Because I’m reimplementing it. It’s horribly closely tied into PlayerEntity (like so much stuff…) and I want to, as you say, simplify the code. Most of the changes I’m making don’t affect complexity much – except unifying the two types of term. As I said, “I’m not going to implement full nestable parenthesised expressions, because JavaScript has that.” – and also because it would complicate matters.

Posted: Thu Mar 15, 2007 12:53 am
by dajt
Yes, I can see you want to stop it being all tangled up with PlayerEntity, and applaud the idea, but I can't see the point of adding new capabilities to it. That will just encourage people to keep using it.

I've had two goes at killing plist scripts now and the damned things just won't die!