diff src/lex.l @ 1:78fd87e624cb

[project @ 1993-08-08 01:13:40 by jwe] Initial revision
author jwe
date Sun, 08 Aug 1993 01:13:40 +0000
parents
children e122c49e9726
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lex.l	Sun Aug 08 01:13:40 1993 +0000
@@ -0,0 +1,1340 @@
+/* lex.l                                                -*- C -*-
+
+Copyright (C) 1992, 1993 John W. Eaton
+
+This file is part of Octave.
+
+Octave 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, or (at your option) any
+later version.
+
+Octave 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 GNU CC; see the file COPYING.  If not, write to the Free
+Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+%x COMMENT
+%x NEW_MATRIX
+%x HELP_FCN
+%s TEXT_FCN
+%s DQSTRING
+%s STRING
+%s MATRIX
+
+%{
+
+// Arrange to get input via readline.
+
+#ifdef YY_INPUT
+#undef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+  if ((result = octave_read (buf, max_size)) < 0) \
+    YY_FATAL_ERROR ("octave_read () in flex scanner failed");
+#endif
+
+// Try to avoid crashing out completely on fatal scanner errors.
+
+#ifdef YY_FATAL_ERROR
+#undef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) \
+  do \
+    { \
+      error (msg); \
+      jump_to_top_level (); \
+    } \
+  while (0)
+#endif
+
+#include "input.h"
+
+// The type of an END token.  This declaration is repeated in parse.y.
+// It must appear before y.tab.h is included.
+enum end_tok_type
+  {
+    simple_end,
+    for_end,
+    function_end,
+    if_end,
+    while_end,
+  };
+
+// The type of a PLOT token.  This declaration is repeated in parse.y.
+// It must appear before y.tab.h is included.
+enum plot_tok_type
+  {
+    two_dee = 2,
+    three_dee = 3,
+  };
+
+#include "SLStack.h"
+
+#include "variables.h"
+#include "symtab.h"
+#include "error.h"
+#include "utils.h"
+#include "tree.h"
+#include "y.tab.h"
+#include "parse.h"
+#include "lex.h"
+
+// Nonzero means we thing we are looking at the beginning of a
+// function definition.
+static int beginning_of_function = 0;
+
+// Nonzero means we think we are looking at a set command.
+static int doing_set = 0;
+
+// GAG.  Stupid kludge so that [[1,2][3,4]] will work.
+static do_comma_insert = 0;
+
+// Brace level count.
+static int braceflag = 0;
+
+// Return transpose or start a string?
+static int quote_is_transpose = 0;
+
+// Nonzero means that we should convert spaces to a comma inside a
+// matrix definition.
+static int convert_spaces_to_comma = 1;
+
+// Another context hack, this time for the plot command's `using',
+// `title', and `with' keywords.
+static int cant_be_identifier = 0;
+
+// Is the closest nesting level a square brace or a paren?
+//
+//  1 -> brace, spaces are important (they can turn into commas)
+//  0 -> paren, spaces are not important
+//
+static SLStack <int> in_brace_or_paren;
+
+static void do_string_escapes (char *s);
+static void fixup_column_count (char *s);
+static int do_comma_insert_check (void);
+static int is_plot_keyword (char *s);
+static int is_keyword (char *s);
+static char *plot_style_token (char *s);
+static symbol_record *lookup_identifier (char *s);
+static void grab_help_text (void);
+static int match_any (char c, char *s);
+static int next_token_is_bin_op (int spc_prev, char *yytext);
+static int next_token_is_postfix_unary_op (int spc_prev, char *yytext);
+static char *strip_trailing_whitespace (char *s);
+
+#define DO_COMMA_INSERT_CHECK yyless (do_comma_insert_check ())
+
+#define RETURN(token) \
+  do \
+    { \
+      current_input_column += yyleng; \
+      quote_is_transpose = 0; \
+      cant_be_identifier = 0; \
+      convert_spaces_to_comma = 1; \
+      return (token); \
+    } \
+  while (0)
+
+#define BIN_OP_RETURN(token) \
+  do \
+    { \
+      current_input_column += yyleng; \
+      quote_is_transpose = 0; \
+      cant_be_identifier = 0; \
+      convert_spaces_to_comma = 0; \
+      return (token); \
+    } \
+  while (0)
+
+%}
+
+D	[0-9]
+S	[ \t]
+N	[\n]
+SN	[ \t\n]
+EL	(\.\.\.)
+Im	[iIjJ]
+QQ	(\'\')
+ECHAR	(\\.)
+QSTR	([^\n\'\\]*({QQ}|{ECHAR})*)
+DQSTR	([^\n\"\\]*{ECHAR}*)
+IDENT	([_a-zA-Z][_a-zA-Z0-9]*)
+EXPON	([DdEe][+-]?{D}+)
+%%
+
+\%			|
+\#			{
+			  if (beginning_of_function)
+			    {
+			      grab_help_text ();
+			      beginning_of_function = 0;
+			    }
+
+			  BEGIN COMMENT;
+			  current_input_column += yyleng;
+			}
+
+<COMMENT>\n		{
+			  BEGIN 0;
+			  current_input_column = 0;
+			  quote_is_transpose = 0;
+			  cant_be_identifier = 0;
+			  convert_spaces_to_comma = 1;
+			  return '\n';
+			}
+
+<COMMENT><<EOF>>	{ RETURN (END_OF_INPUT); }
+
+<COMMENT>.*$		{ current_input_column += yyleng; }
+
+<NEW_MATRIX>[^ \t\n]	{
+			  yyless (0);
+			  BEGIN MATRIX;
+			}
+
+<NEW_MATRIX>{SN}*	{
+			  fixup_column_count (yytext);
+			  BEGIN MATRIX;
+			}
+
+<HELP_FCN>\n		|
+<TEXT_FCN>\n		{
+		          BEGIN 0;
+			  current_input_column = 0;
+			  quote_is_transpose = 0;
+			  cant_be_identifier = 0;
+			  convert_spaces_to_comma = 1;
+			  return '\n';
+			}
+
+<TEXT_FCN>[\;\,]	{
+			  if (doing_set)
+			    {
+			      yylval.string = strsave (yytext);
+			      RETURN (TEXT);
+			    }
+			  else
+			    {
+			      BEGIN 0;
+			      RETURN (',');
+			    }
+		        }
+
+<HELP_FCN>[^ \t\n]*{S}*	    |
+<TEXT_FCN>[^ \t\n\;\,]*{S}* {
+
+			  static char *tok = (char *) NULL;
+			  delete [] tok;
+			  tok = strip_trailing_whitespace (yytext);
+
+			  yylval.string = strsave (tok);
+			  RETURN (TEXT);
+			}
+
+<TEXT_FCN>\'{QSTR}*[\n\'] {
+			  if (yytext[yyleng-1] == '\n')
+			    {
+			      error ("unterminated string constant");
+			      current_input_column = 0;
+			      jump_to_top_level ();
+			    }
+			  else
+			    {
+			      int off1 = doing_set ? 0 : 1;
+			      int off2 = doing_set ? 0 : 2;
+			      yylval.string = strsave (&yytext[off1]);
+			      yylval.string[yyleng-off2] = '\0';
+			      current_input_column += yyleng;
+			    }
+			  do_string_escapes (yylval.string);
+			  return TEXT;
+			}
+
+<TEXT_FCN>\"{DQSTR}*[\n\"] {
+			  if (yytext[yyleng-1] == '\n')
+			    {
+			      error ("unterminated string constant");
+			      current_input_column = 0;
+			      jump_to_top_level ();
+			    }
+			  else
+			    {
+			      int off1 = doing_set ? 0 : 1;
+			      int off2 = doing_set ? 0 : 2;
+			      yylval.string = strsave (&yytext[off1]);
+			      yylval.string[yyleng-off2] = '\0';
+			      current_input_column += yyleng;
+			    }
+			  do_string_escapes (yylval.string);
+			  return TEXT;
+			}
+
+<TEXT_FCN>{S}*		{ current_input_column += yyleng; }
+
+<STRING>{QSTR}*[\n\']	{
+			  if (braceflag)
+			    BEGIN MATRIX;
+			  else
+			    BEGIN 0;
+
+			  if (yytext[yyleng-1] == '\n')
+			    {
+			      error ("unterminated string constant");
+			      current_input_column = 0;
+			      jump_to_top_level ();
+			    }
+			  else
+			    {
+			      yylval.string = strsave (yytext);
+			      yylval.string[yyleng-1] = '\0';
+			      current_input_column += yyleng;
+			    }
+			  do_string_escapes (yylval.string);
+			  quote_is_transpose = 1;
+			  cant_be_identifier = 1;
+			  convert_spaces_to_comma = 1;
+			  return TEXT;
+			}
+
+
+<DQSTRING>{DQSTR}*[\n\"] {
+			  if (braceflag)
+			    BEGIN MATRIX;
+			  else
+			    BEGIN 0;
+
+			  if (yytext[yyleng-1] == '\n')
+			    {
+			      error ("unterminated string constant");
+			      current_input_column = 0;
+			      jump_to_top_level ();
+			    }
+			  else
+			    {
+			      yylval.string = strsave (yytext);
+			      yylval.string[yyleng-1] = '\0';
+			      current_input_column += yyleng;
+			    }
+			  do_string_escapes (yylval.string);
+			  quote_is_transpose = 1;
+			  cant_be_identifier = 1;
+			  convert_spaces_to_comma = 1;
+			  return TEXT;
+			}
+
+<MATRIX>{SN}*\]{S}*/==	{
+
+// For this and the next two rules, we're looking at ']', and we
+// need to know if the next token is '='.
+//
+// All this so we can handle the bogus syntax 
+//
+//   [x,y]                % an expression by itself
+//   [x,y] = expression   % assignment to a list of identifiers
+//   [x,y] == expression  % test for equality
+//
+// It would have been so much easier if the delimiters were simply
+// different for the expression on the left hand side of the equals
+// operator.
+
+			  in_brace_or_paren.pop ();
+			  braceflag--;
+			  if (braceflag == 0)
+			    {
+			      if (!defining_func)
+				promptflag++;
+			      BEGIN 0;
+			    }
+			  fixup_column_count (yytext);
+			  quote_is_transpose = 0;
+			  cant_be_identifier = 0;
+			  convert_spaces_to_comma = 1;
+			  return ']';
+			}
+
+<MATRIX>{SN}*\]{S}*/=	{
+			  in_brace_or_paren.pop ();
+			  braceflag--;
+			  if (braceflag == 0)
+			    {
+			      BEGIN 0;
+			      if (!defining_func)
+				promptflag++;
+			    }
+			  fixup_column_count (yytext);
+			  quote_is_transpose = 0;
+			  cant_be_identifier = 0;
+			  convert_spaces_to_comma = 1;
+			  if (maybe_screwed_again)
+			    return SCREW_TWO;
+			  else
+			    return ']';
+			}
+
+<MATRIX>{SN}*\]{S}*	{
+			  fixup_column_count (yytext);
+
+			  in_brace_or_paren.pop ();
+			  braceflag--;
+			  if (braceflag == 0)
+			    {
+			      if (!defining_func)
+				promptflag++;
+			      BEGIN 0;
+			    }
+			  else
+			    {
+			      int c0 = yytext[yyleng-1];
+			      int spc_prev = (c0 == ' ' || c0 == '\t');
+			      int bin_op = next_token_is_bin_op (spc_prev,
+								 yytext);
+			      int postfix_un_op
+				= next_token_is_postfix_unary_op (spc_prev,
+								  yytext);
+
+			      int c1 = yyinput ();
+			      unput (c1);
+			      int other_op = match_any (c1, ",;\n]");
+
+			      if (! (postfix_un_op || bin_op || other_op)
+				     && in_brace_or_paren.top ()
+				     && convert_spaces_to_comma)
+				{
+				  unput (',');
+				  return ']';
+				}
+			    }
+
+			  quote_is_transpose = 1;
+			  cant_be_identifier = 0;
+			  convert_spaces_to_comma = 1;
+			  return ']';
+			}
+
+<MATRIX>{S}*\,{S}*	{ RETURN (','); }
+
+<MATRIX>{S}+		{
+			  int bin_op = next_token_is_bin_op (1, yytext);
+			  int postfix_un_op
+			    = next_token_is_postfix_unary_op (1, yytext);
+
+ 			  if (! (postfix_un_op || bin_op)
+			      && in_brace_or_paren.top ()
+			      && convert_spaces_to_comma)
+			    RETURN (',');
+			}
+
+<MATRIX>{SN}*\;{SN}*	|
+<MATRIX>{N}{SN}*	{
+			  fixup_column_count (yytext);
+			  quote_is_transpose = 0;
+			  cant_be_identifier = 0;
+			  convert_spaces_to_comma = 1;
+			  return ';';
+			}
+
+\]			{
+			  if (! in_brace_or_paren.empty ())
+			    in_brace_or_paren.pop ();
+
+			  if (plotting)
+			    {
+			      in_plot_range = 0;
+			      RETURN (CLOSE_BRACE);
+			    }
+			  else
+			    RETURN (']');
+			}
+
+{D}+{EXPON}?{Im}	|
+{D}+\.{D}*{EXPON}?{Im}	|
+\.{D}+{EXPON}?{Im}	{
+			  int nread = sscanf (yytext, "%lf", &(yylval.number));
+			  assert (nread == 1);
+			  quote_is_transpose = 1;
+			  cant_be_identifier = 1;
+			  convert_spaces_to_comma = 1;
+			  current_input_column += yyleng;
+			  DO_COMMA_INSERT_CHECK;
+			  return IMAG_NUM;
+			}
+
+{D}+{EXPON}?		|
+{D}+\.{D}*{EXPON}?	|
+\.{D}+{EXPON}?		|
+			{
+			  int nread = sscanf (yytext, "%lf", &(yylval.number));
+			  assert (nread == 1);
+			  quote_is_transpose = 1;
+			  cant_be_identifier = 1;
+			  convert_spaces_to_comma = 1;
+			  current_input_column += yyleng;
+			  DO_COMMA_INSERT_CHECK;
+			  return NUM;
+			}
+
+\[{S}*		{
+		  in_brace_or_paren.push (1);
+		  if (plotting)
+		    {
+		      in_plot_range = 1;
+		      RETURN (OPEN_BRACE);
+		    }
+
+		  if (do_comma_insert)
+		    {
+		      yyless (0);
+		      do_comma_insert = 0;
+		      quote_is_transpose = 0;
+		      cant_be_identifier = 0;
+		      convert_spaces_to_comma = 1;
+		      return (',');
+		    }
+		  else
+		    {
+		      mlnm.push (1);
+		      braceflag++;
+		      promptflag--;
+		      BEGIN NEW_MATRIX;
+		      RETURN ('[');
+		    }
+		}
+
+{S}*		{ current_input_column += yyleng; }
+
+{EL}{S}*\n	{
+
+// Line continuation.
+
+		  promptflag--;
+		  current_input_column = 0;
+		}
+
+<<EOF>>		RETURN (END_OF_INPUT);
+
+{IDENT}{S}*	{
+
+// Truncate the token at the first space or tab but don't write
+// directly on yytext.
+
+		  static char *tok = (char *) NULL;
+		  delete [] tok;
+		  tok = strip_trailing_whitespace (yytext);
+
+		  int kw_token = is_keyword (tok);
+		  if (kw_token)
+		    RETURN (kw_token);
+
+		  if (plotting && cant_be_identifier)
+		    {
+		      int plot_option_kw = is_plot_keyword (tok);
+		      if (plot_option_kw)
+			{
+			  quote_is_transpose = 0;
+			  cant_be_identifier = 0;
+			  convert_spaces_to_comma = 1;
+			  current_input_column += yyleng;
+			  return plot_option_kw;
+			}
+		    }
+
+		  if (plotting && in_plot_style)
+		    {
+		      char *sty = plot_style_token (&tok[1]);
+		      if (sty != (char *) NULL)
+			{
+			  yylval.string = strsave (sty);
+			  if (in_plot_style)
+			    {
+			      in_plot_style = 0;
+			      RETURN (STYLE);
+			    }
+			}
+		    }
+
+		  cant_be_identifier = 1;
+
+// If we are looking at a text style function, set up to gobble its
+// arguments.  These are also reserved words, but only because it
+// would be very difficult to do anything intelligent with them if
+// they were not reserved.
+
+		  if (is_text_function_name (tok))
+		    {
+		      BEGIN TEXT_FCN;
+
+		      if (strcmp (tok, "clear") == 0)
+			return CLEAR;
+		      else if (strcmp (tok, "help") == 0)
+			BEGIN HELP_FCN;
+		      else if (strcmp (tok, "set") == 0)
+			doing_set = 1;
+		    }
+
+		  yylval.sym_rec = lookup_identifier (tok);
+
+		  quote_is_transpose = 1;
+		  current_input_column += yyleng;
+		  DO_COMMA_INSERT_CHECK;
+
+		  if (! in_brace_or_paren.empty ()
+		      && in_brace_or_paren.top ())
+		    {
+		      int c0 = yytext[yyleng-1];
+		      int spc_prev = (c0 == ' ' || c0 == '\t');
+		      int bin_op = next_token_is_bin_op (spc_prev, yytext);
+
+		      int postfix_un_op
+			= next_token_is_postfix_unary_op (spc_prev, yytext);
+
+		      int c1 = yyinput ();
+		      unput (c1);
+		      int other_op = match_any (c1, ",;\n](");
+
+		      if (! (postfix_un_op || bin_op || other_op))
+			unput (',');
+		    }
+
+		  convert_spaces_to_comma = 1;
+		  return NAME;
+		}
+
+{IDENT}/{S}*=	{
+
+// We've found an identifier followed by some space and an equals
+// sign.  If we are working on a function definition and the previous
+// token was `function', we have something like this
+//
+//    function x = y <list> end
+//
+// which is a function named y returning a variable named x.  The
+// symbol y belongs in the global symbol table (nested function
+// definitions are illegal) and the symbol x belongs in the 
+// symbol table local to the function. 
+//
+// If we're not defining a function, this should function exactly like
+// the case above.  I suppose it would be nice to avoid duplicating
+// all the code, eh?
+
+		  int kw_token = is_keyword (yytext);
+		  if (kw_token)
+		    RETURN (kw_token);
+
+		  if (plotting && cant_be_identifier)
+		    {
+		      int plot_option_kw = is_plot_keyword (yytext);
+		      if (plot_option_kw)
+			{
+			  quote_is_transpose = 0;
+		  	  convert_spaces_to_comma = 1;
+			  current_input_column += yyleng;
+			  return plot_option_kw;
+			}
+		    }
+		
+		  cant_be_identifier = 1;
+
+// If we are looking at a text style function, set up to gobble its
+// arguments.  These are also reserved words, but only because it
+// would be very difficult to do anything intelligent with them if
+// they were not reserved.
+
+		  if (is_text_function_name (yytext))
+		    {
+		      BEGIN TEXT_FCN;
+
+		      if (strcmp (yytext, "clear") == 0)
+			return CLEAR;
+		      else if (strcmp (yytext, "help") == 0)
+			BEGIN HELP_FCN;
+		      else if (strcmp (yytext, "set") == 0)
+			doing_set = 1;
+		    }
+
+		  if (defining_func && maybe_screwed)
+		    curr_sym_tab = tmp_local_sym_tab;
+
+		  yylval.sym_rec = lookup_identifier (yytext);
+
+		  convert_spaces_to_comma = 1;
+		  current_input_column += yyleng;
+		  if (defining_func && maybe_screwed)
+		    {
+		      return SCREW;
+		    }
+		  else
+		    {
+		      quote_is_transpose = 1;
+		      DO_COMMA_INSERT_CHECK;
+		      return NAME;
+		    }
+		}
+
+"\n"		{
+		  quote_is_transpose = 0;
+		  cant_be_identifier = 0;
+		  current_input_column = 0;
+		  convert_spaces_to_comma = 1;
+		  return '\n';
+		}
+
+"'"		{
+		  current_input_column++;
+		  convert_spaces_to_comma = 1;
+
+		  if (quote_is_transpose)
+		    {
+		      DO_COMMA_INSERT_CHECK;
+		      return QUOTE;
+		    }
+		  else
+		    BEGIN STRING;
+		}
+
+":"		{
+		  if (plotting && (in_plot_range || in_plot_using))
+		    RETURN (COLON);
+		  else
+		    BIN_OP_RETURN (':');
+		}
+
+\"		{ BEGIN DQSTRING; }
+".**"		{ BIN_OP_RETURN (EPOW); }
+".*"		{ BIN_OP_RETURN (EMUL); }
+"./"		{ BIN_OP_RETURN (EDIV); }
+".\\"		{ BIN_OP_RETURN (ELEFTDIV); }
+".^"		{ BIN_OP_RETURN (EPOW); }
+".'"		{ DO_COMMA_INSERT_CHECK; RETURN (TRANSPOSE); }
+"++"		{ DO_COMMA_INSERT_CHECK; RETURN (PLUS_PLUS); }
+"--"		{ DO_COMMA_INSERT_CHECK; RETURN (MINUS_MINUS); }
+"<="		{ BIN_OP_RETURN (EXPR_LE); }
+"=="		{ BIN_OP_RETURN (EXPR_EQ); }
+"~="		{ BIN_OP_RETURN (EXPR_NE); }
+"!="		{ BIN_OP_RETURN (EXPR_NE); }
+"<>"		{ BIN_OP_RETURN (EXPR_NE); }
+">="		{ BIN_OP_RETURN (EXPR_GE); }
+"||"		{ BIN_OP_RETURN (EXPR_OR); }
+"&&"		{ BIN_OP_RETURN (EXPR_AND); }
+"|"		{ BIN_OP_RETURN (EXPR_OR); }
+"&"		{ BIN_OP_RETURN (EXPR_AND); }
+"!"		{ RETURN (EXPR_NOT); }
+"~"		{ BIN_OP_RETURN (EXPR_NOT); }
+"<"		{ BIN_OP_RETURN (EXPR_LT); }
+">"		{ BIN_OP_RETURN (EXPR_GT); }
+"+"		{ BIN_OP_RETURN ('+'); }
+"-"		{ BIN_OP_RETURN ('-'); }
+"**"		{ BIN_OP_RETURN (POW); }
+"*"		{ BIN_OP_RETURN ('*'); }
+"/"		{ BIN_OP_RETURN ('/'); }
+"\\"		{ BIN_OP_RETURN (LEFTDIV); }
+";"		{ RETURN (';'); }
+","		{ RETURN (','); }
+"^"		{ BIN_OP_RETURN (POW); }
+"="		{ RETURN ('='); }
+"("		{
+		  in_brace_or_paren.push (0);
+		  RETURN ('(');
+		}
+")"		{
+		  if (! in_brace_or_paren.empty ())
+		    in_brace_or_paren.pop ();
+		  DO_COMMA_INSERT_CHECK;
+		  current_input_column++;
+		  quote_is_transpose = 1;
+		  return ')';
+		}
+
+.		{
+
+// We return everything else as single character tokens, which should
+// eventually result in a parse error.
+
+		  RETURN (yytext[0]);
+		}
+
+%%
+
+/*
+ * GAG.
+ *
+ * If we're reading a matrix and the next character is '[', make sure
+ * that we insert a comma ahead of it.
+ */
+int
+do_comma_insert_check (void)
+{
+  int tmp_len = yyleng;
+  int c = yyinput ();
+  do_comma_insert = (braceflag && c == '[');
+  return tmp_len;
+}
+
+/*
+ * Fix things up for errors or interrupts.
+ */
+void
+reset_parser (void)
+{
+  BEGIN 0;
+  promptflag = 1;
+  doing_set = 0;
+  braceflag = 0;
+  maybe_screwed = 0;
+  maybe_screwed_again = 0;
+  looping = 0;
+  iffing = 0;
+  ml.clear ();
+  mlnm.clear ();
+  defining_func = 0;
+  curr_sym_tab = top_level_sym_tab;
+  get_input_from_eval_string = 0;
+  quote_is_transpose = 0;
+  current_input_column = 0;
+  do_comma_insert = 0;
+  plotting = 0;
+  in_plot_range = 0;
+  in_plot_using = 0;
+  in_plot_style = 0;
+  cant_be_identifier = 0;
+  convert_spaces_to_comma = 1;
+  beginning_of_function = 0;
+  in_brace_or_paren.clear ();
+  yyrestart (stdin);
+}
+
+static void
+do_string_escapes (char *s)
+{
+  char *p1 = s;
+  char *p2 = s;
+  while (*p2 != '\0')
+    {
+      if (*p2 == '\\' && *(p2+1) != '\0')
+	{
+	  switch (*++p2)
+	    {
+	    case 'a':
+	      *p1 = '\a';
+	      break;
+	    case 'b': // backspace
+	      *p1 = '\b';
+	      break;
+	    case 'f': // formfeed
+	      *p1 = '\f';
+	      break;
+	    case 'n': // newline
+	      *p1 = '\n';
+	      break;
+	    case 'r': // carriage return
+	      *p1 = '\r';
+	      break;
+	    case 't': // horizontal tab
+	      *p1 = '\t';
+	      break;
+	    case 'v': // vertical tab
+	      *p1 = '\v';
+	      break;
+	    case '\\': // backslash
+	      *p1 = '\\';
+	      break;
+	    case '\'': // quote
+	      *p1 = '\'';
+	      break;
+	    case '"': // double quote
+	      *p1 = '"';
+	      break;
+	    default:
+          warning ("unrecognized escape sequence `\\%c' -- converting to `%c'",
+		   *p2, *p2);
+	      *p1 = *p2;
+	      break;
+	    }
+	}
+      else if (*p2 == '\'' && *(p2+1) == '\'')
+	{
+	  *p1 = '\'';
+	  p2++;
+	}
+      else
+	{
+	  *p1 = *p2;
+	}
+
+      p1++;
+      p2++;
+    }
+
+  *p1 = '\0';
+}
+
+static void
+fixup_column_count (char *s)
+{
+  char c;
+  while ((c = *s++) != '\0')
+    {
+      if (c == '\n')
+	  current_input_column = 0;
+      else
+	current_input_column++;
+    }
+}
+
+#ifdef yywrap
+#undef yywrap
+#endif
+int
+yywrap (void)
+{
+  return 0;
+}
+
+/*
+ * Tell us all what the current buffer is.
+ */
+YY_BUFFER_STATE
+current_buffer (void)
+{
+  return YY_CURRENT_BUFFER;
+}
+
+/*
+ * Create a new buffer.
+ */
+YY_BUFFER_STATE
+create_buffer (FILE *f)
+{
+  return yy_create_buffer (f, YY_BUF_SIZE);
+}
+
+/*
+ * Start reading a new buffer.
+ */
+void
+switch_to_buffer (YY_BUFFER_STATE buf)
+{
+  yy_switch_to_buffer (buf);
+}
+
+/*
+ * Delete a buffer.
+ */
+void
+delete_buffer (YY_BUFFER_STATE buf)
+{
+  yy_delete_buffer (buf);
+}
+
+/*
+ * Restore a buffer (for unwind-prot).
+ */
+void
+restore_input_buffer (void *buf)
+{
+  switch_to_buffer ((YY_BUFFER_STATE) buf);
+}
+
+/*
+ * Delete a buffer (for unwind-prot).
+ */
+void
+delete_input_buffer (void *buf)
+{
+  delete_buffer ((YY_BUFFER_STATE) buf);
+}
+
+static char *plot_styles[] = 
+  {
+    "dots",
+    "dots",
+    "errorbars",
+    "impulses",
+    "lines",
+    "linespoints",
+    "points",
+    (char *) NULL,
+  };
+
+static char *
+plot_style_token (char *s)
+{
+  char **tmp = plot_styles;
+  while (*tmp != (char *) NULL)
+    {
+      if (almost_match (*tmp, s))
+	return *tmp;
+
+      tmp++;
+    }
+
+  return (char *) NULL;
+}
+
+static int
+is_plot_keyword (char *s)
+{
+  if (almost_match ("title", s))
+    return TITLE;
+  else if (almost_match ("using", s))
+    { in_plot_using = 1; return USING; }
+  else if (almost_match ("with", s))
+    { in_plot_style = 1; return WITH; }
+  else
+    return 0;
+}
+
+/*
+ * Handle keywords.  Could probably be more efficient...
+ */
+static int
+is_keyword (char *s)
+{
+  if (plotting && in_plot_style)
+    {
+      char *sty = plot_style_token (s);
+      if (sty != (char *) NULL)
+	{
+	  in_plot_style = 0;
+	  yylval.string = strsave (sty);
+	  return STYLE;
+	}
+    }
+
+  int end_found = 0;
+  if (strcmp ("break", s) == 0)
+    return BREAK;
+  else if (strcmp ("continue", s) == 0)
+    return CONTINUE;
+  else if (strcmp ("else", s) == 0)
+    { return ELSE; }
+  else if (strcmp ("elseif", s) == 0)
+    { return ELSEIF; }
+  else if (strcmp ("end", s) == 0)
+    { end_found = 1; yylval.ettype = simple_end; }
+  else if (strcmp ("endfor", s) == 0)
+    { end_found = 1; yylval.ettype = for_end; }
+  else if (strcmp ("endfunction", s) == 0)
+    { end_found = 1; yylval.ettype = function_end; }
+  else if (strcmp ("endif", s) == 0)
+    { end_found = 1; yylval.ettype = if_end; }
+  else if (strcmp ("endwhile", s) == 0)
+    { end_found = 1; yylval.ettype = while_end; }
+  else if (strcmp ("for", s) == 0)
+    { promptflag--; looping++; return FOR; }
+  else if (strcmp ("function", s) == 0)
+    {
+      if (defining_func)
+	{
+	  error ("sorry, nested functions are a no-no...");
+	  jump_to_top_level ();
+	}
+      else
+	{
+	  tmp_local_sym_tab = new symbol_table ();
+	  curr_sym_tab = tmp_local_sym_tab;
+	  defining_func = 1;
+	  promptflag--;
+	  beginning_of_function = 1;
+	  help_buf[0] = '\0';
+	  return FCN;
+	}
+    }
+  else if (strcmp ("global", s) == 0)
+    return GLOBAL;
+  else if (strcmp ("gplot", s) == 0)
+    { plotting = 1; yylval.pttype = two_dee; return PLOT; }
+  else if (strcmp ("gsplot", s) == 0)
+    { plotting = 1; yylval.pttype = three_dee; return PLOT; }
+  else if (strcmp ("if", s) == 0)
+    { iffing++; promptflag--; return IF; }
+  else if (strcmp ("return", s) == 0)
+    return FUNC_RET;
+  else if (strcmp ("while", s) == 0)
+    { promptflag--; looping++; return WHILE; }
+
+  if (end_found)
+    {
+      if (!defining_func && !looping)
+	promptflag++;
+      return END;
+    }
+
+  return 0;
+}
+
+static symbol_record *
+lookup_identifier (char *name)
+{
+  symbol_record *gsr = global_sym_tab->lookup (name, 0, 0);
+
+  if (curr_sym_tab == top_level_sym_tab && gsr != (symbol_record *) NULL)
+    return gsr;
+
+  return curr_sym_tab->lookup (name, 1, 0);
+}
+
+static void
+grab_help_text (void)
+{
+  int max_len = HELP_BUF_LENGTH - 1;
+
+  int in_comment = 1;
+  int len = 0;
+  int c;
+
+  while ((c = yyinput ()) != EOF)
+    {
+      if (in_comment)
+	{
+	  help_buf[len++] = c;
+	  if (c == '\n')
+	    in_comment = 0;
+	}
+      else
+	{
+	  switch (c)
+	    {
+	    case '%':
+	    case '#':
+	      in_comment = 1;
+	    case ' ':
+	    case '\t':
+	      break;
+	    default:
+	      goto done;
+	    }
+	}
+
+      if (len > max_len)
+	{
+	  message ("grab_help_text",
+		   "buffer overflow after caching %d characters",
+		   max_len);
+
+	  goto done;
+	}
+    }
+
+ done:
+
+// Make sure there's an end of line so yylex sees an end to the
+// comment immediately.
+
+  yyunput (c, yytext);
+  if (c != '\n')
+    yyunput ('\n', yytext);
+
+  help_buf[len] =  '\0';
+}
+
+static int
+match_any (char c, char *s)
+{
+  char tmp;
+  while ((tmp = *s++) != '\0')
+    {
+      if (c == tmp)
+	return 1;
+    }
+  return 0;
+}
+
+static int
+looks_like_bin_op (int spc_prev, int spc_next)
+{
+  return ((spc_prev && spc_next) || ! (spc_prev || spc_next));
+}
+
+static int
+next_char_is_space (void)
+{
+  int c = yyinput ();
+  yyunput (c, yytext);
+  return (c == ' ' || c == '\t');
+}
+
+static int
+next_token_is_postfix_unary_op (int spc_prev, char *yytext)
+{
+  int un_op = 0;
+
+  int c0 = yyinput ();
+  int c1 = yyinput ();
+
+  yyunput (c1, yytext);
+  yyunput (c0, yytext);
+
+  int transpose = (c0 == '.' && c1 == '\'');
+  int hermitian = (c0 == '\'');
+
+  un_op = (transpose || (hermitian && ! spc_prev));
+
+  return un_op;
+}
+
+static int
+next_token_is_bin_op (int spc_prev, char *yytext)
+{
+  int bin_op = 0;
+  int spc_next = 0;
+
+  int c0 = yyinput ();
+  int c1 = yyinput ();
+
+  switch (c0)
+    {
+    case '+':  case '-':  case '/':
+    case ':':  case '\\': case '^':
+      spc_next = (c1 == ' ' || c1 == '\t');
+      break;
+
+    case '&':
+      if (c1 == '&')
+	spc_next = next_char_is_space ();
+      else
+	spc_next = (c1 == ' ' || c1 == '\t');
+      break;
+
+    case '*':
+      if (c1 == '*')
+	spc_next = next_char_is_space ();
+      else
+	spc_next = (c1 == ' ' || c1 == '\t');
+      break;
+	
+    case '|':
+      if (c1 == '|')
+	spc_next = next_char_is_space ();
+      else
+	spc_next = (c1 == ' ' || c1 == '\t');
+      break;
+
+    case '<':
+      if (c1 == '=' || c1 == '>')
+	spc_next = next_char_is_space ();
+      else
+	spc_next = (c1 == ' ' || c1 == '\t');
+      break;
+
+    case '>':
+      if (c1 == '=')
+	spc_next = next_char_is_space ();
+      else
+	spc_next = (c1 == ' ' || c1 == '\t');
+      break;
+
+    case '~':  case '!':  case '=':
+      if (c1 == '=')
+	spc_next = next_char_is_space ();
+      else
+	goto done;
+      break;
+
+    case '.':
+      if (c1 == '*')
+	{
+	  int c2 = yyinput ();
+	  if (c2 == '*')
+	    spc_next = next_char_is_space ();
+	  else
+	    spc_next = (c2 == ' ' || c2 == '\t');
+	  yyunput (c2, yytext);
+	}
+      else if (c1 == '/' || c1 == '\\' || c1 == '^')
+	spc_next = next_char_is_space ();
+      else
+	goto done;
+      break;
+
+    default:
+      goto done;
+    }
+
+  bin_op = looks_like_bin_op (spc_prev, spc_next);
+
+ done:
+  yyunput (c1, yytext);
+  yyunput (c0, yytext);
+
+  return bin_op;
+}
+
+char *
+strip_trailing_whitespace (char *s)
+{
+  char *retval = strsave (s);
+
+  char *t = strchr (retval, ' ');
+  if (t != (char *) NULL)
+    *t = '\0';
+
+  t = strchr (retval, '\t');
+  if (t != (char *) NULL)
+    *t = '\0';
+
+  return retval;
+}
+
+void
+check_for_garbage_after_fcn_def (void)
+{
+// By making a newline be the next character to be read, we will force
+// the parser to return after reading the function.  Calling yyunput
+// with EOF seems not to work...
+
+  int in_comment = 0;
+  int lineno = input_line_number;
+  int c;
+  while ((c = yyinput ()) != EOF)
+    {
+      switch (c)
+	{
+	case ' ':
+	case '\t':
+	case ';':
+	case ',':
+	  break;
+	case '\n':
+	  if (in_comment)
+	    in_comment = 0;
+	  break;
+	case '%':
+	case '#':
+	  in_comment = 1;
+	  break;
+	default:
+	  if (in_comment)
+	    break;
+	  else
+	    {
+	      warning ("ignoring trailing garbage after end of function\n\
+         near line %d of file `%s.m'", lineno, curr_m_file_name);
+	      
+	      yyunput ('\n', yytext);
+	      return;
+	    }
+	}
+    }
+  yyunput ('\n', yytext);
+}
+
+/* Maybe someday...
+
+"+="		return ADD_EQ;
+"-="		return SUB_EQ;
+"*="		return MUL_EQ;
+"/="		return DIV_EQ;
+"\\="		return LEFTDIV_EQ;
+".+="		return ADD_EQ;
+".-="		return SUB_EQ;
+".*="		return EMUL_EQ;
+"./="		return EDIV_EQ;
+".\\="		return ELEFTDIV_EQ;
+
+*/