Define mappings for alphanumeric codes using expressions

A naxp (‘encoded ASCII expression’) uses a regex-like syntax to define how ASCII strings should be mapped to a numerical index.

With one simple expression you can standardise conversion of alphanumeric codes to integer indexes unambiguously and consistently across multiple coding languages and hardware platforms.

Examples

Name naxp Encoded values Bits
Flight designatore.g. AC861 (\A\A|\A\9|\9\A) #[0!0!0!1-9999] 12 m 24
UK postcodee.g. EC4M 8AD \A\A?\9\X? \s!! \9\A\A | GIR \s!! 0AA 1.8 bn 31
Ireland Eircodee.g. A65 F4E2 [AC-FHKNPRTV-Y]\9[\9AC-FHKNPRTV-Y] \s!! [\9AC-FHKNPRTV-Y]{4} 1.5 bn 31
Netherlands postcodee.g. 1012 AB \9\9\9\9 \s!! \A\A 6.8 m 23

Get started

Grab a standard naxp

Check if a pre-written standard naxp already exists for your use case.

Standard naxps

Develop a naxp

Test, debug or develop a naxp interactively, with optional custom code generation.

Interactive naxp design

Embed a naxp

General naxp functionality is available in multiple programming languages.

Find a naxp package

A quick tour

A naxp maps all valid text to a range of consecutive integers starting at 1, and all invalid text to 0.

If two naxps accept the same text and print it the same way then they will produce exactly the same encoded values (and have exactly the same performance).

Characters

Printable ASCII characters are represented as themselves, apart from the following eighteen, which must be escaped with a preceding backslash:

  • thirteen with a special meaning in a naxp, ! # ( ) , - ? [ \ ] { | }, and
  • five that have a special meaning in a regex but not in a naxp, * + . ^ $, which require escaping so that regex users get errors rather than expecting them to work the same way.

Unlike regex, whitespace is always ignored, so feel free to use spaces to make the expression readable. If you want the space character to be part of a naxp then use \s.

Athe character A
\-a hyphen (because - is one of the characters that needs to be escaped)
\sa space

Examples: A\-Z and A \- Z both match A-Z.

Common character set shorthand

Some character sets occur again and again, so naxp has special notation for five of these:

\9a digit, i.e. 0 to 9
\Aan uppercase letter, i.e. A to Z
\aa lowercase letter, i.e. a to z
\Xa digit or an uppercase letter
\xa digit or a lowercase letter

Example: \A\A\9 matches EC4.

General character sets

Square brackets match any of the items inside them. An item in this context is a single character, an inclusive character range written lowest first, or one of the above character set shorthands.

[AEIOU]one of A, E, I, O or U
[A-F]one of the inclusive range A to F
[\9A-F]an uppercase hexadecimal digit

Example: [\9A-F][\9A-F] matches two-digit hexadecimals from 00 to FF.

Options, groups and alternatives

A question mark ? makes the preceding item optional. The ? operator binds very tightly, which means that if you want to apply it to more than one item as a group you need to put that group in parentheses ().

Tip In a regex you use ? to indicate optional text, but a naxp needs to know whether the different possible text values should be encoded differently or not.

If you want different encodings then use ?. For instance, a spreadsheet column could be \A?\A, which is correct because A and AA are different columns.

But if you want to indicate an optional character that doesn’t change meaning, e.g. an optional separator in a postcode then you want the !! and !? operators.

Vertical bar | separates alternatives. The | operator binds very loosely, which means you don’t need parentheses unless the alternatives themselves are complex expressions.

x?Optional x
ABC|XYZeither ABC or XYZ
(ABC)ABC as a group

Example: \A\A?\9\X? and (\A\A|\A)\9\X? are equivalent, and both match B1 and EC4M.

Intervals

An interval, represented using braces {}, repeats one item a fixed number of times, or a range of times. (This is the same syntax as regex, except there is no unbounded form, because a naxp must have a finite count of values.)

x{3}exactly three of x
x{2,4}two to four of x

Example: \A{3} matches LHR but not GB, whereas \A{2,3} matches both.

Variable formats – the ! operator

Sometimes you want to handle variations in text formats that don’t affect meaning. Examples are leading digits in numerical parts of codes, and separators that are not strictly required but often included for readability.

naxp handles this using the binary ‘text unification’ ! operator:

  • The expression on the left matches as usual, but is ignored for encoding purposes.
  • The text on the right is the canonical text to replace it with when mapping an encoded value back to text. The canonical text must be a single fixed string and included in the possible matches for the expression on the left of the !.
x!y anything x matches, printed as y, which must be a constant and included in the text that x matches

Example: \X [\s\-]!\- \9 matches a capital letter and a digit separated by a space or a hyphen, but the version with a hyphen is preferred. So A 1 and A-1 have the same encoded value, but print as A-1.

Note This is a handy feature but, if you use the text unification operator, it is possible for two naxps to accept the same text but encode it differently. This can happen when they produce different canonical versions for the same accepted text (because it is the canonical text that drives the encoding).

Variable format shortcuts – the !! and !? operators

There are two specialised unary versions of the ‘text unification’ ! operator for optional text:

x!! optional x, canonical text includes x.
x!? optional x, canonical text excludes x.

Tip The x in x!! must be a single fixed string – if it’s not then use the binary ! operator.

Example: \A\9\A \s!!\9\A\9 matches Canadian postal codes, e.g. H0H 0H0, which officially should have a space as the fourth character. But it will also tolerate a missing space, e.g. H0H0H0, while (a) giving the same encoding and (b) printing correctly with the space.

Case sensitivity

The \C and \c operators make everything that follows them, to the end of the enclosing group, insensitive to case, respectively treating upper case and lower case as canonical.

A case fold runs to the end of the group it is written in, like a flag in a regex, so writing \C at the front of a whole naxp is the usual way to make it case tolerant. To fold only part of a naxp, put the fold in parentheses: (\CA)B folds the A alone.

\Cx x in either case, canonical text is upper case
\cx x in either case, canonical text is lower case

Example: \C \A\A?\9\X? \s \9\A\A matches a UK postcode regardless of capitalisation. So ec1a 1bb and EC1A 1BB have the same encoded value, and that encoded value prints as EC1A 1BB.

Decimal ranges

Alphanumeric code systems often incorporate decimal numbers and so naxp has a special ‘decimal range’ syntax for this. The syntax is #[a-b], where a and b are the digits representing the lowest and highest values in the range.

Handling leading zeros in numeric text is a perennially thorny issue for alphanumeric codes, so naxp has strong capabilities for handling them. There are four options:

  1. Exclude leading zeros by omitting them, e.g. #[1-999].
  2. Require leading zeros by including them, e.g. #[001-999].
  3. Make leading zeros optional but non-canonical using 0?, e.g. #[0?0?1-999] reprints 1, 01 and 001 as 1.
  4. Make leading zeros optional and canonical using 0!, e.g. #[0!0!1-999] reprints 1, 01 and 001 as 001.
#[1-9999]1 to 9999, one to four digits and no leading zeros
#[01-31]01 to 31, always two digits, with a leading zero where needed
#[0?0?0-105] 0 to 105, accepts leading zeros, canonical text excludes leading zeros
#[0!0!0-105] 0 to 105, accepts leading zeros, canonical text includes leading zeros

Example: (\A\A|\A\9|\9\A)#[0!0!0!1-9999] matches AC861 but reprints it as AC0861.

Tip If the canonical form fully uses leading zeros (e.g. #[001-999] or #[0!0!1-999], but not #[1-999] or #[0?0?1-999]) then the encoded values sort in the same order as the numbers they represent.

Operator precedence

From loosest to tightest binding, so an operator lower in the table applies before one above it. Use parentheses () to override the order, as in a regex.

\C \ceverything to the end of the enclosing group: \CA|b is \C(A|b)
|alternatives: AB|CD is (AB)|(CD)
concatenationone element after another: A?B is (A?)B
! !! !?an element with its quantifier: A?!() is (A?)!()
? {}a single element: AB? is A(B?)

Computational constraints

All naxps define a maximum length for the text they accept. (In comparison, some regexes accept arbitrarily long text.)

For some complex naxps, it is possible for the computations required to get very large. To ensure consistent behaviour across platforms, the constraints on these computations are defined in the specification.

Further reading

For more information, see: