Form
NAME
Form - Raku implementation of Perl-style fixed-width text formatting
SYNOPSIS
use Form;
my @items = ('Widget A', 'Widget B');
my @prices = (9.99, 24.50);
print form
'Item Price',
'----------------------------',
'{[[[[[[[[[[[[[[[[[[[} {]].[}',
@items, @prices;
# Item Price
# ----------------------------
# Widget A 9.99
# Widget B 24.50
DESCRIPTION
Form provides a form() function for creating fixed-width formatted text.
It takes alternating format strings and data arguments, fills each field in
the format with the corresponding data, and returns the completed text as a Str.
my $text = form $format1, $datum1, $datum2,
$format2, $datum3;
Like all Raku subroutines, form can be called in scalar context (returns a string)
or void context (dies loudly, since the result would be lost).
CONCEPTS
Format
A string used as a template. It contains zero or more fields ā fixed-width slots enclosed in braces ā usually separated by literal characters.
Field
A fixed-width slot within a format string, enclosed in { }. The characters
inside determine the type, width, and justification of the data interpolated there.
Fields are designed to look like a stylised picture of the finished result:
{<<<<<<<} Justify text to the left
{>>>>>>>} Justify text to the right
{>>>>>>>} Centre the text
{<<<<>>>} Fully justify text to both margins
Data
A scalar string, number, or array supplied after each format string. form pulls
from each data argument in turn to fill the fields in the preceding format.
Line field vs Block field
A line field interpolates only as much data as fits on a single line, then stops. A block field interpolates all its data over as many output lines as necessary.
Column
One character-width of horizontal space in the output.
FIELD TYPES
All fields are enclosed in { }. The characters inside the braces select the
field type and width; the opening { and closing } each contribute one
column to the field width.
Text Fields
Left-justified
Data is padded with spaces on the right.
{<<<<} line field ā one line only; data truncated if wider than the field
{[[[[} block field ā data wraps across as many lines as needed
print form '{[[[[[[[[[}', 'The quick brown fox';
# The
# quick
# brown
# fox
Right-justified
Data is padded with spaces on the left.
{>>>>} line field
{]]]]]} block field
Centred
Data is padded on both sides; any odd padding goes on the right. Two equivalent syntaxes are supported:
{>>><<<} or {|||||||} line field
{]]][[[ } or {IIIIIII} block field
Fully justified
Whitespace in the data is stretched to fill the field width evenly. The final line of a block field is always left-justified.
{<<<>>>>} line field
{[[[]]]]]} block field
print form '{<<<<<<<<<<<>>>>>>>>>>>>}',
'A fellow of infinite jest';
# A fellow of infinite
Verbatim
Data is copied exactly as-is, without any re-justification or word-wrapping. Newlines in the data produce new lines in the output.
{''''''''} line field ā first line only
{""""""""" block field ā all lines preserved
print form '{""}', "line one\nline two";
# line one
# line two
Numeric Fields
A numeric field aligns a decimal number around a fixed-position decimal marker. The integer part is right-justified; the fractional part is left-justified and zero-padded to the declared number of decimal places.
{>>>.<} line field ā 3 integer columns, dot, 2 fractional columns
{]]].[} block field - same
Field width and decimal places
The opening { contributes one position to the integer width, and the closing
} contributes one position to the fractional width. This makes the minimum
fractional width 1 (one decimal place) i.e. <0.0>, because the } still provides
one column:
{]].} 1 decimal place ( 0 Ć [ + } - 00.0 )
{]].[} 2 decimal places ( 1 Ć [ + } - 00.00 )
{]].[[} 3 decimal places ( 2 Ć [ + } - 00.000 )
A bare trailing decimal such as 0. is not possible by design. This permits the butting
of neighboring numeric columns with 1 (or even 0) space as the gap, i.e. {].} {].} for
0.1 0.2.
Rounding and zero-padding
Numbers are rounded to the declared number of decimal places and zero-padded.
Any Real subtype ā Int, Rat, or Num ā is accepted:
say form '{]].[}', 5; # 5.00 (Int ā zero-padded)
say form '{]].[}', 1.5; # 1.50 (Rat ā zero-padded)
say form '{]].[}', 3.145; # 3.15 (Rat ā rounded up)
say form '{]].[}', 3.144; # 3.14 (Rat ā rounded down)
say form '{]].[}', 5e0; # 5.00 (Num ā zero-padded)
Custom decimal marker
Any character that is not [, ], < < >>, C<< > >, or + may be used as
the decimal marker. The same marker is also recognised in string input data:
say form '{]]],[}', 1.23; # 1,23 (comma marker)
say form '{]]],[}', '1,23'; # 1,23 (comma in input accepted too)
say form '{]]:}', 7.5; # 7:5 (colon marker, 1dp)
Thousands separators
Include a separator character at the desired grouping position inside the integer
part of the field; form infers the grouping pattern from its position. Five
major conventions are supported:
# Brittanic ā groups of 3, comma separator
say form '{],]]],]]].[}', 1234567.89; # 1,234,567.89
# Continental ā groups of 3, period separator, comma decimal
say form '{].]]].]]],[}', 1234567.89; # 1.234.567,89
# Subcontinental ā group of 3 then groups of 2
say form '{]],]],]]].[[}', 1234567.89; # 12,34,567.890
# Hyperspatial ā space separator
say form '{] ]]] ]]].[}', 1234567.89; # 1 234 567.89
# Asiatic ā groups of 4
say form '{]]]],]]]].[}', 1234567.89; # 123,4567.89
# Swiss Army (apostrophe separator)
say form "{]']]]']]].[}", 1234567.89; # 1'234'567.89
Overflow and invalid data
When the integer part is too wide for the available columns, the entire field
fills with # characters. When data cannot be parsed as a number, the field
fills with ? characters:
say form '{]].[}', 9999.9; # ###.##
say form '{]].[}', 'hello'; # ???.??
Negative numbers work naturally; the minus sign occupies one integer column:
say form '{]]].[}', -3.14; # -3.14
Sign-reserved fields
By default the minus sign shifts the digits right, so positive and negative numbers do not align column-for-column. Sign-reserved fields allocate a fixed column for the sign so the digits stay aligned regardless of sign:
{-]].[} leading sign ā one sign column before the integer part
{]].[-} trailing sign ā one sign column after the fractional part
{(]].[)} paren sign ā opening paren before, closing paren after
say form '{-]].[}', 3.14; # 3.14 (space in sign column)
say form '{-]].[}', -3.14; # - 3.14 (minus in sign column)
say form '{]].[-}', 3.14; # 3.14 (trailing space)
say form '{]].[-}', -3.14; # 3.14- (trailing minus)
say form '{(]].[)}', 3.14; # 3.14 (spaces around)
say form '{(]].[)}', -3.14; # ( 3.14) (parens around)
The sign column is included in the total field width. On overflow the sign
column is preserved and the numeric columns fill with #:
say form '{-]].[}', 9999.9; # ###.##
say form '{-]].[}', -9999.9; # -###.##
Vertical Alignment
When a format row mixes block fields that produce different numbers of output
lines, form pads the shorter columns with blank lines to match the tallest.
An alignment modifier before or after the field type controls where the data
appears within that padding.
Top (default)
Data appears at the top; blank lines fill below. This is the default ā no special character is needed.
Middle
Prefix the field type with = (or suffix, or both). Data is centred
vertically; odd padding goes below.
{=[[[[[[[} or {[[[[[[[=} middle-aligned block field
Bottom
Prefix the field type with _ (or suffix, or both). Data is pushed to the
bottom of the padded column.
{_[[[[[[[} or {[[[[[[[_} bottom-aligned block field
DATA ARGUMENTS
Scalar values (Str, Int, Rat, Num, ā¦) and Arrays may be passed
as data. Arrays are not flattened ā each array is bound to its corresponding field
and its elements are consumed one per output row:
my @names = <Alice Bob Carol>;
my @scores = (98, 74, 85);
print form '{<<<<<<<<<} {]].[}',
@names, @scores;
# Alice 98.00
# Bob 74.00
# Carol 85.00
Each array element provides one datum; block fields consume one element per row and may wrap that element over multiple lines.
MULTIPLE FORMAT STRINGS
A single call to form may contain any number of format / data pairs.
form processes them in order and concatenates the results into one string:
print form
'Invoice: {<<<<<}', $id,
'Date: {<<<<<<<<}', $date,
'----------------------------',
'{[[[[[[[[[[[[} {]]].[[}',
@descriptions, @amounts;
Literal strings (format strings with no fields) contribute their text directly, one line per call.
INTERLEAVE
The :interleave adverb lets you write the entire template as a single
multi-line string and supply all data arguments after it, rather than
alternating format / data pairs:
print form :interleave,
q:to/END/,
Invoice: {<<<<<}
Date: {<<<<<<<<}
--------------------
{[[[[[[[[[[[[[[[[[} {]]]].[}
--------------------
END
$id, $date,
@descriptions, @amounts;
form splits the template on newlines and processes each line in order,
consuming data arguments left-to-right across all rows. Lines with no fields
are output as literals.
FIELD REFERENCE TABLE
Field type Line field Block field
āāāāāāāāāāāāāāāāāā āāāāāāāāāāāāāā āāāāāāāāāāāāāā
left-justified {<<<<<<<} {[[[[[[[}
right-justified {>>>>>>>} {]]]]]]]}`
centred {>>><<<} {]]][[[ }
centred (alt) {|||||||} {IIIIIII}
fully justified {<<<>>>>} {[[[]]]]}`
verbatim {'''''''} {"""""""}
numeric {>>>.<<} {]]].[[}
euronumeric {>>>,<<} {]]],[{}
comma'd (Brittanic) {>,>>>,>>>.<<} {],]]],]]].[{}
space'd (Hyperspatial){> >>> >>>.<<} {] ]]] ]]].[{}
subcontinental {>>,>>,>>>.<<} {]],]],]]].[{}
eurocomma (Continen.) {>.>>>.>>>,<<} {].]]].]]],[{}
Swiss Army apostrophe {>'>>>'>>.<<} {]']]]']]].[{}
Asiatic {>>,>>>>>.<<} {]],]]]]].[{}
signed leading {->>.<<} {-]].[}
signed trailing {>>.<-} {]].[-}
signed paren {(>>.<)} {(]].[)}
left/middled {=<<<<<<<} {=[[[[[[[}
right/middled {=>>>>>>>} {=]]]]]]]}`
left/bottomed {_<<<<<<<} {_[[[[[[[}
right/bottomed {_>>>>>>>} {_]]]]]]]}`
AUTHORS
Matthew Walton, Stephen Roe
Source: https://github.com/raku-community-modules/Form
COPYRIGHT AND LICENSE
Copyright 2009 - 2012 Matthew Walton
Copyright 2013 - 2026 Raku Community
This library is free software; you can redistribute it and/or modify it under the Artistic License 2.0.