/***************************************************************** * Mini BASIC * * by Malcolm McLean * * version 1.0 * *****************************************************************/ #ifdef __cplusplus extern "C" { #endif #if defined(__K64F__) #include #include #include #include #include #include #include "k64f_soc.h" #include #include <../../libraries/include/stdmisc.h> #undef stdout #undef stdin #undef stderr extern FILE *stdout; extern FILE *stdin; extern FILE *stderr; #elif defined(__ZPU__) #include #include #include "zpu_soc.h" #include #include #include <../../libraries/include/stdmisc.h> #define acos acosf #define floor floorf #define sin sinf #define cos cosf #define tan tanf #define log logf #define pow powf #define sqrt sqrtf #define asin asinf #define atan atanf #define fmod fmodf #else #error "Target CPU not defined, use __ZPU__ or __K64F__" #endif #include "interrupts.h" #include "ff.h" /* Declarations of FatFs API */ #include #include #include "utils.h" // #if defined __ZPUTA__ #include "zputa_app.h" #elif defined __ZOS__ #include "zOS_app.h" #else #error OS not defined, use __ZPUTA__ or __ZOS__ #endif // #include "mbasic.h" FORLOOP forstack[MAXFORS]; /* stack for for loop conrol */ int nfors; /* number of fors on stack */ VARIABLE *variables; /* the script's variables */ int nvariables; /* number of variables */ DIMVAR *dimvariables; /* dimensioned arrays */ int ndimvariables; /* number of dimensioned arrays */ LINE *lines = NULL; /* list of line starts */ int nlines = 0; /* number of BASIC lines in program */ const char *string; /* string we are parsing */ int token; /* current token (lookahead) */ int errorflag; /* set when error in input encountered */ void cleanup(void); void reporterror(int lineno); int findline(int no); int line(int); void doprint(int); void dolet(int); void dodim(int); int doif(int); int dogoto(int); int doinput(int); void dorem(int); int dofor(int); int donext(int); void lvalue(LVALUE *lv); int boolexpr(void); int boolfactor(void); int relop(void); double expr(void); double term(void); double factor(void); double instr(void); double variable(void); double dimvariable(void); VARIABLE *findvariable(const char *id); DIMVAR *finddimvar(const char *id); DIMVAR *dimension(const char *id, int ndims, ...); void *getdimvar(DIMVAR *dv, ...); VARIABLE *addfloat(const char *id); VARIABLE *addstring(const char *id); DIMVAR *adddimvar(const char *id); char *stringexpr(void); char *chrstring(void); char *strstring(void); char *leftstring(void); char *rightstring(void); char *midstring(void); char *stringstring(void); char *stringdimvar(void); char *stringvar(void); char *stringliteral(void); int integer(double x); void match(int tok); void seterror(int errorcode); int getnextline(int); int gettoken(const char *str); int tokenlen(const char *str, int token); int isstring(int token); double getvalue(const char *str, int *len); void getid(const char *str, char *out, int *len); void mystrgrablit(char *dest, const char *src); char *mystrend(const char *str, char quote); int mystrcount(const char *str, char ch); char *mystrdup(const char *str); char *mystrconcat(const char *str, const char *cat); double factorial(double x); // Method to run the basic script, the lines array is prebuilt. // Returns: 0 on success, 1 on error condition. // int execBasicScript(void) { int curline = 0; int nextline; int answer = 0; int8_t keyIn; // Sanity check, no script no run! // if(nlines == 0) return(0); // Initialise variables. nvariables = 0; variables = 0; dimvariables = 0; ndimvariables = 0; while(curline != -1) { string = lines[curline].str; token = gettoken(string); errorflag = 0; nextline = line(curline); if(errorflag) { reporterror(lines[curline].no); answer = 1; break; } //if(nextline == -1) // break; #if defined __SHARPMZ__ // Get key from system, request ansi key sequence + non-blocking (=2). keyIn = getKey(2); #else // Get key from system, request non-blocking (=0). keyIn = getKey(0); #endif // Check to see if user is requesting an action. if(nextline == -1 || keyIn == CTRL_C) { printf("\nExecution stopped, user request.\n"); break; } if(nextline == 0) { curline++; if(curline == nlines) break; } else { curline = findline(nextline); if(curline == -1) { printf("line %d not found\n", nextline); answer = 1; break; } } } cleanup(); return answer; } /* frees all the memory we have allocated */ void cleanup(void) { int i; int ii; int size; for(i=0;i low + 1) { mid = (high + low)/2; if(lines[mid].no == no) return mid; if(lines[mid].no > no) high = mid; else low = mid; } if(lines[low].no == no) mid = low; else if(lines[high].no == no) mid = high; else mid = -1; return mid; } /* Parse a line. High level parse function */ int line(int curline) { int answer = 0; const char *str; match(VALUE); switch(token) { case PRINT: doprint(curline); break; case LET: dolet(curline); break; case DIM: dodim(curline); break; case IF: answer = doif(curline); break; case GOTO: answer = dogoto(curline); break; case BINPUT: answer = doinput(curline); break; case REM: dorem(curline); return 0; break; case FOR: answer = dofor(curline); break; case NEXT: answer = donext(curline); break; // Poke a value into a memory location. // case POKE: answer = dopoke(curline); break; default: seterror(ERR_SYNTAX); break; } if(token != EOS) { /*match(VALUE);*/ /* check for a newline */ str = string; while(isspace(*str)) { if(*str == '\n') break; str++; } if(*str != '\n') seterror(ERR_SYNTAX); } return answer; } /* the PRINT statement */ void doprint(int curline) { char *str; double x; char output[20]; match(PRINT); while(1) { if(isstring(token)) { str = stringexpr(); if(str) { printf("%s", str); sys_free(str); } } else { x = expr(); sprintf(output, "%g", x); //printf("%g", x); fputs(output, stdout); } if(token == COMMA) { printf(" "); match(COMMA); } else break; } if(token == SEMICOLON) { match(SEMICOLON); } else printf("\r\n"); } /* the LET statement */ void dolet(int curline) { LVALUE lv; 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) sys_free(temp); break; default: break; } } /* the DIM statement */ void dodim(int curline) { int ndims = 0; double dims[6]; char name[32]; int len; DIMVAR *dimvar; int i; int size = 1; match(DIM); switch(token) { case DIMFLTID: case DIMSTRID: getid(string, name, &len); match(token); dims[ndims++] = expr(); while(token == COMMA) { match(COMMA); dims[ndims++] = expr(); if(ndims > 5) { seterror(ERR_TOOMANYDIMS); return; } } match(CPAREN); for(i=0;indims;i++) size *= dimvar->dim[i]; switch(dimvar->type) { case FLTID: i = 0; dimvar->dval[i++] = expr(); while(token == COMMA && i < size) { match(COMMA); dimvar->dval[i++] = expr(); if(errorflag) break; } break; case STRID: i = 0; if(dimvar->str[i]) sys_free(dimvar->str[i]); dimvar->str[i++] = stringexpr(); while(token == COMMA && i < size) { match(COMMA); if(dimvar->str[i]) sys_free(dimvar->str[i]); dimvar->str[i++] = stringexpr(); if(errorflag) break; } break; } if(token == COMMA) seterror(ERR_TOOMANYINITS); } } /* the IF statement. if jump taken, returns new line no, else returns 0 */ int doif(int curline) { int condition; int jump; match(IF); condition = boolexpr(); match(THEN); jump = integer( expr() ); if(condition) return jump; else return 0; } /* the GOTO satement returns new line number */ int dogoto(int curline) { match(GOTO); return integer( expr() ); } /* The FOR statement. Pushes the for stack. Returns line to jump to, or -1 to end program */ int dofor(int curline) { LVALUE lv; char id[32]; char nextid[32]; int len; int idx; double initval; double toval; double stepval; const char *savestring; int answer; match(FOR); getid(string, id, &len); lvalue(&lv); if(lv.type != FLTID) { seterror(ERR_BADTYPE); return -1; } match(EQUALS); initval = expr(); match(TO); toval = expr(); if(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 = string; for(idx=curline+1; idx < nlines; idx++) { string = lines[idx].str; errorflag = 0; token = gettoken(string); match(VALUE); if(token == NEXT) { match(NEXT); if(token == FLTID || token == DIMFLTID) { getid(string, nextid, &len); if(!strcmp(id, nextid)) { answer = getnextline(curline); string = savestring; token = gettoken(string); return answer ? answer : -1; } } } } seterror(ERR_NONEXT); return -1; } else { strcpy(forstack[nfors].id, id); forstack[nfors].nextline = getnextline(curline); forstack[nfors].step = stepval; forstack[nfors].toval = toval; nfors++; return 0; } } /* the NEXT statement updates the counting index, and returns line to jump to */ int donext(int curline) { char id[32]; int len; LVALUE lv; match(NEXT); if(nfors) { getid(string, id, &len); lvalue(&lv); if(lv.type != FLTID) { seterror(ERR_BADTYPE); return -1; } *lv.dval += forstack[nfors-1].step; if( (forstack[nfors-1].step < 0 && *lv.dval < forstack[nfors-1].toval) || (forstack[nfors-1].step > 0 && *lv.dval > forstack[nfors-1].toval) ) { nfors--; return 0; } else { return forstack[nfors-1].nextline; } } else { seterror(ERR_NOFOR); return -1; } } /* the INPUT statement */ int doinput(int curline) { LVALUE lv; char buff[1024]; char *end; match(BINPUT); lvalue(&lv); switch(lv.type) { case FLTID: do { readline((uint8_t *)buff, sizeof(buff), 0, NULL, NULL); if(buff[0] == CTRL_C) { return(-1); } *lv.dval = strtod(buff, &end); } while(end == buff); break; case STRID: if(*lv.sval) { sys_free(*lv.sval); *lv.sval = 0; } // Readline will return entered data, null terminated, upto endof limit given. readline((uint8_t *)buff, sizeof(buff), 0, NULL, NULL); if(buff[0] == CTRL_C) { return(-1); } *lv.sval = mystrdup(buff); if(!*lv.sval) { seterror(ERR_OUTOFMEMORY); return(-1); } break; default: return(0); } return(0); } /* the REM statement. Note is unique as the rest of the line is not parsed */ void dorem(int curline) { match(REM); return; } // The POKE statement. Format is POKE WIDTH, ADDR, DATA // Takes a value and *pokes* (writes) it into a memory location. // int dopoke(int curline) { uint32_t addr; uint32_t data; uint32_t width; match(POKE); width = integer( expr() ); match(COMMA); addr = integer( expr() ); match(COMMA); data = integer( expr() ); switch(width) { case 8: *(uint8_t *)(addr) = data; break; case 16: *(uint16_t *)(addr) = data; break; case 32: *(uint32_t *)(addr) = data; break; default: seterror( ERR_BADVALUE ); break; } return 0; } /* 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. */ void lvalue(LVALUE *lv) { char name[32]; int len; VARIABLE *var; DIMVAR *dimvar; int index[5]; void *valptr = 0; int type; lv->type = ERROR; lv->dval = 0; lv->sval = 0; switch(token) { case FLTID: getid(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(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 = (token == DIMFLTID) ? FLTID : STRID; getid(string, name, &len); match(token); dimvar = finddimvar(name); if(dimvar) { switch(dimvar->ndims) { case 1: index[0] = integer( expr() ); if(errorflag == 0) valptr = getdimvar(dimvar, index[0]); break; case 2: index[0] = integer( expr() ); match(COMMA); index[1] = integer( expr() ); if(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(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(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(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); } } /* parse a boolean expression consists of expressions or strings and relational operators, and parentheses */ int boolexpr(void) { int left; int right; left = boolfactor(); while(1) { switch(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; } } } /* boolean factor, consists of expression relop expression or string relop string, or ( boolexpr() ) */ int boolfactor(void) { int answer; double left; double right; int op; char *strleft; char *strright; int cmp; switch(token) { case OPAREN: match(OPAREN); answer = boolexpr(); match(CPAREN); break; default: if(isstring(token)) { strleft = stringexpr(); op = relop(); strright = stringexpr(); if(!strleft || !strright) { if(strleft) sys_free(strleft); if(strright) sys_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; } sys_free(strleft); sys_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: errorflag = 1; return 0; } } } return answer; } /* get a relational operator returns operator parsed or ERROR */ int relop(void) { switch(token) { case EQUALS: match(EQUALS); return ROP_EQ; case GREATER: match(GREATER); if(token == EQUALS) { match(EQUALS); return ROP_GTE; } return ROP_GT; case LESS: match(LESS); if(token == EQUALS) { match(EQUALS); return ROP_LTE; } else if(token == GREATER) { match(GREATER); return ROP_NEQ; } return ROP_LT; default: seterror(ERR_SYNTAX); return ERROR; } } /* parses an expression */ double expr(void) { double left; double right; left = term(); while(1) { switch(token) { case PLUS: match(PLUS); right = term(); left += right; break; case MINUS: match(MINUS); right = term(); left -= right; break; default: return left; } } } /* parses a term */ double term(void) { double left; double right; left = factor(); while(1) { switch(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; } } } /* parses a factor */ double factor(void) { double answer = 0; char *str; char *end; int len; uint32_t addr; uint32_t width; switch(token) { case OPAREN: match(OPAREN); answer = expr(); match(CPAREN); break; case VALUE: answer = getvalue(string, &len); match(VALUE); break; case MINUS: match(MINUS); answer = -factor(); break; case FLTID: answer = variable(); break; case DIMFLTID: answer = dimvariable(); break; case BE: answer = exp(1.0); match(BE); break; case BPI: answer = acos(0.0) * 2.0; match(BPI); 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); sys_free(str); } else answer = 0; break; case ASCII: match(ASCII); match(OPAREN); str = stringexpr(); match(CPAREN); if(str) { answer = *str; sys_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); sys_free(str); } else answer = 0; break; case VALLEN: match(VALLEN); match(OPAREN); str = stringexpr(); match(CPAREN); if(str) { strtod(str, &end); answer = end - str; sys_free(str); } else answer = 0.0; break; case INSTR: answer = instr(); break; // Peek a given location and return value. Format: PEEK(width, addr) // case PEEK: match(PEEK); match(OPAREN); width = integer( expr() ); match(COMMA); addr = integer( expr() ); match(CPAREN); switch(width) { case 8: answer = *(uint8_t *)(addr); break; case 16: answer = *(uint16_t *)(addr); break; case 32: answer = *(uint32_t *)(addr); break; default: seterror( ERR_BADVALUE ); break; } break; default: if(isstring(token)) seterror(ERR_TYPEMISMATCH); else seterror(ERR_SYNTAX); break; } while(token == SHRIEK) { match(SHRIEK); answer = factorial(answer); } return answer; } /* calcualte the INSTR() function. */ double instr(void) { char *str; char *substr; 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) sys_free(str); if(substr) sys_free(substr); return 0; } if(offset >= 0 && offset < (int) strlen(str)) { end = strstr(str + offset, substr); if(end) answer = end - str + 1.0; } sys_free(str); sys_free(substr); return answer; } /* get the value of a scalar variable from string matches FLTID */ double variable(void) { VARIABLE *var; char id[32]; int len; getid(string, id, &len); match(FLTID); var = findvariable(id); if(var) return var->dval; else { seterror(ERR_NOSUCHVARIABLE); return 0.0; } } /* get value of a dimensioned variable from string. matches DIMFLTID */ double dimvariable(void) { DIMVAR *dimvar; char id[32]; int len; int index[5]; double *answer; answer = NULL; getid(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; } /* find a scalar variable invariables list Params: id - id to get Returns: pointer to that entry, 0 on fail */ VARIABLE *findvariable(const char *id) { int i; for(i=0;i 5) return 0; dv = finddimvar(id); if(!dv) dv = adddimvar(id); if(!dv) { seterror(ERR_OUTOFMEMORY); return 0; } if(dv->ndims) { for(i=0;indims;i++) oldsize *= dv->dim[i]; } else oldsize = 0; va_start(vargs, ndims); for(i=0;itype) { case FLTID: dtemp = sys_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;istr[i]) { sys_free(dv->str[i]); dv->str[i] = 0; } } stemp = sys_realloc(dv->str, size * sizeof(char *)); if(stemp) { dv->str = stemp; for(i=oldsize;istr[i] = 0; } else { for(i=0;istr[i]) { sys_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; } /* 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 */ void *getdimvar(DIMVAR *dv, ...) { va_list vargs; int index[5]; int i; void *answer = 0; va_start(vargs, dv); for(i=0;indims;i++) { index[i] = va_arg(vargs, int); index[i]--; } va_end(vargs); for(i=0;indims;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] ]; 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] ]; 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; } /* add a real varaible to our variable list Params: id - id of varaible to add. Returns: pointer to new entry in table */ VARIABLE *addfloat(const char *id) { VARIABLE *vars; vars = sys_realloc(variables, (nvariables + 1) * sizeof(VARIABLE)); if(vars) { variables = vars; strcpy(variables[nvariables].id, id); variables[nvariables].dval = 0; variables[nvariables].sval = 0; nvariables++; return &variables[nvariables-1]; } else seterror(ERR_OUTOFMEMORY); return 0; } /* add a string variable to table. Params: id - id of variable to get (including trailing $) Retruns: pointer to new entry in table, 0 on fail. */ VARIABLE *addstring(const char *id) { VARIABLE *vars; vars = sys_realloc(variables, (nvariables + 1) * sizeof(VARIABLE)); if(vars) { variables = vars; strcpy(variables[nvariables].id, id); variables[nvariables].sval = 0; variables[nvariables].dval = 0; nvariables++; return &variables[nvariables-1]; } else seterror(ERR_OUTOFMEMORY); return 0; } /* add a new array to our symbol table. Params: id - id of array (include leading () Returns: pointer to new entry, 0 on fail. */ DIMVAR *adddimvar(const char *id) { DIMVAR *vars; vars = sys_realloc(dimvariables, (ndimvariables + 1) * sizeof(DIMVAR)); if(vars) { dimvariables = vars; strcpy(dimvariables[ndimvariables].id, id); dimvariables[ndimvariables].dval = 0; dimvariables[ndimvariables].str = 0; dimvariables[ndimvariables].ndims = 0; dimvariables[ndimvariables].type = strchr(id, '$') ? STRID : FLTID; ndimvariables++; return &dimvariables[ndimvariables-1]; } else seterror(ERR_OUTOFMEMORY); return 0; } /* high level string parsing function. Returns: a malloced pointer, or 0 on error condition. caller must free! */ char *stringexpr(void) { char *left; char *right; char *temp; switch(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(token)) seterror(ERR_TYPEMISMATCH); else seterror(ERR_SYNTAX); return mystrdup(""); } if(!left) { seterror(ERR_OUTOFMEMORY); return 0; } switch(token) { case PLUS: match(PLUS); right = stringexpr(); if(right) { temp = mystrconcat(left, right); sys_free(right); if(temp) { sys_free(left); left = temp; } else seterror(ERR_OUTOFMEMORY); } else seterror(ERR_OUTOFMEMORY); break; default: return left; } return left; } /* parse the CHR$ token */ char *chrstring(void) { double x; char buff[6]; 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; } /* parse the STR$ token */ char *strstring(void) { double x; char buff[64]; char *answer; match(STRSTRING); match(OPAREN); x = expr(); match(CPAREN); sprintf(buff, "%g", x); answer = mystrdup(buff); if(!answer) seterror(ERR_OUTOFMEMORY); return answer; } /* parse the LEFT$ token */ char *leftstring(void) { char *str; int x; 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); sys_free(str); if(!answer) seterror(ERR_OUTOFMEMORY); return answer; } /* parse the RIGHT$ token */ char *rightstring(void) { int x; char *str; 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] ); sys_free(str); if(!answer) seterror(ERR_OUTOFMEMORY); return answer; } /* parse the MID$ token */ char *midstring(void) { char *str; int x; int len; char *answer; 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) { sys_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 = sys_malloc(len + 1); if(!answer) { seterror(ERR_OUTOFMEMORY); return str; } strncpy(answer, temp, len); answer[len] = 0; sys_free(str); return answer; } /* parse the string$ token */ char *stringstring(void) { int x; char *str; 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) { sys_free(str); answer = mystrdup(""); if(!answer) seterror(ERR_OUTOFMEMORY); return answer; } len = strlen(str); answer = sys_malloc( N * len + 1 ); if(!answer) { sys_free(str); seterror(ERR_OUTOFMEMORY); return 0; } for(i=0; i < N; i++) { strcpy(answer + len * i, str); } sys_free(str); return answer; } /* read a dimensioned string variable from input. Returns: pointer to string (not malloced) */ char *stringdimvar(void) { char id[32]; int len; DIMVAR *dimvar; char **answer; int index[5]; answer = NULL; getid(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(!errorflag) if(*answer) return *answer; return ""; } /* parse a string variable. Returns: pointer to string (not malloced) */ char *stringvar(void) { char id[32]; int len; VARIABLE *var; getid(string, id, &len); match(STRID); var = findvariable(id); if(var) { if(var->sval) return var->sval; return ""; } seterror(ERR_NOSUCHVARIABLE); return ""; } /* parse a string literal Returns: malloced string literal Notes: newlines aren't allwed in literals, but blind concatenation across newlines is. */ char *stringliteral(void) { int len = 1; char *answer = 0; char *temp; char *substr; char *end; while(token == QUOTE) { while(isspace(*string)) string++; end = mystrend(string, '"'); if(end) { len = end - string; substr = sys_malloc(len); if(!substr) { seterror(ERR_OUTOFMEMORY); return answer; } mystrgrablit(substr, string); if(answer) { temp = mystrconcat(answer, substr); sys_free(substr); sys_free(answer); answer = temp; if(!answer) { seterror(ERR_OUTOFMEMORY); return answer; } } else answer = substr; string = end; } else { seterror(ERR_SYNTAX); return answer; } match(QUOTE); } return answer; } /* cast a double to an integer, triggering errors if out of range */ int integer(double x) { #if defined(__ZPU__) #define INT_MIN -2147483648L #define INT_MAX 2147483647L #endif if( x < INT_MIN || x > INT_MAX ) seterror( ERR_BADVALUE ); if( x != floor(x) ) seterror( ERR_NOTINT ); return (int) x; } /* check that we have a token of the passed type (if not set the errorflag) Move parser on to next token. Sets token and string. */ void match(int tok) { if(token != tok) { seterror(ERR_SYNTAX); return; } while(isspace(*string)) string++; string += tokenlen(string, token); token = gettoken(string); if(token == ERROR) seterror(ERR_SYNTAX); } /* set the errorflag. Params: errorcode - the error. Notes: ignores error cascades */ void seterror(int errorcode) { if(errorflag == 0 || errorcode == 0) errorflag = errorcode; } /* get the next line number Params: curline - line being processed. Returns: line no of next line, 0 if end Notes: goes to next line then finds first line starting with a digit. */ int getnextline(int curline) { int idx; const char *str; for(idx=curline+1; idx < nlines; idx++) { str = lines[idx].str; while(*str && *str == ' ') str++; if(*str == 0) return 0; if(isdigit(*str)) return atoi(str); } return 0; } /* get a token from the string Params: str - string to read token from Notes: ignores white space between tokens */ int gettoken(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 BE; 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 BPI; 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 BINPUT; 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, "POKE", 4) && !isalnum(str[4])) return POKE; 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, "PEEK", 4) && !isalnum(str[4])) return PEEK; 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; } } /* 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. */ int tokenlen(const char *str, int token) { int len = 0; char buff[32]; switch(token) { 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 BPI: return 2; case BE: 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 BINPUT: return 5; case REM: return 3; case FOR: return 3; case TO: return 2; case NEXT: return 4; case STEP: return 4; case POKE: 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 PEEK: return 4; 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; } } /* test if a token represents a string expression Params: token - token to test Returns: 1 if a string, else 0 */ int isstring(int token) { if(token == STRID || token == QUOTE || token == DIMSTRID || token == CHRSTRING || token == STRSTRING || token == LEFTSTRING || token == RIGHTSTRING || token == MIDSTRING || token == STRINGSTRING) return 1; return 0; } /* 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. */ double getvalue(const char *str, int *len) { double answer; char *end; answer = strtod(str, &end); assert(end != str); *len = end - str; return answer; } /* getid - 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. */ void getid(const char *str, char *out, 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; } /* grab a literal from the parse string. Params: dest - destination string src - source string Notes: strings are in quotes, double quotes the escape */ void mystrgrablit(char *dest, const char *src) { assert(*src == '"'); src++; while(*src) { if(*src == '"') { if(src[1] == '"') { *dest++ = *src; src++; src++; } else break; } else *dest++ = *src++; } *dest++ = 0; } /* 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 */ char *mystrend(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); } /* Count the instances of ch in str Params: str - string to check ch - character to count Returns: no time chs occurs in str. */ int mystrcount(const char *str, char ch) { int answer = 0; while(*str) { if(*str++ == ch) answer++; } return answer; } /* duplicate a string: Params: str - string to duplicate Returns: malloced duplicate. */ char *mystrdup(const char *str) { char *answer; answer = sys_malloc(strlen(str) + 1); if(answer) strcpy(answer, str); return answer; } /* concatenate two strings Params: str - firsts string cat - second string Returns: malloced string. */ char *mystrconcat(const char *str, const char *cat) { int len; char *answer; len = strlen(str) + strlen(cat); answer = sys_malloc(len + 1); if(answer) { strcpy(answer, str); strcat(answer, cat); } return answer; } /* compute x! */ 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; }