Clean up how transformations work.
[pspp] / src / language / command.c
index 3c98c3feda1e2c45a860da84072348e78184a8f5..bffd13ebb0d74b119de6d296b7e4f4fe3ba91f60 100644 (file)
@@ -36,6 +36,7 @@
 #include "libpspp/i18n.h"
 #include "libpspp/message.h"
 #include "libpspp/str.h"
+#include "output/driver.h"
 #include "output/output-item.h"
 
 #include "xmalloca.h"
@@ -54,9 +55,6 @@ cmd_result_is_valid (enum cmd_result result)
     case CMD_SUCCESS:
     case CMD_EOF:
     case CMD_FINISH:
-    case CMD_DATA_LIST:
-    case CMD_END_CASE:
-    case CMD_END_FILE:
     case CMD_FAILURE:
     case CMD_NOT_IMPLEMENTED:
     case CMD_CASCADING_FAILURE:
@@ -88,19 +86,23 @@ cmd_result_is_failure (enum cmd_result result)
 /* Command processing states. */
 enum states
   {
-    S_INITIAL = 0x01,         /* Allowed before active dataset defined. */
-    S_DATA = 0x02,            /* Allowed after active dataset 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_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. */
@@ -122,10 +124,10 @@ 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);
 \f
 /* Command parser. */
@@ -161,7 +163,7 @@ cmd_parse (struct lexer *lexer, struct dataset *ds)
   const struct dictionary *dict = dataset_dict (ds);
   return cmd_parse_in_state (lexer, ds,
                             dataset_has_source (ds) &&
-                            dict_get_var_cnt (dict) > 0 ?
+                            dict_get_n_vars (dict) > 0 ?
                             CMD_STATE_DATA : CMD_STATE_INITIAL);
 }
 
@@ -173,8 +175,8 @@ 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. */
@@ -198,10 +200,10 @@ do_parse_command (struct lexer *lexer,
       result = CMD_FAILURE;
       goto finish;
     }
-  output_item_submit (group_open_item_create_nocopy (
-                        utf8_to_title (command->name),
-                        utf8_to_title (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)
     {
@@ -247,8 +249,8 @@ finish:
     while (lex_token (lexer) == T_ENDCMD)
       lex_get (lexer);
 
-  if (opened)
-    output_item_submit (group_close_item_create ());
+  if (nesting_level != SIZE_MAX)
+    output_close_groups (nesting_level);
 
   return result;
 }
@@ -261,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);
@@ -359,22 +361,22 @@ parse_command_name (struct lexer *lexer, int *n_tokens)
 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 ((int) 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:
@@ -440,14 +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;
 
-  return false;
+    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;
+
+        default:
+          NOT_REACHED ();
+        }
+      break;
+    }
 }
 \f
 /* Command name completion. */
@@ -470,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)
@@ -495,15 +518,12 @@ 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 CMD_SUCCESS;
 }