Pompous logo
www . DanYEY . co . uk
Puck, A Programming Language

Puck is a simple statically-typed memory-managed programming language which is syntactically similar to C-like languages. It can be interpreted, or compiled to a variety of platforms. It is still under development, so new features or target platforms may be added from time to time.

If you have the right permissions, you can access the development page.

Language Features

Programs And Modules

A Puck program is made up of one or more files with the extension .pk. Each program has a program block:


program MyProgramName
{
}

This block is executed, in the manner of a scripting language, but it may also contain functions and type definitions.

Programs can be split into multiple files by creating modules, which have the form:


module MyModuleName
{
}

Module blocks are not executed. They may contain only functions and type definitions. A module can be used by a program, or another module, by using an import statement.


import MyModuleName;

program MyProgramName
{
}

To locate a module in an import statement, the compiler will search the directory containing the program (or module), and all subdirectories. If it is successfully located, the compiler will cache this information in a .lik file to avoid repeating the search the next time the compiler is run. This text file can also be used to specify alternative versions of modules, possibly stored in parent directories.

Statements

Are best shown by example:


program MyProgramName
{
  if (someFlag)
  {
    x = y;
  }
  else
  {
    y = x;
  }
  
  while (someCondition)
  {
    x++;
  }
  
  repeat
  {
    y -= 3;
  } until (y < 81)
  
  for (i = 0; i < 100; i++)
    y = (x + y);
  
  foreach (element in myArray)
  {
    y += element;
  }
}

Blocks of only a single line can omit the braces, as you would expect. Notice that there is no semicolon following the repeat until statement.

Scalar Types

The above example wouldn't run because none of the variables it uses have been declared. Puck has four scalar types: int, bool, float and string. It may seem odd to have strings as a scalar type (since they have variable length), but this is to reflect how strings are often used. The other types are similar to their counterparts in other languages, though it should be noted that defining floats using the exponential format (eg. 3E2) is not supported.

Scalar variables are declared by giving a type and then a name, and optionally an initial value. Scope rules apply as in java or C. Scalar variables may be declared as constants by prefixing the declaration with const.


int x = 0;
const float PI = 3.142;
bool b = true;
string cakes = "cakes";

Each type has an associated default value, which is used for arrays and structs. ints: 0, floats: 0.0, bools: false, and strings: "" (the empty string).

Each type can be converted to the others using a cast.


x = int("3");

The int and float operators are +, -, *, /, % (remainder after division) and ^ (exponentiation). The bool operators are !, || and &&.

The syntax for manipulating strings is of some interest. Single characters in the string can be accessed using square brackets and an index. If the index is negative, it refers to an index counting backwards from the end of the string.


string temp;
temp = cakes[0]; //the string c

temp = cakes[1]; //the string a

temp = cakes[-1]; //the string s


This syntax can also be used to perform substring operations. When a colon and a second index are used, the access returns the string starting at the first index, and ending at the second index (including the characters at both indices). When a comma and a length are used, the access returns the string from the first index, of the length specified.


temp = cakes[0:1]; //the string ca

temp = cakes[1,2]; //the string ak

temp = cakes[-2:-1]; //the string es


This syntax can also be used on the left-hand side of the assignment, to replace sections of the string.


cakes[0] = "b"; //bakes

cakes[1:2] = "it"; //bites


Strings are concatenated using +, and can be duplicated using *.


temp = (cakes+" ") * 3; //bites bites bites 


\n can be used to include a line break (LF) in a quoted string, \r for a carriage return (CR), \" for double quotes, and any single byte character can be entered using \xNN where NN denotes two hexadecimal digits.

Functions

Function definitions and calls are similar to other languages. Functions may only be defined in the program/module block, but may be called from anywhere. It is not necessary to define a function before you call it (in the file).


int addOne(int x)
{
  return x+1;
}

int y;
y = addOne(3);

Where the function does not return a value, or takes no parameters, these are simply omitted:


mySubroutine
{
  //...

}

mySubroutine();

Arrays

Puck currently has support for arrays of variable length, of multiple dimensions containing scalar or struct elements. They are declared by giving the type, the keyword array, an optional length (or lengths) and a name.


int array numbers;
string array(5) words;
int array(3,3,0) mtrx;

Where a length is given, the array is created with the specified length, and each element is filled with the default value for its type.


temp = words[1]; //the empty string


Where several lengths are given, this denotes an array of multiple dimensions. The lengths of every dimension must be provided, but the last dimension may have a length of zero.

Elements can be appended onto the end of arrays, increasing their size. Empty square brackets are used to achieve this.


numbers[] = 6; //numbers now has 1 element at index 0

numbers[] = 7; //numbers now has 2 elements


//words now has 6 elements, all empty except the last

words[] = "word";

//the first row of the first column now contains 1 element

mtrx[0][0][] = 1;

Arrays can be assigned to each other, which causes the whole structure to be copied (you cannot have two "pointers" to the same array).


otherWords = words; //copy all the words

numbers = mtrx[0][0]; //copy part of mtrx


Parts of a multi-dimensional array can be copied in this manner, bearing in mind that the number of dimensions must match.

Structs

A struct is a compound type which may contain multiple scalar variables, but not arrays or other structs. This is to allow the compiler the freedom to place them on the stack. Once a struct type is defined, variables of that type may be declared. Struct definitions may only appear in program or module blocks.


struct MyStructure
{
  int x;
  int y;
  string val;
}

MyStructure st;

Once a struct variable is declared, its elements are filled with the default values for their type. This naturally extends to arrays of structs. Struct members can be accessed by using the . operator.


st.x = 9;
st.y = st.x;

Comments

Comments are as in java and C++. // denotes a comment running to the end of the line. /* ... */ is used for block comments.

Pseudo-variables

What APIs are there? None, as such. There are a number of general-purpose modules written by kind people, and a number of "pseudo-variables" which are a temporary (interpreter only) hack to allow Puck programs communicate with the outside world. The most prominent of these is STDOUT which causes whatever is assigned to it to be written to the standard output. However, it must be declared like any normal variable. Similarly, there is STDIN, and P_0 to P_9 allow access to the first 10 command line arguments.


string STDOUT;
STDOUT = "hello world"; //guess what it does


When compiling for the PIC (16F84) platform, variables RA0 to RA4 and RB0 to RB4 are used to allow the program to access the chip's I/O pins.

Running Puck Programs

Since the compiler does not yet support all the features of the language, the interpreter is the usual way to run the program. The interpreter is invoked as follows (replace ; with : in unix):

java -classpath .;puck.jar PuckInterpreter [program-file]

The compiler can produce assembly or machine code for the following platforms. The table shows the features supported by each platform.

Platform Integers Booleans Function calls Strings Arrays Structs Outputs binaries
JVM Y Y Y Y N N Y
PIC Y Y Y N N N N
ARM Y Y Y N N N N
Dan! 71 Y Y N N N N Y

The compiler is invoked as follows (replace ; with : in unix):

java -classpath .;puck.jar PuckC [-p [platform]] [program-file]

The relevant assemblers are required to generate runnable programs when using the PIC and ARM platforms, but the compiler produces class files when compiling for the JVM. These can be run directly by typing java MyProgramName, and can be debugged using standard programs such as jdb.

Download Puck

The compiler and interpreter are written in java. You will need to install a virtual machine (probably 1.4 and above) to run them.

puck.jar (version 0.5)