/[pspp]/pspp/src/do-if.c
ViewVC logotype

Diff of /pspp/src/do-if.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.12 by blp, Sun Jul 31 21:42:46 2005 UTC revision 1.13 by blp, Thu Nov 3 06:21:46 2005 UTC
# Line 18  Line 18 
18     02110-1301, USA. */     02110-1301, USA. */
19    
20  #include <config.h>  #include <config.h>
21  #include "do-ifP.h"  #include "ctl-stack.h"
22  #include "error.h"  #include "error.h"
23  #include <stdlib.h>  #include <stdlib.h>
24  #include "alloc.h"  #include "alloc.h"
# Line 32  Line 32 
32  #include "gettext.h"  #include "gettext.h"
33  #define _(msgid) gettext (msgid)  #define _(msgid) gettext (msgid)
34    
35  #include "debug-print.h"  /* DO IF, ELSE IF, and ELSE are translated as a single
36       transformation that evaluates each condition and jumps to the
37  /* *INDENT-OFF* */     start of the appropriate block of transformations.  Each block
38  /* Description of DO IF transformations:     of transformations (except for the last) ends with a
39       transformation that jumps past the remaining blocks.
40     DO IF has two transformations.  One is a conditional jump around  
41     a false condition.  The second is an unconditional jump around     So, the following code:
42     the rest of the code after a true condition.  Both of these types  
43     have their destinations backpatched in by the next clause (ELSE IF,         DO IF a.            
44     END IF).         ...block 1...
45           ELSE IF b.
46     The characters `^V<>' are meant to represent arrows.         ...block 2...
47           ELSE.
48     1. DO IF         ...block 3...
49   V<<<<if false         END IF.
50   V  
51   V *. Transformations executed when the condition on DO IF is true.     is effectively translated like this:
52   V  
53   V 2. GOTO>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>V         IF a GOTO 1, IF b GOTO 2, ELSE GOTO 3.
54   V                                                                     V         1: ...block 1...
55   >>1. ELSE IF                                                          V            GOTO 4
56   V<<<<if false                                                         V         2: ...block 2...
57   V                                                                     V            GOTO 4
58   V *. Transformations executed when condition on 1st ELSE IF is true.  V         3: ...block 3...
59   V                                                                     V         4:
  V 2. GOTO>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>V  
  V                                                                     V  
  >>1. ELSE IF                                                          V  
  V<<<<if false                                                         V  
  V                                                                     V  
  V *. Transformations executed when condition on 2nd ELSE IF is true.  V  
  V                                                                     V  
  V 2. GOTO>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>V  
  V                                                                     V  
  >>*. Transformations executed when no condition is true. (ELSE)       V  
                                                                        V  
    *. Transformations after DO IF structure.<<<<<<<<<<<<<<<<<<<<<<<<<<<<  
60    
61  */  */
 /* *INDENT-ON* */  
62    
63  static struct do_if_trns *parse_do_if (void);  /* A conditional clause. */
64  static void add_ELSE_IF (struct do_if_trns *);  struct clause
65  static trns_proc_func goto_trns_proc, do_if_trns_proc;    {
66        struct expression *condition; /* Test expression; NULL for ELSE clause. */
67        int target_index;           /* Transformation to jump to if true. */
68      };
69    
70    /* DO IF transformation. */
71    struct do_if_trns
72      {
73        struct clause *clauses;     /* Clauses. */
74        size_t clause_cnt;          /* Number of clauses. */
75        int past_END_IF_index;      /* Transformation just past last clause. */
76      };
77    
78    static struct ctl_class do_if_class;
79    
80    static int parse_clause (struct do_if_trns *);
81    static void add_clause (struct do_if_trns *,
82                            struct expression *condition, int target_index);
83    static void add_else (struct do_if_trns *);
84    
85    static bool has_else (struct do_if_trns *);
86    static bool must_not_have_else (struct do_if_trns *);
87    static void close_do_if (void *do_if);
88    
89    static trns_proc_func do_if_trns_proc, break_trns_proc;
90  static trns_free_func do_if_trns_free;  static trns_free_func do_if_trns_free;
91    
92  /* Parse DO IF. */  /* Parse DO IF. */
93  int  int
94  cmd_do_if (void)  cmd_do_if (void)
95  {  {
96    struct do_if_trns *t;    struct do_if_trns *do_if = xmalloc (sizeof *do_if);
97      do_if->clauses = NULL;
98    /* Parse the transformation. */    do_if->clause_cnt = 0;
   t = parse_do_if ();  
   if (!t)  
     return CMD_FAILURE;  
99    
100    /* Finish up the transformation, add to control stack, add to    ctl_stack_push (&do_if_class, do_if);
101       transformation list. */    add_transformation (do_if_trns_proc, do_if_trns_free, do_if);
   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);  
102    
103    return CMD_SUCCESS;    return parse_clause (do_if);
104  }  }
105    
106  /* Parse ELSE IF. */  /* Parse ELSE IF. */
107  int  int
108  cmd_else_if (void)  cmd_else_if (void)
109  {  {
110    /* Transformation created. */    struct do_if_trns *do_if = ctl_stack_top (&do_if_class);
111    struct do_if_trns *t;    if (do_if == NULL || !must_not_have_else (do_if))
   
   /* 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)  
112      return CMD_FAILURE;      return CMD_FAILURE;
113      return parse_clause (do_if);
   /* 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;  
114  }  }
115    
116  /* Parse ELSE. */  /* Parse ELSE. */
117  int  int
118  cmd_else (void)  cmd_else (void)
119  {  {
120    struct do_if_trns *t;    struct do_if_trns *do_if = ctl_stack_top (&do_if_class);
121      if (do_if == NULL || !must_not_have_else (do_if))
122    /* Check that we're in a pleasing situation. */      return CMD_FAILURE;
123    if (!ctl_stack || ctl_stack->type != CST_DO_IF)    add_else (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);  
   
124    return lex_end_of_command ();    return lex_end_of_command ();
125  }  }
126    
# Line 192  cmd_else (void) Line 128  cmd_else (void)
128  int  int
129  cmd_end_if (void)  cmd_end_if (void)
130  {  {
131    /* List iterator. */    struct do_if_trns *do_if = ctl_stack_top (&do_if_class);
132    struct do_if_trns *iter;    if (do_if == NULL)
133        return CMD_FAILURE;
   /* 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;  
134    
135    /* Pop control stack. */    ctl_stack_pop (do_if);
   ctl_stack = ctl_stack->down;  
136    
137    return lex_end_of_command ();    return lex_end_of_command ();
138  }  }
139    
140  /* Adds an ELSE IF or ELSE to the chain of them that hangs off the  /* Closes out DO_IF, by adding a sentinel ELSE clause if
141     main DO IF. */     necessary and setting past_END_IF_index. */
142  static void  static void
143  add_ELSE_IF (struct do_if_trns * t)  close_do_if (void *do_if_)
144  {  {
145    /* List iterator. */    struct do_if_trns *do_if = do_if_;
146    struct do_if_trns *iter;    
147      if (!has_else (do_if))
148    iter = (struct do_if_trns *) ctl_stack->trns;      add_else (do_if);
149    while (iter->next)    do_if->past_END_IF_index = next_transformation ();
     iter = iter->next;  
   assert (iter != NULL);  
   
   iter->next = t;  
   iter->false_jump = t->h.index;  
150  }  }
151    
152  /* Parses a DO IF or ELSE IF command and returns a pointer to a mostly  /* Adds an ELSE clause to DO_IF pointing to the next
153     filled in transformation. */     transformation. */
154  static struct do_if_trns *  static void
155  parse_do_if (void)  add_else (struct do_if_trns *do_if)
156  {  {
157    struct do_if_trns *t;    assert (!has_else (do_if));
158    struct expression *e;    add_clause (do_if, NULL, next_transformation ());
159    }
160    
161    e = expr_parse (default_dict, EXPR_BOOLEAN);  /* Returns true if DO_IF does not yet have an ELSE clause.
162    if (!e)     Reports an error and returns false if it does already. */
163      return NULL;  static bool
164    if (token != '.')  must_not_have_else (struct do_if_trns *do_if)
165    {
166      if (has_else (do_if))
167      {      {
168        expr_free (e);        msg (SE, _("This command may not follow ELSE in DO IF...END IF."));
169        lex_error (_("expecting end of command"));        return false;
       return NULL;  
170      }      }
171      else
172        return true;
173    }
174    
175    t = xmalloc (sizeof *t);  /* Returns true if DO_IF already has an ELSE clause,
176    t->h.proc = do_if_trns_proc;     false otherwise. */
177    t->h.free = do_if_trns_free;  static bool
178    t->next = NULL;  has_else (struct do_if_trns *do_if)
179    t->cond = e;  {
180      return (do_if->clause_cnt != 0
181              && do_if->clauses[do_if->clause_cnt - 1].condition == NULL);
182    }
183    
184    /* Parses a DO IF or ELSE IF expression and appends the
185       corresponding clause to DO_IF.  Checks for end of command and
186       returns a command return code. */
187    static int
188    parse_clause (struct do_if_trns *do_if)
189    {
190      struct expression *condition;
191    
192      condition = expr_parse (default_dict, EXPR_BOOLEAN);
193      if (condition == NULL)
194        return CMD_FAILURE;
195    
196      add_clause (do_if, condition, next_transformation ());
197    
198    return t;    return lex_end_of_command ();
199  }  }
200    
201  /* Executes a goto transformation. */  /* Adds a clause to DO_IF that tests for the given CONDITION and,
202  static int     if true, jumps to TARGET_INDEX. */
203  goto_trns_proc (struct trns_header * t, struct ccase * c UNUSED,  static void
204                  int case_num UNUSED)  add_clause (struct do_if_trns *do_if,
205                struct expression *condition, int target_index)
206  {  {
207    return ((struct goto_trns *) t)->dest;    struct clause *clause;
208    
209      if (do_if->clause_cnt > 0)
210        add_transformation (break_trns_proc, NULL, do_if);
211    
212      do_if->clauses = xnrealloc (do_if->clauses,
213                                  do_if->clause_cnt + 1, sizeof *do_if->clauses);
214      clause = &do_if->clauses[do_if->clause_cnt++];
215      clause->condition = condition;
216      clause->target_index = target_index;
217  }  }
218    
219    /* DO IF transformation procedure.
220       Checks each clause and jumps to the appropriate
221       transformation. */
222  static int  static int
223  do_if_trns_proc (struct trns_header * trns, struct ccase * c,  do_if_trns_proc (void *do_if_, struct ccase *c, int case_num UNUSED)
                  int case_num UNUSED)  
224  {  {
225    struct do_if_trns *t = (struct do_if_trns *) trns;    struct do_if_trns *do_if = do_if_;
226    double boolean;    struct clause *clause;
227    
228    boolean = expr_evaluate_num (t->cond, c, case_num);    for (clause = do_if->clauses; clause < do_if->clauses + do_if->clause_cnt;
229    if (boolean == 1.0)         clause++)
     {  
       debug_printf ((_("DO IF %d: true\n"), t->h.index));  
       return -1;  
     }  
   else if (boolean == 0.0)  
     {  
       debug_printf ((_("DO IF %d: false\n"), t->h.index));  
       return t->false_jump;  
     }  
   else  
230      {      {
231        debug_printf ((_("DO IF %d: missing\n"), t->h.index));        if (clause->condition != NULL)
232        return t->missing_jump;          {
233              double boolean = expr_evaluate_num (clause->condition, c, case_num);
234              if (boolean == 1.0)
235                return clause->target_index;
236              else if (boolean == SYSMIS)
237                return do_if->past_END_IF_index;
238            }
239          else
240            return clause->target_index;
241      }      }
242      return do_if->past_END_IF_index;
243  }  }
244    
245    /* Frees a DO IF transformation. */
246  static void  static void
247  do_if_trns_free (struct trns_header * trns)  do_if_trns_free (void *do_if_)
248  {  {
249    struct do_if_trns *t = (struct do_if_trns *) trns;    struct do_if_trns *do_if = do_if_;
250    expr_free (t->cond);    struct clause *clause;
251    
252    /* If brk is NULL then this is the main DO IF; therefore we    for (clause = do_if->clauses; clause < do_if->clauses + do_if->clause_cnt;
253       need to chain down to the ELSE and delete it. */         clause++)
254    if (t->brk == NULL)      expr_free (clause->condition);
255      {    free (do_if->clauses);
256        struct do_if_trns *iter = t->next;    free (do_if);
257        while (iter)  }
258          {  
259            if (!iter->cond)  /* Breaks out of a DO IF construct. */
260              {  static int
261                /* This is the ELSE. */  break_trns_proc (void *do_if_, struct ccase *c UNUSED, int case_num UNUSED)
262                free (iter);  {
263                break;    struct do_if_trns *do_if = do_if_;
264              }  
265            iter = iter->next;    return do_if->past_END_IF_index;
         }  
     }  
266  }  }
267    
268    /* DO IF control structure class definition. */
269    static struct ctl_class do_if_class =
270      {
271        "DO IF",
272        "END IF",
273        close_do_if,
274      };

Legend:
Removed from v.1.12  
changed lines
  Added in v.1.13

savannah-hackers-public@gnu.org
ViewVC Help
Powered by ViewVC 1.1.26