@itemize @bullet
@item
-Without @code{BY} and @code{FORMAT}, the numbers in the text file are
-in @code{F} format separated by spaces or commas. For @code{WRITE},
-@pspp{} uses as many digits of precision needed to represent the
-numbers in the matrix
+With neither @code{BY} nor @code{FORMAT}, the numbers in the text file
+are in @code{F} format separated by spaces or commas. For
+@code{WRITE}, @pspp{} uses as many digits of precision as needed to
+accurately represent the numbers in the matrix.
@item
-With @code{BY @i{width}}, the input area is divided into fixed-width
-fields with the given @i{width}. The input area must be a multiple of
+@code{BY @i{width}} divides the input area into fixed-width fields
+with the given @i{width}. The input area must be a multiple of
@i{width} columns wide. Numbers are read or written as
@code{F@i{width}.0} format.
@item
-With @code{FORMAT=@i{count}F}, the input area is divided into
-@i{count} equal-width fields per line. The input area must be a
-multiple of @i{count} columns wide. Another format type may be
-substituted for @code{F}.
+@code{FORMAT=@i{count}F} divides the input area into @i{count}
+equal-width fields per line. The input area must be a multiple of
+@i{count} columns wide. Another format type may be substituted for
+@code{F}.
@item
-@code{FORMAT=F@i{w}.@i{d}} divides the input area into fixed-width
+@code{FORMAT=F@i{w}}[@code{.@i{d}}] divides the input area into fixed-width
fields with width @i{w}. The input area must be a multiple of @i{w}
columns wide. Another format type may be substituted for @code{F}.
+The @code{READ} command disregards @i{d}.
@item
-If @code{BY} and @code{FORMAT} are both used, then they must agree on
-the field width.
+@code{FORMAT=F} specifies format @code{F} without indicating a field
+width. Another format type may be substituted for @code{F}. The
+@code{WRITE} command accepts this form, but it has no effect unless
+@code{BY} is also used to specify a field width.
@end itemize
+If @code{BY} and @code{FORMAT} both specify or imply a field width,
+then they must indicate the same field width.
+
@node Matrix READ Command
@subsubsection The @code{READ} Command
referenced file if @code{FILE} is omitted.
The @code{FIELD} and @code{FORMAT} subcommands specify how input lines
-are interpreted. @xref{Matrix READ and WRITE Commands}, for details.
+are interpreted. @code{FIELD} is required, but @code{FORMAT} is
+optional. @xref{Matrix READ and WRITE Commands}, for details.
The @code{SIZE} subcommand is required for reading into an entire
variable. Its restricted expression argument should evaluate to a
[@t{/HOLD}]@t{.}
@end display
-The @code{WRITE} command evaluates @i{expression} and writes it to a
-text file in a specified format. Write the expression to evaluate
-just after the command name.
+The @code{WRITE} command evaluates @i{expression} and writes its value
+to a text file in a specified format. Write the expression to
+evaluate just after the command name.
The @code{OUTFILE} subcommand is required in the first @code{WRITE}
command that appears within @code{MATRIX}. It specifies the text file
referenced file if @code{FILE} is omitted.
The @code{FIELD} and @code{FORMAT} subcommands specify how output
-lines are formed. @xref{Matrix READ and WRITE Commands}, for details.
+lines are formed. @code{FIELD} is required, but @code{FORMAT} is
+optional. @xref{Matrix READ and WRITE Commands}, for details.
By default, or with @code{MODE=RECTANGULAR}, the command writes an
entry for every row and column. With @code{MODE=TRIANGULAR}, the
#include <unictype.h>
#include <unistd.h>
#include <unistr.h>
-#include <uniwidth.h>
#include "language/command.h"
#include "language/lexer/macro.h"
}
}
-static int
-count_columns (const char *s_, size_t length)
-{
- const uint8_t *s = CHAR_CAST (const uint8_t *, s_);
- int columns;
- size_t ofs;
- int mblen;
-
- columns = 0;
- for (ofs = 0; ofs < length; ofs += mblen)
- {
- ucs4_t uc;
-
- mblen = u8_mbtouc (&uc, s + ofs, length - ofs);
- if (uc != '\t')
- {
- int width = uc_width (uc, "UTF-8");
- if (width > 0)
- columns += width;
- }
- else
- columns = ROUND_UP (columns + 1, 8);
- }
-
- return columns + 1;
-}
-
static int
lex_token_get_first_column (const struct lex_source *src,
const struct lex_token *token)
{
- return count_columns (&src->buffer[token->line_pos - src->tail],
- token->token_pos - token->line_pos);
+ return utf8_count_columns (&src->buffer[token->line_pos - src->tail],
+ token->token_pos - token->line_pos) + 1;
}
static int
newline = memrchr (start, '\n', end - start);
if (newline != NULL)
start = newline + 1;
- return count_columns (start, end - start);
+ return utf8_count_columns (start, end - start) + 1;
}
static struct msg_location
#include "output/pivot-table.h"
#include "gl/c-ctype.h"
+#include "gl/ftoastr.h"
#include "gl/minmax.h"
#include "gl/xsize.h"
int c1, c2;
enum fmt_type format;
int w;
- int d;
+ //int d;
bool symmetric;
bool reread;
}
struct write_file *wf;
struct matrix_expr *expression;
int c1, c2;
- enum fmt_type format;
- int w;
- int d;
+
+ /* If this is nonnull, WRITE uses this format.
+
+ If this is NULL, WRITE uses free-field format with as many
+ digits of precision as needed. */
+ struct fmt_spec *format;
+
bool triangular;
- bool hold; /* XXX */
+ bool hold;
}
write;
}
lex_get (s->lexer);
}
- else if (!fmt_from_name (p, &read->format))
+ else if (fmt_from_name (p, &read->format))
+ lex_get (s->lexer);
+ else
{
struct fmt_spec format;
if (!parse_format_specifier (s->lexer, &format))
goto error;
read->format = format.type;
read->w = format.w;
- read->d = format.d;
}
}
else
return NULL;
}
+static void
+parse_error (const struct dfm_reader *reader, enum fmt_type format,
+ struct substring data, size_t y, size_t x,
+ int first_column, int last_column, char *error)
+{
+ int line_number = dfm_get_line_number (reader);
+ struct msg_location *location = xmalloc (sizeof *location);
+ *location = (struct msg_location) {
+ .file_name = xstrdup (dfm_get_file_name (reader)),
+ .first_line = line_number,
+ .last_line = line_number + 1,
+ .first_column = first_column,
+ .last_column = last_column,
+ };
+ struct msg *m = xmalloc (sizeof *m);
+ *m = (struct msg) {
+ .category = MSG_C_DATA,
+ .severity = MSG_S_WARNING,
+ .location = location,
+ .text = xasprintf (_("Error reading \"%.*s\" as format %s "
+ "for matrix row %zu, column %zu: %s"),
+ (int) data.length, data.string, fmt_name (format),
+ y + 1, x + 1, error),
+ };
+ msg_emit (m);
+
+ free (error);
+}
+
static void
matrix_read_set_field (struct read_command *read, struct dfm_reader *reader,
- gsl_matrix *m, struct substring p, size_t y, size_t x)
+ gsl_matrix *m, struct substring p, size_t y, size_t x,
+ const char *line_start)
{
const char *input_encoding = dfm_reader_get_encoding (reader);
- union value v;
- char *error = data_in (p, input_encoding, read->format,
- settings_get_fmt_settings (), &v, 0, NULL);
- /* XXX report error if value is missing */
+ char *error;
+ double f;
+ if (fmt_is_numeric (read->format))
+ {
+ union value v;
+ error = data_in (p, input_encoding, read->format,
+ settings_get_fmt_settings (), &v, 0, NULL);
+ if (!error && v.f == SYSMIS)
+ error = xstrdup (_("Matrix data may not contain missing value."));
+ f = v.f;
+ }
+ else
+ {
+ uint8_t s[sizeof (double)];
+ union value v = { .s = s };
+ error = data_in (p, input_encoding, read->format,
+ settings_get_fmt_settings (), &v, sizeof s, "UTF-8");
+ memcpy (&f, s, sizeof f);
+ }
+
if (error)
- msg (SW, _("GET parse error (%.*s): %s"), (int) p.length, p.string, error);
+ {
+ int c1 = utf8_count_columns (line_start, p.string - line_start) + 1;
+ int c2 = c1 + ss_utf8_count_columns (p) - 1;
+ parse_error (reader, read->format, p, y, x, c1, c2, error);
+ }
else
{
- gsl_matrix_set (m, y, x, v.f);
+ gsl_matrix_set (m, y, x, f);
if (read->symmetric && x != y)
- gsl_matrix_set (m, x, y, v.f);
+ gsl_matrix_set (m, x, y, f);
}
}
static bool
matrix_read_line (struct read_command *read, struct dfm_reader *reader,
- struct substring *line)
+ struct substring *line, const char **startp)
{
if (dfm_eof (reader))
{
return false;
}
dfm_expand_tabs (reader);
- *line = ss_substr (dfm_get_record (reader),
- read->c1 - 1, read->c2 - read->c1);
+ struct substring record = dfm_get_record (reader);
+ /* XXX need to recode record into UTF-8 */
+ *startp = record.string;
+ *line = ss_utf8_columns (record, read->c1 - 1, read->c2 - read->c1);
return true;
}
size_t nx = read->symmetric ? y + 1 : m->size2;
struct substring line = ss_empty ();
+ const char *line_start = line.string;
for (size_t x = 0; x < nx; x++)
{
struct substring p;
ss_ltrim (&line, ss_cstr (" ,"));
if (!ss_is_empty (line))
break;
- if (!matrix_read_line (read, reader, &line))
+ if (!matrix_read_line (read, reader, &line, &line_start))
return;
dfm_forward_record (reader);
}
}
else
{
- if (!matrix_read_line (read, reader, &line))
+ if (!matrix_read_line (read, reader, &line, &line_start))
return;
size_t fields_per_line = (read->c2 - read->c1) / read->w;
int f = x % fields_per_line;
p = ss_substr (line, read->w * f, read->w);
}
- matrix_read_set_field (read, reader, m, p, y, x);
+ matrix_read_set_field (read, reader, m, p, y, x, line_start);
}
if (read->w)
struct matrix_cmd *cmd = xmalloc (sizeof *cmd);
*cmd = (struct matrix_cmd) {
.type = MCMD_WRITE,
- .write = { .format = FMT_F },
};
struct file_handle *fh = NULL;
int by = 0;
int repetitions = 0;
int record_width = 0;
- bool seen_format = false;
+ enum fmt_type format = FMT_F;
+ bool has_format = false;
while (lex_match (s->lexer, T_SLASH))
{
if (lex_match_id (s->lexer, "OUTFILE"))
write->hold = true;
else if (lex_match_id (s->lexer, "FORMAT"))
{
- if (seen_format)
+ if (has_format || write->format)
{
lex_sbc_only_once ("FORMAT");
goto error;
}
- seen_format = true;
lex_match (s->lexer, T_EQUALS);
{
repetitions = atoi (p);
p += strspn (p, "0123456789");
- if (!fmt_from_name (p, &write->format))
+ if (!fmt_from_name (p, &format))
{
lex_error (s->lexer, _("Unknown format %s."), p);
goto error;
}
+ has_format = true;
lex_get (s->lexer);
}
- else if (!fmt_from_name (p, &write->format))
+ else if (fmt_from_name (p, &format))
{
- struct fmt_spec format;
- if (!parse_format_specifier (s->lexer, &format))
+ has_format = true;
+ lex_get (s->lexer);
+ }
+ else
+ {
+ struct fmt_spec spec;
+ if (!parse_format_specifier (s->lexer, &spec))
goto error;
- write->format = format.type;
- write->w = format.w;
- write->d = format.d;
+ write->format = xmemdup (&spec, sizeof spec);
}
}
else
goto error;
}
int w = (repetitions ? record_width / repetitions
- : write->w ? write->w
+ : write->format ? write->format->w
: by);
if (by && w != by)
{
w, by);
goto error;
}
- write->w = w;
+ if (w && !write->format)
+ {
+ write->format = xmalloc (sizeof *write->format);
+ *write->format = (struct fmt_spec) { .type = format, .w = w };
+
+ if (!fmt_check_output (write->format))
+ goto error;
+ };
+
+ if (write->format && fmt_var_width (write->format) > sizeof (double))
+ {
+ char s[FMT_STRING_LEN_MAX + 1];
+ fmt_to_string (write->format, s);
+ msg (SE, _("Format %s is too wide for %zu-byte matrix eleemnts."),
+ s, sizeof (double));
+ goto error;
+ }
+
return cmd;
error:
}
const struct fmt_settings *settings = settings_get_fmt_settings ();
- struct fmt_spec format = {
- .type = write->format,
- .w = write->w ? write->w : 40,
- .d = write->d
- };
struct u8_line *line = write->wf->held;
for (size_t y = 0; y < m->size1; y++)
{
int x0 = write->c1;
for (size_t x = 0; x < nx; x++)
{
- /* XXX string values */
- union value v = { .f = gsl_matrix_get (m, y, x) };
- char *s = (write->w
- ? data_out (&v, NULL, &format, settings)
- : data_out_stretchy (&v, NULL, &format, settings, NULL));
+ char *s;
+ double f = gsl_matrix_get (m, y, x);
+ if (write->format)
+ {
+ union value v;
+ if (fmt_is_numeric (write->format->type))
+ v.f = f;
+ else
+ v.s = (uint8_t *) &f;
+ s = data_out (&v, NULL, write->format, settings);
+ }
+ else
+ {
+ s = xmalloc (DBL_BUFSIZE_BOUND);
+ if (c_dtoastr (s, DBL_BUFSIZE_BOUND, FTOASTR_UPPER_E, 0, f)
+ >= DBL_BUFSIZE_BOUND)
+ abort ();
+ }
size_t len = strlen (s);
int width = u8_width (CHAR_CAST (const uint8_t *, s), len, UTF8);
if (width + x0 > write->c2)
u8_line_put (line, x0, x0 + width, s, len);
free (s);
- x0 += write->w ? write->w : width + 1;
+ x0 += write->format ? write->format->w : width + 1;
}
if (y + 1 >= m->size1 && write->hold)
if (!write->hold)
{
u8_line_destroy (line);
+ free (line);
line = NULL;
}
write->wf->held = line;
- dfm_close_writer (writer);
gsl_matrix_free (m);
}
case MCMD_WRITE:
matrix_expr_destroy (cmd->write.expression);
+ free (cmd->write.format);
break;
case MCMD_GET:
#include <string.h>
#include <unicase.h>
#include <unigbrk.h>
+#include <uniwidth.h>
#include "libpspp/assertion.h"
#include "libpspp/compiler.h"
#include "libpspp/hmapx.h"
#include "libpspp/hash-functions.h"
+#include "libpspp/misc.h"
#include "libpspp/pool.h"
#include "libpspp/str.h"
#include "libpspp/version.h"
return prefix_len + tail_len;
}
+/* Returns the number of display columns that would be occupied by the LENGTH
+ bytes of UTF-8 starting at S. */
+size_t
+utf8_count_columns (const char *s_, size_t length)
+{
+ const uint8_t *s = CHAR_CAST (const uint8_t *, s_);
+
+ size_t columns = 0;
+ for (int ofs = 0; ofs < length; )
+ {
+ ucs4_t uc;
+ ofs += u8_mbtouc (&uc, s + ofs, length - ofs);
+ if (uc != '\t')
+ {
+ int width = uc_width (uc, "UTF-8");
+ if (width > 0)
+ columns += width;
+ }
+ else
+ columns = ROUND_UP (columns + 1, 8);
+ }
+ return columns;
+}
+
+/* Returns the byte offset in LENGTH-byte UTF-8 string S that is N_COLUMNS
+ display columns into the string. */
+size_t
+utf8_columns_to_bytes (const char *s_, size_t length, size_t n_columns)
+{
+ const uint8_t *s = CHAR_CAST (const uint8_t *, s_);
+
+ size_t columns = 0;
+ int ofs;
+ for (ofs = 0; ofs < length && columns < n_columns; )
+ {
+ ucs4_t uc;
+ ofs += u8_mbtouc (&uc, s + ofs, length - ofs);
+ if (uc != '\t')
+ {
+ int width = uc_width (uc, "UTF-8");
+ if (width > 0)
+ columns += width;
+ }
+ else
+ columns = ROUND_UP (columns + 1, 8);
+ }
+ return ofs;
+}
+
/* Returns an allocated, null-terminated string, owned by the caller,
containing as many characters[*] from the beginning of S that would fit
within MAX_LEN bytes if the returned string were to be re-encoded in
size_t utf8_encoding_concat_len (const char *head, const char *tail,
const char *encoding, size_t max_len);
+size_t utf8_count_columns (const char *, size_t);
+size_t utf8_columns_to_bytes (const char *, size_t, size_t n_columns);
+
char *utf8_to_filename (const char *filename);
char *filename_to_utf8 (const char *filename);
#include <unistr.h>
#include "libpspp/cast.h"
+#include "libpspp/i18n.h"
#include "libpspp/message.h"
#include "libpspp/pool.h"
else
return 0;
}
+
+size_t
+ss_utf8_count_columns (struct substring s)
+{
+ return utf8_count_columns (s.string, s.length);
+}
+
+/* Returns a substring of S starting at 0-based display column START and
+ running for N display columns. */
+struct substring
+ss_utf8_columns (struct substring s, size_t start, size_t n)
+{
+ ss_advance (&s, utf8_columns_to_bytes (s.string, s.length, start));
+ s.length = utf8_columns_to_bytes (s.string, s.length, n);
+ return s;
+}
\f
/* Initializes ST as an empty string. */
void
ucs4_t ss_get_mb (struct substring *);
ucs4_t ss_at_mb (struct substring, size_t ofs);
int ss_at_mblen (struct substring, size_t ofs);
+size_t ss_utf8_count_columns (struct substring);
+struct substring ss_utf8_columns (struct substring, size_t start, size_t n);
\f
/* Variable length strings. */
5 6
78 89
10 11
+$1 $2 3
+4 $5 6
+$1 $2 $3 4
+ $5$6 $78
+1% 2% 3% 4
+ 56% 7%8
+abcdefghijkl
+ABCDEFGHIJKL
])
AT_DATA([matrix2.txt], [dnl
2, 3, 5, 7
READ x/SIZE={2,6}/FIELD=1 TO 20 BY 5.
PRINT x.
+READ x/SIZE={2,3}/FIELD=1 TO 20/FORMAT=DOLLAR.
+PRINT x.
+READ x/SIZE={2,4}/FIELD=1 TO 20/FORMAT=DOLLAR5.1.
+PRINT x.
+READ x/SIZE={2,4}/FIELD=1 TO 12/FORMAT='4PCT'.
+PRINT x.
+READ x/SIZE={2,4}/FIELD=1 TO 12/FORMAT='4A'.
+PRINT x/FORMAT=A3.
COMPUTE y={}.
LOOP IF NOT EOF('matrix2.txt').
1 2 3 4 5 6
7 8 8 9 10 11
+x
+ 1 2 3
+ 4 5 6
+
+x
+ 1 2 3 4
+ 5 6 7 8
+
+x
+ 1 2 3 4
+ 5 6 7 8
+
+x
+ abc def ghi jkl
+ ABC DEF GHI JKL
+
y
2 3 5 7
11 13 17 19
COMPUTE x={1,2,3}.
READ x(:,:)/FIELD=1 TO 10/SIZE={2,2}/FILE='matrix.txt'.
READ x/FIELD=1 TO 10/SIZE={1,3}/FILE='matrix.txt'/MODE=SYMMETRIC.
+READ x/FIELD=1 TO 10/SIZE=2/FILE='matrix.txt'.
END MATRIX.
])
-AT_DATA([matrix.txt], [])
+AT_DATA([matrix.txt], [dnl
+xyzzy
+.
+])
AT_CHECK([pspp matrix.sps], [1], [dnl
matrix.sps:2.6: error: READ: Syntax error at `!': expecting identifier.
matrix.sps:29: error: MATRIX: Cannot read non-square 1×3 matrix using READ with
MODE=SYMMETRIC.
+
+matrix.txt:1.1-1.4: warning: Error reading "xyzzy" as format F for matrix row
+1, column 1: Field contents are not numeric.
+
+matrix.txt:2.1: warning: Error reading "." as format F for matrix row 2, column
+1: Matrix data may not contain missing value.
])
AT_CLEANUP
+AT_SETUP([MATRIX - WRITE])
+AT_DATA([matrix.sps], [dnl
+MATRIX.
+WRITE {1.5, 2; 3, 4.12345}/OUTFILE='matrix.txt'/FIELD=1 TO 80.
+WRITE {1.5, 2; 3, 4.12345}/OUTFILE='matrix.txt'/FIELD=1 TO 5.
+WRITE {1, 2; 3, 4}/OUTFILE='matrix.txt'/FIELD=1 TO 80 BY 5.
+WRITE {1, 2; 3, 4}/OUTFILE='matrix.txt'/FIELD=1 TO 80/FORMAT=F8.2.
+WRITE {1, 2; 3, 4}/OUTFILE='matrix.txt'/FIELD=1 TO 80/FORMAT=E.
+WRITE {1, 2; 3, 4}/OUTFILE='matrix.txt'/FIELD=1 TO 10 BY 10/FORMAT=E.
+WRITE "abcdefhi"/OUTFILE='matrix.txt'/FIELD=1 TO 80/FORMAT=A8.
+WRITE "abcdefhi"/OUTFILE='matrix.txt'/FIELD=1 TO 80/FORMAT=A4.
+WRITE "abcdefhi"/OUTFILE='matrix.txt'/FIELD=1 TO 80/FORMAT=AHEX12.
+END MATRIX.
+])
+AT_CHECK([pspp matrix.sps])
+AT_CHECK([cat matrix.txt], [0], [dnl
+ 1.5 2
+ 3 4.12345
+ 1.5 2
+ 3
+ 4.12345
+ 1 2
+ 3 4
+ 1.00 2.00
+ 3.00 4.00
+ 1 2
+ 3 4
+ 1.E+000
+ 2.E+000
+ 3.E+000
+ 4.E+000
+ abcdefhi
+ abcd
+ 616263646566
+])
+AT_CLEANUP
+
+AT_SETUP([MATRIX - WRITE - negative])
+AT_DATA([matrix.sps], [dnl
+MATRIX.
+WRITE !.
+WRITE 1/OUTFILE=!.
+WRITE 1/ENCODING=!.
+WRITE 1/FIELD=!.
+WRITE 1/FIELD=1 !.
+WRITE 1/FIELD=1 TO 0.
+WRITE 1/FIELD=1 TO 10 BY 20.
+WRITE 1/FIELD=1 TO 10 BY 6.
+WRITE 1/MODE=TRAPEZOIDAL.
+WRITE 1/FORMAT=F5/FORMAT=F5.
+WRITE 1/FORMAT='5ASDF'.
+WRITE 1/FORMAT=ASDF5.
+WRITE 1/!.
+WRITE 1.
+WRITE 1/FIELD=1 TO 10.
+WRITE 1/FIELD=1 TO 10/OUTFILE='matrix.txt'/FORMAT='15F'.
+WRITE 1/FIELD=1 TO 10 BY 5/OUTFILE='matrix.txt'/FORMAT='5F'.
+WRITE 1/FIELD=1 TO 10 BY 5/OUTFILE='matrix.txt'/FORMAT=E.
+WRITE 1/FIELD=1 TO 10/OUTFILE='matrix.txt'/FORMAT=A9.
+WRITE {1,2}/FIELD=1 TO 10/OUTFILE='matrix.txt'/MODE=TRIANGULAR.
+END MATRIX.
+])
+AT_CHECK([pspp matrix.sps], [1], [dnl
+matrix.sps:2.7: error: WRITE: Syntax error at `!'.
+
+matrix.sps:3.17: error: WRITE: Syntax error at `!': expecting a file name or
+handle name.
+
+matrix.sps:4.18: error: WRITE: Syntax error at `!': expecting string.
+
+matrix.sps:5.15: error: WRITE: Syntax error at `!': Expected positive integer
+for FIELD.
+
+matrix.sps:6.17: error: WRITE: Syntax error at `!': expecting `TO'.
+
+matrix.sps:7.20: error: WRITE: Syntax error at `0': Expected positive integer
+for TO.
+
+matrix.sps:8.26-8.27: error: WRITE: Syntax error at `20': Expected integer
+between 1 and 10 for BY.
+
+matrix.sps:9: error: WRITE: BY 6 does not evenly divide record width 10.
+
+matrix.sps:10.14-10.24: error: WRITE: Syntax error at `TRAPEZOIDAL': expecting
+RECTANGULAR or TRIANGULAR.
+
+matrix.sps:11: error: WRITE: Subcommand FORMAT may only be specified once.
+
+matrix.sps:12.16-12.22: error: WRITE: Syntax error at `'5ASDF'': Unknown format
+ASDF.
+
+matrix.sps:13: error: WRITE: Unknown format type `ASDF'.
+
+matrix.sps:14.9: error: WRITE: Syntax error at `!': expecting OUTFILE, FIELD,
+MODE, HOLD, or FORMAT.
+
+matrix.sps:15: error: WRITE: Required subcommand FIELD was not specified.
+
+matrix.sps:16: error: WRITE: Required subcommand OUTFILE was not specified.
+
+matrix.sps:17: error: WRITE: 15 repetitions cannot fit in record width 10.
+
+matrix.sps:18: error: WRITE: FORMAT specifies 5 repetitions with record width
+10, which implies field width 2, but BY specifies field width 5.
+
+matrix.sps:19: error: WRITE: Output format E5.0 specifies width 5, but E
+requires a width between 6 and 40.
+
+matrix.sps:20: error: WRITE: Format A9 is too wide for 8-byte matrix eleemnts.
+
+matrix.sps:21: error: MATRIX: WRITE with MODE=TRIANGULAR requires a square
+matrix but the matrix to be written has dimensions 1×2.
+])
+AT_CLEANUP
\ No newline at end of file