changeset 2764:2c0f259cf83d

[project @ 1997-03-01 02:30:26 by jwe]
author jwe
date Sat, 01 Mar 1997 02:30:29 +0000
parents d9d00d7e271e
children c44d1e4cd35b
files src/ChangeLog src/SLList-misc.cc src/lex.h src/lex.l src/octave.gperf src/parse.y src/pt-cmd.cc src/pt-cmd.h src/pt-misc.cc src/pt-misc.h src/pt-pr-code.cc src/pt-pr-code.h src/pt-walk.h src/syswait.h src/token.h
diffstat 15 files changed, 488 insertions(+), 18 deletions(-) [+]
line wrap: on
line diff
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,5 +1,34 @@
 Fri Feb 28 01:49:48 1997  John W. Eaton  <jwe@bevo.che.wisc.edu>
 
+	Implement switch statement:
+
+	* parse.y (Vwarn_variable_switch_label): New static variable.
+	(warn_variable_switch_label): New function.
+	(symbols_of_parse): Provide warn_variable_switch_label as Octave
+	variable here.
+	(make_switch_case, finish_switch_command): New functions.
+	(maybe_warn_variable_switch_label): New function.
+	(end_error): Handle endswitch.
+	(switch_command, case_list, case_list1, switch_case, default_case):
+	New nonterminals.
+	(command): Add switch_command here.
+	* lex.l	(is_keyword): Handle switch, case, otherwise, and endswitch.
+	* octave_gperf: Recognize switch, case, otherwise, and endswitch.
+	* token.h (end_tok_type): New item, switch_end.
+	* pt-cmd.cc (tree_switch_command): New class.
+	* pt-misc.cc (tree_switch_case, tree_switch_case_list): New classes.
+	* pt-pr-code.cc (tree_print_code::visit_switch_case,
+	tree_print_code::visit_switch_case_list,
+	tree_print_code::visit_switch_command): New functions.
+ 	* pt-walk.h (tree_walker::visit_switch_case,
+	tree_walker::visit_switch_case_list,
+	tree_walker::visit_switch_command): New pure virtual declarations.
+	Implement new switch statement.
+	* SLList-misc.cc: Instantiate lists of pointers to
+	tree_switch_case objects too.
+
+	* lex.h, lex.l, parse.y: Delete all references to lexer_flags::iffing.
+
 	* syswait.h: Include sys/wait.h on NeXT systems, but don't use the
 	WIFEXTED, WEXITSTATUS, and WIFSIGNALLED macros defined there.
 	Also define waitpid in terms of wait4.  From Rex A. Dieter
--- a/src/SLList-misc.cc
+++ b/src/SLList-misc.cc
@@ -39,10 +39,12 @@
 template class SLNode<tree_if_clause *>;
 template class SLList<tree_if_clause *>;
 
+template class SLList<tree_switch_case *>;
+template class SLNode<tree_switch_case *>;
+
 template class SLList<tree_global *>;
 template class SLNode<tree_global *>;
 
-
 /*
 ;;; Local Variables: ***
 ;;; mode: C++ ***
--- a/src/lex.h
+++ b/src/lex.h
@@ -149,10 +149,6 @@
   // Nonzero means we think we are looking at a set command.
   int doing_set;
 
-  // Nonzero means we're in the middle of defining a conditional
-  // expression.
-  int iffing;
-
   // Nonzero means we're looking at the range part of a plot command.
   int in_plot_range;
 
--- a/src/lex.l
+++ b/src/lex.l
@@ -959,11 +959,13 @@
 	{
 	case all_va_args_kw:
 	case break_kw:
+	case case_kw:
 	case catch_kw:
 	case continue_kw:
 	case else_kw:
 	case elseif_kw:
 	case global_kw:
+	case otherwise_kw:
 	case return_kw:
 	case unwind_protect_cleanup_kw:
  	  break;
@@ -992,6 +994,10 @@
 	  yylval.tok_val = new token (token::if_end, l, c);
 	  break;
 
+	case endswitch_kw:
+	  yylval.tok_val = new token (token::switch_end, l, c);
+	  break;
+
 	case endwhile_kw:
 	  yylval.tok_val = new token (token::while_end, l, c);
 	  break;
@@ -1003,11 +1009,8 @@
 	  break;
 
 	case if_kw:
-	  promptflag--;
-	  lexer_flags.iffing++;
-	  break;
-
 	case try_kw:
+	case switch_kw:
 	case unwind_protect_kw:
 	  promptflag--;
 	  break;
@@ -1908,7 +1911,6 @@
   plotting = 0;
 
   // Not initially inside a loop or if statement.
-  iffing = 0;
   looping = 0;
 
   // Not initially looking at indirect references.
--- a/src/octave.gperf
+++ b/src/octave.gperf
@@ -4,6 +4,7 @@
 {
   all_va_args_kw,
   break_kw,
+  case_kw,
   catch_kw,
   continue_kw,
   else_kw,
@@ -14,6 +15,7 @@
   endfor_kw,
   endfunction_kw,
   endif_kw,
+  endswitch_kw,
   endwhile_kw,
   for_kw,
   function_kw,
@@ -21,8 +23,10 @@
   gplot_kw,
   gsplot_kw,
   if_kw,
+  otherwise_kw,
   replot_kw,
   return_kw,
+  switch_kw,
   try_kw,
   unwind_protect_kw,
   unwind_protect_cleanup_kw,
@@ -34,6 +38,7 @@
 %%
 all_va_args, ALL_VA_ARGS, all_va_args_kw
 break, BREAK, break_kw
+case, CASE, case_kw
 catch, CATCH, catch_kw
 continue, CONTINUE, continue_kw
 else, ELSE, else_kw
@@ -44,6 +49,7 @@
 endfor, END, endfor_kw
 endfunction, END, endfunction_kw
 endif, END, endif_kw
+endswitch, END, endswitch_kw
 endwhile, END, endwhile_kw
 for, FOR, for_kw
 function, FCN, function_kw
@@ -51,8 +57,10 @@
 gplot, PLOT, gplot_kw
 gsplot, PLOT, gsplot_kw
 if, IF, if_kw
+otherwise, OTHERWISE, otherwise_kw
 replot, PLOT, replot_kw
 return, FUNC_RET, return_kw
+switch, SWITCH, switch_kw
 try, TRY, try_kw
 unwind_protect, UNWIND, unwind_protect_kw
 unwind_protect_cleanup, CLEANUP, unwind_protect_cleanup_kw
--- a/src/parse.y
+++ b/src/parse.y
@@ -72,6 +72,9 @@
 //
 static bool Vwarn_assign_as_truth_value;
 
+// If TRUE, generate a warning for variable swich labels.
+static bool Vwarn_variable_switch_label;
+
 // If TRUE, generate a warning for the comma in things like
 //
 //   octave> global a, b = 2
@@ -119,6 +122,9 @@
 // test in a logical expression.
 static void maybe_warn_assign_as_truth_value (tree_expression *expr);
 
+// Maybe print a warning about switch labels that aren't constants.
+static void maybe_warn_variable_switch_label (tree_expression *expr);
+
 // Create a plot command.
 static tree_plot_command *make_plot_command
 	 (token *tok, plot_limits *range, subplot_list *list);
@@ -196,6 +202,15 @@
 static tree_if_clause *make_elseif_clause
 	 (tree_expression *expr, tree_statement_list *list);
 
+// Finish a switch command.
+static tree_switch_command *finish_switch_command
+	 (token *switch_tok, tree_expression *expr,
+	  tree_switch_case_list *list, token *end_tok);
+
+// Build a switch case.
+static tree_switch_case *make_switch_case
+	 (tree_expression *expr, tree_statement_list *list);
+
 // Build an assignment to a variable.
 static tree_expression *make_simple_assignment
 	 (tree_index_expression *var, token *eq_tok, tree_expression *expr);
@@ -271,6 +286,9 @@
   tree_if_command *tree_if_command_type;
   tree_if_clause *tree_if_clause_type;
   tree_if_command_list *tree_if_command_list_type;
+  tree_switch_command *tree_switch_command_type;
+  tree_switch_case *tree_switch_case_type;
+  tree_switch_case_list *tree_switch_case_list_type;
   tree_global *tree_global_type;
   tree_global_init_list *tree_global_init_list_type;
   tree_global_command *tree_global_command_type;
@@ -300,6 +318,7 @@
 %token <tok_val> TEXT STYLE
 %token <tok_val> FOR WHILE
 %token <tok_val> IF ELSEIF ELSE
+%token <tok_val> SWITCH CASE OTHERWISE
 %token <tok_val> BREAK CONTINUE FUNC_RET
 %token <tok_val> UNWIND CLEANUP
 %token <tok_val> TRY CATCH
@@ -333,6 +352,9 @@
 %type <tree_if_command_type> if_command
 %type <tree_if_clause_type> elseif_clause else_clause
 %type <tree_if_command_list_type> if_cmd_list1 if_cmd_list
+%type <tree_switch_command_type> switch_command
+%type <tree_switch_case_type> switch_case default_case
+%type <tree_switch_case_list_type> case_list1 case_list
 %type <tree_global_type> global_decl2
 %type <tree_global_init_list_type> global_decl1
 %type <tree_global_command_type> global_decl
@@ -618,11 +640,10 @@
 		  { $$ = $1; }
 		| global_decl
 		  { $$ = $1; }
+		| switch_command
+		  { $$ = $1; }
 		| if_command
-		  {
-		    lexer_flags.iffing--;
-		    $$ = $1;
-		  }
+		  { $$ = $1; }
 		| UNWIND opt_sep opt_list CLEANUP opt_sep opt_list END
 		  {
 		    if (! ($$ = make_unwind_command ($1, $3, $6, $7)))
@@ -699,6 +720,39 @@
 		  { $$ = new tree_if_clause ($3); }
 		;
 
+switch_command	: SWITCH expression opt_sep case_list END
+		  {
+		    if (! ($$ = finish_switch_command ($1, $2, $4, $5)))
+		      ABORT_PARSE;
+		  }
+		;
+
+case_list	: case_list1
+		  { $$ = $1; }
+		| case_list1 default_case
+		  {
+		    $1->append ($2);
+		    $$ = $1;
+		  }		
+		;
+
+case_list1	: switch_case
+		  { $$ = new tree_switch_case_list ($1); }
+		| case_list1 switch_case
+		  {
+		    $1->append ($2);
+		    $$ = $1;
+		  }
+		;
+
+switch_case	: CASE opt_sep expression opt_sep list
+		  { $$ = make_switch_case ($3, $5); }
+		;
+
+default_case	: OTHERWISE opt_sep opt_list
+		  { $$ = new tree_switch_case ($3); }
+		;
+
 screwed_again	: // empty
 		  { lexer_flags.maybe_screwed_again++; }
 		;
@@ -1280,6 +1334,10 @@
 	  end_error ("try", ettype, l, c);
 	  break;
 
+	case token::switch_end:
+	  end_error ("switch", ettype, l, c);
+	  break;
+
 	case token::unwind_protect_end:
 	  end_error ("unwind_protect", ettype, l, c);
 	  break;
@@ -1351,6 +1409,17 @@
     }
 }
 
+// Maybe print a warning about switch labels that aren't constants.
+
+static void
+maybe_warn_variable_switch_label (tree_expression *expr)
+{
+  if (Vwarn_variable_switch_label && ! expr->is_constant ())
+    {
+      warning ("variable switch label");
+    }
+}
+
 // Create a plot command.
 
 static tree_plot_command *
@@ -1975,6 +2044,35 @@
   return new tree_if_clause (expr, list);
 }
 
+// Finish a switch command.
+
+static tree_switch_command *
+finish_switch_command (token *switch_tok, tree_expression *expr,
+		       tree_switch_case_list *list, token *end_tok)
+{
+  tree_switch_command *retval = 0;
+
+  if (! check_end (end_tok, token::switch_end))
+    {
+      int l = switch_tok->line ();
+      int c = switch_tok->column ();
+
+      retval = new tree_switch_command (expr, list, l, c);
+    }
+
+  return retval;
+}
+
+// Build a switch case.
+
+static tree_switch_case *
+make_switch_case (tree_expression *expr, tree_statement_list *list)
+{
+  maybe_warn_variable_switch_label (expr);
+
+  return new tree_switch_case (expr, list);
+}
+
 // Build an assignment to a variable.
 
 static tree_expression *
@@ -2274,6 +2372,15 @@
   return 0;
 }
 
+static int
+warn_variable_switch_label (void)
+{
+  Vwarn_variable_switch_label
+    = check_preference ("warn_variable_switch_label");
+
+  return 0;
+}
+
 void
 symbols_of_parse (void)
 {
@@ -2287,8 +2394,11 @@
     "produce warning if function name conflicts with file name");
 
   DEFVAR (warn_missing_semicolon, 0.0, 0, warn_missing_semicolon,
-    "produce a warning if a statement in a function file is not
+    "produce a warning if a statement in a function file is not\n\
 terminated with a semicolon");
+
+  DEFVAR (warn_variable_switch_label, 0.0, 0, warn_variable_switch_label,
+    "produce warning for variables used as switch labels");
 }
 
 /*
--- a/src/pt-cmd.cc
+++ b/src/pt-cmd.cc
@@ -517,6 +517,50 @@
   tw.visit_if_command (*this);
 }
 
+// Switch.
+
+tree_switch_command::~tree_switch_command (void)
+{
+  delete expr;
+  delete list;
+}
+
+void
+tree_switch_command::eval (void)
+{
+  if (expr)
+    {
+      octave_value val = expr->eval (0);
+
+      if (! error_state)
+	{
+	  if (list)
+	    list->eval (val);
+
+	  if (error_state)
+	    eval_error ();
+	}
+      else
+	eval_error ();
+    }
+  else
+    ::error ("missing value in switch command near line %d, column %d",
+	     line (), column ());
+}
+
+void
+tree_switch_command::eval_error (void)
+{
+  ::error ("evaluating switch command near line %d, column %d",
+	   line (), column ());
+}
+
+void
+tree_switch_command::accept (tree_walker& tw)
+{
+  tw.visit_switch_command (*this);
+}
+
 // Simple exception handling.
 
 tree_try_catch_command::~tree_try_catch_command (void)
--- a/src/pt-cmd.h
+++ b/src/pt-cmd.h
@@ -34,6 +34,7 @@
 class tree_statement_list;
 class tree_global_init_list;
 class tree_if_command_list;
+class tree_switch_case_list;
 class tree_expression;
 class tree_index_expression;
 class tree_identifier;
@@ -46,6 +47,7 @@
 class tree_while_command;
 class tree_for_command;
 class tree_if_command;
+class tree_switch_command;
 class tree_try_catch_command;
 class tree_unwind_protect_command;
 class tree_no_op_command;
@@ -212,8 +214,6 @@
 
   void eval (void);
 
-  void eval_error (void);
-
   tree_if_command_list *cmd_list (void) { return list; }
 
   void accept (tree_walker& tw);
@@ -224,6 +224,41 @@
   tree_if_command_list *list;
 };
 
+// Switch.
+
+class
+tree_switch_command : public tree_command
+{
+public:
+
+  tree_switch_command (int l = -1, int c = -1)
+    : tree_command (l, c), expr (0), list (0) { }
+
+  tree_switch_command (tree_expression *e, tree_switch_case_list *lst,
+		       int l = -1, int c = -1)
+    : tree_command (l, c), expr (e), list (lst) { }
+
+  ~tree_switch_command (void);
+
+  void eval (void);
+
+  void eval_error (void);
+
+  tree_expression *switch_value (void) { return expr; }
+
+  tree_switch_case_list *case_list (void) { return list; }
+
+  void accept (tree_walker& tw);
+
+private:
+
+  // Value on which to switch.
+  tree_expression *expr;
+
+  // List of cases (case 1, case 2, ..., default)
+  tree_switch_case_list *list;
+};
+
 // Simple exception handling.
 
 class
--- a/src/pt-misc.cc
+++ b/src/pt-misc.cc
@@ -529,6 +529,95 @@
   tw.visit_if_command_list (*this);
 }
 
+// Switch.
+
+tree_switch_case::~tree_switch_case (void)
+{
+  delete label;
+  delete list;
+}
+
+bool
+tree_switch_case::label_matches (const octave_value& val)
+{
+  bool retval = false;
+
+  octave_value label_value = label->eval (false);
+
+  if (! error_state)
+    {
+      if (label_value.is_defined ())
+	{
+	  octave_value tmp = do_binary_op (octave_value::eq,
+					   val, label_value);
+
+	  if (! error_state)
+	    {
+	      if (tmp.is_defined ())
+		retval = tmp.is_true ();
+	      else
+		eval_error ();
+	    }
+	  else
+	    eval_error ();
+	}
+      else
+	eval_error ();
+    }
+  else
+    eval_error ();
+
+  return retval;
+}
+
+int
+tree_switch_case::eval (const octave_value& val)
+{
+  int retval = 0;
+
+  if (is_default_case () || label_matches (val))
+    {
+      if (list)
+	list->eval (true);
+
+      retval = 1;
+    }
+
+  return retval;
+}
+
+void
+tree_switch_case::eval_error (void)
+{
+  ::error ("evaluating switch case label");
+}
+
+void
+tree_switch_case::accept (tree_walker& tw)
+{
+  tw.visit_switch_case (*this);
+}
+
+// List of switch cases.
+
+void
+tree_switch_case_list::eval (const octave_value& val)
+{
+  for (Pix p = first (); p != 0; next (p))
+    {
+      tree_switch_case *t = this->operator () (p);
+
+      if (t->eval (val) || error_state)
+	break;
+    }
+}
+
+void
+tree_switch_case_list::accept (tree_walker& tw)
+{
+  tw.visit_switch_case_list (*this);
+}
+
 /*
 ;;; Local Variables: ***
 ;;; mode: C++ ***
--- a/src/pt-misc.h
+++ b/src/pt-misc.h
@@ -47,6 +47,10 @@
 class tree_va_return_list;
 class tree_global;
 class tree_global_init_list;
+class tree_if_clause;
+class tree_if_command_list;
+class tree_switch_case;
+class tree_switch_case_list;
 
 class tree_walker;
 
@@ -373,6 +377,70 @@
   void accept (tree_walker& tw);
 };
 
+class
+tree_switch_case
+{
+public:
+
+  tree_switch_case (void) : label (0), list (0) { }
+
+  tree_switch_case (tree_statement_list *l)
+    : label (0), list (l) { }
+
+  tree_switch_case (tree_expression *e, tree_statement_list *l)
+    : label (e), list (l) { }
+
+  ~tree_switch_case (void);
+
+  bool is_default_case (void)
+    { return ! label; }
+
+  bool label_matches (const octave_value& val);
+
+  int eval (const octave_value& val);
+
+  void eval_error (void);
+
+  tree_expression *case_label (void) { return label; }
+
+  tree_statement_list *commands (void) { return list; }
+
+  void accept (tree_walker& tw);
+
+private:
+
+  // The case label.
+  tree_expression *label;
+
+  // The list of statements to evaluate if the label matches.
+  tree_statement_list *list;
+};
+
+class
+tree_switch_case_list : public SLList<tree_switch_case *>
+{
+public:
+
+  tree_switch_case_list (void)
+    : SLList<tree_switch_case *> () { }
+
+  tree_switch_case_list (tree_switch_case *t)
+    : SLList<tree_switch_case *> () { append (t); }
+
+  ~tree_switch_case_list (void)
+    {
+      while (! empty ())
+	{
+	  tree_switch_case *t = remove_front ();
+	  delete t;
+	}
+    }
+
+  void eval (const octave_value& val);
+
+  void accept (tree_walker& tw);
+};
+
 #endif
 
 /*
--- a/src/pt-pr-code.cc
+++ b/src/pt-pr-code.cc
@@ -413,6 +413,77 @@
 }
 
 void
+tree_print_code::visit_switch_case (tree_switch_case& cs)
+{
+  indent ();
+
+  if (cs.is_default_case ())
+    os << "otherwise";
+  else
+    os << "case ";
+
+  tree_expression *label = cs.case_label ();
+
+  if (label)
+    label->accept (*this);
+
+  newline ();
+
+  increment_indent_level ();
+
+  tree_statement_list *list = cs.commands ();
+
+  if (list)
+    {
+      list->accept (*this);
+
+      decrement_indent_level ();
+    }
+}
+
+void
+tree_print_code::visit_switch_case_list (tree_switch_case_list& lst)
+{
+  Pix p = lst.first ();
+
+  while (p)
+    {
+      tree_switch_case *elt = lst (p);
+
+      if (elt)
+	elt->accept (*this);
+
+      lst.next (p);
+    }
+}
+
+void
+tree_print_code::visit_switch_command (tree_switch_command& cmd)
+{
+  indent ();
+
+  os << "switch ";
+
+  tree_expression *expr = cmd.switch_value ();
+
+  if (expr)
+    expr->accept (*this);
+
+  newline ();
+
+  increment_indent_level ();
+
+  tree_switch_case_list *list = cmd.case_list ();
+
+  if (list)
+    list->accept (*this);
+
+  indent ();
+
+  os << "endswitch";
+}
+
+void
 tree_print_code::visit_index_expression (tree_index_expression& expr)
 {
   indent ();
--- a/src/pt-pr-code.h
+++ b/src/pt-pr-code.h
@@ -78,6 +78,12 @@
 
   void visit_if_command_list (tree_if_command_list&);
 
+  void visit_switch_case (tree_switch_case&);
+
+  void visit_switch_case_list (tree_switch_case_list&);
+
+  void visit_switch_command (tree_switch_command&);
+
   void visit_index_expression (tree_index_expression&);
 
   void visit_indirect_ref (tree_indirect_ref&);
--- a/src/pt-walk.h
+++ b/src/pt-walk.h
@@ -84,6 +84,15 @@
   visit_if_command_list (tree_if_command_list&) = 0;
 
   virtual void
+  visit_switch_case (tree_switch_case&) = 0;
+
+  virtual void
+  visit_switch_case_list (tree_switch_case_list&) = 0;
+
+  virtual void
+  visit_switch_command (tree_switch_command&) = 0;
+
+  virtual void
   visit_index_expression (tree_index_expression&) = 0;
 
   virtual void
--- a/src/syswait.h
+++ b/src/syswait.h
@@ -29,7 +29,7 @@
 #include <sys/types.h>
 #endif
 
-#if defined (NeXT) && ! defined (_POSIX_SOURCE))
+#if defined (NeXT) && ! defined (_POSIX_SOURCE)
 #define HAVE_SYS_WAIT_H
 #endif
 
--- a/src/token.h
+++ b/src/token.h
@@ -51,6 +51,7 @@
       for_end,
       function_end,
       if_end,
+      switch_end,
       while_end,
       try_catch_end,
       unwind_protect_end,