In all programming languages, very long scripts are usually broken up into manageable pieces. In Axbasic, this is done using subroutines and functions.
A subroutine is a group of statements that you can execute any time your want.
Subroutines start with a SUB statement and end with an END SUB statement.
SUB Hello
PRINT "Hello, world!"
END SUB
A script can have as many subroutines as you want, so each subroutine must have a unique name. In the example above, the subroutine's name is Hello.
The rules for subroutine names are the same as those for variable names.
Traditionally, subroutine names are typed Hello rather than hello or HELLO. This makes it easier for humans to distinguish between keywords like PRINT, variables like number and subroutine names like Hello. As usual, Axbasic has no such problems - all of the following lines are valid.
SUB Hello
SUB hello
SUB HELLO
sub hello
When you want to use a subroutine, we say that you call it. Unsurprisingly, this is done using a CALL statement.
CALL Hello
You can call the same subroutine as often as you want.
FOR a = 1 TO 10
CALL Hello
NEXT a
It doesn't really matter where you place your subroutines. Some programmers prefer to put all of their subroutines at the end of the script; others, at the beginning. Our advice is that you choose one or the other, and stick with it.
Here's a complete script. If you like, you can experiment with moving the subroutine to another position.
! Display a greeting ten times
FOR a = 1 TO 10
CALL Hello
NEXT a
END
SUB Hello
PRINT "Hello, world!"
END SUB
Of course, that script isn't very efficient; it would be much shorter if you just used a quick FOR loop, instead. However, there are many times when using a subroutine will make your life much simpler.
If you're writing a long script, it's usually better to focus on one problem at a time. In this situation, you can just write a CALL statement, and leave the actual subroutine code until later.
! Calculate pi to a trillion decimal places
! ...ten times!
FOR a = 1 to 10
CALL CalculatePi
NEXT a
END
SUB CalculatePi
! I'll finish this part later
END SUB
Often you'll need to supply a subroutine with some data.
LET number = 10
CALL Double_number (number)
The variable inside the brackets is called an argument. You can specify as many arguments as you want.
LET a = 2
LET b = 5
LET c = 9
CALL CalculateTotal (a, b, c)
The subroutine should be told to expect the same number of values. We call these parameters.
SUB CalculateTotal (val1, val2, val3)
If you don't specify exactly the same number of arguments and parameters, you'll see an error.
Let's put those ideas into a single complete script.
! Add up some numbers
LET a = 2
LET b = 5
LET c = 9
CALL AddNumbers (a, b, c)
END
SUB AddNumbers (val1, val2, val3)
LOCAL total
LET total = val1 + val2 + val3
PRINT total
END SUB
There are some things to note here.
val1 is a variable. When the subroutine is called, that variables is given a value (in this case, 2). The same happens to the variables val2 and val3, which are set to 5 and 9 respectively.
These variables are also local variables, which means that they are only available inside the subroutine. If you use this line anywhere else in the script, you'll get an error. (Try it and see for yourself.)
PRINT val1
You can create, or declare, a new local variable with a LOCAL statement.
LOCAL total
The variable total is available inside the subroutine, in every line after the LOCAL statement itself. But it's not available anywhere else.
If you want to declare lots of local variables, just separate them with commas.
LOCAL name$, address$, phone_number
The opposite of a local variable is a so-called global variable.
You can use global variables anywhere in the script, including inside the subroutine itself, but that's usually not the way to go. Ideally:
Break those guidelines only if you have a really good reason to do so, in which case you can declare a global variable with a GLOBAL statement.
GLOBAL name$, address$, phone_number
In many languages, programmers are forced to declare all of their variables as either global or local. This is optional in Axbasic, but it's still recommended if you're writing a long script - it's handy to have a complete list of global variables that you consult whenever you need to.
! Add up some numbers
GLOBAL a, b, c
LET a = 2
LET b = 5
LET c = 9
CALL Add_Numbers (a, b, c)
END
SUB Add_Numbers (val1, val2, val3)
LOCAL total
LET total = val1 + val2 + val3
PRINT total
END SUB
In Axbasic, variables are global unless you make them local, so in the script above the GLOBAL statement doesn't actually change the script's behaviour.
Global variables and local variables can share the same names. When you use a variable, Axbasic checks if there's a local variable with that name. If so, Axbasic uses it. Otherwise, Axbasic uses the global variable instead. You can test it for yourself by running this script.
! Demonstrate global and local variables
GLOBAL number
LET number = 10
PRINT "Before the subroutine, the variable is global"
PRINT number
CALL Print_Something
PRINT "After the subroutine, the variable is global"
PRINT number
END
SUB Print_Something
LOCAL number
LET number = 5
PRINT "During the subroutine, the variable is local"
PRINT number
END SUB
As well as subroutines, Axbasic has functions. A function is traditionally explained using a black box.
Axmud has a large number of built-in functions. Many of them are purely mathematical but others are really useful for the kinds of scripts you'll want to write.
For example, to convert a decimal number into an integer, you can use the Int () function.
LET number = 3.1415926356
LET result = Int (number)
PRINT result
You can cut out the middle man and PRINT the function's output directly.
LET number = 3.1415926356
PRINT Int (number)
If you want to know the current room's exits, you could use the Getexit$ () function.
PRINT Getexit$ (1)
PRINT Getexit$ (2)
PRINT Getexit$ (3)
Those statements might return values like these:
north
east
southeast
A string variable like name$ ends with a dollar character. The Getexit$ () function returns a string value, so it too ends with a dollar character. (This is a general rule. Functions always return a single value, which is either numeric or a string. If it's a string, the function's name ends with a dollar character.)
Function names are traditionally typed in the same way as subroutine names. All of the following lines are valid, but only the first one is recommended.
PRINT Int (number)
PRINT INT (number)
PRINT int (number)
A variable cannot have the same name as a built-in function. This applies to both string and numeric variables. Both of the following statements will generate an error:
LET Getexit$ = "north"
LET Int = 5
We'll be discussing Axbasic's built-in functions throughout the rest of this tutorial.
Axbasic's built-in functions are useful, but you can create as many of your own functions as you like.
To define a function, you use a DEF statement.
DEF Multiply (x) = x * 10
We can split this line into two parts.
DEF Multiply (x)
The name of the function is Multiply, and it has a single parameter, x.
x * 10
The function accepts a value, storing it temporarily in the local variable x. Then the function multiplies the value by 10 and returns the result.
Unlike a subroutine, a function must be defined before it can be used. You should probably define all of your functions near the beginning of your script.
To call the function, we just use its name.
! Multiply 5 by 10
DEF Multiply (x) = x * 10
LET number = 5
PRINT Multiply (number)
END
Functions can have more than one parameter, if necessary. For example, a function that multiplies three numbers might look like this:
DEF Multiply (x, y, z) = x * y * z
Your own functions can't have the same name as a keyword like PRINT or WHILE. Some keywords like ANGLE can only be used after another keyword, and it's ok to use those words as function names. There is, in fact, a built-in function called Angle ().
Your own functions can have the same name as a variable. However, once the function is defined, the variable's value will no longer be accessible. You can avoid a lot of problems by using distinct names for your variables and functions.
If you have a background in other programming languages, you might be asking yourself why subroutines don't have a return value too. The answer is that Axbasic subroutines always have a return value; it's just that we've been ignoring that value until now.
The default return value is 0. Consider, for example, the following subroutine.
SUB Hello
PRINT "Hello, world!"
END SUB
This subroutine returns the value 0 almost invisibly. If you want to return a different value - the number 5, perhaps - you can use a RETURN statement.
SUB Hello
PRINT "Hello, world!"
RETURN 5
END SUB
After CALLing a subroutine, the return value is discarded unless you store it somewhere.
LET result = CALL hello
PRINT result
Note that you can't combine these two lines, as you would do for a function. Compare the following script, which is correct:
LET result = CALL Hello
PRINT result
END
SUB Hello
PRINT "Hello, world!"
RETURN 5
END SUB
...with this script, which is not correct:
PRINT Hello
END
SUB Hello
PRINT "Hello, world!"
RETURN 5
END SUB
In the first line of the broken script, Axbasic assumes that Hello is a variable, not a subroutine. A numeric variable which hasn't been assigned a value has the value 0, and that's the value that is PRINTed. The subroutine is never actually called.
If you want to return a string, rather than a number, you must use a SUB STRING statement.
LET result$ = CALL Hello
PRINT result$
END
SUB STRING Hello
RETURN "Hello, world!"
END SUB
When you write long scripts, it might be useful to clarify (to human readers) which of your subroutines are returning strings, and which are returning numbers, in which case you can use both SUB STRING and SUB NUMERIC statements.
This is optional; if you don't use SUB STRING, Axbasic will just assume the return value is numeric. Both of the following lines have the same effect.
SUB Hello
SUB NUMERIC Hello
Inside a subroutine, you can use a RETURN statement at any time to return the result.
! Yet another guessing game
PRINT "Guess my name!"
LET result$ = CALL Check_Name
PRINT result$
END
SUB STRING Check_Name
INPUT string$
IF string$ = "bilbo" THEN
RETURN "correct"
ELSE
RETURN "not correct"
END IF
END SUB
Subroutines and functions can't share the same names. If you try, you'll see an error.
If you forget to add a RETURN statement to your subroutine, the subroutine will return a default value: the number 0 for a NUMERIC subroutine, or an empty string for a STRING subroutine.
If you want to return the default value before the end of the subroutine, you can use EXIT SUB. This example subroutine returns -1 for a negative number, +1 for a positive number, or the default value (0) if the argument is also 0.
SUB Classify (number)
IF number > 0 THEN
RETURN 1
ELSE IF number < 0 THEN
RETURN -1
ELSE
EXIT SUB
END IF
END SUB
The subroutine can contain any number of EXIT SUBs, but only ever one END SUB.