Start of variable support. Not working properly yet.

This commit is contained in:
Scott Duensing 2022-12-02 20:25:47 -06:00
parent ffe9b5224d
commit 06ca429d82
3 changed files with 592 additions and 256 deletions

View file

@ -37,10 +37,30 @@ enum ParserKeywordE {
PARSE_PALETTE, PARSE_PALETTE,
PARSE_PLOT, PARSE_PLOT,
PARSE_RECTANGLE, PARSE_RECTANGLE,
PARSE_RESET PARSE_RESET,
// After this are keywords for things that aren't commands but still need to emit bytecode.
PARSE_MATH,
PARSE_LABEL
}; };
typedef enum ParserKeywordE ParserKeywordT; typedef enum ParserKeywordE ParserKeywordT;
enum ParserMathE {
MATH_NONE = 0,
MATH_ASSIGN,
MATH_ADD,
MATH_SUBTRACT,
MATH_MULTIPLY,
MATH_DIVIDE,
MATH_MOD,
MATH_POW,
MATH_SQRT,
MATH_ABS,
MATH_COS,
MATH_SIN,
MATH_TAN
};
typedef enum ParserMathE ParserMathT;
typedef struct PointS { typedef struct PointS {
int x; int x;

View file

@ -20,24 +20,33 @@
*/ */
#include <errno.h>
#include "common.h" #include "common.h"
#include "vecparse.h" #include "vecparse.h"
typedef struct CommandsS { #define IS_NUMBER(n) (n & 0x8000 ? TRUE : FALSE)
typedef struct KeywordsS {
char *command; char *command;
ParserKeywordT keyword; ParserKeywordT id;
} CommandsT; } KeywordsT;
typedef struct OperatorsS {
char *operator;
ParserMathT id;
} OperatorsT;
static void ensureBufferSize(VecByteCodeT *bytecode, int needed); static void ensureBufferSize(VecByteCodeT *bytecode, int needed);
static void outputByte(VecByteCodeT *bytecode, unsigned char byte); static void outputByte(VecByteCodeT *bytecode, unsigned short word);
static void outputWord(VecByteCodeT *bytecode, unsigned short word); static void outputWord(VecByteCodeT *bytecode, unsigned short word);
static gboolean parserGetNextValue(char *token, char **valueEnd, int *x); static gboolean parserGetNextValue(char *token, char **valueEnd, char **variables, int *x);
static gboolean parserGetWord(char *word, char **tokenEnd); static gboolean parserGetWord(char *word, char **tokenEnd);
static gboolean parserGetX(char **tokenEnd, int *x); static gboolean parserGetX(char **tokenEnd, char **variables, int *x);
static gboolean parserGetXY(char **tokenEnd, int *x, int *y); static gboolean parserGetXY(char **tokenEnd, char **variables, int *x, int *y);
static gboolean parserGetXYZ(char **tokenEnd, int *x, int *y, int *z); static gboolean parserGetXYZ(char **tokenEnd, char **variables, int *x, int *y, int *z);
static void ensureBufferSize(VecByteCodeT *bytecode, int needed) { static void ensureBufferSize(VecByteCodeT *bytecode, int needed) {
@ -55,8 +64,16 @@ static void ensureBufferSize(VecByteCodeT *bytecode, int needed) {
} }
static void outputByte(VecByteCodeT *bytecode, unsigned char byte) { static void outputByte(VecByteCodeT *bytecode, unsigned short word) {
unsigned char byte = (unsigned char)word;
ensureBufferSize(bytecode, 1); ensureBufferSize(bytecode, 1);
// If the word passed in is a variable, set the MSb in the byte as well.
if (word & 0x8000) {
byte |= 0x80;
}
bytecode->bytes[bytecode->length++] = byte; bytecode->bytes[bytecode->length++] = byte;
} }
@ -68,18 +85,45 @@ static void outputWord(VecByteCodeT *bytecode, unsigned short word) {
} }
static gboolean parserGetNextValue(char *token, char **valueEnd, int *x) { static gboolean parserGetNextValue(char *token, char **valueEnd, char **variables, int *x) {
char *value; char *value;
char *endPtr = NULL; char *endPtr = NULL;
int index;
// Return next value in a comma separated list. // Return next value in a comma separated list.
//***TODO*** Variable support.
value = strtok_r(token, ",", valueEnd); value = strtok_r(token, ",", valueEnd);
if (value == NULL) return FALSE; if (value == NULL) return FALSE;
// Is it a variable or number?
if (strlen(value) > 1 && value[0] == '%') {
// It's a variable. Do we know it?
*x = -1;
for (index=0; index<arrlen(variables); index++) {
if (strcasecmp(variables[index], value) == 0) {
*x = index; // Found.
break;
}
if (*x < 0) return FALSE;
// Set MSb to indicate it's a variable.
*x |= 0x8000;
}
} else {
// It's a number.
errno = 0; endPtr = NULL; errno = 0; endPtr = NULL;
*x = (int)strtol(value, &endPtr, 10); *x = (int)strtol(value, &endPtr, 10);
if (errno != 0 || *endPtr != 0) return FALSE; if (errno != 0 || *endPtr != 0) return FALSE;
// Is it negative? If so, we need it in our format.
if (*x < 0) {
*x = -*x;
*x |= 0x4000;
}
// Ensure MSb is cleared so we know it's a number.
*x &= 0x7fff;
}
return TRUE; return TRUE;
} }
@ -98,24 +142,52 @@ static gboolean parserGetWord(char *word, char **tokenEnd) {
} }
static gboolean parserGetX(char **tokenEnd, int *x) { static gboolean parserGetX(char **tokenEnd, char **variables, int *x) {
char *value; char *value;
char *endPtr = NULL; char *endPtr = NULL;
int index;
// Return single value. // Return single value.
//***TODO*** Variable support.
value = strtok_r(NULL, " ", tokenEnd); value = strtok_r(NULL, " ", tokenEnd);
if (value == NULL) return FALSE; if (value == NULL) return FALSE;
errno = 0; endPtr = NULL;
// Is it a variable or number?
if (strlen(value) > 1 && value[0] == '%') {
// It's a variable. Do we know it?
*x = -1;
for (index=0; index<arrlen(variables); index++) {
if (strcasecmp(variables[index], value) == 0) {
*x = index; // Found.
break;
}
if (*x < 0) return FALSE;
// Set MSb to indicate it's a variable.
*x |= 0x8000;
}
} else {
// It's a number.
errno = 0;
endPtr = NULL;
*x = (int)strtol(value, &endPtr, 10); *x = (int)strtol(value, &endPtr, 10);
if (errno != 0 || *endPtr != 0) return FALSE; if (errno != 0 || *endPtr != 0) return FALSE;
// Is it negative? If so, we need it in our format.
if (*x < 0) {
*x = -*x;
*x |= 0x4000;
}
// Ensure MSb is cleared so we know it's a number.
*x &= 0x7fff;
}
return TRUE; return TRUE;
} }
static gboolean parserGetXY(char **tokenEnd, int *x, int *y) { static gboolean parserGetXY(char **tokenEnd, char **variables, int *x, int *y) {
char *token; char *token;
char *valueEnd; char *valueEnd;
@ -124,14 +196,14 @@ static gboolean parserGetXY(char **tokenEnd, int *x, int *y) {
token = strtok_r(NULL, " ", tokenEnd); token = strtok_r(NULL, " ", tokenEnd);
if (token == NULL) return FALSE; if (token == NULL) return FALSE;
if (!parserGetNextValue(token, &valueEnd, x)) return FALSE; if (!parserGetNextValue(token, &valueEnd, variables, x)) return FALSE;
if (!parserGetNextValue(NULL, &valueEnd, y)) return FALSE; if (!parserGetNextValue(NULL, &valueEnd, variables, y)) return FALSE;
return TRUE; return TRUE;
} }
static gboolean parserGetXYZ(char **tokenEnd, int *x, int *y, int *z) { static gboolean parserGetXYZ(char **tokenEnd, char **variables, int *x, int *y, int *z) {
char *token; char *token;
char *valueEnd; char *valueEnd;
@ -140,16 +212,16 @@ static gboolean parserGetXYZ(char **tokenEnd, int *x, int *y, int *z) {
token = strtok_r(NULL, " ", tokenEnd); token = strtok_r(NULL, " ", tokenEnd);
if (token == NULL) return FALSE; if (token == NULL) return FALSE;
if (!parserGetNextValue(token, &valueEnd, x)) return FALSE; if (!parserGetNextValue(token, &valueEnd, variables, x)) return FALSE;
if (!parserGetNextValue(NULL, &valueEnd, y)) return FALSE; if (!parserGetNextValue(NULL, &valueEnd, variables, y)) return FALSE;
if (!parserGetNextValue(NULL, &valueEnd, z)) return FALSE; if (!parserGetNextValue(NULL, &valueEnd, variables, z)) return FALSE;
return TRUE; return TRUE;
} }
int vecparser(char *programIn, VecByteCodeT *bytecode) { int vecparser(char *programIn, VecByteCodeT *bytecode) {
int command; int keyword;
char *line; char *line;
char *lineEnd; char *lineEnd;
gboolean lineOkay; gboolean lineOkay;
@ -164,8 +236,9 @@ int vecparser(char *programIn, VecByteCodeT *bytecode) {
PointT p1; PointT p1;
PointT p2; PointT p2;
PointT *points = NULL; PointT *points = NULL;
char **variables = NULL;
int result = -1; // Returns -1 on success or line number of first error. int result = -1; // Returns -1 on success or line number of first error.
CommandsT commands[] = { KeywordsT commands[] = {
{ "BOX", PARSE_BOX }, { "BOX", PARSE_BOX },
{ "CIRCLE", PARSE_CIRCLE }, { "CIRCLE", PARSE_CIRCLE },
{ "CLEAR", PARSE_CLEAR }, { "CLEAR", PARSE_CLEAR },
@ -180,6 +253,21 @@ int vecparser(char *programIn, VecByteCodeT *bytecode) {
{ "RESET", PARSE_RESET }, { "RESET", PARSE_RESET },
{ NULL, PARSE_NONE } { NULL, PARSE_NONE }
}; };
OperatorsT math[] = {
{ "=", MATH_ASSIGN },
{ "+", MATH_ADD },
{ "-", MATH_SUBTRACT },
{ "*", MATH_MULTIPLY },
{ "/", MATH_DIVIDE },
{ "MOD", MATH_MOD },
{ "POW", MATH_POW },
{ "SQRT", MATH_SQRT },
{ "ABS", MATH_ABS },
{ "COS", MATH_COS },
{ "SIN", MATH_SIN },
{ "TAN", MATH_TAN },
{ NULL, MATH_NONE }
};
// Parse code. // Parse code.
lineNumber = 0; lineNumber = 0;
@ -189,14 +277,64 @@ int vecparser(char *programIn, VecByteCodeT *bytecode) {
// Get the first token on the line. // Get the first token on the line.
token = strtok_r(line, " ", &tokenEnd); token = strtok_r(line, " ", &tokenEnd);
// Is this something we care about? // Is this something we care about? It'll be math, a label, or a keyword.
command = 0;
lineOkay = FALSE; lineOkay = FALSE;
while (commands[command].command) {
if (strcasecmp(commands[command].command, token) == 0) { // Is it math?
if (strlen(token) > 1 && token[0] == '%') {
// We're doing some kind of math.
// Look up variable index in table, or add it if needed.
// Variable index is stored in y1 for later.
y1 = -1;
for (y2=0; y2<arrlen(variables); y2++) {
if (strcasecmp(variables[y2], token) == 0) {
y1 = y2; // Found.
break;
}
}
if (y1 < 0) {
// Variable is not yet known. Add it to list.
arrput(variables, token);
y1 = arrlen(variables) - 1;
}
// Find the operator.
token = strtok_r(NULL, " ", &tokenEnd);
if (token != NULL) {
keyword = 0;
while (math[keyword].operator) {
if (strcasecmp(math[keyword].operator, token) == 0) {
// Yep! Gather arguments and generate bytecode.
if (!parserGetX(&tokenEnd, variables, &x1)) break;
outputByte(bytecode, PARSE_MATH);
outputByte(bytecode, y1);
outputByte(bytecode, math[keyword].id);
outputWord(bytecode, x1);
lineOkay = TRUE;
break;
}
keyword++;
}
}
} else { // doing math
// Is it a label?
if (strlen(token) > 1 && token[strlen(token) - 1] == ':') {
// It's a label.
} else { // doing a label
// It's a keyword.
keyword = 0;
while (commands[keyword].command) {
if (strcasecmp(commands[keyword].command, token) == 0) {
// Yep! Gather arguments and generate bytecode. // Yep! Gather arguments and generate bytecode.
switch (commands[command].keyword) { switch (commands[keyword].id) {
case PARSE_NONE: case PARSE_NONE:
// Won't happen, but silences an error. // Won't happen, but silences an error.
@ -204,11 +342,15 @@ int vecparser(char *programIn, VecByteCodeT *bytecode) {
case PARSE_BOX: case PARSE_BOX:
// Box (value),(value) to (value),(value) // Box (value),(value) to (value),(value)
if (!parserGetXY(&tokenEnd, &x1, &y1)) break; if (!parserGetXY(&tokenEnd, variables, &x1, &y1)) break;
if (!parserGetWord("TO", &tokenEnd)) break; if (!parserGetWord("TO", &tokenEnd)) break;
if (!parserGetXY(&tokenEnd, &x2, &y2)) break; if (!parserGetXY(&tokenEnd, variables, &x2, &y2)) break;
if (x2 < x1 || x1 < 0 || x2 < 0 || x1 > 319 || x2 > 319) break; if (IS_NUMBER(x1) && IS_NUMBER(x2) && x2 < x1) break;
if (y2 < y1 || y1 < 0 || y2 < 0 || y1 > 199 || y2 > 199) break; if (IS_NUMBER(x1) && (x1 < 0 || x1 > 319)) break;
if (IS_NUMBER(x2) && (x2 < 0 || x2 > 319)) break;
if (IS_NUMBER(y1) && IS_NUMBER(y2) && y2 < y1) break;
if (IS_NUMBER(y1) && (y1 < 0 || y1 > 199)) break;
if (IS_NUMBER(y2) && (y2 < 0 || y2 > 199)) break;
outputByte(bytecode, PARSE_BOX); outputByte(bytecode, PARSE_BOX);
outputWord(bytecode, x1); outputWord(bytecode, x1);
outputWord(bytecode, y1); outputWord(bytecode, y1);
@ -219,11 +361,11 @@ int vecparser(char *programIn, VecByteCodeT *bytecode) {
case PARSE_CIRCLE: case PARSE_CIRCLE:
// Circle (value) at (value),(value) // Circle (value) at (value),(value)
if (!parserGetX(&tokenEnd, &y2)) break; if (!parserGetX(&tokenEnd, variables, &y2)) break;
if (!parserGetWord("AT", &tokenEnd)) break; if (!parserGetWord("AT", &tokenEnd)) break;
if (!parserGetXY(&tokenEnd, &x1, &y1)) break; if (!parserGetXY(&tokenEnd, variables, &x1, &y1)) break;
if (x1 < 0 || x1 > 319) break; if (IS_NUMBER(x1) && (x1 < 0 || x1 > 319)) break;
if (y1 < 0 || y1 > 199) break; if (IS_NUMBER(y1) && (y1 < 0 || y1 > 199)) break;
outputByte(bytecode, PARSE_CIRCLE); outputByte(bytecode, PARSE_CIRCLE);
outputWord(bytecode, y2); outputWord(bytecode, y2);
outputWord(bytecode, x1); outputWord(bytecode, x1);
@ -238,8 +380,8 @@ int vecparser(char *programIn, VecByteCodeT *bytecode) {
case PARSE_COLOR: case PARSE_COLOR:
// Color (short) // Color (short)
if (!parserGetX(&tokenEnd, &x1)) break; if (!parserGetX(&tokenEnd, variables, &x1)) break;
if (x1 < 0 || x1 > 15) break; if (IS_NUMBER(x1) && (x1 < 0 || x1 > 15)) break;
outputByte(bytecode, PARSE_COLOR); outputByte(bytecode, PARSE_COLOR);
outputByte(bytecode, x1); outputByte(bytecode, x1);
lineOkay = TRUE; lineOkay = TRUE;
@ -255,11 +397,15 @@ int vecparser(char *programIn, VecByteCodeT *bytecode) {
case PARSE_ELLIPSE: case PARSE_ELLIPSE:
// Ellipse (value),(value) to (value),(value) // Ellipse (value),(value) to (value),(value)
if (!parserGetXY(&tokenEnd, &x1, &y1)) break; if (!parserGetXY(&tokenEnd, variables, &x1, &y1)) break;
if (!parserGetWord("TO", &tokenEnd)) break; if (!parserGetWord("TO", &tokenEnd)) break;
if (!parserGetXY(&tokenEnd, &x2, &y2)) break; if (!parserGetXY(&tokenEnd, variables, &x2, &y2)) break;
if (x2 < x1 || x1 < 0 || x2 < 0 || x1 > 319 || x2 > 319) break; if (IS_NUMBER(x1) && IS_NUMBER(x2) && x2 < x1) break;
if (y2 < y1 || y1 < 0 || y2 < 0 || y1 > 199 || y2 > 199) break; if (IS_NUMBER(x1) && (x1 < 0 || x1 > 319)) break;
if (IS_NUMBER(x2) && (x2 < 0 || x2 > 319)) break;
if (IS_NUMBER(y1) && IS_NUMBER(y2) && y2 < y1) break;
if (IS_NUMBER(y1) && (y1 < 0 || y1 > 199)) break;
if (IS_NUMBER(y2) && (y2 < 0 || y2 > 199)) break;
outputByte(bytecode, PARSE_ELLIPSE); outputByte(bytecode, PARSE_ELLIPSE);
outputWord(bytecode, x1); outputWord(bytecode, x1);
outputWord(bytecode, y1); outputWord(bytecode, y1);
@ -270,14 +416,14 @@ int vecparser(char *programIn, VecByteCodeT *bytecode) {
case PARSE_FILL: case PARSE_FILL:
// Fill (value),(value) {to (value} // Fill (value),(value) {to (value}
if (!parserGetXY(&tokenEnd, &x1, &y1)) break; if (!parserGetXY(&tokenEnd, variables, &x1, &y1)) break;
if (x1 < 0 || x1 > 319) break; if (IS_NUMBER(x1) && (x1 < 0 || x1 > 319)) break;
if (y1 < 0 || y1 > 199) break; if (IS_NUMBER(y1) && (y1 < 0 || y1 > 199)) break;
// Do they want to fill to a certain color? Or over the current color? // Do they want to fill to a certain color? Or over the current color?
x2 = 16; // 16 == Fill, otherwise FillTo x2 = 16; // 16 == Fill, otherwise FillTo
if (parserGetWord("TO", &tokenEnd)) { if (parserGetWord("TO", &tokenEnd)) {
if (!parserGetX(&tokenEnd, &x2)) break; if (!parserGetX(&tokenEnd, variables, &x2)) break;
if (x2 < 0 || x2 > 15) break; if (IS_NUMBER(x2) && (x2 < 0 || x2 > 15)) break;
} }
outputByte(bytecode, PARSE_FILL); outputByte(bytecode, PARSE_FILL);
outputWord(bytecode, x1); outputWord(bytecode, x1);
@ -286,21 +432,25 @@ int vecparser(char *programIn, VecByteCodeT *bytecode) {
lineOkay = TRUE; lineOkay = TRUE;
break; break;
case PARSE_LABEL:
// Won't happen.
break;
case PARSE_LINE: case PARSE_LINE:
// Line (value),(value) to (value),(value) [to ...] // Line (value),(value) to (value),(value) [to ...]
points = NULL; points = NULL;
if (!parserGetXY(&tokenEnd, &p1.x, &p1.y)) break; if (!parserGetXY(&tokenEnd, variables, &p1.x, &p1.y)) break;
if (!parserGetWord("TO", &tokenEnd)) break; if (!parserGetWord("TO", &tokenEnd)) break;
if (!parserGetXY(&tokenEnd, &p2.x, &p2.y)) break; if (!parserGetXY(&tokenEnd, variables, &p2.x, &p2.y)) break;
if (p1.x < 0 || p1.x > 319) break; if (IS_NUMBER(p1.x) && (p1.x < 0 || p1.x > 319)) break;
if (p1.y < 0 || p1.y > 199) break; if (IS_NUMBER(p1.y) && (p1.y < 0 || p1.y > 199)) break;
if (p2.x < 0 || p2.x > 319) break; if (IS_NUMBER(p2.x) && (p2.x < 0 || p2.x > 319)) break;
if (p2.y < 0 || p2.y > 199) break; if (IS_NUMBER(p2.y) && (p2.y < 0 || p2.y > 199)) break;
arrput(points, p1); arrput(points, p1);
arrput(points, p2); arrput(points, p2);
isOkay = TRUE; isOkay = TRUE;
while (parserGetWord("TO", &tokenEnd)) { while (parserGetWord("TO", &tokenEnd)) {
if (!parserGetXY(&tokenEnd, &p1.x, &p1.y)) { if (!parserGetXY(&tokenEnd, variables, &p1.x, &p1.y)) {
// Error. Unwind array. // Error. Unwind array.
while (arrlen(points) > 0) { while (arrlen(points) > 0) {
arrdel(points, 0); arrdel(points, 0);
@ -314,7 +464,7 @@ int vecparser(char *programIn, VecByteCodeT *bytecode) {
if (isOkay) { if (isOkay) {
outputByte(bytecode, PARSE_LINE); outputByte(bytecode, PARSE_LINE);
outputWord(bytecode, arrlen(points)); outputWord(bytecode, arrlen(points));
for (x1=0; x1<arrlen(points); x1++) { for (x1 = 0; x1 < arrlen(points); x1++) {
outputWord(bytecode, points[x1].x); outputWord(bytecode, points[x1].x);
outputWord(bytecode, points[x1].y); outputWord(bytecode, points[x1].y);
} }
@ -327,15 +477,19 @@ int vecparser(char *programIn, VecByteCodeT *bytecode) {
} }
break; break;
case PARSE_MATH:
// Won't happen.
break;
case PARSE_PALETTE: case PARSE_PALETTE:
// Palette (short) AS (short),(short),(short) // Palette (short) AS (short),(short),(short)
if (!parserGetX(&tokenEnd, &x1)) break; if (!parserGetX(&tokenEnd, variables, &x1)) break;
if (!parserGetWord("AS", &tokenEnd)) break; if (!parserGetWord("AS", &tokenEnd)) break;
if (!parserGetXYZ(&tokenEnd, &x2, &y1, &y2)) break; if (!parserGetXYZ(&tokenEnd, variables, &x2, &y1, &y2)) break;
if (x1 < 0 || x1 > 15) break; if (IS_NUMBER(x1) && (x1 < 0 || x1 > 15)) break;
if (x2 < 0 || x2 > 15) break; if (IS_NUMBER(x2) && (x2 < 0 || x2 > 15)) break;
if (y1 < 0 || y1 > 15) break; if (IS_NUMBER(y1) && (y1 < 0 || y1 > 15)) break;
if (y2 < 0 || y2 > 15) break; if (IS_NUMBER(y2) && (y2 < 0 || y2 > 15)) break;
outputByte(bytecode, PARSE_PALETTE); outputByte(bytecode, PARSE_PALETTE);
outputByte(bytecode, x1); outputByte(bytecode, x1);
outputByte(bytecode, x2); outputByte(bytecode, x2);
@ -346,9 +500,9 @@ int vecparser(char *programIn, VecByteCodeT *bytecode) {
case PARSE_PLOT: case PARSE_PLOT:
// Plot (value),(value) // Plot (value),(value)
if (!parserGetXY(&tokenEnd, &x1, &y1)) break; if (!parserGetXY(&tokenEnd, variables, &x1, &y1)) break;
if (x1 < 0 || x1 > 319) break; if (IS_NUMBER(x1) && (x1 < 0 || x1 > 319)) break;
if (y1 < 0 || y1 > 199) break; if (IS_NUMBER(y1) && (y1 < 0 || y1 > 199)) break;
outputByte(bytecode, PARSE_PLOT); outputByte(bytecode, PARSE_PLOT);
outputWord(bytecode, x1); outputWord(bytecode, x1);
outputWord(bytecode, y1); outputWord(bytecode, y1);
@ -357,11 +511,15 @@ int vecparser(char *programIn, VecByteCodeT *bytecode) {
case PARSE_RECTANGLE: case PARSE_RECTANGLE:
// Rectangle (value),(value) to (value),(value) // Rectangle (value),(value) to (value),(value)
if (!parserGetXY(&tokenEnd, &x1, &y1)) break; if (!parserGetXY(&tokenEnd, variables, &x1, &y1)) break;
if (!parserGetWord("TO", &tokenEnd)) break; if (!parserGetWord("TO", &tokenEnd)) break;
if (!parserGetXY(&tokenEnd, &x2, &y2)) break; if (!parserGetXY(&tokenEnd, variables, &x2, &y2)) break;
if (x2 < x1 || x1 < 0 || x2 < 0 || x1 > 319 || x2 > 319) break; if (IS_NUMBER(x1) && IS_NUMBER(x2) && x2 < x1) break;
if (y2 < y1 || y1 < 0 || y2 < 0 || y1 > 199 || y2 > 199) break; if (IS_NUMBER(x1) && (x1 < 0 || x1 > 319)) break;
if (IS_NUMBER(x2) && (x2 < 0 || x2 > 319)) break;
if (IS_NUMBER(y1) && IS_NUMBER(y2) && y2 < y1) break;
if (IS_NUMBER(y1) && (y1 < 0 || y1 > 199)) break;
if (IS_NUMBER(y2) && (y2 < 0 || y2 > 199)) break;
outputByte(bytecode, PARSE_RECTANGLE); outputByte(bytecode, PARSE_RECTANGLE);
outputWord(bytecode, x1); outputWord(bytecode, x1);
outputWord(bytecode, y1); outputWord(bytecode, y1);
@ -377,16 +535,20 @@ int vecparser(char *programIn, VecByteCodeT *bytecode) {
} // switch } // switch
// Stop looking for this command - we handled it. // Stop looking for this keyword - we handled it.
break; break;
} else { } else {
// Keep looking until we find this command or run out of commands. // Keep looking until we find this keyword or run out of commands.
command++; keyword++;
} // command match } // keyword match
} // loop over commands } // loop over commands
} // label
} // math
// Is everything still okay? // Is everything still okay?
if (!lineOkay) { if (!lineOkay) {
// Nope - error. // Nope - error.

View file

@ -27,7 +27,7 @@
#include "common.h" #include "common.h"
#include <string.h> #include <string.h>
#include <errno.h> #include <math.h>
#include "scintillaHeaders.h" #include "scintillaHeaders.h"
#include "gladeVector.h" #include "gladeVector.h"
#include "vector.h" #include "vector.h"
@ -59,23 +59,42 @@ typedef struct VectorDataS {
cairo_surface_t *trace; cairo_surface_t *trace;
jlContextT *jlc; jlContextT *jlc;
double traceImagePercent; double traceImagePercent;
float *variables;
} VectorDataT; } VectorDataT;
static int _nextEditorId = 0; static int _nextEditorId = 0;
static int byte(VectorDataT *self, unsigned char byte);
EVENT gboolean drawVectorImageDraw(GtkWidget *widget, cairo_t *cr, gpointer userData); EVENT gboolean drawVectorImageDraw(GtkWidget *widget, cairo_t *cr, gpointer userData);
EVENT void drawVectorImageClick(GtkWidget *object, GdkEventButton *event, gpointer userData); EVENT void drawVectorImageClick(GtkWidget *object, GdkEventButton *event, gpointer userData);
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);
static int getWord(VecByteCodeT *bytecode, int *index);
EVENT void menuVectorFileClose(GtkWidget *object, gpointer userData); EVENT void menuVectorFileClose(GtkWidget *object, gpointer userData);
static void renderBytecode(VecByteCodeT *bytecode, VectorDataT *self); static void renderBytecode(VecByteCodeT *bytecode, VectorDataT *self);
EVENT void scaleVectorTraceImageValueChanged(GtkWidget *object, gpointer userData); EVENT void scaleVectorTraceImageValueChanged(GtkWidget *object, gpointer userData);
static float variable(VectorDataT *self, unsigned char byte);
static int word(VectorDataT *self, unsigned short word);
EVENT gboolean winVectorClose(GtkWidget *object, gpointer userData); EVENT gboolean winVectorClose(GtkWidget *object, gpointer userData);
static void winVectorDelete(gpointer userData); static void winVectorDelete(gpointer userData);
static int byte(VectorDataT *self, unsigned char byte) {
int value;
// Decode our weird byte format, look up variables, and return sane numbers.
// Is this a variable?
if (byte & 0x80) {
value = variable(self, byte);
} else {
// Bytes can't be negative in our world, so just return it.
value = byte;
}
return value;
}
EVENT gboolean drawVectorImageDraw(GtkWidget *widget, cairo_t *cr, gpointer userData) { EVENT gboolean drawVectorImageDraw(GtkWidget *widget, cairo_t *cr, gpointer userData) {
VectorDataT *self = (VectorDataT *)userData; VectorDataT *self = (VectorDataT *)userData;
int width = cairo_image_surface_get_width(self->surface); int width = cairo_image_surface_get_width(self->surface);
@ -180,12 +199,12 @@ EVENT void editorVectorNotify(GtkWidget *sciWidget, gint ctrlID, struct SCNotifi
SSM(SCI_MARKERADD, lineNumber, MARKER_ERROR_ARROW); SSM(SCI_MARKERADD, lineNumber, MARKER_ERROR_ARROW);
SSM(SCI_MARKERADD, lineNumber, MARKER_ERROR_HIGHLIGHT); SSM(SCI_MARKERADD, lineNumber, MARKER_ERROR_HIGHLIGHT);
} else { } else {
// All good!
renderBytecode(&byteCode, self);
//***DEBUG*** //***DEBUG***
FILE *out = fopen("bytecode.bin", "wb"); FILE *out = fopen("bytecode.bin", "wb");
fwrite(byteCode.bytes, byteCode.length, 1, out); fwrite(byteCode.bytes, byteCode.length, 1, out);
fclose(out); fclose(out);
// All good!
renderBytecode(&byteCode, self);
} }
// Release bytecode. // Release bytecode.
@ -208,6 +227,18 @@ EVENT void editorVectorNotify(GtkWidget *sciWidget, gint ctrlID, struct SCNotifi
} }
static int getWord(VecByteCodeT *bytecode, int *index) {
int word;
word = bytecode->bytes[*index] << 8;
*index = *index + 1;
word += bytecode->bytes[*index];
*index = *index + 1;
return word;
}
EVENT void fileVectorTraceImageFileSet(GtkWidget *object, gpointer userData) { EVENT void fileVectorTraceImageFileSet(GtkWidget *object, gpointer userData) {
VectorDataT *self = (VectorDataT *)userData; VectorDataT *self = (VectorDataT *)userData;
char *filename; char *filename;
@ -282,9 +313,13 @@ static void renderBytecode(VecByteCodeT *bytecode, VectorDataT *self) {
int y2; int y2;
int count; int count;
int i; int i;
float f1;
float f2;
#define GET_BYTE (bytecode->bytes[index++]) #define GET_BYTE (bytecode->bytes[index++])
#define GET_WORD ((bytecode->bytes[index++] << 8) + bytecode->bytes[index++]) #define GET_WORD getWord(bytecode, &index)
self->variables = NULL;
while (index < bytecode->length) { while (index < bytecode->length) {
switch (bytecode->bytes[index++]) { switch (bytecode->bytes[index++]) {
@ -294,18 +329,18 @@ static void renderBytecode(VecByteCodeT *bytecode, VectorDataT *self) {
break; break;
case PARSE_BOX: case PARSE_BOX:
x1 = GET_WORD; x1 = word(self, GET_WORD);
y1 = GET_WORD; y1 = word(self, GET_WORD);
x2 = GET_WORD; x2 = word(self, GET_WORD);
y2 = GET_WORD; y2 = word(self, GET_WORD);
printf("Box %d,%d to %d,%d\n", x1, y1, x2, y2); printf("Box %d,%d to %d,%d\n", x1, y1, x2, y2);
jlDrawBox(self->jlc, x1, y1, x2, y2); jlDrawBox(self->jlc, x1, y1, x2, y2);
break; break;
case PARSE_CIRCLE: case PARSE_CIRCLE:
y2 = GET_WORD; y2 = word(self, GET_WORD);
x1 = GET_WORD; x1 = word(self, GET_WORD);
y1 = GET_WORD; y1 = word(self, GET_WORD);
printf("Circle %d at %d,%d\n", y2, x1, y1); printf("Circle %d at %d,%d\n", y2, x1, y1);
jlDrawCircle(self->jlc, x1, y1, y2); jlDrawCircle(self->jlc, x1, y1, y2);
break; break;
@ -316,28 +351,28 @@ static void renderBytecode(VecByteCodeT *bytecode, VectorDataT *self) {
break; break;
case PARSE_COLOR: case PARSE_COLOR:
x1 = GET_BYTE; x1 = byte(self, GET_BYTE);
printf("Color %d\n", x1); printf("Color %d\n", x1);
jlDrawColorSet(self->jlc, x1); jlDrawColorSet(self->jlc, x1);
break; break;
case PARSE_COMMENT: case PARSE_COMMENT:
// Nothing to do! // Won't happen. Nothing to do!
break; break;
case PARSE_ELLIPSE: case PARSE_ELLIPSE:
x1 = GET_WORD; x1 = word(self, GET_WORD);
y1 = GET_WORD; y1 = word(self, GET_WORD);
x2 = GET_WORD; x2 = word(self, GET_WORD);
y2 = GET_WORD; y2 = word(self, GET_WORD);
printf("Ellipse %d,%d to %d,%d\n", x1, y1, x2, y2); printf("Ellipse %d,%d to %d,%d\n", x1, y1, x2, y2);
jlDrawEllipse(self->jlc, x1, y1, x2, y2); jlDrawEllipse(self->jlc, x1, y1, x2, y2);
break; break;
case PARSE_FILL: case PARSE_FILL:
x1 = GET_WORD; x1 = word(self, GET_WORD);
y1 = GET_WORD; y1 = word(self, GET_WORD);
x2 = GET_BYTE; x2 = byte(self, GET_BYTE);
if (x2 > 15) { if (x2 > 15) {
printf("Fill %d,%d\n", x1, y1); printf("Fill %d,%d\n", x1, y1);
jlDrawFill(self->jlc, x1, y1); jlDrawFill(self->jlc, x1, y1);
@ -347,14 +382,17 @@ static void renderBytecode(VecByteCodeT *bytecode, VectorDataT *self) {
} }
break; break;
case PARSE_LABEL:
break;
case PARSE_LINE: case PARSE_LINE:
count = GET_WORD; count = word(self, GET_WORD);
x1 = GET_WORD; x1 = word(self, GET_WORD);
y1 = GET_WORD; y1 = word(self, GET_WORD);
printf("Line %d,%d", x1, y1); printf("Line %d,%d", x1, y1);
for (i=0; i<count - 1; i++) { for (i=0; i<count - 1; i++) {
x2 = GET_WORD; x2 = word(self, GET_WORD);
y2 = GET_WORD; y2 = word(self, GET_WORD);
printf(" to %d,%d", x2, y2); printf(" to %d,%d", x2, y2);
jlDrawLine(self->jlc, x1, y1, x2, y2); jlDrawLine(self->jlc, x1, y1, x2, y2);
x1 = x2; x1 = x2;
@ -363,27 +401,97 @@ static void renderBytecode(VecByteCodeT *bytecode, VectorDataT *self) {
printf("\n"); printf("\n");
break; break;
case PARSE_MATH:
x1 = GET_BYTE; // var index
y1 = GET_BYTE; // operand
x2 = GET_WORD; // Possible variable
if (x2 & 0x8000) {
f2 = variable(self, x2);
} else {
f2 = x2;
}
f1 = variable(self, x1);
switch (y1) {
case MATH_NONE:
// Well, none!
break;
case MATH_ASSIGN:
f1 = f2;
break;
case MATH_ADD:
f1 += f2;
break;
case MATH_SUBTRACT:
f1 -= f2;
break;
case MATH_MULTIPLY:
f1 *= f2;
break;
case MATH_DIVIDE:
f1 /= f2;
break;
case MATH_MOD:
f1 = modff(f1, &f2);
break;
case MATH_POW:
f1 = powf(f1, f2);
break;
case MATH_SQRT:
f1 = sqrtf(f2);
break;
case MATH_ABS:
f1 = fabsf(f2);
break;
case MATH_COS:
f1 = cosf(f2);
break;
case MATH_SIN:
f1 = sinf(f2);
break;
case MATH_TAN:
f1 = tanf(f2);
break;
}
// Make sure we have enough slots for this variable.
while (arrlen(self->variables) <= x1) {
arrput(self->variables, 0.0f);
}
self->variables[x1] = f1;
break;
case PARSE_PALETTE: case PARSE_PALETTE:
x1 = GET_BYTE; x1 = byte(self, GET_BYTE);
x2 = GET_BYTE; x2 = byte(self, GET_BYTE);
y1 = GET_BYTE; y1 = byte(self, GET_BYTE);
y2 = GET_BYTE; y2 = byte(self, GET_BYTE);
printf("Palette %d as %d,%d,%d\n", x1, x2, y1, y2); printf("Palette %d as %d,%d,%d\n", x1, x2, y1, y2);
jlPaletteSet(self->jlc, x1, x2, y1, y2); jlPaletteSet(self->jlc, x1, x2, y1, y2);
break; break;
case PARSE_PLOT: case PARSE_PLOT:
x1 = GET_WORD; x1 = word(self, GET_WORD);
y1 = GET_WORD; y1 = word(self, GET_WORD);
printf("Plot %d,%d\n", x1, y1); printf("Plot %d,%d\n", x1, y1);
jlDrawPixelSet(self->jlc, x1, y1); jlDrawPixelSet(self->jlc, x1, y1);
break; break;
case PARSE_RECTANGLE: case PARSE_RECTANGLE:
x1 = GET_WORD; x1 = word(self, GET_WORD);
y1 = GET_WORD; y1 = word(self, GET_WORD);
x2 = GET_WORD; x2 = word(self, GET_WORD);
y2 = GET_WORD; y2 = word(self, GET_WORD);
printf("Rectangle %d,%d to %d,%d\n", x1, y1, x2, y2); printf("Rectangle %d,%d to %d,%d\n", x1, y1, x2, y2);
jlDrawBoxFilled(self->jlc, x1, y1, x2, y2); jlDrawBoxFilled(self->jlc, x1, y1, x2, y2);
break; break;
@ -399,6 +507,12 @@ static void renderBytecode(VecByteCodeT *bytecode, VectorDataT *self) {
} // switch } // switch
} // while } // while
// Clear variables.
while (arrlen(self->variables) > 0) {
arrdel(self->variables, 0);
}
arrfree(self->variables);
// Refresh widget. // Refresh widget.
gtk_widget_queue_draw(self->drawVectorImage); gtk_widget_queue_draw(self->drawVectorImage);
} }
@ -416,6 +530,43 @@ EVENT void scaleVectorTraceImageValueChanged(GtkWidget *object, gpointer userDat
} }
static float variable(VectorDataT *self, unsigned char byte) {
int value;
// Clear possibly set variable flag.
byte &= 0xbf;
// Look up variable and return value.
if (byte < arrlen(self->variables)) {
value = self->variables[byte];
} else {
// We don't know this variable yet. Be like BASIC.
value = 0;
}
return value;
}
static int word(VectorDataT *self, unsigned short word) {
int value;
// Decode our weird word format, look up variables, and return sane numbers.
// Is this a variable?
if (word & 0x8000) {
value = variable(self, word);
} else {
// Is it negative?
if (word & 0x4000) {
// Clear the negative flag and actually negate it.
value = 0 - (word & 0xbfff);
} else {
// Just a boring number, copy it out.
value = word;
}
}
return value;
}
EVENT gboolean winVectorClose(GtkWidget *object, gpointer userData) { EVENT gboolean winVectorClose(GtkWidget *object, gpointer userData) {
// userData is not reliable due to menuVectorFileClose and util indirectly calling us. // userData is not reliable due to menuVectorFileClose and util indirectly calling us.
VectorDataT *self = (VectorDataT *)utilGetWindowData(object); VectorDataT *self = (VectorDataT *)utilGetWindowData(object);
@ -511,10 +662,13 @@ void winVectorCreate(void) {
// Debug // Debug
SSM(SCI_INSERTTEXT, 0, (sptr_t) SSM(SCI_INSERTTEXT, 0, (sptr_t)
"reset\n" "reset\n"
"palette 15 as 15,0,0\n" "%red = 15\n"
"color 15\n" "palette %red as 15,0,0\n"
"color %red\n"
"box 0,0 to 319,199\n" "box 0,0 to 319,199\n"
"color 14\n" "%yellow = %red\n"
"%yellow - 1\n"
"color %yellow\n"
"line 27,22 to 289,24 to 297,169 to 27,166 to 27,23\n" "line 27,22 to 289,24 to 297,169 to 27,166 to 27,23\n"
"color 11\n" "color 11\n"
"circle 50 at 159,95\n" "circle 50 at 159,95\n"