diff --git a/COPYING b/COPYING index 5aed07c81..902fe927a 100644 --- a/COPYING +++ b/COPYING @@ -80,6 +80,17 @@ 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. +Mini Basic +^^^^^^^^^^ + +The Mini Basic implementation at apps/interpreters derives from version 1.0 +by Malcolm McLean, Leeds University, and was released under the Creative +Commons Attibution license. I am not legal expert, but this license +appears to be compatible with the NuttX BSD license see: +https://creativecommons.org/licenses/ . I, however, cannot take +responsibility for any actions that you might take based on my +understanding. Please use your own legal judgement. + THTTPD ^^^^^^ diff --git a/include/interpreters/minibasic.h b/include/interpreters/minibasic.h new file mode 100644 index 000000000..f2a6d4ae8 --- /dev/null +++ b/include/interpreters/minibasic.h @@ -0,0 +1,72 @@ +/**************************************************************************** + * 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 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. + * + ****************************************************************************/ + +#ifndef __APPS_INCLUDE_INTERPRETERS_MINIBASIC_H +#define __APPS_INCLUDE_INTERPRETERS_MINIBASIC_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * 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); + +#endif \ No newline at end of file diff --git a/interpreters/minibasic/Kconfig b/interpreters/minibasic/Kconfig new file mode 100644 index 000000000..81d8d7354 --- /dev/null +++ b/interpreters/minibasic/Kconfig @@ -0,0 +1,52 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config INTERPRETERS_MINIBASIC + bool "Mini Basic Interpreter support" + default n + select LIBC_FLOATINGPOINT + ---help--- + This is a Basic interpreter written by Malcolm McLean + + NOTE: This interpreter requires a usable math.h header file. By + default, the math library (and hence, math.h) are not provided by + NuttX. Therefore, when the Basic code includes math.h it will + either fail to find the math.h header file or, worse, will take an + incompatible version of math.h from your toolchain. The toolchain's + version of math.h will be incompatible because it will have been + implemented to work with a different version of the C library. + + Normally, developers will use an optimized math library for their + processor architecture and do the following: + + - Save a customized copy of math.h from your tool chain in + nuttx/arch//include + - Set CONFIG_ARCH_MATH_H=y in your .config file to select this + architecture-specific math.h header file. + + An option is to use the built-in, generic, unoptimized NuttX math + library that is selected by simply by: + + - Set CONFIG_LIBM=y in your .config file + +if INTERPRETERS_MINIBASIC + +config INTERPRETER_MINIBASIC_VERSION + string "Version number" + default "1.0" + +config INTERPRETER_MINIBASIC_PRIORITY + int "Basic interpreter priority" + default 100 + ---help--- + Task priority of the Basic interpreter main task + +config INTERPRETER_MINIBASIC_STACKSIZE + int "Basic interpreter stack size" + default 4096 + ---help--- + Size of the stack allocated for the Basic interpreter main task + +endif diff --git a/interpreters/minibasic/Make.defs b/interpreters/minibasic/Make.defs new file mode 100644 index 000000000..99bcdad45 --- /dev/null +++ b/interpreters/minibasic/Make.defs @@ -0,0 +1,38 @@ +############################################################################ +# interpreters/minibasic/Make.defs +# +# Copyright (C) 2016 Gregory Nutt. All rights reserved. +# Author: Gregory Nutt +# +# 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. +# +############################################################################ + +ifeq ($(CONFIG_INTERPRETERS_MINIBASIC),y) +CONFIGURED_APPS += interpreters/minibasic +endif diff --git a/interpreters/minibasic/Makefile b/interpreters/minibasic/Makefile new file mode 100644 index 000000000..dfc79d68f --- /dev/null +++ b/interpreters/minibasic/Makefile @@ -0,0 +1,116 @@ +############################################################################ +# interpreters/minibasic/Makefile +# +# Copyright (C) 2016 Gregory Nutt. All rights reserved. +# Author: Gregory Nutt +# +# 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. +# +############################################################################ + +-include $(TOPDIR)/.config +-include $(TOPDIR)/Make.defs +include $(APPDIR)/Make.defs + +# Mini Basic Library + +ASRCS = +CSRCS = basic.c script.c + +DEPPATH = --dep-path . +VPATH = . + +AOBJS = $(ASRCS:.S=$(OBJEXT)) +COBJS = $(CSRCS:.c=$(OBJEXT)) + +SRCS = $(ASRCS) $(CSRCS) +OBJS = $(AOBJS) $(COBJS) + +ifeq ($(CONFIG_WINDOWS_NATIVE),y) + BIN = ..\..\libapps$(LIBEXT) +else +ifeq ($(WINTOOL),y) + BIN = ..\\..\\libapps$(LIBEXT) +else + BIN = ../../libapps$(LIBEXT) +endif +endif + +# Mini Basic built-in application info + +CONFIG_INTERPRETER_MINIBASIC_PRIORITY ?= 100 +CONFIG_INTERPRETER_MINIBASIC_STACKSIZE ?= 4096 + +APPNAME = basic +PRIORITY = $(CONFIG_INTERPRETER_MINIBASIC_PRIORITY) +STACKSIZE = $(CONFIG_INTERPRETER_MINIBASIC_STACKSIZE) + +# Build targets + +all: .built +.PHONY: context .depend depend clean distclean + +$(AOBJS): %$(OBJEXT): %.S + $(call ASSEMBLE, $<, $@) + +$(COBJS): %$(OBJEXT): %.c + $(call COMPILE, $<, $@) + +.built: $(OBJS) + $(call ARCHIVE, $(BIN), $(OBJS)) + $(Q) touch .built + +install: + +ifeq ($(CONFIG_NSH_BUILTIN_APPS),y) +$(BUILTIN_REGISTRY)$(DELIM)$(APPNAME)_main.bdat: $(DEPCONFIG) Makefile + $(call REGISTER,$(APPNAME),$(PRIORITY),$(STACKSIZE),$(APPNAME)_main) + +context: $(BUILTIN_REGISTRY)$(DELIM)$(APPNAME)_main.bdat +else +context: +endif + +.depend: Makefile $(SRCS) + $(Q) $(MKDEP) $(DEPPATH) "$(CC)" -- $(CFLAGS) -- $(SRCS) >Make.dep + $(Q) touch $@ + +depend: .depend + +clean: + $(call DELFILE, .built) + $(call CLEAN) + +distclean: clean + $(call DELFILE, Make.dep) + $(call DELFILE, .depend) + +-include Make.dep +.PHONY: preconfig + +preconfig: diff --git a/interpreters/minibasic/basic.c b/interpreters/minibasic/basic.c new file mode 100644 index 000000000..16efd80e5 --- /dev/null +++ b/interpreters/minibasic/basic.c @@ -0,0 +1,4074 @@ +/**************************************************************************** + * 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 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 +#include +#include +#include +#include +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* 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 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 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 + ****************************************************************************/ + +static struct mb_forloop_s g_forstack[MAXFORS]; /* Stack for for loop conrol */ +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 strem */ +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 */ + +/**************************************************************************** + * 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 har *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')) + { + 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 + { + strcpy(g_forstack[nfors].id, 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; + char buff[1024]; + FAR char *end; + + match(INPUT); + lvalue(&lv); + + switch (lv.type) + { + case FLTID: + while (fscanf(g_fpin, "%lf", lv.dval) != 1) + { + fgetc(g_fpin); + if (feof(g_fpin)) + { + seterror(ERR_EOF); + return; + } + } + break; + + case STRID: + if (*lv.sval) + { + free(*lv.sval); + *lv.sval = 0; + } + + if (fgets(buff, sizeof(buff), g_fpin) == 0) + { + seterror(ERR_EOF); + return; + } + + end = strchr(buff, '\n'); + if (!end) + { + seterror(ERR_INPUTTOOLONG); + return; + } + + *end = 0; + *lv.sval = mystrdup(buff); + if (!*lv.sval) + { + seterror(ERR_OUTOFMEMORY); + return; + } + break; + + default: + return; + } +} + +/**************************************************************************** + * Name: dorem + * + * Description: + * The REM statement. + * Note is unique as the rest of the line is not parsed + * + ****************************************************************************/ + +static void dorem(void) +{ + match(REM); + return; +} + +/**************************************************************************** + * 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 = ERROR; + lv->dval = 0; + lv->sval = 0; + + 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 = 0; + 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 = 0; + 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 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 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: + * Calcualte 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; + + 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) + { + 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 varaible to our variable list + * Params: id - id of varaible 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; + strcpy(g_variables[g_nvariables].id, id); + g_variables[g_nvariables].dval = 0; + g_variables[g_nvariables].sval = 0; + 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; + strcpy(g_variables[g_nvariables].id, id); + g_variables[g_nvariables].sval = 0; + g_variables[g_nvariables].dval = 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; + strcpy(g_dimvariables[g_ndimvariables].id, id); + g_dimvariables[g_ndimvariables].dval = 0; + g_dimvariables[g_ndimvariables].str = 0; + 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; + char buff[6]; + FAR char *answer; + + match(CHRSTRING); + match(OPAREN); + x = integer(expr()); + match(CPAREN); + + buff[0] = (char)x; + buff[1] = 0; + answer = mystrdup(buff); + + if (!answer) + { + seterror(ERR_OUTOFMEMORY); + } + + return answer; +} + +/**************************************************************************** + * Name: strstring + * + * Description: + * Parse the STR$ token + * + ****************************************************************************/ + +static FAR char *strstring(void) +{ + double x; + char buff[64]; + FAR char *answer; + + match(STRSTRING); + match(OPAREN); + x = expr(); + match(CPAREN); + + sprintf(buff, "%g", x); + answer = mystrdup(buff); + 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; + } + + strncpy(answer, temp, len); + answer[len] = 0; + 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++) + { + strcpy(answer + len * i, str); + } + + 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; + 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) + { + 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 the 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 == 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 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; + char buff[32]; + + 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, buff, &len); + return len; + + case FLTID: + getid(str, buff, &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 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 + * Retuns: 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) +{ + FAR char *answer; + + answer = malloc(strlen(str) + 1); + if (answer) + { + strcpy(answer, str); + } + + return answer; +} + +/**************************************************************************** + * 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); + answer = malloc(len + 1); + if (answer) + { + strcpy(answer, str); + strcat(answer, cat); + } + + 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; +} diff --git a/interpreters/minibasic/script.c b/interpreters/minibasic/script.c new file mode 100644 index 000000000..2245ab33c --- /dev/null +++ b/interpreters/minibasic/script.c @@ -0,0 +1,176 @@ + +/**************************************************************************** + * apps/interpreters/minibasic/sript.c + * Driver file for MiniBasic. + * + * 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 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 + +#include +#include + +#include "interpreters/minibasic.h" + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Here is a simple script to play with */ + +static FAR char *script = + "10 REM Test Script\n" + "20 REM Tests the Interpreter\n" + "30 REM By Malcolm Mclean\n" + "35 PRINT \"HERE\" \n" + "40 PRINT INSTR(\"FRED\", \"ED\", 4)\n" + "50 PRINT VALLEN(\"12a\"), VALLEN(\"xyz\")\n" + "60 LET x = SQRT(3.0) * SQRT(3.0)\n" + "65 LET x = INT(x + 0.5)\n" "70 PRINT MID$(\"1234567890\", x, -1)\n"; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: leftstring + * + * Description: + * Function to slurp in an ASCII file + * Params: path - path to file + * Returns: malloced string containing whole file + * + ****************************************************************************/ + +static FAR char *loadfile(FAR char *path) +{ + FILE *fp; + int ch; + long i = 0; + long size = 0; + FAR char *answer; + + fp = fopen(path, "r"); + if (!fp) + { + printf("Can't open %s\n", path); + return 0; + } + + fseek(fp, 0, SEEK_END); + size = ftell(fp); + fseek(fp, 0, SEEK_SET); + + answer = malloc(size + 100); + if (!answer) + { + printf("Out of memory\n"); + fclose(fp); + return 0; + } + + while ((ch = fgetc(fp)) != EOF) + { + answer[i++] = ch; + } + + answer[i++] = 0; + + fclose(fp); + return answer; +} + +/**************************************************************************** + * Name: leftstring + * + * Description: + * + * + ****************************************************************************/ + +static void usage(void) +{ + printf("MiniBasic: a BASIC interpreter\n"); + printf("usage:\n"); + printf("Basic