LoginLogin
Might make SBS readonly: thread

Duplicate variable that does not exist

Root / SmileBASIC Bug Reports / [.]

S_DE_SolutionsCreated:
There is a Bug (or should i say peculiarity?) that should be know, if you don't want to go in trouble: Working with subprogram or functions can be fine, if you are knowing this: If you need a real global-variable, you have to define it bevor every other Line of Code. Also, every line after the new setting VAR will know this Value (but no Line before ) This can be sometimes nice and sometimes frustrating, because you can't write that you want, there you want. (Besides: Running a subprogram that define the variables will not work.) Look at this example, that generate the Error ("Duplicate variable in...."):
GOSUB @INI
GOSUB @MAIN
END

@MAIN
WHILE BUTTON()!=#X
  PRINT A
WEND
RETURN
'END

@INI
VAR A = "It runs ; let us sell it !"
RETURN
'END
You see, there is only one Variable, but a Error will showed. That means: SmileBasic ignores, that the Variable should set at the first Sub before the second is started. If you change the order the code will work fine:
GOSUB @INI
GOSUB @MAIN
END

@INI
VAR A = "It runs ; let us sell it !"
RETURN
'END

@MAIN
WHILE BUTTON()!=#X
  PRINT A
WEND
RETURN
'END
If you needed only a temporary variable, you should put your subprogram (or function) on the last line of Code (as it possible). (I have posted this here, because i haven't found a description of this simple problem.) Used Smile-Basic Version: 3.6.0 (Europe-Version)

I would first put OPTION STRICT at the beginning of your code. It helps with variables in functions. Then I would put the VAR A out of the @INI (probably at the VERY beginning of your code.) It thinks the variable is in @INI so once it's out of @INI it gets rid of the variable. To make it keep the variable, you have to define the variable out of the GOSUB/function. Like this:
OPTION STRICT
VAR A$

GOSUB@INI
GOSUB@MAIN
STOP

@INI
 A$="It runs ; let us sell it!"
RETURN

@MAIN
 WHILE BUTTON()!=#X
  PRINT A$
 WEND
RETURN
You could see I did multiple things to the code. Like instead of END I put STOP. Stop does almost the same thing, but it also works inside functions. I also made A inti A$ because it is a string, so that gives SMILEBASIC more information about your variables and makes your code easier to read, because that's how you know it's a string. Then, I put the OPTION STRICT at the beginning of the code. This makes me define all my variables, so that when reading your code you know what all your variables are; SMILEBASIC can also know whether your variables are specific to the functions or universal throughout your code. I also put VAR A$ after that, because that's how you define variables. If you don't put that and you have option strict, it'll give an error that says, "UNDEFINED VARIABLE IN..." Hope you understand all that.

Variables aren't created when VAR is reached, they're all created before the program starts. SB searches for variables from top to bottom, so VAR needs to be before (ABOVE) any other places where that variable is used. (This is the same way almost every programming language works) Your example would normally be written:
VAR A$ = "It runs ; let us sell it !"

WHILE BUTTON()!=#X
 PRINT A$
WEND
Generally, programs in SB are structured as: <variable declarations/initialization> <main loop> <function definitions>

Thanks for the supplement. I know that OPION STRICT can be useful, but not for this case. If you insert it in the first example, only the correct error line is given out, but without saying why. So i have tried to explain, how SMILE Basic read the entered code bevor it execute. The example is intended for easy understanding. Therefore, I decided not to use an explicit string-variable (like A$) , otherwise this is not only for string-variable. I want to say: It plays a role in which order the programs are written. I hope this example explain is better:
OPTION STRICT
VAR A = 1
GOSUB @PROGRAM1
VAR B$ = "Test"
GOSUB @PROGRAM2
GOSUB @PROGRAM3
END '(STOP dont make the same: the program take a break, and dont ended.)

@PROGRAM1
'This program should only know VAR A and VAR B$ (B$ is unknow  - why ever)
' VAR from later (C$, D) are unknow)

PRINT    'Insert here A, B$, C$ or D and look, what will be happend.
VAR C$ = "RUN"

RETURN

@PROGRAM2
'This knows VAR A, VAR B$ and VAR C$
' VAR D from later is unknow

PRINT    'Insert here A, B$, C$ or D and look, what will be happend.
VAR D = 7

RETURN

@PROGRAM3
'Knows all VAR to use

PRINT D

Return
If you only need a temporary VAR for calculation (or others) you can do this ways: 1. Make sure, that is defined it before you are using any @-SUB. Use this VAR only for this @SUB 2. Make sure, that the VAR don't needed in a later line of the Code. For example: set it after your Main-loop I Hope this explain shows better, what I tryed to say.

Thanks @12ME21 That i have tried to say, but i dont know what is the best way.

This isn't a bug. It is correct behavior. I happen to have my 3DS nearby so let's try some things.
A = "HELLO"
Result: type mismatch in 1:1. A is a number, A$ would be a string. You are trying to put a string in a number variable. To fix it we write.
A$ = "HELLO"
Result: OK Let's try another:
PRINT "<" + A$ + ">"
Result: <> I neither case did we declare the variable. SmileBasic allocated it for us. It did that when parsing your code and saw PRINT A, but when you got to VAR, it already had a variable called A. So you got a redefinition error. You should always declare variables before they are used. Option Strict requires you declare all variables so when turned on tells you that you have an undeclared variable on the print A line. But, consider this code.
SPELL$ = "MAGIC"
PRINT "SPELL: " + SPEIL$
Expected result: SPELL: MAGIC Actual result: SPELL: Why, because on line two I misspelled SPELL$ as SPEIL$. We ended up with two variables instead of one and SmileBasic gave us no indication of the type-o. Imagine trying to track down an error like that in thousands of lines of code. What happens if we add OPTION STRICT to the top of the code? An undefined variable error is generated and it tells you where the type-o is. This is extremely valuable once you write any significant amount of code. Now onto the ugly elephant in the room, GOSUB, stop using GOTO/GOSUB. If you need help using functions I am happy to help. I have gone over it at length, so I will spare you a repeat. You probably didn't know that GOSUB was a problem in your code but it is. When you use functions, any variables declared within the function are local to it and not visible globally. Using GOSUB has the sinister side effect of requiring you to use only global variables. Without intending to, one routine can accidentally stomp over top of another routines data. Consider two GOSUB subroutines the first has a for loop that uses variable I to go from 1 to 5 and calls the second via GOSUB each time through the loop. The second GOSUB uses I as a FOR loop counter going from 1 to 3 then returns. Result is that the first GOSUB loops forever due to the second editing its counter. Again miserable to debug for code of any scale. If they had local variables in a function it would have worked as intended.
GOSUB @A
END

@A
FOR I = 1 TO 5
 PRINT "@A: "; I
 GOSUB @B
NEXT I

RETURN

@B
FOR I = 1 TO 3
 PRINT "@B: "; I
NEXT I

RETURN
Versus:
A
END

DEF A
 VAR I
 FOR I = 1 TO 5
  PRINT "A: "; I
  B
 NEXT I

END

DEF B
 VAR I
 FOR I = 1 TO 3
  PRINT "B: "; I
 NEXT I

END
The second works as intended, the first doesn't. The second is also more reusable. You can copy and paste it into future code without bringing along a mess of global variables that may already be in use for something else. If you had used functions your second declaration of A would have been local. It would have run without crashing but not as intended. However the detail of it being in a function probably would have prompted you to move the variable definition to the global scope where it belonged, then it would have worked as intended. I think you want something like this:
OPTION STRICT
VAR A$

INI
MAIN

END

DEF MAIN
 WHILE (BUTTON() AND #X) == 0
  PRINT A$
 WEND

END

DEF INI
 A$ = "It runs ; let us sell it !"
END
The differences are minor but pay off. Lessons:
  • OPTION STRICT is awesome, use it
  • Declare all variables. Also declare them at the top of the code/function before use
  • String variables should end with a $
  • Gosub is terrible
  • Local variables reduce bugs and improve code reuse/portability

Variables aren't created when VAR is reached, they're all created before the program starts.
Oh yeah. Cool thanks.

Thanks @seggiepants for your update, and their explanation regarding the difference between subroutine and function. I know, that i forget the"$" after "A" in my postet Example. (I have posted my code in a simplified, modified form, and don't try it again. Sorry my mistake annoyed her.) By the way: For Testing i modified my bad example to a function and this do not work too. (no suprising) The Results: - Without OPTION STRICT it shows nothing. (because the Variable is now created by Smilebasic, but without a Value) - Whith OPTION STRICT it shows the Line of Error So, the result is therefore comparable. So its good to know, that @12ME21 wrote: Variables aren't created when VAR is reached, they're all created before the program starts. SB searches for variables from top to bottom, so VAR needs to be before (ABOVE) any other places where that variable is used. (This is the same way almost every programming language works) Generally, programs in SB are structured as: <variable declarations/initialization> <main loop> <function definitions> This shows, why (Sub-)program and functions don't generate the same errors, and why it is better to use function for template-VAR. Also why global-VAR have to defined on first Codeline (or earlier :-)