Mercurial > hg > octave-nkf
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
new file mode 100644 --- /dev/null +++ b/src/lex.l @@ -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; + +*/