// TinyBASIC.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include
#include
#include
#include
#include
#define NUM_LAB100
#define LAB_LEN10
#define FOR_NEST25
#define SUB_NEST25
#define PROG_SIZE10000
#define DELIMITER1
#define VARIABLE2
#define NUMBER3
#define COMMAND4
#define STRING5
#define QUOTE6
#define PRINT1
#define INPUT2
#define IF3
#define THEN4
#define FOR5
#define NEXT6
#define TO7
#define GOTO8
#define EOL9
#define FINISHED10
#define GOSUB11
#define RETURN12
#define END13
// Holds expressions to be analyzed.
char* prog;
// Holds environment for longjmp().
jmp_buf e_buf;
// 26 user variables,A-Z.
int variables[ 26 ] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
// Keywords lookup table.Commands must be entered lowercase in this table.
struct commands {
char command[ 20 ];
char tok;
} table[] = {
{ "print", PRINT },
{ "input", INPUT },
{ "if",IF },
{ "then", THEN },
{ "goto", GOTO },
{ "for", FOR},
{ "next", NEXT },
{ "to",TO },
{ "gosub", GOSUB },
{ "return",RETURN },
{ "end", END},
{ "", END}
};
char token[ 80 ];
char token_type;
char tok;
struct label {
char name[ LAB_LEN ];
char* p;// Points to place to go in source file.
} label_table[ NUM_LAB ];
char* find_label();
char* gpop();
// Stack for FOR-NEXT loop.
struct for_stack {
int var;// Counter variable.
int target;// Target value.
char* loc;
} fstack[ FOR_NEST ];
struct for_stack fpop();
// Stack for GOSUB.
char* gstack[ SUB_NEST ];
// Index to top of FOR stack.
int ftos;
// Index to top of GOSUB stack.
int gtos;
void print();
void scan_labels();
void find_eol();
void exec_goto();
void exec_if();
void exec_for();
void exec_next();
void input();
void gosub();
void greturn();
int load_program( char* p,char* fname );
void label_init();
int get_next_label( char* s );
void fpush( struct for_stack i );
void gpush( char* s );
int look_up( char* s );
void get_exp( int* result );
//void level1( int* result );
void assignment();
void level2( int* result );
void level3( int* result );
void level4( int* result );
void level5( int* result );
void level6( int* result );
void primitive( int* result );
void arith( char o,int* r,int* h );
void unary( char o,int* r );
void putback();
void serror( int error );
void get_token();
int iswhite( char c );
int isdelim( char c );
int find_var( char* s );
int _tmain(int argc, _TCHAR* argv[])
{
char* p_buf;
if (argc != 2)
{
printf("Usage: TinyBasic \r\n");
exit(1);
}
// Allocate memory for the program.
if (!(p_buf = (char*)malloc(PROG_SIZE)))
{
printf("Allocate memory failure!\r\n");
exit(1);
}
// Load the program to execute.
if (!load_program(p_buf,argv[1]))
{
free(p_buf);
printf("Can not load the program %s to execute!\r\n",argv[1]);
exit(1);
}
// Initialize the long jump buffer.
if (setjmp(e_buf))
{
free(p_buf);
printf("Can not set long jump!\r\n");
exit(1);
}
prog = p_buf;
scan_labels();
ftos = 0;
gtos = 0;
do
{
//token_type = get_token();
get_token();
// Check for assignment statement.
if(token_type == VARIABLE)
{
putback();// Return the variable to the input stream.
assignment();// Must be assignment statement.
}
// Is command.
else
{
switch(tok)
{
case PRINT:
print();
break;
case GOTO:
exec_goto();
break;
case IF:
exec_if();
break;
case FOR:
exec_for();
break;
case NEXT:
exec_next();
break;
case INPUT:
input();
break;
case GOSUB:
gosub();
break;
case RETURN:
greturn();
break;
case END:
exit(0);
}
}
} while(tok != FINISHED);
free(p_buf);
return 0;
}
int load_program(char* p,char* fname)
{
FILE* fp;
int i;
if(!(fp = fopen(fname,"rb")))
return 0;
i = 0;
do
{
*p = getc(fp);
p++;
i++;
} while(!feof(fp) && i < PROG_SIZE);
*(p - 2) = '\0';// Null terminate the program.
fclose(fp);
return 1;
}
void assignment()
{
int var;
int value;
// Get the variable name.
get_token();
if(!isalpha(*token))
{
serror(4);
return;
}
var = toupper(*token) - 'A';
if(*token != '=')
{
serror(3);
return;
}
// Get the value to assign to variable.
get_exp(&value);
variables[var] = value;
}
// Execute a simple version of the BASIC "PRINT" statement.
void print()
{
int answer;
int len = 0;
int spaces;
char last_delim;
do
{
get_token();// Get next list item.
if ( tok == EOL || tok == FINISHED )
break;
if (token_type == QUOTE)
{// Is String.
printf(token);
len += (int)strlen( token );
get_token();
}
else
{// Is expression.
putback();
get_exp(&answer);
get_token();
len += printf("%d",answer);
}
last_delim = *token;
if (*token == ';')
{
// Compute number of sapces to move to next TAB.
spaces = 8 - (len % 8);
// Add in the tabbing position.
len += spaces;
while(spaces)
{
printf(" ");
spaces --;
}
}
else if(*token == ',')
{
// Do nothing.
}
else if(tok != EOL && tok != FINISHED)
{
serror(0);
}
} while(*token == ';' || *token == ',');
if(tok == EOL || tok == FINISHED)
{
if(last_delim != ';' && last_delim != ',')
printf("\n");
}
else
{
serror(0);
}
}
// Find all labels.
void scan_labels()
{
int addr;
char* temp;
// Zero all labels.
label_init();
// Save pointer to top of program.
temp = prog;
// If the first token in the file is label.
get_token();
if(token_type == NUMBER)
{
strcpy(label_table[0].name,token);
label_table[0].p = prog;
}
find_eol();
do
{
get_token();
if(token_type == NUMBER)
{
addr = get_next_label(token);
if(addr == -1 || addr == -2)
{
addr == -1 ? serror(5) : serror(6);
}
strcpy(label_table[addr].name,token);
label_table[addr].p = prog;
}
// If not on a blank line,find next line.
if(tok != EOL)
find_eol();
} while(tok != FINISHED);
prog = temp;
}
// Find the start of the next line.
void find_eol()
{
while(*prog != '\n' && *prog != '\0')
++prog;
if(*prog)
prog++;
}
// Return index of next free position in label arrary.
// -1: is returned if the array is full;
// -2: is returned when duplicate label is found.
int get_next_label(char* s)
{
int t;
for(t = 0; t < NUM_LAB; ++t)
{
if(label_table[t].name[0] == 0)
return t;
if(!strcmp(label_table[t].name,s))
return -2;
}
return -1;
}
// Find location of given label.A null is returned if label is not found;otherwise a pointer to
// the position of label is returned.
char* find_label(char* s)
{
int t;
for(t = 0; t < NUM_LAB; ++t)
{
if(!strcmp(label_table[t].name,s))
return label_table[t].p;
}
return 0;
}
// Execute a GOTO command.
void exec_goto()
{
char* loc;
// Get label to goto.
get_token();
// Find the location of label.
loc = find_label(token);
if(loc == '\0')
serror(7);// Label not define.
else
prog = loc;// Start program running at that loc.
}
// Initilize the array that holds the labels.
// By convention,a null label name indicates that array position is unused.
void label_init()
{
int t;
for(t = 0; t < NUM_LAB; ++t)
label_table[t].name[0] = '\0';
}
// Execute an IF statement.
void exec_if()
{
int x;
int y;
int cond;
char op;
get_exp(&x);// Get left expression.
get_token();// Get the operator.
if(!strchr("=<>",*token))
{
serror(0);
return;
}
op = *token;
get_exp(&y);// Get the right expression.
// Determine the outcome.
cond = 0;
switch( op )
{
case '<':
if(x < y)
cond = 1;
break;
case '>':
if(x > y)
cond = 1;
break;
case '=':
if(x == y)
cond = 1;
break;
}
if(cond)
{// Is true so process target of IF.
get_token();
if(tok != THEN)
{
serror(8);
return;
}// else program execution starts on next line.
}
else
find_eol();// Find start of next line.
}
// Execute a FOR loop.
void exec_for()
{
struct for_stack i;
int value;
get_token();// Read the control variable.
if(!isalpha(*token))
{
serror(4);
return;
}
i.var = toupper(*token) - 'A';// Save its index.
get_token();// Read the equals sign.
if(*token != '=')
{
serror(3);
return;
}
get_exp(&value);// Get initial value.
variables[i.var] = value;
get_token();
if(tok != TO)
serror(9);// Read and discard the TO.
get_exp(&i.target);// Get target value.
// If loop can execute at least once,push info on stack.
if(value >= variables[i.var])
{
i.loc = prog;
fpush(i);
}
else// Otherwise,skip loop code altogether.
{
while(tok != NEXT)
get_token();
}
}
// Execute a NEXT statement.
void exec_next()
{
struct for_stack i;
i = fpop();// Read the loop info.
variables[i.var]++;// Increment control variable.
if(variables[i.var] > i.target)
return;// All down.
fpush(i);
prog = i.loc;// Loop.
}
// Push function for the FOR stack.
void fpush(struct for_stack i)
{
if(ftos > FOR_NEST)
serror( 0 );
fstack[ftos] = i;
ftos++;
}
// Pop function for the FOR stack.
struct for_stack fpop()
{
ftos--;
if(ftos < 0)
serror(11);
return(fstack[ftos]);
}
// Execute a simple form of the BASIC INPUT command.
void input()
{
char var;
int i;
get_token();// See if prompt string is present.
if(token_type == QUOTE)
{
printf(token);
// If so,print it and check for comma.
get_token();
if(*token != ',')
serror(1);
get_token();
}
else printf("?");// Otherwise,prompt with "?".
var = toupper(*token) - 'A';// Get the input variable.
scanf("%d", &i);// Read input.
variables[var] = i;// Store it.
}
// Execute a GOSUB statement.
void gosub()
{
char* loc;
get_token();
// Find the label to call.
loc = find_label(token);
if(loc == '\0')
{
serror( 7 );
}
else
{
gpush( prog );// Save place to return to.
prog = loc;// Start program running at that loc.
}
}
// Return from GOSUB.
void greturn()
{
prog = gpop();
}
// GOSUB stack push fuction.
void gpush(char* s)
{
gtos++;
if (gtos == SUB_NEST)
{
serror(12);
return;
}
gstack[gtos] = s;
}
// GOSUB stack pop function.
char* gpop()
{
if(gtos == 0)
{
serror(13);
return 0;
}
return gstack[gtos--];
}
void get_exp(int* result)
{
get_token();
if(!(*token))
{
serror(2);
return;
}
level2(result);
putback();// Retuen last token read to input string.
}
void level2(int* result)
{
char op;
int hold;
level3(result);
while ((op = *token) == '+' || op == '-')
{
get_token();
level3(&hold);
arith(op,result,&hold);
}
}
void level3(int* result)
{
char op;
int hold;
level4(result);
while ((op = *token) == '*' || op == '/' || op == '%')
{
get_token();
level4(&hold);
arith(op,result,&hold);
}
}
void level4(int* result)
{
int hold;
level5(result);
if (*token == '^')
{
get_token();
level4(&hold);
arith('^',result, &hold);
}
}
void level5(int* result)
{
char op;
op = 0;
if ((token_type == DELIMITER) && *token == '+' || *token == '-')
{
op = *token;
get_token();
}
level6(result);
if(op)
{
unary(op, result);
}
}
void level6(int* result)
{
if ((*token == '(') && (token_type == DELIMITER))
{
get_token();
level2(result);
if (*token != ')')
{
serror(1);
return;
}
get_token();
}
else
{
primitive(result);
}
}
void primitive(int* result)
{
switch(token_type)
{
case VARIABLE:
*result = find_var(token);
get_token();
return;
case NUMBER:
*result = atoi(token);
get_token();
return;
default:
serror( 0 );
}
}
void arith(char o,int* r,int* h)
{
int t;
int ex;
switch(o)
{
case '-':
*r = *r - *h;
break;
case '+':
*r = *r + *h;
break;
case '*':
*r = (*r) * (*h);
break;
case '/':
*r = (*r) / (*h);
break;
case '%':
t = (*r) / (*h);
*r = *r - (t * (*h));
break;
case '^':
ex = *r;
if (*h == 0)
{
*r = 1;
}
else
{
for(t = *h - 1; t > 0; --t)
{
*r = (*r) * ex;
}
}
break;
}
}
void unary(char o, int* r)
{
if(o == '-')
*r = -(*r);
}
void putback()
{
char* t;
t = token;
for(; *t; t++)
prog--;
}
void serror(int error)
{
static char* e[] =
{
"Error: Syntax error!\r\n",
"Error: unbalanced parentheses!\r\n",
"Error: No expression present!\r\n",
"Error: equals sign expected!\r\n",
"Error: not a variable!\r\n",
"Error: label table full!\r\n",
"Error: duplicate label!\r\n",
"Error: undefined label!\r\n",
"Error: THEN expected!\r\n",
"Error: TO expected!\r\n",
"Error: too many nested FOR loops\r\n",
"Error: NEXT without FOR!\r\n",
"Error: too many nested GOSUB\r\n",
"Error: RETURN without GOSUB!\r\n"
};
printf("%s", e[error]);
longjmp(e_buf, 1);// Return to save point.
}
void get_token()
{
char* temp;
token_type = 0;
tok = 0;
temp = token;
if (*prog == '\0')
{
*token = 0;
tok = FINISHED;
token_type = DELIMITER;
return;
}
// Skip over white space.
while (iswhite(*prog))
++prog;
if (*prog == '\r')
{// CrLf.
++prog;
++prog;
tok = EOL;
token[0] = '\r';
token[1] = '\n';
token[2] = '\0';
token_type = DELIMITER;
return;
}
if (strchr("+-*^/%=;(),><", *prog))
{// Delimiter.
*temp = *prog;
prog++;// Advance to next position.
temp++;
*temp = 0;
token_type = DELIMITER;
return;
}
if (*prog == '"')
{// Quoted string.
prog++;
while (*prog != '"' && *prog != '\r')
*temp++ = *prog++;
if (*prog == '\r')
serror(1);
prog++;
*temp = 0;
token_type = QUOTE;
return;
}
if (isdigit(*prog))
{// Number.
while(!isdelim(*prog))
*temp++ = *prog++;
*temp = 0;
token_type = NUMBER;
return;
}
if (isalpha(*prog))
{// Variable or command.
while (!isdelim(*prog))
*temp++ = *prog++;
token_type = STRING;
}
*temp = 0;
// See if a string is a command or a variable.
if (token_type == STRING)
{
tok = look_up(token);
if(!tok)
token_type = VARIABLE;
else
token_type = COMMAND;
}
}
// Look up a token's internal representation in the token table.
int look_up(char* s)
{
int i;
char* p;
p = s;
while (*p)
{
*p = tolower(*p);
p++;
}
for (i = 0; *table[ i ].command; i++)
{
if (!strcmp(table.command, s))
return table.tok;
}
return 0;
}
int iswhite(char c)
{
if (c == ' ' || c == 0x9)
return 1;
return 0;
}
int isdelim(char c)
{
if (strchr(";,+-< >/*%^=()", c) || c == 0x9 || c == '\r' || c == 0)
return 1;
return 0;
}
int find_var(char* s)
{
if (!isalpha(*s))
{
serror(4);
return 0;
}
return variables[toupper(*token) - 'A'];
}