tiny bug with NG bcc

BlitzMax Forums/BlitzMax NG/tiny bug with NG bcc

GW(Posted 2017) [#1]
I made this mistake today while making a quick one-off program.
Local lo:Double = low()
Local hi:Double = high()
Local binsize:Double = (hi-low)/numbins

Accidentally attempting to subtract a func pointer[low] from a double instead of the local[lo].

Vanilla bmax catches it, but with NG it gets passes to gcc and complains there. I suppose there might be a situation where you'd want to subtract a number and pointer without casting though.

Just an FYI. definitely not urgent.


Derron(Posted 2017) [#2]
Imho this also exposes a flaw in the blitzmax-language itself: you can leave out brackets/braces. Imho you should use it in all cases (yes, even SetColor(255,0,0) and SetAlpha(1.0) would need it then - also print("hey")).


low 'works
print low 'fails


Seems vanilla complains as soon as a function is used without brackets in an "ambiguous way".

low 'works
print low 'fails
low + high() 'fails
functionWithParam low 'fails
functionWithParam(low) 'fails
functionWithParam low() works


Will try to raise an issue on github for this problem

Edit:
https://github.com/bmx-ng/bcc/issues/240

bye
Ron


GW(Posted 2017) [#3]
ya, It seems that if the result of the function is not a simple assignment with a defined auto-cast, the compiler will catch it.
I agree though, I always use parens on functions as a personal style, even void functions.
I've noticed over the years that Marks bmax style is leave out the parens.
I think the compiler being generous is still a plus though.


Derron(Posted 2017) [#4]
Brucey committed a fix for this issue.

Bye
Ron


FireballStarfish(Posted 2017) [#5]
(accidental doube post)


FireballStarfish(Posted 2017) [#6]
Seems vanilla complains as soon as a function is used without brackets in an "ambiguous way".

It's not really ambiguous and I personally don't think of it as a flaw; I like not having to add parentheses everywhere.
It's actually quite simple: when you use a function call as an expression, you need the parentheses, but when you use it as a statement they're optional.
Think of the statement as a normal line of code. If your function call is the last, or "outer"most action to be performed there (i.e. the function name is at the beginning of the line), then the parentheses are optional. For example, if you write
functionWithParam low()
or
functionWithParam(low())
then the call to functionWithParam is a statement, this the parentheses for it optional. But the call to low is an expression, so its parentheses are required.

functionWithParam low
or
functionWithParam(low)
is also valid syntax, but with a different meaning: in this case, you're not calling low and passing the result to functionWithParam; instead you're passing low itself.

By the way, an addition can't be a statement, so the example
low + high()
that you gave can't ever be a valid line of code by itself, regardless of parentheses. But an assignment is one, so you could write
x = low() + high()
where both calls are expressions, requires both pairs of parentheses.


Derron(Posted 2017) [#7]
By the way, an addition can't be a statement, so the example
low + high()




This compiles and runs with vanilla:
SuperStrict
Framework Brl.StandardIO

Function Integer:int(i1:int=1, i2:int=2)
	print "Integer: i1="+i1+" i2="+i2
	return i1+i2
End Function

Integer + Integer()


The ambiguous part there is, that you need to know the function definition when calling them in a similar way. So if you think "Integer" is called "Millisecs" and returns a value - but in reality it also accepts a param to eg. return "Millisecs since last call" or so, then you run into trouble as "+ Integer()" is now evaluated as a param with content "[ignored]positive-sign and result-of-function-call".

which is why the output of above is:
Integer: i1=1 i2=2
Integer: i1=3 i2=2

(the "i1=3" is important)

I know that you "normally" do not call things that way and that in this case the result of the "addition" is only possible when used in conjunction with the needed brackets. But it still feels "dirty".



Edit, here is another thing:
SuperStrict
Framework Brl.StandardIO


Function Str:string(s1:string="1", s2:string="2")
	print "Str: s1="+s1+" s2="+s2
	return s1+s2
End Function

print ("hey") 'print accepts brackets (no suprise)
'print (Str 1,2) 'but this still fails while not being ambiguous
print (Str(1,2)) 'while this works
print Str(1,2) 'and this too
Str 1,2 'and this


So why is the second print not working? Isn't there no "alternative" way of interpreting that line?


bye
Ron


FireballStarfish(Posted 2017) [#8]
This compiles and runs with vanilla: [...] The ambiguous part there is, that you need to know the function definition when calling them in a similar way.

But this is why I emphasized that an addition cannot be a statement. If your line only consists of
Integer + Integer()
and it compiles, then you know something is amiss, because it can't have been interpreted as an addition.
Doing an addition without using the result for anything would be pointless. But if you do use the result, as in
Local x:Int = Integer + Integer()
then the code won't compile anyway because the parentheses are mandatory for both calls.

print ("hey") 'print accepts brackets (no suprise)
'print (Str 1,2) 'but this still fails while not being ambiguous
print (Str(1,2)) 'while this works
print Str(1,2) 'and this too
Str 1,2 'and this
So why is the second print not working? Isn't there no "alternative" way of interpreting that line?
That's what I tried to explain in my last post. It's not about whether the parameters would be ambiguous. It's about statements and expressions.
Each whole line in the bottom block of your example code there is a standalone statement (in your example: each of your Print calls and the Str call on the last line). Arguments to calls are always expressions (in your example: "hey", each Str call except on the last line, 1 and 2). The only time parentheses can be omitted in a function call is when that call itself is a statement.



I have run into certain special cases where it indeed gets confusing, but those are mostly due to its interaction with a different, very questionable aspect of the BlitzMax syntax: the fact that statements do not actually need to be separated from each other by a newline or a semicolon, or, in fact anything. This is for some reason valid BlitzMax:
SuperStrict
Framework BRL.StandardIO

Local a:Int = 4Local b:Int = 5Print a + "," + b

Normally this can be ignored, but combining this with optional parentheses can create surprises in some cases like this:
SuperStrict
Framework BRL.Blitz

Function GetFunction(s:String)()
	Return WriteStdout
End Function

Function GetString:String()
	Return "hi"
End Function

GetFunction() GetString()

You might think this prints "hi", because the last line of this code should be equal to WriteStdout "hi". After all, GetFunction() returns WriteStdout and GetString() returns "hi". But what it actually does is calling GetFunction() and GetString() as a separate statement each (as if they were separate lines), causing it to not write anything to the console at all. If you want it to print "hi" then the last line needs to be written like this:
GetFunction()(GetString())
But again, this is more of an issue with the lack of mandatory separators, not with optional parentheses (and it comes up relatively rarely in practice).


Derron(Posted 2017) [#9]
Thanks for your elaborative post.

While I understand that bcc handles expressions and statements differently I still think brace-enforcement would help. A Blitzmax-gamedeveloper should not need to think about expressions and statements. He should be able to write as "seen elsewhere".


@ your code (local local print)
I hope nobody is coding like this ;-)


Bye
Ron


FireballStarfish(Posted 2017) [#10]
Well, that game developer can always use the rule of thumb "parentheses are optional when the function name is the leftmost thing on the line", which almost always works (unless they're doing something weird like in our examples), and when in doubt, add the parens to make sure. :) Personally I feel it makes my code much more readable to omit them in most cases.

@ your code (local local print)
I hope nobody is coding like this ;-)
Well, with some extra whitespace I sometimes write compact select statements like this and, very rarely, one-line loops. But it wouldn't hurt to be forced to insert a couple semicolons there either.


Derron(Posted 2017) [#11]
Yes, for Select you are right ... am using this too ;-)

Exception is a very long "case" (eg. multiple values chained with commas).

For local i:int=0 to 10;print i;next

Yes, did that too but the ";" still do not make it look less dirty or more readable.


@ rule of thumb
If you are no "hardcore coder" but one who tries to get his idea running (and not more) these rules are hard to get. You see that omitting the braces is possible - and then there are situations which do not allow it.
In a quick search I did not find information about "parentheses rules" for the blitzmax language - maybe it is already clarified somewhere.


bye
Ron


grable(Posted 2017) [#12]
Its quite simple really.

The ONLY time you can skip parens is when your calling a named function/method as a statement.
All other times its an expression and you are either using its return value or you are already inside some other expression.

EDIT: Reading the backlog, i see you already mention this Derron. But i think the distinction is important, even for newbies.