/* basic_proj1_part2.c */ /* interpret BASIC program */ /* for each line, ensure line numbers and valid instruction: */ /* LET, INPUT, PRINT, GOTO, GOSUB, RETURN, IF */ /* assume line numbers in order */ #include char nextChar; /* next character read from input */ int charClass; /* character classes: */ #define LETTER 0 /* [a-zA-Z] */ #define DIGIT 1 /* [0-9] */ #define EQUALS_SIGN 2 #define GREATER_THAN_SIGN 3 #define LESS_THAN_SIGN 4 #define PLUS_SIGN 5 #define DOUBLE_QUOTE 6 #define DOLLAR_SIGN 7 #define NEWLINE 8 #define UNKNOWN 9 /* also EOF from stdio.h */ /* tokens: */ #define INTEGER_LITERAL 1 /* including line number */ #define EQUALS_OPERATOR 2 #define GREATER_THAN_OPERATOR 3 #define LESS_THAN_OPERATOR 4 #define PLUS_OPERATOR 5 #define LET 6 #define PRINT 7 #define INPUT 8 #define REMARK 9 #define IF 10 #define THEN 11 #define GOTO 12 #define GOSUB 13 #define RETURN 14 #define IDENTIFIER 15 #define QUOTED_STRING 16 #define END 17 #define EOLN 18 #define UNKNOWN_TOKEN 19 #define MAXLENGTH 80 char lexeme[MAXLENGTH]; int lexemeLength; #define MAXLINES 100 FILE *fp; /* getChar() function */ void getChar() { /* get next character from file */ nextChar = getc(fp); /* determine the character class */ if (nextChar == EOF) charClass = EOF; else if (nextChar == '\n') charClass = NEWLINE; else if (nextChar == '=') charClass = EQUALS_SIGN; else if (nextChar == '>') charClass = GREATER_THAN_SIGN; else if (nextChar == '<') charClass = LESS_THAN_SIGN; else if (nextChar == '+') charClass = PLUS_SIGN; else if (nextChar == '"') charClass = DOUBLE_QUOTE; else if (nextChar == '$') charClass = DOLLAR_SIGN; else if (isalpha(nextChar)) charClass = LETTER; else if (isdigit(nextChar)) charClass = DIGIT; else charClass = UNKNOWN; } /* addChar() function */ void addChar() { if (lexemeLength < MAXLENGTH) { lexeme[lexemeLength++] = nextChar; lexeme[lexemeLength] = '\0'; } else fprintf(stderr, "ERROR: Lexeme is too long\n"); } /* lookup() function */ int lookup(char *lexeme) { if ( strcmp( lexeme, "IF" ) == 0 ) return IF; else if ( strcmp( lexeme, "THEN" ) == 0 ) return THEN; else if ( strcmp( lexeme, "LET" ) == 0 ) return LET; else if ( strcmp( lexeme, "PRINT" ) == 0 ) return PRINT; else if ( strcmp( lexeme, "INPUT" ) == 0 ) return INPUT; else if ( strcmp( lexeme, "REM" ) == 0 ) return REMARK; else if ( strcmp( lexeme, "GOTO" ) == 0 ) return GOTO; else if ( strcmp( lexeme, "GOSUB" ) == 0 ) return GOSUB; else if ( strcmp( lexeme, "RETURN" ) == 0 ) return RETURN; else if ( strcmp( lexeme, "END" ) == 0 ) return END; else return IDENTIFIER; } int lex() { int token; lexemeLength = 0; static int first = 1; if (first) { getChar(); first = 0; } /* skip whitespace */ while (nextChar != '\n' && isspace(nextChar)) getChar(); switch (charClass) { case NEWLINE: addChar(); getChar(); return EOLN; case LETTER: addChar(); getChar(); while ( (token = lookup(lexeme)) == IDENTIFIER && (charClass == LETTER || charClass == DIGIT) ) { addChar(); getChar(); } if ( token == IDENTIFIER && charClass == DOLLAR_SIGN ) { addChar(); getChar(); } if ( token == REMARK ) { while ( charClass != NEWLINE && charClass != EOF ) { addChar(); getChar(); } } return token; case DIGIT: addChar(); getChar(); while (charClass == DIGIT) { addChar(); getChar(); } return INTEGER_LITERAL; case EQUALS_SIGN: addChar(); getChar(); return EQUALS_OPERATOR; case GREATER_THAN_SIGN: addChar(); getChar(); return GREATER_THAN_OPERATOR; case LESS_THAN_SIGN: addChar(); getChar(); return LESS_THAN_OPERATOR; case PLUS_SIGN: addChar(); getChar(); return PLUS_OPERATOR; case DOUBLE_QUOTE: addChar(); getChar(); while ( charClass != DOUBLE_QUOTE && charClass != NEWLINE && charClass != EOF ) { addChar(); getChar(); } nextChar = '"'; addChar(); getChar(); return QUOTED_STRING; case EOF: return EOF; case UNKNOWN: default: /* including DOLLAR_SIGN */ addChar(); getChar(); return UNKNOWN_TOKEN; } } struct basicToken { int token; char *text; struct basicToken *nextToken; }; struct basicLine { int lineNumber; char line[MAXLENGTH]; struct basicToken *tokens; }; struct basicVariable { char *name; void *value; }; #define MAXSYMBOLS 10 struct basicVariable symbols[MAXSYMBOLS]; int getSymbol(char *name) { int x; for (x = 0 ; x < MAXSYMBOLS && symbols[x].name != NULL ; x++) if (strcmp(symbols[x].name, name) == 0) return x; return -1; } void addOrUpdateSymbol(char *name, void *value) { int x = getSymbol(name); if (x == -1) { x = 0; while (x < MAXSYMBOLS && symbols[x].name != NULL) x++; symbols[x].name = (char *)malloc(strlen(name) + 1); strcpy(symbols[x].name, name); } if (name[strlen(name)-1] == '$') { symbols[x].value = (void *)malloc(strlen(value) + 1); strcpy(symbols[x].value, value); } else { symbols[x].value = (void *)malloc(sizeof(int)); *(int *)(symbols[x].value) = *(int *)value; } } main(int argc, char *argv[]) { int x; struct basicLine lines[MAXLINES]; int nextLinesIndex = 0; int firstToken = 1; struct basicToken *tokens = NULL; struct basicToken *endToken = NULL; int lineNumber = -1; int lastLineNumber = -1; char line[MAXLENGTH]; line[0] = '\0'; fp = fopen(argv[1], "r"); if ( fp == NULL ) { fprintf(stderr, "Unable to open input file.\n"); exit(1); } do { x = lex(); if (x != EOF && firstToken) { firstToken = 0; if (x == INTEGER_LITERAL) { lineNumber = atoi(lexeme); } else { fprintf(stderr, "*** NO LINE NUMBER\n"); } } if (x == EOLN || x == EOF) { if (lineNumber != -1 && lineNumber < lastLineNumber) { int y, z = 0; fprintf(stderr, "*** WARNING: LINE %d IS NOT IN THE PROPER ORDER\n", lineNumber); while (lines[z].lineNumber < lineNumber) z++; for ( y = nextLinesIndex ; y >= z ; y-- ) { lines[y].lineNumber = lines[y-1].lineNumber; strcpy(lines[y].line, lines[y-1].line); lines[y].tokens = lines[y-1].tokens; } nextLinesIndex++; lines[z].lineNumber = lineNumber; strcpy(lines[z].line, line); lines[z].tokens = tokens; } else { lines[nextLinesIndex].lineNumber = lineNumber; strcpy(lines[nextLinesIndex].line, line); lines[nextLinesIndex++].tokens = tokens; lastLineNumber = lineNumber; } lineNumber = -1; line[0] = '\0'; firstToken = 1; tokens = NULL; endToken = NULL; } else { struct basicToken *newToken = (struct basicToken *)malloc(sizeof(struct basicToken)); if (tokens == NULL) { tokens = newToken; } else { endToken->nextToken = newToken; } newToken->token = x; newToken->text = (char *)malloc(strlen(lexeme) + 1); strcpy(newToken->text, lexeme); newToken->nextToken = NULL; endToken = newToken; sprintf(line + strlen(line), "%s ", lexeme); } } while (x != EOF); printf("INPUT PROGRAM ==>\n"); for (x = 0 ; x < nextLinesIndex ; x++) { printf("%s\n", lines[x].line); } for (x = 0 ; x < MAXSYMBOLS ; x++) { symbols[x].name = NULL; symbols[x].value = NULL; } printf("STARTING EXECUTION...\n"); for (x = 0 ; x < nextLinesIndex ; x++) { lineNumber = lines[x].lineNumber; int token = -1; struct basicToken *t = lines[x].tokens; if (t == NULL) continue; if (t->nextToken == NULL) continue; t = t->nextToken; /* skip line number */ token = t->token; switch (token) { case REMARK: break; case LET: t = t->nextToken; if (t == NULL) fprintf(stderr, "*** SYNTAX ERROR AT LINE %d\n", lineNumber); else if (t->token != IDENTIFIER) fprintf(stderr, "*** SYNTAX ERROR AT LINE %d\n", lineNumber); else { int v; char ident[5]; strcpy(ident, t->text); t = t->nextToken; if (t == NULL || t->token != EQUALS_OPERATOR) fprintf(stderr, "*** SYNTAX ERROR AT LINE %d\n", lineNumber); else { t = t->nextToken; if (t == NULL) fprintf(stderr, "*** SYNTAX ERROR AT LINE %d\n", lineNumber); else { int isInt = ident[strlen(ident)-1] != '$'; if (isInt && t->token == INTEGER_LITERAL) { v = atoi(t->text); addOrUpdateSymbol(ident, &v); } else if (isInt && t->token == IDENTIFIER) { int j = getSymbol(t->text); if (j == -1) fprintf(stderr, "*** SYNTAX ERROR AT LINE %d\n", lineNumber); else { v = *(int *)(symbols[j].value); addOrUpdateSymbol(ident, &v); } } else if (!isInt && t->token == QUOTED_STRING) { char value[strlen(t->text)]; strcpy(value, t->text + 1); value[strlen(value) - 1] = '\0'; addOrUpdateSymbol(ident, value); } /* look for PLUS_OPERATOR */ t = t->nextToken; if (t != NULL) if (t->token != PLUS_OPERATOR) fprintf(stderr, "*** SYNTAX ERROR AT LINE %d\n", lineNumber); else { t = t->nextToken; if (t == NULL) fprintf(stderr, "*** SYNTAX ERROR AT LINE %d\n", lineNumber) ; else { if (t->token == INTEGER_LITERAL) { v += atoi(t->text); addOrUpdateSymbol(ident, &v); } else if (t->token == IDENTIFIER) { int j = getSymbol(t->text); if (j == -1) fprintf(stderr, "*** SYNTAX ERROR AT LINE %d\n", lineNum ber); else { v += *(int *)(symbols[j].value); addOrUpdateSymbol(ident, &v); } } } } } } } break; case PRINT: t = t->nextToken; if (t == NULL) fprintf(stderr, "*** SYNTAX ERROR AT LINE %d\n", lineNumber); else if (t->token == IDENTIFIER) { int j = getSymbol(t->text); if (j == -1) fprintf(stderr, "*** SYNTAX ERROR AT LINE %d\n", lineNumber); else { if (t->text[strlen(t->text)-1] == '$') printf("%s\n", (char *)(symbols[j].value)); else printf("%d\n", *(int *)(symbols[j].value)); } } else if (t->token == INTEGER_LITERAL) { printf("%s\n", t->text); } else if (t->token == QUOTED_STRING) { char value[strlen(t->text)]; strcpy(value, t->text + 1); value[strlen(value) - 1] = '\0'; printf("%s\n", value); } else fprintf(stderr, "*** SYNTAX ERROR AT LINE %d\n", lineNumber); break; case INPUT: t = t->nextToken; if (t == NULL) fprintf(stderr, "*** SYNTAX ERROR AT LINE %d\n", lineNumber); else if (t->token == IDENTIFIER) { char value[100]; scanf("%s", value); if (t->text[strlen(t->text)-1] == '$') { addOrUpdateSymbol(t->text, value); } else { int i = atoi(value); addOrUpdateSymbol(t->text, &i); } } else fprintf(stderr, "*** SYNTAX ERROR AT LINE %d\n", lineNumber); break; case IF: break; case GOTO: break; case GOSUB: break; case RETURN: break; case END: exit(0); default: fprintf(stderr, "*** SYNTAX ERROR ON LINE %d (SKIPPED)\n", lineNumber); } } }