gridspan is a small, offline, standard-library-only Elixir command-line tool for checking and
working with named spreadsheet ranges such as B2:D4. It is intended for people using a
terminal, a script, or continuous integration (CI). It reads range descriptions from standard
input and supports validation, per-range sizing, combined coverage, intersections, and shifts.
The project requires Elixir ~> 1.15 and a compatible Erlang/OTP installation. The project
manifest does not pin a particular OTP release. No network connection or third-party dependency
is needed to build or run gridspan.
From the repository directory, build the escript:
mix escript.buildThen run a command, supplying range lines on standard input:
escript ./gridspan COMMANDReplace COMMAND with one of the commands below. In the examples later in this guide,
gridspan is shorthand for the built escript invoked as escript ./gridspan; it is not a
separate command installed on the system.
The same Mix build and escript invocation are used on Windows, macOS, and Linux, provided
Elixir and Erlang/OTP are installed. In Windows PowerShell, pipe text to the escript or redirect
input from a file; in POSIX shells, the examples use printf. The escript path is relative to
the repository directory.
The command synopsis is:
usage: gridspan check [--rows N] [--cols N]
gridspan size [--rows N] [--cols N]
gridspan union [--rows N] [--cols N]
gridspan overlap [--rows N] [--cols N]
gridspan shift [--rows N] [--cols N]
The five command names are lowercase and case-sensitive. With no command, gridspan writes the
synopsis to standard error and exits with status 2. An unrecognized first word, including an
empty word or a differently capitalized command, reports gridspan: unknown command WORD and
also exits 2.
As the first word, help, -h, or --help prints the synopsis to standard output and exits
0. --version prints gridspan 0.1.0 and exits 0. These help and version forms ignore any
words that follow them.
All five commands accept --rows N and --cols N. These options are validated for every
command, but only shift uses their values. The defaults are zero rows and zero columns. For
shift, positive row offsets move ranges down and negative offsets move them up; positive
column offsets move them right and negative offsets move them left.
Arguments after a command are checked from left to right, and the first problem is reported
without reading input. A word not beginning with a hyphen, including an empty word or exactly
-, is an unexpected argument. A hyphen-prefixed word other than --rows or --cols is an
unknown option. An option consumes the next word as its value even if that word looks like
another option. For each recognized option, checks are made in this order: whether that option
was already supplied, whether its value is missing, and whether the value is valid. Thus the
error templates are:
gridspan: unexpected argument WORDgridspan: unknown option WORDgridspan: given twice: OPTIONgridspan: missing value for OPTIONgridspan: bad value for OPTION
An option value is a whole decimal number from -9999 through 9999. It may have a minus
sign but no plus sign; it cannot have leading zeroes, except that 0 itself is valid. Negative
zero is invalid. For example, 0, 7, and -7 are valid values, while 007, +7,
-0, and values outside the permitted range are not.
A valid invocation of one of the five commands reads standard input. Help, version, and invalid
command lines do not read it. A successful command exits 0, writes its result to standard output,
and writes nothing to standard error. A command-line error exits 2; an input error exits 1. On
failure, standard output is empty and standard error contains one line beginning with
gridspan: , followed by the message and a line feed. Text input and output use ASCII and LF
line endings.
Each meaningful input line contains a name and a cell or range, separated by one or more spaces or tabs:
NAME RANGE
A name has 1 to 12 characters. It begins with a lowercase letter and is followed only by lowercase letters, digits, or hyphens. Each name must be unique.
A cell consists of a column followed immediately by a row number. Columns use one or two
uppercase letters: A is column 1, Z is 26, AA is 27, and ZZ is 702, the final
column. Rows are decimal numbers from 1 through 9999, with no leading zero. For example,
ZZ9999 is the last cell. Three-letter columns, lowercase letters, absent row numbers, zero,
leading-zero row numbers, and rows above 9999 are invalid.
A range is one cell or two cells separated by one colon, for example B2:D4. It covers all
cells between its start and end columns and rows, inclusive. The end must not be to the left of
the start or above it. A one-cell range can be written as C3 or C3:C3.
Input is split at each LF. A final line without an LF still counts; empty input has no lines.
One CR at the end of each line is removed. Lines containing only spaces and tabs are ignored.
A line whose first word starts with # is a comment and is ignored; inline comments are not
supported. Ignored lines still count toward the maximum of 40 input lines. More than 40 lines
is reported before any other input error.
Leading and trailing spaces or tabs are ignored when separating the two words. Every other line must have exactly two words. Input checks for each line occur in this order:
- The line must have exactly two words.
- The name must be valid.
- The range syntax and its cells must be valid.
- The range must not run backwards.
- The name must not duplicate an earlier meaningful line.
Errors include the original 1-based line number, including skipped lines. The exact input error templates are:
gridspan: input is too long (max 40 lines)gridspan: line N: expected NAME RANGEgridspan: line N: bad name WORDgridspan: line N: bad range WORDgridspan: line N: range runs backwardsgridspan: line N: duplicate name NAME
All commands process ranges in input order. A range is printed canonically as START:END,
except that a one-cell range is printed as one cell such as C3, not C3:C3. Counts use a
singular noun only when the count is 1 (1 range, 0 ranges, 1 cell, 9 cells).
checkprintsok N ranges, where N is the number of meaningful range lines. With no ranges, it printsok 0 ranges.sizeprints oneNAME RANGE RxC N cellsline per range, in input order. R and C are the row and column counts for that range, and N is its cell count. It then printstotal T cells, where T counts distinct covered cells, so a cell covered by multiple ranges is counted once. With no ranges, it printstotal 0 cells.unionprintsbounds nonewhen there are no ranges. Otherwise it printsbounds RANGEfor the smallest rectangle containing all ranges, followed bycovered T of B cells. T is the number of distinct covered cells, while B is the area of the bounding rectangle; T can be smaller than B. The wordcellsis always plural in this line.overlapprints each pair sharing at least one cell asNAME1 NAME2 RANGE N cells, followed byoverlaps K. Pairs are ordered by input position: earlier first, then later partners in input order. The printed range is the inclusive intersection, and N is its cell count. A single shared cell is printed as that cell. Ranges that only meet along an edge without sharing a cell do not overlap. With no overlapping pairs, the only line isoverlaps 0.shiftprintsNAME OLD NEWfor every shifted range, in input order, thenshifted K ranges. With no ranges, it printsshifted 0 ranges. The row and column offsets are signed, with positive values moving down/right and negative values moving up/left. Every shifted cell must stay within columns 1 through 702 and rows 1 through 9999. If any range would leave the sheet, the command reports the first such range in input order, using its original input line number, and produces no partial output:gridspan: line N: NAME moves outside the sheet.
These examples use a POSIX shell in the repository directory, after building the escript with
mix escript.build. Here, gridspan stands for escript ./gridspan. The final example uses
2>&1 to combine standard error with standard output so the error lines are visible.
$ printf 'a A1:C3\nb B2:D4\n' | gridspan overlap
a b B2:C3 4 cells
overlaps 1
$ printf 'a A1:C3\nb B2:D4\nc F6\n' | gridspan size; printf 'a A1:C3\nb B2:D4\nc F6\n' | gridspan union
a A1:C3 3x3 9 cells
b B2:D4 3x3 9 cells
c F6 1x1 1 cell
total 15 cells
bounds A1:F6
covered 15 of 36 cells
$ printf 'a Z1:AA2\nb C3\n' | gridspan shift --cols 3 --rows 1
a Z1:AA2 AC2:AD3
b C3 F4
shifted 2 ranges
$ printf 'a B2:A1\n' | gridspan check 2>&1; printf 'a A1\n' | gridspan shift --rows -1 2>&1
gridspan: line 1: range runs backwards
gridspan: line 1: a moves outside the sheet
Run the project checks from the repository directory:
mix format --check-formatted
mix compile --warnings-as-errors
mix testSee LICENSE for the MIT license.
Built with BAKED, an open-source coding agent (bench round R97).