Expression operators

jaynabonne
I have not been able to find a comprehensive list of the operators available in a Quest expression, but Quest uses FLEE (http://flee.codeplex.com) and this page lists some of them:

http://flee.codeplex.com/wikipage?title ... Title=Home

Ironically, the one I needed the other day and discovered by testing is "floor", which is not listed. So I'd say, if in doubt, experiment!

I was surprised to see the conditional operator. It's a tertiary operator known as ?: in C-like languages. It can be a more compact form then the equivalent if/then/else statements, especially for assignments. I have some code to simplify!

jaynabonne
For those interested in the conditional operator, here's an example (since the syntax is odd and unexpected, and it took a couple of tries to work it out):

cond = true

s = if(cond, "The truth shall set you free", "Liar, liar, pants on fire.")

msg(s)


It's a bit contrived (e.g. "cond" is jammed to true), but it shows how it works. This will print out "The truth shall set you free" since cond is true. If cond is set to false, it will print out the other string...

jaynabonne
Some more experimentation:

msg(pi)

3.14159265358979

msg(e)

2.71828182845905

msg(sqrt(2))

1.4142135623731

msg(asin(1))

1.5707963267949 (which is pi/2)

msg(abs(-3))

3

msg(floor(2.5))

2

msg(ceiling(2.5))

3

msg(pow(2, 3))

8

msg(log(e))

1

It also supports trig functions like sin, cos, and tan, and even more exotic ones like sinh and cosh. (Note these use radians, not degrees.)

jaynabonne
Holy smokes...

l = NewStringList()
list add(l, "A")
list add(l, "B")
list add(l, "C")
list add(l, "D")
msg(l[2])

C

d = NewDictionary()
dictionary add(d, "Name", "Quest")
dictionary add(d, "Author", "Alex Warren")
msg(d["Name"])

Quest

Life just got a whole lot simpler...

HegemonKhan
yes indeed! Thank you for discovering this!

now, I just need to make use of lists and dictionaries more often, to save me the trouble of writing out long msg scripts!

Alex
The list and dictionary indexing is new to me too :)

That's a lot neater than using the ListItem and DictionaryItem functions.

The math functions that are available are those from .NET's System.Math: http://msdn.microsoft.com/en-us/library ... .math.aspx

jaynabonne
Great! Just what I was looking for. And I'm glad to see max and min in there, too. :) (Not that I don't like Quest's if/then/else statements...)

jaynabonne
Some bad news: some of the cooler aspects of this (like being able to use dict[key] for accessing dictionary values and the ternary comma operator) do *not* work in published games. What works fine from the Quest editor gives odd error messages when the .quest game is run (even locally). So be sure if you want to use any of the above that you test it in a .quest game before getting in too deep.

I'm not sure if this is a bug or a "feature". I'll open an issue for it.

homeeman
This is all great, jaynabonne! You've done us all a service!

jaynabonne
Thanks homeeman. :)

One more correction (going the other way now): the ternary operator works fine. It's just the expression syntax with brackets that Quest doesn't like. My original code had combined one of those with the ternary operator, which made it look the latter was at fault. But I think it's all to do with brackets.

If you put this code in a .aslx file:

msg(mydict["key"])

then Quest will change it to:

msg(mydict{UNKNOWN TEMPLATE: "key"})

It actually will change your source file. If the code is in a library, it fails at runtime the same, even though it doesn't touch the source (since it can't).

I've logged an issue for this. Hopefully, the rest of the (non-bracket) expressons work fine.

jaynabonne
An update: with Quest 5.4.1, this issue has been fixed. I have successfully used this [index] syntax successfully in my code.

Sora574
I was going to create another thread, but it seems better to put it in this one.

When using the list indexing, (list[0], list[1], etc) if the value is a string and you put it into a function parameter, like 'dictionary add (d, list[0], list[1])' then Quest will throw an error saying something like 'Error running script: Error compiling expression 'list[0]': RootExpressionElement: Cannot convert type 'Object' to expression result of 'String''.
However, it will convert it to a string with ToString(list[0])... So it seems like Quest is being lazy and not converting it automatically. Is this what's supposed to happen?

jaynabonne
I had asked Alex this myself. See https://quest.codeplex.com/workitem/1280.

The answer (for those too lazy to click):

Anything defined by Quest itself that can return any type (e.g. ListItem in this case) will have an "unknown" return type - in the C# code, anything that returns "object".

This is why functions like StringListItem exist, as that will have a known return type of string.



So the problem is that something like "list[0]" can have any type, which confuses things. Ironically, if you put it into a variable first, it works fine.

jaynabonne
More fun with expressions...

The trick to all of this is to keep in mind that any code you write that involves an expression will use FLEE. Code that doesn't won't. (I'm not sure what the boundary is for that.) And the two don't always behave the same.

Parameter checking

Given this (fairly pointless) function definition:

  <function name="MyFunc" parameters="f1,f2" type="string">
msg("f1 is: " + f1)
return ("Howdy!")
</function>


What will the following calls print out?

A) MyFunc(1,2)
B) msg(MyFunc(1,2))
C) MyFunc(1)
D) msg(MyFunc(1))

Answers:

A) f1 is: 1
B) f1 is: 1
Howdy!
C) Error running script: Too few parameters passed to MyFunc function - only 1 passed, but 2 expected
D) f1 is: 1
Howdy!

That last one is the interesting one. If you try to use f2, you will find that it's undefined in that case. What seems to happen is that a non-expression function call checks parameters. One inside an expression is invoked by FLEE and does not do parameter checking - and any unpassed parameters will be undefined. You could, in theory, use IsDefined to check for whether the parameter has been passed... but I would say stay away from taking advantage of this (undocumented) behavior unless you're willing to possibly pay the price down the road.

Delegates

rundelegate can only be used outside an expression.
RunDelegateFunction can only be used inside an expression.

For example, you can do either of these:

rundelegate(obj, "Method")
x = RunDelegateFunction(obj, "Method2")

but you can't do either of:

x = rundelegate(obj, "Method2")
RunDelegateFunction(obj, "Method")


Eval

"Eval" (or "eval") can only be used inside an expression. And it can only evaluate an expression (something that has an ultimate value). For instance, you can do this:

x = eval("1+1")
x = eval("RunDelegateFunction(player, \"doit\")")


But you can't do:

x = eval("msg(\"Howdy!\")")


You can do this, however:

<function name="MyMsg" parameters="s" type="boolean">
msg(s)
return (true)
</function>

x = eval("MyMsg(\"Howdy!\")")

Sora574
jaynabonne wrote:So the problem is that something like "list[0]" can have any type, which confuses things. Ironically, if you put it into a variable first, it works fine.

Hmm... So would it be possible to make/use something like 'list[0].toString'?

Probably not, right? Because it would try to use list[0] like an object and toString as an attribute
What about list[0.toString]? I think that would just search for the string '0', right?

jaynabonne
Besides ToString(list[0]), you can also do this:

    	dictionary add(d, cast(list[0], String), list[1])


"cast" is a FLEE built-in function.

Sora574
Hmm... That's even longer than just using ToString()
It's not that big of a deal, though. I was just wondering.

jaynabonne
Another point of note regarding expressions:

For those of you who would like to do bitwise operations, it turns out that FLEE does support them - they are actually overloads of the standard AND, OR, and NOT operators, which are usually used for Boolean operations. The operator behaviors depend on the types of the operands (just as "+" differs depending on whether you are "plus'ing" ints or strings). If the operands are Boolean, you get the standard logical operators. If the operators are ints, then you get bitwise operations.

Here are some examples:

msg (1 and 3)

1

msg(0xf7 and 0xcf)

199 (= 0xc7)

msg(0x30 or 0x1e)

62 ( = 0x3e)

msg(not 255)

-1 (note that ints are signed!)

msg(0x1234 and not 0xff)

4608 (= 0x1200)

msg(0x1234 xor 0x1230)

4

(Note that xor works for logical operands as well, though that is just the same as "not equals"!)

Enjoy! :)

Alex
Interesting, but I'd be wary of using any of these that don't work unchanged in JavaScript!

jaynabonne
Could you explain that? I'm not sure what you mean. :)

Which ones "don't work unchanged" (?) in Javascript? Or I guess more importantly, what is the issue and how would I know?

Alex
When a game is converted to JS, there are a few standard transformations that are applied to turn FLEE format expressions into ones which are compatible with JS. Anything that's not used in Quest won't have a conversion, so I'd advise against doing anything clever that is FLEE-specific. We convert "and" in an expression to "&&" for example, which will break any bitwise operations. The converter won't know ahead of time which elements are int and which are boolean, so it will be hard to update the converter to handle this correctly.

jaynabonne
Hmm. Ok. That's too bad but good to know!

Just one more question: since it works fine at least in the desktop player, when is a game converted to JS?

Alex
When a game is converted into a mobile app (potentially in the future, also stand-alone web pages or desktop apps).

This topic is now closed. Topics are closed after 60 days of inactivity.

Support

Forums