271 lines
6.5 KiB
C
271 lines
6.5 KiB
C
|
|
#include <ctype.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "bindings.h"
|
|
#include "gcl.h"
|
|
|
|
#define null ((void*) 0)
|
|
|
|
/*----------------------------------------------------------------------+
|
|
| Definitions |
|
|
+----------------------------------------------------------------------*/
|
|
|
|
typedef enum {
|
|
endType, openType, closeType, wordType, condType, loopType
|
|
} WordType_t;
|
|
|
|
// Disected word
|
|
struct word {
|
|
char prefix; // optional
|
|
char *name; // null in case of a number
|
|
int number; // length in case of a name
|
|
char operator[3]; // or a conditional
|
|
};
|
|
|
|
/*----------------------------------------------------------------------+
|
|
| Variables |
|
|
+----------------------------------------------------------------------*/
|
|
|
|
char *cursor;
|
|
char *compileError;
|
|
int blockDepth;
|
|
|
|
int base, offset, size;
|
|
unsigned char page[256];
|
|
|
|
/*----------------------------------------------------------------------+
|
|
| Internal functions |
|
|
+----------------------------------------------------------------------*/
|
|
|
|
/*
|
|
* Get the next word skipping whitespace and nested comments.
|
|
* Classify the word according to its structure.
|
|
* Generate an error message if malformed.
|
|
* Most work is done here already.
|
|
*/
|
|
static WordType_t nextWord(struct word *word)
|
|
{
|
|
#define hasMore\
|
|
(isprint(*cursor) && !strchr("[{", *cursor))
|
|
|
|
#define error(s)\
|
|
((compileError = (s)), -1)
|
|
|
|
memset(word, 0, sizeof *word);
|
|
|
|
// Whitespace
|
|
while (1) {
|
|
// Spaces or newlines
|
|
if (strchr(" \n", *cursor) || strncmp("\r\n", cursor, 2)==0) {
|
|
cursor++;
|
|
continue;
|
|
}
|
|
// Comments
|
|
if (*cursor == '{') {
|
|
cursor++;
|
|
int depth = 1;
|
|
do {
|
|
if (*cursor == 0) return error("Comment without end");
|
|
if (!isprint(*cursor)) return error("Invalid byte");
|
|
if (*cursor == '{') depth++;
|
|
if (*cursor == '}') depth--;
|
|
cursor++;
|
|
} while (depth > 0);
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// End of file/line
|
|
if (*cursor == 0) return endType;
|
|
|
|
// Blocks
|
|
if (*cursor == '[') return cursor++, openType;
|
|
if (*cursor == ']') return cursor++, closeType;
|
|
|
|
// Prefix
|
|
if (strchr("<>\\%", *cursor))
|
|
word->prefix = *cursor++;
|
|
|
|
// Word or number
|
|
char *s = strchr("-+", *cursor);
|
|
char sign = s ? *cursor++ : 0;
|
|
if (isdigit(*cursor)) {
|
|
// Decimal number (ignore overflows)
|
|
int n = 0;
|
|
do {
|
|
n = 10 * n + (*cursor - '0');
|
|
cursor++;
|
|
} while (isdigit(*cursor));
|
|
word->number = (sign == '-') ? -n : n;
|
|
} else if (*cursor == '$') {
|
|
// Hexadecimal number (ignore overflows)
|
|
int n = 0;
|
|
s = ++cursor;
|
|
while (1) {
|
|
if (isdigit(*cursor))
|
|
n = 16 * n + (*cursor - '0');
|
|
else if ('a' <= *cursor && *cursor <= 'f')
|
|
n = 16 * n + (*cursor - 'a');
|
|
else if ('A' <= *cursor && *cursor <= 'F')
|
|
n = 16 * n + (*cursor - 'A');
|
|
else
|
|
break;
|
|
cursor++;
|
|
}
|
|
if (cursor == s)
|
|
return error("Missing hex");
|
|
word->number = (sign == '-') ? -n : n;
|
|
} else if (sign)
|
|
return error("Sign without number");
|
|
else if (isalpha(*cursor)) {
|
|
// Name
|
|
word->name = cursor++;
|
|
while (isalnum(*cursor) || *cursor == '_')
|
|
cursor++;
|
|
word->number = cursor - word->name;
|
|
} else
|
|
return error("Invalid word");
|
|
|
|
// Conditionals
|
|
if (strncmp("if", word->name, 2)==0 && word->number == 2 &&
|
|
!word->prefix && !sign) {
|
|
int i = 0;
|
|
if (*cursor == '<') word->operator[i++] = *cursor++;
|
|
if (*cursor == '>') word->operator[i++] = *cursor++;
|
|
if (*cursor == '=' && i<2) word->operator[i++] = *cursor++;
|
|
if (i > 0 && *cursor++ == '0') {
|
|
if (strncmp(cursor, "loop", 4)==0)
|
|
cursor += 4;
|
|
if (!hasMore)
|
|
return condType;
|
|
}
|
|
return error("Invalid conditional");
|
|
}
|
|
|
|
// Operator
|
|
s = strchr("+-&|^=.,:;?!<", *cursor);
|
|
if (s) {
|
|
word->operator[0] = *cursor++;
|
|
if (cursor[-1] == cursor[0] && strchr("+-<", *cursor))
|
|
word->operator[1] = *cursor++; // ++ -- <<
|
|
}
|
|
|
|
if (hasMore)
|
|
return error("Invalid word");
|
|
return wordType;
|
|
}
|
|
|
|
static int compileWord(struct word *w)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static void emit(int byte)
|
|
{
|
|
if (offset < size)
|
|
page[offset] = byte;
|
|
offset++;
|
|
}
|
|
|
|
static int emitIf(int cond)
|
|
{
|
|
emit(BCC);
|
|
emit(cond);
|
|
return 0;
|
|
}
|
|
|
|
static int emitIfLoop(int cond)
|
|
{
|
|
emit(BCC);
|
|
emit(cond);
|
|
return 0;
|
|
}
|
|
|
|
static int compileCond(struct word *w)
|
|
{
|
|
if (strcmp(w->operator, "<")==0) return emitIf(GE);
|
|
if (strcmp(w->operator, ">")==0) return emitIf(LE);
|
|
if (strcmp(w->operator, "=")==0) return emitIf(NE);
|
|
if (strcmp(w->operator, "<>")==0) return emitIf(EQ);
|
|
if (strcmp(w->operator, "<=")==0) return emitIf(GT);
|
|
if (strcmp(w->operator, ">=")==0) return emitIf(LT);
|
|
return -1;
|
|
}
|
|
|
|
static int compileLoop(struct word *w)
|
|
{
|
|
if (strcmp(w->operator, "<")==0) return emitIfLoop(LT);
|
|
if (strcmp(w->operator, ">")==0) return emitIfLoop(GT);
|
|
if (strcmp(w->operator, "=")==0) return emitIfLoop(EQ);
|
|
if (strcmp(w->operator, "<>")==0) return emitIfLoop(NE);
|
|
if (strcmp(w->operator, "<=")==0) return emitIfLoop(LE);
|
|
if (strcmp(w->operator, ">=")==0) return emitIfLoop(GE);
|
|
return -1;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------+
|
|
| External functions |
|
|
+----------------------------------------------------------------------*/
|
|
|
|
void compileBegin(void)
|
|
{
|
|
compileError = null;
|
|
blockDepth = 0;
|
|
|
|
base = 0x0200;
|
|
offset = 0;
|
|
size = 250;
|
|
}
|
|
|
|
int compileLine(char *line)
|
|
{
|
|
cursor = line;
|
|
struct word w;
|
|
while (1) {
|
|
switch (nextWord(&w)) {
|
|
case endType:
|
|
break;
|
|
case openType:
|
|
blockDepth++;
|
|
continue;
|
|
case closeType:
|
|
if (blockDepth <= 0)
|
|
return error("Close block without open");
|
|
blockDepth--;
|
|
continue;
|
|
case wordType:
|
|
if (compileWord(&w))
|
|
return -1;
|
|
continue;
|
|
case condType:
|
|
if (compileCond(&w))
|
|
return -1;
|
|
continue;
|
|
case loopType:
|
|
if (compileLoop(&w))
|
|
return -1;
|
|
continue;
|
|
default:
|
|
return -1;
|
|
}
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int compileEnd(void)
|
|
{
|
|
if (blockDepth > 0)
|
|
return error("Open block without close");
|
|
return 0;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------+
|
|
| |
|
|
+----------------------------------------------------------------------*/
|
|
|