/* 
 * @cond
 * The following section will be excluded from the documentation.
 */
/* *********************************************************************************************************************
PicoMite MMBasic

MMBasic.h

<COPYRIGHT HOLDERS>  Geoff Graham, Peter Mather
Copyright (c) 2021, <COPYRIGHT HOLDERS> All rights reserved. 
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.	The name MMBasic be used when referring to the interpreter in any documentation and promotional material and the original copyright message be displayed 
    on the console at startup (additional copyright messages may be added).
4.	All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed 
    by the <copyright holder>.
5.	Neither the name of the <copyright holder> 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 <COPYRIGHT HOLDERS> 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 <COPYRIGHT HOLDERS> 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 __MMBASIC_H
#define __MMBASIC_H

#ifdef __cplusplus
extern "C" {
#endif

#include <stdlib.h>
#include <setjmp.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <math.h>
#include <stdint.h>
#include <stdbool.h>
#if defined(MAXIMITE) || defined(UBW32) || defined(DUINOMITE) || defined(COLOUR)
  #define MMFAMILY
#endif

#include "configuration.h"                          // memory configuration defines for the particular hardware this is running on

// Types used to define an item of data.  Often they are ORed together.
// Used in tokens, variables and arguments to functions
#define T_NOTYPE       0                            // type not set or discovered
#define T_NBR       0x01                            // number (or float) type
#define T_STR       0x02                            // string type
#define T_INT       0x04                            // 64 bit integer type
#define T_PTR       0x08                            // the variable points to another variable's data
#define T_IMPLIED   0x10                            // the variables type does not have to be specified with a suffix
#define T_CONST     0x20                            // the contents of this variable cannot be changed
#define T_BLOCKED   0x40                            // Hash table entry blocked after ERASE
#define T_EXPLICIT  0x80                            // Was the variable specified with a type suffix
#define TypeMask(a) ((a) & (T_NBR | T_INT | T_STR)) // macro to isolate the variable type bits

// types of tokens.  These are or'ed with the data types above to fully define a token
#define T_INV       0                               // an invalid token
#define T_NA        0                               // an invalid token
#define T_CMD       0x10                            // a command
#define T_OPER      0x20                            // an operator
#define T_FUN       0x40                            // a function (also used for a function that can operate as a command)
#define T_FNA       0x80                            // a function that has no arguments

#define C_BASETOKEN 0x80                            // the base of the token numbers

// flags used in the program lines
#define T_CMDEND    0                               // end of a command
#define T_NEWLINE   1                               // Single byte indicating the start of a new line
#define T_LINENBR   2                               // three bytes for a line number
#define T_LABEL     3                               // variable length indicating a label

#define E_END       255                             // dummy last operator in an expression

// these constants are used in the second argument of the findvar() function, they should be or'd together
#define V_FIND              0x0000                    // a straight forward find, if the variable is not found it is created and set to zero
#define V_NOFIND_ERR        0x0200                    // throw an error if not found
#define V_NOFIND_NULL       0x0400                    // return a null pointer if not found
#define V_DIM_VAR           0x0800                    // dimension an array
#define V_LOCAL             0x1000                    // create a local variable
#define V_EMPTY_OK          0x2000                    // allow an empty array variable.  ie, var()
#define V_FUNCT             0x4000                    // we are defining the name of a function

// these flags are used in the last argument in expression()
#define E_NOERROR           true
#define E_ERROR             0
#define E_DONE_GETVAL       0b10

extern struct s_vartbl  s_vartbl_val;
struct s_funtbl {
	char name[MAXVARLEN];                       // variable's name
	uint32_t index;
};

extern struct s_funtbl funtbl[MAXSUBFUN];
typedef struct s_vartbl {                               // structure of the variable table
	unsigned char name[MAXVARLEN];                       // variable's name
	unsigned char type;                                  // its type (T_NUM, T_INT or T_STR)
	unsigned char level;                                 // its subroutine or function level (used to track local variables)
    unsigned char size;                         // the number of chars to allocate for each element in a string array
    unsigned char namelen;
#ifdef rp2350
    int __attribute__ ((aligned (4))) dims[MAXDIM];                     // the dimensions. it is an array if the first dimension is NOT zero
#else
    short __attribute__ ((aligned (4))) dims[MAXDIM];                     // the dimensions. it is an array if the first dimension is NOT zero
#endif
    union u_val{
        MMFLOAT f;                              // the value if it is a float
        long long int i;                        // the value if it is an integer
        MMFLOAT *fa;                            // pointer to the allocated memory if it is an array of floats
        long long int *ia;                      // pointer to the allocated memory if it is an array of integers
        unsigned char *s;                                // pointer to the allocated memory if it is a string
    }  val;
} vartbl_val;

typedef struct s_hash {                            
	short hash;                                
    short level;                       
} hash_val;

extern struct s_vartbl g_vartbl[];

extern int g_varcnt;                              // number of variables defined (eg, largest index into the variable table)
//extern int g_Localvarcnt;                              // number of LOCAL variables defined (eg, largest index into the variable table)
extern int g_Globalvarcnt;                              // number of GLOBAL variables defined (eg, largest index into the variable table)
extern int g_Localvarcnt;                              // number of GLOBAL variables defined (eg, largest index into the variable table)
extern int g_VarIndex;                            // index of the current variable.  set after the findvar() function has found/created a variable
extern int g_LocalIndex;                          // used to track the level of local variables

extern int g_OptionBase;                          // value of OPTION BASE
extern unsigned char OptionExplicit, OptionEscape, OptionConsole;                     // true if OPTION EXPLICIT has been used
extern bool OptionNoCheck;
extern unsigned char DefaultType;                        // the default type if a variable is not specifically typed


#if !defined(BOOL_ALREADY_DEFINED)
    #define BOOL_ALREADY_DEFINED
    typedef enum _BOOL { FALSE = 0, TRUE } BOOL;    // Undefined size
#endif

#ifndef true
    #define true        1
#endif

#ifndef false
    #define false       0
#endif

#define MAXLINENBR          65001                                   // maximim acceptable line number
#define NOLINENBR           MAXLINENBR+1
// skip whitespace
// finishes with x pointing to the next non space char
#define skipspace(x)    while(*x == ' ') x++

// skip to the next element
// finishes pointing to the zero unsigned char that preceeds an element
#define skipelement(x)  while(*x) x++

// skip to the next line
// skips text and and element separators until it is pointing to the zero char marking the start of a new line.
// the next byte will be either the newline token or zero char if end of program
#define skipline(x)     while(!(x[-1] == 0 && (x[0] == T_NEWLINE || x[0] == 0)))x++
// find a token
// finishes pointing to the token or zero unsigned char if not found in the line
#define findtoken(x)    while(*x != (tkn) && *x)x++
#define IsDigitinline(a)	( a >= '0' && a <= '9' )
//extern const char namestart[256];
//extern const char namein[256];
//extern const char nameend[256];
//extern const char upper[256];
//#define mytoupper(a) upper[(unsigned int)a]
#define mytoupper(a) toupper(a)
//#define isnamestart(c)  (namestart[(uint8_t)c])                    // true if valid start of a variable name
//#define isnamechar(c)   (namein[(uint8_t)c])        // true if valid part of a variable name
//#define isnameend(c)    (nameend[(uint8_t)c])        // true if valid at the end of a variable name
#define isnamestart(c)  (isalpha((unsigned char)c) || c == '_')                    // true if valid start of a variable name
#define isnamechar(c)   (isalnum((unsigned char)c) || c == '_' || c == '.')        // true if valid part of a variable name
#define isnameend(c)    (isalnum((unsigned char)c) || c == '_' || c == '.' || c == '$' || c == '!' || c == '%')        // true if valid at the end of a variable name
#define tokentype(i)    ((i >= C_BASETOKEN && i < TokenTableSize - 1 + C_BASETOKEN) ? (tokentbl[i - C_BASETOKEN].type) : 0)             // get the type of a token
#define tokenfunction(i)((i >= C_BASETOKEN && i < TokenTableSize - 1 + C_BASETOKEN) ? (tokentbl[i - C_BASETOKEN].fptr) : (tokentbl[0].fptr))    // get the function pointer  of a token
#define tokenname(i)    ((i >= C_BASETOKEN && i < TokenTableSize - 1 + C_BASETOKEN) ? (tokentbl[i - C_BASETOKEN].name) : (unsigned char *)"")            // get the name of a token

#define commandfunction(i)((i < CommandTableSize - 1) ? (commandtbl[i].fptr) : (commandtbl[0].fptr))    // get the function pointer  of a token
#define commandname(i)  ((i < CommandTableSize - 1 ) ? (commandtbl[i].name) : (unsigned char *)"")        // get the name of a command

// this macro will allocate temporary memory space and build an argument table in it
// x = pointer to the basic text to be split up (unsigned char *)
// y = maximum number of args (will throw an error if exceeded) (int)
// s = a string of characters to be used in detecting where to split the text (unsigned char *)
#define getargs(x, y, s) unsigned char argbuf[STRINGSIZE + STRINGSIZE/2]; unsigned char *argv[y]; int argc; makeargs(x, y, argbuf, argv, &argc, s)

extern int CommandTableSize, TokenTableSize;

extern volatile int MMAbort;
extern jmp_buf mark;                            // longjump to recover from an error
extern unsigned char BreakKey;                           // console break key (defaults to CTRL-C)
extern jmp_buf jmprun;
extern int ProgMemSize;

extern int NextData;                            // used to track the next item to read in DATA & READ stmts
extern unsigned char *NextDataLine;                      // used to track the next line to read in DATA & READ stmts
extern unsigned char *CurrentLinePtr,*SaveCurrentLinePtr;                    // pointer to the current line being executed
extern unsigned char *ContinuePoint;                     // Where to continue from if using the continue statement
extern int ProgramChanged;                                                 // true if the program in memory has been changed and not saved

extern unsigned char inpbuf[];                           // used to store user keystrokes until we have a line
extern unsigned char tknbuf[];                           // used to store the tokenised representation of the users input line
extern unsigned char lastcmd[];                          // used to store the command history in case the user uses the up arrow at the command prompt

extern MMFLOAT farg1, farg2, fret;                // Global floating point variables used by operators
extern long long int  iarg1, iarg2, iret;        // Global integer variables used by operators
extern unsigned char *sarg1, *sarg2, *sret;              // Global string pointers used by operators
extern int targ;                                // Global type of argument (string or float) returned by an operator

extern int cmdtoken;                            // Token number of the command
extern unsigned char *cmdline;                           // Command line terminated with a zero unsigned char and trimmed of spaces
extern unsigned char *nextstmt;                          // Pointer to the next statement to be executed.
extern unsigned char *ep;                                // Pointer to the argument to a function

extern int OptionErrorSkip;                    // value of OPTION ERROR
extern int MMerrno;

extern char MMErrMsg[MAXERRMSG];                // array holding the error msg

extern unsigned char *subfun[];                          // Table of subroutines and functions built when the program starts running
extern char CurrentSubFunName[MAXVARLEN + 1];   // the name of the current sub or fun
extern char CurrentInterruptName[MAXVARLEN + 1];// the name of the current interrupt function

struct s_tokentbl {                             // structure of the token table
    unsigned char *name;                                 // the string (eg, PRINT, FOR, ASC(, etc)
    unsigned char type;                                  // the type returned (T_NBR, T_STR, T_INT)
    unsigned char precedence;                            // precedence used by operators only.  operators with equal precedence are processed left to right.
    void (*fptr)(void);                         // pointer to the function that will interpret that token
};
extern const struct s_tokentbl tokentbl[];
extern const struct s_tokentbl commandtbl[];

extern unsigned char tokenTHEN, tokenELSE, tokenGOTO, tokenEQUAL, tokenTO, tokenSTEP, tokenWHILE, tokenUNTIL, tokenGOSUB, tokenAS, tokenFOR;
extern unsigned short cmdIF, cmdENDIF, cmdEND_IF, cmdELSEIF, cmdELSE_IF, cmdELSE, cmdSELECT_CASE, cmdFOR, cmdNEXT, cmdWHILE, cmdENDSUB, cmdENDFUNCTION, cmdLOCAL, cmdSTATIC, cmdCASE, cmdDO, cmdLOOP, cmdCASE_ELSE, cmdEND_SELECT;
extern unsigned short cmdSUB, cmdFUN, cmdCSUB, cmdIRET, cmdComment, cmdEndComment;

extern unsigned char *GetIntAddress(unsigned char *p);
extern void MMPrintString(char *s);

// void error(unsigned char *msg) ;
void  error(char *msg, ...);
void  InitBasic(void);
int FloatToInt32(MMFLOAT);
long long int  FloatToInt64(MMFLOAT x);
void makeargs(unsigned char **tp, int maxargs, unsigned char *argbuf, unsigned char *argv[], int *argc, unsigned char *delim);
void *findvar(unsigned char *, int);
void erasearray(unsigned char *n);
void  ClearVars(int level, bool all);
void  ClearStack(void);
void  ClearRuntime(bool all);
void  ClearProgram(bool psram);
void *DoExpression(unsigned char *p, int *t);
unsigned char *GetNextCommand(unsigned char *p, unsigned char **CLine, unsigned char *EOFMsg) ;
int CheckEmpty(char *p);
unsigned char *evaluate(unsigned char *p, MMFLOAT *fa, long long int  *ia, unsigned char **sa, int *ta, int noerror);
unsigned char *doexpr(unsigned char *p, MMFLOAT *fa, long long int  *ia, unsigned char **sa, int *oo, int *t);
void DefinedSubFun(int iscmd, unsigned char *cmd, int index, MMFLOAT *fa, long long int  *i64, unsigned char **sa, int *t);
MMFLOAT getnumber(unsigned char *p);
long long int  getinteger(unsigned char *p);
long long int getint(unsigned char *p, long long int min, long long int max);
unsigned char *getstring(unsigned char *p);
void  tokenise(int console);
void ExecuteProgram(unsigned char *);
void AddProgramLine(int append);
unsigned char *findline(int, int);
unsigned char *findlabel(unsigned char *labelptr);
unsigned char *skipvar(unsigned char *p, int noerror);
unsigned char *skipexpression(unsigned char *p);
int FunctionType(unsigned char *p);
unsigned char *getclosebracket(unsigned char *p);
void makeupper(unsigned char *p);
void checkend(unsigned char *p);
char *fstrstr (const char *s1, const char *s2);
int GetCommandValue(unsigned char *n);
int GetTokenValue(unsigned char *n);
unsigned char *checkstring(unsigned char *p, unsigned char *tkn);
int GetLineLength(unsigned char *p);
unsigned char *MtoC(unsigned char *p);
unsigned char *CtoM(unsigned char *p);
void Mstrcpy(unsigned char *dest, unsigned char *src);
void Mstrcat(unsigned char *dest, unsigned char *src);
int Mstrcmp(unsigned char *s1, unsigned char *s2);
unsigned char *getCstring(unsigned char *p);
unsigned char *getFstring(unsigned char *p);
int IsValidLine(int line);
void InsertLastcmd(unsigned char *s);
int  CountLines(unsigned char *target);
extern jmp_buf ErrNext;   
int FindSubFun(unsigned char *p, int type);
void PrepareProgram(int ErrAbort);
void MMfputs(unsigned char *p, int filenbr);
extern int TempStringClearStart;                                           // used to prevent clearing of space in an expression that called a FUNCTION
extern unsigned char *LibMemory;                           // library memory
extern unsigned char *ProgMemory;                           // program memory
extern int PSize;                               // size of the program in program memory
extern unsigned char *cmdline;                           // Command line terminated with a zero unsigned char and trimmed of spaces
extern unsigned char *nextstmt;                          // Pointer to the next statement to be executed.
extern unsigned char PromptString[MAXPROMPTLEN];                                    // the prompt for input, an empty string means use the default
extern int multi;
extern void str_replace(char *target, const char *needle, const char *replacement, uint8_t ignore);
extern void  MIPS16 STR_REPLACE(char *target, const char *needle, const char *replacement, uint8_t ignore);
#if defined(MMFAMILY)
extern unsigned char FunKey[NBRPROGKEYS][MAXKEYLEN + 1]; // used by the programmable function keys
#endif

#if defined(MMFAMILY) || defined(DOS)
extern unsigned char *ModuleTable[MAXMODULES];           // list of pointers to library modules loaded in memory;
extern int NbrModules;                          // the number of library modules currently loaded
#endif

#if defined(__PIC32MX__)
inline int str_equal(const unsigned char *s1, const unsigned char *s2);
#else
void IntToStrPad(char *p, long long int nbr, signed char padch, int maxch, int radix);
void IntToStr(char *strr, long long int nbr, unsigned int base);
void FloatToStr(char *p, MMFLOAT f, int m, int n, unsigned char ch);
int str_equal(const unsigned char *s1, const unsigned char *s2);
#endif
int  mystrncasecmp (const unsigned char *s1, const unsigned char *s2, size_t n);
int mem_equal(unsigned char *s1, unsigned char *s2, int i);
extern int emptyarray;
typedef uint16_t CommandToken;

#ifdef __cplusplus
}
#endif
#endif /* __MMBASIC_H */
/*  @endcond */
