@menu
* Tokens:: Characters combine to form tokens.
* Commands:: Tokens combine to form commands.
+* Syntax Variants:: Batch vs. Interactive mode
* Types of Commands:: Commands come in several flavors.
* Order of Commands:: Commands combine to form syntax files.
* Missing Observations:: Handling missing observations.
* BNF:: How command syntax is described.
@end menu
+
@node Tokens
@section Tokens
@cindex language, lexical analysis
by default, although you can use the NULLINE subcommand of @cmd{SET}
to disable this feature (@pxref{SET}).
-In batch mode only, that is, when reading commands from a file instead
-of an interactive user, any line that contains a non-space character
-in the leftmost column begins a new command. Thus, each command
-consists of a flush-left line followed by any number of lines indented
-from the left margin. In this mode, a plus or minus sign
-(@samp{+}, @samp{@minus{}}) as the first character
-in a line is ignored and causes that line to begin a new command,
-which allows for visual indentation of a command without that command
-being considered part of the previous command.
+@node Syntax Variants
+@section Variants of syntax.
+
+@cindex Batch syntax
+@cindex Interactive syntax
+
+There are two variants of command syntax, @i{viz}: @dfn{batch} mode and
+@dfn{interactive} mode.
+Batch mode is the default when reading commands from a file.
+Interactive mode is the default when commands are typed at a prompt
+by a user.
+Certain commands, such as @cmd{INSERT} (@pxref{INSERT}), may explicitly
+change the syntax mode.
+
+In batch mode, any line that contains a non-space character
+in the leftmost column begins a new command.
+Thus, each command consists of a flush-left line followed by any
+number of lines indented from the left margin.
+In this mode, a plus or minus sign (@samp{+}, @samp{@minus{}}) as the
+first character in a line is ignored and causes that line to begin a
+new command, which allows for visual indentation of a command without
+that command being considered part of the previous command.
+The period terminating the end of a command is optional but recommended.
+
+In interactive mode, each command must either be terminated with a period,
+or an empty line must follow the command.
+The use of (@samp{+} and @samp{@minus{}} as continuation characters is not
+permitted.
@node Types of Commands
@section Types of Commands
* FINISH:: Terminate the PSPP session.
* HOST:: Temporarily return to the operating system.
* INCLUDE:: Include a file within the current one.
+* INSERT:: Insert a file within the current one.
* PERMISSIONS:: Change permissions on a file.
* SET:: Adjust PSPP runtime parameters.
* SHOW:: Display runtime parameters.
@node INCLUDE
@section INCLUDE
@vindex INCLUDE
-@vindex @@
@display
-Two possible syntaxes:
- INCLUDE 'file-name'.
- @@file-name.
+ INCLUDE [FILE=]'file-name'.
@end display
@cmd{INCLUDE} causes the PSPP command processor to read an
additional command file as if it were included bodily in the current
command file.
-
+If errors are encountered in the included file, then command processing will
+stop and no more commands will be processed.
Include files may be nested to any depth, up to the limit of available
memory.
+
+The @cmd{INSERT} command (@pxref{INSERT}) may be used instead of
+@cmd{INCLUDE} if you require more flexible options.
+The syntax
+@example
+INCLUDE FILE=@var{file-name}.
+@end example
+@noindent
+functions identically to
+@example
+INSERT FILE=@var{file-name} ERROR=STOP CD=NO SYNTAX=BATCH.
+@end example
+
+
+@node INSERT
+@section INSERT
+@vindex INSERT
+
+@display
+ INSERT [FILE=]'file-name'
+ [CD=@{NO,YES@}]
+ [ERROR=@{CONTINUE,STOP@}]
+ [SYNTAX=@{BATCH,INTERACTIVE@}].
+@end display
+
+@cmd{INSERT} is similar to @cmd{INCLUDE} (@pxref{INCLUDE})
+but somewhat more flexible.
+It causes the command processor to read a file as if it were embedded in the
+current command file.
+
+If @samp{CD=YES} is specified, then before including the file, the
+current directory will be changed to the directory of the included
+file.
+The default setting is @samp{CD=NO}.
+Note that this directory will remain current until it is
+changed explicitly (with the @cmd{CD} command, or a subsequent
+@cmd{INSERT} command with the @samp{CD=YES} option).
+It will not revert to its original setting even after the included
+file is finished processing.
+
+If @samp{ERROR=STOP} is specified, errors encountered in the
+inserted file will cause processing to immediately cease.
+Otherwise processing will continue at the next command.
+The default setting is @samp{ERROR=CONTINUE}.
+
+If @samp{SYNTAX=INTERACTIVE} is specified then the syntax contained in
+the included file must conform to interactive syntax
+conventions. @xref{Syntax Variants}.
+The default setting is @samp{SYNTAX=BATCH}.
+
@node PERMISSIONS
-@comment node-name, next, previous, up
@section PERMISSIONS
@vindex PERMISSIONS
@cindex mode
msgstr ""
"Project-Id-Version: PSPP 0.4.3\n"
"Report-Msgid-Bugs-To: pspp-dev@gnu.org\n"
-"POT-Creation-Date: 2007-08-31 07:06+0800\n"
+"POT-Creation-Date: 2007-09-01 08:59+0800\n"
"PO-Revision-Date: 2006-07-28 19:32+0800\n"
"Last-Translator: John Darrington <john@darrington.wattle.id.au>\n"
"Language-Team: German <pspp-dev@gnu.org>\n"
msgid "Only one index clause may be specified."
msgstr ""
-#: src/language/control/repeat.c:168
+#: src/language/control/repeat.c:173
#, c-format
msgid "Dummy variable name \"%s\" hides dictionary variable \"%s\"."
msgstr ""
-#: src/language/control/repeat.c:173
+#: src/language/control/repeat.c:178
#, c-format
msgid "Dummy variable name \"%s\" is given twice."
msgstr ""
-#: src/language/control/repeat.c:219
+#: src/language/control/repeat.c:224
#, c-format
msgid ""
"Dummy variable \"%.*s\" had %d substitutions, so \"%.*s\" must also, but %d "
"were specified."
msgstr ""
-#: src/language/control/repeat.c:331
+#: src/language/control/repeat.c:336
msgid "DO REPEAT may not nest in compatibility mode."
msgstr ""
-#: src/language/control/repeat.c:433
+#: src/language/control/repeat.c:438
msgid "Ranges may only have integer bounds"
msgstr ""
-#: src/language/control/repeat.c:442
+#: src/language/control/repeat.c:447
#, c-format
msgid "%g TO %g is an invalid range."
msgstr ""
-#: src/language/control/repeat.c:477
+#: src/language/control/repeat.c:482
msgid "String expected."
msgstr ""
-#: src/language/control/repeat.c:496
+#: src/language/control/repeat.c:501
msgid "No matching DO REPEAT."
msgstr ""
msgstr[0] ""
msgstr[1] ""
-#: src/language/data-io/print-space.c:73 src/language/lexer/lexer.c:464
+#: src/language/data-io/print-space.c:73 src/language/lexer/lexer.c:471
#: src/language/stats/autorecode.c:154 src/language/xforms/select-if.c:59
msgid "expecting end of command"
msgstr ""
"s."
msgstr ""
-#: src/language/dictionary/value-labels.c:156 src/language/lexer/lexer.c:608
+#: src/language/dictionary/value-labels.c:156 src/language/lexer/lexer.c:615
msgid "expecting string"
msgstr ""
-#: src/language/dictionary/value-labels.c:165 src/language/lexer/lexer.c:622
+#: src/language/dictionary/value-labels.c:165 src/language/lexer/lexer.c:629
msgid "expecting integer"
msgstr ""
msgid "expecting format type"
msgstr ""
-#: src/language/lexer/lexer.c:270
+#: src/language/lexer/lexer.c:277
#, c-format
msgid "%s does not form a valid number."
msgstr ""
-#: src/language/lexer/lexer.c:374
+#: src/language/lexer/lexer.c:381
#, c-format
msgid "Bad character in input: `%c'."
msgstr ""
-#: src/language/lexer/lexer.c:376
+#: src/language/lexer/lexer.c:383
#, c-format
msgid "Bad character in input: `\\%o'."
msgstr ""
-#: src/language/lexer/lexer.c:412
+#: src/language/lexer/lexer.c:419
#, c-format
msgid "Subcommand %s may only be specified once."
msgstr ""
-#: src/language/lexer/lexer.c:420
+#: src/language/lexer/lexer.c:427
#, c-format
msgid "missing required subcommand %s"
msgstr ""
-#: src/language/lexer/lexer.c:449
+#: src/language/lexer/lexer.c:456
#, c-format
msgid "Syntax error %s at %s."
msgstr ""
-#: src/language/lexer/lexer.c:452
+#: src/language/lexer/lexer.c:459
#, c-format
msgid "Syntax error at %s."
msgstr ""
-#: src/language/lexer/lexer.c:577 src/language/lexer/lexer.c:594
+#: src/language/lexer/lexer.c:584 src/language/lexer/lexer.c:601
#, c-format
msgid "expecting `%s'"
msgstr ""
-#: src/language/lexer/lexer.c:635
+#: src/language/lexer/lexer.c:642
msgid "expecting number"
msgstr ""
-#: src/language/lexer/lexer.c:647
+#: src/language/lexer/lexer.c:654
msgid "expecting identifier"
msgstr ""
-#: src/language/lexer/lexer.c:1045
+#: src/language/lexer/lexer.c:1048
msgid "binary"
msgstr ""
-#: src/language/lexer/lexer.c:1050
+#: src/language/lexer/lexer.c:1053
msgid "octal"
msgstr ""
-#: src/language/lexer/lexer.c:1055
+#: src/language/lexer/lexer.c:1058
msgid "hex"
msgstr ""
-#: src/language/lexer/lexer.c:1065
+#: src/language/lexer/lexer.c:1068
#, c-format
msgid "String of %s digits has %d characters, which is not a multiple of %d."
msgstr ""
-#: src/language/lexer/lexer.c:1094
+#: src/language/lexer/lexer.c:1097
#, c-format
msgid "`%c' is not a valid %s digit."
msgstr ""
-#: src/language/lexer/lexer.c:1128
+#: src/language/lexer/lexer.c:1131
msgid "Unterminated string constant."
msgstr ""
-#: src/language/lexer/lexer.c:1182
+#: src/language/lexer/lexer.c:1185
msgid "Unexpected end of file in string concatenation."
msgstr ""
-#: src/language/lexer/lexer.c:1190
+#: src/language/lexer/lexer.c:1193
msgid "String expected following `+'."
msgstr ""
-#: src/language/lexer/lexer.c:1203
+#: src/language/lexer/lexer.c:1206
#, c-format
msgid "String exceeds 255 characters in length (%d characters)."
msgstr ""
msgid "Reading `%s': %s."
msgstr ""
-#: src/language/syntax-file.c:125
+#: src/language/syntax-file.c:124
#, c-format
msgid "Closing `%s': %s."
msgstr ""
msgid "Only USE ALL is currently implemented."
msgstr ""
-#: src/language/utilities/include.c:47
+#: src/language/utilities/include.c:47 src/language/utilities/insert.c:138
msgid "expecting file name"
msgstr ""
-#: src/language/utilities/include.c:62
+#: src/language/utilities/include.c:63 src/language/utilities/insert.c:149
#, c-format
msgid "Can't find `%s' in include file search path."
msgstr ""
+#: src/language/utilities/insert.c:60
+msgid "Expecting BATCH or INTERACTIVE after SYNTAX."
+msgstr ""
+
+#: src/language/utilities/insert.c:77
+msgid "Expecting YES or NO after CD."
+msgstr ""
+
+#: src/language/utilities/insert.c:94
+msgid "Expecting CONTINUE or STOP after ERROR."
+msgstr ""
+
+#: src/language/utilities/insert.c:101
+#, fuzzy, c-format
+msgid "Unexpected token: `%s'."
+msgstr "plotzlich ist der Datei beendet"
+
#: src/language/utilities/permissions.c:73
#, c-format
msgid "Expecting %s or %s."
msgid "%s --- PSPP Output"
msgstr "PSPP Dateiaufbereiter"
-#: src/ui/terminal/command-line.c:219
+#: src/ui/terminal/command-line.c:223
#, c-format
msgid ""
"PSPP, a program for statistical analysis of sample data.\n"
"\n"
msgstr ""
-#: src/ui/terminal/command-line.c:254
+#: src/ui/terminal/command-line.c:258
#, c-format
msgid ""
"\n"
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: pspp-dev@gnu.org\n"
-"POT-Creation-Date: 2007-08-31 07:06+0800\n"
+"POT-Creation-Date: 2007-09-01 08:59+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
msgid "Only one index clause may be specified."
msgstr ""
-#: src/language/control/repeat.c:168
+#: src/language/control/repeat.c:173
#, c-format
msgid "Dummy variable name \"%s\" hides dictionary variable \"%s\"."
msgstr ""
-#: src/language/control/repeat.c:173
+#: src/language/control/repeat.c:178
#, c-format
msgid "Dummy variable name \"%s\" is given twice."
msgstr ""
-#: src/language/control/repeat.c:219
+#: src/language/control/repeat.c:224
#, c-format
msgid ""
"Dummy variable \"%.*s\" had %d substitutions, so \"%.*s\" must also, but %d "
"were specified."
msgstr ""
-#: src/language/control/repeat.c:331
+#: src/language/control/repeat.c:336
msgid "DO REPEAT may not nest in compatibility mode."
msgstr ""
-#: src/language/control/repeat.c:433
+#: src/language/control/repeat.c:438
msgid "Ranges may only have integer bounds"
msgstr ""
-#: src/language/control/repeat.c:442
+#: src/language/control/repeat.c:447
#, c-format
msgid "%g TO %g is an invalid range."
msgstr ""
-#: src/language/control/repeat.c:477
+#: src/language/control/repeat.c:482
msgid "String expected."
msgstr ""
-#: src/language/control/repeat.c:496
+#: src/language/control/repeat.c:501
msgid "No matching DO REPEAT."
msgstr ""
msgstr[0] ""
msgstr[1] ""
-#: src/language/data-io/print-space.c:73 src/language/lexer/lexer.c:464
+#: src/language/data-io/print-space.c:73 src/language/lexer/lexer.c:471
#: src/language/stats/autorecode.c:154 src/language/xforms/select-if.c:59
msgid "expecting end of command"
msgstr ""
"s."
msgstr ""
-#: src/language/dictionary/value-labels.c:156 src/language/lexer/lexer.c:608
+#: src/language/dictionary/value-labels.c:156 src/language/lexer/lexer.c:615
msgid "expecting string"
msgstr ""
-#: src/language/dictionary/value-labels.c:165 src/language/lexer/lexer.c:622
+#: src/language/dictionary/value-labels.c:165 src/language/lexer/lexer.c:629
msgid "expecting integer"
msgstr ""
msgid "expecting format type"
msgstr ""
-#: src/language/lexer/lexer.c:270
+#: src/language/lexer/lexer.c:277
#, c-format
msgid "%s does not form a valid number."
msgstr ""
-#: src/language/lexer/lexer.c:374
+#: src/language/lexer/lexer.c:381
#, c-format
msgid "Bad character in input: `%c'."
msgstr ""
-#: src/language/lexer/lexer.c:376
+#: src/language/lexer/lexer.c:383
#, c-format
msgid "Bad character in input: `\\%o'."
msgstr ""
-#: src/language/lexer/lexer.c:412
+#: src/language/lexer/lexer.c:419
#, c-format
msgid "Subcommand %s may only be specified once."
msgstr ""
-#: src/language/lexer/lexer.c:420
+#: src/language/lexer/lexer.c:427
#, c-format
msgid "missing required subcommand %s"
msgstr ""
-#: src/language/lexer/lexer.c:449
+#: src/language/lexer/lexer.c:456
#, c-format
msgid "Syntax error %s at %s."
msgstr ""
-#: src/language/lexer/lexer.c:452
+#: src/language/lexer/lexer.c:459
#, c-format
msgid "Syntax error at %s."
msgstr ""
-#: src/language/lexer/lexer.c:577 src/language/lexer/lexer.c:594
+#: src/language/lexer/lexer.c:584 src/language/lexer/lexer.c:601
#, c-format
msgid "expecting `%s'"
msgstr ""
-#: src/language/lexer/lexer.c:635
+#: src/language/lexer/lexer.c:642
msgid "expecting number"
msgstr ""
-#: src/language/lexer/lexer.c:647
+#: src/language/lexer/lexer.c:654
msgid "expecting identifier"
msgstr ""
-#: src/language/lexer/lexer.c:1045
+#: src/language/lexer/lexer.c:1048
msgid "binary"
msgstr ""
-#: src/language/lexer/lexer.c:1050
+#: src/language/lexer/lexer.c:1053
msgid "octal"
msgstr ""
-#: src/language/lexer/lexer.c:1055
+#: src/language/lexer/lexer.c:1058
msgid "hex"
msgstr ""
-#: src/language/lexer/lexer.c:1065
+#: src/language/lexer/lexer.c:1068
#, c-format
msgid "String of %s digits has %d characters, which is not a multiple of %d."
msgstr ""
-#: src/language/lexer/lexer.c:1094
+#: src/language/lexer/lexer.c:1097
#, c-format
msgid "`%c' is not a valid %s digit."
msgstr ""
-#: src/language/lexer/lexer.c:1128
+#: src/language/lexer/lexer.c:1131
msgid "Unterminated string constant."
msgstr ""
-#: src/language/lexer/lexer.c:1182
+#: src/language/lexer/lexer.c:1185
msgid "Unexpected end of file in string concatenation."
msgstr ""
-#: src/language/lexer/lexer.c:1190
+#: src/language/lexer/lexer.c:1193
msgid "String expected following `+'."
msgstr ""
-#: src/language/lexer/lexer.c:1203
+#: src/language/lexer/lexer.c:1206
#, c-format
msgid "String exceeds 255 characters in length (%d characters)."
msgstr ""
msgid "Reading `%s': %s."
msgstr ""
-#: src/language/syntax-file.c:125
+#: src/language/syntax-file.c:124
#, c-format
msgid "Closing `%s': %s."
msgstr ""
msgid "Only USE ALL is currently implemented."
msgstr ""
-#: src/language/utilities/include.c:47
+#: src/language/utilities/include.c:47 src/language/utilities/insert.c:138
msgid "expecting file name"
msgstr ""
-#: src/language/utilities/include.c:62
+#: src/language/utilities/include.c:63 src/language/utilities/insert.c:149
#, c-format
msgid "Can't find `%s' in include file search path."
msgstr ""
+#: src/language/utilities/insert.c:60
+msgid "Expecting BATCH or INTERACTIVE after SYNTAX."
+msgstr ""
+
+#: src/language/utilities/insert.c:77
+msgid "Expecting YES or NO after CD."
+msgstr ""
+
+#: src/language/utilities/insert.c:94
+msgid "Expecting CONTINUE or STOP after ERROR."
+msgstr ""
+
+#: src/language/utilities/insert.c:101
+#, c-format
+msgid "Unexpected token: `%s'."
+msgstr ""
+
#: src/language/utilities/permissions.c:73
#, c-format
msgid "Expecting %s or %s."
msgid "%s --- PSPP Output"
msgstr ""
-#: src/ui/terminal/command-line.c:219
+#: src/ui/terminal/command-line.c:223
#, c-format
msgid ""
"PSPP, a program for statistical analysis of sample data.\n"
"\n"
msgstr ""
-#: src/ui/terminal/command-line.c:254
+#: src/ui/terminal/command-line.c:258
#, c-format
msgid ""
"\n"
+2007-09-05 John Darrington <john@darrington.wattle.id.au>
+
+ * command.c (do_parse_command): Translate CMD_FAILURE into
+ CMD_CASCADING_FAILURE, if the ERRMODE_STOP is set on the syntax
+ source.
+
+
2007-06-06 Ben Pfaff <blp@gnu.org>
* command.def: Add DEBUG DATASHEET command. Remove DEBUG CASEFILE
#include <libpspp/str.h>
#include <output/manager.h>
#include <output/table.h>
+#include <libpspp/getl.h>
#if HAVE_SYS_WAIT_H
#include <sys/wait.h>
/* Parses an entire command, from command name to terminating
dot. */
static enum cmd_result
-do_parse_command (struct lexer *lexer, struct dataset *ds, enum cmd_state state)
+do_parse_command (struct lexer *lexer,
+ struct dataset *ds, enum cmd_state state)
{
const struct command *command;
enum cmd_result result;
set_completion_state (state);
lex_get (lexer);
if (lex_token (lexer) == T_STOP)
- return CMD_EOF;
+ {
+ result = CMD_EOF;
+ goto finish;
+ }
else if (lex_token (lexer) == '.')
{
/* Null commands can result from extra empty lines. */
- return CMD_SUCCESS;
+ result = CMD_SUCCESS;
+ goto finish;
}
+
prompt_set_style (PROMPT_LATER);
/* Parse the command name. */
command = parse_command_name (lexer);
if (command == NULL)
- return CMD_FAILURE;
+ {
+ result = CMD_FAILURE;
+ goto finish;
+ }
else if (command->function == NULL)
{
msg (SE, _("%s is unimplemented."), command->name);
- return CMD_NOT_IMPLEMENTED;
+ result = CMD_NOT_IMPLEMENTED;
+ goto finish;
}
else if ((command->flags & F_TESTING) && !get_testing_mode ())
{
msg (SE, _("%s may be used only in testing mode."), command->name);
- return CMD_FAILURE;
+ result = CMD_FAILURE;
+ goto finish;
}
else if ((command->flags & F_ENHANCED) && get_syntax () != ENHANCED)
{
msg (SE, _("%s may be used only in enhanced syntax mode."),
command->name);
- return CMD_FAILURE;
+ result = CMD_FAILURE;
+ goto finish;
}
else if (!in_correct_state (command, state))
{
report_state_mismatch (command, state);
- return CMD_FAILURE;
+ result = CMD_FAILURE;
+ goto finish;
}
/* Execute command. */
msg_set_command_name (NULL);
assert (cmd_result_is_valid (result));
+
+ finish:
+ if ( cmd_result_is_failure (result))
+ {
+ const struct source_stream *cs = lex_get_source_stream (lexer);
+
+ if ( source_stream_current_error_mode (cs) == ERRMODE_STOP )
+ {
+ msg (MW, _("Error encountered while ERROR=STOP is effective."));
+ result = CMD_CASCADING_FAILURE;
+ }
+ }
+
return result;
}
DEF_CMD (S_ANY, 0, "FINISH", cmd_finish)
DEF_CMD (S_ANY, F_KEEP_FINAL_TOKEN, "HOST", cmd_host)
DEF_CMD (S_ANY, 0, "INCLUDE", cmd_include)
+DEF_CMD (S_ANY, 0, "INSERT", cmd_insert)
DEF_CMD (S_ANY, 0, "N OF CASES", cmd_n_of_cases)
DEF_CMD (S_ANY, F_ABBREV, "N", cmd_n_of_cases)
DEF_CMD (S_ANY, 0, "NEW FILE", cmd_new_file)
const char *file_name; /* File name. */
int line_number; /* Line number. */
struct substring text; /* Contents. */
- enum getl_syntax syntax; /* Syntax mode. */
};
/* The type of substitution made for a DO REPEAT macro. */
struct pool *);
static void do_repeat_filter (struct getl_interface *,
- struct string *, enum getl_syntax);
+ struct string *);
static bool do_repeat_read (struct getl_interface *,
- struct string *, enum getl_syntax *);
+ struct string *);
static void do_repeat_close (struct getl_interface *);
static bool always_false (const struct getl_interface *);
static const char *do_repeat_name (const struct getl_interface *);
block->parent.location = do_repeat_location;
if (!ll_is_empty (&block->lines))
- getl_include_source (lex_get_source_stream (lexer), &block->parent);
+ getl_include_source (lex_get_source_stream (lexer),
+ &block->parent,
+ lex_current_syntax_mode (lexer),
+ lex_current_error_mode (lexer)
+ );
else
pool_destroy (block->pool);
const char *cur_file_name;
struct repeat_line *line;
struct string text;
- enum getl_syntax syntax;
bool command_ends_before_line, command_ends_after_line;
/* Retrieve an input line and make a copy of it. */
- if (!lex_get_line_raw (lexer, &syntax))
+ if (!lex_get_line_raw (lexer))
return false;
ds_init_string (&text, lex_entire_line_ds (lexer));
line->file_name = previous_file_name;
line->line_number = getl_source_location (lex_get_source_stream (lexer));
ss_alloc_substring_pool (&line->text, ds_ss (&text), block->pool);
- line->syntax = syntax;
+
/* Check whether the line contains a DO REPEAT or END
REPEAT command. */
- lex_preprocess_line (&text, syntax,
+ lex_preprocess_line (&text,
+ lex_current_syntax_mode (lexer),
&command_ends_before_line,
&command_ends_after_line);
if (recognize_do_repeat (ds_ss (&text)))
repeated lines. */
static void
do_repeat_filter (struct getl_interface *block_,
- struct string *line, enum getl_syntax syntax UNUSED)
+ struct string *line)
{
struct repeat_block *block = (struct repeat_block *) block_;
bool in_apos, in_quote, dot;
was obtained, false if the source is exhausted. */
static bool
do_repeat_read (struct getl_interface *interface,
- struct string *output, enum getl_syntax *syntax)
+ struct string *output)
{
struct repeat_block *block = (struct repeat_block *) interface;
struct repeat_line *line;
line = current_line (interface);
ds_assign_substring (output, line->text);
- *syntax = line->syntax;
return true;
}
prompt_set_style (PROMPT_DATA);
}
- if (!lex_get_line_raw (r->lexer, NULL))
+ if (!lex_get_line_raw (r->lexer))
{
msg (SE, _("Unexpected end-of-file while reading data in BEGIN "
"DATA. This probably indicates "
#define DUMP_TOKENS 0
+
struct lexer
{
struct string line_buffer;
return lex->ss;
}
+enum syntax_mode
+lex_current_syntax_mode (const struct lexer *lex)
+{
+ return source_stream_current_syntax_mode (lex->ss);
+}
+
+enum error_mode
+lex_current_error_mode (const struct lexer *lex)
+{
+ return source_stream_current_error_mode (lex->ss);
+}
+
void
lex_destroy (struct lexer *lexer)
*LINE_STARTS_COMMAND and *LINE_ENDS_COMMAND appropriately. */
void
lex_preprocess_line (struct string *line,
- enum getl_syntax syntax,
+ enum syntax_mode syntax,
bool *line_starts_command,
bool *line_ends_command)
{
Sets *SYNTAX, if SYNTAX is non-null, to the line's syntax
mode. */
bool
-lex_get_line_raw (struct lexer *lexer, enum getl_syntax *syntax)
+lex_get_line_raw (struct lexer *lexer)
{
- enum getl_syntax dummy;
- bool ok;
-
- if (syntax == NULL)
- syntax = &dummy;
- ok = getl_read_line (lexer->ss, &lexer->line_buffer, syntax);
- journal_write (*syntax == GETL_BATCH, ds_cstr (&lexer->line_buffer));
+ bool ok = getl_read_line (lexer->ss, &lexer->line_buffer);
+ enum syntax_mode mode = lex_current_syntax_mode (lexer);
+ journal_write (mode == GETL_BATCH, ds_cstr (&lexer->line_buffer));
return ok;
}
lex_get_line (struct lexer *lexer)
{
bool line_starts_command;
- enum getl_syntax syntax = GETL_BATCH;
- if (!lex_get_line_raw (lexer, &syntax))
+ if (!lex_get_line_raw (lexer))
{
lexer->prog = NULL;
return false;
}
- lex_preprocess_line (&lexer->line_buffer, syntax,
+ lex_preprocess_line (&lexer->line_buffer,
+ lex_current_syntax_mode (lexer),
&line_starts_command, &lexer->dot);
if (line_starts_command)
struct lexer * lex_create (struct source_stream *);
void lex_destroy (struct lexer *);
+/* State accessors */
struct source_stream * lex_get_source_stream (const struct lexer *);
-
+enum syntax_mode lex_current_syntax_mode (const struct lexer *);
+enum error_mode lex_current_error_mode (const struct lexer *);
/* Common functions. */
void lex_get (struct lexer *);
const struct string *lex_entire_line_ds (const struct lexer *);
const char *lex_rest_of_line (const struct lexer *);
bool lex_end_dot (const struct lexer *);
-void lex_preprocess_line (struct string *, enum getl_syntax,
+void lex_preprocess_line (struct string *, enum syntax_mode,
bool *line_starts_command,
bool *line_ends_command);
void lex_discard_line (struct lexer *);
/* Weird line reading functions. */
bool lex_get_line (struct lexer *);
-bool lex_get_line_raw (struct lexer *, enum getl_syntax *);
+bool lex_get_line_raw (struct lexer *);
/* Token names. */
const char *lex_token_name (int);
/* Reads a line from syntax file source S into LINE.
Returns true if successful, false at end of file. */
-bool
+static bool
read_syntax_file (struct getl_interface *s,
- struct string *line, enum getl_syntax *syntax)
+ struct string *line)
{
struct syntax_file_source *sfs = (struct syntax_file_source *) s;
if (get_echo ())
tab_output_text (TAB_LEFT | TAB_FIX, ds_cstr (line));
- *syntax = GETL_BATCH;
return true;
}
#if !SYNTAX_FILE
#define SYNTAX_FILE 1
-#include <stdbool.h>
-#include <libpspp/getl.h>
-
-struct string;
-
-bool read_syntax_file (struct getl_interface *s,
- struct string *line, enum getl_syntax *syntax);
+struct getl_interface;
/* Creates a syntax file source with file name FN. */
-struct getl_interface * create_syntax_file_source (const char *fn) ;
+struct getl_interface * create_syntax_file_source (const char *) ;
#endif
static bool
read_single_line (struct getl_interface *i,
- struct string *line,
- enum getl_syntax *syntax_rules UNUSED)
+ struct string *line)
{
struct syntax_string_source *sss = (struct syntax_string_source *) i;
/* PSPP - a program for statistical analysis.
- Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+ Copyright (C) 1997-9, 2000, 2007 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 <language/lexer/lexer.h>
#include <libpspp/str.h>
#include <data/file-name.h>
+#include <dirname.h>
+#include <canonicalize.h>
#include "gettext.h"
#define _(msgid) gettext (msgid)
+static int parse_insert (struct lexer *lexer, char **filename);
+
+
int
cmd_include (struct lexer *lexer, struct dataset *ds UNUSED)
{
- struct source_stream *ss;
- char *found_fn;
+ char *filename = NULL;
+ int status = parse_insert (lexer, &filename);
+
+ if ( CMD_SUCCESS != status)
+ return status;
+
+ lex_get (lexer);
+
+ status = lex_end_of_command (lexer);
+
+ if ( status == CMD_SUCCESS)
+ {
+ struct source_stream *ss = lex_get_source_stream (lexer);
+
+ assert (filename);
+ getl_include_source (ss, create_syntax_file_source (filename),
+ GETL_BATCH, ERRMODE_STOP);
+ free (filename);
+ }
+
+ return status;
+}
+
+
+int
+cmd_insert (struct lexer *lexer, struct dataset *ds UNUSED)
+{
+ enum syntax_mode syntax_mode = GETL_INTERACTIVE;
+ enum error_mode error_mode = ERRMODE_CONTINUE;
+ char *filename = NULL;
+ int status = parse_insert (lexer, &filename);
+ bool cd = false;
+
+ if ( CMD_SUCCESS != status)
+ return status;
+
+ lex_get (lexer);
+
+ while ( '.' != lex_token (lexer))
+ {
+ if (lex_match_id (lexer, "SYNTAX"))
+ {
+ lex_match (lexer, '=');
+ if ( lex_match_id (lexer, "INTERACTIVE") )
+ syntax_mode = GETL_INTERACTIVE;
+ else if ( lex_match_id (lexer, "BATCH"))
+ syntax_mode = GETL_BATCH;
+ else
+ {
+ lex_error(lexer,
+ _("Expecting BATCH or INTERACTIVE after SYNTAX."));
+ return CMD_FAILURE;
+ }
+ }
+ else if (lex_match_id (lexer, "CD"))
+ {
+ lex_match (lexer, '=');
+ if ( lex_match_id (lexer, "YES") )
+ {
+ cd = true;
+ }
+ else if ( lex_match_id (lexer, "NO"))
+ {
+ cd = false;
+ }
+ else
+ {
+ lex_error (lexer, _("Expecting YES or NO after CD."));
+ return CMD_FAILURE;
+ }
+ }
+ else if (lex_match_id (lexer, "ERROR"))
+ {
+ lex_match (lexer, '=');
+ if ( lex_match_id (lexer, "CONTINUE") )
+ {
+ error_mode = ERRMODE_CONTINUE;
+ }
+ else if ( lex_match_id (lexer, "STOP"))
+ {
+ error_mode = ERRMODE_STOP;
+ }
+ else
+ {
+ lex_error (lexer, _("Expecting CONTINUE or STOP after ERROR."));
+ return CMD_FAILURE;
+ }
+ }
+
+ else
+ {
+ lex_error (lexer, _("Unexpected token: `%s'."),
+ lex_token_representation (lexer));
+
+ return CMD_FAILURE;
+ }
+ }
+
+ status = lex_end_of_command (lexer);
+
+ if ( status == CMD_SUCCESS)
+ {
+ struct source_stream *ss = lex_get_source_stream (lexer);
+
+ assert (filename);
+ getl_include_source (ss, create_syntax_file_source (filename),
+ syntax_mode,
+ error_mode);
+
+ if ( cd )
+ {
+ char *directory = dir_name (filename);
+ chdir (directory);
+ free (directory);
+ }
+
+ free (filename);
+ }
+
+ return status;
+}
+
+
+static int
+parse_insert (struct lexer *lexer, char **filename)
+{
char *target_fn;
+ char *relative_filename;
/* Skip optional FILE=. */
if (lex_match_id (lexer, "FILE"))
if (lex_token (lexer) != T_ID && lex_token (lexer) != T_STRING)
{
lex_error (lexer, _("expecting file name"));
- return CMD_CASCADING_FAILURE;
+ return CMD_FAILURE;
}
target_fn = ds_cstr (lex_tokstr (lexer));
- ss = lex_get_source_stream (lexer);
- found_fn = fn_search_path (target_fn, getl_include_path ( ss ));
+ relative_filename =
+ fn_search_path (target_fn,
+ getl_include_path (lex_get_source_stream (lexer)));
- if (found_fn != NULL)
+ if ( ! relative_filename)
{
- getl_include_source (ss, create_syntax_file_source (found_fn));
- free (found_fn);
- }
- else
- msg (SE, _("Can't find `%s' in include file search path."),
+ msg (SE, _("Can't find `%s' in include file search path."),
target_fn);
+ return CMD_FAILURE;
+ }
- lex_get (lexer);
- return lex_end_of_command (lexer);
+ *filename = canonicalize_file_name (relative_filename);
+ free (relative_filename);
+
+ return CMD_SUCCESS;
}
+2007-09-05 John Darrington <john@darrington.wattle.id.au>
+
+ * getl.c: Add extra members to struct getl_source, to maintain the
+ error mode and the syntax_mode.
+
2007-07-25 Ben Pfaff <blp@gnu.org>
* getl.c (getl_append_source): Add source to *end* of list.
struct ll ll; /* Element in the sources list */
struct getl_interface *interface;
+ enum syntax_mode syntax_mode;
+ enum error_mode error_mode;
};
struct source_stream
return ll_data (ll, struct getl_source, ll );
}
+enum syntax_mode
+source_stream_current_syntax_mode (const struct source_stream *ss)
+{
+ struct getl_source *cs = current_source (ss);
+
+ return cs->syntax_mode;
+}
+
+
+
+enum error_mode
+source_stream_current_error_mode (const struct source_stream *ss)
+{
+ struct getl_source *cs = current_source (ss);
+
+ return cs->error_mode;
+}
+
+
+
/* Initialize getl. */
struct source_stream *
create_source_stream (const char *initial_include_path)
/* Appends source S to the list of source files. */
void
-getl_append_source (struct source_stream *ss, struct getl_interface *i)
+getl_append_source (struct source_stream *ss,
+ struct getl_interface *i,
+ enum syntax_mode syntax_mode,
+ enum error_mode err_mode)
{
struct getl_source *s = xzalloc (sizeof ( struct getl_source ));
s->interface = i ;
+ s->syntax_mode = syntax_mode;
+ s->error_mode = err_mode;
ll_push_tail (&ss->sources, &s->ll);
}
/* Nests source S within the current source file. */
void
-getl_include_source (struct source_stream *ss, struct getl_interface *i)
+getl_include_source (struct source_stream *ss,
+ struct getl_interface *i,
+ enum syntax_mode syntax_mode,
+ enum error_mode err_mode)
{
struct getl_source *current = current_source (ss);
struct getl_source *s = xzalloc (sizeof ( struct getl_source ));
s->included_from = current ;
s->includes = NULL;
+ s->syntax_mode = syntax_mode;
+ s->error_mode = err_mode;
current->includes = s;
ll_push_head (&ss->sources, &s->ll);
/* Reads a single line into LINE.
Returns true when a line has been read, false at end of input.
- On success, sets *SYNTAX to the style of the syntax read. */
+*/
bool
-getl_read_line (struct source_stream *ss, struct string *line,
- enum getl_syntax *syntax)
+getl_read_line (struct source_stream *ss, struct string *line)
{
assert (ss != NULL);
while (!ll_is_empty (&ss->sources))
struct getl_source *s = current_source (ss);
ds_clear (line);
- if (s->interface->read (s->interface, line, syntax))
+ if (s->interface->read (s->interface, line))
{
while (s)
{
if (s->interface->filter)
- s->interface->filter (s->interface, line, *syntax);
+ s->interface->filter (s->interface, line);
s = s->included_from;
}
struct getl_source;
/* Syntax rules that apply to a given source line. */
-enum getl_syntax
+enum syntax_mode
{
/* Each line that begins in column 1 starts a new command. A
`+' or `-' in column 1 is ignored to allow visual
GETL_INTERACTIVE
};
+enum error_mode
+ {
+ /* When errors are encountered, report the error and continue to
+ the next command. */
+ ERRMODE_CONTINUE,
+
+ /* When errors are encountered, abort the current stream. */
+ ERRMODE_STOP
+ };
+
/* An abstract base class for objects which act as line buffers for the
PSPP. Ie anything which might contain content for the lexer */
struct getl_interface
Returns true if succesful, false on failure or at end of
input. */
bool (*read) (struct getl_interface *,
- struct string *, enum getl_syntax *);
+ struct string *);
/* Close and destroy the interface */
void (*close) (struct getl_interface *);
/* Filter for current and all included sources, which may
modify the line. Usually null. */
void (*filter) (struct getl_interface *,
- struct string *line, enum getl_syntax);
+ struct string *line);
/* Returns the name of the source */
const char * (*name) (const struct getl_interface *);
struct source_stream;
struct source_stream * create_source_stream (const char *);
+
+enum syntax_mode source_stream_current_syntax_mode
+ (const struct source_stream *);
+
+
+enum error_mode source_stream_current_error_mode
+ (const struct source_stream *);
+
+
void destroy_source_stream (struct source_stream *);
void getl_clear_include_path (struct source_stream *);
void getl_abort_noninteractive (struct source_stream *);
bool getl_is_interactive (const struct source_stream *);
-bool getl_read_line (struct source_stream *, struct string *,
- enum getl_syntax *);
+bool getl_read_line (struct source_stream *, struct string *);
+
+void getl_append_source (struct source_stream *, struct getl_interface *s,
+ enum syntax_mode, enum error_mode) ;
-void getl_append_source (struct source_stream *, struct getl_interface *s) ;
-void getl_include_source (struct source_stream *, struct getl_interface *s) ;
+void getl_include_source (struct source_stream *, struct getl_interface *s,
+ enum syntax_mode, enum error_mode) ;
const char * getl_source_name (const struct source_stream *);
int getl_source_location (const struct source_stream *);
lexer = lex_create (the_source_stream);
- getl_append_source (the_source_stream, sss);
+ getl_append_source (the_source_stream, sss, GETL_BATCH, ERRMODE_CONTINUE);
for (;;)
{
static bool
read_line_from_buffer (struct getl_interface *i,
- struct string *line,
- enum getl_syntax *syntax_rules)
+ struct string *line)
{
gchar *text;
GtkTextIter next_line;
char *pspprc_fn = fn_search_path ("rc", config_path);
if (pspprc_fn != NULL)
{
- getl_append_source (ss, create_syntax_file_source (pspprc_fn));
+ getl_append_source (ss,
+ create_syntax_file_source (pspprc_fn),
+ GETL_BATCH,
+ ERRMODE_CONTINUE
+ );
free (pspprc_fn);
}
outp_configure_macro (argv[i]);
else
{
- getl_append_source (ss, create_syntax_file_source (argv[i]));
+ getl_append_source (ss,
+ create_syntax_file_source (argv[i]),
+ GETL_BATCH,
+ ERRMODE_CONTINUE
+ );
syntax_files++;
}
if (!syntax_files || interactive_mode)
{
- getl_append_source (ss, create_readln_source () );
+ getl_append_source (ss, create_readln_source (),
+ GETL_INTERACTIVE,
+ ERRMODE_CONTINUE
+ );
if (!cleared_device_defaults)
outp_configure_add ("interactive");
}
static bool
read_interactive (struct getl_interface *s,
- struct string *line, enum getl_syntax *syntax)
+ struct string *line)
{
struct readln_source *is =
(struct readln_source *) s ;
- *syntax = GETL_INTERACTIVE;
return is->interactive_func (line, prompt_get_style ());
}
tests/command/filter.sh \
tests/command/flip.sh \
tests/command/import-export.sh \
+ tests/command/insert.sh \
tests/command/lag.sh \
tests/command/list.sh \
tests/command/loop.sh \
--- /dev/null
+#!/bin/sh
+
+# This program tests the INSERT command
+
+TEMPDIR=/tmp/pspp-tst-$$
+TESTFILE=$TEMPDIR/`basename $0`.sps
+
+# ensure that top_srcdir and top_builddir are absolute
+if [ -z "$top_srcdir" ] ; then top_srcdir=. ; fi
+if [ -z "$top_builddir" ] ; then top_builddir=. ; fi
+top_srcdir=`cd $top_srcdir; pwd`
+top_builddir=`cd $top_builddir; pwd`
+
+PSPP=$top_builddir/src/ui/terminal/pspp
+
+STAT_CONFIG_PATH=$top_srcdir/config
+export STAT_CONFIG_PATH
+
+LANG=C
+export LANG
+
+
+cleanup()
+{
+ if [ x"$PSPP_TEST_NO_CLEANUP" != x ] ; then
+ echo "NOT cleaning $TEMPDIR"
+ return ;
+ fi
+ rm -rf $TEMPDIR
+}
+
+
+fail()
+{
+ echo $activity
+ echo FAILED
+ cleanup;
+ exit 1;
+}
+
+
+no_result()
+{
+ echo $activity
+ echo NO RESULT;
+ cleanup;
+ exit 2;
+}
+
+pass()
+{
+ cleanup;
+ exit 0;
+}
+
+mkdir -p $TEMPDIR
+
+cd $TEMPDIR
+
+activity="create wrapper 1"
+cat <<EOF > $TESTFILE
+INSERT
+ FILE='$TEMPDIR/foo.sps'
+ SYNTAX=INTERACTIVE
+ .
+
+
+LIST.
+
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+
+#The following syntax intentionally omits periods from some lines
+#It's an example of "batch" syntax
+activity="create insert"
+cat <<EOF > $TEMPDIR/foo.sps
+input program.
++ loop #i = 1 to 100.
++ compute z = #i
++ end case.
++ end loop
+end file.
+end input program.
+
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+
+
+#This command should fail
+activity="run program 1"
+$SUPERVISOR $PSPP --testing-mode -o raw-ascii $TESTFILE > /dev/null
+if [ $? -eq 0 ] ; then fail ; fi
+
+
+activity="create wrapper 2"
+cat <<EOF > $TESTFILE
+INSERT
+ FILE='$TEMPDIR/foo.sps'
+ SYNTAX=BATCH
+ .
+
+
+LIST.
+
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+
+activity="run program 2"
+$SUPERVISOR $PSPP --testing-mode -o raw-ascii $TESTFILE
+if [ $? -ne 0 ] ; then fail ; fi
+
+
+# Now test the CD subcommand
+
+activity="mkdir 1"
+mkdir $TEMPDIR/Dir1
+if [ $? -ne 0 ] ; then no_result ; fi
+
+activity="create wrapper 3"
+cat <<EOF > $TESTFILE
+INSERT
+ FILE='$TEMPDIR/Dir1/foo.sps'
+ CD=NO
+ .
+
+
+LIST.
+
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+
+activity="create wrapper 4"
+cat <<EOF > $TEMPDIR/Dir1/foo.sps
+INSERT
+ FILE='bar.sps'
+ CD=NO
+ .
+
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+
+activity="create wrapper 5"
+cat <<EOF > $TEMPDIR/Dir1/bar.sps
+DATA LIST LIST /x *.
+BEGIN DATA.
+1
+2
+3
+END DATA.
+
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+
+
+# This command should fail
+activity="run program 3"
+$SUPERVISOR $PSPP --testing-mode -o raw-ascii $TESTFILE > /dev/null
+if [ $? -eq 0 ] ; then fail ; fi
+
+activity="create wrapper 6"
+cat <<EOF > $TESTFILE
+INSERT
+ FILE='$TEMPDIR/Dir1/foo.sps'
+ CD=YES
+ .
+
+LIST.
+
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+
+activity="run program 4"
+$SUPERVISOR $PSPP --testing-mode -o raw-ascii $TESTFILE
+if [ $? -ne 0 ] ; then fail ; fi
+
+
+# Now test the ERROR= feature
+
+activity="create wrapper 7"
+cat <<EOF > $TESTFILE
+INSERT
+ FILE='$TEMPDIR/foo.sps'
+ ERROR=STOP.
+ .
+
+LIST.
+
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+
+
+activity="create included file"
+cat <<EOF > $TEMPDIR/foo.sps
+DATA LIST NOTABLE LIST /x *.
+BEGIN DATA.
+1
+2
+3
+END DATA.
+
+* The following line is erroneous
+
+DISPLAY AKSDJ.
+
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+
+activity="run program 5"
+$SUPERVISOR $PSPP --testing-mode -o raw-ascii $TESTFILE > /dev/null
+if [ $? -ne 1 ] ; then no_result ; fi
+
+activity="examine output 1"
+diff $TEMPDIR/pspp.list - <<EOF
+$TEMPDIR/foo.sps:10: error: DISPLAY: AKSDJ is not a variable name.
+warning: Error encountered while ERROR=STOP is effective.
+$TEMPDIR/foo.sps:10: error: Stopping syntax file processing here to avoid a cascade of dependent command failures.
+
+EOF
+if [ $? -ne 0 ] ; then fail ; fi
+
+
+activity="create wrapper 8"
+cat <<EOF > $TESTFILE
+INSERT
+ FILE='$TEMPDIR/foo.sps'
+ ERROR=CONTINUE.
+ .
+
+LIST.
+
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+
+activity="run program 6"
+$SUPERVISOR $PSPP --testing-mode -o raw-ascii $TESTFILE > /dev/null
+if [ $? -ne 1 ] ; then no_result ; fi
+
+activity="examine output 2"
+diff $TEMPDIR/pspp.list - <<EOF
+$TEMPDIR/foo.sps:10: error: DISPLAY: AKSDJ is not a variable name.
+
+ x
+--------
+ 1.00
+ 2.00
+ 3.00
+
+EOF
+if [ $? -ne 0 ] ; then fail ; fi
+
+
+
+pass;