Pawn

From Bauman National Library
This page was last modified on 1 June 2016, at 17:10.
Pawn
Designed by ITB CompuPhase
First appeared 1998
Stable release 4.0.4733 / 22.06.2012
OS Cross-platform
License Apache 2.0 with the exception of linking
Filename extensions .pwn, .p, .amx
Website pawn
Influenced by
Small-C

Pawn (former name - Small) - simple, not typed 32-bit programming language with a syntax similar to C++. The compiler generates a P-code (or byte code) that runs on an abstract machine. Pawn source code files have .pwn extension. Compiled and ready to execute files obtain .amx extension.

History of the language

Introduction to Pawn language and concept of abstract machine has been described as the prospect of programmers, and published in Dr. Dobb's Journal in October 1999. The language was designed for novice programmers who allow many errors related to the release of the computer's memory. C language has been taken as the basis for Pawn. The goal of the developers was to simplify and change C language in a way that avoids the syntax of the language or to bypass the mistakes that allow inexperienced programmers. C language has been chosen because it had reached its peak in development and it was quite popular.

Language features

  • Pawn - it is a C-like scripting language.
  • You can connect files (include) in Pawn, so you can organize the orderly structure of the code.
  • Pawn - is a scripting language with a compiler that performs static checks and the abstract machine that (static) verifies the P-code and dynamic testing.
  • In order to port, Pawn was written in ANSI C
  • Pawn supports Unicode / UCS-4 and UTF-8, and code pages. The compiler can convert the source code entered in a special code page Unicode; it also supports source code files in UTF-8.
  • It was installed on the microcontroller Atmel ATmega128, Philips LPC2138 and LPC2106 (ARM7TDMI core with 32 KB RAM), as well as Texas Instrument MSP430F1611 (kernel MSP430 with 10 KB of RAM and 48 KB Flash ROM), because it allows you to run large scripts with a small memory.
  • Documentation of the source code can be done using the "comment documentation," Pawn compiler removes these comments, combines them with the information from the source code and writes in the XML-file, which you can view (and print) using a web browser.
  • Pawn supports states and automatons, including the variables of local conditions.

Data types

There are 4 data types in Pawn:

  • Integer
  • Numbers with floating point
  • Logic type, Boolean
  • Symbol

Arithmetic

Fundamental elements of most programs are calculations, decisions (conditional execution), iterations (loops) and variables to store input data, output data and intermediate results. The next program example illustrates many of these concepts. The program calculates the greatest common divisor of two values using an algorithm invented by Euclides.

/*
The greatest common divisor of two values,
using Euclides’ algorithm .
*/
main()
{
	print "Input two values\n"
	new a = getvalue()
	new b = getvalue()
	while (a != b)
		if (a > b)
			a = a - b
		else
			b = b - a
	printf "The greatest common divisor is %d\n", a
}

Function main now contains more than just a single "print" statement. When the body of a function contains more than one statement, these statements must be embodied in braces —the "{" and "}" characters. This groups the instructions to a single compound statement. The getvalue function (also common predefined function) reads in a value from the keyboard and returns the result.

Arrays and constants

Next to simple variables with a size of a single cell, pawn supports "array variables" that hold many cells/values. The following example program displays a series of prime numbers using the well known "sieve of Eratosthenes". The program also introduces another new concept: symbolic constants. Symbolic constants look like variables, but they cannot be changed.

/* Print all primes below 100, using the "Sieve of Eratosthenes" */
main()
{
	const max_primes = 100
	new series[max_primes] = { true, ... }
	for (new i = 2; i < max_primes; ++i)
		if (series[i])
		{
			printf "%d ", i
			/* filter all multiples of this "prime" from the list */
			for (new j = 2 * i; j < max_primes; j += i)
				series[j] = false
		}
}

Functions

Larger programs separate tasks and operations into functions. Using functions increases the modularity of programs and functions, when well written, are portable to other programs. The following example implements a function to calculate numbers from the Fibonacci series. The Fibonacci sequence was discovered by Leonardo “Fibonacci” of Pisa, an Italian mathematician of the 13th century—whose greatest achievement was popularizing for the Western world the Hindu-Arabic numerals. The goal of the sequence was to describe the growth of a population of (idealized) rabbits; and the sequence is 1, 1, 2, 3, 5, 8, 13, 21,. . . (every next value is the sum of its two predecessors).

/* Calculation of Fibonacci numbers by iteration */
main()
{
	print "Enter a value: "
	new v = getvalue()
	if (v > 0)
		printf "The value of Fibonacci number %d is %d\n",
		v, fibonacci(v)
	else
		printf "The Fibonacci number %d does not exist\n", v
}

fibonacci(n)
{
	assert n > 0
	new a = 0, b = 1
	for (new i = 2; i < n; i++)
	{
		new c = a + b
		a = b
		b = c
	}
	return a + b
}

The assert instruction at the top of the fibonacci function deserves explicit mention; it guards against "impossible" or invalid conditions. A negative Fibonacci number is invalid, and the assert statement flags it as a programmer’s error if this case ever occurs. Assertions should only flag programmer’s errors, never user input errors.

The implementation of a user-defined function is not much different than of function main. Function fibonacci shows two new concepts, though: it receives an input value through a parameter and it returns a value (it has a "result"). Function parameters are declared in the function header; the single parameter in this example is “n”. Inside the function, a parameter behaves as a local variable, but one whose value is passed from the outside at the call to the function. The return statement ends a function and sets the result of the function. It need not appear at the very end of the function; early exits are permitted.

Rational numbers

All calculations done up to this point involved only whole numbers —integer values. Pawn also has support for numbers that can hold fractional values: these are called "rational numbers". However, whether this support is enabled depends on the host application. Rational numbers can be implemented as either floating-point or fixed-point numbers. Floating-point arithmetic is commonly used for general-purpose and scientific calculations, while fixed- point arithmetic is more suitable for financial processing and applications where rounding errors should not come into play (or at least, they should be predictable). The pawn toolkit has both a floating-point and a fixed-point module, and the details (and trade-offs) for these modules in their respective documentation. The issue is, however, that a host application may implement either floating-point or fixed-point, or both or neither. The program below requires that at least either kind of rational number support is available; it will fail to run if the host application does not support rational numbers at all.

#include <rational>
main()
{
	new Rational: Celsius
	new Rational: Fahrenheit
	print "Celsius\t Fahrenheit\n"
	for (Celsius = 5; Celsius <= 25; Celsius++)
	{
		Fahrenheit = (Celsius * 1.8) + 32
		printf "%r \t %r\n", Celsius, Fahrenheit
	}
}

Strings

Pawn has no intrinsic “string” type; character strings are stored in arrays, with the convention that the array element behind the last valid character is zero. Working with strings is therefore equivalent with working with arrays. Staying on the subject of strings and arrays, below is a program that separates a string of text into individual words and counts them. It is a simple program that shows features of the pawn language.

/* word count: count words on a string that the user types */
	#include <string>
	main()
	{
		print "Please type a string: "
		new string[100]
		getstring string, sizeof string
		new count = 0
		new word[20]
		new index
		for ( ;; )
		{
			word = strtok(string, index)
			if (strlen(word) == 0)
				break
			count++
			printf "Word %d: ’%s’\n", count, word
		}
		printf "\nNumber of words: %d\n", count
	}	strtok(const string[], &amp;index)
	{
		new length = strlen(string)
		/* skip leading white space */
		while (index < length &amp;&amp; string[index] <= ’ ’)
			index++
		/* store the word letter for letter */
		new offset = index /* save start position of token */
		new result[20] /* string to store the word in */
		while (index < length &amp;&amp; string[index] > ’ ’ &amp;&amp; index - offset < sizeof result - 1)
		{
			result[index - offset] = string[index]
			index++
		}
		result[index - offset] = EOS /* zero-terminate the string */
		return result
	}

Arrays and symbolic subscripts (structured data)

In a typeless language, we might assign a different purpose to some array elements than to other elements in the same array. pawn supports symbolic substripts that allow to assign specific tag names or ranges to individual array elements. The example to illustrate symbolic subscripts and arrays is longer than previous pawn programs, and it also displays a few other features, such as global variables and named parameters.

/* Priority queue (for simple text strings) */
#include <string>
main()
{
	new msg[.text{40}, .priority]
	/* insert a few items (read from console input) */
	printf "Please insert a few messages and their priorities; " ...
	"end with an empty string\n"
	for ( ;; )
	{
		printf "Message: "
		getstring msg.text, .pack = true
		if (strlen(msg.text) == 0)
		break
		printf "Priority: "
		msg.priority = getvalue()
		if (!insert(msg))
		{
			printf "Queue is full, cannot insert more items\n"
			break
		}
	}

	/* now print the messages extracted from the queue */
	printf "\nContents of the queue:\n"
	while (extract(msg))
		printf "[%d] %s\n", msg.priority, msg.text
}

const queuesize = 10
new queue[queuesize][.text{40}, .priority]
new queueitems = 0

insert(const item[.text{40}, .priority])
{
	/* check if the queue can hold one more message */
	if (queueitems == queuesize)
		return false /* queue is full */
	/* find the position to insert it to */
	new pos = queueitems /* start at the bottom */
	while (pos > 0 &amp;&amp; item.priority > queue[pos-1].priority)
		--pos /* higher priority: move up a slot */
	/* make place for the item at the insertion spot */
	for (new i = queueitems; i > pos; --i)
		queue[i] = queue[i-1]
	/* add the message to the correct slot */
	queue[pos] = item
	queueitems++
	return true
}

extract(item[.text{40}, .priority])
{
	/* check whether the queue has one more message */
	if (queueitems == 0)
		return false /* queue is empty */
	/* copy the topmost item */
	item = queue[0]
	--queueitems
	/* move the queue one position up */
	for (new i = 0; i < queueitems; ++i)
		queue[i] = queue[i+1]
	return true
}

Function main starts with a declaration of array variable msg. The array has two fields, “.text” and “.priority”; the “.text” field is declared as a subarray holding 40 characters. The period is required for symbolic subscripts and there may be no space between the period and the name. When an array is declared with symbolic subscripts, it may only be indexed with these subscripts. It would be an error to say, for example, "msg[0]". On the other hand, since there can only be a single symbolic subscript between the brackets, the brackets become optional. That is, you can write "msg.priority" as a shorthand for "msg.[priority]". Further in main are two loops. The for loop reads strings and priority values from the console and inserts them in a queue. The while loop below that extracts element by element from the queue and prints the information on the screen. The point to note, is that the for loop stores both the string and the priority number (an integer) in the same variable msg; indeed, function main declares only a single variable. Function getstring stores the message text that you type starting at array msg.text while the priority value is stored (by an assignment a few lines lower) in msg.priority. The printf function in the while loop reads the string and the value from those positions as well.

Code examples

"Hello, World" looks like in C

#include <core>
main() 
{
    print("Hello World!");
    return 1; // Return 1
}

Example of creating and using an array of integer values

#include <core>
main() 
{
    new array[4]; // Initialize array with 4 elements
    array[0] = 43; // Change value of the elements with index 0
    array[1] = 10; // Change value of the elements with index 1
    array[2] = 799; // Change value of the elements with index 2
    array[3] = 1212; // Change value of the elements with index 3
    return 1; // Return 1
}

Example of the cycle and if-else statement

#include <core>
main() 
{
    for(new i = 0; i < 10; i++)
    {
        if(i != 9) printf("%d,", i);
        else print("nine");
    }
    return 1; // Return 1
}

References

  1. The Pawn language
  2. Language Guide
  3. The Pawn language - language and toolkit features

The article is written by Makarov D. V.