tmedwards / sugarcube-2

SugarCube is a free (gratis and libre) story format for Twine/Twee.
https://www.motoslave.net/sugarcube/2/
BSD 2-Clause "Simplified" License
177 stars 41 forks source link

Macros <<if>> try call object with using function in object #147

Closed EleXte closed 2 years ago

EleXte commented 2 years ago

I have next code in JS part of my project:

setup.Routine.present = function (who, passage) {
    var p = State.variables.routes[who];
    if (p.active.loc === passage) {
        return true;
    }
    return false;
};

When i try call this function next way: <<if setup.Routine.present(NPCName, passage()) == true >>

I catch next error: Error: <<if>>: bad conditional expression in <<if>> clause: NPCName is not defined

(NPCName is just placeholder to more understandable what must be in that place)

Thiss error happen whatever when my code present in function or commented.

When i write NPCName in quotes ("NPCName" - like this) "if" macros not show error and all works.

What happen by my opinion: When "if" macros in parsing "NPCName" is regarded as object and algoritm try call him from unknow location (beacouse i have in "State.variables" object with that name and him is full of properties) and catching error. But in documentation writed, what for hand over object i need write him with $ like this "$NPCName". And all strings writed with quotes, but not have any notice what without quotes string be regarded as object.

I'm not realy sure what this is bug, I'll leave that for your final decision. But need fix macros or add notice to documentation.

And sorry for my english, still learning him =(

Project details.

Desktop details.

tmedwards commented 2 years ago

You cannot simply make up "placeholder" identifiers. If you're passing something into the function it must either be an existing identifier or a literal.

Assuming $routes is an object, whatever you pass to the who parameter must be or hold a string.

Additionally, since your function doesn't test if p is defined, you have to specify a value to who that's actually a key in $routes or you'll get an error when you attempt to reference properties on p—i.e., p.active.loc. You can fix the p the problem by testing if the value of p is truthy before attempting to reference its properties—e.g., (p && p.active.loc === passage).

To sum up, you should be calling your function something like the following:

/* String literal. */
<<if setup.Routine.present("NPCName", passage())>>

/* Story variable, containing a string. */
<<if setup.Routine.present($name, passage())>>

/* Temporary variable, containing a string. */
<<if setup.Routine.present(_who, passage())>>
EleXte commented 2 years ago

In next screenshot i delete all code from function (except return) and still catch error. And I special add if macros where who dont crosses over any object or variable in project (string with Serenity name and this if is empty on second screenshot). Screenshot_2 Screenshot_4

Why this behavior disturb me? Many macros can freely parsing of string in arguments without quoters. I dont have example rith now, but i try find all what can and post this later.

In documentation, in section TwineScript->Variables->Variable Names, have next description:

The very first, and mandatory, character is their sigil, which denotes whether they are a story or temporary variable. The sigil must be a dollar sign ($) for story variables or an underscore (_) for temporary variables.

But in this situation parsing algoritm trying replace string to object or variable without having that symbols.

I'm not opponent to hard rule of writing strint in quotes, but when I forget adding them and catch error like this first i do revision all of my code, than go to rereading documentation and don't can understand what happening wrong. Becouse in some times I have many elements in passage based on procedures with dynamic generating and documentation tolded me that problem cant be in macros becouse need sigils.

Let's summarize what I said (I hope this help to undestand you my bad english): Documentation told if not have sigil - this not variable (of any type). Macros send of error with undefined variables even without code in function. In some cases macros don't see on presence of quotes and still count string as string (include widget macros).

And I go to try find good example for ignoring presence of quotes.

tmedwards commented 2 years ago

The <<if>> macro is an expression type, not a discrete argument type—see: Macro Arguments.

In the following example:

<<if setup.Routine.present(NPCName, passage())>>

The sole argument to <<if>> is the expression:

setup.Routine.present(NPCName, passage())

That is a TwineScript expression, which is ultimately just sugared JavaScript.

The JavaScript error there is caused by attempting to pass a nonexistent identifier during the function call. It has nothing to do with the contents of the function.