The manual clarifies:
SQL identifiers and key words must begin with a letter (a-z, but also
letters with diacritical marks and non-Latin letters) or an underscore
(_). Subsequent characters in an identifier or key word can be
letters, underscores, digits (0-9), or dollar signs ($). Note that
dollar signs are not allowed in identifiers according to the letter of
the SQL standard, so their use might render applications less
portable. The SQL standard will not define a key word that contains
digits or starts or ends with an underscore, so identifiers of this
form are safe against possible conflict with future extensions of the
standard.
The system uses no more than NAMEDATALEN-1 bytes of an identifier;
longer names can be written in commands, but they will be truncated.
By default, NAMEDATALEN is 64 so the maximum identifier length is 63
bytes. If this limit is problematic, it can be raised by changing the
NAMEDATALEN constant in src/include/pg_config_manual.h.
And:
There is a second kind of identifier: the delimited identifier or
quoted identifier. It is formed by enclosing an arbitrary sequence of
characters in double-quotes ("). [...]
Quoted identifiers can contain any character, except the character
with code zero. (To include a double quote, write two double quotes.)
This allows constructing table or column names that would otherwise
not be possible, such as ones containing spaces or ampersands. The
length limitation still applies.
There is more, you can even use escaped unicode characters like: U&"d\0061t\+000061". Read the whole chapter.
So any character, except the character with code zero is allowed in a valid identifier, once the name is double-quoted. And without double-quotes, even simple strings like 'select' may be invalid if they happen to be reserved words. (The concept of reserved words is an unfortunate one, set by the SQL standard, hard to change now.)
You might just let Postgres do the work, using quote_ident():
SELECT quote_ident('0of') = '0of';
Quotes are added only if necessary.
The expression returns true for valid identifiers. Or just used the result of quote_ident('$identifier') to get a legal name in either case (quoted if necessary).