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/array.c
|
||||||
src/draw.c
|
src/draw.c
|
||||||
src/image.c
|
src/image.c
|
||||||
|
src/vecparse.c
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(${CMAKE_PROJECT_NAME} ${SOURCE_FILES})
|
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 "utils.h"
|
||||||
#include "draw.h"
|
#include "draw.h"
|
||||||
#include "image.h"
|
#include "image.h"
|
||||||
|
#include "vecparse.h"
|
||||||
|
|
||||||
|
|
||||||
#define SSM(m, w, l) scintilla_send_message(self->sci, m, w, l)
|
#define SSM(m, w, l) scintilla_send_message(self->sci, m, w, l)
|
||||||
|
@ -44,13 +45,6 @@
|
||||||
#define PREVIEW_HEIGHT 400
|
#define PREVIEW_HEIGHT 400
|
||||||
|
|
||||||
|
|
||||||
enum PassE {
|
|
||||||
PASS_DRAW,
|
|
||||||
PASS_GENERATE
|
|
||||||
};
|
|
||||||
typedef enum PassE PassT;
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct VectorDataS {
|
typedef struct VectorDataS {
|
||||||
WindowDataT windowData;
|
WindowDataT windowData;
|
||||||
GtkWidget *drawVectorImage;
|
GtkWidget *drawVectorImage;
|
||||||
|
@ -68,18 +62,6 @@ typedef struct VectorDataS {
|
||||||
} VectorDataT;
|
} 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;
|
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 editorVectorNotify(GtkWidget *sciWidget, gint ctrlID, struct SCNotification *notifyData, gpointer userData);
|
||||||
EVENT void fileVectorTraceImageFileSet(GtkWidget *object, gpointer userData);
|
EVENT void fileVectorTraceImageFileSet(GtkWidget *object, gpointer userData);
|
||||||
EVENT void menuVectorFileClose(GtkWidget *object, gpointer userData);
|
EVENT void menuVectorFileClose(GtkWidget *object, gpointer userData);
|
||||||
static gboolean parseBox(PassT pass, char **tokenEnd, VectorDataT *self);
|
static void renderBytecode(VecByteCodeT *bytecode, 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);
|
|
||||||
EVENT void scaleVectorTraceImageValueChanged(GtkWidget *object, gpointer userData);
|
EVENT void scaleVectorTraceImageValueChanged(GtkWidget *object, gpointer userData);
|
||||||
EVENT gboolean winVectorClose(GtkWidget *object, gpointer userData);
|
EVENT gboolean winVectorClose(GtkWidget *object, gpointer userData);
|
||||||
static void winVectorDelete(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) {
|
EVENT void editorVectorNotify(GtkWidget *sciWidget, gint ctrlID, struct SCNotification *notifyData, gpointer userData) {
|
||||||
|
|
||||||
VectorDataT *self = (VectorDataT *)userData;
|
VectorDataT *self = (VectorDataT *)userData;
|
||||||
int lineNumber = (int)SSM(SCI_LINEFROMPOSITION, (uptr_t)notifyData->position, (sptr_t)0);
|
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)sciWidget;
|
||||||
(void)ctrlID;
|
(void)ctrlID;
|
||||||
|
@ -194,10 +162,36 @@ EVENT void editorVectorNotify(GtkWidget *sciWidget, gint ctrlID, struct SCNotifi
|
||||||
switch (notifyData->nmhdr.code) {
|
switch (notifyData->nmhdr.code) {
|
||||||
case SCN_MODIFIED:
|
case SCN_MODIFIED:
|
||||||
if (notifyData->modificationType & SC_MOD_INSERTTEXT || notifyData->modificationType & SC_MOD_DELETETEXT) {
|
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.
|
// Parse code.
|
||||||
parser(PASS_DRAW, userData);
|
byteCode.bytes = NULL;
|
||||||
// Refresh widget.
|
byteCode.length = 0;
|
||||||
gtk_widget_queue_draw(self->drawVectorImage);
|
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.
|
// Mark text dirty. SCN_SAVEPOINTLEFT isn't being reliable.
|
||||||
self->windowData.isDirty = TRUE;
|
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 x1;
|
||||||
int y1;
|
int y1;
|
||||||
int x2;
|
int x2;
|
||||||
int y2;
|
int y2;
|
||||||
|
int count;
|
||||||
// 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 i;
|
int i;
|
||||||
int r;
|
|
||||||
int g;
|
|
||||||
int b;
|
|
||||||
|
|
||||||
if (!parserGetX(tokenEnd, &i)) return FALSE;
|
#define GET_BYTE (bytecode->bytes[index++])
|
||||||
if (!parserGetWord("AS", tokenEnd)) return FALSE;
|
#define GET_WORD ((bytecode->bytes[index++] << 8) + bytecode->bytes[index++])
|
||||||
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;
|
|
||||||
|
|
||||||
switch (pass) {
|
while (index < bytecode->length) {
|
||||||
case PASS_DRAW:
|
switch (bytecode->bytes[index++]) {
|
||||||
jlPaletteSet(self->jlc, i, r, g, b);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PASS_GENERATE:
|
case PARSE_NONE:
|
||||||
break;
|
// 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) {
|
case PARSE_CLEAR:
|
||||||
int x1;
|
printf("Clear\n");
|
||||||
int y1;
|
jlDrawClear(self->jlc);
|
||||||
|
break;
|
||||||
|
|
||||||
// Plot (value),(value)
|
case PARSE_COLOR:
|
||||||
if (!parserGetXY(tokenEnd, &x1, &y1)) return FALSE;
|
x1 = GET_BYTE;
|
||||||
if (x1 < 0 || x1 > 319) return FALSE;
|
printf("Color %d\n", x1);
|
||||||
if (y1 < 0 || y1 > 199) return FALSE;
|
jlDrawColorSet(self->jlc, x1);
|
||||||
|
break;
|
||||||
|
|
||||||
switch (pass) {
|
case PARSE_COMMENT:
|
||||||
case PASS_DRAW:
|
// Nothing to do!
|
||||||
jlDrawPixelSet(self->jlc, x1, y1);
|
break;
|
||||||
break;
|
|
||||||
|
|
||||||
case PASS_GENERATE:
|
case PARSE_ELLIPSE:
|
||||||
break;
|
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;
|
case PARSE_FILL:
|
||||||
}
|
x1 = GET_WORD;
|
||||||
|
y1 = GET_WORD;
|
||||||
|
x2 = GET_BYTE;
|
||||||
static gboolean parseRectangle(PassT pass, char **tokenEnd, VectorDataT *self) {
|
if (x2 > 15) {
|
||||||
int x1;
|
printf("Fill %d,%d\n", x1, y1);
|
||||||
int y1;
|
jlDrawFill(self->jlc, x1, y1);
|
||||||
int x2;
|
} else {
|
||||||
int y2;
|
printf("Fill %d,%d to %d\n", x1, y1, x2);
|
||||||
|
jlDrawFillTo(self->jlc, x1, y1, x2);
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
break;
|
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.
|
case PARSE_LINE:
|
||||||
free(code);
|
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;
|
||||||
|
|
||||||
/*
|
case PARSE_PALETTE:
|
||||||
* (value) is a 16-bit integer. Since we only need a fraction of the
|
x1 = GET_BYTE;
|
||||||
* possible values provided by this, we steal a couple bits for our
|
x2 = GET_BYTE;
|
||||||
* own use. All values are stored without messing with 2's complement.
|
y1 = GET_BYTE;
|
||||||
*
|
y2 = GET_BYTE;
|
||||||
* Type Negative Value
|
printf("Palette %d as %d,%d,%d\n", x1, x2, y1, y2);
|
||||||
* \ /__________/_
|
jlPaletteSet(self->jlc, x1, x2, y1, y2);
|
||||||
* \ // \
|
break;
|
||||||
* 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_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) {
|
case PARSE_RECTANGLE:
|
||||||
char *value;
|
x1 = GET_WORD;
|
||||||
char *endPtr = NULL;
|
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.
|
case PARSE_RESET:
|
||||||
//***TODO*** Variable support.
|
printf("Reset\n");
|
||||||
|
jlPaletteDefault(self->jlc);
|
||||||
|
jlDrawColorSet(self->jlc, 0);
|
||||||
|
jlDrawClear(self->jlc);
|
||||||
|
jlDrawColorSet(self->jlc, 15);
|
||||||
|
break;
|
||||||
|
|
||||||
value = strtok_r(token, ",", valueEnd);
|
} // switch
|
||||||
if (value == NULL) return FALSE;
|
} // while
|
||||||
errno = 0; endPtr = NULL;
|
|
||||||
*x = (int)strtol(value, &endPtr, 10);
|
|
||||||
if (errno != 0) return FALSE;
|
|
||||||
|
|
||||||
return TRUE;
|
// Refresh widget.
|
||||||
}
|
gtk_widget_queue_draw(self->drawVectorImage);
|
||||||
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue