diff --git a/src/vecparse.c b/src/vecparse.c index dfa0b3c..9256688 100644 --- a/src/vecparse.c +++ b/src/vecparse.c @@ -21,6 +21,7 @@ #include +#include #include "common.h" #include "vecparse.h" @@ -38,15 +39,21 @@ typedef struct OperatorsS { ParserMathT id; } OperatorsT; +typedef struct LabelS { + char *key; + int value; +} LabelT; -static void ensureBufferSize(VecByteCodeT *bytecode, int needed); -static void outputByte(VecByteCodeT *bytecode, unsigned short word); -static void outputWord(VecByteCodeT *bytecode, unsigned short word); -static gboolean parserGetNextValue(char *token, char **valueEnd, char **variables, int *x); -static gboolean parserGetWord(char *word, char **tokenEnd); -static gboolean parserGetX(char **tokenEnd, char **variables, int *x); -static gboolean parserGetXY(char **tokenEnd, char **variables, int *x, int *y); -static gboolean parserGetXYZ(char **tokenEnd, char **variables, int *x, int *y, int *z); + +static void ensureBufferSize(VecByteCodeT *bytecode, int needed); +static void outputByte(VecByteCodeT *bytecode, unsigned short word); +static void outputWord(VecByteCodeT *bytecode, unsigned short word); +static gboolean parserGetNextValue(char *token, char **valueEnd, char **variables, int *x); +static gboolean parserGetWord(char *word, char **tokenEnd); +static gboolean parserGetX(char **tokenEnd, char **variables, int *x); +static gboolean parserGetXY(char **tokenEnd, char **variables, int *x, int *y); +static gboolean parserGetXYZ(char **tokenEnd, char **variables, int *x, int *y, int *z); +static char *strtoke(char *str, const char *delim, char **savePtr); static void ensureBufferSize(VecByteCodeT *bytecode, int needed) { @@ -101,7 +108,6 @@ static gboolean parserGetNextValue(char *token, char **valueEnd, char **variable // It's a variable. Do we know it? *x = -1; for (index=0; index 1 && token[0] == '%') { + // Is this a blank line? + if (token == NULL) { - // We're doing some kind of math. + // Yep - just move on. + lineOkay = TRUE; - // Look up variable index in table, or add it if needed. - // Variable index is stored in y1 for later. - y1 = -1; - for (y2=0; y2 1 && token[0] == '%') { + + // We're doing some kind of math. + + // Look up variable index in table, or add it if needed. + // Variable index is stored in y1 for later. + y1 = -1; + for (y2 = 0; y2 < arrlen(variables); y2++) { + if (strcasecmp(variables[y2], token) == 0) { + y1 = y2; // Found. break; } - keyword++; } - } + if (y1 < 0) { + // Variable is not yet known. Add it to list. + arrput(variables, token); + y1 = arrlen(variables) - 1; + } + // Mark as variable. + y1 |= 0x80; - } else { // doing math + // Find the operator. + token = strtok_r(NULL, " ", &tokenEnd); + if (token != NULL) { + keyword = 0; + while (math[keyword].operator) { + if (strcasecmp(math[keyword].operator, token) == 0) { + // Yep! Gather arguments and generate bytecode. + if (!parserGetX(&tokenEnd, variables, &x1)) break; + outputByte(bytecode, PARSE_MATH); + outputByte(bytecode, y1); + outputByte(bytecode, math[keyword].id); + outputWord(bytecode, x1); + lineOkay = TRUE; + break; + } + keyword++; + } + } - // Is it a label? - if (strlen(token) > 1 && token[strlen(token) - 1] == ':') { + } else { // doing math - // It's a label. + // Is it a label? + if (strlen(token) > 1 && token[strlen(token) - 1] == ':') { - } else { // doing a label + // It's a label. - // It's a keyword. - keyword = 0; - while (commands[keyword].command) { - if (strcasecmp(commands[keyword].command, token) == 0) { + // Have we already used this label? + x2 = -1; + for (x1 = 0; x1 < hmlen(labels); x1++) { + if (strcasecmp(token, labels[x1].key) == 0) { + x2 = x1; + break; + } + } + if (x2 < 0) { + // New label. Add to hashmap. + hmput(labels, token, bytecode->length); + lineOkay = TRUE; + } - // Yep! Gather arguments and generate bytecode. - switch (commands[keyword].id) { + } else { // doing a label - case PARSE_NONE: - // Won't happen, but silences an error. - break; + // It's a keyword. + keyword = 0; + while (commands[keyword].command) { + if (strcasecmp(commands[keyword].command, token) == 0) { - case PARSE_BOX: - // Box (value),(value) to (value),(value) - if (!parserGetXY(&tokenEnd, variables, &x1, &y1)) break; - if (!parserGetWord("TO", &tokenEnd)) break; - if (!parserGetXY(&tokenEnd, variables, &x2, &y2)) break; - if (IS_NUMBER(x1) && IS_NUMBER(x2)) if (x2 < x1) break; - if (IS_NUMBER(x1)) if (x1 < 0 || x1 > 319) break; - if (IS_NUMBER(x2)) if (x2 < 0 || x2 > 319) break; - if (IS_NUMBER(y1) && IS_NUMBER(y2)) if (y2 < y1) break; - if (IS_NUMBER(y1)) if (y1 < 0 || y1 > 199) break; - if (IS_NUMBER(y2)) if (y2 < 0 || y2 > 199) break; - outputByte(bytecode, PARSE_BOX); - outputWord(bytecode, x1); - outputWord(bytecode, y1); - outputWord(bytecode, x2); - outputWord(bytecode, y2); - lineOkay = TRUE; - break; + // Yep! Gather arguments and generate bytecode. + switch (commands[keyword].id) { - case PARSE_CIRCLE: - // Circle (value) at (value),(value) - if (!parserGetX(&tokenEnd, variables, &y2)) break; - if (!parserGetWord("AT", &tokenEnd)) break; - if (!parserGetXY(&tokenEnd, variables, &x1, &y1)) break; - if (IS_NUMBER(x1)) if (x1 < 0 || x1 > 319) break; - if (IS_NUMBER(y1)) if (y1 < 0 || y1 > 199) break; - outputByte(bytecode, PARSE_CIRCLE); - outputWord(bytecode, y2); - outputWord(bytecode, x1); - outputWord(bytecode, y1); - lineOkay = TRUE; - break; + case PARSE_NONE: + // Won't happen, but silences an error. + break; - case PARSE_CLEAR: - outputByte(bytecode, PARSE_CLEAR); - lineOkay = TRUE; - break; + case PARSE_BOX: + // Box (value),(value) to (value),(value) + if (!parserGetXY(&tokenEnd, variables, &x1, &y1)) break; + if (!parserGetWord("TO", &tokenEnd)) break; + if (!parserGetXY(&tokenEnd, variables, &x2, &y2)) break; + if (IS_NUMBER(x1) && IS_NUMBER(x2)) if (x2 < x1) break; + if (IS_NUMBER(x1)) if (x1 < 0 || x1 > 319) break; + if (IS_NUMBER(x2)) if (x2 < 0 || x2 > 319) break; + if (IS_NUMBER(y1) && IS_NUMBER(y2)) if (y2 < y1) break; + if (IS_NUMBER(y1)) if (y1 < 0 || y1 > 199) break; + if (IS_NUMBER(y2)) if (y2 < 0 || y2 > 199) break; + outputByte(bytecode, PARSE_BOX); + outputWord(bytecode, x1); + outputWord(bytecode, y1); + outputWord(bytecode, x2); + outputWord(bytecode, y2); + lineOkay = TRUE; + break; - case PARSE_COLOR: - // Color (short) - if (!parserGetX(&tokenEnd, variables, &x1)) break; - if (IS_NUMBER(x1)) if (x1 < 0 || x1 > 15) break; - outputByte(bytecode, PARSE_COLOR); - outputByte(bytecode, x1); - lineOkay = TRUE; - break; + case PARSE_CIRCLE: + // Circle (value) at (value),(value) + if (!parserGetX(&tokenEnd, variables, &y2)) break; + if (!parserGetWord("AT", &tokenEnd)) break; + if (!parserGetXY(&tokenEnd, variables, &x1, &y1)) break; + if (IS_NUMBER(x1)) if (x1 < 0 || x1 > 319) break; + if (IS_NUMBER(y1)) if (y1 < 0 || y1 > 199) break; + outputByte(bytecode, PARSE_CIRCLE); + outputWord(bytecode, y2); + outputWord(bytecode, x1); + outputWord(bytecode, y1); + lineOkay = TRUE; + break; - case PARSE_COMMENT: - // Eat the rest of the line. - while (token != NULL) { - token = strtok_r(NULL, " ", &tokenEnd); - } - lineOkay = TRUE; - break; + case PARSE_CLEAR: + outputByte(bytecode, PARSE_CLEAR); + lineOkay = TRUE; + break; - case PARSE_ELLIPSE: - // Ellipse (value),(value) to (value),(value) - if (!parserGetXY(&tokenEnd, variables, &x1, &y1)) break; - if (!parserGetWord("TO", &tokenEnd)) break; - if (!parserGetXY(&tokenEnd, variables, &x2, &y2)) break; - if (IS_NUMBER(x1) && IS_NUMBER(x2)) if (x2 < x1) break; - if (IS_NUMBER(x1)) if (x1 < 0 || x1 > 319) break; - if (IS_NUMBER(x2)) if (x2 < 0 || x2 > 319) break; - if (IS_NUMBER(y1) && IS_NUMBER(y2)) if (y2 < y1) break; - if (IS_NUMBER(y1)) if (y1 < 0 || y1 > 199) break; - if (IS_NUMBER(y2)) if (y2 < 0 || y2 > 199) break; - outputByte(bytecode, PARSE_ELLIPSE); - outputWord(bytecode, x1); - outputWord(bytecode, y1); - outputWord(bytecode, x2); - outputWord(bytecode, y2); - lineOkay = TRUE; - break; + case PARSE_COLOR: + // Color (short) + if (!parserGetX(&tokenEnd, variables, &x1)) break; + if (IS_NUMBER(x1)) if (x1 < 0 || x1 > 15) break; + outputByte(bytecode, PARSE_COLOR); + outputByte(bytecode, x1); + lineOkay = TRUE; + break; - case PARSE_FILL: - // Fill (value),(value) {to (value} - if (!parserGetXY(&tokenEnd, variables, &x1, &y1)) break; - if (IS_NUMBER(x1)) if (x1 < 0 || x1 > 319) break; - if (IS_NUMBER(y1)) if (y1 < 0 || y1 > 199) break; - // Do they want to fill to a certain color? Or over the current color? - x2 = 16; // 16 == Fill, otherwise FillTo - if (parserGetWord("TO", &tokenEnd)) { - if (!parserGetX(&tokenEnd, variables, &x2)) break; - if (IS_NUMBER(x2)) if (x2 < 0 || x2 > 15) break; - } - outputByte(bytecode, PARSE_FILL); - outputWord(bytecode, x1); - outputWord(bytecode, y1); - outputByte(bytecode, x2); - lineOkay = TRUE; - break; + case PARSE_COMMENT: + // Eat the rest of the line. + while (token != NULL) { + token = strtok_r(NULL, " ", &tokenEnd); + } + lineOkay = TRUE; + break; - case PARSE_LABEL: - // Won't happen. - break; + case PARSE_ELLIPSE: + // Ellipse (value),(value) to (value),(value) + if (!parserGetXY(&tokenEnd, variables, &x1, &y1)) break; + if (!parserGetWord("TO", &tokenEnd)) break; + if (!parserGetXY(&tokenEnd, variables, &x2, &y2)) break; + if (IS_NUMBER(x1) && IS_NUMBER(x2)) if (x2 < x1) break; + if (IS_NUMBER(x1)) if (x1 < 0 || x1 > 319) break; + if (IS_NUMBER(x2)) if (x2 < 0 || x2 > 319) break; + if (IS_NUMBER(y1) && IS_NUMBER(y2)) if (y2 < y1) break; + if (IS_NUMBER(y1)) if (y1 < 0 || y1 > 199) break; + if (IS_NUMBER(y2)) if (y2 < 0 || y2 > 199) break; + outputByte(bytecode, PARSE_ELLIPSE); + outputWord(bytecode, x1); + outputWord(bytecode, y1); + outputWord(bytecode, x2); + outputWord(bytecode, y2); + lineOkay = TRUE; + break; - case PARSE_LINE: - // Line (value),(value) to (value),(value) [to ...] - points = NULL; - if (!parserGetXY(&tokenEnd, variables, &p1.x, &p1.y)) break; - if (!parserGetWord("TO", &tokenEnd)) break; - if (!parserGetXY(&tokenEnd, variables, &p2.x, &p2.y)) break; - if (IS_NUMBER(p1.x)) if (p1.x < 0 || p1.x > 319) break; - if (IS_NUMBER(p1.y)) if (p1.y < 0 || p1.y > 199) break; - if (IS_NUMBER(p2.x)) if (p2.x < 0 || p2.x > 319) break; - if (IS_NUMBER(p2.y)) if (p2.y < 0 || p2.y > 199) break; - arrput(points, p1); - arrput(points, p2); - isOkay = TRUE; - while (parserGetWord("TO", &tokenEnd)) { - if (!parserGetXY(&tokenEnd, variables, &p1.x, &p1.y)) { - // Error. Unwind array. + case PARSE_FILL: + // Fill (value),(value) {to (value} + if (!parserGetXY(&tokenEnd, variables, &x1, &y1)) break; + if (IS_NUMBER(x1)) if (x1 < 0 || x1 > 319) break; + if (IS_NUMBER(y1)) if (y1 < 0 || y1 > 199) break; + // Do they want to fill to a certain color? Or over the current color? + x2 = 16; // 16 == Fill, otherwise FillTo + if (parserGetWord("TO", &tokenEnd)) { + if (!parserGetX(&tokenEnd, variables, &x2)) break; + if (IS_NUMBER(x2)) if (x2 < 0 || x2 > 15) break; + } + outputByte(bytecode, PARSE_FILL); + outputWord(bytecode, x1); + outputWord(bytecode, y1); + outputByte(bytecode, x2); + lineOkay = TRUE; + break; + + case PARSE_LABEL: + // Won't happen. + break; + + case PARSE_LINE: + // Line (value),(value) to (value),(value) [to ...] + points = NULL; + if (!parserGetXY(&tokenEnd, variables, &p1.x, &p1.y)) break; + if (!parserGetWord("TO", &tokenEnd)) break; + if (!parserGetXY(&tokenEnd, variables, &p2.x, &p2.y)) break; + if (IS_NUMBER(p1.x)) if (p1.x < 0 || p1.x > 319) break; + if (IS_NUMBER(p1.y)) if (p1.y < 0 || p1.y > 199) break; + if (IS_NUMBER(p2.x)) if (p2.x < 0 || p2.x > 319) break; + if (IS_NUMBER(p2.y)) if (p2.y < 0 || p2.y > 199) break; + arrput(points, p1); + arrput(points, p2); + isOkay = TRUE; + while (parserGetWord("TO", &tokenEnd)) { + if (!parserGetXY(&tokenEnd, variables, &p1.x, &p1.y)) { + // Error. Unwind array. + while (arrlen(points) > 0) { + arrdel(points, 0); + } + arrfree(points); + isOkay = FALSE; + break; + } + arrput(points, p1); + } + if (isOkay) { + outputByte(bytecode, PARSE_LINE); + outputWord(bytecode, arrlen(points)); + for (x1 = 0; x1 < arrlen(points); x1++) { + outputWord(bytecode, points[x1].x); + outputWord(bytecode, points[x1].y); + } + // Unwind array. while (arrlen(points) > 0) { arrdel(points, 0); } arrfree(points); - isOkay = FALSE; - break; + lineOkay = TRUE; } - arrput(points, p1); - } - if (isOkay) { - outputByte(bytecode, PARSE_LINE); - outputWord(bytecode, arrlen(points)); - for (x1 = 0; x1 < arrlen(points); x1++) { - outputWord(bytecode, points[x1].x); - outputWord(bytecode, points[x1].y); - } - // Unwind array. - while (arrlen(points) > 0) { - arrdel(points, 0); - } - arrfree(points); + break; + + case PARSE_MATH: + // Won't happen. + break; + + case PARSE_PALETTE: + // Palette (short) AS (short),(short),(short) + if (!parserGetX(&tokenEnd, variables, &x1)) break; + if (!parserGetWord("AS", &tokenEnd)) break; + if (!parserGetXYZ(&tokenEnd, variables, &x2, &y1, &y2)) break; + if (IS_NUMBER(x1)) if (x1 < 0 || x1 > 15) break; + if (IS_NUMBER(x2)) if (x2 < 0 || x2 > 15) break; + if (IS_NUMBER(y1)) if (y1 < 0 || y1 > 15) break; + if (IS_NUMBER(y2)) if (y2 < 0 || y2 > 15) break; + outputByte(bytecode, PARSE_PALETTE); + outputByte(bytecode, x1); + outputByte(bytecode, x2); + outputByte(bytecode, y1); + outputByte(bytecode, y2); lineOkay = TRUE; - } - break; + break; - case PARSE_MATH: - // Won't happen. - break; + case PARSE_PLOT: + // Plot (value),(value) + if (!parserGetXY(&tokenEnd, variables, &x1, &y1)) break; + if (IS_NUMBER(x1)) if (x1 < 0 || x1 > 319) break; + if (IS_NUMBER(y1)) if (y1 < 0 || y1 > 199) break; + outputByte(bytecode, PARSE_PLOT); + outputWord(bytecode, x1); + outputWord(bytecode, y1); + lineOkay = TRUE; + break; - case PARSE_PALETTE: - // Palette (short) AS (short),(short),(short) - if (!parserGetX(&tokenEnd, variables, &x1)) break; - if (!parserGetWord("AS", &tokenEnd)) break; - if (!parserGetXYZ(&tokenEnd, variables, &x2, &y1, &y2)) break; - if (IS_NUMBER(x1)) if (x1 < 0 || x1 > 15) break; - if (IS_NUMBER(x2)) if (x2 < 0 || x2 > 15) break; - if (IS_NUMBER(y1)) if (y1 < 0 || y1 > 15) break; - if (IS_NUMBER(y2)) if (y2 < 0 || y2 > 15) break; - outputByte(bytecode, PARSE_PALETTE); - outputByte(bytecode, x1); - outputByte(bytecode, x2); - outputByte(bytecode, y1); - outputByte(bytecode, y2); - lineOkay = TRUE; - break; + case PARSE_RECTANGLE: + // Rectangle (value),(value) to (value),(value) + if (!parserGetXY(&tokenEnd, variables, &x1, &y1)) break; + if (!parserGetWord("TO", &tokenEnd)) break; + if (!parserGetXY(&tokenEnd, variables, &x2, &y2)) break; + if (IS_NUMBER(x1) && IS_NUMBER(x2)) if (x2 < x1) break; + if (IS_NUMBER(x1)) if (x1 < 0 || x1 > 319) break; + if (IS_NUMBER(x2)) if (x2 < 0 || x2 > 319) break; + if (IS_NUMBER(y1) && IS_NUMBER(y2)) if (y2 < y1) break; + if (IS_NUMBER(y1)) if (y1 < 0 || y1 > 199) break; + if (IS_NUMBER(y2)) if (y2 < 0 || y2 > 199) break; + outputByte(bytecode, PARSE_RECTANGLE); + outputWord(bytecode, x1); + outputWord(bytecode, y1); + outputWord(bytecode, x2); + outputWord(bytecode, y2); + lineOkay = TRUE; + break; - case PARSE_PLOT: - // Plot (value),(value) - if (!parserGetXY(&tokenEnd, variables, &x1, &y1)) break; - if (IS_NUMBER(x1)) if (x1 < 0 || x1 > 319) break; - if (IS_NUMBER(y1)) if (y1 < 0 || y1 > 199) break; - outputByte(bytecode, PARSE_PLOT); - outputWord(bytecode, x1); - outputWord(bytecode, y1); - lineOkay = TRUE; - break; + case PARSE_RESET: + outputByte(bytecode, PARSE_RESET); + lineOkay = TRUE; + break; - case PARSE_RECTANGLE: - // Rectangle (value),(value) to (value),(value) - if (!parserGetXY(&tokenEnd, variables, &x1, &y1)) break; - if (!parserGetWord("TO", &tokenEnd)) break; - if (!parserGetXY(&tokenEnd, variables, &x2, &y2)) break; - if (IS_NUMBER(x1) && IS_NUMBER(x2)) if (x2 < x1) break; - if (IS_NUMBER(x1)) if (x1 < 0 || x1 > 319) break; - if (IS_NUMBER(x2)) if (x2 < 0 || x2 > 319) break; - if (IS_NUMBER(y1) && IS_NUMBER(y2)) if (y2 < y1) break; - if (IS_NUMBER(y1)) if (y1 < 0 || y1 > 199) break; - if (IS_NUMBER(y2)) if (y2 < 0 || y2 > 199) break; - outputByte(bytecode, PARSE_RECTANGLE); - outputWord(bytecode, x1); - outputWord(bytecode, y1); - outputWord(bytecode, x2); - outputWord(bytecode, y2); - lineOkay = TRUE; - break; + } // switch - case PARSE_RESET: - outputByte(bytecode, PARSE_RESET); - lineOkay = TRUE; - break; + // Stop looking for this keyword - we handled it. + break; - } // switch + } else { + // Keep looking until we find this keyword or run out of commands. + keyword++; + } // keyword match - // Stop looking for this keyword - we handled it. - break; + } // loop over commands - } else { - // Keep looking until we find this keyword or run out of commands. - keyword++; - } // keyword match + } // label - } // loop over commands + } // math - } // label - - } // math + } // blank line // Is everything still okay? if (!lineOkay) { @@ -567,7 +647,8 @@ int vecparser(char *programIn, VecByteCodeT *bytecode) { } // Move to next line. - line = strtok_r(NULL, "\n", &lineEnd); + line = strtoke(NULL, "\n", &lineEnd); + while (line != NULL && (line[0] == ' ' || line[0] == '\t')) line++; lineNumber++; } // read program line