EnTerr
Roku Guru
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
11-04-2014
09:47 PM
Puzzle: Invalid value for left-side of expression
I just shot myself in the foot in a rather fascinating way, I thought i'll share it as a puzzle.
I was sketching a stack object constructor as an example (don't ask why, that's besides the point):
And then i tested it:
I kept reading and reading, and re-reading #6 ` push: function(x): m.stk[m.ptr] = x: m.ptr = m.ptr + 1: end function,` and i could not see anything to justify the error. Took me a long while to find the cause.
Can you see the reason?
I was sketching a stack object constructor as an example (don't ask why, that's besides the point):
function Stack(n):
dim stack[n]
return {
stk: stack,
ptr: 0,
push: function(x): m.stk[m.ptr] = x: m.ptr = m.ptr + 1: end function,
pull: function(): m.ptr = m.ptr - 1: return m.stk[m.ptr]: end function
}
end function
And then i tested it:
BrightScript Debugger> s = Stack(100)Whaaat?
BrightScript Debugger> s.push(42)
Invalid value for left-side of expression. (runtime error &he4) in pkg:/source/main.brs(6)
I kept reading and reading, and re-reading #6 ` push: function(x): m.stk[m.ptr] = x: m.ptr = m.ptr + 1: end function,` and i could not see anything to justify the error. Took me a long while to find the cause.
Can you see the reason?
7 REPLIES 7

TheEndless
Channel Surfer
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
11-04-2014
10:08 PM
Re: Puzzle: Invalid value for left-side of expression
My guess would be it's because you tried to use a locally scoped variable with the same name as the function, so m.stk is actually equal to the function Stack() and not your array stack.
My Channels: http://roku.permanence.com - Twitter: @TheEndlessDev
Instant Watch Browser (NetflixIWB), Aquarium Screensaver (AQUARIUM), Clever Clocks Screensaver (CLEVERCLOCKS), iTunes Podcasts (ITPC), My Channels (MYCHANNELS)
Instant Watch Browser (NetflixIWB), Aquarium Screensaver (AQUARIUM), Clever Clocks Screensaver (CLEVERCLOCKS), iTunes Podcasts (ITPC), My Channels (MYCHANNELS)
EnTerr
Roku Guru
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
11-05-2014
02:54 PM
Re: Puzzle: Invalid value for left-side of expression
"TheEndless" wrote:
My guess would be it's because you tried to use a locally scoped variable with the same name as the function, so m.stk is actually equal to the function Stack() and not your array stack.
Yep. Insidious, isn't it?
Since there is no primary error on name-duping and as bonus the error printed is not what i'd expect.
This seems like a great use for one of them new-fangled WARNING messages, if not a full-blown error. 'Cause current behavior is not particularly sane. (And if at some point in the future a reasonable policy on name scope priority comes, it is always easier to allow something that was forbidden - than to forbid something that was allowed).
I don't even know how the heck does this mis-happen? Since all local variables are resolved to indices at compile-time and that did not happen at compile time for `stk: stack`, doesn't that mean compiler first checked the global function table - as it was at that point - before locals table? And if so - and here it goes crazy - that will make it non-deterministic, since what if "Stack()" was not the current or previous function but defined a few lines later? Wouldn't then `stk: stack` usage resolved into a local?
I would like to say "thanks but not really" to the person that recently disabled console's "DA" command, it sure is more fun me having to talk out of my <censored> that to look at op-codes and figure what might be wrong.
EnTerr
Roku Guru
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
11-19-2014
11:19 AM
Re: Puzzle: Invalid value for left-side of expression
"EnTerr" wrote:
I don't even know how the heck does this mis-happen? Since all local variables are resolved to indices at compile-time and that did not happen at compile time for `stk: stack`, doesn't that mean compiler first checked the global function table - as it was at that point - before locals table? And if so - and here it goes crazy - that will make it non-deterministic, since what if "Stack()" was not the current or previous function but defined a few lines later? Wouldn't then `stk: stack` usage resolved into a local?
Necessarily speaking from my Equus asinus, i experimented and seems that BrS compiler does "hoisting" for global functions. What would you expect foo() to print here:
sub foo():[spoiler=spoiler:3hkbg4ey]BrightScript Debugger> foo()
bar = 1
foobar = bar
print foobar
end sub
'
' ... big chunk of code here ...
'
sub bar():
end sub
<bsTypedValue: Function>[/spoiler:3hkbg4ey]
The result is startling for couple of reasons. First, bar gets treated differently depending on which side of the assignment operator it is! When it is a L-value, being assigned to - it is a local variable (we see later the global did not get mangled). When it is a R-value (to the right of assignment) though, it returns the global.
And second (this requires a little more thinking but): when compiling foo(), how did compiler know there will be a global bar() - since it comes much later textually? The explanation is "function hoisting" - it has gone first through all source to peek at all function names before starting to compile function bodies. (I know function hoisting from Javascript and odd things happen there too because of it).
Weird, huh! And yet there is not even a warning during execution.

TheEndless
Channel Surfer
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
11-19-2014
11:34 AM
Re: Puzzle: Invalid value for left-side of expression
"EnTerr" wrote:"EnTerr" wrote:
I don't even know how the heck does this mis-happen? Since all local variables are resolved to indices at compile-time and that did not happen at compile time for `stk: stack`, doesn't that mean compiler first checked the global function table - as it was at that point - before locals table? And if so - and here it goes crazy - that will make it non-deterministic, since what if "Stack()" was not the current or previous function but defined a few lines later? Wouldn't then `stk: stack` usage resolved into a local?
Necessarily speaking from my Equus asinus, i experimented and seems that BrS compiler does "hoisting" for global functions. What would you expect foo() to print here:sub foo():[spoiler=spoiler:2j6ds0kf]BrightScript Debugger> foo()
bar = 1
foobar = bar
print foobar
end sub
'
' ... big chunk of code here ...
'
sub bar():
end sub
<bsTypedValue: Function>[/spoiler:2j6ds0kf]
The result is startling for couple of reasons. First, bar gets treated differently depending on which side of the assignment operator it is! When it is a L-value, being assigned to - it is a local variable (we see later the global did not get mangled). When it is a R-value (to the right of assignment) though, it returns the global.
And second (this requires a little more thinking but): when compiling foo(), how did compiler know there will be a global bar() - since it comes much later textually? The explanation is "function hoisting" - it has gone first through all source to peek at all function names before starting to compile function bodies. (I know function hoisting from Javascript and odd things happen there too because of it).
Weird, huh! And yet there is not even a warning during execution.
I'm not sure why you'd be startled by that result. It's already been established that functions always take precedence over variables (FWIW, I'm not happy about it either). I'd also expect it to do "function hoisting".. otherwise you'd have to be very deliberate in how you structure your BRS files, and you wouldn't be able to define "methods" in the construction of an AA-based "class".
My Channels: http://roku.permanence.com - Twitter: @TheEndlessDev
Instant Watch Browser (NetflixIWB), Aquarium Screensaver (AQUARIUM), Clever Clocks Screensaver (CLEVERCLOCKS), iTunes Podcasts (ITPC), My Channels (MYCHANNELS)
Instant Watch Browser (NetflixIWB), Aquarium Screensaver (AQUARIUM), Clever Clocks Screensaver (CLEVERCLOCKS), iTunes Podcasts (ITPC), My Channels (MYCHANNELS)
EnTerr
Roku Guru
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
11-19-2014
12:17 PM
Re: Puzzle: Invalid value for left-side of expression
Hm, did you really have to quote the whole shebang i said? 😉
"TheEndless" wrote:Nope, not always. See the example i gave, read the juxtaposition of Lvalue and Rvalue. Therein lies the problem, it an easy-to-learn language does not make. It should not flip-flop in treatment of names depending on which side of "=" they lie.
I'm not sure why you'd be startled by that result. It's already been established that functions always take precedence over variables (FWIW, I'm not happy about it either).
I'd also expect it to do "function hoisting".. otherwise you'd have to be very deliberate in how you structure your BRS files,Which is why hoisting was done, so that you don't have to put Main() at the very end and also can have mutually-recursive functions (foo() calls bar(), bar() calls foo()). I did not object to it - rather pointed it was done and the quirks it adds.
and you wouldn't be able to define "methods" in the construction of an AA-based "class".That... that's completely unrelated. This
obj = {relies neither on hoisting nor on named-function globality. Or did i misunderstand you, through example if i did.
f: function(): return m.field: end function,
field: 0
}

TheEndless
Channel Surfer
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
11-19-2014
01:05 PM
Re: Puzzle: Invalid value for left-side of expression
"EnTerr" wrote:
Hm, did you really have to quote the whole shebang i said?
I was too lazy for cut and paste, so just hit the quote button on your previous post. So, no, I didn't have to quote the whole thing... 😉
"EnTerr" wrote:
Nope, not always. See the example i gave, read the juxtaposition of Lvalue and Rvalue. Therein lies the problem, it an easy-to-learn language does not make. It should not flip-flop in treatment of names depending on which side of "=" they lie.
That's an issue of scope, I think. The Lvalue is a locally scoped Set, so there's most likely a locally scoped "bar" variable on the stack at that point. You just can't get to it, because as an Rvalue, it's evaluated to the global function instead.
"EnTerr" wrote:
That... that's completely unrelated. Thisobj = {
f: function(): return m.field: end function,
field: 0
}
relies neither on hoisting nor on named-function globality. Or did i misunderstand you, through example if i did.
I don't think you misunderstood, you just didn't expand your understanding to include the scenario I was referring to. I rarely use anonymous functions in my code, so I was referring to something like this:
Function MyClass() As Object
this = {
ClassName: "MyClass"
MyMethod: MyClass_MyMethod
}
Return this
End Function
Sub MyClass_MyMethod()
' Do Something
End Sub
Without function hoisting, that wouldn't be possible, because MyClass_MyMethod wouldn't exist, yet, when MyClass was defined.
My Channels: http://roku.permanence.com - Twitter: @TheEndlessDev
Instant Watch Browser (NetflixIWB), Aquarium Screensaver (AQUARIUM), Clever Clocks Screensaver (CLEVERCLOCKS), iTunes Podcasts (ITPC), My Channels (MYCHANNELS)
Instant Watch Browser (NetflixIWB), Aquarium Screensaver (AQUARIUM), Clever Clocks Screensaver (CLEVERCLOCKS), iTunes Podcasts (ITPC), My Channels (MYCHANNELS)
EnTerr
Roku Guru
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
11-25-2014
12:48 PM
Re: Puzzle: Invalid value for left-side of expression
"TheEndless" wrote:Don't have to copy&paste. I do select + delete. "Cut on byte pollution, save the environment" 😄
I was too lazy for cut and paste, so just hit the quote button on your previous post. So, no, I didn't have to quote the whole thing... 😉
And that is the crux of the matter!"EnTerr" wrote:
Nope, not always. See the example i gave, read the juxtaposition of Lvalue and Rvalue. Therein lies the problem, it an easy-to-learn language does not make. It should not flip-flop in treatment of names depending on which side of "=" they lie.
That's an issue of scope, I think. The Lvalue is a locally scoped Set, so there's most likely a locally scoped "bar" variable on the stack at that point. You just can't get to it, because as an Rvalue, it's evaluated to the global function instead.
It should not change the semantics (who are you talking about) depending on whether a name is used on the left side or the right side of =.
There is no other language that confuses name resolution like that. Not to mention the morass of explaining l-value vs r-value (which one has probably never heard of unless they've read K&R)
I rarely use anonymous functions in my code, so I was referring to something like this:Curious. You wouldn't happen to come from C++ provenance, are you?Function MyClass() As Object
this = {
ClassName: "MyClass"
MyMethod: MyClass_MyMethod
}
Return this
End Function
Sub MyClass_MyMethod()
' Do Something
End Sub
It's not worse style, it's just... different. I don't think there is any difference between anonymous and "onymous" functions in B/S. There is no reflection to ask fn of its own name. Yeah, you can use the global-named ones as standalone functions for testing but then again i can "steal" anonymous function from an object/instance and do the same.
Without function hoisting, that wouldn't be possible, because MyClass_MyMethod wouldn't exist, yet, when MyClass was defined.Yeah, so what? 😄 That would just follow from (an imaginary) general rule that a function - MyClass() in your case - can only use other functions defined before it. In other words MyClass_MyMethod() would have to be textually written before it. Which turns out to be reasonable requirement, if you have done programming in Pascal/Modula. Hoisting is needed in the cases of circular reasoning, though that also has a work-around - forward declarations.