License: Public Domain , Load it with Quicklisp: (ql:quickload "parse-number-range")
Library type: Thematic utilities , Project complexity: Simple

parse-number-range parses loop's convenient for-as-arithmetic syntax into 5 simple values:
from, to, limit-kind (:inclusive, :exclusive or nil if unbounded), by (step) and direction (+ or -)).

Further related utilities are provided.
Intended for easy implementation of analogous functionality in other constructs.

Here's the package name, along with 2 nicknames:

Explicit qualification of the exported symbols is recommended, and one might (:import-from) select symbols, but (:use) is strongly discouraged.

parse-number-range » API

parse-number-range » API » Generalities

Here are the 5 "fundamental" values returned from parse and accepted by unparse:

from

The form introduced by :from, :upfrom or :downfrom. Defaults to 0;

to

The form introduced by :to, :upto, :downto, :below or :above. nil means "unbounded";

limit-kind

:inclusive or :exclusive if there's a limit, or nil if unbounded;

by

The form introduced by :by;

direction

+ or -.

Some functions accept a keyword-policy &key argument, which must be either :strict or :loose. (from 1 to 10) would be valid with :loose but not with :strict, which demands (:from 1 :to 10).

Use of :loose is strongly discouraged, as it's an unnecessary and evil feature that typically results in arbitrary interning of symbols in arbitrary packages.

parse-number-range » API » Parse API

Function parse range &key (keyword-policy :strict)
                          (extrasp nil)
                          (clause-kinds-p extrasp)
                          (clause-keywords-p extrasp)
                          (clauses-alist-p extrasp)
  => from to limit-kind by direction
       &key clause-kinds clause-keywords clauses-alist

By default, parse returns only the 5 "fundamental" values (see above):

from

Defaults to 0;

to

Defaults to nil;

limit-kind

Defaults to nil (unbounded);

by

Defaults to 1;

direction

Defaults to +.

parse can compute and return 3 additional pieces of information, on request:

return value request-keyword description
clause-kinds :clause-kinds-p

The clause kinds that were present,
in the order they appeared in.

clause-keywords :clause-keywords-p

The clause keywords that were present,
in the order they appeared in.

clauses-alist :clauses-alist-p

Alist: (clause-kind . clause-keyword)

Function unparse from to limit-kind by direction &key clause-kinds
  => range

As you might have guessed, this does the reverse of parse...

unparse uses flags-to-keywords internally and thus inherits its biases.

clause-kinds should be a list of up to 3 elements being a permutation of the clause-kinds :from, :to and :by. This determines the order that the clauses will appear in, which might matter in the presence of side-effects. nil designates the default order, (:from :to :by).

If the list has 0 or 1 elements, then the default order is used, (:from :to :by).

If the list has 2 elements, then those clauses will appear in the order specified but no order is specified between the other elements and these ones and between the other elements among themselves. (Yeah, a less simplistic implementation would allow simpler and more useful semantics.)

If the list has 3 elements, then the order is completely specified, explicitly.

Function canonicalize range &key (clause-kinds :preserve)
                                 (keyword-policy :strict)

This simply feeds the results of parse into unparse. If clause-kinds is :preserve, then :clause-kinds-p t will be specified for parse and the resulting clause-kinds result will be fed to unparse. Else, the provided clause-kinds is fed directly to unparse.

parse-number-range » API » Info API

Function kind keyword &key (errorp t) (keyword-policy :strict)
                => kind direction limit-kind

This function takes a loop keyword and returns the following information:

Argument Return values
keyword kind direction limit-kind
:from:fromnilnil
:downfrom:from-nil
:upfrom:from+nil
:to:tonil:inclusive
:upto:to+:inclusive
:downto:to-:inclusive
:below:to+:exclusive
:above:to-:exclusive
:by:bynilnil
Function flags keyword &key (errorp t) (keyword-policy :strict)
  => direction limit-kind

This convenience function simply forwards to kind and returns all values but the first.

Function flags-to-keywords direction limit-kind
  => from-keyword to-keyword

This function does roughly the opposite of the KIND function. It returns a FROM-KEYWORD and a TO-KEYWORD (nil if unbounded) that properly indicate the supplied DIRECTION and LIMIT-KIND.

There are typically multiple valid sets of answers that could be returned. This function is currently biased as follows, with no way to request a different bias:

  • The direction and limit-kind will always be represented in the to-keyword, and never in the from-keyword, except for the following exception:

    (flags-to-keywords '- :exclusive) => :downfrom, nil
  • :to is always returned in preference to :upto.