Parser generating bytecode and renderer interpreting it.
This commit is contained in:
parent
544b19da61
commit
ffe9b5224d
4 changed files with 626 additions and 487 deletions
|
@ -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})
|
||||
|
|
61
include/vecparse.h
Normal file
61
include/vecparse.h
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* JoeyDev
|
||||
* Copyright (C) 2018-2023 Scott Duensing <scott@kangaroopunch.com>
|
||||
*
|
||||
* 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
|
425
src/vecparse.c
Normal file
425
src/vecparse.c
Normal file
|
@ -0,0 +1,425 @@
|
|||
/*
|
||||
* JoeyDev
|
||||
* Copyright (C) 2018-2023 Scott Duensing <scott@kangaroopunch.com>
|
||||
*
|
||||
* 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<arrlen(points); x1++) {
|
||||
outputWord(bytecode, points[x1].x);
|
||||
outputWord(bytecode, points[x1].y);
|
||||
}
|
||||
// Unwind array.
|
||||
while (arrlen(points) > 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;
|
||||
}
|
626
src/vector.c
626
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; x<arrlen(points)-1; x++) {
|
||||
jlDrawLine(self->jlc, 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; i<count - 1; i++) {
|
||||
x2 = GET_WORD;
|
||||
y2 = GET_WORD;
|
||||
printf(" to %d,%d", x2, y2);
|
||||
jlDrawLine(self->jlc, 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);
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue