Pawn Style Guide

This document is a short guide on the generally accepted naming conventions and other aspects of Pawn source code to aid easier communication of intent and streamline debugging and sharing of code.

See also:

Terminology

Statement

A statement is a piece of code that imperatively tells the host program to do something. A statement is a valid piece of code that yields some result.

a = b + c;

This is a statement composed of a variable being assigned the result of an [#Expression].

SetPlayerColor(playerid, 0xFF4700FF);

This is a statement telling the program to call a function with some arguments.

x + 8

This is not a statement as the result is not used anywhere, this is just an [#Expression].

Compound Statement

A compound statement is a collection of statements surrounded by braces.

{
new message[] = "hi!";
print(message);
}

This is a compound statement composed of two statements.

if (a == b)
{
print("hello!");
}

This is a compound statement with an if condition, this is usually referred to as an "if statement".

return Function1(), Function2(), Function3();

This is not a compound statement, it's a chain of statements separated by commas. This form of chaining statements is considered bad practice.

Expression

An expression is a piece of syntax that yields a value, it's not a valid statement unless the yielded value is used in some way.

Expressions are often composed to form statements.

a + b

This is a simple addition expression that takes two values and applies the add operator to them.

Guidelines

Braces

Allman braces are preferred:

function()
{
//
}

However, if you can't shake the muscle memory, K&R braces are also valid Pawn:

function() {
//
}

Switches

Switches must use two indent levels, one for the switch block and another for each case statement or compound statement.

switch (variable)
{
case 0:
return 0;
case 1:
return 1;
case 2:
return 2;
default:
return -1;
}
switch (variable)
{
case 0:
{
// code...
return 0;
}
case 1:
{
// code...
return 1;
}
case 2:
{
// code...
return 2;
}
default:
{
// code...
return -1;
}
}

Compound Statements (Blocks)

Blocks must always use braces, even if only a single line of code exists within a block. This applies to all levels including functions.

func()
{
singleLineExpr();
}
func()
{
if ()
{
singleLineExpr();
}
}
func()
{
if ()
{
singleLineExpr();
}
else if ()
{
//
}
else
{
//
}
}

Naming

Functions

Functions must be named with PascalCase.

Global Variables

Global variables must always use g prefixed PascalCase, so gVariableName and should generally always be static.

Constant globals must use SCREAMING_SNAKE_CASE.

Local Variables

Local variables must always use camelCase and must never use single letter names, apart from:

  • i, j, k, etc in for loops
  • x, y, z, etc in mathematical contexts

Enumerators

Enumerators, if named, must be prefixed with E_ and use SCREAMING_SNAKE_CASE. The one exception is when avoiding a Index tag mismatch warning, in which case an e_ prefix may be used. SCREAMING_SNAKE_CASE must still be used even with the lower case e_ prefix.

Enumerator fields must also be SCREAMING_SNAKE_CASE and use the enumerator name as a prefix.

static enum E_PLAYER {
E_PLAYER_CASH,
Float:E_PLAYER_HEALTH,
}

To avoid index tag warnings in some cases:

static enum e_PLAYER {
e_PLAYER_CASH,
Float:e_PLAYER_HEALTH,
}

Enumerators must always be declared static unless used outside the module.

Macros and Pre-Processor Definitions

Macros must always use SCREAMING_SNAKE_CASE regardless of their usage.

Pre-Processor definitions (constant definitions) must also use SCREAMING_SNAKE_CASE.

This helps differentiate between variables and constants as well as functions and macros.

It's generally advised to avoid inventing new syntactical elements in order to prevent confusion among newcomers as to which words are part of the language and which words are from libraries.

However, some older libraries do this and cannot change because of backwards compatibility.

Documentation

Always document exported functions with a simple line comment in the format // FunctionName does X, Y and Z and returns A where the first word is the name of the function itself followed by a brief description of what it does. No need to waste time describing each individual parameter. For example:

// LoadPlayerAccount is called to initiate the account load process. This
// function will trigger HTTP calls to get player data, it will display dialogs
// to the player and eventually, once the process has completed, the event
// `OnPlayerLogin` is emitted on success. On failure, the player is kicked.
stock Error:LoadPlayerAccount(playerid)
{
// code...
}

Each package should have a README and, if necessary, each module should have a comment on the very first line describing what that module provides.