From 9cd1aef2f5fe8fc9665a6f94b8bd4613e2d7e070 Mon Sep 17 00:00:00 2001 From: Scott Duensing Date: Sun, 4 Dec 2022 21:31:59 -0600 Subject: [PATCH] Parser emitting CALL, GOTO, IF, and RETURN. They need added to the renderer. --- include/vecparse.h | 4 + src/vecparse.c | 248 ++++++++++++++++++++++++++++----------------- src/vector.c | 2 + 3 files changed, 159 insertions(+), 95 deletions(-) diff --git a/include/vecparse.h b/include/vecparse.h index 8c4effa..be4c4f1 100644 --- a/include/vecparse.h +++ b/include/vecparse.h @@ -27,17 +27,21 @@ enum ParserKeywordE { PARSE_NONE = 0, PARSE_BOX, + PARSE_CALL, PARSE_CIRCLE, PARSE_CLEAR, PARSE_COLOR, PARSE_COMMENT, PARSE_ELLIPSE, PARSE_FILL, + PARSE_GOTO, + PARSE_IF, PARSE_LINE, PARSE_PALETTE, PARSE_PLOT, PARSE_RECTANGLE, PARSE_RESET, + PARSE_RETURN, // After this are keywords for things that aren't commands but still need to emit bytecode. PARSE_MATH, PARSE_LABEL diff --git a/src/vecparse.c b/src/vecparse.c index 9256688..4f9b6ae 100644 --- a/src/vecparse.c +++ b/src/vecparse.c @@ -21,7 +21,6 @@ #include -#include #include "common.h" #include "vecparse.h" @@ -46,14 +45,15 @@ typedef struct LabelS { static void ensureBufferSize(VecByteCodeT *bytecode, int needed); +static int labelGetValue(LabelT *labels, char *label); static void outputByte(VecByteCodeT *bytecode, unsigned short word); static void outputWord(VecByteCodeT *bytecode, unsigned short word); +static char *parserGetNextLine(char *str, char **savePtr); static gboolean parserGetNextValue(char *token, char **valueEnd, char **variables, int *x); -static gboolean parserGetWord(char *word, char **tokenEnd); +static int parserGetWord(char *wordList, 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) { @@ -71,6 +71,31 @@ static void ensureBufferSize(VecByteCodeT *bytecode, int needed) { } +static int labelGetValue(LabelT *labels, char *label) { + int index; + int found = -1; + + // Do we know this label? We search ourselves so it's case-insensitive. + for (index = 0; index < hmlen(labels); index++) { + printf("Label %d of %d; %s == %s\n", index, (int)hmlen(labels), labels[index].key, label); + if (strcasecmp(label, labels[index].key) == 0) { + found = index; + break; + } + } + if (found < 0) { + // We don't yet know this label, so it's a forward reference. + // Record it with a negative location, so we can resolve it later. + index = 0 - (hmlen(labels) + 1); + hmput(labels, label, index); + } else { + index = labels[index].value; + } + + return index; +} + + static void outputByte(VecByteCodeT *bytecode, unsigned short word) { unsigned char byte = (unsigned char)word; @@ -92,6 +117,35 @@ static void outputWord(VecByteCodeT *bytecode, unsigned short word) { } +// Custom strtok_r because: +// A sequence of two or more contiguous delimiter bytes in the +// parsed string is considered to be a single delimiter. +// Which is exactly what we don't want for the line parser. +static char *parserGetNextLine(char *str, char **savePtr) { + char *token = NULL; + + if (str) { + *savePtr = str; + } + + if (!*savePtr) { + return NULL; + } + + token = *savePtr; + *savePtr = strpbrk(*savePtr, "\n"); + + if (*savePtr) { + **savePtr = 0; + *savePtr = *savePtr + 1; + } + + while (token != NULL && (token[0] == ' ' || token[0] == '\t')) token++; + + return token; +} + + static gboolean parserGetNextValue(char *token, char **valueEnd, char **variables, int *x) { char *value; char *endPtr = NULL; @@ -128,7 +182,7 @@ static gboolean parserGetNextValue(char *token, char **valueEnd, char **variable *x = -*x; *x |= 0x4000; } - // Ensure MSb is cleared so we know it's a number. + // Ensure MSb is cleared, so we know it's a number. *x &= 0x7fff; } @@ -136,16 +190,27 @@ static gboolean parserGetNextValue(char *token, char **valueEnd, char **variable } -static gboolean parserGetWord(char *word, char **tokenEnd) { +static int parserGetWord(char *wordList, char **tokenEnd) { char *token; + char *word; + char *wordEnd = NULL; + int index = -1; - // Is this token the "WORD"? + // Returns -1 if the word is not found in the wordlist. + // Returns a zero-based index of the position in the list + // if one of the words is found. token = strtok_r(NULL, " ", tokenEnd); - if (token == NULL) return FALSE; - if (strcasecmp(token, word) != 0) return FALSE; + if (token == NULL) return index; - return TRUE; + word = strtok_r(wordList, " ", &wordEnd); + while (word != NULL) { + index++; + if (strcasecmp(token, word) == 0) break; + word = strtok_r(NULL, " ", &wordEnd); + } + + return index; } @@ -186,7 +251,7 @@ static gboolean parserGetX(char **tokenEnd, char **variables, int *x) { *x = -*x; *x |= 0x4000; } - // Ensure MSb is cleared so we know it's a number. + // Ensure MSb is cleared, so we know it's a number. *x &= 0x7fff; } @@ -227,59 +292,6 @@ static gboolean parserGetXYZ(char **tokenEnd, char **variables, int *x, int *y, } -// Custom strtok_r because: -// A sequence of two or more contiguous delimiter bytes in the -// parsed string is considered to be a single delimiter. -// Which is exactly what we don't want for the line parser. -static char *strtoke(char *str, const char *delim, char **savePtr) { - /* - char *end = NULL; - - if (str == NULL) { - str = *savePtr; - } - - if (*str == 0) { - *savePtr = str; - return NULL; - } - - str = strpbrk(str, delim); - end = str + 1; - - if (*end == 0) { - *savePtr++ = end; - return str; - } - - *end = 0; - *savePtr = end + 1; - - return str; - */ - char *token = NULL; // found token - // assign new start in case - if (str) { - *savePtr = str; - } - // check whether text to parse left - if (!*savePtr) { - return NULL; - } - // remember current start as found token - token = *savePtr; - // find next occurrence of delim - *savePtr = strpbrk(*savePtr, delim); - // replace delim with terminator and move start to follower - if (*savePtr) { - **savePtr = 0; - *savePtr = *savePtr + 1; - } - // done - return token; -} - - int vecparser(char *programIn, VecByteCodeT *bytecode) { int keyword; char *line; @@ -301,18 +313,22 @@ int vecparser(char *programIn, VecByteCodeT *bytecode) { int result = -1; // Returns -1 on success or line number of first error. KeywordsT commands[] = { { "BOX", PARSE_BOX }, + { "CALL", PARSE_CALL }, { "CIRCLE", PARSE_CIRCLE }, { "CLEAR", PARSE_CLEAR }, { "COLOR", PARSE_COLOR }, { "//", PARSE_COMMENT }, { "ELLIPSE", PARSE_ELLIPSE }, + { "GOTO", PARSE_GOTO }, + { "IF", PARSE_IF }, { "FILL", PARSE_FILL }, { "LINE", PARSE_LINE }, { "PALETTE", PARSE_PALETTE }, { "PLOT", PARSE_PLOT }, - { "RECTANGLE", PARSE_RECTANGLE }, - { "RESET", PARSE_RESET }, - { NULL, PARSE_NONE } + { "RECTANGLE", PARSE_RECTANGLE }, + { "RESET", PARSE_RESET }, + { "RETURN", PARSE_RETURN }, + { NULL, PARSE_NONE } }; OperatorsT math[] = { { "=", MATH_ASSIGN }, @@ -325,26 +341,23 @@ int vecparser(char *programIn, VecByteCodeT *bytecode) { { "SQRT", MATH_SQRT }, { "ABS", MATH_ABS }, { "COS", MATH_COS }, - { "SIN", MATH_SIN }, - { "TAN", MATH_TAN }, - { NULL, MATH_NONE } + { "SIN", MATH_SIN }, + { "TAN", MATH_TAN }, + { NULL, MATH_NONE } }; // Parse code. lineNumber = 0; - line = strtoke(programIn, "\n", &lineEnd); - while (line != NULL && (line[0] == ' ' || line[0] == '\t')) line++; + line = parserGetNextLine(programIn, &lineEnd); while (line != NULL) { - printf("%d [%s]\n", lineNumber, line); - // Get the first token on the line. token = strtok_r(line, " ", &tokenEnd); // Is this something we care about? It'll be math, a label, or a keyword. lineOkay = FALSE; - // Is this a blank line? + // Is this a blank line with no tokens? if (token == NULL) { // Yep - just move on. @@ -368,7 +381,7 @@ int vecparser(char *programIn, VecByteCodeT *bytecode) { } if (y1 < 0) { // Variable is not yet known. Add it to list. - arrput(variables, token); + arrput(variables, token); y1 = arrlen(variables) - 1; } // Mark as variable. @@ -400,8 +413,12 @@ int vecparser(char *programIn, VecByteCodeT *bytecode) { // It's a label. + // Remove trailing colon. + token[strlen(token) - 1] = 0; + // Have we already used this label? x2 = -1; + // We search ourselves so it's case-insensitive. for (x1 = 0; x1 < hmlen(labels); x1++) { if (strcasecmp(token, labels[x1].key) == 0) { x2 = x1; @@ -431,7 +448,7 @@ int vecparser(char *programIn, VecByteCodeT *bytecode) { case PARSE_BOX: // Box (value),(value) to (value),(value) if (!parserGetXY(&tokenEnd, variables, &x1, &y1)) break; - if (!parserGetWord("TO", &tokenEnd)) break; + if (parserGetWord("TO", &tokenEnd) < 0) 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; @@ -447,10 +464,19 @@ int vecparser(char *programIn, VecByteCodeT *bytecode) { lineOkay = TRUE; break; + case PARSE_CALL: + // Call (label) + if (tokenEnd == NULL) break; + x1 = labelGetValue(labels, tokenEnd); + outputByte(bytecode, PARSE_CALL); + outputWord(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 (parserGetWord("AT", &tokenEnd) < 0) 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; @@ -486,7 +512,7 @@ int vecparser(char *programIn, VecByteCodeT *bytecode) { case PARSE_ELLIPSE: // Ellipse (value),(value) to (value),(value) if (!parserGetXY(&tokenEnd, variables, &x1, &y1)) break; - if (!parserGetWord("TO", &tokenEnd)) break; + if (parserGetWord("TO", &tokenEnd) < 0) 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; @@ -509,7 +535,7 @@ int vecparser(char *programIn, VecByteCodeT *bytecode) { 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 (parserGetWord("TO", &tokenEnd) >= 0) { if (!parserGetX(&tokenEnd, variables, &x2)) break; if (IS_NUMBER(x2)) if (x2 < 0 || x2 > 15) break; } @@ -520,6 +546,32 @@ int vecparser(char *programIn, VecByteCodeT *bytecode) { lineOkay = TRUE; break; + case PARSE_GOTO: + // Goto (label) + if (tokenEnd == NULL) break; + x1 = labelGetValue(labels, tokenEnd); + outputByte(bytecode, PARSE_GOTO); + outputWord(bytecode, x1); + lineOkay = TRUE; + break; + + case PARSE_IF: + // If (value) (compare) (value) (goto|call) (label) + if (!parserGetX(&tokenEnd, variables, &x1)) break; + y1 = parserGetWord("== != < > <= >=", &tokenEnd); + if (y1 < 0) break; + if (!parserGetX(&tokenEnd, variables, &x2)) break; + y2 = parserGetWord("goto call", &tokenEnd); + if (y2 < 0) break; + if (tokenEnd == NULL) break; + outputByte(bytecode, PARSE_IF); + outputWord(bytecode, x1); + outputByte(bytecode, y1); + outputWord(bytecode, x2); + outputByte(bytecode, y2); + outputWord(bytecode, labelGetValue(labels, tokenEnd)); + break; + case PARSE_LABEL: // Won't happen. break; @@ -528,7 +580,7 @@ int vecparser(char *programIn, VecByteCodeT *bytecode) { // Line (value),(value) to (value),(value) [to ...] points = NULL; if (!parserGetXY(&tokenEnd, variables, &p1.x, &p1.y)) break; - if (!parserGetWord("TO", &tokenEnd)) break; + if (parserGetWord("TO", &tokenEnd) < 0) 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; @@ -537,16 +589,14 @@ int vecparser(char *programIn, VecByteCodeT *bytecode) { arrput(points, p1); arrput(points, p2); isOkay = TRUE; - while (parserGetWord("TO", &tokenEnd)) { + while (parserGetWord("TO", &tokenEnd) >= 0) { if (!parserGetXY(&tokenEnd, variables, &p1.x, &p1.y)) { - // Error. Unwind array. - while (arrlen(points) > 0) { - arrdel(points, 0); - } - arrfree(points); + // Error. isOkay = FALSE; break; } + if (IS_NUMBER(p1.x)) if (p1.x < 0 || p1.x > 319) { isOkay = FALSE; break; } + if (IS_NUMBER(p1.y)) if (p1.y < 0 || p1.y > 199) { isOkay = FALSE; break; } arrput(points, p1); } if (isOkay) { @@ -556,11 +606,6 @@ int vecparser(char *programIn, VecByteCodeT *bytecode) { outputWord(bytecode, points[x1].x); outputWord(bytecode, points[x1].y); } - // Unwind array. - while (arrlen(points) > 0) { - arrdel(points, 0); - } - arrfree(points); lineOkay = TRUE; } break; @@ -572,7 +617,7 @@ int vecparser(char *programIn, VecByteCodeT *bytecode) { case PARSE_PALETTE: // Palette (short) AS (short),(short),(short) if (!parserGetX(&tokenEnd, variables, &x1)) break; - if (!parserGetWord("AS", &tokenEnd)) break; + if (parserGetWord("AS", &tokenEnd) < 0) 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; @@ -600,7 +645,7 @@ int vecparser(char *programIn, VecByteCodeT *bytecode) { case PARSE_RECTANGLE: // Rectangle (value),(value) to (value),(value) if (!parserGetXY(&tokenEnd, variables, &x1, &y1)) break; - if (!parserGetWord("TO", &tokenEnd)) break; + if (parserGetWord("TO", &tokenEnd) < 0) 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; @@ -621,8 +666,22 @@ int vecparser(char *programIn, VecByteCodeT *bytecode) { lineOkay = TRUE; break; + case PARSE_RETURN: + // Return + outputByte(bytecode, PARSE_RETURN); + lineOkay = TRUE; + break; + } // switch + // Unwind point array if needed. + if (points != NULL) { + while (arrlen(points) > 0) { + arrdel(points, 0); + } + arrfree(points); + } + // Stop looking for this keyword - we handled it. break; @@ -647,8 +706,7 @@ int vecparser(char *programIn, VecByteCodeT *bytecode) { } // Move to next line. - line = strtoke(NULL, "\n", &lineEnd); - while (line != NULL && (line[0] == ' ' || line[0] == '\t')) line++; + line = parserGetNextLine(NULL, &lineEnd); lineNumber++; } // read program line diff --git a/src/vector.c b/src/vector.c index 2ccdd02..0f29662 100644 --- a/src/vector.c +++ b/src/vector.c @@ -319,6 +319,8 @@ static void renderBytecode(VecByteCodeT *bytecode, VectorDataT *self) { #define GET_BYTE (bytecode->bytes[index++]) #define GET_WORD getWord(bytecode, &index) + printf("-----------------------------------\n"); + self->variables = NULL; while (index < bytecode->length) {