/* Common configuration for all MSAVEs. */
struct msg_location *location; /* Range of lines for first MSAVE. */
struct file_handle *outfile; /* Output file for all the MSAVEs. */
+ struct msg_location *outfile_location;
struct string_array variables; /* VARIABLES subcommand. */
+ struct msg_location *variables_location;
struct string_array fnames; /* FNAMES subcommand. */
+ struct msg_location *fnames_location;
struct string_array snames; /* SNAMES subcommand. */
+ struct msg_location *snames_location;
/* Collects and owns factors and splits. The individual msave_command
structs point to these but do not own them. (This is because factors
if (bf != floor (bf) || bf <= LONG_MIN || bf > LONG_MAX)
{
msg_at (SE, matrix_expr_location (e->subs[1]),
- _("Exponent %.1f in matrix multiplication is non-integer "
+ _("Exponent %.1f in matrix exponentiation is non-integer "
"or outside the valid range."), bf);
return NULL;
}
{
if (!lvalue->var)
{
- msg (SE, _("Undefined variable %s."), lex_tokcstr (s->lexer));
+ lex_error (s->lexer, _("Undefined variable %s."),
+ lex_tokcstr (s->lexer));
goto error;
}
struct matrix_get
{
+ struct lexer *lexer;
struct matrix_lvalue *dst;
struct dataset *dataset;
struct file_handle *file;
if (lex_token (s->lexer) != T_SLASH && lex_token (s->lexer) != T_ENDCMD)
{
- size_t depth = 0;
- for (size_t i = 0; ; i++)
- {
- enum token_type t = lex_next_token (s->lexer, i);
- if (t == T_LPAREN || t == T_LBRACK || t == T_LCURLY)
- depth++;
- else if ((t == T_RPAREN || t == T_RBRACK || t == T_RCURLY) && depth)
- depth--;
- else if ((t == T_SLASH && !depth) || t == T_ENDCMD || t == T_STOP)
- {
- if (i > 0)
- cmd->print.title = lex_next_representation (s->lexer, 0, i - 1);
- break;
- }
- }
-
+ int start_ofs = lex_ofs (s->lexer);
cmd->print.expression = matrix_parse_exp (s);
if (!cmd->print.expression)
goto error;
+ cmd->print.title = lex_ofs_representation (s->lexer, start_ofs,
+ lex_ofs (s->lexer) - 1);
}
while (lex_match (s->lexer, T_SLASH))
{
if (!s->in_loop)
{
- msg (SE, _("BREAK not inside LOOP."));
+ lex_next_error (s->lexer, -1, -1, _("BREAK not inside LOOP."));
return NULL;
}
cmd->release.vars[cmd->release.n_vars++] = var;
}
else
- lex_error (s->lexer, _("Variable name expected."));
+ lex_error (s->lexer, _("Syntax error expecting variable name."));
lex_get (s->lexer);
if (!lex_match (s->lexer, T_COMMA))
for (size_t i = 0; i < nv.size; i++)
{
char *name = trimmed_string (gsl_vector_get (&nv, i));
- if (dict_id_is_valid (dict, name, true))
+ char *error = dict_id_is_valid__ (dict, name);
+ if (!error)
string_array_append_nocopy (&names, name);
else
- ok = false;
+ {
+ msg_at (SE, save_location, "%s", error);
+ free (error);
+ ok = false;
+ }
}
}
gsl_matrix_free (nm);
if (!save->expression)
goto error;
+ int names_start = 0;
+ int names_end = 0;
while (lex_match (s->lexer, T_SLASH))
{
if (lex_match_id (s->lexer, "OUTFILE"))
{
lex_match (s->lexer, T_EQUALS);
matrix_expr_destroy (names);
+ names_start = lex_ofs (s->lexer);
names = matrix_parse_exp (s);
+ names_end = lex_ofs (s->lexer) - 1;
if (!names)
goto error;
}
fh = fh_ref (s->prev_save_file);
else
{
- lex_sbc_missing ("OUTFILE");
+ lex_sbc_missing (s->lexer, "OUTFILE");
goto error;
}
}
if (variables.n && names)
{
- msg (SW, _("VARIABLES and NAMES both specified; ignoring NAMES."));
+ lex_ofs_msg (s->lexer, SW, names_start, names_end,
+ _("Ignoring NAMES because VARIABLES was also specified."));
matrix_expr_destroy (names);
names = NULL;
}
if (!read->dst)
goto error;
+ int by_ofs = 0;
+ int format_ofs = 0;
+ int record_width_start = 0, record_width_end = 0;
+
int by = 0;
int repetitions = 0;
int record_width = 0;
{
lex_match (s->lexer, T_EQUALS);
+ record_width_start = lex_ofs (s->lexer);
if (!lex_force_int_range (s->lexer, "FIELD", 1, INT_MAX))
goto error;
read->c1 = lex_integer (s->lexer);
|| !lex_force_int_range (s->lexer, "TO", read->c1, INT_MAX))
goto error;
read->c2 = lex_integer (s->lexer) + 1;
+ record_width_end = lex_ofs (s->lexer);
lex_get (s->lexer);
record_width = read->c2 - read->c1;
read->c2 - read->c1))
goto error;
by = lex_integer (s->lexer);
+ by_ofs = lex_ofs (s->lexer);
+ int field_end = lex_ofs (s->lexer);
lex_get (s->lexer);
if (record_width % by)
{
- msg (SE, _("BY %d does not evenly divide record width %d."),
- by, record_width);
+ lex_ofs_error (
+ s->lexer, record_width_start, field_end,
+ _("Field width %d does not evenly divide record width %d."),
+ by, record_width);
+ lex_ofs_msg (s->lexer, SN, record_width_start, record_width_end,
+ _("This syntax designates the record width."));
+ lex_ofs_msg (s->lexer, SN, by_ofs, by_ofs,
+ _("This syntax specifies the field width."));
goto error;
}
}
{
if (seen_format)
{
- lex_sbc_only_once ("FORMAT");
+ lex_sbc_only_once (s->lexer, "FORMAT");
goto error;
}
seen_format = true;
if (lex_token (s->lexer) != T_STRING && !lex_force_id (s->lexer))
goto error;
+ format_ofs = lex_ofs (s->lexer);
const char *p = lex_tokcstr (s->lexer);
if (c_isdigit (p[0]))
{
if (!read->c1)
{
- lex_sbc_missing ("FIELD");
+ lex_sbc_missing (s->lexer, "FIELD");
goto error;
}
{
msg (SE, _("SIZE is required for reading data into a full matrix "
"(as opposed to a submatrix)."));
+ msg_at (SN, read->dst->var_location,
+ _("This expression designates a full matrix."));
goto error;
}
fh = fh_ref (s->prev_read_file);
else
{
- lex_sbc_missing ("FILE");
+ lex_sbc_missing (s->lexer, "FILE");
goto error;
}
}
{
msg (SE, _("%d repetitions cannot fit in record width %d."),
repetitions, record_width);
+ lex_ofs_msg (s->lexer, SN, format_ofs, format_ofs,
+ _("This syntax designates the number of repetitions."));
+ lex_ofs_msg (s->lexer, SN, record_width_start, record_width_end,
+ _("This syntax designates the record width."));
goto error;
}
int w = (repetitions ? record_width / repetitions
: by);
if (by && w != by)
{
+ msg (SE, _("This command specifies two different field widths."));
if (repetitions)
- msg (SE, _("FORMAT specifies %d repetitions with record width %d, "
- "which implies field width %d, "
- "but BY specifies field width %d."),
- repetitions, record_width, w, by);
+ {
+ lex_ofs_msg (s->lexer, SN, format_ofs, format_ofs,
+ ngettext ("This syntax specifies %d repetition.",
+ "This syntax specifies %d repetitions.",
+ repetitions),
+ repetitions);
+ lex_ofs_msg (s->lexer, SN, record_width_start, record_width_end,
+ _("This syntax designates record width %d, "
+ "which divided by %d repetitions implies "
+ "field width %d."),
+ record_width, repetitions, w);
+ }
else
- msg (SE, _("FORMAT specifies field width %d but BY specifies %d."),
- w, by);
+ lex_ofs_msg (s->lexer, SN, format_ofs, format_ofs,
+ _("This syntax specifies field width %d."), w);
+
+ lex_ofs_msg (s->lexer, SN, by_ofs, by_ofs,
+ _("This syntax specifies field width %d."), by);
goto error;
}
read->w = w;
if (!write->expression)
goto error;
+ int by_ofs = 0;
+ int format_ofs = 0;
+ int record_width_start = 0, record_width_end = 0;
+
int by = 0;
int repetitions = 0;
int record_width = 0;
{
lex_match (s->lexer, T_EQUALS);
+ record_width_start = lex_ofs (s->lexer);
+
if (!lex_force_int_range (s->lexer, "FIELD", 1, INT_MAX))
goto error;
write->c1 = lex_integer (s->lexer);
|| !lex_force_int_range (s->lexer, "TO", write->c1, INT_MAX))
goto error;
write->c2 = lex_integer (s->lexer) + 1;
+ record_width_end = lex_ofs (s->lexer);
lex_get (s->lexer);
record_width = write->c2 - write->c1;
if (!lex_force_int_range (s->lexer, "BY", 1,
write->c2 - write->c1))
goto error;
+ by_ofs = lex_ofs (s->lexer);
+ int field_end = lex_ofs (s->lexer);
by = lex_integer (s->lexer);
lex_get (s->lexer);
if (record_width % by)
{
- msg (SE, _("BY %d does not evenly divide record width %d."),
- by, record_width);
+ lex_ofs_error (
+ s->lexer, record_width_start, field_end,
+ _("Field width %d does not evenly divide record width %d."),
+ by, record_width);
+ lex_ofs_msg (s->lexer, SN, record_width_start, record_width_end,
+ _("This syntax designates the record width."));
+ lex_ofs_msg (s->lexer, SN, by_ofs, by_ofs,
+ _("This syntax specifies the field width."));
goto error;
}
}
{
if (has_format || write->format)
{
- lex_sbc_only_once ("FORMAT");
+ lex_sbc_only_once (s->lexer, "FORMAT");
goto error;
}
if (lex_token (s->lexer) != T_STRING && !lex_force_id (s->lexer))
goto error;
+ format_ofs = lex_ofs (s->lexer);
const char *p = lex_tokcstr (s->lexer);
if (c_isdigit (p[0]))
{
if (!write->c1)
{
- lex_sbc_missing ("FIELD");
+ lex_sbc_missing (s->lexer, "FIELD");
goto error;
}
fh = fh_ref (s->prev_write_file);
else
{
- lex_sbc_missing ("OUTFILE");
+ lex_sbc_missing (s->lexer, "OUTFILE");
goto error;
}
}
*/
if (repetitions > record_width)
{
- msg (SE, _("%d repetitions cannot fit in record width %d."),
- repetitions, record_width);
+ lex_ofs_msg (s->lexer, SN, format_ofs, format_ofs,
+ _("This syntax designates the number of repetitions."));
+ lex_ofs_msg (s->lexer, SN, record_width_start, record_width_end,
+ _("This syntax designates the record width."));
goto error;
}
int w = (repetitions ? record_width / repetitions
: by);
if (by && w != by)
{
+ msg (SE, _("This command specifies two different field widths."));
if (repetitions)
- msg (SE, _("FORMAT specifies %d repetitions with record width %d, "
- "which implies field width %d, "
- "but BY specifies field width %d."),
- repetitions, record_width, w, by);
+ {
+ lex_ofs_msg (s->lexer, SN, format_ofs, format_ofs,
+ ngettext ("This syntax specifies %d repetition.",
+ "This syntax specifies %d repetitions.",
+ repetitions),
+ repetitions);
+ lex_ofs_msg (s->lexer, SN, record_width_start, record_width_end,
+ _("This syntax designates record width %d, "
+ "which divided by %d repetitions implies "
+ "field width %d."),
+ record_width, repetitions, w);
+ }
else
- msg (SE, _("FORMAT specifies field width %d but BY specifies %d."),
- w, by);
+ lex_ofs_msg (s->lexer, SN, format_ofs, format_ofs,
+ _("This syntax specifies field width %d."), w);
+
+ lex_ofs_msg (s->lexer, SN, by_ofs, by_ofs,
+ _("This syntax specifies field width %d."), by);
goto error;
}
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;
- };
+ char *error = fmt_check_output__ (write->format);
+ if (error)
+ {
+ msg (SE, "%s", error);
+ free (error);
+
+ if (has_format)
+ lex_ofs_msg (s->lexer, SN, format_ofs, format_ofs,
+ _("This syntax specifies format %s."),
+ fmt_name (format));
+
+ if (repetitions)
+ {
+ lex_ofs_msg (s->lexer, SN, format_ofs, format_ofs,
+ ngettext ("This syntax specifies %d repetition.",
+ "This syntax specifies %d repetitions.",
+ repetitions),
+ repetitions);
+ lex_ofs_msg (s->lexer, SN, record_width_start, record_width_end,
+ _("This syntax designates record width %d, "
+ "which divided by %d repetitions implies "
+ "field width %d."),
+ record_width, repetitions, w);
+ }
+
+ if (by)
+ lex_ofs_msg (s->lexer, SN, by_ofs, by_ofs,
+ _("This syntax specifies field width %d."), by);
+
+ 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 elements."),
- s, sizeof (double));
+ char fs[FMT_STRING_LEN_MAX + 1];
+ fmt_to_string (write->format, fs);
+ lex_ofs_error (s->lexer, format_ofs, format_ofs,
+ _("Format %s is too wide for %zu-byte matrix elements."),
+ fs, sizeof (double));
goto error;
}
*cmd = (struct matrix_command) {
.type = MCMD_GET,
.get = {
+ .lexer = s->lexer,
.dataset = s->dataset,
.user = { .treatment = MGET_ERROR },
.system = { .treatment = MGET_ERROR },
if (get->n_vars)
{
- lex_sbc_only_once ("VARIABLES");
+ lex_sbc_only_once (s->lexer, "VARIABLES");
goto error;
}
if (get->n_vars)
{
- if (!var_syntax_evaluate (get->vars, get->n_vars, dict,
+ if (!var_syntax_evaluate (get->lexer, get->vars, get->n_vars, dict,
&vars, &n_vars, PV_NUMERIC))
return;
}
{
if (dict_get_n_vars (dataset_dict (dataset)) == 0)
{
- msg_at (ME, cmd->location,
+ msg_at (SE, cmd->location,
_("The %s command cannot read an empty active file."),
command_name);
return false;
static bool
variables_changed (const char *keyword,
- const struct string_array *new,
- const struct string_array *old)
-{
- if (new->n)
- {
- if (!old->n)
- {
- msg (SE, _("%s may only be specified on MSAVE if it was specified "
- "on the first MSAVE within MATRIX."), keyword);
+ const struct string_array *new_vars,
+ const struct msg_location *new_vars_location,
+ const struct msg_location *new_location,
+ const struct string_array *old_vars,
+ const struct msg_location *old_vars_location,
+ const struct msg_location *old_location)
+{
+ if (new_vars->n)
+ {
+ if (!old_vars->n)
+ {
+ msg_at (SE, new_location,
+ _("%s may only be specified on MSAVE if it was specified "
+ "on the first MSAVE within MATRIX."), keyword);
+ msg_at (SN, old_location,
+ _("The first MSAVE in MATRIX did not specify %s."),
+ keyword);
+ msg_at (SN, new_vars_location,
+ _("This is the specification of %s on a later MSAVE."),
+ keyword);
return true;
}
- else if (!string_array_equal_case (old, new))
- {
- msg (SE, _("%s must specify the same variables each time within "
- "a given MATRIX."), keyword);
+ if (!string_array_equal_case (old_vars, new_vars))
+ {
+ msg_at (SE, new_location,
+ _("%s must specify the same variables on each MSAVE "
+ "within a given MATRIX."), keyword);
+ msg_at (SE, old_vars_location,
+ _("This is the specification of %s on the first MSAVE."),
+ keyword);
+ msg_at (SE, new_vars_location,
+ _("This is a different specification of %s on a later MSAVE."),
+ keyword);
return true;
}
}
const struct msave_common *new)
{
if (new->outfile && !fh_equal (old->outfile, new->outfile))
- msg (SE, _("OUTFILE must name the same file on each MSAVE "
- "within a single MATRIX command."));
- else if (variables_changed ("VARIABLES", &new->variables, &old->variables)
- || variables_changed ("FNAMES", &new->fnames, &old->fnames)
- || variables_changed ("SNAMES", &new->snames, &old->snames))
- msg_at (SN, old->location,
- _("This is the location of the first MSAVE command."));
- else
+ {
+ msg (SE, _("OUTFILE must name the same file on each MSAVE "
+ "within a single MATRIX command."));
+ msg_at (SN, old->outfile_location,
+ _("This is the OUTFILE on the first MSAVE command."));
+ msg_at (SN, new->outfile_location,
+ _("This is the OUTFILE on a later MSAVE command."));
+ return false;
+ }
+
+ if (!variables_changed ("VARIABLES",
+ &new->variables, new->variables_location, new->location,
+ &old->variables, old->variables_location, old->location)
+ && !variables_changed ("FNAMES",
+ &new->fnames, new->fnames_location, new->location,
+ &old->fnames, old->fnames_location, old->location)
+ && !variables_changed ("SNAMES",
+ &new->snames, new->snames_location, new->location,
+ &old->snames, old->snames_location, old->location))
return false;
return true;
{
msg_location_destroy (common->location);
fh_unref (common->outfile);
+ msg_location_destroy (common->outfile_location);
string_array_destroy (&common->variables);
+ msg_location_destroy (common->variables_location);
string_array_destroy (&common->fnames);
+ msg_location_destroy (common->fnames_location);
string_array_destroy (&common->snames);
+ msg_location_destroy (common->snames_location);
for (size_t i = 0; i < common->n_factors; i++)
matrix_expr_destroy (common->factors[i]);
}
static bool
-parse_var_names (struct lexer *lexer, struct string_array *sa)
+parse_var_names (struct lexer *lexer, struct string_array *sa,
+ struct msg_location **locationp)
{
lex_match (lexer, T_EQUALS);
string_array_clear (sa);
+ msg_location_destroy (*locationp);
+ *locationp = NULL;
struct dictionary *dict = dict_create (get_default_encoding ());
char **names;
size_t n_names;
+ int start_ofs = lex_ofs (lexer);
bool ok = parse_DATA_LIST_vars (lexer, dict, &names, &n_names,
PV_NO_DUPLICATE | PV_NO_SCRATCH);
+ int end_ofs = lex_ofs (lexer) - 1;
dict_unref (dict);
if (ok)
if (ss_equals_case (ss_cstr (names[i]), ss_cstr ("ROWTYPE_"))
|| ss_equals_case (ss_cstr (names[i]), ss_cstr ("VARNAME_")))
{
- msg (SE, _("Variable name %s is reserved."), names[i]);
+ lex_ofs_error (lexer, start_ofs, end_ofs,
+ _("Variable name %s is reserved."), names[i]);
for (size_t j = 0; j < n_names; j++)
free (names[i]);
free (names);
return false;
}
- string_array_clear (sa);
sa->strings = names;
sa->n = sa->allocated = n_names;
+ *locationp = lex_ofs_location (lexer, start_ofs, end_ofs);
}
return ok;
}
lex_match (s->lexer, T_EQUALS);
fh_unref (common->outfile);
+ int start_ofs = lex_ofs (s->lexer);
common->outfile = fh_parse (s->lexer, FH_REF_FILE, NULL);
if (!common->outfile)
goto error;
+ msg_location_destroy (common->outfile_location);
+ common->outfile_location = lex_ofs_location (s->lexer, start_ofs,
+ lex_ofs (s->lexer) - 1);
}
else if (lex_match_id (s->lexer, "VARIABLES"))
{
- if (!parse_var_names (s->lexer, &common->variables))
+ if (!parse_var_names (s->lexer, &common->variables,
+ &common->variables_location))
goto error;
}
else if (lex_match_id (s->lexer, "FNAMES"))
{
- if (!parse_var_names (s->lexer, &common->fnames))
+ if (!parse_var_names (s->lexer, &common->fnames,
+ &common->fnames_location))
goto error;
}
else if (lex_match_id (s->lexer, "SNAMES"))
{
- if (!parse_var_names (s->lexer, &common->snames))
+ if (!parse_var_names (s->lexer, &common->snames,
+ &common->snames_location))
goto error;
}
else if (lex_match_id (s->lexer, "SPLIT"))
}
if (!msave->rowtype)
{
- lex_sbc_missing ("TYPE");
+ lex_sbc_missing (s->lexer, "TYPE");
goto error;
}
{
if (common->fnames.n && !factors)
{
- msg (SE, _("FNAMES requires FACTOR."));
+ msg_at (SE, common->fnames_location, _("FNAMES requires FACTOR."));
goto error;
}
if (common->snames.n && !splits)
{
- msg (SE, _("SNAMES requires SPLIT."));
+ msg_at (SE, common->snames_location, _("SNAMES requires SPLIT."));
goto error;
}
if (!common->outfile)
{
- lex_sbc_missing ("OUTFILE");
+ lex_sbc_missing (s->lexer, "OUTFILE");
goto error;
}
common->location = lex_ofs_location (s->lexer, start_ofs,
}
static struct dictionary *
-msave_create_dict (const struct msave_common *common,
- const struct msg_location *location)
+msave_create_dict (const struct msave_common *common)
{
struct dictionary *dict = dict_create (get_default_encoding ());
const char *dup_factor = msave_add_vars (dict, &common->fnames);
if (dup_factor)
{
- msg_at (SE, location, _("Duplicate or invalid FACTOR variable name %s."),
+ msg_at (SE, common->fnames_location,
+ _("Duplicate or invalid FACTOR variable name %s."),
dup_factor);
goto error;
}
const char *dup_var = msave_add_vars (dict, &common->variables);
if (dup_var)
{
- msg_at (SE, location, _("Duplicate or invalid variable name %s."),
+ msg_at (SE, common->variables_location,
+ _("Duplicate or invalid variable name %s."),
dup_var);
goto error;
}
if (!common->writer)
{
- struct dictionary *dict = msave_create_dict (common, cmd->location);
+ struct dictionary *dict = msave_create_dict (common);
if (!dict)
goto error;
if (lex_at_phrase (s->lexer, "END MATRIX"))
{
- msg (SE, _("Premature END MATRIX within %s."), command_name);
+ lex_next_error (s->lexer, 0, 1,
+ _("Premature END MATRIX within %s."), command_name);
return false;
}