X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Flanguage%2Fcontrol%2Fdo-if.c;h=66c3e78e379b927275c3653afcb1f0c22258d189;hb=3c7e6939901e8c2f24eb4975e6feb68e74678f92;hp=4837366643f271fe0961f09f710fa8a468013a16;hpb=8eac4df36306cd357bba29ffbfaddc537fc0be47;p=pspp diff --git a/src/language/control/do-if.c b/src/language/control/do-if.c index 4837366643..66c3e78e37 100644 --- a/src/language/control/do-if.c +++ b/src/language/control/do-if.c @@ -1,293 +1,240 @@ -/* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. - Written by Ben Pfaff . +/* PSPP - a program for statistical analysis. + Copyright (C) 1997-9, 2000, 2009-2012 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 the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. + 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 + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. */ + along with this program. If not, see . */ #include #include -#include "control-stack.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "data/dataset.h" +#include "data/transformations.h" +#include "language/command.h" +#include "language/data-io/inpt-pgm.h" +#include "language/expressions/public.h" +#include "language/lexer/lexer.h" +#include "libpspp/compiler.h" +#include "libpspp/message.h" +#include "libpspp/str.h" + +#include "gl/xalloc.h" #include "gettext.h" #define _(msgid) gettext (msgid) -/* DO IF, ELSE IF, and ELSE are translated as a single - transformation that evaluates each condition and jumps to the - start of the appropriate block of transformations. Each block - of transformations (except for the last) ends with a - transformation that jumps past the remaining blocks. - - So, the following code: - - DO IF a. - ...block 1... - ELSE IF b. - ...block 2... - ELSE. - ...block 3... - END IF. - - is effectively translated like this: - - IF a GOTO 1, IF b GOTO 2, ELSE GOTO 3. - 1: ...block 1... - GOTO 4 - 2: ...block 2... - GOTO 4 - 3: ...block 3... - 4: - -*/ - /* A conditional clause. */ -struct clause +struct clause { + struct msg_location *location; struct expression *condition; /* Test expression; NULL for ELSE clause. */ - int target_index; /* Transformation to jump to if true. */ + struct trns_chain xforms; }; /* DO IF transformation. */ struct do_if_trns { struct clause *clauses; /* Clauses. */ - size_t clause_cnt; /* Number of clauses. */ - int past_END_IF_index; /* Transformation just past last clause. */ - }; - -static const struct ctl_class do_if_class; - -static int parse_clause (struct do_if_trns *); -static void add_clause (struct do_if_trns *, - struct expression *condition, int target_index); -static void add_else (struct do_if_trns *); + size_t n_clauses; /* Number of clauses. */ -static bool has_else (struct do_if_trns *); -static bool must_not_have_else (struct do_if_trns *); -static void close_do_if (void *do_if); + const struct trns_chain *resume; + size_t ofs; + }; -static trns_finalize_func do_if_finalize_func; -static trns_proc_func do_if_trns_proc, break_trns_proc; -static trns_free_func do_if_trns_free; +static const struct trns_class do_if_trns_class; -/* Parse DO IF. */ -int -cmd_do_if (void) +static void +start_clause (struct lexer *lexer, struct dataset *ds, + bool condition, struct do_if_trns *do_if, + size_t *allocated_clauses, bool *ok) { - struct do_if_trns *do_if = xmalloc (sizeof *do_if); - do_if->clauses = NULL; - do_if->clause_cnt = 0; + if (*ok && do_if->n_clauses > 0 + && !do_if->clauses[do_if->n_clauses - 1].condition) + { + if (condition) + msg (SE, _("ELSE IF is not allowed following ELSE " + "within DO IF...END IF.")); + else + msg (SE, _("Only one ELSE is allowed within DO IF...END IF.")); - ctl_stack_push (&do_if_class, do_if); - add_transformation_with_finalizer (current_dataset, do_if_finalize_func, - do_if_trns_proc, do_if_trns_free, do_if); + msg_at (SN, do_if->clauses[do_if->n_clauses - 1].location, + _("This is the location of the previous ELSE clause.")); - return parse_clause (do_if); -} + msg_at (SN, do_if->clauses[0].location, + _("This is the location of the DO IF command.")); -/* Parse ELSE IF. */ -int -cmd_else_if (void) -{ - struct do_if_trns *do_if = ctl_stack_top (&do_if_class); - if (do_if == NULL || !must_not_have_else (do_if)) - return CMD_CASCADING_FAILURE; - return parse_clause (do_if); -} + *ok = false; + } -/* Parse ELSE. */ -int -cmd_else (void) -{ - struct do_if_trns *do_if = ctl_stack_top (&do_if_class); - if (do_if == NULL || !must_not_have_else (do_if)) - return CMD_CASCADING_FAILURE; - add_else (do_if); - return lex_end_of_command (); -} + if (do_if->n_clauses >= *allocated_clauses) + do_if->clauses = x2nrealloc (do_if->clauses, allocated_clauses, + sizeof *do_if->clauses); + struct clause *clause = &do_if->clauses[do_if->n_clauses++]; -/* Parse END IF. */ -int -cmd_end_if (void) -{ - struct do_if_trns *do_if = ctl_stack_top (&do_if_class); - if (do_if == NULL) - return CMD_CASCADING_FAILURE; + *clause = (struct clause) { .location = NULL }; + if (condition) + { + clause->condition = expr_parse_bool (lexer, ds); + if (!clause->condition) + lex_discard_rest_of_command (lexer); + } + clause->location = lex_ofs_location (lexer, 0, lex_ofs (lexer)); - ctl_stack_pop (do_if); + lex_end_of_command (lexer); + lex_get (lexer); - return lex_end_of_command (); + proc_push_transformations (ds); } -/* Closes out DO_IF, by adding a sentinel ELSE clause if - necessary and setting past_END_IF_index. */ static void -close_do_if (void *do_if_) +finish_clause (struct dataset *ds, struct do_if_trns *do_if) { - struct do_if_trns *do_if = do_if_; - - if (!has_else (do_if)) - add_else (do_if); - do_if->past_END_IF_index = next_transformation (current_dataset); + struct clause *clause = &do_if->clauses[do_if->n_clauses - 1]; + proc_pop_transformations (ds, &clause->xforms); } -/* Adds an ELSE clause to DO_IF pointing to the next - transformation. */ -static void -add_else (struct do_if_trns *do_if) +/* Parse DO IF. */ +int +cmd_do_if (struct lexer *lexer, struct dataset *ds) { - assert (!has_else (do_if)); - add_clause (do_if, NULL, next_transformation (current_dataset)); -} + struct do_if_trns *do_if = xmalloc (sizeof *do_if); + *do_if = (struct do_if_trns) { .n_clauses = 0 }; -/* Returns true if DO_IF does not yet have an ELSE clause. - Reports an error and returns false if it does already. */ -static bool -must_not_have_else (struct do_if_trns *do_if) -{ - if (has_else (do_if)) + size_t allocated_clauses = 0; + bool ok = true; + + start_clause (lexer, ds, true, do_if, &allocated_clauses, &ok); + while (!lex_match_phrase (lexer, "END IF")) { - msg (SE, _("This command may not follow ELSE in DO IF...END IF.")); - return false; + if (lex_token (lexer) == T_STOP) + { + lex_error (lexer, NULL); + break; + } + else if (lex_match_phrase (lexer, "ELSE IF")) + { + finish_clause (ds, do_if); + start_clause (lexer, ds, true, do_if, &allocated_clauses, &ok); + } + else if (lex_match_id (lexer, "ELSE")) + { + finish_clause (ds, do_if); + start_clause (lexer, ds, false, do_if, &allocated_clauses, &ok); + } + else + cmd_parse_in_state (lexer, ds, + (in_input_program () + ? CMD_STATE_NESTED_INPUT_PROGRAM + : CMD_STATE_NESTED_DATA)); } - else - return true; -} + finish_clause (ds, do_if); -/* Returns true if DO_IF already has an ELSE clause, - false otherwise. */ -static bool -has_else (struct do_if_trns *do_if) -{ - return (do_if->clause_cnt != 0 - && do_if->clauses[do_if->clause_cnt - 1].condition == NULL); -} + add_transformation (ds, &do_if_trns_class, do_if); -/* Parses a DO IF or ELSE IF expression and appends the - corresponding clause to DO_IF. Checks for end of command and - returns a command return code. */ -static int -parse_clause (struct do_if_trns *do_if) -{ - struct expression *condition; - - condition = expr_parse (dataset_dict (current_dataset), EXPR_BOOLEAN); - if (condition == NULL) - return CMD_CASCADING_FAILURE; - - add_clause (do_if, condition, next_transformation (current_dataset)); - - return lex_end_of_command (); + return ok ? CMD_SUCCESS : CMD_FAILURE; } -/* Adds a clause to DO_IF that tests for the given CONDITION and, - if true, jumps to TARGET_INDEX. */ -static void -add_clause (struct do_if_trns *do_if, - struct expression *condition, int target_index) +int +cmd_inside_do_if (struct lexer *lexer UNUSED, struct dataset *ds UNUSED) { - struct clause *clause; - - if (do_if->clause_cnt > 0) - add_transformation (current_dataset, break_trns_proc, NULL, do_if); - - do_if->clauses = xnrealloc (do_if->clauses, - do_if->clause_cnt + 1, sizeof *do_if->clauses); - clause = &do_if->clauses[do_if->clause_cnt++]; - clause->condition = condition; - clause->target_index = target_index; + msg (SE, _("This command cannot appear outside DO IF...END IF.")); + return CMD_FAILURE; } -/* Finalizes DO IF by clearing the control stack, thus ensuring - that all open DO IFs are closed. */ -static void -do_if_finalize_func (void *do_if_ UNUSED) +static const struct trns_chain * +do_if_find_clause (const struct do_if_trns *do_if, + struct ccase *c, casenumber case_num) { - /* This will be called multiple times if multiple DO IFs were - executed, which is slightly unclean, but at least it's - idempotent. */ - ctl_stack_clear (); + for (size_t i = 0; i < do_if->n_clauses; i++) + { + const struct clause *clause = &do_if->clauses[i]; + if (!clause->condition) + return &clause->xforms; + + double boolean = expr_evaluate_num (clause->condition, c, case_num); + if (boolean != 0.0) + return boolean == SYSMIS ? NULL : &clause->xforms; + } + return NULL; } -/* DO IF transformation procedure. - Checks each clause and jumps to the appropriate - transformation. */ -static int -do_if_trns_proc (void *do_if_, struct ccase *c, casenum_t case_num UNUSED) +static enum trns_result +do_if_trns_proc (void *do_if_, struct ccase **c, casenumber case_num) { struct do_if_trns *do_if = do_if_; - struct clause *clause; - for (clause = do_if->clauses; clause < do_if->clauses + do_if->clause_cnt; - clause++) + const struct trns_chain *chain; + size_t start; + if (do_if->resume) + { + chain = do_if->resume; + start = do_if->ofs; + do_if->resume = NULL; + do_if->ofs = 0; + } + else + { + chain = do_if_find_clause (do_if, *c, case_num); + if (!chain) + return TRNS_CONTINUE; + start = 0; + } + + for (size_t i = start; i < chain->n; i++) { - if (clause->condition != NULL) + const struct transformation *trns = &chain->xforms[i]; + enum trns_result r = trns->class->execute (trns->aux, c, case_num); + switch (r) { - double boolean = expr_evaluate_num (clause->condition, c, case_num); - if (boolean == 1.0) - return clause->target_index; - else if (boolean == SYSMIS) - return do_if->past_END_IF_index; + case TRNS_CONTINUE: + break; + + case TRNS_BREAK: + case TRNS_DROP_CASE: + case TRNS_ERROR: + case TRNS_END_FILE: + return r; + + case TRNS_END_CASE: + do_if->resume = chain; + do_if->ofs = i; + return r; } - else - return clause->target_index; } - return do_if->past_END_IF_index; + return TRNS_CONTINUE; } -/* Frees a DO IF transformation. */ static bool do_if_trns_free (void *do_if_) { struct do_if_trns *do_if = do_if_; - struct clause *clause; - for (clause = do_if->clauses; clause < do_if->clauses + do_if->clause_cnt; - clause++) - expr_free (clause->condition); + for (size_t i = 0; i < do_if->n_clauses; i++) + { + struct clause *clause = &do_if->clauses[i]; + + msg_location_destroy (clause->location); + expr_free (clause->condition); + + trns_chain_uninit (&clause->xforms); + } free (do_if->clauses); free (do_if); return true; } -/* Breaks out of a DO IF construct. */ -static int -break_trns_proc (void *do_if_, struct ccase *c UNUSED, casenum_t case_num UNUSED) -{ - struct do_if_trns *do_if = do_if_; - - return do_if->past_END_IF_index; -} - -/* DO IF control structure class definition. */ -static const struct ctl_class do_if_class = - { - "DO IF", - "END IF", - close_do_if, - }; +static const struct trns_class do_if_trns_class = { + .name = "DO IF", + .execute = do_if_trns_proc, + .destroy = do_if_trns_free, +};