PocketC Language

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

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.

The Global Variables

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];

You can also give default values to the variables:

int nine = 9, eight = 8, zero;
string days[7] = { "Sun", "Mon", "Tues" };

In the case of arrays, the initials values must be in braces and separated by commas. If the number of values in the initializer list is less than the length of the array, then the uninitialized elements have default values. For the days array above, the last 4 members of days are the empty string ("").

We'll discuss variables a little more later.

The Functions

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);
}

Note: If you are creating large arrays, it is best to make them global variables instead of local variable is possible. 

Before we go any further, we need to talk a little bit about expressions.

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);

Assignment

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.

Operators

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 ), and @[] (string character accessor) Of these, only the logical 'not' and @[] 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.

String Character Accessor

To get or set an individual character within a string variable, use stringVariable@[index]. The index of the first character is 0. You will produce a runtime error if you attempt to access a character that is past the end of the string. Example:

string str = "bob";
...
puts(str@[1]); // Prints the second letter of str
str@[1] = 'X'; // changes str from "bob" to "bXb"

Note: the string character accessor cannot be used with pointers, nor can the address of the resulting string be take. In other words, the following expressions are not valid: &str@[i], *pstr@[i], (*pstr)@[i]

Increment / Decrement

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

Automatic Conversion and Casting

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

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
else stmtB
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
while (expr)
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)
stmt
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 or switch statement..
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
switch (expr)
{ stmts }
Evaluates the expression expr. If stmts contains a case statement with a matching value, the code immediately following the case statement is executed until either a break statement or the end of the switch statement is reached. If no matching case statement is found and a default statement exists in the switch, the code immediately following the default statement is executed until either a break statement or the end of the switch statement is reached. If no case statement matches the expr, and no default statement is present, everything in the switch statement is skipped, and execution continues after the final closing brace. expr must not evaluate to a float value.
case constant:

A marker within a switch statement. The constant must be either a char (case 'a':), an int (case 3:), or a string (case "apple":) If the constant matches the expr in the switch statement, then the code immediately following this marker is run, until a break statement or the end of the switch statement is reached.

default: An optional marker within a switch statement. If none of the cases in the switch statement match the switch expr, the code immediately following this marker is executed, until a break statement or the end of the switch statement is reached.
{ 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


Statement Examples

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.

switch, case, default

which_number(int x) {
   switch (x) {
      case 1:
         puts("x == 1\n");
         break;
      case 2:
      case 3:
         puts("x == 2 or x == 3\n");
         break;
      case 8:
         puts("x == 8\n");
      case 10:
         puts("x == 8 or x == 10\n");
         break;
      default:
         puts("x is not 1,2,3,8, or 10\n");
   }
}

The which_number function is passed a value, and will print out a fact or two about it. If the value is 1, case 1 is executed and breaks to the end of the switch. If the value is 2 or 3, the code following case 3: is executed. If the value is 8, both "x=8" and "x=8 or x=10" is printed because there is no break before case 10:, this is called fall-through. If none of the cases match the passed in value, the code following default: is executed.

Pointers

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.

Include

Using the include keyword, it becomes possible to write programs whose source code is longer than the 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.
Notes: An included memo can begin with '/$' instead of '//' to hide it from the compile form. You may also include a doc file. However, PocketC searches for a matching memo before searching for a matching doc file.
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
}
Note: include can only be used at the top level (i.e. you cannot use include within a function)

Using Native Libraries

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.

Special characters

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...

Preprocessor

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.

#undef

Undefines a previously defined macro. If the macro was never defined, this does nothing.

Example:

#define DEBUG

main() {
#ifdef DEBUG
  puts("In debugging mode");
#else
  puts("In normal mode");
#endif
}

The compiler predefines these macros:

#define __PKTC__ 1
#define __PKTC_PALM__ 1

That should just about cover it. Next, look over the Built-in Functions to see which may be useful for you.