From ffe9b5224d611d68166523e74889d7c33b943805 Mon Sep 17 00:00:00 2001 From: Scott Duensing Date: Wed, 30 Nov 2022 19:14:39 -0600 Subject: [PATCH] Parser generating bytecode and renderer interpreting it. --- CMakeLists.txt | 1 + include/vecparse.h | 61 +++++ src/vecparse.c | 425 ++++++++++++++++++++++++++++++ src/vector.c | 626 ++++++++++----------------------------------- 4 files changed, 626 insertions(+), 487 deletions(-) create mode 100644 include/vecparse.h create mode 100644 src/vecparse.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 599139c..8b4101f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,7 @@ set(SOURCE_FILES src/array.c src/draw.c src/image.c + src/vecparse.c ) add_executable(${CMAKE_PROJECT_NAME} ${SOURCE_FILES}) diff --git a/include/vecparse.h b/include/vecparse.h new file mode 100644 index 0000000..71f79e1 --- /dev/null +++ b/include/vecparse.h @@ -0,0 +1,61 @@ +/* + * JoeyDev + * Copyright (C) 2018-2023 Scott Duensing + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. +*/ + + +#ifndef VECPARSE_H +#define VECPARSE_H + + +enum ParserKeywordE { + PARSE_NONE = 0, + PARSE_BOX, + PARSE_CIRCLE, + PARSE_CLEAR, + PARSE_COLOR, + PARSE_COMMENT, + PARSE_ELLIPSE, + PARSE_FILL, + PARSE_LINE, + PARSE_PALETTE, + PARSE_PLOT, + PARSE_RECTANGLE, + PARSE_RESET +}; +typedef enum ParserKeywordE ParserKeywordT; + + +typedef struct PointS { + int x; + int y; +} PointT; + + +typedef struct VecByteCodeS { + unsigned char *bytes; + int length; + int bufferSize; +} VecByteCodeT; + + +int vecparser(char *programIn, VecByteCodeT *bytecode); + + +#endif // VECPARSE_H diff --git a/src/vecparse.c b/src/vecparse.c new file mode 100644 index 0000000..8d6115e --- /dev/null +++ b/src/vecparse.c @@ -0,0 +1,425 @@ +/* + * JoeyDev + * Copyright (C) 2018-2023 Scott Duensing + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. +*/ + + +#include "common.h" +#include "vecparse.h" + + +typedef struct CommandsS { + char *command; + ParserKeywordT keyword; +} CommandsT; + + +static void ensureBufferSize(VecByteCodeT *bytecode, int needed); +static void outputByte(VecByteCodeT *bytecode, unsigned char byte); +static void outputWord(VecByteCodeT *bytecode, unsigned short word); +static gboolean parserGetNextValue(char *token, char **valueEnd, int *x); +static gboolean parserGetWord(char *word, char **tokenEnd); +static gboolean parserGetX(char **tokenEnd, int *x); +static gboolean parserGetXY(char **tokenEnd, int *x, int *y); +static gboolean parserGetXYZ(char **tokenEnd, int *x, int *y, int *z); + + +static void ensureBufferSize(VecByteCodeT *bytecode, int needed) { + unsigned char *temp = NULL; + + if (bytecode->bufferSize < bytecode->length + needed) { + bytecode->bufferSize += 1024; + temp = realloc(bytecode->bytes, bytecode->bufferSize); + if (temp == NULL) { + //***TODO*** Something bad happened. + } else { + bytecode->bytes = temp; + } + } +} + + +static void outputByte(VecByteCodeT *bytecode, unsigned char byte) { + ensureBufferSize(bytecode, 1); + bytecode->bytes[bytecode->length++] = byte; +} + + +static void outputWord(VecByteCodeT *bytecode, unsigned short word) { + ensureBufferSize(bytecode, 2); + bytecode->bytes[bytecode->length++] = (word & 0xFF00) >> 8; + bytecode->bytes[bytecode->length++] = word & 0x00FF; +} + + +static gboolean parserGetNextValue(char *token, char **valueEnd, int *x) { + char *value; + char *endPtr = NULL; + + // Return next value in a comma separated list. + //***TODO*** Variable support. + + value = strtok_r(token, ",", valueEnd); + if (value == NULL) return FALSE; + errno = 0; endPtr = NULL; + *x = (int)strtol(value, &endPtr, 10); + if (errno != 0 || *endPtr != 0) return FALSE; + + return TRUE; +} + + +static gboolean parserGetWord(char *word, char **tokenEnd) { + char *token; + + // Is this token the "WORD"? + + token = strtok_r(NULL, " ", tokenEnd); + if (token == NULL) return FALSE; + if (strcasecmp(token, word) != 0) return FALSE; + + return TRUE; +} + + +static gboolean parserGetX(char **tokenEnd, int *x) { + char *value; + char *endPtr = NULL; + + // Return single value. + //***TODO*** Variable support. + + value = strtok_r(NULL, " ", tokenEnd); + if (value == NULL) return FALSE; + errno = 0; endPtr = NULL; + *x = (int)strtol(value, &endPtr, 10); + if (errno != 0 || *endPtr != 0) return FALSE; + + return TRUE; +} + + +static gboolean parserGetXY(char **tokenEnd, int *x, int *y) { + char *token; + char *valueEnd; + + // Return values of X,Y pair. + + token = strtok_r(NULL, " ", tokenEnd); + if (token == NULL) return FALSE; + + if (!parserGetNextValue(token, &valueEnd, x)) return FALSE; + if (!parserGetNextValue(NULL, &valueEnd, y)) return FALSE; + + return TRUE; +} + + +static gboolean parserGetXYZ(char **tokenEnd, int *x, int *y, int *z) { + char *token; + char *valueEnd; + + // Return values of X,Y pair. + + token = strtok_r(NULL, " ", tokenEnd); + if (token == NULL) return FALSE; + + if (!parserGetNextValue(token, &valueEnd, x)) return FALSE; + if (!parserGetNextValue(NULL, &valueEnd, y)) return FALSE; + if (!parserGetNextValue(NULL, &valueEnd, z)) return FALSE; + + return TRUE; +} + + +int vecparser(char *programIn, VecByteCodeT *bytecode) { + int command; + char *line; + char *lineEnd; + gboolean lineOkay; + gboolean isOkay; + char *token; + char *tokenEnd; + int lineNumber; + int x1; + int y1; + int x2; + int y2; + PointT p1; + PointT p2; + PointT *points = NULL; + int result = -1; // Returns -1 on success or line number of first error. + CommandsT commands[] = { + { "BOX", PARSE_BOX }, + { "CIRCLE", PARSE_CIRCLE }, + { "CLEAR", PARSE_CLEAR }, + { "COLOR", PARSE_COLOR }, + { "//", PARSE_COMMENT }, + { "ELLIPSE", PARSE_ELLIPSE }, + { "FILL", PARSE_FILL }, + { "LINE", PARSE_LINE }, + { "PALETTE", PARSE_PALETTE }, + { "PLOT", PARSE_PLOT }, + { "RECTANGLE", PARSE_RECTANGLE }, + { "RESET", PARSE_RESET }, + { NULL, PARSE_NONE } + }; + + // Parse code. + lineNumber = 0; + line = strtok_r(programIn, "\n", &lineEnd); + while (line != NULL) { + + // Get the first token on the line. + token = strtok_r(line, " ", &tokenEnd); + + // Is this something we care about? + command = 0; + lineOkay = FALSE; + while (commands[command].command) { + if (strcasecmp(commands[command].command, token) == 0) { + + // Yep! Gather arguments and generate bytecode. + switch (commands[command].keyword) { + + case PARSE_NONE: + // Won't happen, but silences an error. + break; + + case PARSE_BOX: + // Box (value),(value) to (value),(value) + if (!parserGetXY(&tokenEnd, &x1, &y1)) break; + if (!parserGetWord("TO", &tokenEnd)) break; + if (!parserGetXY(&tokenEnd, &x2, &y2)) break; + if (x2 < x1 || x1 < 0 || x2 < 0 || x1 > 319 || x2 > 319) break; + if (y2 < y1 || y1 < 0 || y2 < 0 || y1 > 199 || y2 > 199) break; + outputByte(bytecode, PARSE_BOX); + outputWord(bytecode, x1); + outputWord(bytecode, y1); + outputWord(bytecode, x2); + outputWord(bytecode, y2); + lineOkay = TRUE; + break; + + case PARSE_CIRCLE: + // Circle (value) at (value),(value) + if (!parserGetX(&tokenEnd, &y2)) break; + if (!parserGetWord("AT", &tokenEnd)) break; + if (!parserGetXY(&tokenEnd, &x1, &y1)) break; + if (x1 < 0 || x1 > 319) break; + if (y1 < 0 || y1 > 199) break; + outputByte(bytecode, PARSE_CIRCLE); + outputWord(bytecode, y2); + outputWord(bytecode, x1); + outputWord(bytecode, y1); + lineOkay = TRUE; + break; + + case PARSE_CLEAR: + outputByte(bytecode, PARSE_CLEAR); + lineOkay = TRUE; + break; + + case PARSE_COLOR: + // Color (short) + if (!parserGetX(&tokenEnd, &x1)) break; + if (x1 < 0 || x1 > 15) break; + outputByte(bytecode, PARSE_COLOR); + outputByte(bytecode, x1); + 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_ELLIPSE: + // Ellipse (value),(value) to (value),(value) + if (!parserGetXY(&tokenEnd, &x1, &y1)) break; + if (!parserGetWord("TO", &tokenEnd)) break; + if (!parserGetXY(&tokenEnd, &x2, &y2)) break; + if (x2 < x1 || x1 < 0 || x2 < 0 || x1 > 319 || x2 > 319) break; + if (y2 < y1 || y1 < 0 || y2 < 0 || y1 > 199 || y2 > 199) break; + outputByte(bytecode, PARSE_ELLIPSE); + outputWord(bytecode, x1); + outputWord(bytecode, y1); + outputWord(bytecode, x2); + outputWord(bytecode, y2); + lineOkay = TRUE; + break; + + case PARSE_FILL: + // Fill (value),(value) {to (value} + if (!parserGetXY(&tokenEnd, &x1, &y1)) break; + if (x1 < 0 || x1 > 319) break; + 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, &x2)) break; + if (x2 < 0 || x2 > 15) break; + } + outputByte(bytecode, PARSE_FILL); + outputWord(bytecode, x1); + outputWord(bytecode, y1); + outputByte(bytecode, x2); + lineOkay = TRUE; + break; + + case PARSE_LINE: + // Line (value),(value) to (value),(value) [to ...] + points = NULL; + if (!parserGetXY(&tokenEnd, &p1.x, &p1.y)) break; + if (!parserGetWord("TO", &tokenEnd)) break; + if (!parserGetXY(&tokenEnd, &p2.x, &p2.y)) break; + if (p1.x < 0 || p1.x > 319) break; + if (p1.y < 0 || p1.y > 199) break; + if (p2.x < 0 || p2.x > 319) break; + if (p2.y < 0 || p2.y > 199) break; + arrput(points, p1); + arrput(points, p2); + isOkay = TRUE; + while (parserGetWord("TO", &tokenEnd)) { + if (!parserGetXY(&tokenEnd, &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 0) { + arrdel(points, 0); + } + arrfree(points); + lineOkay = TRUE; + } + break; + + case PARSE_PALETTE: + // Palette (short) AS (short),(short),(short) + if (!parserGetX(&tokenEnd, &x1)) break; + if (!parserGetWord("AS", &tokenEnd)) break; + if (!parserGetXYZ(&tokenEnd, &x2, &y1, &y2)) break; + if (x1 < 0 || x1 > 15) break; + if (x2 < 0 || x2 > 15) break; + if (y1 < 0 || y1 > 15) break; + 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_PLOT: + // Plot (value),(value) + if (!parserGetXY(&tokenEnd, &x1, &y1)) break; + if (x1 < 0 || x1 > 319) break; + if (y1 < 0 || y1 > 199) break; + outputByte(bytecode, PARSE_PLOT); + outputWord(bytecode, x1); + outputWord(bytecode, y1); + lineOkay = TRUE; + break; + + case PARSE_RECTANGLE: + // Rectangle (value),(value) to (value),(value) + if (!parserGetXY(&tokenEnd, &x1, &y1)) break; + if (!parserGetWord("TO", &tokenEnd)) break; + if (!parserGetXY(&tokenEnd, &x2, &y2)) break; + if (x2 < x1 || x1 < 0 || x2 < 0 || x1 > 319 || x2 > 319) break; + if (y2 < y1 || y1 < 0 || y2 < 0 || y1 > 199 || y2 > 199) break; + outputByte(bytecode, PARSE_RECTANGLE); + outputWord(bytecode, x1); + outputWord(bytecode, y1); + outputWord(bytecode, x2); + outputWord(bytecode, y2); + lineOkay = TRUE; + break; + + case PARSE_RESET: + outputByte(bytecode, PARSE_RESET); + lineOkay = TRUE; + break; + + } // switch + + // Stop looking for this command - we handled it. + break; + + } else { + // Keep looking until we find this command or run out of commands. + command++; + } // command match + + } // loop over commands + + // Is everything still okay? + if (!lineOkay) { + // Nope - error. + result = lineNumber; + break; + } + + // Move to next line. + line = strtok_r(NULL, "\n", &lineEnd); + lineNumber++; + } // read program line + + /* + * (value) is a 16-bit integer. Since we only need a fraction of the + * possible values provided by this, we steal a couple bits for our + * own use. All values are stored without messing with 2's complement. + * + * Type Negative Value + * \ /__________/_ + * \ // \ + * tnvvvvvvvvvvvvvv + * + * So with this scheme we can store values from -16383 to 16383 (yes, + * zero is represented twice). + * + * The Type bit determines if the value stored is a literal value or a + * reference to a variable in the variable table. + * + * (short) is a simplified version used for colors. It is always positive + * and has a range from 0 to 127 with Type being the MSb. This effectively + * limits the number of available variables to 128. + * + */ + + return result; +} diff --git a/src/vector.c b/src/vector.c index 2ba7148..41841fb 100644 --- a/src/vector.c +++ b/src/vector.c @@ -34,6 +34,7 @@ #include "utils.h" #include "draw.h" #include "image.h" +#include "vecparse.h" #define SSM(m, w, l) scintilla_send_message(self->sci, m, w, l) @@ -44,13 +45,6 @@ #define PREVIEW_HEIGHT 400 -enum PassE { - PASS_DRAW, - PASS_GENERATE -}; -typedef enum PassE PassT; - - typedef struct VectorDataS { WindowDataT windowData; GtkWidget *drawVectorImage; @@ -68,18 +62,6 @@ typedef struct VectorDataS { } VectorDataT; -typedef struct CommandsS { - char *command; - gboolean (*parserFunction)(PassT pass, char **tokenEnd, VectorDataT *self); -} CommandsT; - - -typedef struct PointS { - int x; - int y; -} PointT; - - static int _nextEditorId = 0; @@ -88,24 +70,7 @@ EVENT void drawVectorImageClick(GtkWidget *object, GdkEventButton *event, g EVENT void editorVectorNotify(GtkWidget *sciWidget, gint ctrlID, struct SCNotification *notifyData, gpointer userData); EVENT void fileVectorTraceImageFileSet(GtkWidget *object, gpointer userData); EVENT void menuVectorFileClose(GtkWidget *object, gpointer userData); -static gboolean parseBox(PassT pass, char **tokenEnd, VectorDataT *self); -static gboolean parseCircle(PassT pass, char **tokenEnd, VectorDataT *self); -static gboolean parseClear(PassT pass, char **tokenEnd, VectorDataT *self); -static gboolean parseColor(PassT pass, char **tokenEnd, VectorDataT *self); -static gboolean parseComment(PassT pass, char **tokenEnd, VectorDataT *self); -static gboolean parseEllipse(PassT pass, char **tokenEnd, VectorDataT *self); -static gboolean parseFill(PassT pass, char **tokenEnd, VectorDataT *self); -static gboolean parseLine(PassT pass, char **tokenEnd, VectorDataT *self); -static gboolean parsePalette(PassT pass, char **tokenEnd, VectorDataT *self); -static gboolean parsePlot(PassT pass, char **tokenEnd, VectorDataT *self); -static gboolean parseRectangle(PassT pass, char **tokenEnd, VectorDataT *self); -static gboolean parseReset(PassT pass, char **tokenEnd, VectorDataT *self); -static void parser(PassT pass, gpointer userData); -static gboolean parserGetNextValue(char *token, char **valueEnd, int *x); -static gboolean parserGetWord(char *word, char **tokenEnd); -static gboolean parserGetX(char **tokenEnd, int *x); -static gboolean parserGetXY(char **tokenEnd, int *x, int *y); -static gboolean parserGetXYZ(char **tokenEnd, int *x, int *y, int *z); +static void renderBytecode(VecByteCodeT *bytecode, VectorDataT *self); EVENT void scaleVectorTraceImageValueChanged(GtkWidget *object, gpointer userData); EVENT gboolean winVectorClose(GtkWidget *object, gpointer userData); static void winVectorDelete(gpointer userData); @@ -183,8 +148,11 @@ EVENT void drawVectorImageClick(GtkWidget *object, GdkEventButton *event, gpoint EVENT void editorVectorNotify(GtkWidget *sciWidget, gint ctrlID, struct SCNotification *notifyData, gpointer userData) { - VectorDataT *self = (VectorDataT *)userData; - int lineNumber = (int)SSM(SCI_LINEFROMPOSITION, (uptr_t)notifyData->position, (sptr_t)0); + VectorDataT *self = (VectorDataT *)userData; + int lineNumber = (int)SSM(SCI_LINEFROMPOSITION, (uptr_t)notifyData->position, (sptr_t)0); + int length = SSM(SCI_GETLENGTH, 0, 0); + char *code; + VecByteCodeT byteCode; (void)sciWidget; (void)ctrlID; @@ -194,10 +162,36 @@ EVENT void editorVectorNotify(GtkWidget *sciWidget, gint ctrlID, struct SCNotifi switch (notifyData->nmhdr.code) { case SCN_MODIFIED: if (notifyData->modificationType & SC_MOD_INSERTTEXT || notifyData->modificationType & SC_MOD_DELETETEXT) { + // Allocate space to fetch code from editor. + code = (char *)malloc(length + 1); + if (!code) return; + // Clear error markers. + SSM(SCI_MARKERDELETEALL, MARKER_ERROR_ARROW, 0); + SSM(SCI_MARKERDELETEALL, MARKER_ERROR_HIGHLIGHT, 0); + // Fetch code. + SSM(SCI_GETTEXT, length, (sptr_t)code); // Parse code. - parser(PASS_DRAW, userData); - // Refresh widget. - gtk_widget_queue_draw(self->drawVectorImage); + byteCode.bytes = NULL; + byteCode.length = 0; + byteCode.bufferSize = 0; + lineNumber = vecparser(code, &byteCode); + if (lineNumber >= 0) { + // Mark lines that fail to parse. + SSM(SCI_MARKERADD, lineNumber, MARKER_ERROR_ARROW); + SSM(SCI_MARKERADD, lineNumber, MARKER_ERROR_HIGHLIGHT); + } else { + // All good! + renderBytecode(&byteCode, self); + //***DEBUG*** + FILE *out = fopen("bytecode.bin", "wb"); + fwrite(byteCode.bytes, byteCode.length, 1, out); + fclose(out); + } + + // Release bytecode. + if (byteCode.bytes != NULL) DEL(byteCode.bytes); + // Release code. + DEL(code); // Mark text dirty. SCN_SAVEPOINTLEFT isn't being reliable. self->windowData.isDirty = TRUE; } @@ -280,475 +274,133 @@ EVENT void menuVectorFileClose(GtkWidget *object, gpointer userData) { } -static gboolean parseBox(PassT pass, char **tokenEnd, VectorDataT *self) { +static void renderBytecode(VecByteCodeT *bytecode, VectorDataT *self) { + int index = 0; int x1; int y1; int x2; int y2; - - // Box (value),(value) to (value),(value) - if (!parserGetXY(tokenEnd, &x1, &y1)) return FALSE; - if (!parserGetWord("TO", tokenEnd)) return FALSE; - if (!parserGetXY(tokenEnd, &x2, &y2)) return FALSE; - if (x2 < x1 || x1 < 0 || x2 < 0 || x1 > 319 || x2 > 319) return FALSE; - if (y2 < y1 || y1 < 0 || y2 < 0 || y1 > 199 || y2 > 199) return FALSE; - - switch (pass) { - case PASS_DRAW: - jlDrawBox(self->jlc, x1, y1, x2, y2); - break; - - case PASS_GENERATE: - break; - } - - return TRUE; -} - - -static gboolean parseCircle(PassT pass, char **tokenEnd, VectorDataT *self) { - int r; - int x1; - int y1; - - // Circle (value) at (value),(value) - if (!parserGetX(tokenEnd, &r)) return FALSE; - if (!parserGetWord("AT", tokenEnd)) return FALSE; - if (!parserGetXY(tokenEnd, &x1, &y1)) return FALSE; - if (x1 < 0 || x1 > 319) return FALSE; - if (y1 < 0 || y1 > 199) return FALSE; - - switch (pass) { - case PASS_DRAW: - jlDrawCircle(self->jlc, x1, y1, r); - break; - - case PASS_GENERATE: - break; - } - - return TRUE; -} - - -static gboolean parseClear(PassT pass, char **tokenEnd, VectorDataT *self) { - - switch (pass) { - case PASS_DRAW: - jlDrawClear(self->jlc); - break; - - case PASS_GENERATE: - break; - } - - return TRUE; -} - - -static gboolean parseColor(PassT pass, char **tokenEnd, VectorDataT *self) { - int c; - - // Color (short) - if (!parserGetX(tokenEnd, &c)) return FALSE; - if (c < 0 || c > 15) return FALSE; - - switch (pass) { - case PASS_DRAW: - jlDrawColorSet(self->jlc, c); - break; - - case PASS_GENERATE: - break; - } - - return TRUE; -} - - -static gboolean parseComment(PassT pass, char **tokenEnd, VectorDataT *self) { - char *token = (char *)1; // Just can't be NULL. - - // Eat the rest of the line. - while (token != NULL) { - token = strtok_r(NULL, " ", tokenEnd); - } - - return TRUE; -} - - -static gboolean parseEllipse(PassT pass, char **tokenEnd, VectorDataT *self) { - int x1; - int y1; - int x2; - int y2; - - // Ellipse (value),(value) to (value),(value) - if (!parserGetXY(tokenEnd, &x1, &y1)) return FALSE; - if (!parserGetWord("TO", tokenEnd)) return FALSE; - if (!parserGetXY(tokenEnd, &x2, &y2)) return FALSE; - if (x2 < x1 || x1 < 0 || x2 < 0 || x1 > 319 || x2 > 319) return FALSE; - if (y2 < y1 || y1 < 0 || y2 < 0 || y1 > 199 || y2 > 199) return FALSE; - - switch (pass) { - case PASS_DRAW: - jlDrawEllipse(self->jlc, x1, y1, x2, y2); - break; - - case PASS_GENERATE: - break; - } - - return TRUE; -} - - -static gboolean parseFill(PassT pass, char **tokenEnd, VectorDataT *self) { - int c = 16; - int x1; - int y1; - - // Fill (value),(value) {to (value} - if (!parserGetXY(tokenEnd, &x1, &y1)) return FALSE; - if (x1 < 0 || x1 > 319) return FALSE; - if (y1 < 0 || y1 > 199) return FALSE; - // Do they want to fill to a certain color? Or over the current color? - if (parserGetWord("TO", tokenEnd)) { - if (!parserGetX(tokenEnd, &c)) return FALSE; - if (c < 0 || c > 15) return FALSE; - } - - switch (pass) { - case PASS_DRAW: - if (c > 15) { - jlDrawFill(self->jlc, x1, y1); - } else { - jlDrawFillTo(self->jlc, x1, y1, c); - } - break; - - case PASS_GENERATE: - break; - } - - return TRUE; -} - - -static gboolean parseLine(PassT pass, char **tokenEnd, VectorDataT *self) { - int x; - PointT p1; - PointT p2; - PointT *points = NULL; - - // Line (value),(value) to (value),(value) [to ...] - if (!parserGetXY(tokenEnd, &p1.x, &p1.y)) return FALSE; - if (!parserGetWord("TO", tokenEnd)) return FALSE; - if (!parserGetXY(tokenEnd, &p2.x, &p2.y)) return FALSE; - if (p1.x < 0 || p1.x > 319) return FALSE; - if (p1.y < 0 || p1.y > 199) return FALSE; - if (p2.x < 0 || p2.x > 319) return FALSE; - if (p2.y < 0 || p2.y > 199) return FALSE; - arrput(points, p1); - arrput(points, p2); - while (parserGetWord("TO", tokenEnd)) { - if (!parserGetXY(tokenEnd, &p1.x, &p1.y)) { - // Error. Unwind array. - while (arrlen(points) > 0) arrdel(points, 0); - arrfree(points); - return FALSE; - } - arrput(points, p1); - } - - switch (pass) { - case PASS_DRAW: - for (x=0; xjlc, points[x].x, points[x].y, points[x+1].x, points[x+1].y); - } - break; - - case PASS_GENERATE: - break; - } - - // Unwind array. - while (arrlen(points) > 0) arrdel(points, 0); - arrfree(points); - - return TRUE; -} - - -static gboolean parsePalette(PassT pass, char **tokenEnd, VectorDataT *self) { + int count; int i; - int r; - int g; - int b; - if (!parserGetX(tokenEnd, &i)) return FALSE; - if (!parserGetWord("AS", tokenEnd)) return FALSE; - if (!parserGetXYZ(tokenEnd, &r, &g, &b)) return FALSE; - if (i < 0 || i > 15) return FALSE; - if (r < 0 || r > 15) return FALSE; - if (g < 0 || g > 15) return FALSE; - if (b < 0 || b > 15) return FALSE; +#define GET_BYTE (bytecode->bytes[index++]) +#define GET_WORD ((bytecode->bytes[index++] << 8) + bytecode->bytes[index++]) - switch (pass) { - case PASS_DRAW: - jlPaletteSet(self->jlc, i, r, g, b); - break; + while (index < bytecode->length) { + switch (bytecode->bytes[index++]) { - case PASS_GENERATE: - break; - } + case PARSE_NONE: + // Won't happen, but silences an error. + break; - return TRUE; -} + case PARSE_BOX: + x1 = GET_WORD; + y1 = GET_WORD; + x2 = GET_WORD; + y2 = GET_WORD; + printf("Box %d,%d to %d,%d\n", x1, y1, x2, y2); + jlDrawBox(self->jlc, x1, y1, x2, y2); + break; + case PARSE_CIRCLE: + y2 = GET_WORD; + x1 = GET_WORD; + y1 = GET_WORD; + printf("Circle %d at %d,%d\n", y2, x1, y1); + jlDrawCircle(self->jlc, x1, y1, y2); + break; -static gboolean parsePlot(PassT pass, char **tokenEnd, VectorDataT *self) { - int x1; - int y1; + case PARSE_CLEAR: + printf("Clear\n"); + jlDrawClear(self->jlc); + break; - // Plot (value),(value) - if (!parserGetXY(tokenEnd, &x1, &y1)) return FALSE; - if (x1 < 0 || x1 > 319) return FALSE; - if (y1 < 0 || y1 > 199) return FALSE; + case PARSE_COLOR: + x1 = GET_BYTE; + printf("Color %d\n", x1); + jlDrawColorSet(self->jlc, x1); + break; - switch (pass) { - case PASS_DRAW: - jlDrawPixelSet(self->jlc, x1, y1); - break; + case PARSE_COMMENT: + // Nothing to do! + break; - case PASS_GENERATE: - break; - } + case PARSE_ELLIPSE: + x1 = GET_WORD; + y1 = GET_WORD; + x2 = GET_WORD; + y2 = GET_WORD; + printf("Ellipse %d,%d to %d,%d\n", x1, y1, x2, y2); + jlDrawEllipse(self->jlc, x1, y1, x2, y2); + break; - return TRUE; -} - - -static gboolean parseRectangle(PassT pass, char **tokenEnd, VectorDataT *self) { - int x1; - int y1; - int x2; - int y2; - - // Rectangle (value),(value) to (value),(value) - if (!parserGetXY(tokenEnd, &x1, &y1)) return FALSE; - if (!parserGetWord("TO", tokenEnd)) return FALSE; - if (!parserGetXY(tokenEnd, &x2, &y2)) return FALSE; - if (x2 < x1 || x1 < 0 || x2 < 0 || x1 > 319 || x2 > 319) return FALSE; - if (y2 < y1 || y1 < 0 || y2 < 0 || y1 > 199 || y2 > 199) return FALSE; - - switch (pass) { - case PASS_DRAW: - jlDrawBoxFilled(self->jlc, x1, y1, x2, y2); - break; - - case PASS_GENERATE: - break; - } - - return TRUE; -} - - -static gboolean parseReset(PassT pass, char **tokenEnd, VectorDataT *self) { - // Reset - - // Reset draw context. - jlContextDel(&self->jlc); - self->jlc = jlContextNew(cairo_image_surface_get_data(self->surface)); - - switch (pass) { - case PASS_DRAW: - jlDrawColorSet(self->jlc, 0); - jlDrawClear(self->jlc); - jlDrawColorSet(self->jlc, 15); - break; - - case PASS_GENERATE: - break; - } - - return TRUE; -} - - -static void parser(PassT pass, gpointer userData) { - VectorDataT *self = (VectorDataT *)userData; - int length = SSM(SCI_GETLENGTH, 0, 0); - int x; - int lineNumber = 0; - char *code; - char *line; - char *lineEnd; - gboolean lineOkay; - char *token; - char *tokenEnd; - CommandsT commands[] = { - { "BOX", parseBox }, - { "CIRCLE", parseCircle }, - { "CLEAR", parseClear }, - { "COLOR", parseColor }, - { "//", parseComment }, - { "ELLIPSE", parseEllipse }, - { "FILL", parseFill }, - { "LINE", parseLine }, - { "PALETTE", parsePalette }, - { "PLOT", parsePlot }, - { "RECTANGLE", parseRectangle }, - { "RESET", parseReset }, - { NULL, NULL } - }; - - // Allocate space to fetch code from editor. - code = (char *)malloc(length + 1); - if (!code) return; - - // Fetch code. - SSM(SCI_GETTEXT, length, (sptr_t)code); - - if (pass == PASS_DRAW) { - // Clear error markers. - SSM(SCI_MARKERDELETEALL, MARKER_ERROR_ARROW, 0); - SSM(SCI_MARKERDELETEALL, MARKER_ERROR_HIGHLIGHT, 0); - } - - // Parse code. - line = strtok_r(code, "\n", &lineEnd); - while (line != NULL) { - // Get the first token on the line. - token = strtok_r(line, " ", &tokenEnd); - // Is this something we care about? - x = 0; - lineOkay = FALSE; - while (commands[x].command) { - if (strcasecmp(commands[x].command, token) == 0) { - if (commands[x].parserFunction(pass, &tokenEnd, self) == TRUE) { - lineOkay = TRUE; + case PARSE_FILL: + x1 = GET_WORD; + y1 = GET_WORD; + x2 = GET_BYTE; + if (x2 > 15) { + printf("Fill %d,%d\n", x1, y1); + jlDrawFill(self->jlc, x1, y1); + } else { + printf("Fill %d,%d to %d\n", x1, y1, x2); + jlDrawFillTo(self->jlc, x1, y1, x2); } break; - } - x++; - } - if (lineOkay == FALSE && pass == PASS_DRAW) { - // Mark lines that fail to parse. - SSM(SCI_MARKERADD, lineNumber, MARKER_ERROR_ARROW); - SSM(SCI_MARKERADD, lineNumber, MARKER_ERROR_HIGHLIGHT); - } - // Move to next line. - line = strtok_r(NULL, "\n", &lineEnd); - lineNumber++; - } - // Release code. - free(code); + case PARSE_LINE: + count = GET_WORD; + x1 = GET_WORD; + y1 = GET_WORD; + printf("Line %d,%d", x1, y1); + for (i=0; ijlc, x1, y1, x2, y2); + x1 = x2; + y1 = y2; + } + printf("\n"); + break; - /* - * (value) is a 16-bit integer. Since we only need a fraction of the - * possible values provided by this, we steal a couple bits for our - * own use. All values are stored without messing with 2's complement. - * - * Type Negative Value - * \ /__________/_ - * \ // \ - * tnvvvvvvvvvvvvvv - * - * So with this scheme we can store values from -16383 to 16383 (yes, - * zero is represented twice). - * - * The Type bit determines if the value stored is a literal value or a - * reference to a variable in the variable table. - * - * (short) is a simplified version used for colors. It is always positive - * and has a range from 0 to 127 with Type being the MSb. This effectively - * limits the number of available variables to 128. - * - */ -} + case PARSE_PALETTE: + x1 = GET_BYTE; + x2 = GET_BYTE; + y1 = GET_BYTE; + y2 = GET_BYTE; + printf("Palette %d as %d,%d,%d\n", x1, x2, y1, y2); + jlPaletteSet(self->jlc, x1, x2, y1, y2); + break; + case PARSE_PLOT: + x1 = GET_WORD; + y1 = GET_WORD; + printf("Plot %d,%d\n", x1, y1); + jlDrawPixelSet(self->jlc, x1, y1); + break; -static gboolean parserGetNextValue(char *token, char **valueEnd, int *x) { - char *value; - char *endPtr = NULL; + case PARSE_RECTANGLE: + x1 = GET_WORD; + y1 = GET_WORD; + x2 = GET_WORD; + y2 = GET_WORD; + printf("Rectangle %d,%d to %d,%d\n", x1, y1, x2, y2); + jlDrawBoxFilled(self->jlc, x1, y1, x2, y2); + break; - // Return next value in a comma separated list. - //***TODO*** Variable support. + case PARSE_RESET: + printf("Reset\n"); + jlPaletteDefault(self->jlc); + jlDrawColorSet(self->jlc, 0); + jlDrawClear(self->jlc); + jlDrawColorSet(self->jlc, 15); + break; - value = strtok_r(token, ",", valueEnd); - if (value == NULL) return FALSE; - errno = 0; endPtr = NULL; - *x = (int)strtol(value, &endPtr, 10); - if (errno != 0) return FALSE; + } // switch + } // while - return TRUE; -} - - -static gboolean parserGetWord(char *word, char **tokenEnd) { - char *token; - - // Is this token the "WORD"? - - token = strtok_r(NULL, " ", tokenEnd); - if (token == NULL) return FALSE; - if (strcasecmp(token, word) != 0) return FALSE; - - return TRUE; -} - - -static gboolean parserGetX(char **tokenEnd, int *x) { - char *value; - char *endPtr = NULL; - - // Return single value. - //***TODO*** Variable support. - - value = strtok_r(NULL, " ", tokenEnd); - if (value == NULL) return FALSE; - errno = 0; endPtr = NULL; - *x = (int)strtol(value, &endPtr, 10); - if (errno != 0) return FALSE; - - return TRUE; -} - - -static gboolean parserGetXY(char **tokenEnd, int *x, int *y) { - char *token; - char *valueEnd; - - // Return values of X,Y pair. - - token = strtok_r(NULL, " ", tokenEnd); - if (token == NULL) return FALSE; - - if (!parserGetNextValue(token, &valueEnd, x)) return FALSE; - if (!parserGetNextValue(NULL, &valueEnd, y)) return FALSE; - - return TRUE; -} - - -static gboolean parserGetXYZ(char **tokenEnd, int *x, int *y, int *z) { - char *token; - char *valueEnd; - - // Return values of X,Y pair. - - token = strtok_r(NULL, " ", tokenEnd); - if (token == NULL) return FALSE; - - if (!parserGetNextValue(token, &valueEnd, x)) return FALSE; - if (!parserGetNextValue(NULL, &valueEnd, y)) return FALSE; - if (!parserGetNextValue(NULL, &valueEnd, z)) return FALSE; - - return TRUE; + // Refresh widget. + gtk_widget_queue_draw(self->drawVectorImage); }