Math syntax parsing issue Topic is solved

Get help with using AutoHotkey (v2 or newer) and its commands and hotkeys
User avatar
mikeyww
Posts: 28871
Joined: 09 Sep 2014, 18:38

Re: Math syntax parsing issue

Post by mikeyww » 08 Sep 2023, 07:30

This is not an inconsistency. The only inconsistency I found elsewhere in our other examples relates to how the first two operands are processed in other examples where those operands are incrementing a value.

User avatar
andymbody
Posts: 1034
Joined: 02 Jul 2017, 23:47

Re: Math syntax parsing issue

Post by andymbody » 08 Sep 2023, 07:34

a POINTER issue maybe:

Run this... and pay attention to the elements that contain (i)

Code: Select all

arr := [i:=1000, -2000, i:=3, i=4, i:=5, i:="element6", 7]
MsgBox(arr[1])
MsgBox(arr[2])
MsgBox(arr[3])
MsgBox(arr[4])
MsgBox(arr[5])
MsgBox(arr[6])
MsgBox(arr[7])

User avatar
mikeyww
Posts: 28871
Joined: 09 Sep 2014, 18:38

Re: Math syntax parsing issue

Post by mikeyww » 08 Sep 2023, 07:37

I see no problem with internal consistency here, as the value of all i is simply updated as the math or evaluation proceeds from left to right.

I noticed earlier that a non-incrementing operand would "stop" the incrementing to its left, but I did find an inconsistency with the first two operands as compared to subsequent ones.

User avatar
andymbody
Posts: 1034
Joined: 02 Jul 2017, 23:47

Re: Math syntax parsing issue

Post by andymbody » 08 Sep 2023, 07:46

bonobo wrote:
08 Sep 2023, 01:19
So ultimately it seems to stem from something about how assignment expressions are evaluated inside larger expressions.
After further testing, I think the result you got for each element is simply the final value of i after each element was calculated from the array expression. (i) then holds the final value, and this value (via pointer?) is displayed as the value for the elements that contain the (i) variable.

So even tho you produced a similar result to the ++d issue, it may of may not be related.

See my previous example to see if you agree.

User avatar
andymbody
Posts: 1034
Joined: 02 Jul 2017, 23:47

Re: Math syntax parsing issue

Post by andymbody » 08 Sep 2023, 07:50

mikeyww wrote:
08 Sep 2023, 07:37
I see no problem with internal consistency here, as the value of all i is simply updated as the math or evaluation proceeds from left to right.
Thanks Mike, I agree that this is a non-issue after you pointed this out. Do you think this variable update might be causing the "strange" ++d result as well (in the original discussion)?

User avatar
Noitalommi_2
Posts: 393
Joined: 16 Aug 2023, 10:58

Re: Math syntax parsing issue

Post by Noitalommi_2 » 08 Sep 2023, 08:13

teadrinker wrote:
07 Sep 2023, 22:56
Another one example:

Code: Select all

d := 1
MsgBox ++d + ++d
Try to predict, without executing the code, what this expression will equal.
I predicted 5 and it's wrong.

User avatar
mikeyww
Posts: 28871
Joined: 09 Sep 2014, 18:38

Re: Math syntax parsing issue

Post by mikeyww » 08 Sep 2023, 09:01

According to what we have learned, the value must be 6, because the final d value is 3.

The next one is 7, because d becomes 2, 2 + 2 is 4, and 3 is then added to that.

Code: Select all

#Requires AutoHotkey v2.0
d := 1
MsgBox ++d + d + ++d

User avatar
andymbody
Posts: 1034
Joined: 02 Jul 2017, 23:47

Re: Math syntax parsing issue

Post by andymbody » 08 Sep 2023, 10:02

mikeyww wrote:
08 Sep 2023, 09:01
According to what we have learned, the value must be 6, because the final d value is 3.
So, are you suggesting that the values produced in the beginning of the thread are normal and expected (not a bug) based on what we have learned and we can move on?

This still makes me uneasy based on the comparisons with other languages. But so be it if that's the case.

Andy

User avatar
boiler
Posts: 17706
Joined: 21 Dec 2014, 02:44

Re: Math syntax parsing issue

Post by boiler » 08 Sep 2023, 10:15

mikeyww wrote: According to what we have learned, the value must be 6, because the final d value is 3.

The next one is 7, because d becomes 2, 2 + 2 is 4, and 3 is then added to that.

Code: Select all

#Requires AutoHotkey v2.0
d := 1
MsgBox ++d + d + ++d
This example would seem to be explained not by the final d value, because that would result in 9, but by what thqby described regarding what gets pushed on the stack and when. To me, that may explain the result, but it still doesn’t square it with how it’s documented. It doesn’t make sense for AHK users to have to understand what’s happening under the hood. They should be able to look at the code and documentation and take it at face value.

User avatar
andymbody
Posts: 1034
Joined: 02 Jul 2017, 23:47

Re: Math syntax parsing issue

Post by andymbody » 08 Sep 2023, 10:36

So this is my understanding at this point.

Essentially you can envision the expression operation as follows?

1. All ++d found in the expression are calculated prior to any math operators being applied from left to right.
2. Then the individual math operations are performed from left to right using the final value of d that was obtained using step 1.

Because... the expression is a separate operation to the display operation of msgbox. The Msgbox retrieves its values AFTER the entire expression has been performed, and new values have been stored in memory for d.

Does that sound accurate?

I am unable to do any tests with this in mind, but can later.

If this is accurate then I agree with @boiler about it being a bit of a pain to keep track of for many AHK coders.

just me
Posts: 9763
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Math syntax parsing issue

Post by just me » 08 Sep 2023, 11:05

And that's my traditional understanding:

Code: Select all

d := 1
MsgBox ((d) * (++d) * (++d))
What happens:
  1. (d) * (++d)
    1. (++d) -> d = 2
    2. (d) * (d) -> r1 = 4 (interim result)
  2. (r1) * (++d)
    1. (++d) -> d = 3
    2. (r1) * (d) -> 12
What could happen:
  1. (d) * (++d)
    1. (d) -> l = 1 (left operand)
    2. (++d) -> r = 2 (right operand)
    3. (l) * (r) -> i = 2 (interim result)
  2. (i) * (++d)
    1. (++d) -> r = 3 (right operand)
    2. (i) * (r) -> 6

User avatar
mikeyww
Posts: 28871
Joined: 09 Sep 2014, 18:38

Re: Math syntax parsing issue

Post by mikeyww » 08 Sep 2023, 11:47

Andy, I was just explaining how the bug works! :)

Perhaps "bug" is a matter of opinion here. I do call this a bug because it's a result that many did not expect or want-- so agree with you as well as boiler.

I had noted that the non-incremented operand "stops" the incrementing for operands to its left-- but do not feel that this is best!

It does explain why this is 7.

Code: Select all

#Requires AutoHotkey v2.0
d := 1
MsgBox ++d + d + ++d
2 + 2 + 2
then 2 + 2 + 3.

Sensible? Not really!

User avatar
andymbody
Posts: 1034
Joined: 02 Jul 2017, 23:47

Re: Math syntax parsing issue

Post by andymbody » 08 Sep 2023, 12:01

just me wrote:
08 Sep 2023, 11:05
And that's my traditional understanding:
My understanding was incorrect then. It still increments d from left to right along with the math operations.

I found a solution... avoid using ++x (in complex expressions). There are other ways that don't result in smoke coming out of my ears. :headwall:
mikeyww wrote:
08 Sep 2023, 11:47
Andy, I was just explaining how the bug works! :)
Sensible? Not really!
Thank you sir!

Time to start the weekend. :D

Andy

User avatar
mikeyww
Posts: 28871
Joined: 09 Sep 2014, 18:38

Re: Math syntax parsing issue

Post by mikeyww » 08 Sep 2023, 13:06

Yes, one can definitely work around this through using multiple math statements in sequence.

User avatar
TheArkive
Posts: 1032
Joined: 05 Aug 2016, 08:06
Location: The Construct
Contact:

Re: Math syntax parsing issue

Post by TheArkive » 09 Sep 2023, 03:55

Yah, that's what I ended up doing as well. Very interesting discourse. An opinion was even changed :-P

lexikos
Posts: 9780
Joined: 30 Sep 2013, 04:07
Contact:

Re: Math syntax parsing issue  Topic is solved

Post by lexikos » 10 Sep 2023, 06:44

++
--
The operator may appear either before or after the variable's name. If it appears before the name, the operation is performed and its result is used by the next operation (the result is a variable reference in this case).
Source: Variables and Expressions - Definition & Usage | AutoHotkey v2
This comment from the := is also true for pre-inc/dec:
If an assignment is used as the input for some other operator, its value is the variable itself.
Source: Variables and Expressions - Definition & Usage | AutoHotkey v2
++d is evaluated and the result is d itself, as documented. As in some other languages, it is unspecified exactly when d will be evaluated/dereferenced. What you are expecting is for the left-hand variable in ++d + ++d to be dereferenced before the right-hand sub-expression is evaluated. It could be argued that doing so would contradict or remove any meaning from "the result is a variable reference".

It is the same with [d, ++d] or any function call where a variable is modified as a side-effect of evaluating the parameters.

In v1, it is the means by which d or ++d can be passed to a ByRef parameter, by reference. In v2 that particular excuse doesn't apply, because &d must be used for ByRef.

In the current implementation (and this should not be taken as any kind of guarantee), a variable is not dereferenced before becoming input to an operator or function. The variable itself is the input, and the operator or function dereferences the variable if that is what it is designed to do. An operator will use whatever value the variable has when the operator is evaluated, not the value one operand had before the other operand was evaluated. This is consistent across all operators, so for instance, &++x takes a variable reference, (++x) /= 2 increments and then halves, and ++x + ++x does what it does.

Suppose that ++x resulted in the value of x when the next operator doesn't need a variable. In v2 there's no expectation that f(++x) will pass x by reference, so this would be reasonable; x:=0, [++x, ++x] could result in [1, 2], as you'd likely expect. x+=1 should behave the same as ++x, and maybe you would have the same expectations of it anyway. All compound assignments should behave consistently, so for instance, x:=1, [x *= 3, x <<= 2] should result in [3, 12]. This is all feasible so far, without sacrificing performance or other things.

But there's also .= and not just compound assignment, but := as well. For instance, a:="A", f(a, a:="B") should call f("A", "B"); but in order to do that, it needs to copy the string contained by a. This is implied by the fact that a variable has "string capacity" and an "internal string buffer". To limit the performance impact to just the necessary situations, more complexity is needed.

That could be in the form of more complex analysis at load-time to determine whether side-effects of an expression may alter a variable before its value will be used, in order to avoid copying when possible. This could be either very difficult or somewhat ineffective, as functions called by the expression might indirectly cause a variable's value to change. If we err on the side of caution and simplicity, and assume any function call could modify any variable, strings would still be copied unnecessarily.

Or it could be by changing how strings are implemented, such as to use reference-counted strings. More complexity is needed to preserve backward-compatibility. For instance, using StrPtr(x) or VarSetStrCapacity(&x) might imply that the string must be copied rather than referenced. There are also several different ways strings are represented in the current implementation, and the code mostly lacks the kind of abstraction that would facilitate this type of change. Over time, I have taken some opportunities to make the code more ready for this, and intend to do it eventually.

Aside from strings, there is also objects. If a variable reference evaluates to an object, a counted reference to the object must be held; otherwise f(a:=[], a:={}) would either pass the same object to both parameters, or pass a dangling pointer to the first parameter. This means an additional AddRef and Release, but it's probably nothing compared to the open-ended cost of copying a string. It probably should be done for other reasons anyway; for instance, f(Buffer(1).ptr, a := 2) is fine because the temporary object is guaranteed to be released only after the expression completes, but currently a:=Buffer(1), f(a.ptr, a := 2) won't work because a := 2 has the side-effect of deleting the object.

User avatar
mikeyww
Posts: 28871
Joined: 09 Sep 2014, 18:38

Re: Math syntax parsing issue

Post by mikeyww » 10 Sep 2023, 07:13

I appreciate all of these details (understood a couple of them! :) ).

My question: does the following script show the values that you would have predicted?

Code: Select all

#Requires AutoHotkey v2.0
d := 1
MsgBox ++d + ++d + d
d := 1
MsgBox ++d + d + ++d

lexikos
Posts: 9780
Joined: 30 Sep 2013, 04:07
Contact:

Re: Math syntax parsing issue

Post by lexikos » 10 Sep 2023, 07:46

Yes.

They give different results because the two sub-expressions of the first + are evaluated, and their side-effects carried out, before the third sub-expression is evaluated. The result of the first + is a number, not a variable, so it is unaffected by the additional ++d in the second case.

User avatar
andymbody
Posts: 1034
Joined: 02 Jul 2017, 23:47

Re: Math syntax parsing issue

Post by andymbody » 10 Sep 2023, 08:07

mikeyww wrote:
10 Sep 2023, 07:13
I appreciate all of these details (understood a couple of them!
Well, that's 2 more than me! I had no idea how complex things got behind the scenes. Thank you for for creating higher level languages so idiots like me can play too. :crazy:

User avatar
TheArkive
Posts: 1032
Joined: 05 Aug 2016, 08:06
Location: The Construct
Contact:

Re: Math syntax parsing issue

Post by TheArkive » 10 Sep 2023, 08:26

Thanks for the in-depth explanation lexikos. I've certainly moved on to writing less ambiguous expressions, and it's interesting to see the inner workings of AHK explained, and see possible future trajectories for its development.

Post Reply

Return to “Ask for Help (v2)”