X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Flanguage%2Fcommand.c;h=bffd13ebb0d74b119de6d296b7e4f4fe3ba91f60;hb=778b0aa95a03dd8d69ee0ddce69eeb01ba851b3e;hp=3da5da69919a510adbd56f4f39c79df1de31847b;hpb=691c25e36fd1ee722dd35419d6110e3876b99f9c;p=pspp diff --git a/src/language/command.c b/src/language/command.c index 3da5da6991..bffd13ebb0 100644 --- a/src/language/command.c +++ b/src/language/command.c @@ -1,5 +1,5 @@ /* 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 @@ -24,21 +24,21 @@ #include #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" @@ -50,10 +50,19 @@ 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, @@ -77,20 +86,23 @@ cmd_result_is_failure (enum cmd_result result) /* 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. */ @@ -112,15 +124,16 @@ static const struct command commands[] = #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); /* 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 @@ -130,13 +143,14 @@ enum cmd_result 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; @@ -148,8 +162,8 @@ cmd_parse (struct lexer *lexer, struct dataset *ds) { 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); } @@ -161,13 +175,12 @@ do_parse_command (struct lexer *lexer, 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; @@ -180,17 +193,17 @@ do_parse_command (struct lexer *lexer, 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) { @@ -216,26 +229,28 @@ do_parse_command (struct lexer *lexer, 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; } @@ -248,7 +263,7 @@ find_best_match (struct substring s, const struct command **matchp) 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); @@ -259,51 +274,65 @@ find_best_match (struct substring s, const struct command **matchp) 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) @@ -320,18 +349,10 @@ parse_command_name (struct lexer *lexer) 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; } @@ -340,72 +361,72 @@ parse_command_name (struct lexer *lexer) 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; @@ -421,13 +442,35 @@ report_state_mismatch (const struct command *command, enum cmd_state state) 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; + } } /* Command name completion. */ @@ -450,7 +493,7 @@ cmd_complete (const char *prefix, const struct command **cmd) 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) @@ -475,36 +518,36 @@ cmd_finish (struct lexer *lexer UNUSED, struct dataset *ds UNUSED) 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; } @@ -514,29 +557,25 @@ cmd_erase (struct lexer *lexer, struct dataset *ds UNUSED) 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; }