| /**************************************************************************** |
| * apps/interpreters/minibasic/basic.c |
| * |
| * Copyright (C) 2016 Gregory Nutt. All rights reserved. |
| * |
| * This file was taken from Mini Basic, versino 1.0 developed by Malcolm |
| * McLean, Leeds University. Mini Basic version 1.0 was released the |
| * Creative Commons Attribution license which, from my reading, appears to |
| * be compatible with the NuttX BSD-style license: |
| * |
| * 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. Neither the name NuttX nor the names of its contributors may be |
| * used to endorse or promote products derived from this software |
| * without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "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 THE |
| * COPYRIGHT OWNER OR CONTRIBUTORS 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGE. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <stdarg.h> |
| #include <math.h> |
| #include <limits.h> |
| #include <ctype.h> |
| #include <assert.h> |
| |
| /**************************************************************************** |
| * Pre-processor Definitions |
| ****************************************************************************/ |
| |
| /* Configuration */ |
| |
| #ifndef CONFIG_INTERPRETER_MINIBASIC_IOBUFSIZE |
| # define CONFIG_INTERPRETER_MINIBASIC_IOBUFSIZE 1024 |
| #endif |
| |
| #define IOBUFSIZE CONFIG_INTERPRETER_MINIBASIC_IOBUFSIZE |
| |
| /* Tokens defined */ |
| |
| #define EOS 0 |
| #define VALUE 1 |
| #define PI 2 |
| #define E 3 |
| |
| #define DIV 10 |
| #define MULT 11 |
| #define OPAREN 12 |
| #define CPAREN 13 |
| #define PLUS 14 |
| #define MINUS 15 |
| #define SHRIEK 16 |
| #define COMMA 17 |
| #define MOD 200 |
| |
| #define SYNTAX_ERROR 20 |
| #define EOL 21 |
| #define EQUALS 22 |
| #define STRID 23 |
| #define FLTID 24 |
| #define DIMFLTID 25 |
| #define DIMSTRID 26 |
| #define QUOTE 27 |
| #define GREATER 28 |
| #define LESS 29 |
| #define SEMICOLON 30 |
| |
| #define PRINT 100 |
| #define LET 101 |
| #define DIM 102 |
| #define IF 103 |
| #define THEN 104 |
| #define AND 105 |
| #define OR 106 |
| #define GOTO 107 |
| #define INPUT 108 |
| #define REM 109 |
| #define FOR 110 |
| #define TO 111 |
| #define NEXT 112 |
| #define STEP 113 |
| |
| #define SIN 5 |
| #define COS 6 |
| #define TAN 7 |
| #define LN 8 |
| #define POW 9 |
| #define SQRT 18 |
| #define ABS 201 |
| #define LEN 202 |
| #define ASCII 203 |
| #define ASIN 204 |
| #define ACOS 205 |
| #define ATAN 206 |
| #define INT 207 |
| #define RND 208 |
| #define VAL 209 |
| #define VALLEN 210 |
| #define INSTR 211 |
| |
| #define CHRSTRING 300 |
| #define STRSTRING 301 |
| #define LEFTSTRING 302 |
| #define RIGHTSTRING 303 |
| #define MIDSTRING 304 |
| #define STRINGSTRING 305 |
| |
| /* Relational operators defined */ |
| |
| #define ROP_EQ 1 /* equals */ |
| #define ROP_NEQ 2 /* doesn't equal */ |
| #define ROP_LT 3 /* less than */ |
| #define ROP_LTE 4 /* less than or equals */ |
| #define ROP_GT 5 /* greater than */ |
| #define ROP_GTE 6 /* greater than or equals */ |
| |
| /* Error codes (in BASIC script) defined */ |
| |
| #define ERR_CLEAR 0 |
| #define ERR_SYNTAX 1 |
| #define ERR_OUTOFMEMORY 2 |
| #define ERR_IDTOOLONG 3 |
| #define ERR_NOSUCHVARIABLE 4 |
| #define ERR_BADSUBSCRIPT 5 |
| #define ERR_TOOMANYDIMS 6 |
| #define ERR_TOOMANYINITS 7 |
| #define ERR_BADTYPE 8 |
| #define ERR_TOOMANYFORS 9 |
| #define ERR_NONEXT 10 |
| #define ERR_NOFOR 11 |
| #define ERR_DIVIDEBYZERO 12 |
| #define ERR_NEGLOG 13 |
| #define ERR_NEGSQRT 14 |
| #define ERR_BADSINCOS 15 |
| #define ERR_EOF 16 |
| #define ERR_ILLEGALOFFSET 17 |
| #define ERR_TYPEMISMATCH 18 |
| #define ERR_INPUTTOOLONG 19 |
| #define ERR_BADVALUE 20 |
| #define ERR_NOTINT 21 |
| |
| #define MAXFORS 32 /* Maximum number of nested fors */ |
| |
| /**************************************************************************** |
| * Private Types |
| ****************************************************************************/ |
| |
| struct mb_line_s |
| { |
| int no; /* Line number */ |
| FAR const char *str; /* Points to start of line */ |
| }; |
| |
| struct mb_variable_s |
| { |
| char id[32]; /* Id of variable */ |
| double dval; /* Its value if a real */ |
| FAR char *sval; /* Its value if a string (malloced) */ |
| }; |
| |
| struct mb_dimvar_s |
| { |
| char id[32]; /* Id of dimensioned variable */ |
| int type; /* Its type, STRID or FLTID */ |
| int ndims; /* Number of dimensions */ |
| int dim[5]; /* Dimensions in x y order */ |
| FAR char **str; /* Pointer to string data */ |
| FAR double *dval; /* Pointer to real data */ |
| }; |
| |
| struct mb_lvalue_s |
| { |
| int type; /* Type of variable (STRID or FLTID or SYNTAX_ERROR) */ |
| FAR char **sval; /* Pointer to string data */ |
| FAR double *dval; /* Pointer to real data */ |
| }; |
| |
| struct mb_forloop_s |
| { |
| char id[32]; /* Id of control variable */ |
| int nextline; /* Line below FOR to which control passes */ |
| double toval; /* Terminal value */ |
| double step; /* Step size */ |
| }; |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| /* NOTE: The use of these globals precludes the use of Mini Basic on |
| * multiple threads (at least in a flat address environment). If you |
| * want multiple copies of Mini Basic to run, you would need to: |
| * (1) Create a new struct mb_state_s that contains all of the following |
| * as fields. |
| * (2) Allocate an instance of struct mb_state_s in basic() as part of the |
| * initialization logic. And, |
| * (3) Pass the instance of struct mb_state_s to every Mini Basic function. |
| */ |
| |
| static struct mb_forloop_s g_forstack[MAXFORS]; /* Stack for for loop control */ |
| static int nfors; /* Number of fors on stack */ |
| |
| static FAR struct mb_variable_s *g_variables; /* The script's variables */ |
| static int g_nvariables; /* Number of variables */ |
| |
| static FAR struct mb_dimvar_s *g_dimvariables; /* Dimensioned arrays */ |
| static int g_ndimvariables; /* Number of dimensioned arrays */ |
| |
| static FAR struct mb_line_s *g_lines; /* List of line starts */ |
| static int nlines; /* Number of BASIC g_lines in program */ |
| |
| static FILE *g_fpin; /* Input stream */ |
| static FILE *g_fpout; /* Output stream */ |
| static FILE *g_fperr; /* Error stream */ |
| |
| static FAR const char *g_string; /* String we are parsing */ |
| static int g_token; /* Current token (lookahead) */ |
| static int g_errorflag; /* Set when error in input encountered */ |
| static char g_iobuffer[IOBUFSIZE]; /* I/O buffer */ |
| |
| /**************************************************************************** |
| * Private Function Prototypes |
| ****************************************************************************/ |
| |
| static int setup(FAR const char *script); |
| static void cleanup(void); |
| |
| static void reporterror(int lineno); |
| static int findline(int no); |
| |
| static int line(void); |
| static void doprint(void); |
| static void dolet(void); |
| static void dodim(void); |
| static int doif(void); |
| static int dogoto(void); |
| static void doinput(void); |
| static void dorem(void); |
| static int dofor(void); |
| static int donext(void); |
| |
| static void lvalue(FAR struct mb_lvalue_s *lv); |
| |
| static int boolexpr(void); |
| static int boolfactor(void); |
| static int relop(void); |
| |
| static double expr(void); |
| static double term(void); |
| static double factor(void); |
| static double instr(void); |
| static double variable(void); |
| static double dimvariable(void); |
| |
| static FAR struct mb_variable_s *findvariable(FAR const char *id); |
| static FAR struct mb_dimvar_s *finddimvar(FAR const char *id); |
| static FAR struct mb_dimvar_s *dimension(FAR const char *id, int ndims, ...); |
| static FAR void *getdimvar(FAR struct mb_dimvar_s *dv, ...); |
| static FAR struct mb_variable_s *addfloat(FAR const char *id); |
| static FAR struct mb_variable_s *addstring(FAR const char *id); |
| static FAR struct mb_dimvar_s *adddimvar(FAR const char *id); |
| |
| static FAR char *stringexpr(void); |
| static FAR char *chrstring(void); |
| static FAR char *strstring(void); |
| static FAR char *leftstring(void); |
| static FAR char *rightstring(void); |
| static FAR char *midstring(void); |
| static FAR char *stringstring(void); |
| static FAR char *stringdimvar(void); |
| static FAR char *stringvar(void); |
| static FAR char *stringliteral(void); |
| |
| static int integer(double x); |
| |
| static void match(int tok); |
| static void seterror(int errorcode); |
| static int getnextline(FAR const char *str); |
| static int gettoken(FAR const char *str); |
| static int tokenlen(FAR const char *str, int tokenid); |
| |
| static int isstring(int tokenid); |
| static double getvalue(FAR const char *str, FAR int *len); |
| static void getid(FAR const char *str, FAR char *out, FAR int *len); |
| |
| static void mystrgrablit(FAR char *dest, FAR const char *src); |
| static FAR char *mystrend(FAR const char *str, char quote); |
| static int mystrcount(FAR const char *str, char ch); |
| static FAR char *mystrdup(FAR const char *str); |
| static FAR char *mystrconcat(FAR const char *str, FAR const char *cat); |
| static double factorial(double x); |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: setup |
| * |
| * Description: |
| * Sets up all our globals, including the list of lines. |
| * Params: script - the script passed by the user |
| * Returns: 0 on success, -1 on failure |
| * |
| * |
| ****************************************************************************/ |
| |
| static int setup(FAR const char *script) |
| { |
| int i; |
| |
| nlines = mystrcount(script, '\n'); |
| g_lines = malloc(nlines * sizeof(struct mb_line_s)); |
| if (!g_lines) |
| { |
| if (g_fperr) |
| { |
| fprintf(g_fperr, "Out of memory\n"); |
| } |
| |
| return -1; |
| } |
| |
| for (i = 0; i < nlines; i++) |
| { |
| if (isdigit(*script)) |
| { |
| g_lines[i].str = script; |
| g_lines[i].no = strtol(script, 0, 10); |
| } |
| else |
| { |
| i--; |
| nlines--; |
| } |
| |
| script = strchr(script, '\n'); |
| script++; |
| } |
| |
| if (!nlines) |
| { |
| if (g_fperr) |
| { |
| fprintf(g_fperr, "Can't read program\n"); |
| } |
| |
| free(g_lines); |
| return -1; |
| } |
| |
| for (i = 1; i < nlines; i++) |
| { |
| if (g_lines[i].no <= g_lines[i - 1].no) |
| { |
| if (g_fperr) |
| { |
| fprintf(g_fperr, "program lines %d and %d not in order\n", |
| g_lines[i - 1].no, g_lines[i].no); |
| } |
| |
| free(g_lines); |
| return -1; |
| } |
| } |
| |
| g_nvariables = 0; |
| g_variables = 0; |
| |
| g_dimvariables = 0; |
| g_ndimvariables = 0; |
| |
| return 0; |
| } |
| |
| /**************************************************************************** |
| * Name: cleanup |
| * |
| * Description: |
| * Frees all the memory we have allocated |
| * |
| ****************************************************************************/ |
| |
| static void cleanup(void) |
| { |
| int i; |
| int ii; |
| int size; |
| |
| for (i = 0; i < g_nvariables; i++) |
| { |
| if (g_variables[i].sval) |
| { |
| free(g_variables[i].sval); |
| } |
| } |
| |
| if (g_variables) |
| { |
| free(g_variables); |
| } |
| |
| g_variables = 0; |
| g_nvariables = 0; |
| |
| for (i = 0; i < g_ndimvariables; i++) |
| { |
| if (g_dimvariables[i].type == STRID) |
| { |
| if (g_dimvariables[i].str) |
| { |
| size = 1; |
| for (ii = 0; ii < g_dimvariables[i].ndims; ii++) |
| { |
| size *= g_dimvariables[i].dim[ii]; |
| } |
| |
| for (ii = 0; ii < size; ii++) |
| { |
| if (g_dimvariables[i].str[ii]) |
| { |
| free(g_dimvariables[i].str[ii]); |
| } |
| } |
| |
| free(g_dimvariables[i].str); |
| } |
| } |
| else if (g_dimvariables[i].dval) |
| { |
| free(g_dimvariables[i].dval); |
| } |
| } |
| |
| if (g_dimvariables) |
| { |
| free(g_dimvariables); |
| } |
| |
| g_dimvariables = 0; |
| g_ndimvariables = 0; |
| |
| if (g_lines) |
| { |
| free(g_lines); |
| } |
| |
| g_lines = 0; |
| nlines = 0; |
| } |
| |
| /**************************************************************************** |
| * Name: reporterror |
| * |
| * Description: |
| * Frror report function. |
| * For reporting errors in the user's script. |
| * Checks the global g_errorflag. |
| * Writes to g_fperr. |
| * Params: lineno - the line on which the error occurred |
| * |
| ****************************************************************************/ |
| |
| static void reporterror(int lineno) |
| { |
| if (!g_fperr) |
| { |
| return; |
| } |
| |
| switch (g_errorflag) |
| { |
| case ERR_CLEAR: |
| assert(0); |
| break; |
| |
| case ERR_SYNTAX: |
| fprintf(g_fperr, "Syntax error line %d\n", lineno); |
| break; |
| |
| case ERR_OUTOFMEMORY: |
| fprintf(g_fperr, "Out of memory line %d\n", lineno); |
| break; |
| |
| case ERR_IDTOOLONG: |
| fprintf(g_fperr, "Identifier too long line %d\n", lineno); |
| break; |
| |
| case ERR_NOSUCHVARIABLE: |
| fprintf(g_fperr, "No such variable line %d\n", lineno); |
| break; |
| |
| case ERR_BADSUBSCRIPT: |
| fprintf(g_fperr, "Bad subscript line %d\n", lineno); |
| break; |
| |
| case ERR_TOOMANYDIMS: |
| fprintf(g_fperr, "Too many dimensions line %d\n", lineno); |
| break; |
| |
| case ERR_TOOMANYINITS: |
| fprintf(g_fperr, "Too many initialisers line %d\n", lineno); |
| break; |
| |
| case ERR_BADTYPE: |
| fprintf(g_fperr, "Illegal type line %d\n", lineno); |
| break; |
| |
| case ERR_TOOMANYFORS: |
| fprintf(g_fperr, "Too many nested fors line %d\n", lineno); |
| break; |
| |
| case ERR_NONEXT: |
| fprintf(g_fperr, "For without matching next line %d\n", lineno); |
| break; |
| |
| case ERR_NOFOR: |
| fprintf(g_fperr, "Next without matching for line %d\n", lineno); |
| break; |
| |
| case ERR_DIVIDEBYZERO: |
| fprintf(g_fperr, "Divide by zero lne %d\n", lineno); |
| break; |
| |
| case ERR_NEGLOG: |
| fprintf(g_fperr, "Negative logarithm line %d\n", lineno); |
| break; |
| |
| case ERR_NEGSQRT: |
| fprintf(g_fperr, "Negative square root line %d\n", lineno); |
| break; |
| |
| case ERR_BADSINCOS: |
| fprintf(g_fperr, "Sine or cosine out of range line %d\n", lineno); |
| break; |
| |
| case ERR_EOF: |
| fprintf(g_fperr, "End of input file %d\n", lineno); |
| break; |
| |
| case ERR_ILLEGALOFFSET: |
| fprintf(g_fperr, "Illegal offset line %d\n", lineno); |
| break; |
| |
| case ERR_TYPEMISMATCH: |
| fprintf(g_fperr, "Type mismatch line %d\n", lineno); |
| break; |
| |
| case ERR_INPUTTOOLONG: |
| fprintf(g_fperr, "Input too long line %d\n", lineno); |
| break; |
| |
| case ERR_BADVALUE: |
| fprintf(g_fperr, "Bad value at line %d\n", lineno); |
| break; |
| |
| case ERR_NOTINT: |
| fprintf(g_fperr, "Not an integer at line %d\n", lineno); |
| break; |
| |
| default: |
| fprintf(g_fperr, "ERROR line %d\n", lineno); |
| break; |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: findline |
| * |
| * Description: |
| * Binary search for a line |
| * Params: no - line number to find |
| * Returns: index of the line, or -1 on fail. |
| * |
| ****************************************************************************/ |
| |
| static int findline(int no) |
| { |
| int high; |
| int low; |
| int mid; |
| |
| low = 0; |
| high = nlines - 1; |
| while (high > low + 1) |
| { |
| mid = (high + low) / 2; |
| if (g_lines[mid].no == no) |
| { |
| return mid; |
| } |
| |
| if (g_lines[mid].no > no) |
| { |
| high = mid; |
| } |
| else |
| { |
| low = mid; |
| } |
| } |
| |
| if (g_lines[low].no == no) |
| { |
| mid = low; |
| } |
| else if (g_lines[high].no == no) |
| { |
| mid = high; |
| } |
| else |
| { |
| mid = -1; |
| } |
| |
| return mid; |
| } |
| |
| /**************************************************************************** |
| * Name: line |
| * |
| * Description: |
| * Parse a line. High level parse function |
| * |
| ****************************************************************************/ |
| |
| static int line(void) |
| { |
| int answer = 0; |
| FAR const char *str; |
| |
| match(VALUE); |
| |
| switch (g_token) |
| { |
| case PRINT: |
| doprint(); |
| break; |
| |
| case LET: |
| dolet(); |
| break; |
| |
| case DIM: |
| dodim(); |
| break; |
| |
| case IF: |
| answer = doif(); |
| break; |
| |
| case GOTO: |
| answer = dogoto(); |
| break; |
| |
| case INPUT: |
| doinput(); |
| break; |
| |
| case REM: |
| dorem(); |
| return 0; |
| |
| case FOR: |
| answer = dofor(); |
| break; |
| |
| case NEXT: |
| answer = donext(); |
| break; |
| |
| default: |
| seterror(ERR_SYNTAX); |
| break; |
| } |
| |
| if (g_token != EOS) |
| { |
| /* match(VALUE); */ |
| |
| /* check for a newline */ |
| |
| str = g_string; |
| while (isspace(*str)) |
| { |
| if (*str == '\n') |
| { |
| break; |
| } |
| |
| str++; |
| } |
| |
| if (*str != '\n') |
| { |
| seterror(ERR_SYNTAX); |
| } |
| } |
| |
| return answer; |
| } |
| |
| /**************************************************************************** |
| * Name: doprint |
| * |
| * Description: |
| * The PRINT statement |
| * |
| ****************************************************************************/ |
| |
| static void doprint(void) |
| { |
| FAR char *str; |
| double x; |
| |
| match(PRINT); |
| |
| while (1) |
| { |
| if (isstring(g_token)) |
| { |
| str = stringexpr(); |
| if (str) |
| { |
| fprintf(g_fpout, "%s", str); |
| free(str); |
| } |
| } |
| else |
| { |
| x = expr(); |
| fprintf(g_fpout, "%g", x); |
| } |
| |
| if (g_token == COMMA) |
| { |
| fprintf(g_fpout, " "); |
| match(COMMA); |
| } |
| else |
| { |
| break; |
| } |
| } |
| |
| if (g_token == SEMICOLON) |
| { |
| match(SEMICOLON); |
| fflush(g_fpout); |
| } |
| else |
| { |
| fprintf(g_fpout, "\n"); |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: dolet |
| * |
| * Description: |
| * The LET statement |
| * |
| ****************************************************************************/ |
| |
| static void dolet(void) |
| { |
| struct mb_lvalue_s lv; |
| FAR char *temp; |
| |
| match(LET); |
| lvalue(&lv); |
| match(EQUALS); |
| switch (lv.type) |
| { |
| case FLTID: |
| *lv.dval = expr(); |
| break; |
| |
| case STRID: |
| temp = *lv.sval; |
| *lv.sval = stringexpr(); |
| if (temp) |
| { |
| free(temp); |
| } |
| |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: dodim |
| * |
| * Description: |
| * The DIM statement |
| * |
| ****************************************************************************/ |
| |
| static void dodim(void) |
| { |
| int ndims = 0; |
| double dims[6]; |
| char name[32]; |
| int len; |
| FAR struct mb_dimvar_s *dimvar; |
| int i; |
| int size = 1; |
| |
| match(DIM); |
| |
| switch (g_token) |
| { |
| case DIMFLTID: |
| case DIMSTRID: |
| getid(g_string, name, &len); |
| match(g_token); |
| dims[ndims++] = expr(); |
| while (g_token == COMMA) |
| { |
| match(COMMA); |
| dims[ndims++] = expr(); |
| if (ndims > 5) |
| { |
| seterror(ERR_TOOMANYDIMS); |
| return; |
| } |
| } |
| |
| match(CPAREN); |
| |
| for (i = 0; i < ndims; i++) |
| { |
| if (dims[i] < 0 || dims[i] != (int)dims[i]) |
| { |
| seterror(ERR_BADSUBSCRIPT); |
| return; |
| } |
| } |
| |
| switch (ndims) |
| { |
| case 1: |
| dimvar = dimension(name, 1, (int)dims[0]); |
| break; |
| |
| case 2: |
| dimvar = dimension(name, 2, (int)dims[0], (int)dims[1]); |
| break; |
| |
| case 3: |
| dimvar = dimension(name, 3, (int)dims[0], |
| (int)dims[1], (int)dims[2]); |
| break; |
| |
| case 4: |
| dimvar = |
| dimension(name, 4, (int)dims[0], (int)dims[1], (int)dims[2], |
| (int)dims[3]); |
| break; |
| |
| case 5: |
| dimvar = |
| dimension(name, 5, (int)dims[0], (int)dims[1], (int)dims[2], |
| (int)dims[3], (int)dims[4]); |
| break; |
| } |
| break; |
| |
| default: |
| seterror(ERR_SYNTAX); |
| return; |
| } |
| |
| if (dimvar == 0) |
| { |
| /* Out of memory */ |
| |
| seterror(ERR_OUTOFMEMORY); |
| return; |
| } |
| |
| if (g_token == EQUALS) |
| { |
| match(EQUALS); |
| |
| for (i = 0; i < dimvar->ndims; i++) |
| { |
| size *= dimvar->dim[i]; |
| } |
| |
| switch (dimvar->type) |
| { |
| case FLTID: |
| i = 0; |
| dimvar->dval[i++] = expr(); |
| while (g_token == COMMA && i < size) |
| { |
| match(COMMA); |
| dimvar->dval[i++] = expr(); |
| if (g_errorflag) |
| { |
| break; |
| } |
| } |
| break; |
| |
| case STRID: |
| i = 0; |
| if (dimvar->str[i]) |
| { |
| free(dimvar->str[i]); |
| } |
| |
| dimvar->str[i++] = stringexpr(); |
| |
| while (g_token == COMMA && i < size) |
| { |
| match(COMMA); |
| if (dimvar->str[i]) |
| { |
| free(dimvar->str[i]); |
| } |
| |
| dimvar->str[i++] = stringexpr(); |
| if (g_errorflag) |
| { |
| break; |
| } |
| } |
| break; |
| } |
| |
| if (g_token == COMMA) |
| { |
| seterror(ERR_TOOMANYINITS); |
| } |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: doif |
| * |
| * Description: |
| * The IF statement. |
| * If jump taken, returns new line no, else returns 0 |
| * |
| ****************************************************************************/ |
| |
| static int doif(void) |
| { |
| int condition; |
| int jump; |
| |
| match(IF); |
| condition = boolexpr(); |
| match(THEN); |
| jump = integer(expr()); |
| if (condition) |
| { |
| return jump; |
| } |
| else |
| { |
| return 0; |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: dogoto |
| * |
| * Description: |
| * The GOTO satement |
| * Returns new line number |
| * |
| ****************************************************************************/ |
| |
| static int dogoto(void) |
| { |
| match(GOTO); |
| return integer(expr()); |
| } |
| |
| /**************************************************************************** |
| * Name: dofor |
| * |
| * Description: |
| * The FOR statement. |
| * |
| * Pushes the for stack. |
| * Returns line to jump to, or -1 to end program |
| * |
| ****************************************************************************/ |
| |
| static int dofor(void) |
| { |
| struct mb_lvalue_s lv; |
| char id[32]; |
| char nextid[32]; |
| int len; |
| double initval; |
| double toval; |
| double stepval; |
| FAR const char *savestring; |
| int answer; |
| |
| match(FOR); |
| getid(g_string, id, &len); |
| |
| lvalue(&lv); |
| if (lv.type != FLTID) |
| { |
| seterror(ERR_BADTYPE); |
| return -1; |
| } |
| |
| match(EQUALS); |
| initval = expr(); |
| match(TO); |
| toval = expr(); |
| |
| if (g_token == STEP) |
| { |
| match(STEP); |
| stepval = expr(); |
| } |
| else |
| { |
| stepval = 1.0; |
| } |
| |
| *lv.dval = initval; |
| |
| if (nfors > MAXFORS - 1) |
| { |
| seterror(ERR_TOOMANYFORS); |
| return -1; |
| } |
| |
| if ((stepval < 0 && initval < toval) || |
| (stepval > 0 && initval > toval)) |
| { |
| savestring = g_string; |
| while ((g_string = strchr(g_string, '\n')) != NULL) |
| { |
| g_errorflag = 0; |
| g_token = gettoken(g_string); |
| match(VALUE); |
| if (g_token == NEXT) |
| { |
| match(NEXT); |
| if (g_token == FLTID || g_token == DIMFLTID) |
| { |
| getid(g_string, nextid, &len); |
| if (!strcmp(id, nextid)) |
| { |
| answer = getnextline(g_string); |
| g_string = savestring; |
| g_token = gettoken(g_string); |
| return answer ? answer : -1; |
| } |
| } |
| } |
| } |
| |
| seterror(ERR_NONEXT); |
| return -1; |
| } |
| else |
| { |
| strlcpy(g_forstack[nfors].id, id, sizeof(g_forstack[nfors].id)); |
| g_forstack[nfors].nextline = getnextline(g_string); |
| g_forstack[nfors].step = stepval; |
| g_forstack[nfors].toval = toval; |
| nfors++; |
| return 0; |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: donext |
| * |
| * Description: |
| * The NEXT statement |
| * Updates the counting index, and returns line to jump to |
| * |
| ****************************************************************************/ |
| |
| static int donext(void) |
| { |
| char id[32]; |
| int len; |
| struct mb_lvalue_s lv; |
| |
| match(NEXT); |
| |
| if (nfors) |
| { |
| getid(g_string, id, &len); |
| lvalue(&lv); |
| if (lv.type != FLTID) |
| { |
| seterror(ERR_BADTYPE); |
| return -1; |
| } |
| |
| *lv.dval += g_forstack[nfors - 1].step; |
| if ((g_forstack[nfors - 1].step < 0 && |
| *lv.dval < g_forstack[nfors - 1].toval) || |
| (g_forstack[nfors - 1].step > 0 && |
| *lv.dval > g_forstack[nfors - 1].toval)) |
| { |
| nfors--; |
| return 0; |
| } |
| else |
| { |
| return g_forstack[nfors - 1].nextline; |
| } |
| } |
| else |
| { |
| seterror(ERR_NOFOR); |
| return -1; |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: doinput |
| * |
| * Description: |
| * The INPUT statement |
| * |
| ****************************************************************************/ |
| |
| static void doinput(void) |
| { |
| struct mb_lvalue_s lv; |
| FAR char *end; |
| |
| match(INPUT); |
| lvalue(&lv); |
| |
| switch (lv.type) |
| { |
| case FLTID: |
| { |
| FAR char *ptr; |
| int nch; |
| |
| /* Copy floating point number to a g_iobuffer. Skip over leading |
| * spaces and terminate with a NUL when a space, tab, newline, EOF, |
| * or comma is detected. |
| */ |
| |
| for (nch = 0, ptr = g_iobuffer; nch < (IOBUFSIZE - 1); nch++) |
| { |
| int ch = fgetc(g_fpin); |
| if (ch == EOF) |
| { |
| seterror(ERR_EOF); |
| return; |
| } |
| |
| if (ch == ' ' || ch == '\t' || ch == ',' || ch == '\n') |
| { |
| ungetc(ch, g_fpin); |
| break; |
| } |
| } |
| |
| *ptr = '\0'; |
| |
| /* Use strtod() to get the floating point value from the substring |
| * in g_iobuffer. |
| */ |
| |
| *lv.dval = strtod(g_iobuffer, &ptr); |
| if (ptr == g_iobuffer) |
| { |
| seterror(ERR_SYNTAX); |
| return; |
| } |
| } |
| break; |
| |
| case STRID: |
| { |
| if (*lv.sval) |
| { |
| free(*lv.sval); |
| *lv.sval = NULL; |
| } |
| |
| if (fgets(g_iobuffer, IOBUFSIZE, g_fpin) == 0) |
| { |
| seterror(ERR_EOF); |
| return; |
| } |
| |
| end = strchr(g_iobuffer, '\n'); |
| if (!end) |
| { |
| seterror(ERR_INPUTTOOLONG); |
| return; |
| } |
| |
| *end = 0; |
| *lv.sval = mystrdup(g_iobuffer); |
| if (!*lv.sval) |
| { |
| seterror(ERR_OUTOFMEMORY); |
| return; |
| } |
| } |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: dorem |
| * |
| * Description: |
| * The REM statement. |
| * Note is unique as the rest of the line is not parsed |
| * |
| ****************************************************************************/ |
| |
| static void dorem(void) |
| { |
| match(REM); |
| } |
| |
| /**************************************************************************** |
| * Name: lvalue |
| * |
| * Description: |
| * Get an lvalue from the environment |
| * Params: lv - structure to fill. |
| * Notes: missing variables (but not out of range subscripts) |
| * are added to the variable list. |
| * |
| ****************************************************************************/ |
| |
| static void lvalue(FAR struct mb_lvalue_s *lv) |
| { |
| char name[32]; |
| int len; |
| FAR struct mb_variable_s *var; |
| FAR struct mb_dimvar_s *dimvar; |
| int index[5]; |
| FAR void *valptr = 0; |
| int type; |
| |
| lv->type = SYNTAX_ERROR; |
| lv->dval = NULL; |
| lv->sval = NULL; |
| |
| switch (g_token) |
| { |
| case FLTID: |
| { |
| getid(g_string, name, &len); |
| match(FLTID); |
| var = findvariable(name); |
| if (!var) |
| { |
| var = addfloat(name); |
| } |
| |
| if (!var) |
| { |
| seterror(ERR_OUTOFMEMORY); |
| return; |
| } |
| |
| lv->type = FLTID; |
| lv->dval = &var->dval; |
| lv->sval = NULL; |
| } |
| break; |
| |
| case STRID: |
| { |
| getid(g_string, name, &len); |
| match(STRID); |
| var = findvariable(name); |
| if (!var) |
| { |
| var = addstring(name); |
| } |
| |
| if (!var) |
| { |
| seterror(ERR_OUTOFMEMORY); |
| return; |
| } |
| |
| lv->type = STRID; |
| lv->sval = &var->sval; |
| lv->dval = NULL; |
| } |
| break; |
| |
| case DIMFLTID: |
| case DIMSTRID: |
| { |
| type = (g_token == DIMFLTID) ? FLTID : STRID; |
| getid(g_string, name, &len); |
| match(g_token); |
| dimvar = finddimvar(name); |
| if (dimvar) |
| { |
| switch (dimvar->ndims) |
| { |
| case 1: |
| { |
| index[0] = integer(expr()); |
| if (g_errorflag == 0) |
| { |
| valptr = getdimvar(dimvar, index[0]); |
| } |
| } |
| break; |
| |
| case 2: |
| { |
| index[0] = integer(expr()); |
| match(COMMA); |
| index[1] = integer(expr()); |
| if (g_errorflag == 0) |
| { |
| valptr = getdimvar(dimvar, index[0], index[1]); |
| } |
| } |
| break; |
| |
| case 3: |
| { |
| index[0] = integer(expr()); |
| match(COMMA); |
| index[1] = integer(expr()); |
| match(COMMA); |
| index[2] = integer(expr()); |
| if (g_errorflag == 0) |
| { |
| valptr = getdimvar(dimvar, index[0], |
| index[1], index[2]); |
| } |
| } |
| break; |
| |
| case 4: |
| { |
| index[0] = integer(expr()); |
| match(COMMA); |
| index[1] = integer(expr()); |
| match(COMMA); |
| index[2] = integer(expr()); |
| match(COMMA); |
| index[3] = integer(expr()); |
| if (g_errorflag == 0) |
| { |
| valptr = getdimvar(dimvar, index[0], |
| index[1], index[2], index[3]); |
| } |
| } |
| break; |
| |
| case 5: |
| { |
| index[0] = integer(expr()); |
| match(COMMA); |
| index[1] = integer(expr()); |
| match(COMMA); |
| index[2] = integer(expr()); |
| match(COMMA); |
| index[3] = integer(expr()); |
| match(COMMA); |
| index[4] = integer(expr()); |
| if (g_errorflag == 0) |
| { |
| valptr = getdimvar(dimvar, index[0], |
| index[1], index[2], index[3]); |
| } |
| } |
| break; |
| } |
| |
| match(CPAREN); |
| } |
| else |
| { |
| seterror(ERR_NOSUCHVARIABLE); |
| return; |
| } |
| |
| if (valptr) |
| { |
| lv->type = type; |
| if (type == FLTID) |
| { |
| lv->dval = valptr; |
| } |
| else if (type == STRID) |
| { |
| lv->sval = valptr; |
| } |
| else |
| { |
| assert(0); |
| } |
| } |
| } |
| break; |
| |
| default: |
| seterror(ERR_SYNTAX); |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: boolexpr |
| * |
| * Description: |
| * Parse a boolean expression |
| * Consists of expressions or strings and relational operators, |
| * and parentheses |
| * |
| ****************************************************************************/ |
| |
| static int boolexpr(void) |
| { |
| int left; |
| int right; |
| |
| left = boolfactor(); |
| |
| while (1) |
| { |
| switch (g_token) |
| { |
| case AND: |
| match(AND); |
| right = boolexpr(); |
| return (left && right) ? 1 : 0; |
| |
| case OR: |
| match(OR); |
| right = boolexpr(); |
| return (left || right) ? 1 : 0; |
| |
| default: |
| return left; |
| } |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: boolfactor |
| * |
| * Description: |
| * Boolean factor, consists of expression relop expression |
| * or string relop string, or ( boolexpr() ) |
| * |
| ****************************************************************************/ |
| |
| static int boolfactor(void) |
| { |
| int answer; |
| double left; |
| double right; |
| int op; |
| FAR char *strleft; |
| FAR char *strright; |
| int cmp; |
| |
| switch (g_token) |
| { |
| case OPAREN: |
| match(OPAREN); |
| answer = boolexpr(); |
| match(CPAREN); |
| break; |
| |
| default: |
| if (isstring(g_token)) |
| { |
| strleft = stringexpr(); |
| op = relop(); |
| strright = stringexpr(); |
| if (!strleft || !strright) |
| { |
| if (strleft) |
| { |
| free(strleft); |
| } |
| |
| if (strright) |
| { |
| free(strright); |
| } |
| |
| return 0; |
| } |
| |
| cmp = strcmp(strleft, strright); |
| switch (op) |
| { |
| case ROP_EQ: |
| answer = cmp == 0 ? 1 : 0; |
| break; |
| |
| case ROP_NEQ: |
| answer = cmp == 0 ? 0 : 1; |
| break; |
| |
| case ROP_LT: |
| answer = cmp < 0 ? 1 : 0; |
| break; |
| |
| case ROP_LTE: |
| answer = cmp <= 0 ? 1 : 0; |
| break; |
| |
| case ROP_GT: |
| answer = cmp > 0 ? 1 : 0; |
| break; |
| |
| case ROP_GTE: |
| answer = cmp >= 0 ? 1 : 0; |
| break; |
| |
| default: |
| answer = 0; |
| } |
| |
| free(strleft); |
| free(strright); |
| } |
| else |
| { |
| left = expr(); |
| op = relop(); |
| right = expr(); |
| switch (op) |
| { |
| case ROP_EQ: |
| answer = (left == right) ? 1 : 0; |
| break; |
| |
| case ROP_NEQ: |
| answer = (left != right) ? 1 : 0; |
| break; |
| |
| case ROP_LT: |
| answer = (left < right) ? 1 : 0; |
| break; |
| |
| case ROP_LTE: |
| answer = (left <= right) ? 1 : 0; |
| break; |
| |
| case ROP_GT: |
| answer = (left > right) ? 1 : 0; |
| break; |
| |
| case ROP_GTE: |
| answer = (left >= right) ? 1 : 0; |
| break; |
| |
| default: |
| g_errorflag = 1; |
| return 0; |
| } |
| } |
| } |
| |
| return answer; |
| } |
| |
| /**************************************************************************** |
| * Name: relop |
| * |
| * Description: |
| * Get a relational operator |
| * Returns operator parsed or SYNTAX_ERROR |
| * |
| ****************************************************************************/ |
| |
| static int relop(void) |
| { |
| switch (g_token) |
| { |
| case EQUALS: |
| match(EQUALS); |
| return ROP_EQ; |
| |
| case GREATER: |
| match(GREATER); |
| if (g_token == EQUALS) |
| { |
| match(EQUALS); |
| return ROP_GTE; |
| } |
| |
| return ROP_GT; |
| |
| case LESS: |
| match(LESS); |
| if (g_token == EQUALS) |
| { |
| match(EQUALS); |
| return ROP_LTE; |
| } |
| else if (g_token == GREATER) |
| { |
| match(GREATER); |
| return ROP_NEQ; |
| } |
| |
| return ROP_LT; |
| |
| default: |
| seterror(ERR_SYNTAX); |
| return SYNTAX_ERROR; |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: expr |
| * |
| * Description: |
| * Parses an expression |
| * |
| ****************************************************************************/ |
| |
| static double expr(void) |
| { |
| double left; |
| double right; |
| |
| left = term(); |
| |
| while (1) |
| { |
| switch (g_token) |
| { |
| case PLUS: |
| match(PLUS); |
| right = term(); |
| left += right; |
| break; |
| |
| case MINUS: |
| match(MINUS); |
| right = term(); |
| left -= right; |
| break; |
| |
| default: |
| return left; |
| } |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: term |
| * |
| * Description: |
| * Parses a term |
| * |
| ****************************************************************************/ |
| |
| static double term(void) |
| { |
| double left; |
| double right; |
| |
| left = factor(); |
| |
| while (1) |
| { |
| switch (g_token) |
| { |
| case MULT: |
| match(MULT); |
| right = factor(); |
| left *= right; |
| break; |
| |
| case DIV: |
| match(DIV); |
| right = factor(); |
| if (right != 0.0) |
| { |
| left /= right; |
| } |
| else |
| { |
| seterror(ERR_DIVIDEBYZERO); |
| } |
| break; |
| |
| case MOD: |
| match(MOD); |
| right = factor(); |
| left = fmod(left, right); |
| break; |
| |
| default: |
| return left; |
| } |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: factor |
| * |
| * Description: |
| * Parses a factor |
| * |
| ****************************************************************************/ |
| |
| static double factor(void) |
| { |
| double answer = 0; |
| FAR char *str; |
| FAR char *end; |
| int len; |
| |
| switch (g_token) |
| { |
| case OPAREN: |
| match(OPAREN); |
| answer = expr(); |
| match(CPAREN); |
| break; |
| |
| case VALUE: |
| answer = getvalue(g_string, &len); |
| match(VALUE); |
| break; |
| |
| case MINUS: |
| match(MINUS); |
| answer = -factor(); |
| break; |
| |
| case FLTID: |
| answer = variable(); |
| break; |
| |
| case DIMFLTID: |
| answer = dimvariable(); |
| break; |
| |
| case E: |
| answer = exp(1.0); |
| match(E); |
| break; |
| |
| case PI: |
| answer = acos(0.0) * 2.0; |
| match(PI); |
| break; |
| |
| case SIN: |
| match(SIN); |
| match(OPAREN); |
| answer = expr(); |
| match(CPAREN); |
| answer = sin(answer); |
| break; |
| |
| case COS: |
| match(COS); |
| match(OPAREN); |
| answer = expr(); |
| match(CPAREN); |
| answer = cos(answer); |
| break; |
| |
| case TAN: |
| match(TAN); |
| match(OPAREN); |
| answer = expr(); |
| match(CPAREN); |
| answer = tan(answer); |
| break; |
| |
| case LN: |
| match(LN); |
| match(OPAREN); |
| answer = expr(); |
| match(CPAREN); |
| if (answer > 0) |
| { |
| answer = log(answer); |
| } |
| else |
| { |
| seterror(ERR_NEGLOG); |
| } |
| break; |
| |
| case POW: |
| match(POW); |
| match(OPAREN); |
| answer = expr(); |
| match(COMMA); |
| answer = pow(answer, expr()); |
| match(CPAREN); |
| break; |
| |
| case SQRT: |
| match(SQRT); |
| match(OPAREN); |
| answer = expr(); |
| match(CPAREN); |
| if (answer >= 0.0) |
| { |
| answer = sqrt(answer); |
| } |
| else |
| { |
| seterror(ERR_NEGSQRT); |
| } |
| break; |
| |
| case ABS: |
| match(ABS); |
| match(OPAREN); |
| answer = expr(); |
| match(CPAREN); |
| answer = fabs(answer); |
| break; |
| |
| case LEN: |
| match(LEN); |
| match(OPAREN); |
| str = stringexpr(); |
| match(CPAREN); |
| if (str) |
| { |
| answer = strlen(str); |
| free(str); |
| } |
| else |
| { |
| answer = 0; |
| } |
| break; |
| |
| case ASCII: |
| match(ASCII); |
| match(OPAREN); |
| str = stringexpr(); |
| match(CPAREN); |
| if (str) |
| { |
| answer = *str; |
| free(str); |
| } |
| else |
| { |
| answer = 0; |
| } |
| break; |
| |
| case ASIN: |
| match(ASIN); |
| match(OPAREN); |
| answer = expr(); |
| match(CPAREN); |
| if (answer >= -1 && answer <= 1) |
| { |
| answer = asin(answer); |
| } |
| else |
| { |
| seterror(ERR_BADSINCOS); |
| } |
| break; |
| |
| case ACOS: |
| match(ACOS); |
| match(OPAREN); |
| answer = expr(); |
| match(CPAREN); |
| if (answer >= -1 && answer <= 1) |
| { |
| answer = acos(answer); |
| } |
| else |
| { |
| seterror(ERR_BADSINCOS); |
| } |
| break; |
| |
| case ATAN: |
| match(ATAN); |
| match(OPAREN); |
| answer = expr(); |
| match(CPAREN); |
| answer = atan(answer); |
| break; |
| |
| case INT: |
| match(INT); |
| match(OPAREN); |
| answer = expr(); |
| match(CPAREN); |
| answer = floor(answer); |
| break; |
| |
| case RND: |
| match(RND); |
| match(OPAREN); |
| answer = expr(); |
| match(CPAREN); |
| answer = integer(answer); |
| if (answer > 1) |
| { |
| answer = floor(rand() / (RAND_MAX + 1.0) * answer); |
| } |
| else if (answer == 1) |
| { |
| answer = rand() / (RAND_MAX + 1.0); |
| } |
| else |
| { |
| if (answer < 0) |
| { |
| srand((unsigned)-answer); |
| } |
| |
| answer = 0; |
| } |
| break; |
| |
| case VAL: |
| match(VAL); |
| match(OPAREN); |
| str = stringexpr(); |
| match(CPAREN); |
| if (str) |
| { |
| answer = strtod(str, 0); |
| free(str); |
| } |
| else |
| { |
| answer = 0; |
| } |
| break; |
| |
| case VALLEN: |
| match(VALLEN); |
| match(OPAREN); |
| str = stringexpr(); |
| match(CPAREN); |
| if (str) |
| { |
| strtod(str, &end); |
| answer = end - str; |
| free(str); |
| } |
| else |
| { |
| answer = 0.0; |
| } |
| break; |
| |
| case INSTR: |
| answer = instr(); |
| break; |
| |
| default: |
| if (isstring(g_token)) |
| { |
| seterror(ERR_TYPEMISMATCH); |
| } |
| else |
| { |
| seterror(ERR_SYNTAX); |
| } |
| break; |
| } |
| |
| while (g_token == SHRIEK) |
| { |
| match(SHRIEK); |
| answer = factorial(answer); |
| } |
| |
| return answer; |
| } |
| |
| /**************************************************************************** |
| * Name: instr |
| * |
| * Description: |
| * Calculate the INSTR() function. |
| * |
| ****************************************************************************/ |
| |
| static double instr(void) |
| { |
| FAR char *str; |
| FAR char *substr; |
| FAR char *end; |
| double answer = 0; |
| int offset; |
| |
| match(INSTR); |
| match(OPAREN); |
| str = stringexpr(); |
| match(COMMA); |
| substr = stringexpr(); |
| match(COMMA); |
| offset = integer(expr()); |
| offset--; |
| match(CPAREN); |
| |
| if (!str || !substr) |
| { |
| if (str) |
| { |
| free(str); |
| } |
| |
| if (substr) |
| { |
| free(substr); |
| } |
| |
| return 0; |
| } |
| |
| if (offset >= 0 && offset < (int)strlen(str)) |
| { |
| end = strstr(str + offset, substr); |
| if (end) |
| { |
| answer = end - str + 1.0; |
| } |
| } |
| |
| free(str); |
| free(substr); |
| return answer; |
| } |
| |
| /**************************************************************************** |
| * Name: variable |
| * |
| * Description: |
| * Get the value of a scalar variable from string |
| * matches FLTID |
| * |
| * |
| ****************************************************************************/ |
| |
| static double variable(void) |
| { |
| FAR struct mb_variable_s *var; |
| char id[32]; |
| int len; |
| |
| getid(g_string, id, &len); |
| match(FLTID); |
| var = findvariable(id); |
| if (var) |
| { |
| return var->dval; |
| } |
| else |
| { |
| seterror(ERR_NOSUCHVARIABLE); |
| return 0.0; |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: dimvariable |
| * |
| * Description: |
| * Get value of a dimensioned variable from string. |
| * matches DIMFLTID |
| * |
| ****************************************************************************/ |
| |
| static double dimvariable(void) |
| { |
| FAR struct mb_dimvar_s *dimvar; |
| char id[32]; |
| int len; |
| int index[5]; |
| FAR double *answer = NULL; |
| |
| getid(g_string, id, &len); |
| match(DIMFLTID); |
| dimvar = finddimvar(id); |
| if (!dimvar) |
| { |
| seterror(ERR_NOSUCHVARIABLE); |
| return 0.0; |
| } |
| |
| if (dimvar) |
| { |
| switch (dimvar->ndims) |
| { |
| case 1: |
| index[0] = integer(expr()); |
| answer = getdimvar(dimvar, index[0]); |
| break; |
| |
| case 2: |
| index[0] = integer(expr()); |
| match(COMMA); |
| index[1] = integer(expr()); |
| answer = getdimvar(dimvar, index[0], index[1]); |
| break; |
| |
| case 3: |
| index[0] = integer(expr()); |
| match(COMMA); |
| index[1] = integer(expr()); |
| match(COMMA); |
| index[2] = integer(expr()); |
| answer = getdimvar(dimvar, index[0], index[1], index[2]); |
| break; |
| |
| case 4: |
| index[0] = integer(expr()); |
| match(COMMA); |
| index[1] = integer(expr()); |
| match(COMMA); |
| index[2] = integer(expr()); |
| match(COMMA); |
| index[3] = integer(expr()); |
| answer = getdimvar(dimvar, index[0], index[1], index[2], index[3]); |
| break; |
| |
| case 5: |
| index[0] = integer(expr()); |
| match(COMMA); |
| index[1] = integer(expr()); |
| match(COMMA); |
| index[2] = integer(expr()); |
| match(COMMA); |
| index[3] = integer(expr()); |
| match(COMMA); |
| index[4] = integer(expr()); |
| answer = getdimvar(dimvar, index[0], index[1], |
| index[2], index[3], index[4]); |
| break; |
| } |
| |
| match(CPAREN); |
| } |
| |
| if (answer != NULL) |
| { |
| return *answer; |
| } |
| |
| return 0.0; |
| } |
| |
| /**************************************************************************** |
| * Name: findvariable |
| * |
| * Description: |
| * Find a scalar variable invariables list |
| * Params: id - id to get |
| * Returns: pointer to that entry, 0 on fail |
| * |
| ****************************************************************************/ |
| |
| static FAR struct mb_variable_s *findvariable(FAR const char *id) |
| { |
| int i; |
| |
| for (i = 0; i < g_nvariables; i++) |
| { |
| if (!strcmp(g_variables[i].id, id)) |
| { |
| return &g_variables[i]; |
| } |
| } |
| |
| return 0; |
| } |
| |
| /**************************************************************************** |
| * Name: finddimvar |
| * |
| * Description: |
| * Get a dimensioned array by name |
| * Params: id (includes opening parenthesis) |
| * Returns: pointer to array entry or 0 on fail |
| * |
| ****************************************************************************/ |
| |
| static struct mb_dimvar_s *finddimvar(FAR const char *id) |
| { |
| int i; |
| |
| for (i = 0; i < g_ndimvariables; i++) |
| { |
| if (!strcmp(g_dimvariables[i].id, id)) |
| { |
| return &g_dimvariables[i]; |
| } |
| } |
| |
| return 0; |
| } |
| |
| /**************************************************************************** |
| * Name: dimension |
| * |
| * Description: |
| * Dimension an array. |
| * Params: id - the id of the array (include leading () |
| * ndims - number of dimension (1-5) |
| * ... - integers giving dimension size, |
| * |
| ****************************************************************************/ |
| |
| static FAR struct mb_dimvar_s *dimension(FAR const char *id, int ndims, ...) |
| { |
| FAR struct mb_dimvar_s *dv; |
| va_list vargs; |
| int size = 1; |
| int oldsize = 1; |
| int i; |
| int dimensions[5]; |
| FAR double *dtemp; |
| FAR char **stemp; |
| |
| assert(ndims <= 5); |
| if (ndims > 5) |
| { |
| return 0; |
| } |
| |
| dv = finddimvar(id); |
| if (!dv) |
| { |
| dv = adddimvar(id); |
| } |
| |
| if (!dv) |
| { |
| seterror(ERR_OUTOFMEMORY); |
| return 0; |
| } |
| |
| if (dv->ndims) |
| { |
| for (i = 0; i < dv->ndims; i++) |
| { |
| oldsize *= dv->dim[i]; |
| } |
| } |
| else |
| { |
| oldsize = 0; |
| } |
| |
| va_start(vargs, ndims); |
| for (i = 0; i < ndims; i++) |
| { |
| dimensions[i] = va_arg(vargs, int); |
| size *= dimensions[i]; |
| } |
| |
| va_end(vargs); |
| |
| switch (dv->type) |
| { |
| case FLTID: |
| dtemp = realloc(dv->dval, size * sizeof(double)); |
| if (dtemp) |
| { |
| dv->dval = dtemp; |
| } |
| else |
| { |
| seterror(ERR_OUTOFMEMORY); |
| return 0; |
| } |
| break; |
| |
| case STRID: |
| if (dv->str) |
| { |
| for (i = size; i < oldsize; i++) |
| { |
| if (dv->str[i]) |
| { |
| free(dv->str[i]); |
| dv->str[i] = 0; |
| } |
| } |
| } |
| |
| stemp = realloc(dv->str, size * sizeof(char *)); |
| if (stemp) |
| { |
| dv->str = stemp; |
| for (i = oldsize; i < size; i++) |
| { |
| dv->str[i] = 0; |
| } |
| } |
| else |
| { |
| for (i = 0; i < oldsize; i++) |
| { |
| if (dv->str[i]) |
| { |
| free(dv->str[i]); |
| dv->str[i] = 0; |
| } |
| } |
| |
| seterror(ERR_OUTOFMEMORY); |
| return 0; |
| } |
| break; |
| |
| default: |
| assert(0); |
| } |
| |
| for (i = 0; i < 5; i++) |
| { |
| dv->dim[i] = dimensions[i]; |
| } |
| |
| dv->ndims = ndims; |
| |
| return dv; |
| } |
| |
| /**************************************************************************** |
| * Name: getdimvar |
| * |
| * Description: |
| * Get the address of a dimensioned array element. |
| * Works for both string and real arrays. |
| * Params: dv - the array's entry in variable list |
| * ... - integers telling which array element to get |
| * Returns: the address of that element, 0 on fail |
| * |
| ****************************************************************************/ |
| |
| static FAR void *getdimvar(FAR struct mb_dimvar_s *dv, ...) |
| { |
| va_list vargs; |
| int index[5]; |
| int i; |
| FAR void *answer = 0; |
| |
| va_start(vargs, dv); |
| for (i = 0; i < dv->ndims; i++) |
| { |
| index[i] = va_arg(vargs, int); |
| index[i]--; |
| } |
| |
| va_end(vargs); |
| |
| for (i = 0; i < dv->ndims; i++) |
| { |
| if (index[i] >= dv->dim[i] || index[i] < 0) |
| { |
| seterror(ERR_BADSUBSCRIPT); |
| return 0; |
| } |
| } |
| |
| if (dv->type == FLTID) |
| { |
| switch (dv->ndims) |
| { |
| case 1: |
| answer = &dv->dval[index[0]]; |
| break; |
| |
| case 2: |
| answer = &dv->dval[index[1] * dv->dim[0] + index[0]]; |
| break; |
| case 3: |
| answer = &dv->dval[index[2] * (dv->dim[0] * dv->dim[1]) |
| + index[1] * dv->dim[0] + index[0]]; |
| break; |
| |
| case 4: |
| answer = |
| &dv->dval[index[3] * (dv->dim[0] + dv->dim[1] + dv->dim[2]) + |
| index[2] * (dv->dim[0] * dv->dim[1]) + |
| index[1] * dv->dim[0] + index[0]]; |
| break; |
| |
| case 5: |
| answer = |
| &dv->dval[index[4] * |
| (dv->dim[0] + dv->dim[1] + dv->dim[2] + |
| dv->dim[3]) + index[3] * (dv->dim[0] + dv->dim[1] + |
| dv->dim[2]) + |
| index[2] * (dv->dim[0] + dv->dim[1]) + |
| index[1] * dv->dim[0] + index[0]]; |
| break; |
| } |
| } |
| else if (dv->type == STRID) |
| { |
| switch (dv->ndims) |
| { |
| case 1: |
| answer = &dv->str[index[0]]; |
| break; |
| |
| case 2: |
| answer = &dv->str[index[1] * dv->dim[0] + index[0]]; |
| break; |
| |
| case 3: |
| answer = &dv->str[index[2] * (dv->dim[0] * dv->dim[1]) |
| + index[1] * dv->dim[0] + index[0]]; |
| break; |
| |
| case 4: |
| answer = |
| &dv->str[index[3] * (dv->dim[0] + dv->dim[1] + dv->dim[2]) + |
| index[2] * (dv->dim[0] * dv->dim[1]) + |
| index[1] * dv->dim[0] + index[0]]; |
| break; |
| |
| case 5: |
| answer = |
| &dv->str[index[4] * |
| (dv->dim[0] + dv->dim[1] + dv->dim[2] + dv->dim[3]) + |
| index[3] * (dv->dim[0] + dv->dim[1] + dv->dim[2]) + |
| index[2] * (dv->dim[0] + dv->dim[1]) + |
| index[1] * dv->dim[0] + index[0]]; |
| break; |
| } |
| } |
| |
| return answer; |
| } |
| |
| /**************************************************************************** |
| * Name: addfloat |
| * |
| * Description: |
| * Add a real variable to our variable list |
| * Params: id - id of variable to add. |
| * Returns: pointer to new entry in table |
| * |
| ****************************************************************************/ |
| |
| static FAR struct mb_variable_s *addfloat(FAR const char *id) |
| { |
| FAR struct mb_variable_s *vars; |
| |
| vars = |
| realloc(g_variables, (g_nvariables + 1) * sizeof(struct mb_variable_s)); |
| if (vars) |
| { |
| g_variables = vars; |
| strlcpy(g_variables[g_nvariables].id, id, |
| sizeof(g_variables[g_nvariables].id)); |
| g_variables[g_nvariables].dval = 0.0; |
| g_variables[g_nvariables].sval = NULL; |
| g_nvariables++; |
| return &g_variables[g_nvariables - 1]; |
| } |
| else |
| { |
| seterror(ERR_OUTOFMEMORY); |
| } |
| |
| return 0; |
| } |
| |
| /**************************************************************************** |
| * Name: addstring |
| * |
| * Description: |
| * Add a string variable to table. |
| * Params: id - id of variable to get (including trailing $) |
| * Returns: pointer to new entry in table, 0 on fail. |
| * |
| ****************************************************************************/ |
| |
| static FAR struct mb_variable_s *addstring(FAR const char *id) |
| { |
| FAR struct mb_variable_s *vars; |
| |
| vars = |
| realloc(g_variables, (g_nvariables + 1) * sizeof(struct mb_variable_s)); |
| if (vars) |
| { |
| g_variables = vars; |
| strlcpy(g_variables[g_nvariables].id, id, |
| sizeof(g_variables[g_nvariables].id)); |
| g_variables[g_nvariables].sval = NULL; |
| g_variables[g_nvariables].dval = 0.0; |
| g_nvariables++; |
| return &g_variables[g_nvariables - 1]; |
| } |
| else |
| { |
| seterror(ERR_OUTOFMEMORY); |
| } |
| |
| return 0; |
| } |
| |
| /**************************************************************************** |
| * Name: adddimvar |
| * |
| * Description: |
| * Add a new array to our symbol table. |
| * Params: id - id of array (include leading () |
| * Returns: pointer to new entry, 0 on fail. |
| * |
| ****************************************************************************/ |
| |
| static FAR struct mb_dimvar_s *adddimvar(FAR const char *id) |
| { |
| FAR struct mb_dimvar_s *vars; |
| |
| vars = realloc(g_dimvariables, |
| (g_ndimvariables + 1) * sizeof(struct mb_dimvar_s)); |
| if (vars) |
| { |
| g_dimvariables = vars; |
| strlcpy(g_dimvariables[g_ndimvariables].id, id, |
| sizeof(g_dimvariables[g_ndimvariables].id)); |
| g_dimvariables[g_ndimvariables].dval = NULL; |
| g_dimvariables[g_ndimvariables].str = NULL; |
| g_dimvariables[g_ndimvariables].ndims = 0; |
| g_dimvariables[g_ndimvariables].type = strchr(id, '$') ? STRID : FLTID; |
| g_ndimvariables++; |
| return &g_dimvariables[g_ndimvariables - 1]; |
| } |
| else |
| { |
| seterror(ERR_OUTOFMEMORY); |
| } |
| |
| return 0; |
| } |
| |
| /**************************************************************************** |
| * Name: stringexpr |
| * |
| * Description: |
| * High level string parsing function. |
| * Returns: a malloced pointer, or 0 on error condition. |
| * caller must free! |
| * |
| ****************************************************************************/ |
| |
| static FAR char *stringexpr(void) |
| { |
| FAR char *left; |
| FAR char *right; |
| FAR char *temp; |
| |
| switch (g_token) |
| { |
| case DIMSTRID: |
| left = mystrdup(stringdimvar()); |
| break; |
| |
| case STRID: |
| left = mystrdup(stringvar()); |
| break; |
| |
| case QUOTE: |
| left = stringliteral(); |
| break; |
| |
| case CHRSTRING: |
| left = chrstring(); |
| break; |
| |
| case STRSTRING: |
| left = strstring(); |
| break; |
| |
| case LEFTSTRING: |
| left = leftstring(); |
| break; |
| |
| case RIGHTSTRING: |
| left = rightstring(); |
| break; |
| |
| case MIDSTRING: |
| left = midstring(); |
| break; |
| |
| case STRINGSTRING: |
| left = stringstring(); |
| break; |
| |
| default: |
| if (!isstring(g_token)) |
| { |
| seterror(ERR_TYPEMISMATCH); |
| } |
| else |
| { |
| seterror(ERR_SYNTAX); |
| } |
| |
| return mystrdup(""); |
| } |
| |
| if (!left) |
| { |
| seterror(ERR_OUTOFMEMORY); |
| return 0; |
| } |
| |
| switch (g_token) |
| { |
| case PLUS: |
| match(PLUS); |
| right = stringexpr(); |
| if (right) |
| { |
| temp = mystrconcat(left, right); |
| free(right); |
| if (temp) |
| { |
| free(left); |
| left = temp; |
| } |
| else |
| { |
| seterror(ERR_OUTOFMEMORY); |
| } |
| } |
| else |
| { |
| seterror(ERR_OUTOFMEMORY); |
| } |
| |
| break; |
| |
| default: |
| return left; |
| } |
| |
| return left; |
| } |
| |
| /**************************************************************************** |
| * Name: chrstring |
| * |
| * Description: |
| * Parse the CHR$ token |
| * |
| ****************************************************************************/ |
| |
| static FAR char *chrstring(void) |
| { |
| double x; |
| FAR char *answer; |
| |
| match(CHRSTRING); |
| match(OPAREN); |
| x = integer(expr()); |
| match(CPAREN); |
| |
| g_iobuffer[0] = (char)x; |
| g_iobuffer[1] = 0; |
| answer = mystrdup(g_iobuffer); |
| |
| if (!answer) |
| { |
| seterror(ERR_OUTOFMEMORY); |
| } |
| |
| return answer; |
| } |
| |
| /**************************************************************************** |
| * Name: strstring |
| * |
| * Description: |
| * Parse the STR$ token |
| * |
| ****************************************************************************/ |
| |
| static FAR char *strstring(void) |
| { |
| double x; |
| FAR char *answer; |
| |
| match(STRSTRING); |
| match(OPAREN); |
| x = expr(); |
| match(CPAREN); |
| |
| snprintf(g_iobuffer, sizeof(g_iobuffer), "%g", x); |
| answer = mystrdup(g_iobuffer); |
| if (!answer) |
| { |
| seterror(ERR_OUTOFMEMORY); |
| } |
| |
| return answer; |
| } |
| |
| /**************************************************************************** |
| * Name: leftstring |
| * |
| * Description: |
| * Parse the LEFT$ token |
| * |
| ****************************************************************************/ |
| |
| static FAR char *leftstring(void) |
| { |
| FAR char *str; |
| int x; |
| FAR char *answer; |
| |
| match(LEFTSTRING); |
| match(OPAREN); |
| str = stringexpr(); |
| if (!str) |
| { |
| return 0; |
| } |
| |
| match(COMMA); |
| x = integer(expr()); |
| match(CPAREN); |
| |
| if (x > (int)strlen(str)) |
| { |
| return str; |
| } |
| |
| if (x < 0) |
| { |
| seterror(ERR_ILLEGALOFFSET); |
| return str; |
| } |
| |
| str[x] = 0; |
| answer = mystrdup(str); |
| free(str); |
| if (!answer) |
| { |
| seterror(ERR_OUTOFMEMORY); |
| } |
| |
| return answer; |
| } |
| |
| /**************************************************************************** |
| * Name: rightstring |
| * |
| * Description: |
| * Parse the RIGHT$ token |
| * |
| ****************************************************************************/ |
| |
| static FAR char *rightstring(void) |
| { |
| int x; |
| FAR char *str; |
| FAR char *answer; |
| |
| match(RIGHTSTRING); |
| match(OPAREN); |
| str = stringexpr(); |
| if (!str) |
| { |
| return 0; |
| } |
| |
| match(COMMA); |
| x = integer(expr()); |
| match(CPAREN); |
| |
| if (x > (int)strlen(str)) |
| { |
| return str; |
| } |
| |
| if (x < 0) |
| { |
| seterror(ERR_ILLEGALOFFSET); |
| return str; |
| } |
| |
| answer = mystrdup(&str[strlen(str) - x]); |
| free(str); |
| if (!answer) |
| { |
| seterror(ERR_OUTOFMEMORY); |
| } |
| |
| return answer; |
| } |
| |
| /**************************************************************************** |
| * Name: midstring |
| * |
| * Description: |
| * Parse the MID$ token |
| * |
| ****************************************************************************/ |
| |
| static FAR char *midstring(void) |
| { |
| FAR char *str; |
| int x; |
| int len; |
| FAR char *answer; |
| FAR char *temp; |
| |
| match(MIDSTRING); |
| match(OPAREN); |
| str = stringexpr(); |
| match(COMMA); |
| x = integer(expr()); |
| match(COMMA); |
| len = integer(expr()); |
| match(CPAREN); |
| |
| if (!str) |
| { |
| return 0; |
| } |
| |
| if (len == -1) |
| { |
| len = strlen(str) - x + 1; |
| } |
| |
| if (x > (int)strlen(str) || len < 1) |
| { |
| free(str); |
| answer = mystrdup(""); |
| if (!answer) |
| { |
| seterror(ERR_OUTOFMEMORY); |
| } |
| |
| return answer; |
| } |
| |
| if (x < 1.0) |
| { |
| seterror(ERR_ILLEGALOFFSET); |
| return str; |
| } |
| |
| temp = &str[x - 1]; |
| |
| answer = malloc(len + 1); |
| if (!answer) |
| { |
| seterror(ERR_OUTOFMEMORY); |
| return str; |
| } |
| |
| strlcpy(answer, temp, len + 1); |
| free(str); |
| return answer; |
| } |
| |
| /**************************************************************************** |
| * Name: stringstring |
| * |
| * Description: |
| * Parse the string$ token |
| * |
| ****************************************************************************/ |
| |
| static FAR char *stringstring(void) |
| { |
| int x; |
| FAR char *str; |
| FAR char *answer; |
| int len; |
| int N; |
| int i; |
| |
| match(STRINGSTRING); |
| match(OPAREN); |
| x = integer(expr()); |
| match(COMMA); |
| str = stringexpr(); |
| match(CPAREN); |
| |
| if (!str) |
| { |
| return 0; |
| } |
| |
| N = x; |
| |
| if (N < 1) |
| { |
| free(str); |
| answer = mystrdup(""); |
| if (!answer) |
| { |
| seterror(ERR_OUTOFMEMORY); |
| } |
| |
| return answer; |
| } |
| |
| len = strlen(str); |
| answer = malloc(N * len + 1); |
| if (!answer) |
| { |
| free(str); |
| seterror(ERR_OUTOFMEMORY); |
| return 0; |
| } |
| |
| for (i = 0; i < N; i++) |
| { |
| strlcpy(answer + len * i, str, (N - i) * len + 1); |
| } |
| |
| free(str); |
| return answer; |
| } |
| |
| /**************************************************************************** |
| * Name: stringdimvar |
| * |
| * Description: |
| * Read a dimensioned string variable from input. |
| * Returns: pointer to string (not malloced) |
| * |
| ****************************************************************************/ |
| |
| static FAR char *stringdimvar(void) |
| { |
| char id[32]; |
| int len; |
| FAR struct mb_dimvar_s *dimvar; |
| FAR char **answer = NULL; |
| int index[5]; |
| |
| getid(g_string, id, &len); |
| match(DIMSTRID); |
| dimvar = finddimvar(id); |
| |
| if (dimvar) |
| { |
| switch (dimvar->ndims) |
| { |
| case 1: |
| index[0] = integer(expr()); |
| answer = getdimvar(dimvar, index[0]); |
| break; |
| |
| case 2: |
| index[0] = integer(expr()); |
| match(COMMA); |
| index[1] = integer(expr()); |
| answer = getdimvar(dimvar, index[0], index[1]); |
| break; |
| |
| case 3: |
| index[0] = integer(expr()); |
| match(COMMA); |
| index[1] = integer(expr()); |
| match(COMMA); |
| index[2] = integer(expr()); |
| answer = getdimvar(dimvar, index[0], index[1], index[2]); |
| break; |
| |
| case 4: |
| index[0] = integer(expr()); |
| match(COMMA); |
| index[1] = integer(expr()); |
| match(COMMA); |
| index[2] = integer(expr()); |
| match(COMMA); |
| index[3] = integer(expr()); |
| answer = getdimvar(dimvar, index[0], index[1], index[2], index[3]); |
| break; |
| |
| case 5: |
| index[0] = integer(expr()); |
| match(COMMA); |
| index[1] = integer(expr()); |
| match(COMMA); |
| index[2] = integer(expr()); |
| match(COMMA); |
| index[3] = integer(expr()); |
| match(COMMA); |
| index[4] = integer(expr()); |
| answer = |
| getdimvar(dimvar, index[0], index[1], |
| index[2], index[3], index[4]); |
| break; |
| } |
| |
| match(CPAREN); |
| } |
| else |
| { |
| seterror(ERR_NOSUCHVARIABLE); |
| } |
| |
| if (!g_errorflag) |
| { |
| if (answer != NULL && *answer != NULL) |
| { |
| return *answer; |
| } |
| } |
| |
| return ""; |
| } |
| |
| /**************************************************************************** |
| * Name: stringvar |
| * |
| * Description: |
| * Parse a string variable. |
| * Returns: pointer to string (not malloced) |
| * |
| ****************************************************************************/ |
| |
| static FAR char *stringvar(void) |
| { |
| char id[32]; |
| int len; |
| FAR struct mb_variable_s *var; |
| |
| getid(g_string, id, &len); |
| match(STRID); |
| var = findvariable(id); |
| if (var) |
| { |
| if (var->sval) |
| { |
| return var->sval; |
| } |
| |
| return ""; |
| } |
| |
| seterror(ERR_NOSUCHVARIABLE); |
| return ""; |
| } |
| |
| /**************************************************************************** |
| * Name: stringliteral |
| * |
| * Description: |
| * Parse a string literal |
| * Returns: malloced string literal |
| * Notes: newlines aren't allowed in literals, but blind |
| * concatenation across newlines is. |
| * |
| ****************************************************************************/ |
| |
| static FAR char *stringliteral(void) |
| { |
| int len = 1; |
| FAR char *answer = 0; |
| FAR char *temp; |
| FAR char *substr; |
| FAR char *end; |
| |
| while (g_token == QUOTE) |
| { |
| while (isspace(*g_string)) |
| { |
| g_string++; |
| } |
| |
| end = mystrend(g_string, '"'); |
| if (end) |
| { |
| len = end - g_string; |
| substr = malloc(len); |
| if (!substr) |
| { |
| seterror(ERR_OUTOFMEMORY); |
| return answer; |
| } |
| |
| mystrgrablit(substr, g_string); |
| if (answer) |
| { |
| temp = mystrconcat(answer, substr); |
| free(substr); |
| free(answer); |
| answer = temp; |
| if (!answer) |
| { |
| seterror(ERR_OUTOFMEMORY); |
| return answer; |
| } |
| } |
| else |
| { |
| answer = substr; |
| } |
| |
| g_string = end; |
| } |
| else |
| { |
| seterror(ERR_SYNTAX); |
| return answer; |
| } |
| |
| match(QUOTE); |
| } |
| |
| return answer; |
| } |
| |
| /**************************************************************************** |
| * Name: integer |
| * |
| * Description: |
| * Cast a double to an integer, triggering errors if out of range |
| * |
| ****************************************************************************/ |
| |
| static int integer(double x) |
| { |
| if (x < INT_MIN || x > INT_MAX) |
| { |
| seterror(ERR_BADVALUE); |
| } |
| |
| if (x != floor(x)) |
| { |
| seterror(ERR_NOTINT); |
| } |
| |
| return (int)x; |
| } |
| |
| /**************************************************************************** |
| * Name: match |
| * |
| * Description: |
| * Check that we have a token of the passed type (if not set g_errorflag) |
| * Move parser on to next token. Sets token and string. |
| * |
| ****************************************************************************/ |
| |
| static void match(int tok) |
| { |
| if (g_token != tok) |
| { |
| seterror(ERR_SYNTAX); |
| return; |
| } |
| |
| while (isspace(*g_string)) |
| { |
| g_string++; |
| } |
| |
| g_string += tokenlen(g_string, g_token); |
| g_token = gettoken(g_string); |
| if (g_token == SYNTAX_ERROR) |
| { |
| seterror(ERR_SYNTAX); |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: seterror |
| * |
| * Description: |
| * Set the errorflag. |
| * Params: errorcode - the error. |
| * Notes: ignores error cascades |
| * |
| ****************************************************************************/ |
| |
| static void seterror(int errorcode) |
| { |
| if (g_errorflag == 0 || errorcode == 0) |
| { |
| g_errorflag = errorcode; |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: getnextline |
| * |
| * Description: |
| * Get the next line number |
| * Params: str - pointer to parse string |
| * Returns: line no of next line, 0 if end |
| * Notes: goes to newline, then finds |
| * first line starting with a digit. |
| * |
| ****************************************************************************/ |
| |
| static int getnextline(FAR const char *str) |
| { |
| while (*str) |
| { |
| while (*str && *str != '\n') |
| { |
| str++; |
| } |
| |
| if (*str == 0) |
| { |
| return 0; |
| } |
| |
| str++; |
| if (isdigit(*str)) |
| { |
| return atoi(str); |
| } |
| } |
| |
| return 0; |
| } |
| |
| /**************************************************************************** |
| * Name: gettoken |
| * |
| * Description: |
| * Get a token from the string |
| * Params: str - string to read token from |
| * Notes: ignores white space between tokens |
| * |
| ****************************************************************************/ |
| |
| static int gettoken(FAR const char *str) |
| { |
| while (isspace(*str)) |
| { |
| str++; |
| } |
| |
| if (isdigit(*str)) |
| { |
| return VALUE; |
| } |
| |
| switch (*str) |
| { |
| case 0: |
| return EOS; |
| |
| case '\n': |
| return EOL; |
| |
| case '/': |
| return DIV; |
| |
| case '*': |
| return MULT; |
| |
| case '(': |
| return OPAREN; |
| |
| case ')': |
| return CPAREN; |
| |
| case '+': |
| return PLUS; |
| |
| case '-': |
| return MINUS; |
| |
| case '!': |
| return SHRIEK; |
| |
| case ',': |
| return COMMA; |
| |
| case ';': |
| return SEMICOLON; |
| |
| case '"': |
| return QUOTE; |
| |
| case '=': |
| return EQUALS; |
| |
| case '<': |
| return LESS; |
| |
| case '>': |
| return GREATER; |
| |
| default: |
| if (!strncmp(str, "e", 1) && !isalnum(str[1])) |
| { |
| return E; |
| } |
| |
| if (isupper(*str)) |
| { |
| if (!strncmp(str, "SIN", 3) && !isalnum(str[3])) |
| { |
| return SIN; |
| } |
| |
| if (!strncmp(str, "COS", 3) && !isalnum(str[3])) |
| { |
| return COS; |
| } |
| |
| if (!strncmp(str, "TAN", 3) && !isalnum(str[3])) |
| { |
| return TAN; |
| } |
| |
| if (!strncmp(str, "LN", 2) && !isalnum(str[2])) |
| { |
| return LN; |
| } |
| |
| if (!strncmp(str, "POW", 3) && !isalnum(str[3])) |
| { |
| return POW; |
| } |
| |
| if (!strncmp(str, "PI", 2) && !isalnum(str[2])) |
| { |
| return PI; |
| } |
| |
| if (!strncmp(str, "SQRT", 4) && !isalnum(str[4])) |
| { |
| return SQRT; |
| } |
| |
| if (!strncmp(str, "PRINT", 5) && !isalnum(str[5])) |
| { |
| return PRINT; |
| } |
| |
| if (!strncmp(str, "LET", 3) && !isalnum(str[3])) |
| { |
| return LET; |
| } |
| |
| if (!strncmp(str, "DIM", 3) && !isalnum(str[3])) |
| { |
| return DIM; |
| } |
| |
| if (!strncmp(str, "IF", 2) && !isalnum(str[2])) |
| { |
| return IF; |
| } |
| |
| if (!strncmp(str, "THEN", 4) && !isalnum(str[4])) |
| { |
| return THEN; |
| } |
| |
| if (!strncmp(str, "AND", 3) && !isalnum(str[3])) |
| { |
| return AND; |
| } |
| |
| if (!strncmp(str, "OR", 2) && !isalnum(str[2])) |
| { |
| return OR; |
| } |
| |
| if (!strncmp(str, "GOTO", 4) && !isalnum(str[4])) |
| { |
| return GOTO; |
| } |
| |
| if (!strncmp(str, "INPUT", 5) && !isalnum(str[5])) |
| { |
| return INPUT; |
| } |
| |
| if (!strncmp(str, "REM", 3) && !isalnum(str[3])) |
| { |
| return REM; |
| } |
| |
| if (!strncmp(str, "FOR", 3) && !isalnum(str[3])) |
| { |
| return FOR; |
| } |
| |
| if (!strncmp(str, "TO", 2) && !isalnum(str[2])) |
| { |
| return TO; |
| } |
| |
| if (!strncmp(str, "NEXT", 4) && !isalnum(str[4])) |
| { |
| return NEXT; |
| } |
| |
| if (!strncmp(str, "STEP", 4) && !isalnum(str[4])) |
| { |
| return STEP; |
| } |
| |
| if (!strncmp(str, "MOD", 3) && !isalnum(str[3])) |
| { |
| return MOD; |
| } |
| |
| if (!strncmp(str, "ABS", 3) && !isalnum(str[3])) |
| { |
| return ABS; |
| } |
| |
| if (!strncmp(str, "LEN", 3) && !isalnum(str[3])) |
| { |
| return LEN; |
| } |
| |
| if (!strncmp(str, "ASCII", 5) && !isalnum(str[5])) |
| { |
| return ASCII; |
| } |
| |
| if (!strncmp(str, "ASIN", 4) && !isalnum(str[4])) |
| { |
| return ASIN; |
| } |
| |
| if (!strncmp(str, "ACOS", 4) && !isalnum(str[4])) |
| { |
| return ACOS; |
| } |
| |
| if (!strncmp(str, "ATAN", 4) && !isalnum(str[4])) |
| { |
| return ATAN; |
| } |
| |
| if (!strncmp(str, "INT", 3) && !isalnum(str[3])) |
| { |
| return INT; |
| } |
| |
| if (!strncmp(str, "RND", 3) && !isalnum(str[3])) |
| { |
| return RND; |
| } |
| |
| if (!strncmp(str, "VAL", 3) && !isalnum(str[3])) |
| { |
| return VAL; |
| } |
| |
| if (!strncmp(str, "VALLEN", 6) && !isalnum(str[6])) |
| { |
| return VALLEN; |
| } |
| |
| if (!strncmp(str, "INSTR", 5) && !isalnum(str[5])) |
| { |
| return INSTR; |
| } |
| |
| if (!strncmp(str, "CHR$", 4)) |
| { |
| return CHRSTRING; |
| } |
| |
| if (!strncmp(str, "STR$", 4)) |
| { |
| return STRSTRING; |
| } |
| |
| if (!strncmp(str, "LEFT$", 5)) |
| { |
| return LEFTSTRING; |
| } |
| |
| if (!strncmp(str, "RIGHT$", 6)) |
| { |
| return RIGHTSTRING; |
| } |
| |
| if (!strncmp(str, "MID$", 4)) |
| { |
| return MIDSTRING; |
| } |
| |
| if (!strncmp(str, "STRING$", 7)) |
| { |
| return STRINGSTRING; |
| } |
| } |
| |
| /* end isupper() */ |
| |
| if (isalpha(*str)) |
| { |
| while (isalnum(*str)) |
| { |
| str++; |
| } |
| |
| switch (*str) |
| { |
| case '$': |
| return str[1] == '(' ? DIMSTRID : STRID; |
| |
| case '(': |
| return DIMFLTID; |
| |
| default: |
| return FLTID; |
| } |
| } |
| |
| return SYNTAX_ERROR; |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: tokenlen |
| * |
| * Description: |
| * Get the length of a token. |
| * Params: str - pointer to the string containing the token |
| * token - the type of the token read |
| * Returns: length of the token, or 0 for EOL to prevent |
| * it being read past. |
| * |
| ****************************************************************************/ |
| |
| static int tokenlen(FAR const char *str, int tokenid) |
| { |
| int len = 0; |
| |
| switch (tokenid) |
| { |
| case EOS: |
| return 0; |
| |
| case EOL: |
| return 1; |
| |
| case VALUE: |
| getvalue(str, &len); |
| return len; |
| |
| case DIMSTRID: |
| case DIMFLTID: |
| case STRID: |
| getid(str, g_iobuffer, &len); |
| return len; |
| |
| case FLTID: |
| getid(str, g_iobuffer, &len); |
| return len; |
| |
| case PI: |
| return 2; |
| |
| case E: |
| return 1; |
| |
| case SIN: |
| return 3; |
| |
| case COS: |
| return 3; |
| |
| case TAN: |
| return 3; |
| |
| case LN: |
| return 2; |
| |
| case POW: |
| return 3; |
| |
| case SQRT: |
| return 4; |
| |
| case DIV: |
| return 1; |
| |
| case MULT: |
| return 1; |
| |
| case OPAREN: |
| return 1; |
| |
| case CPAREN: |
| return 1; |
| |
| case PLUS: |
| return 1; |
| |
| case MINUS: |
| return 1; |
| |
| case SHRIEK: |
| return 1; |
| |
| case COMMA: |
| return 1; |
| |
| case QUOTE: |
| return 1; |
| |
| case EQUALS: |
| return 1; |
| |
| case LESS: |
| return 1; |
| |
| case GREATER: |
| return 1; |
| |
| case SEMICOLON: |
| return 1; |
| |
| case SYNTAX_ERROR: |
| return 0; |
| |
| case PRINT: |
| return 5; |
| |
| case LET: |
| return 3; |
| |
| case DIM: |
| return 3; |
| |
| case IF: |
| return 2; |
| |
| case THEN: |
| return 4; |
| |
| case AND: |
| return 3; |
| |
| case OR: |
| return 2; |
| |
| case GOTO: |
| return 4; |
| |
| case INPUT: |
| return 5; |
| |
| case REM: |
| return 3; |
| |
| case FOR: |
| return 3; |
| |
| case TO: |
| return 2; |
| |
| case NEXT: |
| return 4; |
| |
| case STEP: |
| return 4; |
| |
| case MOD: |
| return 3; |
| |
| case ABS: |
| return 3; |
| |
| case LEN: |
| return 3; |
| |
| case ASCII: |
| return 5; |
| |
| case ASIN: |
| return 4; |
| |
| case ACOS: |
| return 4; |
| |
| case ATAN: |
| return 4; |
| |
| case INT: |
| return 3; |
| |
| case RND: |
| return 3; |
| |
| case VAL: |
| return 3; |
| |
| case VALLEN: |
| return 6; |
| |
| case INSTR: |
| return 5; |
| |
| case CHRSTRING: |
| return 4; |
| |
| case STRSTRING: |
| return 4; |
| |
| case LEFTSTRING: |
| return 5; |
| |
| case RIGHTSTRING: |
| return 6; |
| |
| case MIDSTRING: |
| return 4; |
| |
| case STRINGSTRING: |
| return 7; |
| |
| default: |
| assert(0); |
| return 0; |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: isstring |
| * |
| * Description: |
| * Test if a token represents a string expression |
| * Params: token - token to test |
| * Returns: 1 if a string, else 0 |
| * |
| ****************************************************************************/ |
| |
| static int isstring(int tokenid) |
| { |
| if (tokenid == STRID || tokenid == QUOTE || tokenid == DIMSTRID |
| || tokenid == CHRSTRING || tokenid == STRSTRING |
| || tokenid == LEFTSTRING || tokenid == RIGHTSTRING |
| || tokenid == MIDSTRING || tokenid == STRINGSTRING) |
| { |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| /**************************************************************************** |
| * Name: getvalue |
| * |
| * Description: |
| * Get a numerical value from the parse string |
| * Params: str - the string to search |
| * len - return pinter for no chars read |
| * Returns: the value of the string. |
| * |
| ****************************************************************************/ |
| |
| static double getvalue(FAR const char *str, FAR int *len) |
| { |
| double answer; |
| FAR char *end; |
| |
| answer = strtod(str, &end); |
| assert(end != str); |
| *len = end - str; |
| return answer; |
| } |
| |
| /**************************************************************************** |
| * Name: getid |
| * |
| * Description: |
| * Get an id from the parse string: |
| * Params: str - string to search |
| * out - id output [32 chars max ] |
| * len - return pointer for id length |
| * Notes: triggers an error if id > 31 chars |
| * the id includes the $ and ( qualifiers. |
| * |
| ****************************************************************************/ |
| |
| static void getid(FAR const char *str, FAR char *out, FAR int *len) |
| { |
| int nread = 0; |
| while (isspace(*str)) |
| { |
| str++; |
| } |
| |
| assert(isalpha(*str)); |
| while (isalnum(*str)) |
| { |
| if (nread < 31) |
| { |
| out[nread++] = *str++; |
| } |
| else |
| { |
| seterror(ERR_IDTOOLONG); |
| break; |
| } |
| } |
| |
| if (*str == '$') |
| { |
| if (nread < 31) |
| { |
| out[nread++] = *str++; |
| } |
| else |
| { |
| seterror(ERR_IDTOOLONG); |
| } |
| } |
| |
| if (*str == '(') |
| { |
| if (nread < 31) |
| { |
| out[nread++] = *str++; |
| } |
| else |
| { |
| seterror(ERR_IDTOOLONG); |
| } |
| } |
| |
| out[nread] = 0; |
| *len = nread; |
| } |
| |
| /**************************************************************************** |
| * Name: mystrgrablit |
| * |
| * Description: |
| * Grab a literal from the parse string. |
| * Params: dest - destination string |
| * src - source string |
| * Notes: strings are in quotes, double quotes the escape |
| * |
| ****************************************************************************/ |
| |
| static void mystrgrablit(FAR char *dest, FAR const char *src) |
| { |
| assert(*src == '"'); |
| src++; |
| |
| while (*src) |
| { |
| if (*src == '"') |
| { |
| if (src[1] == '"') |
| { |
| *dest++ = *src; |
| src++; |
| src++; |
| } |
| else |
| { |
| break; |
| } |
| } |
| else |
| { |
| *dest++ = *src++; |
| } |
| } |
| |
| *dest++ = 0; |
| } |
| |
| /**************************************************************************** |
| * Name: mystrend |
| * |
| * Description: |
| * Find where a source string literal ends |
| * Params: src - string to check (must point to quote) |
| * quote - character to use for quotation |
| * Returns: pointer to quote which ends string |
| * Notes: quotes escape quotes |
| * |
| ****************************************************************************/ |
| |
| static FAR char *mystrend(FAR const char *str, char quote) |
| { |
| assert(*str == quote); |
| str++; |
| |
| while (*str) |
| { |
| while (*str != quote) |
| { |
| if (*str == '\n' || *str == 0) |
| { |
| return 0; |
| } |
| |
| str++; |
| } |
| |
| if (str[1] == quote) |
| { |
| str += 2; |
| } |
| else |
| { |
| break; |
| } |
| } |
| |
| return (char *)(*str ? str : 0); |
| } |
| |
| /**************************************************************************** |
| * Name: mystrcount |
| * |
| * Description: |
| * Count the instances of ch in str |
| * Params: str - string to check |
| * ch - character to count |
| * Returns: no time chs occurs in str. |
| * |
| ****************************************************************************/ |
| |
| static int mystrcount(FAR const char *str, char ch) |
| { |
| int answer = 0; |
| |
| while (*str) |
| { |
| if (*str++ == ch) |
| { |
| answer++; |
| } |
| } |
| |
| return answer; |
| } |
| |
| /**************************************************************************** |
| * Name: mystrdup |
| * |
| * Description: |
| * Duplicate a string: |
| * Params: str - string to duplicate |
| * Returns: malloced duplicate. |
| * |
| ****************************************************************************/ |
| |
| static FAR char *mystrdup(FAR const char *str) |
| { |
| return strdup(str); |
| } |
| |
| /**************************************************************************** |
| * Name: mystrconcat |
| * |
| * Description: |
| * Concatenate two strings |
| * Params: str - firsts string |
| * cat - second string |
| * Returns: malloced string. |
| * |
| ****************************************************************************/ |
| |
| static FAR char *mystrconcat(FAR const char *str, FAR const char *cat) |
| { |
| int len; |
| FAR char *answer; |
| |
| len = strlen(str) + strlen(cat) + 1; |
| answer = malloc(len); |
| if (answer) |
| { |
| strlcpy(answer, str, len); |
| strlcat(answer, cat, len); |
| } |
| |
| return answer; |
| } |
| |
| /**************************************************************************** |
| * Name: factorial |
| * |
| * Description: |
| * Compute x! |
| * |
| ****************************************************************************/ |
| |
| static double factorial(double x) |
| { |
| double answer = 1.0; |
| double t; |
| |
| if (x > 1000.0) |
| { |
| x = 1000.0; |
| } |
| |
| for (t = 1; t <= x; t += 1.0) |
| { |
| answer *= t; |
| } |
| |
| return answer; |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: basic |
| * |
| * Description: |
| * Interpret a BASIC script |
| * |
| * Input Parameters: |
| * script - the script to run |
| * in - input stream |
| * out - output stream |
| * err - error stream |
| * |
| * Returned Value: |
| * Returns: 0 on success, 1 on error condition. |
| * |
| ****************************************************************************/ |
| |
| int basic(FAR const char *script, FILE * in, FILE * out, FILE * err) |
| { |
| int curline = 0; |
| int nextline; |
| int answer = 0; |
| |
| g_fpin = in; |
| g_fpout = out; |
| g_fperr = err; |
| |
| if (setup(script) == -1) |
| { |
| return 1; |
| } |
| |
| while (curline != -1) |
| { |
| g_string = g_lines[curline].str; |
| g_token = gettoken(g_string); |
| g_errorflag = 0; |
| |
| nextline = line(); |
| if (g_errorflag) |
| { |
| reporterror(g_lines[curline].no); |
| answer = 1; |
| break; |
| } |
| |
| if (nextline == -1) |
| { |
| break; |
| } |
| |
| if (nextline == 0) |
| { |
| curline++; |
| if (curline == nlines) |
| break; |
| } |
| else |
| { |
| curline = findline(nextline); |
| if (curline == -1) |
| { |
| if (g_fperr) |
| { |
| fprintf(g_fperr, "line %d not found\n", nextline); |
| } |
| |
| answer = 1; |
| break; |
| } |
| } |
| } |
| |
| cleanup(); |
| return answer; |
| } |