Remilia's Pascal Style Guide
Overall
- Remilia is allowed to break these rules when she sees fit.
- Assume a max column width of 120 characters.
- Target x86-64-v1 and 32-bit ARM as a minimum.
- If you’re inlining a function, put the
inlinedeclaration in both the interface and implementation sections. - DO NOT use anything from Lazarus. Stick to just the RTL, FCL, and SDMPas. If something is missing, add it to SDMPas.
- DO NOT make your code dependent on a particular IDE, even slightly. The code should be easy to work on in Emacs, JED, Lazarus, Notepad, or even ed.
- Three spaces for indentation. Never use tabs.
- Use
self.inside of methods when referring to a field or another method. Exceptions for this are extremely rare. - Use horizontal rules going from columns 0 to 80 (inclusive) to separate major pieces of code. Use horizontal rules going from the correct indentation level to column 69 (inclusive) if you need to visually separate blocks inside a block.
- Main files get named
.pas. Included files (and you should definitely be splitting stuff up into included files then using{$include ...}) should get named.inc. Nothing else should be in your source tree (no.lpror anything like that). - Use
(*====*)style horizontal rules. - Use
//for comments. - Use
FreeAndNilwhen possible. Use this setup when handling
THashMapclasses:if someTable.size > 0 then begin iter := someTable.Iterator; assert(iter <> nil); // We just checked size, this must be true try repeat // ...code... until not iter.MoveNext; finally if iter <> nil then FreeAndNil(iter); end; end;- Use
HasFlagfrom SDMPas to check flags unless you need super speed and can’t afford the extremely small overhead of an inline function. Example:if HasFlag(someVar, $20)instead ofif (someVar and $20) <> 0. - Don’t do
procedure Foo(var1, var2 : UInt8);. Instead doprocedure Foo(var1 : UInt8; var2 : UInt8);. - Sort your
usesin this order: RTL units, FCL units, other external units, SDMPas units, local units. Put these categories on their own line, and sort their entries alphabetical. The first category goes on the same line as theuseskeyword. - Be liberal with your usage of
assert. Using it is objectively a Good Thing™. No, I will not debate you on this. You can always disable them later for a “release” build with a compiler flag. - Use Rake for building stuff, not fpcmake or Makefiles or anything like that.
Compiler Declarations
- Use lowerPascalCase when possible.
- Always use ObjFPC mode:
{$mode objfpc}. - Always use AnsiStrings:
{$H+}or{$longStrings on}. - Always use the UTF-8 codepage:
{$codePage utf8}. - Always enable range checking by default:
{$rangeChecks on}. - Always enable C-style operators:
{$coperators on}. - Always enable scoped enumerations:
{$scopedEnums on}. - Always disable writable constants:
{$writeableConst off}. - When using floating point in the unit code, a minimum floating point precision
of 64-bits unless you are 100% sure 32-bits is fine:
{$minFpConstPrec 64}. - Enable inlining of code (and optionally disable it when doing a debug build):
{$inline on}. - Always use Intel-style assembly if you’re adding assembly code into your unit
for x86/x86-64:
{$asmMode intel}(and be sure to wrap it in an{$ifdef...to check for the CPU architecture).
Names
- If a class field is private, add
myto its name, e.g.myTable : TUInt32Array; - PascalCase for most symbol names, but lowerPascalCase for local variables, function/procedure/method parameters, and class fields that start with “my” (e.g. myTable).
- Prefix
Tonto type names, e.g.TEmulator. - Prefix
Conto constants, e.g.CTableSize. - Prefix
Eonto exception types, and have them end withError, e.g.EEmulatorError. - For the love of the gods, try not to use variable names like
iorjorx. Use descriptive names likeidxorcoefforbraBandSize. Occasional exceptions when porting complex code from another language is fine.
Operators
- ALWAYS put spaces around your operators. So don’t do this:
foo:=bar+2;. Do this instead:foo := bar + 2;Same with colons, so dofunction Foo : UInt16;instead offunction Foo:UInt16; - Don’t be afraid to use excessive parentheses when doing math stuff.
- Prefer C-style operators (e.g.
foo += 69) when possible rather than code such asfoo := foo + 69;.
Types
- Use
Stringwhenever possible rather than one of the other string types. - Prefer
Int32,UInt8, etc. overIntegerorLongIntorWordand so on. - Use
Float80instead ofExtended,Float64instead ofDouble, andFloat32instead ofSingle. These are all in SDMPas’sSDMTypesunit. Don’t useReal. - Prefer
SizeIntandSizeUIntfor any sort of array or pointer index. - When possible, and if it won’t cause a large impact on performance, prefer to use advanced records to store data. It saves on some mental effort when it comes to memory handling, and makes for cleaner code.
Numbers
- Hex values always use upper case.
- Have at least two digits in all hex values, e.g.
$0Finstead of$F. - Don’t leave off the trailing 0 on floats, so
1.0instead of1.(is this even possible in Pascal?).
If, While, Case, For, etc.
ALWAYS use the full
then begin,do begin,end else begin, and so on. This prevents things such as dangling if statements. DO NOT do something like this for any block type, even if it’s possible:if foo = 69 then bar := 42; else bar := 36;
The SINGLE exception to this is for case statements that only have a single line of code, or get automatically wrapped in a block because the one statement is a block (see below).
If it’s a long block (almost a full screen or more), then add a comment at the
end;to indicate what it’s ending (minus the “do begin” orthen begin. For example:for idx := 0 to 69 do begin // ... copious amounts of code ... end; // for idx := 0 to 69
Case statements should always use a form like this:
case someValue of 0: // Zeroth thing if foo then begin bar := 69; end; // This is a much larger description of what the "1" and "2" case does. // For example, it spans multiple lines. So it goes above the case. 1, 2: begin bar := 36; foo := 9; end; else begin foo := 0; bar := 0; end; end;Note how the descriptions go next to each case unless they span multiple lines. Also note how you always use
beginandend(on their own lines, properly indented) except when they are not needed for a single case. Theelseshould have abeginandendas well (on their own lines, properly indented) unless they are not needed.If the case statement contains cases that all fit on one line and none of them need
beginandend, then put the code for the case on the same line as the case label (and theelseif it also fits, but theelsecan use abeginandendor have its code on a separate line if it looks better).case someValue of 0: foo := 69; 1, 2: foo := 42; else foo := 0; end;
