511 lines
11 KiB
C
511 lines
11 KiB
C
#include "mystdlib.h"
|
|
#include "basic_tokens.h"
|
|
#include "basic_expr.h"
|
|
#include "basic_utils.h"
|
|
#include "basic_extern.h"
|
|
#include "basic_textual.h"
|
|
|
|
char* cur;
|
|
token* curTok;
|
|
token* prevTok;
|
|
char* parseError;
|
|
char parseErrorCode;
|
|
|
|
static short cmdCodeByHash(numeric h) {
|
|
// replaced array with switch to save RAM
|
|
switch (h) {
|
|
case 0x018F: // REM
|
|
return CMD_REM;
|
|
case 0x067C: // PRINT
|
|
return CMD_PRINT;
|
|
case 0x075E: // INPUT
|
|
return CMD_INPUT;
|
|
case 0x00D4: // IF
|
|
return CMD_IF;
|
|
case 0x03E3: // GOTO
|
|
return CMD_GOTO;
|
|
case 0x07AC: // GOSUB
|
|
return CMD_GOSUB;
|
|
case 0x0D0E: // RETURN
|
|
return CMD_RETURN;
|
|
case 0x01CC: // END
|
|
return CMD_END;
|
|
case 0x01CF: // DIM
|
|
return CMD_DIM;
|
|
case 0x0783: // DELAY
|
|
return CMD_DELAY;
|
|
case 0x03CD: // DATA
|
|
return CMD_DATA;
|
|
case 0x03DA: // EMIT
|
|
return CMD_EMIT;
|
|
default:
|
|
return extraCommandByHash(h);
|
|
}
|
|
}
|
|
|
|
char* getCurTokPos() {
|
|
return cur;
|
|
}
|
|
|
|
short tokenSize(token* t) {
|
|
switch (t->type) {
|
|
case TT_NUMBER:
|
|
return 1 + sizeof(t->body.integer);
|
|
case TT_NAME:
|
|
case TT_COMMENT:
|
|
case TT_LITERAL:
|
|
case TT_FUNCTION:
|
|
case TT_VARIABLE:
|
|
return 2 + t->body.str.len;
|
|
case TT_FUNC_END:
|
|
case TT_SYMBOL:
|
|
case TT_ARRAY:
|
|
case TT_COMMAND:
|
|
return 1 + sizeof(t->body.symbol);
|
|
case TT_NONE:
|
|
case TT_ERROR:
|
|
case TT_SEPARATOR:
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
short tokenChainSize(token* src) {
|
|
token* t = src;
|
|
while (t->type != TT_NONE) {
|
|
t = nextToken(t);
|
|
}
|
|
return (short)((char*)(void*)t - (char*)(void*)src) + 1;
|
|
}
|
|
|
|
void copyToken(token* dst, token* src) {
|
|
memcpy(dst, src, tokenSize(src));
|
|
}
|
|
|
|
void setTokenError(char* pos, char code) {
|
|
parseErrorCode = code;
|
|
parseError = pos;
|
|
curTok->type = TT_ERROR;
|
|
}
|
|
|
|
token* nextToken(token* t) {
|
|
return (token*)(void*) ((char*)(void*)t + tokenSize(t));
|
|
}
|
|
|
|
void skipTokenInInput(char offset) {
|
|
cur = skipSpaces(cur + offset);
|
|
}
|
|
|
|
static void advance(char* s) {
|
|
cur = skipSpaces(s);
|
|
prevTok = curTok;
|
|
curTok = nextToken(curTok);
|
|
}
|
|
|
|
static void substCommandFound(char code) {
|
|
curTok->type = TT_COMMAND;
|
|
curTok->body.command = code;
|
|
nextToken(curTok)->type = TT_ERROR;
|
|
}
|
|
|
|
void trySubstCmd(void) {
|
|
short i;
|
|
numeric cmdHash = tokenHash(curTok);
|
|
i = cmdCodeByHash(cmdHash);
|
|
if (i >= 0) {
|
|
substCommandFound(i);
|
|
return;
|
|
}
|
|
}
|
|
|
|
char parseName(char checkCmd) {
|
|
short i = 0;
|
|
if (!isAlpha(*cur)) {
|
|
return 0;
|
|
}
|
|
curTok->type = TT_NAME;
|
|
while (isAlNum(cur[i])) {
|
|
curTok->body.str.text[i] = toUpper(cur[i]);
|
|
i++;
|
|
}
|
|
curTok->body.str.len = i;
|
|
if (checkCmd) {
|
|
trySubstCmd();
|
|
}
|
|
advance(cur + i);
|
|
return 1;
|
|
}
|
|
|
|
char parseChar(void) {
|
|
if (cur[0] != '\'') {
|
|
return 0;
|
|
}
|
|
curTok->type = TT_NUMBER;
|
|
curTok->body.integer = ((unsigned char*) cur)[1];
|
|
cur += 2;
|
|
advance(cur);
|
|
return 1;
|
|
}
|
|
|
|
char parseNumber(void) {
|
|
char base = 10;
|
|
if (!isDigit(*cur)) {
|
|
return parseChar();
|
|
}
|
|
curTok->type = TT_NUMBER;
|
|
curTok->body.integer = 0;
|
|
if (cur[0] == '0') {
|
|
if (toUpper(cur[1]) == 'X') {
|
|
base = 16;
|
|
cur += 2;
|
|
} else if (toUpper(cur[1]) == 'B') {
|
|
base = 2;
|
|
cur += 2;
|
|
} else {
|
|
base = 8;
|
|
}
|
|
}
|
|
while (isDigitBased(*cur, base)) {
|
|
curTok->body.integer = curTok->body.integer * base + makeDigit(*cur, base);
|
|
cur++;
|
|
}
|
|
advance(cur);
|
|
return 1;
|
|
}
|
|
|
|
char parseNone(void) {
|
|
if (*cur != 0) {
|
|
setTokenError(cur, 5);
|
|
return 0;
|
|
}
|
|
curTok->type = TT_NONE;
|
|
return 1;
|
|
}
|
|
|
|
char parseComment(void) {
|
|
unsigned char len = strlen(cur);
|
|
curTok->type = TT_COMMENT;
|
|
curTok->body.str.len = len;
|
|
memcpy(&(curTok->body.str.text), cur, len);
|
|
advance(cur + len);
|
|
return parseNone();
|
|
}
|
|
|
|
char parseLiteral() {
|
|
if (*cur != '"') {
|
|
return 0;
|
|
}
|
|
short i = 1;
|
|
curTok->type = TT_LITERAL;
|
|
while (cur[i] != 0 && cur[i] != '"') {
|
|
curTok->body.str.text[i - 1] = cur[i];
|
|
i++;
|
|
}
|
|
curTok->body.str.len = i - 1;
|
|
advance(cur + i + (cur[i] == '"' ? 1 : 0));
|
|
return 1;
|
|
}
|
|
|
|
char parseSymbol() {
|
|
curTok->type = TT_SYMBOL;
|
|
char c = 0;
|
|
if (cur[0] == '<') {
|
|
if (cur[1] == '>') {
|
|
c = '#';
|
|
} else if (cur[1] == '=') {
|
|
c = '{';
|
|
}
|
|
} else if (cur[0] == '>' && cur[1] == '=') {
|
|
c = '}';
|
|
}
|
|
if (c != 0) {
|
|
cur++;
|
|
} else {
|
|
c = cur[0];
|
|
}
|
|
curTok->body.symbol = toUpper(c);
|
|
advance(cur + 1);
|
|
return 1;
|
|
}
|
|
|
|
char parseLineNumber(void) {
|
|
char* start = cur;
|
|
if (!parseNumber()) {
|
|
return 1;
|
|
}
|
|
if (prevTok->body.integer < 1 || prevTok->body.integer > MAX_LINE_NUMBER) {
|
|
setTokenError(start, 7);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
char parseSemicolon(void) {
|
|
if (*cur != ';') {
|
|
setTokenError(cur, 4);
|
|
return 0;
|
|
}
|
|
curTok->type = TT_SEPARATOR;
|
|
advance(cur + 1);
|
|
return 1;
|
|
}
|
|
|
|
char* skipSubscripts() {
|
|
char* p = cur;
|
|
char br = 0;
|
|
while (*p != 0) {
|
|
if (*p == '(') {
|
|
br += 1;
|
|
} else if (*p == ')') {
|
|
br -= 1;
|
|
if (br == 0) {
|
|
break;
|
|
}
|
|
}
|
|
p++;
|
|
}
|
|
return p;
|
|
}
|
|
|
|
char assignmentSyntax(void) {
|
|
char* p;
|
|
if (*cur == '=') {
|
|
return CMD_LET;
|
|
} else if (*cur != '(') {
|
|
setTokenError(cur, 2);
|
|
return 0;
|
|
}
|
|
p = skipSubscripts();
|
|
if (*p == 0) {
|
|
setTokenError(p, 10);
|
|
return 0;
|
|
}
|
|
p = skipSpaces(p + 1);
|
|
if (*p != '=') {
|
|
setTokenError(cur, 2);
|
|
return 0;
|
|
}
|
|
return CMD_LETA;
|
|
}
|
|
|
|
char parseSubscripts(void) {
|
|
char* p = skipSubscripts();
|
|
*p = ';';
|
|
cur++;
|
|
if (!parseExpression()) {
|
|
return 0;
|
|
}
|
|
parseSemicolon();
|
|
*p = ')';
|
|
return 1;
|
|
}
|
|
|
|
char parseAssignment(void) {
|
|
char synt = assignmentSyntax();
|
|
if (!synt) {
|
|
return 0;
|
|
}
|
|
curTok->type = TT_COMMAND;
|
|
memmove((char*)(void*)prevTok + tokenSize(curTok), prevTok, tokenSize(prevTok));
|
|
prevTok->type = TT_COMMAND;
|
|
prevTok->body.command = synt;
|
|
curTok = nextToken(prevTok);
|
|
if (synt == CMD_LETA) {
|
|
curTok->type = TT_ARRAY;
|
|
curTok->body.symbol = curTok->body.str.text[0];
|
|
}
|
|
prevTok = curTok;
|
|
curTok = nextToken(curTok);
|
|
if (synt == CMD_LETA) {
|
|
if (!parseSubscripts()) {
|
|
return 0;
|
|
}
|
|
}
|
|
cur = skipSpaces(cur + 1);
|
|
return parseExpression() && parseNone();
|
|
}
|
|
|
|
char parseExprOrLiteral(void) {
|
|
if (parseLiteral()) {
|
|
return 1;
|
|
}
|
|
return parseExpression();
|
|
}
|
|
|
|
char parseVar(void) {
|
|
if (!parseName(0)) {
|
|
setTokenError(cur, 3);
|
|
return 0;
|
|
}
|
|
prevTok->type = TT_VARIABLE;
|
|
return 1;
|
|
}
|
|
|
|
char parseExprList(void) {
|
|
if (!parseExpression()) {
|
|
return 0;
|
|
}
|
|
while (*cur != 0) {
|
|
if (!parseSemicolon() || !parseExpression()) {
|
|
return 0;
|
|
}
|
|
}
|
|
return parseNone();
|
|
}
|
|
|
|
char parsePrintList(void) {
|
|
if (!parseExprOrLiteral()) {
|
|
return 0;
|
|
}
|
|
while (*cur != 0) {
|
|
if (!parseSemicolon() || !parseExprOrLiteral()) {
|
|
return 0;
|
|
}
|
|
}
|
|
return parseNone();
|
|
}
|
|
|
|
char parseDataList(void) {
|
|
do {
|
|
if (!parseNumber() && !parseLiteral()) {
|
|
setTokenError(cur, 8);
|
|
return 0;
|
|
}
|
|
} while (*cur != 0);
|
|
return parseNone();
|
|
}
|
|
|
|
char parseNExpressions(char cnt) {
|
|
if (cnt > 0) {
|
|
if (!parseExpression()) {
|
|
return 0;
|
|
}
|
|
while (--cnt > 0) {
|
|
if (!parseSemicolon() || !parseExpression()) {
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
return parseNone();
|
|
}
|
|
|
|
char parseLabel(void) {
|
|
if (parseNumber()) {
|
|
return parseNone();
|
|
}
|
|
setTokenError(cur, 8);
|
|
return 0;
|
|
}
|
|
|
|
char parseStatement(void);
|
|
|
|
char parseConditional(void) {
|
|
if (!parseExpression()) {
|
|
return 0;
|
|
}
|
|
return parseSemicolon() && parseStatement();
|
|
}
|
|
|
|
char parseAllocate() {
|
|
if (!parseName(0)) {
|
|
setTokenError(cur, 3);
|
|
return 0;
|
|
}
|
|
prevTok->type = TT_ARRAY;
|
|
prevTok->body.symbol = prevTok->body.str.text[0];
|
|
curTok = nextToken(prevTok);
|
|
if (!parseNumber()) {
|
|
setTokenError(cur, 8);
|
|
return 0;
|
|
}
|
|
if (*cur != 0) {
|
|
if (!parseSymbol() || prevTok->body.symbol != 'B') {
|
|
setTokenError(cur, 9);
|
|
return 0;
|
|
}
|
|
}
|
|
return parseNone();
|
|
}
|
|
|
|
void parseSpecialWithError() {
|
|
curTok = nextToken(curTok);
|
|
while (parseName(0) || parseNumber() || parseLiteral()) {
|
|
}
|
|
curTok->type = TT_ERROR;
|
|
}
|
|
|
|
char parseStatement(void) {
|
|
char cmd;
|
|
if (!parseName(1)) {
|
|
setTokenError(cur, 1);
|
|
return 0;
|
|
} else if (prevTok->type != TT_COMMAND) {
|
|
if (parseAssignment()) {
|
|
return 1;
|
|
} else {
|
|
parseSpecialWithError();
|
|
return 0;
|
|
}
|
|
}
|
|
cmd = prevTok->body.command;
|
|
if (cmd == CMD_REM) {
|
|
return parseComment();
|
|
} else if (cmd == CMD_GOTO || cmd == CMD_GOSUB) {
|
|
return parseLabel();
|
|
} else if (cmd == CMD_RETURN || cmd == CMD_END) {
|
|
return parseNone();
|
|
} else if (cmd == CMD_PRINT) {
|
|
return parsePrintList();
|
|
} else if (cmd == CMD_INPUT) {
|
|
return parseVar() && parseNone();
|
|
} else if (cmd == CMD_IF) {
|
|
return parseConditional();
|
|
} else if (cmd == CMD_DIM) {
|
|
return parseAllocate();
|
|
} else if (cmd == CMD_DELAY) {
|
|
return parseNExpressions(1);
|
|
} else if (cmd == CMD_DATA) {
|
|
return parseDataList();
|
|
} else if (cmd == CMD_EMIT) {
|
|
return parseExprList();
|
|
} else if (cmd >= CMD_EXTRA) {
|
|
return parseNExpressions(extraCmdArgCnt[cmd - CMD_EXTRA]);
|
|
}
|
|
setTokenError(cur, 6);
|
|
return 0;
|
|
}
|
|
|
|
void parseLine(char* line, token* tokens) {
|
|
cur = line;
|
|
curTok = tokens;
|
|
prevTok = NULL;
|
|
setTokenError(NULL, 0);
|
|
if (parseLineNumber()) {
|
|
if (*cur != 0) {
|
|
parseStatement();
|
|
} else {
|
|
parseNone();
|
|
}
|
|
}
|
|
}
|
|
|
|
char tokenClass(token* t) {
|
|
return t->type & 0xF0;
|
|
}
|
|
|
|
numeric tokenHash(token* t) {
|
|
if (tokenClass(t) != TT_NAME) {
|
|
return 0;
|
|
}
|
|
return hashOfNStr(&(t->body.str));
|
|
}
|
|
|
|
char* getParseErrorPos(void) {
|
|
return parseError;
|
|
}
|
|
|
|
void getParseErrorMsg(char* s) {
|
|
outputConstStr(ID_PARSING_ERRORS, parseErrorCode, s);
|
|
}
|
|
|