What is a variable, exactly? Let’s start with the difference between a constant value and a variable value. The number 6 is a constant: we can’t change it. We can’t tell the program: “In this particular circumstance, let’s treat this 6 like it was actually 21.” Consider a situation, however, where we may want to record a particular value at one point in order to refer to it later. In other words, we may want to use a value that we won’t know at the time we write the code that will be using it.
Here’s a piece of code that, as we’ll see shortly, prints a single line of output with a number in the middle:
print "The temperature is "; number temp; " degrees."
That statement may print
The temperature is 10 degrees.
or
The temperature is –9 degrees.
or any other similar variation depending on what the variable temp
happens to be equal to at the time.[1]
Hugo supports two kinds of variables: global and local. Either type simply holds an integer value, so a variable can hold a simple value, an object number, a dictionary address, a routine address, or any other standard Hugo data type through an assignment such as:
a = 1
nextobj = parent(obj)
temp_word = "the"
Global variables are visible throughout the program. They must be defined similarly to properties and attributes as
global <global variable name>[ = <initial value>]
Local variables, on the other hand, are recognized only within the routine in which they are defined. They are defined using
local <local variable name>[ = <initial value>]
Global variables must of course have a unique name, different from that of any other data object; local variables, on the other hand, may share the names of local variables in other routines.
In either case, global or local, the default initial value is 0 if no other value is given. For example,
global time_of_day = 1100
is equal to 1100 when the program is run, and is visible at any point in the program, by any object or routine. On the other hand, the variables
local a, max = 100, t
are visible only within the block of code where they are defined, and are initialized to 0, 100, and 0, respectively, each time that section of code (be it a routine, property routine, event, etc.) is run.
The compiler defines a set of engine globals: global variables that are referenced directly by the engine, but which may otherwise be treated like any other global variables. These are:
|
direct object of an action |
|
indirect object |
|
self-referential object |
|
total number of words in command |
|
the player object |
|
the player, or character obj. (for scripts) |
|
specified by the command |
|
location of the |
|
if not false (0), run |
|
for input; default is |
|
the total number of objects |
|
after certain operations |
The object
, xobject
, and verbroutine
globals are set up by the engine depending on what command is entered by the player.
The self
global is undefined except when an object is being referenced (as in a property routine or event).
In that case, it is set to the number of that object.
The player
variable holds the number of the object that the player is controlling; the endflag
variable must be 0 unless something has occurred to end the game; and the prompt
variable represents the dictionary word appearing at the start of an input line (which most programs set to >
by convention).
The objects
variable can be set by the program, but to no useful effect.
The engine will reset it to the “real” value whenever referenced.
(All object numbers range from 0 to the value of objects
.)
The system_status
variable may be read (after a resource operation or a system
call; see the relevant sections for an explanation of these functions) to check for an error value.
See the section on “Resources” for possible return values.
Important
|
Setting |
Constants are simply labels that represent a non-modifiable value.
constant FIRST_NAME "John"
constant LAST_NAME "Smith"
(Note the lack of an =
sign between, for example, FIRST_NAME
and John
.)
print LAST_NAME; ", "; FIRST_NAME
results in:
Constants can, like any other Hugo data type, be integers, dictionary entries, object numbers, etc.
It is not absolutely necessary that a constant be given a definite value if the constant is to be used as some sort of flag or marker, etc. Therefore,
constant THIS_RESULT
constant THAT_RESULT
will have unique values from each other, as well as from any other constant defined without a specific value.
Sometimes it may be useful to enumerate a series of constants in sequence. Instead of defining them all individually, it is possible to use:
enumerate start = 1
{
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY
}
giving:
MONDAY = 1, TUESDAY = 2, WEDNESDAY = 3,
THURSDAY = 4, FRIDAY = 5
The start value is optional. If omitted, it is 0. Also, it is possible to change the current value at any point (therefore also affecting all following values).
enumerate
{
A, B, C = 5, D, E
}
giving:
A = 0, B = 1, C = 5, D = 6, E = 7.
Finally, it is possible to alter the step value of the enumeration using the step
keyword followed by +n
, -n
, *n
, or /n
, where n
is a constant integer value.
To start with 1 and count by multiples of two:
enumerate step *2
{
A = 1, B, C, D
}
giving:
A = 1, B = 2, C = 4, D = 8.
Enumeration of global variables is also possible, using the globals
specifier, as in:
enumerate globals { <global1>, <global2>,... }
Otherwise the specifier constants
(as opposed to globals
) is implied as the default.
Text can be printed — that is, output to the screen during running of a Hugo program — using two different methods.
The first is the basic print
command, the simplest form of which is
print "<string>"
where <string>
consists of a series of alphanumeric characters and punctuation.
The backslash character (\
) is handled specially.
It modifies how the character following it in a string is treated.[2]
\"
|
inserts quotation marks |
\\
|
insert a literal backslash character |
\_
|
insert a forced space, overriding left-justification for the rest of the string |
\n
|
insert a forced newline |
As usual, a single \
at the end of a line signals that the line continues with the following line.
Examples:
print "\"Hello!\""
“Hello!”
print "Print a...\n...newline"
Print a…
…newline
print "One\\two\\three"
One\two\three
print " Left-justified"
print "\_ Not left-justified"
Left-justified
Not left-justified
print "This is a \
single line."
This is a single line.
Note
|
Although
will produce the same result, since the line break occurs within quotation marks. |
After each of the above print commands, a newline is printed.
To avoid this, append a semicolon (;
) to the end of the print
statement.
print "This is a ";
print "single line."
This is a single line.
Print
statements may also contain data types, or a combination of data types and strings.
The command
print "The "; object.name; " is closed."
will print the word located at the dictionary address specified by object.name
, so that if object.name
points to the word box
, the resulting output would be:
The box is closed.
To capitalize the first letter of the specified word, use the capital
modifier.
print "The "; capital object.name; " is closed."
The Box is closed.
To print the data type as a value instead of referencing the dictionary, use the number
modifier.
For example, if the variable time
holds the value 5,
print "There are "; number time; " seconds remaining."
There are 5 seconds remaining.
If number
were not used, the engine would try to find a word at the dictionary address 5, and the result will likely be garbage.
Mainly for debugging purposes, the modifier hex
prints the data type as a hexadecimal number instead of a decimal one.
If the variable val
equals 127,
print number val; " is "; hex val; " in hexadecimal."
127 is 7F in hexadecimal.
The second way to print text is from the text bank, from which — if memory is in short supply — sections are loaded from disk only when they are needed by the program. This method is provided so that lengthy blocks of text — such as description and narration — do not take up valuable space if memory is limited. The command consists simply of a quoted string without any preceding statement.
"This string would be written to disk."
This string would be written to disk.
or
"So would this one ";
"and this one."
So would this one and this one.
Notice that a semicolon at the end of the statement still overrides the newline. The in-string formatting combinations are still usable with these print statements, but since each statement is a single line, data types and other modifiers may not be compounded. Because of that,
"\"Hello,\" he said."
will write
“Hello,” he said.
to the .HEX file text bank, but
"There are "; number time_left; " seconds remaining."
is illegal.
The color of text may be changed using the color
command (also valid with the U.K. spelling colour
).
The format is
color <foreground>[, <background>[, <input color>]]
where the background color is not necessary. If no background color is specified, the current one is assumed). The input color is also not necessary — this refers to the color of player input and, if not given, is the same as the foreground color.
The standard color set with corresponding values and constant labels (defined in hugolib.h) is:
COLOR | VALUE | LABEL |
---|---|---|
Black |
0 |
|
Blue |
1 |
|
Green |
2 |
|
Cyan |
3 |
|
Red |
4 |
|
Magenta |
5 |
|
Brown |
6 |
|
White |
7 |
|
Dark gray |
8 |
|
Light blue |
9 |
|
Light green |
10 |
|
Light cyan |
11 |
|
Light red |
12 |
|
Light magenta |
13 |
|
Yellow |
14 |
|
Bright white |
15 |
|
Default foreground |
16 |
|
Default background |
17 |
|
Default statusline (fore) |
18 |
|
Default statusline (back) |
19 |
|
Match foreground |
20 |
|
Tip
|
Since the labels are defined in hugolib.h, when using the library, it is never necessary to refer to a color by its numerical value. |
It is expected that, regardless of the system, any color will print visibly on any other color.
Video technology and shortcomings of the visible light spectrum conspire to foil this plan, however, it is suggested for practicality that white (and less frequently bright while) be used for most text-printing.
Blue and black are fairly standard background colors for light-colored (such as white) text — this is a common combination for default text (as is dark text, such as black, on a white background).
A game author can use the DEF_FOREGROUND
, DEF_BACKGROUND
, DEF_SL_FOREGROUND
, and DEF_SL_BACKGROUND
colors (as is done in sample.hug and is the default in shell.hug) since this uses the colors supplied by the Hugo Engine, allowing the user to change colors to his or her liking if the port supports that capability.
Magenta printing on a cyan background is accomplished by
color MAGENTA, CYAN
or
color 5, 3 ! if not using HUGOLIB.H
A current line can be filled — with blank spaces in the current color — to a specified column (essentially a tab stop) using the print to…
structure as follows:
print "Time:"; to 40; "Date:"
where the value following to
does not exceed the maximum line length in the engine global linelength
.
The resulting output will be something like:
Time: Date:
Text can be specifically located using the locate
command via
locate <column>, <row>
where
locate 1, 1
places text output at the top left corner of the current text window.
Neither <column>
nor <row>
may exceed the current window boundaries — the engine will automatically constrain them as necessary.
As listed above, the following are valid printing sequences that may be embedded in printed strings:
\"
|
quotation marks |
\\
|
a literal backslash character |
\_
|
a forced space, overriding left-justification for the rest of the string |
\n
|
a newline |
The next set of formatting sequences control the appearance of printed text by turning on and off boldface, italic, proportional, and underlined printing. Not all computers and operating systems are able to provide all types of printed output; however, the engine can be relied upon to properly process any formatting — i.e., proportionally printed text will still look fine even on a system that has only a fixed-width font, such as a Unix text terminal or DOS output (although, of course, it won’t be proportionally spaced).
\B
|
boldface on |
\b
|
boldface off |
\I
|
italics on |
\i
|
italics off |
\P
|
proportional printing on |
\p
|
proportional printing off |
\U
|
underlining on |
\u
|
underlining off |
A statement like the following:
"A \Bbold string with some \Iitalics\i and \Uunderline\b thrown in.\u"
will result in output like:
A bold string with some italics and underline thrown in.
Print style can also be changed using the Font
routine in hugolib.h, so that in
Font(<font change code>)
the <font change code>
can be one or more of:
|
|
|
|
|
|
and can subsequently be used alone or in combination such as:
Font(BOLD_ON | ITALICS_ON | PROP_OFF)
It’s preferable to rely on the Font
function and the various font constants instead of embedding multiple font-change formatting sequences because if for no other reason than it being clearer to understand when reading the source code.
Special characters can also be printed via formatting sequences. Note that these characters are contained in the Latin-1 character set; if a particular system is incapable of displaying it, it will display the normal-ASCII equivalent.
Warning
|
The following examples, appearing in parentheses, may not display properly on all computers and printers. |
|
accent grave |
followed by a letter |
||
|
accent acute |
followed by a letter |
||
|
tilde |
followed by a letter |
||
|
circumflex |
followed by a letter |
||
|
umlaut |
followed by a letter |
||
|
cedilla |
followed by c or C |
||
\< or \> |
Spanish quotation marks (« ») |
|||
|
upside-down exclamation point (¡) |
|||
|
upside-down question mark (¿) |
|||
|
ae ligature (æ) |
|||
|
AE ligature (Æ) |
|||
|
cents symbol (¢) |
|||
|
British pound (£) |
|||
|
Japanese Yen (¥) |
|||
|
any ASCII or Latin-1 character where xxx represents the three-digit ASCII number (or Latin-1 code) of the character to be printed, e.g.
|
Note
|
It is possible to embed Latin-1 characters directly into printed text in source code using a text editor that allows it — but ensure that the non-ASCII characters are indeed Latin-1. Using non-Latin-1 fonts (such as Mac-encoded fonts or other encodings) will result in the wrong character(s) being printed on various platforms. Also note that platforms which cannot display Latin-1 characters (including some Unix-based terminal displays, DOS windows, etc.) may not have proper Latin-1-to-ASCII translation in order to decode Latin-1 characters embedded directly in printed text. For this reason, or if you’re not positive whether your font encoding is Latin-1, stick to using the special-character sequences described above, which are guaranteed to work properly across platforms. |
Hugo allows use of all standard mathematical operators:
*
|
multiplication |
/
|
integer division |
which take precedence[3] over:
+
|
addition |
-
|
subtraction |
Comparisons are also valid as operators, returning Boolean true or false (1 or 0) so that
2 + (n = 1)
5 - (n > 1)
evaluate respectively to 3 and 5 if n
is 1, and 2 and 4 if n
is 2 or greater.
Valid relational operators are
==
|
equal to |
~=
|
not equal to |
<
|
less than |
>
|
greater than |
<=
|
less than or equal to |
>=
|
greater than or equal to |
Logical operators (and
, or
, and not
) are also allowed.
(x and y) or (a and b)
(j + 5) and not ObjectisLight(k)
Using and
results in true (1) if both values are non-zero.
Using or
results in true if either is non-zero; not
results in true only if the following value is zero.
1 and 1 = 1 1 and 0 = 0 5 and 3 = 1 0 and 9 = 0 0 and 169 and 1 = 0 1 and 12 and 1233 = 1 1 or 1 = 1 35 or 0 = 1 0 or 0 = 0 not 0 = 1 not 1 = 0 not 8 = 0 not (8 and 0) = 1 1 and 7 or (14 and not 0) = 1 (0 or not 1) and 3 = 0
Additionally, bitwise operators are provided:
|
(Bitwise AND) |
|
|
|
(Bitwise OR) |
|
|
|
(Bitwise NOT/inverse) |
Note
|
As mentioned previously, a detailed explanation of bitwise operations is a little beyond the scope of this manual; programmers may occasionally use the |
Any Hugo data type can appear in an expression, including routines, attribute tests, properties, constants, and variables. Standard mathematical rules for order of significance in evaluating an expression apply, so that parenthetical sub-expressions are evaluated first, followed by multiplication and division, followed by addition and subtraction.
Some sample combinations are:
10 + object.size ! integer constant and property
object is openable + 1 ! attribute test and constant
FindLight(location) + a ! return value and variable
1 and object is light ! constant, logical test, and attribute
Expressions can be evaluated and assigned to either a variable, a property, or an array element.
<variable> = <expression> <object>.<property> [#<element>] = <expression> <array>[<element>] = <expression>
Something like
number_of_items = number_of_items + 1
if number_of_items > 10
{
print "Too many items!"
}
can be coded more simply as
if ++number_of_items > 10
{
print "Too many items!"
}
The ++
operator increases the following variable by one before returning the value of the variable.
Similarly, --
can precede a variable to decrease the value by one before returning it.
Since these operators act before the value is returned, they are called “pre-increment” and “pre-decrement”.
If ++
or --
comes after a variable, the value of the variable is returned and then the value is increased or decreased, respectively.
In this usage, the operators are called “post-increment” and “post-decrement”.
For example,
while ++i < 5 ! pre-increment
{
print number i; " ";
}
will output:
But
while i++ < 5 ! post-increment
{
print number i; " ";
}
will output:
Since in the second example, the variable is increased before getting the value, while in the second example, it is increased after checking it.
It is also possible to use the operators +=
, -=
, *=
, /=
, &=
, and |=
.
These can also be used to modify a variable at the same time its value is being checked.
All of these, however, operate before the value in question is returned.
x = 5
y = 10
print "x = "; number (x*=y); ", y = "; number y
Result:
When the compiler is processing any of the above lines, the efficient operator takes precedence over a normal (i.e., single-character) operator. For example,
x = y + ++z
is actually compiled as
x = y++ + z
since the ++
is parsed first.
To properly code this line with a pre-increment on the z
variable instead of a post-increment on y
, use parentheses to order the various operators:
x = y + (++z)
Prior to this point, little has been said about arrays. Arrays are sets of values that share a common name, and where the elements are referenced by number. Arrays are defined by
array <arrayname> [<array size>]
where <array size>
must be a numerical constant.
An array definition reserves a block of memory of <array size>
,[4] so that, for example,
array test_array[10]
reserves ten possible storage elements for the array.
Keep in mind that <array size>
determines the size of the array, not the maximum element number.
Elements begin counting at 0, so that test_array
, with 10 elements, has members numbered from 0 to 9.
Trying to access test_array[10]
or higher will return a zero value (and, if running in the debugger, cause a debugger warning).
Trying to assign it by mistake will have no effect.
To prevent such out-of-bounds array reading/writing, an array’s length may be read via:
array[]
where no element number is specified. Using the above example,
print number test_array[]
would result in 10
.
Array elements can be assigned more than one at a time, as in
<arrayname> = <element1>, <element2>, ...
where <element1>
and <element2>
can be expressions or single values.
Elements need not be all of the same type, either, so that
test_array[0] = (10+5), "Hello!", FindLight(location)
is perfectly legal (although perhaps not perfectly useful). More common is a usage like
names[0] = "Ned", "Sue", "Bob", "Maria"
or
test_array[2] = 5, 4, 3, 2, 1
The array can then be accessed by
print names[0]; " and "; names[3]
or
b = test_array[3] + test_array[5]
which would set the variable b
to 4 + 2, or 6.
Because array space is statically allocated by the compiler, all arrays must be declared at the global level. Local arrays are illegal, as are entire arrays passed as arguments.[5] However, single elements of arrays are valid arguments.
It is, however, possible to pass an array address as an argument, and the routine can then access the elements of the array using the array
modifier.
For example, if items
is an array containing:
items[0] = "apples"
items[1] = "oranges"
items[2] = "socks"
The following:
routine Test(v)
{
print array v[2]
}
can be called using
Test(items)
to produce the output
even though v
is an argument (i.e., local variable), and technically not an array.
The line
print array v[2]
tells the engine to treat v
as an array address, so that we can follow it with [<element number>]
.
Arrays also allow a Hugo programmer to implement what are known as string arrays, which are textual strings, somewhat similar but not identical to dictionary entries. Most significantly, since they are arrays, string arrays may be altered at runtime by a program (unlike dictionary entries, which are hard-coded into the program’s dictionary). A string array is an array containing a series of character values, terminated by a zero value.
If the array apple_array
holds the string array apple
, the actual elements of apple_array
look like:
apple_array[0] = 'a'
apple_array[1] = 'p'
apple_array[2] = 'p'
apple_array[3] = 'l'
apple_array[4] = 'e'
apple_array[5] = 0
Hugo provides a handy way to store a dictionary entry in an array as a series of characters using the string
built-in function:
string(<array address>, <dict. entry>, <max. length>)
For example,
string(a, word[1], 10)
will store up to 10 characters from word[1]
into the array a
.
Note
|
It is expected in the preceding example that |
It’s not necessary to look at the return value from string
, but it can be useful, since it lets us know how many characters were written to the string.
For example,
x = string(a, "microscopic", 10)
will store up to 10 characters of “microscopic” in the array a
, and return the length of the stored string to the variable x
.[6]
The Hugo Library defines the functions StringCopy
, StringEqual
, StringLength
, and StringPrint
, which are extremely useful when dealing with string arrays.
StringCopy
copies one string array to another array.
StringCopy(<new array>, <old array>[, <length>])
For example,
StringCopy(a, b)
copies the contents of b
to a
, while
StringCopy(a, b, 5)
copies only up to 5 characters of b
to a
.
x = StringEqual(<string1>, <string2>) x = StringCompare(<string1>, <string2>)
StringEqual
returns true only if the two specified string arrays are identical.
StringCompare
returns 1 if <string1>
is lexically greater than <string2>
, -1 if <string1>
is lexically less than <string2>
, and 0 if the two strings are identical.
StringLength
returns the length of a string array, as in:
len = StringLength(a)
and StringPrint
prints a string array (or part of it).
StringPrint(<array address>[, <start>, <end>])
For example, if the array a
contains “presto”,
StringPrint(a)
will print “presto”, but
StringPrint(a, 1, 4)
will print “res”.
Note
|
The |
An interesting side-effect of being able to pass array addresses as arguments is that it is possible to “cheat” the address, so that, for example,
StringCopy(a, b+2)
will copy b
to a
, beginning with the third letter of b
(since the first letter of b
is b[0]
).
It should also be kept in mind that string arrays and dictionary entries are two entirely separate animals, and that comparing them directly using StringCompare
is not possible.
That is, while a dictionary entry is a simple value representing an address, a string array is a series of values each representing a character in the string.
The library provides the following to overcome this:
StringDictCompare(<array>, <dict. entry>)
which returns the same values (1, -1, 0) as StringCompare
, depending on whether the string array is lexically greater than, less than, or equal to the dictionary entry.
There is also a complement to string
: the dict
built-in function, that dynamically creates a new dictionary entry at runtime.
Its syntax is:
x = dict(<array>, <maxlen>) x = dict(parse$, <maxlen>)
where the contents of <array>
or parse$
are written into the dictionary, to a maximum of <maxlen>
characters, and the address of the new word is returned.
However, since this requires extending the actual length of the dictionary table in the game file, it is necessary to provide for this during compilation. Inserting
$MAXDICTEXTEND=<number>
at the start of the source file will write a buffer of <number>
empty bytes at the end of the dictionary.
(MAXDICTEXTEND
is, by default, 0.)
Dynamic dictionary extension is used primarily in situations where the player may be able to, for example, name an object, then refer to that object by the new name, or whenever the game needs to introduce new words into the dictionary that are not known at compile-time.
However, a guideline for programmers is that there should be a limit to how many new words the program or player can cause to be created, so that the total length of the new entries never exceeds <number>
, keeping in mind that the length of an entry is the number of characters plus one (the byte representing the actual length).
That is, the word test
requires 5 bytes.)
Program flow can be controlled using a variety of constructions, each of which is built around an expression that evaluates to false (zero) or non-false (non-zero).
The most basic of these is the if
statement.
if <expression> {...conditional code block...}
The enclosing braces are not necessary if the code block is a single line.
Note also that the conditional block may begin (and even end) on the same line as the if
statement provided that braces are used.
if <expression> ...single line... if <expression> {...conditional code block...}
If braces are not used for a single line, the compiler automatically inserts them, although special care must be taken when constructing a block of code nesting several single-line conditionals. While
if <expression1> if <expression2> ...conditional code block...
may be properly interpreted, other constructions (particularly those involving some of the more complex program-flow concepts we’re about to get into) may not be. Therefore, it’s always best to be as clear as possible about your intent, more along the lines of:
if <expression1> { if <expression2> ...conditional code block... }
More elaborate uses of if
involve the use of elseif
and else
.
if <expression1> ...first conditional code block... elseif <expression2> ...second conditional code block... elseif <expression3> ...third conditional code block... ... else ...default code block...
In this case, the engine evaluates each expression until it finds one that is true, and then executes it.
Control then passes to the next non-if
/elseif
/else
statement following the conditional construction.
If no true expression is found, the default code block is executed.
If, for example, <expression1>
evaluates to a non-false value, then none of the following expressions are tested.
Of course, all three (if
, elseif
, and else
) need not be used every time, and simple if
-elseif
and if
-else
combinations are perfectly valid.
In certain cases, the if
statement may not lend itself perfectly to clarity, and the select
-case
construction may be more appropriate.
The general form is:
select <var> case <value1>[, <value2>, ...] ...first conditional code block... case <value3>[, <value4>, ...] ...second conditional code block... ... case else ...default code block...[7]
In this case, the evaluation is essentially
if <var> = <value1> [or <var> = <value2> ...]
There is no limit on the number of values (separated by commas) that can appear on a line following case
.[8]
The same rules for bracing multiple-line code blocks apply as with if
(as well as for every other type of conditional block).
Basic loops may be coded using while
and do
-while
.
while <expression> ...conditional code block...
do ..conditional code block... while <expression>
Each of these executes the conditional code block as long as <expression>
holds true.
It is assumed that the code block somehow alters <expression>
so that at some point it will become false; otherwise the loop will execute endlessly.
while x <= 10
{
x = x + 1
print "x is "; number x
}
do
{
x = x + 1
print "x is "; number x
}
while x <= 10
The only difference between the two is that if <expression>
is false at the outset, the while
code block will never run.
The do
-while
code block will run at least once even if <expression>
is false at the outset.
It is also important to recognize — with while
or do
-while
loops — that the expression is tested each time the loop executes.
The most important side effect of this is that if you’re doing something in the expression that has some effect — whether printing something, calling a function, or modifying some other value — this will happen every time the expression is evaluated.
The most complex loop construction uses the for
statement:
for (<assignment>; <expression>; <modifier>) ...conditional code block...
For example:
for (i=1; i<=15; i=i+1)
print "i is equal to: "; number i
First, the engine executes the assignment setting i = 1
.
Next, it checks to see if the expression holds true (if i
is less than or equal to 15).
If it does, it executes the print
statement and the modifying assignment that increments i
.
It continues the loop until the expression tests false.
Not all elements of the for
construction are necessary.
For example, the assignment may be omitted, as in
for (; i<=15; i=i+1)
and the engine will simply use the existing value of i
, whatever it was before this point.
With
for (i=1;;i=i+1)
the loop will execute endlessly, unless some other means of exit is provided.
The modifying expression does not have to be an arithmetic expression as shown above.
It may be a routine that modifies a global variable, for example, which is then tested by the for
loop.
A second form of a for
loop is:
for <var> in <object> ...conditional code block...
which loops through all the children of <object>
(if any), setting the variable <var>
to the object number of each child in sequence, so that
for i in mysuitcase
print i.name
will print the names of each object in the mysuitcase
object.
Hugo also supports jump
commands and labels.
A label is simply a user-specified token preceded by a colon (:
) at the beginning of a line.
The label name must be a unique token in the program.[9]
print "We're about to make a jump."
jump NextLine
print "This will never get printed."
:NextLine
print "But this will."
One final concept is important in program flow, and that is break
.
At any point during a loop, it may be necessary to exit immediately (and probably prematurely).
The break
statement passes control to the statement immediately following the current loop.
In the example:
do { while <expression2> { ... if <expression3> break ... } ... } while <expression1>
the break
causes the immediately running while <expression2>
loop to terminate, even if <expression2>
is true.
However, the external do
-while <expression1>
loop continues to run.
It has been previously stated that lines ending in and
or or
are continued onto the next line in the case of long conditional expressions.
A second useful provision is the ability to use a comma to separate options within a conditional expression.
As a result,
if word[1] = "one", "two", "three"
while object is open, not locked
if box not in livingroom, garage
if a ~= 1, 2, 3
are interpreted as:
if word[1]="one" or word[1]="two" or word[1]="three"
while object is open and object is not locked
if box not in livingroom and box not in garage
if a ~= 1 and a ~= 1 and a ~= 3
respectively.
Tip
|
Note that with an |
! Sample to print various typefaces/colors:
#include "hugolib.h"
routine main
{
print "Text may be printed in \Bboldface\b,
\Iitalics\i, or \Uunderlined\u typefaces."
color RED ! or color 4
print "\nGet ready. ";
color YELLOW ! color 14
print "Get set. ";
color GREEN ! color 2
print "Go!"
}
The output will be:
Text may be printed in boldface, italics, or underlined typefaces.
Get ready. Get set. Go!
with “boldface”, “italics” and “underlined” printed in their respective typefaces. “Get ready”, “Get set”, and “Go!” will all appear on the same line in three different colors.
Warning
|
Note that not all computers will be able to print all typefaces. The basic Unix and MS-DOS ports, for example, use color changes instead of actual typeface changes, and do not support proportional printing. |
#include "hugolib.h"
routine main
{
StringTests
return
}
array s1[32]
array s2[10]
array s3[10]
routine StringTests
{
local a, len
a = "This is a sample string."
len = string(s1, a, 31)
string(s2, "Apple", 9)
string(s3, "Tomato", 9)
print "a = \""; a; "\""
print "(Dictionary address: "; number a; ")"
print "s1 contains \""; StringPrint(s1); "\""
print "(Array address: "; number s1;
print ", length = "; number len; ")"
print "s2 is \""; StringPrint(s2);
print "\", s3 is \""; StringPrint(s3); "\""
"\nStringCompare(s1, s2) = ";
print number StringCompare(s1, s2)
"StringCompare(s1, s3) = ";
print number StringCompare(s1, s3)
}
The output will be:
a = “This is a sample string.”
(Dictionary address: 1040)
s1 contains “This is a sample string.”
(Array address: 1643, length = 24)
s2 is “Apple”, s3 is “Tomato”
StringCompare(s1, s2) = 1
StringCompare(s1, s3) = -1
As is evident above, a dictionary entry does not need to be a single word; any piece of text which is referred to by the program as a value gets entered into the dictionary table.
The argument 31 in the first call to the string
function allows up to 31 characters from a
to be copied to s1
, but since the length of a
is only 24 characters, only 25 values (including the terminating 0) get copied, and the string length of s1
is returned in len
.
Since “A(pple)” is lexically less than “T(his…)”, comparing the two returns -1.
As “To(mato)” is lexically greater than “Th(is…)”, StringCompare
returns 1.
\n
are treated as two characters in a string.
parse$
and serial$
may be used in place of the dictionary entry address; see [sec_7-2] for a description.
case
unless explicitly told not to do so; such is not the case with Hugo.
case
line that runs on forever and ever, eventually you’ll reach the point where, for buffer reasons, the compiler isn’t able to compile it, and it will complain with an appropriate error.
jump
keyword is more or less equivalent to goto
in other languages. The reason it’s different in Hugo is mainly to encourage the use of the proper alternatives (i.e., for
and while
or do
-while
loops) in keeping with proper programming practices. And, in the end, less jump
s and labels make for far more readable code.