/******************************************************************************* * Copyright (c) 2013-2021, Andrés Martinelli * * All rights reserved. * * * * This file is a part of sc-im * * * * sc-im is a spreadsheet program that is based on sc. The original authors * * of sc are James Gosling and Mark Weiser, and mods were later added by * * Chuck Martin. * * * * Redistribution and use in source and binary forms, with or without * * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * * notice, this list of conditions and the following disclaimer in the * * documentation and/or other materials provided with the distribution. * * 3. All advertising materials mentioning features or use of this software * * must display the following acknowledgement: * * This product includes software developed by Andrés Martinelli * * . * * 4. Neither the name of the Andrés Martinelli nor the * * names of other contributors may be used to endorse or promote products * * derived from this software without specific prior written permission. * * * * THIS SOFTWARE IS PROVIDED BY ANDRES MARTINELLI ''AS IS'' AND ANY * * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * * DISCLAIMED. IN NO EVENT SHALL ANDRES MARTINELLI BE LIABLE FOR ANY * * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;* * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *******************************************************************************/ /** * \file lex.c * \author Andrés Martinelli * \date 2017-07-18 * \brief TODO Write a tbrief file description. */ #include #include #include #include #include #include #include #include #include #include #include "lex.h" #include "sc.h" #include "conf.h" #include "utils/string.h" typedef int bool; enum { false, true }; #include "y.tab.h" jmp_buf wakeup; jmp_buf fpe_buf; bool decimal = FALSE; /** * \brief TODO Document fpe_trap() * * \return none */ void fpe_trap(int signo) { #if defined(i386) asm(" fnclex"); asm(" fwait"); #endif longjmp(fpe_buf, 1); } struct key { char * key; int val; }; struct key experres[] = { #include "experres.h" { 0, 0 } }; struct key statres[] = { #include "statres.h" { 0, 0 } }; #include "macros.h" #include "tui.h" #include "range.h" /** * \brief TODO Document yylex() * * \return none */ int yylex() { char * p = line + linelim; int ret = 0; static int isfunc = 0; static bool isgoto = 0; static bool colstate = 0; static int dateflag; static char *tokenst = NULL; static int tokenl; while (isspace(*p)) p++; if (*p == '\0') { isfunc = isgoto = 0; ret = -1; } else if (isalpha(*p) || (*p == '_')) { register char *la; /* lookahead pointer */ register struct key *tblp; if ( !tokenst ) { tokenst = p; tokenl = 0; } /* * This picks up either 1 or 2 alpha characters (a column) or * tokens made up of alphanumeric chars and '_' (a function or * token or command or a range name) */ while (isalpha(*p) && isascii(*p)) { p++; tokenl++; } la = p; while (isdigit(*la) || (*la == '$')) la++; /* * A COL is 1 or 2 char alpha with nothing but digits following * (no alpha or '_') */ if (!isdigit(*tokenst) && tokenl && tokenl <= 2 && (colstate || (isdigit(*(la-1)) && !(isalpha(*la) || (*la == '_'))))) { ret = COL; yylval.ival = atocol(tokenst, tokenl); } else { while (isalpha(*p) || (*p == '_') || isdigit(*p)) { p++; tokenl++; } ret = WORD; if (!linelim || isfunc) { if (isfunc) isfunc--; for (tblp = linelim ? experres : statres; tblp->key; tblp++) { if (((tblp->key[0]^tokenst[0])&0137)==0) { int i = 1; while (tblp->key[i] && ikey[i])&0137)==0) i++; if (i >= tokenl && tblp->key[i] == '\0') { ret = tblp->val; colstate = (ret <= S_FORMAT); if (isgoto) { isfunc = isgoto = 0; if (ret != K_ERROR && ret != K_INVALID) ret = WORD; } break; } } } } if (ret == WORD) { if (tokenst && ( ! strncmp(line, "nmap", 4) || ! strncmp(line, "imap", 4) || ! strncmp(line, "vmap", 4) ) ) { char * p = tokenst; char * ptr = p; while (*ptr && ( (*ptr != ' ') //|| (*ptr != '\t') || (*(ptr-1) == '\\'))) ptr++; ptr = malloc((unsigned) (ptr - p)); yylval.sval = ptr; while ( *p && ( (*p != ' ') || (*p != '\t') || ( *(p+1) && *(p-1) && *(p-1) == '\\' && *(p+1) != '\0' && *(p+1) != '\n' && *(p+1) != '\r' ) ) ) *ptr++ = *p++; if (*p && *p == ' ') (*ptr)--; (*ptr)--; if (*ptr) *ptr = '\0'; ret = MAPWORD; } else { struct range * r; if (! find_range(tokenst, tokenl, (struct ent *)0, (struct ent *)0, &r)) { yylval.rval.left = r->r_left; yylval.rval.right = r->r_right; if (r->r_is_range) ret = RANGE; else ret = VAR; } else if ( str_in_str(line, ":") == -1) { yylval.sval = tokenst; } else { linelim = p-line; yyerror("Unintelligible word"); } } } } // 117 } else if ((*p == '.') || isdigit(*p)) { // 89 void (*sig_save)(); double v = 0.0; int temp; char *nstart = p; sig_save = signal(SIGFPE, fpe_trap); if (setjmp(fpe_buf)) { (void) signal(SIGFPE, sig_save); yylval.fval = v; sc_error("Floating point exception\n"); isfunc = isgoto = 0; tokenst = NULL; return FNUMBER; } if (*p=='.' && dateflag) { /* .'s in dates are returned as tokens. */ ret = *p++; dateflag--; } else { if (*p != '.') { tokenst = p; tokenl = 0; do { v = v*10.0 + (double) ((unsigned) *p - '0'); tokenl++; } while (isdigit(*++p)); if (dateflag) { ret = NUMBER; yylval.ival = (int)v; /* * If a string of digits is followed by two .'s separated by * one or two digits, assume this is a date and return the * .'s as tokens instead of interpreting them as decimal * points. dateflag counts the .'s as they're returned. */ } else if (*p=='.' && isdigit(*(p+1)) && (*(p+2)=='.' || (isdigit(*(p+2)) && *(p+3)=='.'))) { ret = NUMBER; yylval.ival = (int)v; dateflag = 2; } else if (*p == 'e' || *p == 'E') { while (isdigit(*++p)) ; if (isalpha(*p) || *p == '_') { linelim = p - line; return (yylex()); } else ret = FNUMBER; } else if (isalpha(*p) || *p == '_') { linelim = p - line; return (yylex()); } } if ((!dateflag && *p=='.') || ret == FNUMBER) { ret = FNUMBER; yylval.fval = strtod(nstart, &p); if (!isfinite(yylval.fval)) ret = K_ERR; else decimal = TRUE; } else { /* A NUMBER must hold at least MAXROW and MAXCOL */ /* This is consistent with a short row and col in struct ent */ //if (v > (double)32767 || v < (double)-32768) { //if (v > (double) INT_MAX || v < (double) INT_MIN) { if (v > (double) MAXROWS || v < (double) -MAXROWS) { ret = FNUMBER; yylval.fval = v; } else { temp = (int)v; if((double)temp != v) { ret = FNUMBER; yylval.fval = v; } else { ret = NUMBER; yylval.ival = temp; } } } } (void) signal(SIGFPE, sig_save); } else if (*p=='"') { char *ptr; ptr = p+1; /* "string" or "string\"quoted\"" */ while (*ptr && ((*ptr != '"') || (*(ptr-1) == '\\'))) ptr++; ptr = scxmalloc((unsigned)(ptr-p)); yylval.sval = ptr; p++; while (*p && ((*p != '"') || (*(p-1) == '\\' && *(p+1) != '\0' && *(p+1) != '\n'))) *ptr++ = *p++; *ptr = '\0'; if (*p) p++; ret = STRING; } else if (*p=='[') { while (*p && *p!=']') p++; if (*p) p++; linelim = p-line; tokenst = NULL; return yylex(); } else { ret = *p++; } linelim = p-line; if (!isfunc) isfunc = ((ret == '@') + (ret == S_GOTO) - (ret == S_SET)); if (ret == S_GOTO) isgoto = TRUE; tokenst = NULL; return ret; } /** * \brief TODO Document atocol() * * \details Given a token string starting with a symbolid column name and * its valid length, convert column name ("A"-"Z" or "AA"-"ZZ") to a * column number (0-N). Never mind if the column number is illegal * (too high). The procedure's name and function are the inverse of coltoa(). * * \details Case-insensitivity is done crudely, but ignoring the 040 bit. * * \return none */ int atocol(char *string, int len) { register int col; col = (toupper(string[0])) - 'A'; if (len == 2) /* has second char */ col = ((col + 1) * 26) + ((toupper(string[1])) - 'A'); return (col); }