First, PocketC is a case sensitive language, meaning that typing word is not the same as type Word.
There are three elements to a PocketC applet: the title line, the global variables, and the functions.
The title is by far the easiest part of the PocketC language. The first line of your applet is two slashes followed by the name of your applet. Example:
// My Applet
This is also known as a comment. Anytime the compiler finds '//
' in
your applet, it will ignore the rest of the line that it is on. This allows you to place
explanatory text in your applet. There is one other way to put a comment in your applet,
by surrounding the text with '/* */
'. This method allows you to spread a
comment out over several lines. Example:
/* This is a multi-line comment. All the text between the asterisks is ignored */
Multi-line comments are not nestable. In other words:
/* comment1 /* comment2 */ a=b+c; */
is NOT valid.
Variables are the things that are used to store values in a program. There are four types of variables in PocketC:
Type | Name | Example |
---|---|---|
integer (32-bit, signed) | int |
1, 2, 5, -789, 452349 |
floating point (32-bit) | float |
-1.2, 3.141592, 5.7e-4 |
characters (8-bit, signed) | char |
'a', 'b', '#' |
strings | string |
"Bob" "Katie" "Hello" |
pointers | pointer |
discussed later |
Note: String constants may only be 255 characters. To strore a longer
string in a variable, use addition: str = "long1..." +
"long2...";
Variables are declared like this:
variable-type name[,name...];
Here are a few examples:
int myInteger, row, column; string name; float pi; char c, last, first; pointer ptr;
It is also possible to have an array of values. An array is a list of values
that are stored in one variable. Arrays are declared like normal variables except that the
variable name is followed by '[size]
' where size is the number
of item that the variable can hold. A declaration might look like this:
int values[10]; string names[7];
Of course, arrays and normal variables can be declared together:
int row, values[10], column; string name, colors[8];
We'll discuss variables a little more later.
Functions are the most important part of a program because they contain the actual
instructions that make a program useful. All functions have a name and a parameter list
(which may be empty) and are declared like the:
func-name(
[param-type param-name,...
]) { statements
}
Statements are discussed later, but for now, here are a few examples:
area(int width, int height) { return width * height; } square(float x) { return x * x; } five() { return 5; }
There is one special function name which all programs must have: main
. The
main
function is the function which is called first in your program. When the
main
function exits, the program terminates. The main
function
must be declared with no paramters:
// My Applet main() { puts("Hello World"); }
Functions can also have local variables, which are variables that can only be accessed within the function that declares them. Global variables, however, can be accessed from anywhere. Local variables are declared in the same way that global variables are except that they immediately follow the opening brace of a function:
// My Applet main() { string localString; localString = "Hello World"; puts(localString); }
Before we go any further, we need to talk a little bit about expressions.
An expression is any number of constants, variables, and function calls connected by operators and parentheses.
A constant is any value that is directly entered into the program, such as: 5
5.4 'a' "String"
A value stored in a variable can be accessed by just typing its name: myInteger
name
However, if that variable is an array, each value must be accessed individually by index.
The valid indices for a given array are 0 to n-1 where n is the number of
values in the array. So an array declared:
string names[4]
can be accessed like so:
names[0] = "first name"; names[1] = "second name"; names[2] = "third name"; names[3] = "fourth name";
A function call consists of the name of a function, followed by an open paren, the parameter list, and a closing paren:
area(5, 7); square(8.9); clear(); text(30, 55, "Game Over");
A function can only be called after it is defined. If you want to call a function before defining it, you can use a function prototype. A prototype of a function is a global line (not within another function) which states the name and parameters of a function followed by a semicolon:
area(int x, int y); square(float); // the use of variable names is optional in a declaration
These three basic elements can be combined with operators:
5 + 7 - area(12, 34); square(5) * pi; "Hello, " + "World";
Of course, function calls can have expressions in them as well:
area(6+3, 8*9); area(8 * square(4), 7);
Variable assignment is actually just another form of expression. Assignment is done in
one of two ways--for a normal variable:
name = expression
and for an array:
name[index-expression] = expression
Here are a few examples:
int myInt, numbers[3]; string myString; ... myInt = 8; myString = "Animaniacs"; numbers[0] = myInt + 5; numbers[2] = numbers[0] * 8;
However, since PocketC is loosely typed, any type of value can be assigned to any type of variable and the value will be automatically converted:
myString = 95; // The value of myString is now "95" numbers[1] = "78"; // The value of numbers[1] is now 78; numbers["2"] = "2"; // Another neat trick. numbers[2] is now 2
Now, what are all the operators that can be used in an expression, and what is their associativity? Good question.
The following table is in order of precedence, lowest first.
Operator |
Assoc |
Description |
---|---|---|
= |
right | assigns the value of the expression on the right to the variable on the left. Evaluates to the expression on the right. |
|| |
left | logical 'or', evaluates to 0 if false, 1 if true |
&& |
left | logical 'and' |
| |
left | bitwise 'or' |
^ |
left | bitwise 'xor' |
& |
left | bitwise 'and' |
== != < <= > >= |
left | relational operators. == (equal), != (not equal), <= (less than or equal), >= (greater than or equal). These evaluate to 1 if the expression is true, 0 otherwise |
<< >> | left | bitwise shift operators. The operands must be int or char. |
+ - |
left | addition, subtraction (subtraction cannot be used with a string argument) |
* / % |
left | multiplication, division, modulus (cannot be used with strings, nor can modulus be used with floats) |
- ! ++ - - ~ * [] () & |
left | - (negation), ! (logical 'not'), ++ (increment), -- (decrement), ~ (bitwise neg), [] (array subscript), () (function pointer dereference), & (address of ) (Of these, only the logical 'not' can be used with strings) |
Notes: No shortcut logic is performed on the operands of || and &&
The compound assignment operators (+=, *=, etc.) are not supported.
The comma and conditional operators (?:) are not supported.
The ++ and -- operators are special in that they must be placed before or after a variable and modify the value of the variable. The ++ increments the value of a variable by one, while the -- decrements by one. The caveat is that if the ++/-- is placed in front of the variable, the expression evaluates to the value of the variable after it is incremented/decremented. If it is placed after the variable, the expression evaluates to the variable's previous value. Example:
int myInt; ... myInt = 8; puts(++myInt); // Prints "9" to the output form myInt = 8; puts(myInt++); // Prints "8" to the output form, but myInt is now 9
Just like in assignments statements, automatic conversion takes place in every part of an expression. If the two arguments to an operator are of different types, one of arguments will be promoted to the less strict type. The promotion order is char to int to float to string. So in the expression:
"Result is: " + 5;
The constant 5 is first promoted to a string, and the two strings are concatenated. This may have some undesirable side effects. For example, if you want to write an expression and result to the output form, you might do something like this:
puts("5 + 7 = " + 5 + 7); // Prints "5 + 7 = 57"
This probably wasn't the desired outcome. Instead, you would want the expression evaluated first, then concatenated to the string. The parentheses can be used to accomplish this:
puts("5 + 7 = " + (5 + 7)); // Prints "5 + 7 = 12"
One problem remains. Suppose you want to find the floating point value of a fraction of two integer.
puts("7 / 5 = " + (7 / 5)); // Prints "7 / 5 = 1"
This output is because both arguments are integers, so the result is also an integer. To solve this, we can cast one of them to a float:
puts("7 / 5 = " + ((float)7 / 5)); // Prints " 7 / 5 = 1.4"
This forces the integer 7 to a floating point number before dividing it by 5.
Statements are the individual parts that make up the body of a function. The following are the available statements:
Statement | Description |
---|---|
return; |
Returns immediately from the current function (with a default return value of integer 0) |
return expr; |
Returns immediately from the current function, returning the value of the expression expr |
if (expr) stmt |
Evaluates the expression expr, if its result is true (non-zero or non-empty string), the statement stmt is executed, otherwise stmt is skipped, and execution continues |
if (expr) stmtA |
Evaluates the expression expr, if its result is true (non-zero or non-empty string), the statement stmtA is executed, otherwise stmtB is executed |
while (expr) stmt |
The expression expr is evaluated. If it is true (non-zero or non-empty string), stmt is executed. The loop then begin again, evaluating expr and executing stmt until expr is no longer true. This means that stmt will never execute if expr is initially false |
do stmt |
The same as while except that the statement stmt is executed
before expr is evaluated. This guarantees that stmt will execute at least
once |
for (init;cond;iter) |
The initializer expression init is first evaluated. The condition expression cond
is evaluated. If it is true, stmt is executed and the iterator expression iter
is evaluated continuing the loop, otherwise the the for loop ends. Note: init
is evaluated only once. |
break; |
Immediately exits from the directly enclosing while/do/for loop. |
continue; |
Immediately restarts the directly enclosing while/do/for loop. In a for
loop, the iter expression is evaluated, followed by the cond expression and
possibly the stmt |
{ statements } |
A brace followed by a list of statements, followed by another brace is considered a single statement |
expression; |
An expression followed by a semicolon is also considered to be a statement |
return
Let's visit a previous example function to see how return works.
five() { return 5; }
Since the return value of the function five is always 5, we can use the function any place we would normal put the constant 5.
puts("Five is " + five()); // Prints "Five is 5"
Also, since return
causes the function to exit immediately, we could do
this:
five() { return 5; puts("This won't print"); }
and we would have the same effect.
if
lessThan5(int x) { if (x < 5) puts("Less than five"); puts("Hello"); }
If this function is called with a number less than 5, "Less than five" will be printed followed by the word "Hello", otherwise, only the word "Hello" is printed.
if ... then
lessThan5(int x) { if (x < 5) puts("Less than five"); else puts("Greater than or equal to five"); }
If this function is called with a number less than 5, "Less than five" is printed, otherwise "Greater than or equal to five" is printed.
while
count() { int x; x = 5; while (x > 0) { puts(x); x = x - 1; } }
This bit of code will print the numbers from 5 to 1 counting backwards. Notice that
braces were placed around the two lines of code in the while
loop to make
them act as a single statement.
do ... while
count() { int x; x = 6; do { x = x - 1; // could also be x-- puts(x); } while (x > 0); }
This bit of code (similar to the previous example) will print the numbers from 5 to 0
counting backwards. The zero is printed in this case because the expression x < 0
is not evaluated until after the loop
for
output() { string list[4]; int index; list[0] = "Zero"; list[1] = "One"; list[2] = "Two"; list[3] = "Three"; for (index = 0 ; index < 4 ; index++) puts(list[index]); }
This example will print out "ZeroOneTwoThree". When we disect it we see that
the array list is initialized first. We then reach the for
loop.
First, the initializer is evaluated, setting index to 0. Next, the condition is evaluated index
< 4
, which is true, so the body of the loop executes, printing "Zero".
The iterator expression is then evaluated, increasing index by one. This continues
until index is equal to 4, at which point the loop exits without executing the body
again.
break
count() { int x; x = 5; while (x > 0) { if (x == 1) break; puts(x); x = x - 1; } }
In this slightly more complex piece of code, the counting goes on as it normally would,
printing out "5432". However, when x reaches 1, break
is
executed, breaking out of the while
loop early, before the 1 gets printed.
continue
count() { int x; x = 6; while (x > 1) { x--; // Do the subtraction first if (x == 3) continue; puts(x); } }
In this clearly contrived example, the output is "5421". When x
reaches 3, the continue
is executed, passing execution to the beginning of
the loop, skipping over the puts.
Note: Pointers are an advanced topic, which should be dealt with after the user is familiar with all the other programming concepts.
All variables are stored at some address in memory. A pointer is a variable which refers to another variable by containing that variable's address.
There are two primary operators which are used with pointers, * and &. The * operator dereferences the pointer. A dereferenced pointer acts just like the data to which it points. The & operator returns the address of a given variable. To illustrate:
pointer p, q; int i; main() { i = 5; p = &i; // Assign the address of 'i' to the pointer 'p' // now, typing '*p' is the same as typing 'i' puts(*p); // Print the value of 'i' *p = 7; // Assign 7 to 'i' q = p; // Assign the value of 'p', which is the address of 'i', to 'q' // now, typing '*q' is the also the same as typing 'i' // Things not to do p = 8; // BAD! Don't assign a constant value to a pointer *i = 9; // BAD! Don't try to dereference a non-pointer }
A pointer can also be used to take the address of a function (but NOT a built in function!). Unlike variables, however, the & operator is NOT used. Calling a function through a pointer is a little tricky. First, the code looks ugly. Second, no error checking can be done on the parameters, so type conversions are not done and the number of arguments is not confirmed. For example:
func(int x) { return 5*x; } main() { int result; pointer ptr; ptr = func; // Take the address of a function result = (*ptr)(5); // call the function (ugly) // Things not to do result = (*ptr)("5"); // this won't work, since the string // isn't converted to an integer result = (*ptr)(5,7); // this will compile, but will result // in stack corruption because the // wrong number of arguments are used }
Pointers and arrays
Pointers and arrays are fairly similar. Pointers can use the [] operator, and an array
variable (when not used with []) results in the address of the first element. For example:
int array[5]; pointer p; main() { p = array; // Assign the address of the first element of // 'array' to 'p' *p = 7; // Assign 7 to array[0] p[1] = 8; // Assign 8 to array[1] }
This enables the pointers to arrays to be passed as function parameters. This also allows the user to implement their own version of two-dimensional arrays. By creating an array of pointers, each of which is a pointer to an array (or part of one), a two-dimensional array can be simulated.
int array[100]; pointer twod[10]; // after init(), this can be treated // like at 10x10 matrix init() { int i; for (i=0;i<10;i++) twod[i]=array + i*10; // Pointer arithmetic } main() { int x, y; init(); for (x=0;x<10;x++) for (y=0;y<10;y++) twod[x][y]=x * y; // Sets array[x*10 + y] = x*y }
Pointer arithmetic
Pointer values can used in a limited number of expression. You can add and subtract from a
pointer (and, thus, can use the increment and decrement operators as well). When you add 1
to a pointer, the pointer points to the next value in memory. Similarly, when you subtract
1 from a pointer, the pointer points to the previous value in memory. Caution should be
used when using pointer arithmetic, because dereferencing an invalid memory location will
cause an error in the applet.
Using the include
keyword, it becomes possible to write programs whose
source code is longer than te current 4K Memo Pad limit, or to create memos of frequently
used code. The contents of the included file are functionally inserted into the line
containing the include
keyword.
Note: An included memo can begin with '/$' instead of '//' to hide it from the
compile form.
Example
/$ MyFunctions times5(int x) { return x*5; }
Another memo:
// My Program include "MyFunctions" main() { int y; y = times5(7); puts(y); // Prints 35 }
The compiler sees this as:
// My Program times5(int x) { return x*5; } main() { int y; y = times5(7); puts(y); // Prints 35 }
To use the functions defined by a native library, you must first tell the compiler to
load the library by using the library
keyword. The functions are then used as
if they were normal functions. (DO NOT use this keyword with MathLib. MathLib functions
are available as built-in functions.)
Example:
// My Applet // PocketCLib defines times5(int) library "PocketCLib" main() { int x; x = times5(7); }
For information of creating native libraries, see native.html.
There are two ways to add special characters to a string. The first is by appending them by number, such as:
str = "Here is a neat little square: " + (char)149;
The other method is through using escape sequences. The following escape sequences are supported:
Escape sequence | \\ | \' | \" | \n | \t | \x |
---|---|---|---|---|---|---|
Interpretation | \ | ' | " | newline | tab | character specified by the following two hex digits. Example: '\x95' is the block character (decimal 149) |
So, to create a string that contains a quote:
str = "She said \"I'm sorry,\" but it was too late..."; puts(str); // Prints: She said "I'm sorry," but it was too late...
Just as in C, PocketC contains a preprocessor, which allow a programmer to define macros and conditionally compile a section of code based on the presence of a given macro.
#define macro macro data
A macro is an identifier which, when read by the compiler, is replaced by the macro's data. A macro's data can be any number of tokens (including zero). The macro data is terminated by the next newline. For example:
#define calc 5 * (x + 7) main() { int x, y; x = 9; y = calc; puts("y = " + y); }
The compiler sees this as:
main() { int x, y; x = 9; y = 5 * (x + 7); puts("y = " + y); }
#ifdef macro
If the macro has been previously defined (even if it is defined to be nothing), the section of code between it and the matching #endif is compiled. Otherwise, the compiler ignores the section of code.
#ifndef macro
If the macro has NOT been previously defined, the section of code between it and the matching #endif is compiled. Otherwise, the compiler ignores the section of code.
#endif
Marks the end of a section of code preceeded by an #ifdef or #ifndef.
#else
Placed in the middle of a #ifdef or #ifndef block, the code between the #else and the #endif is compiled if and only if the previous block was not.
Example:
#define DEBUG main() { #ifdef DEBUG puts("In debugging mode"); #else puts("In normal mode"); #endif }
That should just about cover it. Next, look over the Built-in Functions to see which may be useful for you.