/* PSPP - a program for statistical analysis.
- Copyright (C) 1997-9, 2000, 2009, 2010 Free Software Foundation, Inc.
+ Copyright (C) 1997-9, 2000, 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include <errno.h>
#include "data/casereader.h"
+#include "data/dataset.h"
#include "data/dictionary.h"
-#include "data/procedure.h"
+#include "data/session.h"
#include "data/settings.h"
#include "data/variable.h"
#include "language/lexer/command-name.h"
#include "language/lexer/lexer.h"
-#include "language/prompt.h"
#include "libpspp/assertion.h"
#include "libpspp/compiler.h"
+#include "libpspp/i18n.h"
#include "libpspp/message.h"
#include "libpspp/str.h"
-#include "libpspp/getl.h"
-#include "output/text-item.h"
+#include "output/driver.h"
+#include "output/output-item.h"
-#include "xalloc.h"
#include "xmalloca.h"
#include "gettext.h"
static inline bool
cmd_result_is_valid (enum cmd_result result)
{
- return (result == CMD_SUCCESS || result == CMD_EOF || result == CMD_FINISH
- || (result >= CMD_PRIVATE_FIRST && result <= CMD_PRIVATE_LAST)
- || result == CMD_FAILURE || result == CMD_NOT_IMPLEMENTED
- || result == CMD_CASCADING_FAILURE);
+ switch (result)
+ {
+ case CMD_SUCCESS:
+ case CMD_EOF:
+ case CMD_FINISH:
+ case CMD_FAILURE:
+ case CMD_NOT_IMPLEMENTED:
+ case CMD_CASCADING_FAILURE:
+ return true;
+
+ default:
+ return false;
+ }
}
/* Returns true if RESULT indicates success,
/* Command processing states. */
enum states
{
- S_INITIAL = 0x01, /* Allowed before active file defined. */
- S_DATA = 0x02, /* Allowed after active file defined. */
- S_INPUT_PROGRAM = 0x04, /* Allowed in INPUT PROGRAM. */
- S_FILE_TYPE = 0x08, /* Allowed in FILE TYPE. */
- S_ANY = 0x0f /* Allowed anywhere. */
+ S_INITIAL = 1 << CMD_STATE_INITIAL,
+ S_DATA = 1 << CMD_STATE_DATA,
+ S_INPUT_PROGRAM = 1 << CMD_STATE_INPUT_PROGRAM,
+ S_FILE_TYPE = 1 << CMD_STATE_FILE_TYPE,
+ S_NESTED_DATA = 1 << CMD_STATE_NESTED_DATA,
+ S_NESTED_INPUT_PROGRAM = 1 << CMD_STATE_NESTED_INPUT_PROGRAM,
+
+ S_NESTED_ANY = S_NESTED_DATA | S_NESTED_INPUT_PROGRAM,
+ S_ANY = S_INITIAL | S_DATA | S_INPUT_PROGRAM | S_FILE_TYPE | S_NESTED_ANY,
};
/* Other command requirements. */
enum flags
{
- F_ENHANCED = 0x10, /* Allowed only in enhanced syntax mode. */
- F_TESTING = 0x20, /* Allowed only in testing mode. */
- F_KEEP_FINAL_TOKEN = 0x40,/* Don't skip final token in command name. */
- F_ABBREV = 0x80 /* Not a candidate for name completion. */
+ F_ENHANCED = 1 << 0, /* Allowed only in enhanced syntax mode. */
+ F_TESTING = 1 << 1, /* Allowed only in testing mode. */
+ F_ABBREV = 1 << 2 /* Not a candidate for name completion. */
};
/* A single command. */
#undef DEF_CMD
#undef UNIMPL_CMD
-static const size_t command_cnt = sizeof commands / sizeof *commands;
+static const size_t n_commands = sizeof commands / sizeof *commands;
static bool in_correct_state (const struct command *, enum cmd_state);
-static bool report_state_mismatch (const struct command *, enum cmd_state);
+static void report_state_mismatch (const struct command *, enum cmd_state);
static void set_completion_state (enum cmd_state);
\f
/* Command parser. */
-static const struct command *parse_command_name (struct lexer *lexer);
+static const struct command *parse_command_name (struct lexer *,
+ int *n_tokens);
static enum cmd_result do_parse_command (struct lexer *, struct dataset *, enum cmd_state);
/* Parses an entire command, from command name to terminating
cmd_parse_in_state (struct lexer *lexer, struct dataset *ds,
enum cmd_state state)
{
+ struct session *session = dataset_session (ds);
int result;
result = do_parse_command (lexer, ds, state);
+ ds = session_active_dataset (session);
assert (!proc_is_open (ds));
unset_cmd_algorithm ();
- dict_clear_aux (dataset_dict (ds));
if (!dataset_end_of_command (ds))
result = CMD_CASCADING_FAILURE;
{
const struct dictionary *dict = dataset_dict (ds);
return cmd_parse_in_state (lexer, ds,
- proc_has_active_file (ds) &&
- dict_get_var_cnt (dict) > 0 ?
+ dataset_has_source (ds) &&
+ dict_get_n_vars (dict) > 0 ?
CMD_STATE_DATA : CMD_STATE_INITIAL);
}
struct dataset *ds, enum cmd_state state)
{
const struct command *command = NULL;
+ size_t nesting_level = SIZE_MAX;
enum cmd_result result;
- bool opened = false;
+ int n_tokens;
/* Read the command's first token. */
- prompt_set_style (PROMPT_FIRST);
set_completion_state (state);
- lex_get (lexer);
if (lex_token (lexer) == T_STOP)
{
result = CMD_EOF;
goto finish;
}
- prompt_set_style (PROMPT_LATER);
-
/* Parse the command name. */
- command = parse_command_name (lexer);
+ command = parse_command_name (lexer, &n_tokens);
if (command == NULL)
{
result = CMD_FAILURE;
goto finish;
}
- text_item_submit (text_item_create (TEXT_ITEM_COMMAND_OPEN, command->name));
- opened = true;
+
+ nesting_level = output_open_group (group_item_create_nocopy (
+ utf8_to_title (command->name),
+ utf8_to_title (command->name)));
if (command->function == NULL)
{
else
{
/* Execute command. */
+ int i;
+
+ for (i = 0; i < n_tokens; i++)
+ lex_get (lexer);
result = command->function (lexer, ds);
}
assert (cmd_result_is_valid (result));
- finish:
+finish:
if (cmd_result_is_failure (result))
- {
- lex_discard_rest_of_command (lexer);
- if (source_stream_current_error_mode (
- lex_get_source_stream (lexer)) == ERRMODE_STOP )
- {
- msg (MW, _("Error encountered while ERROR=STOP is effective."));
- result = CMD_CASCADING_FAILURE;
- }
- }
+ lex_interactive_reset (lexer);
+ else if (result == CMD_SUCCESS)
+ result = lex_end_of_command (lexer);
- if (opened)
- text_item_submit (text_item_create (TEXT_ITEM_COMMAND_CLOSE,
- command->name));
+ lex_discard_rest_of_command (lexer);
+ if (result != CMD_EOF && result != CMD_FINISH)
+ while (lex_token (lexer) == T_ENDCMD)
+ lex_get (lexer);
+
+ if (nesting_level != SIZE_MAX)
+ output_close_groups (nesting_level);
return result;
}
int missing_words;
command_matcher_init (&cm, s);
- for (cmd = commands; cmd < &commands[command_cnt]; cmd++)
+ for (cmd = commands; cmd < &commands[n_commands]; cmd++)
command_matcher_add (&cm, ss_cstr (cmd->name), CONST_CAST (void *, cmd));
*matchp = command_matcher_get_match (&cm);
return missing_words;
}
-/* Parse the command name and return a pointer to the corresponding
- struct command if successful.
- If not successful, return a null pointer. */
+static bool
+parse_command_word (struct lexer *lexer, struct string *s, int n)
+{
+ bool need_space = ds_last (s) != EOF && ds_last (s) != '-';
+
+ switch (lex_next_token (lexer, n))
+ {
+ case T_DASH:
+ ds_put_byte (s, '-');
+ return true;
+
+ case T_ID:
+ if (need_space)
+ ds_put_byte (s, ' ');
+ ds_put_cstr (s, lex_next_tokcstr (lexer, n));
+ return true;
+
+ case T_POS_NUM:
+ if (lex_next_is_integer (lexer, n))
+ {
+ int integer = lex_next_integer (lexer, n);
+ if (integer >= 0)
+ {
+ if (need_space)
+ ds_put_byte (s, ' ');
+ ds_put_format (s, "%ld", lex_next_integer (lexer, n));
+ return true;
+ }
+ }
+ return false;
+
+ default:
+ return false;
+ }
+}
+
+/* Parses the command name. On success returns a pointer to the corresponding
+ struct command and stores the number of tokens in the command name into
+ *N_TOKENS. On failure, returns a null pointer and stores the number of
+ tokens required to determine that no command name was present into
+ *N_TOKENS. */
static const struct command *
-parse_command_name (struct lexer *lexer)
+parse_command_name (struct lexer *lexer, int *n_tokens)
{
const struct command *command;
int missing_words;
struct string s;
-
- if (lex_token (lexer) == T_EXP
- || lex_token (lexer) == T_ASTERISK
- || lex_token (lexer) == T_LBRACK)
- {
- static const struct command c = { S_ANY, 0, "COMMENT", cmd_comment };
- return &c;
- }
+ int word;
command = NULL;
missing_words = 0;
ds_init_empty (&s);
- for (;;)
+ word = 0;
+ while (parse_command_word (lexer, &s, word))
{
- if (lex_token (lexer) == T_DASH)
- ds_put_byte (&s, '-');
- else if (lex_token (lexer) == T_ID)
- {
- if (!ds_is_empty (&s) && ds_last (&s) != '-')
- ds_put_byte (&s, ' ');
- ds_put_cstr (&s, lex_tokid (lexer));
- }
- else if (lex_is_integer (lexer) && lex_integer (lexer) >= 0)
- {
- if (!ds_is_empty (&s) && ds_last (&s) != '-')
- ds_put_byte (&s, ' ');
- ds_put_format (&s, "%ld", lex_integer (lexer));
- }
- else
- break;
-
missing_words = find_best_match (ds_ss (&s), &command);
if (missing_words <= 0)
break;
-
- lex_get (lexer);
+ word++;
}
if (command == NULL && missing_words > 0)
else
msg (SE, _("Unknown command `%s'."), ds_cstr (&s));
}
- else if (missing_words == 0)
- {
- if (!(command->flags & F_KEEP_FINAL_TOKEN))
- lex_get (lexer);
- }
- else if (missing_words < 0)
- {
- assert (missing_words == -1);
- assert (!(command->flags & F_KEEP_FINAL_TOKEN));
- }
ds_destroy (&s);
+
+ *n_tokens = (word + 1) + missing_words;
return command;
}
static bool
in_correct_state (const struct command *command, enum cmd_state state)
{
- return ((state == CMD_STATE_INITIAL && command->states & S_INITIAL)
- || (state == CMD_STATE_DATA && command->states & S_DATA)
- || (state == CMD_STATE_INPUT_PROGRAM
- && command->states & S_INPUT_PROGRAM)
- || (state == CMD_STATE_FILE_TYPE && command->states & S_FILE_TYPE));
+ return command->states & (1 << state);
}
/* Emits an appropriate error message for trying to invoke
COMMAND in STATE. */
-static bool
+static void
report_state_mismatch (const struct command *command, enum cmd_state state)
{
assert (!in_correct_state (command, state));
- if (state == CMD_STATE_INITIAL || state == CMD_STATE_DATA)
+
+ switch (state)
{
- switch (command->states)
+ case CMD_STATE_INITIAL:
+ case CMD_STATE_DATA:
+ switch ((int) command->states
+ & (S_INITIAL | S_DATA | S_INPUT_PROGRAM | S_FILE_TYPE))
{
/* One allowed state. */
case S_INITIAL:
- msg (SE, _("%s is allowed only before the active file has "
+ msg (SE, _("%s is allowed only before the active dataset has "
"been defined."), command->name);
break;
case S_DATA:
- msg (SE, _("%s is allowed only after the active file has "
+ msg (SE, _("%s is allowed only after the active dataset has "
"been defined."), command->name);
break;
case S_INPUT_PROGRAM:
- msg (SE, _("%s is allowed only inside INPUT PROGRAM."),
- command->name);
+ msg (SE, _("%s is allowed only inside %s."),
+ command->name, "INPUT PROGRAM");
break;
case S_FILE_TYPE:
- msg (SE, _("%s is allowed only inside FILE TYPE."), command->name);
+ msg (SE, _("%s is allowed only inside %s."), command->name, "FILE TYPE");
break;
/* Two allowed states. */
case S_INITIAL | S_DATA:
NOT_REACHED ();
case S_INITIAL | S_INPUT_PROGRAM:
- msg (SE, _("%s is allowed only before the active file has "
- "been defined or inside INPUT PROGRAM."), command->name);
+ msg (SE, _("%s is allowed only before the active dataset has been defined or inside %s."),
+ command->name, "INPUT PROGRAM");
break;
case S_INITIAL | S_FILE_TYPE:
- msg (SE, _("%s is allowed only before the active file has "
- "been defined or inside FILE TYPE."), command->name);
+ msg (SE, _("%s is allowed only before the active dataset has been defined or inside %s."),
+ command->name, "FILE TYPE");
break;
case S_DATA | S_INPUT_PROGRAM:
- msg (SE, _("%s is allowed only after the active file has "
- "been defined or inside INPUT PROGRAM."), command->name);
+ msg (SE, _("%s is allowed only after the active dataset has been defined or inside %s."),
+ command->name, "INPUT PROGRAM");
break;
case S_DATA | S_FILE_TYPE:
- msg (SE, _("%s is allowed only after the active file has "
- "been defined or inside FILE TYPE."), command->name);
+ msg (SE, _("%s is allowed only after the active dataset has been defined or inside %s."),
+ command->name, "FILE TYPE");
break;
case S_INPUT_PROGRAM | S_FILE_TYPE:
- msg (SE, _("%s is allowed only inside INPUT PROGRAM "
- "or inside FILE TYPE."), command->name);
+ msg (SE, _("%s is allowed only inside %s or inside %s."), command->name,
+ "INPUT PROGRAM", "FILE TYPE");
break;
/* Three allowed states. */
case S_DATA | S_INPUT_PROGRAM | S_FILE_TYPE:
- msg (SE, _("%s is allowed only after the active file has "
+ msg (SE, _("%s is allowed only after the active dataset has "
"been defined, inside INPUT PROGRAM, or inside "
"FILE TYPE."), command->name);
break;
case S_INITIAL | S_INPUT_PROGRAM | S_FILE_TYPE:
- msg (SE, _("%s is allowed only before the active file has "
+ msg (SE, _("%s is allowed only before the active dataset has "
"been defined, inside INPUT PROGRAM, or inside "
"FILE TYPE."), command->name);
break;
default:
NOT_REACHED ();
}
- }
- else if (state == CMD_STATE_INPUT_PROGRAM)
- msg (SE, _("%s is not allowed inside %s."), command->name, "INPUT PROGRAM" );
- else if (state == CMD_STATE_FILE_TYPE)
- msg (SE, _("%s is not allowed inside %s."), command->name, "FILE TYPE");
+ break;
+
+ case CMD_STATE_INPUT_PROGRAM:
+ msg (SE, _("%s is not allowed inside %s."),
+ command->name, "INPUT PROGRAM");
+ break;
+
+ case CMD_STATE_FILE_TYPE:
+ msg (SE, _("%s is not allowed inside %s."), command->name, "FILE TYPE");
+ break;
+
+ case CMD_STATE_NESTED_DATA:
+ case CMD_STATE_NESTED_INPUT_PROGRAM:
+ switch ((int) command->states & S_NESTED_ANY)
+ {
+ case 0:
+ msg (SE, _("%s is not allowed inside DO IF or LOOP."), command->name);
+ break;
+
+ case S_NESTED_DATA:
+ msg (SE, _("In INPUT PROGRAM, "
+ "%s is not allowed inside DO IF or LOOP."), command->name);
+ break;
- return false;
+ default:
+ NOT_REACHED ();
+ }
+ break;
+ }
}
\f
/* Command name completion. */
if (*cmd == NULL)
*cmd = commands;
- for (; *cmd < commands + command_cnt; (*cmd)++)
+ for (; *cmd < commands + n_commands; (*cmd)++)
if (!memcasecmp ((*cmd)->name, prefix, strlen (prefix))
&& (!((*cmd)->flags & F_TESTING) || settings_get_testing_mode ())
&& (!((*cmd)->flags & F_ENHANCED) || settings_get_syntax () == ENHANCED)
int
cmd_n_of_cases (struct lexer *lexer, struct dataset *ds)
{
- /* Value for N. */
- int x;
-
- if (!lex_force_int (lexer))
+ if (!lex_force_int_range (lexer, "N OF CASES", 1, LONG_MAX))
return CMD_FAILURE;
- x = lex_integer (lexer);
+ long n = lex_integer (lexer);
lex_get (lexer);
if (!lex_match_id (lexer, "ESTIMATED"))
- dict_set_case_limit (dataset_dict (ds), x);
+ dict_set_case_limit (dataset_dict (ds), n);
- return lex_end_of_command (lexer);
+ return CMD_SUCCESS;
}
/* Parses, performs the EXECUTE procedure. */
int
-cmd_execute (struct lexer *lexer, struct dataset *ds)
+cmd_execute (struct lexer *lexer UNUSED, struct dataset *ds)
{
bool ok = casereader_destroy (proc_open (ds));
if (!proc_commit (ds) || !ok)
return CMD_CASCADING_FAILURE;
- return lex_end_of_command (lexer);
+ return CMD_SUCCESS;
}
/* Parses, performs the ERASE command. */
int
cmd_erase (struct lexer *lexer, struct dataset *ds UNUSED)
{
+ char *filename;
+ int retval;
+
if (settings_get_safer_mode ())
{
- msg (SE, _("This command not allowed when the SAFER option is set."));
+ msg (SE, _("This command not allowed when the %s option is set."), "SAFER");
return CMD_FAILURE;
}
if (!lex_force_string (lexer))
return CMD_FAILURE;
- if (remove (ds_cstr (lex_tokstr (lexer))) == -1)
+ filename = utf8_to_filename (lex_tokcstr (lexer));
+ retval = remove (filename);
+ free (filename);
+
+ if (retval == -1)
{
msg (SW, _("Error removing `%s': %s."),
- ds_cstr (lex_tokstr (lexer)), strerror (errno));
+ lex_tokcstr (lexer), strerror (errno));
return CMD_FAILURE;
}
+ lex_get (lexer);
return CMD_SUCCESS;
}
/* Parses, performs the NEW FILE command. */
int
-cmd_new_file (struct lexer *lexer, struct dataset *ds)
-{
- proc_discard_active_file (ds);
-
- return lex_end_of_command (lexer);
-}
-
-/* Parses a comment. */
-int
-cmd_comment (struct lexer *lexer, struct dataset *ds UNUSED)
+cmd_new_file (struct lexer *lexer UNUSED, struct dataset *ds)
{
- lex_skip_comment (lexer);
+ dataset_clear (ds);
return CMD_SUCCESS;
}