zscript-doc/glossary/Style.md

265 lines
13 KiB
Markdown
Raw Normal View History

2019-04-05 16:34:55 -07:00
<!-- vim-markdown-toc GFM -->
2019-08-14 03:31:41 -07:00
* [Style](#style)
* [Capitalization Conventions](#capitalization-conventions)
* [Rationale](#rationale)
* [Word Choice](#word-choice)
* [Rationale](#rationale-1)
* [Naming Type Members](#naming-type-members)
* [Rationale](#rationale-2)
* [Layout Conventions](#layout-conventions)
* [Rationale](#rationale-3)
* [Commenting Conventions](#commenting-conventions)
* [Rationale](#rationale-4)
* [Language Guidelines](#language-guidelines)
* [Rationale](#rationale-5)
2019-04-05 16:34:55 -07:00
<!-- vim-markdown-toc -->
2019-08-14 03:31:41 -07:00
# Style
2019-04-05 16:34:55 -07:00
This is a style guide for the ZScript documentation to encourage best-practice
coding with it, focused on clarity and consistency of formatting. It is also
intended to be a general style guide for writing new code, but this purpose is
ancillary and can be ignored. Within the abstract of each component of style
will be rationale for choices made.
## Capitalization Conventions
ZScript is case insensitive, however this guide aims to give a way to
consistently capitalize all identifiers in the language. Due to case
insensitivity, it may be difficult to apply these guidelines to real code, but
nonetheless a consistent capitalization style should be followed. Do note that
you must not rely on case differences in identifiers due to this.
Capitalize the first letter of each word in an identifier. Acronyms over 2
characters in length are considered whole words, so for instance prefer
`XmlWidget` over `XMLWidget` but `IOStream` over `IoStream`. Acronyms of one
2019-08-14 02:40:30 -07:00
character also count, so prefer `PrintF` over `Printf`. Members with `m`
prefixes in unmodifiable code must not capitalize the `m`.
2019-04-05 16:34:55 -07:00
For identifiers of parameter names and local scope variables do not capitalize
the first word. In these cases, prefer `xmlWidget` over `XmlWidget` or
`ioStream` over `IOStream`. The same for acronyms of one character, so `typeF`
over `TypeF` or `nChars` over `NChars`. (Note that the former two are malformed
names, however. See the "Word Choice" section for more information.)
2019-08-14 02:40:30 -07:00
Constants (declared with `const`, `static const`, or `enum`) must be all
uppercase, and must separate words with underscores.
2019-04-05 16:34:55 -07:00
Argument names in base archive methods may be renamed, but arguments with
defaults may not be renamed as they are part of the API.
### Rationale
Case insensitivity in programming is generally regarded as bad practice because
the lack of strictness is often a failing in language design. It is not
imperative here to reason for or against case insensitivity, however. Code
within this style is to be written as uniformly as possible, therefore
capitalization is also to be uniform. It is the user's choice whether to follow
this rule or not, just as any other, but for authors of this documentation the
sole imperative is consistency.
This style of acronym capitalization has been chosen so as to defer to prior
art, primarily C#'s standard, which has been in use for over a decade now. The
purpose of not capitalizing more than two characters is to make it more natural
to read acronyms where normally they are forced to be next to other capitalized
words.
Not capitalizing the first word of parameter and local scope variables is also
decided by deferring to prior art. The majority of programming style guides
written advocate for this. The purpose in ZScript is primarily moot due to case
insensitivity, but we apply these rules to make reading easier and more
consistent with most other programming languages that have existed.
2019-08-14 02:40:30 -07:00
Capitalizing constants and enumerations is an artifact of the way they are
declared in ZDoom, and also in the original Linux Doom source code. This is
2019-04-05 16:34:55 -07:00
extended to static arrays for consistency.
2019-08-14 02:40:30 -07:00
Flags were capitalized in Linux Doom due to being constants, and internally
within ZDoom they are still constants, but due to the style of ZScript using
full capitalization appears inconsistent. This is especially true due to the
use of `m` prefixes in places within ZScript's standard library.
2019-04-05 16:34:55 -07:00
## Word Choice
In new identifiers, do not add underscores anywhere within the identifier,
unless explicitly stated in this style guide or necessary because of
unmodifiable code (such as that in GZDoom's base archive.) Do not use Hungarian
notation.
Do not use keywords or types as names except for the identifier "`name`". Do
not, for instance, declare `string Class;`, even though the language will allow
you to do so, although `string Name;` is fine.
It is generally favorable to use shortened terminology, for instance `CanMoveZ`
instead of `CanMoveVertically`. Try to make names readable in English, however:
`CanThrowItem` instead of `ThrowableItem`.
In the same vein, use abbreviations where conventional, but avoid them where
unnecessary. Prefer `GetMobj` to `GetMapObject` but also `GetAngle` to
`GetAng`.
### Rationale
The forbidding of underscores and Hungarian notation are in accordance with
ZDoom's coding style. These choices are also present in other prior art, but
the main deciding factor is that of the engine itself.
The usage of most keywords in variable names is allowed in ZScript because the
parser considers most identifiers to be contextual. In many contexts, keywords
are allowed in places they shouldn't be by most standards. Besides the
precedence of prior art, it appears that the engine itself also avoids
(ab)using this relaxation of context.
The engine and this style do, however, allow use of the identifier "`name`" in
variables and members because it is a frequently used word and would be absurd
to disallow.
Shortened terminology such as abbreviations or contractions are favored due to
long-standing conventions within ZDoom's source code. This is also historically
relevant because of engines such as Unreal Engine which ZDoom takes inspiration
from. Common terminology which may be shortened can be found on [the Doom
Wiki.][2]
[2]: https://doomwiki.org/wiki/Category:Doom_engine
## Naming Type Members
Always name methods with verbs or verb phrases, such as "`Split`" or
"`CompareTo`." Always try to make names forward compatible, i.e. such that they
will likely not conflict with new functions when they are added to the engine.
In mods, this can even involve prefixing method names with one unique to the
mod. Avoid violent words such as `Die`, `Destroy`, `Kill`, except where
literally applicable. Prefer for instance `Stop`, `Drop`, `Halt`.
Always name members with nouns, noun phrases or adjectives. Boolean values
2019-08-14 02:40:30 -07:00
should often be prefixed with `Is`, `Has`, `Can`, and other existential
present-tense verbs. All members of class types should be prefixed with `m_`,
despite rules against Hungarian notation and underscores. Try to name members
productively rather than vaguely, instead of `RefInt` write `RefCount`.
2019-04-05 16:34:55 -07:00
### Rationale
Forwards compatibility is necessary primarily in mods. In this documentation it
should be taken into account when writing example code, often when writing
examples that involve inheriting from a class in the engine. ZScript will hard
error on load if there is more than one definition of a function, so it is
necessary to never have a conflict in names, including capitalization-wise.
The purpose of avoiding violent words in method names is to not unnecessarily
invoke potentially uncomfortable or triggering imagery. In much of the base
class code, it is literally the action being taken, as Doom is indeed a violent
game. In these cases it is entirely normal to use such verbs. An example of
when this is entirely wasteful is ZDoom's garbage collector (which is exposed
with different naming conventions) in which the flag for objects marked for
finalization upon the next collection cycle is named `EuthanizeMe`. This kind
of wording is certainly jocular and potentially amusing, but a 'funny' name
such as this serves no real purpose, and could easily be re-worded to prevent
potential discomfort.
Members are prefixed with `m_` in order to defuse potential clashes with local
variables, since case sensitivity cannot defuse them. The alternative to this
is to prefix all member variable accesses with `self.`. However, this is
potentially excessive and is not entirely productive in writing short and
readable examples. This also provides a layer of forward compatibility, as
ZDoom does not use `m_` prefixes anywhere within its code. For the opposite
reason, as structure types cannot inherit, there is no need to prefix their
members, and so this prefix is omitted within them.
## Layout Conventions
2019-08-14 03:35:49 -07:00
Use tabs with a width of 3 characters for indentation. Indent at each block,
but do not indent `case` labels. Align all code to 80 columns.
2019-04-05 16:34:55 -07:00
Write only one statement or declaration per line, except in the case of
multiple-assignment operations, in which case pairing all of the related
declarations and the statement on the same line is allowed as long as it does
not exceed 80 columns.
Always add one blank line between separate method definitions, and between
member declarations and member definitions. Add one blank line between local
variable declarations and statements, unless specified otherwise. Add one blank
line between conditional statements, except the `else` block of an `if`
statement.
Do not place a space before the parentheses of a conditional or loop statement
such as `if` or `for`. Always write `if()` and not `if ()`.
Always place opening braces on their own line. Using braces is not necessary
when there is a single sub-statement, for instance with `if(x) y = z;`.
### Rationale
The convention of 3 spaces for indentation comes from [Eternity Engine's style
guideline.][1] There is no other reason for this decision, other than it is
2019-08-14 03:35:49 -07:00
pleasing to the eye while not being excessive. Hardware tabs are used instead
of spaces in order to allow for user configuration, increasing accessibility.
The indentation and blank line rules are generally the same as the majority of
C-like language style guidelines.
2019-04-05 16:34:55 -07:00
Alignment to 80 columns is for the purpose of reading the raw documentation
text under standard size Linux terminals. This is useful, for instance, when
reading diff files under the `git` console client. This guideline can generally
be ignored outside of this documentation.
Writing multiple-assignment operations all on one line serves the purpose of
grouping all of the relevant information together. In most languages with
multiple assignment, you are able to do the declaration and assignment in one
line, so this crudely mirrors a more well implemented language syntax.
Placing opening braces on their own line greatly increases code readability,
especially for people hard of sight. Other styles make it easy to confuse where
a block begins and create a less clear visual line between the start and end.
The purpose of this is to increase readability of this documentation, and not
to make code look more pleasing.
[1]: https://github.com/team-eternity/eternity/blob/master/docs/ee_style_guide.md
## Commenting Conventions
Do not use block comments, except for the purpose of example or code that will
intentionally not work. Comments may be placed anywhere except at the end of
the opening line of a conditional or loop statement, the opening brace of a
block, or the ending brace of a block. Always use proper case and punctuation
when writing comments. Always put one space between the comment delimiter and
comment text.
### Rationale
Block comments are mostly an adage of old programming languages made to be
printed on paper. In modern times they are most useful for commenting out large
blocks of code. They are rather clumsy to format well, and so single-line
comments are to be used instead of them. One could argue that if a consistent
formatting style was added, this would not be a problem. However, it is
generally easier and more readable to just use line comments.
## Language Guidelines
Use `let` declarations when the type of the object will be obvious from what it
is assigned. Do not use them if it is not obvious, or in example when it is
necessary to proclaim the type to make reading easier.
Don't use integer types except for `int` except in example. Similarly, do not
use float types except for `double` except in example.
No restrictions are placed on the usage of parentheses. Always place spaces
between the operands of binary expressions (`1 + 1`) but never unary
expressions (`-5`.)
Do not place semicolons at the end of `enum` or `struct` definitions. However, always place commas at the end of the last enumeration variant.
### Rationale
Integer and floating-point types other than `int` and `double` are primarily
for internal use. They can be used by user types, but often it's not necessary
and can be harmful.
Placing semicolons at the end of top-level items is not meant to be used by end
users. Its main purpose is to make ZScript easier to port from C++. On the
other hand, placing a comma at the end of the last enumeration in a set is a
common practice, as it allows the user to place more variants without touching
much.
<!-- EOF -->