X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fdo-if.c;h=ad636f79c1a81f7c70d272f3e70967a9775dae02;hb=16aa47dbdde420fe82032f7d2e166fdf4e974df5;hp=6f9aa507ed5ecada4516e8a13622df036f5fa103;hpb=97d6c6f6b1922621ca013668eba9a9a9f71d60fe;p=pspp-builds.git diff --git a/src/do-if.c b/src/do-if.c index 6f9aa507..ad636f79 100644 --- a/src/do-if.c +++ b/src/do-if.c @@ -14,178 +14,113 @@ 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., 59 Temple Place - Suite 330, Boston, MA - 02111-1307, USA. */ + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. */ #include -#include "do-ifP.h" +#include "ctl-stack.h" #include "error.h" #include #include "alloc.h" #include "command.h" #include "error.h" -#include "expr.h" +#include "expressions/public.h" #include "lexer.h" #include "str.h" #include "var.h" -#include "debug-print.h" - -#if DEBUGGING -#include -#endif - -/* *INDENT-OFF* */ -/* Description of DO IF transformations: - - DO IF has two transformations. One is a conditional jump around - a false condition. The second is an unconditional jump around - the rest of the code after a true condition. Both of these types - have their destinations backpatched in by the next clause (ELSE IF, - END IF). - - The characters `^V<>' are meant to represent arrows. - - 1. DO IF - V<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>V - V V - >>1. ELSE IF V - V<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>V - V V - >>1. ELSE IF V - V<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>V - V V - >>*. Transformations executed when no condition is true. (ELSE) V - V - *. Transformations after DO IF structure.<<<<<<<<<<<<<<<<<<<<<<<<<<<< +#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: */ -/* *INDENT-ON* */ -static struct do_if_trns *parse_do_if (void); -static void add_ELSE_IF (struct do_if_trns *); -static trns_proc_func goto_trns_proc, do_if_trns_proc; +/* A conditional clause. */ +struct clause + { + struct expression *condition; /* Test expression; NULL for ELSE clause. */ + int target_index; /* Transformation to jump to if true. */ + }; + +/* 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 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 *); + +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); + +static trns_proc_func do_if_trns_proc, break_trns_proc; static trns_free_func do_if_trns_free; /* Parse DO IF. */ int cmd_do_if (void) { - struct do_if_trns *t; + struct do_if_trns *do_if = xmalloc (sizeof *do_if); + do_if->clauses = NULL; + do_if->clause_cnt = 0; - /* Parse the transformation. */ - t = parse_do_if (); - if (!t) - return CMD_FAILURE; + ctl_stack_push (&do_if_class, do_if); + add_transformation (do_if_trns_proc, do_if_trns_free, do_if); - /* Finish up the transformation, add to control stack, add to - transformation list. */ - t->brk = NULL; - t->ctl.type = CST_DO_IF; - t->ctl.down = ctl_stack; - t->ctl.trns = (struct trns_header *) t; - t->ctl.brk = NULL; - t->has_else = 0; - ctl_stack = &t->ctl; - add_transformation ((struct trns_header *) t); - - return CMD_SUCCESS; + return parse_clause (do_if); } /* Parse ELSE IF. */ int cmd_else_if (void) { - /* Transformation created. */ - struct do_if_trns *t; - - /* Check that we're in a pleasing situation. */ - if (!ctl_stack || ctl_stack->type != CST_DO_IF) - { - msg (SE, _("There is no DO IF to match with this ELSE IF.")); - return CMD_FAILURE; - } - if (((struct do_if_trns *) ctl_stack->trns)->has_else) - { - msg (SE, _("The ELSE command must follow all ELSE IF commands " - "in a DO IF structure.")); - return CMD_FAILURE; - } - - /* Parse the transformation. */ - t = parse_do_if (); - if (!t) + struct do_if_trns *do_if = ctl_stack_top (&do_if_class); + if (do_if == NULL || !must_not_have_else (do_if)) return CMD_FAILURE; - - /* Stick in the breakout transformation. */ - t->brk = xmalloc (sizeof *t->brk); - t->brk->h.proc = goto_trns_proc; - t->brk->h.free = NULL; - - /* Add to list of transformations, add to string of ELSE IFs. */ - add_transformation ((struct trns_header *) t->brk); - add_transformation ((struct trns_header *) t); - - add_ELSE_IF (t); - - if (token != '.') - { - msg (SE, _("End of command expected.")); - return CMD_TRAILING_GARBAGE; - } - - return CMD_SUCCESS; + return parse_clause (do_if); } /* Parse ELSE. */ int cmd_else (void) { - struct do_if_trns *t; - - /* Check that we're in a pleasing situation. */ - if (!ctl_stack || ctl_stack->type != CST_DO_IF) - { - msg (SE, _("There is no DO IF to match with this ELSE.")); - return CMD_FAILURE; - } - - if (((struct do_if_trns *) ctl_stack->trns)->has_else) - { - msg (SE, _("There may be at most one ELSE clause in each DO IF " - "structure. It must be the last clause.")); - return CMD_FAILURE; - } - - /* Note that the ELSE transformation is *not* added to the list of - transformations. That's because it doesn't need to do anything. - Its goto transformation *is* added, because that's necessary. - The main DO IF do_if_trns is the destructor for this ELSE - do_if_trns. */ - t = xmalloc (sizeof *t); - t->next = NULL; - t->brk = xmalloc (sizeof *t->brk); - t->brk->h.proc = goto_trns_proc; - t->brk->h.free = NULL; - t->cond = NULL; - add_transformation ((struct trns_header *) t->brk); - t->h.index = t->brk->h.index + 1; - - /* Add to string of ELSE IFs. */ - add_ELSE_IF (t); - + struct do_if_trns *do_if = ctl_stack_top (&do_if_class); + if (do_if == NULL || !must_not_have_else (do_if)) + return CMD_FAILURE; + add_else (do_if); return lex_end_of_command (); } @@ -193,133 +128,147 @@ cmd_else (void) int cmd_end_if (void) { - /* List iterator. */ - struct do_if_trns *iter; - - /* Check that we're in a pleasing situation. */ - if (!ctl_stack || ctl_stack->type != CST_DO_IF) - { - msg (SE, _("There is no DO IF to match with this END IF.")); - return CMD_FAILURE; - } - - /* Chain down the list, backpatching destinations for gotos. */ - iter = (struct do_if_trns *) ctl_stack->trns; - for (;;) - { - if (iter->brk) - iter->brk->dest = n_trns; - iter->missing_jump = n_trns; - if (iter->next) - iter = iter->next; - else - break; - } - iter->false_jump = n_trns; + struct do_if_trns *do_if = ctl_stack_top (&do_if_class); + if (do_if == NULL) + return CMD_FAILURE; - /* Pop control stack. */ - ctl_stack = ctl_stack->down; + ctl_stack_pop (do_if); return lex_end_of_command (); } -/* Adds an ELSE IF or ELSE to the chain of them that hangs off the - main DO IF. */ +/* Closes out DO_IF, by adding a sentinel ELSE clause if + necessary and setting past_END_IF_index. */ static void -add_ELSE_IF (struct do_if_trns * t) +close_do_if (void *do_if_) { - /* List iterator. */ - struct do_if_trns *iter; - - iter = (struct do_if_trns *) ctl_stack->trns; - while (iter->next) - iter = iter->next; - assert (iter != NULL); - - iter->next = t; - iter->false_jump = t->h.index; + 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 (); } -/* Parses a DO IF or ELSE IF command and returns a pointer to a mostly - filled in transformation. */ -static struct do_if_trns * -parse_do_if (void) +/* Adds an ELSE clause to DO_IF pointing to the next + transformation. */ +static void +add_else (struct do_if_trns *do_if) { - struct do_if_trns *t; - struct expression *e; + assert (!has_else (do_if)); + add_clause (do_if, NULL, next_transformation ()); +} - e = expr_parse (EXPR_BOOLEAN); - if (!e) - return NULL; - if (token != '.') +/* 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)) { - expr_free (e); - lex_error (_("expecting end of command")); - return NULL; + msg (SE, _("This command may not follow ELSE in DO IF...END IF.")); + return false; } + else + return true; +} - t = xmalloc (sizeof *t); - t->h.proc = do_if_trns_proc; - t->h.free = do_if_trns_free; - t->next = NULL; - t->cond = e; +/* 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); +} + +/* 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; - return t; + condition = expr_parse (default_dict, EXPR_BOOLEAN); + if (condition == NULL) + return CMD_FAILURE; + + add_clause (do_if, condition, next_transformation ()); + + return lex_end_of_command (); } -/* Executes a goto transformation. */ -static int -goto_trns_proc (struct trns_header * t, struct ccase * c UNUSED, - int case_num UNUSED) +/* 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) { - return ((struct goto_trns *) t)->dest; + struct clause *clause; + + if (do_if->clause_cnt > 0) + add_transformation (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; } +/* DO IF transformation procedure. + Checks each clause and jumps to the appropriate + transformation. */ static int -do_if_trns_proc (struct trns_header * trns, struct ccase * c, - int case_num UNUSED) +do_if_trns_proc (void *do_if_, struct ccase *c, int case_num UNUSED) { - struct do_if_trns *t = (struct do_if_trns *) trns; - union value bool; + struct do_if_trns *do_if = do_if_; + struct clause *clause; - expr_evaluate (t->cond, c, case_num, &bool); - if (bool.f == 1.0) - { - debug_printf ((_("DO IF %d: true\n"), t->h.index)); - return -1; - } - else if (bool.f == 0.0) - { - debug_printf ((_("DO IF %d: false\n"), t->h.index)); - return t->false_jump; - } - else + for (clause = do_if->clauses; clause < do_if->clauses + do_if->clause_cnt; + clause++) { - debug_printf ((_("DO IF %d: missing\n"), t->h.index)); - return t->missing_jump; + if (clause->condition != NULL) + { + 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; + } + else + return clause->target_index; } + return do_if->past_END_IF_index; } +/* Frees a DO IF transformation. */ static void -do_if_trns_free (struct trns_header * trns) +do_if_trns_free (void *do_if_) { - struct do_if_trns *t = (struct do_if_trns *) trns; - expr_free (t->cond); + 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); + free (do_if->clauses); + free (do_if); +} - /* If brk is NULL then this is the main DO IF; therefore we - need to chain down to the ELSE and delete it. */ - if (t->brk == NULL) - { - struct do_if_trns *iter = t->next; - while (iter) - { - if (!iter->cond) - { - /* This is the ELSE. */ - free (iter); - break; - } - iter = iter->next; - } - } +/* Breaks out of a DO IF construct. */ +static int +break_trns_proc (void *do_if_, struct ccase *c UNUSED, int 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 struct ctl_class do_if_class = + { + "DO IF", + "END IF", + close_do_if, + };