commit 57f209e18c0aeac9e0fb49ad0bc552531e7c78a5 Author: Scott Duensing Date: Wed May 13 19:08:25 2020 -0500 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bc21fc7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +build-* +*.bin +*.lst +*~ +*.user diff --git a/compiler/compiler.pro b/compiler/compiler.pro new file mode 100644 index 0000000..a8124f1 --- /dev/null +++ b/compiler/compiler.pro @@ -0,0 +1,16 @@ +TEMPLATE = app + +include(/home/scott/joey/dist/joey.pri) + +HEADERS += \ + utlist.h + +SOURCES += \ + main.c + +OTHER_FILES += \ + ../notes.txt + +DISTFILES += \ + ../test.dbl \ + ../test.lst diff --git a/compiler/main.c b/compiler/main.c new file mode 100644 index 0000000..58476a2 --- /dev/null +++ b/compiler/main.c @@ -0,0 +1,1088 @@ +#include +#include +#include +#include + +#include "utlist.h" + +#include "../vm/vm.h" + + +// /home/scott/joey/rpgengine/dbltw/test.dbl /home/scott/joey/rpgengine/dbltw/test.bin /home/scott/joey/rpgengine/dbltw/test.lst + + +#define MAX_KEYWORD_ARGS 8 +#define MAX_ARG_SIZE 1024 +#define MAX_LINE_SIZE 8192 + +#define STATE_KEYWORD 1 +#define STATE_ARGUMENT 2 + +#define QUOTE_NONE 1 +#define QUOTE_SINGLE 2 +#define QUOTE_DOUBLE 3 + + +typedef int (*keywordHandler)(int argc, char *argv[], char *file, int line); + +typedef struct { + char *keyword; + int argc; // Negative values represent the minimum required + keywordHandler handler; +} KeywordT; + +typedef struct { + char *opcode; + int argc; + int stackPop; + int stackPush; + unsigned char value; +} OpcodeT; + +typedef struct { + int16_t value; + int labelLine; + char *labelFile; + char *text; +} StackT; + +typedef struct variableT_ { + int16_t index; + unsigned char exported; + char *name; + struct variableT_ *next; +} VariableT; + +typedef struct labelT_ { + uint16_t addr; + unsigned char exported; + int line; + char *name; + char *file; + struct labelT_ *next; +} LabelT; + +typedef struct defineT_ { + char *name; + char *value; + struct defineT_ *next; +} DefineT; + +typedef struct bitstreamT_ { + uint16_t addr; + unsigned char opcode; + StackT *argument; + struct bitstreamT_ *next; +} BitstreamT; + + +int argIsNumber(char *arg); +int argIsString(char *arg); +int argIsVariable(char *arg); +void emitOpcode(char *opcode); +void emitStackItem(StackT *item); +int16_t findVariable(char *varName); +int kw_add(int argc, char *argv[], char *file, int line); +int kw_call(int argc, char *argv[], char *file, int line); +int kw_define(int argc, char *argv[], char *file, int line); +int kw_do(int argc, char *argv[], char *file, int line); +int kw_end(int argc, char *argv[], char *file, int line); +int kw_export(int argc, char *argv[], char *file, int line); +int kw_goto(int argc, char *argv[], char *file, int line); +int kw_if(int argc, char *argv[], char *file, int line); +int kw_include(int argc, char *argv[], char *file, int line); +int kw_return(int argc, char *argv[], char *file, int line); +int kw_set(int argc, char *argv[], char *file, int line); +int kw_subtract(int argc, char *argv[], char *file, int line); +void op_add(void); +void op_boc(void); +void op_exe(void); +void op_jmp(void); +void op_hlt(void); +void op_pop(char *varName); +void op_puc(StackT *arg); +void op_puv(int16_t varIndex); +void op_sub(void); +int parse(char *sourceIn); +void push(char *arg, unsigned char isLabel, char *file, int line); +void strReplace(char *target, const char *needle, const char *replacement); + + +static KeywordT keywords[] = { + { "add", 2, kw_add }, + { "call", 1, kw_call }, + { "define", 2, kw_define }, + { "do", -1, kw_do }, + { "end", 0, kw_end }, + { "export", 2, kw_export }, + { "goto", 1, kw_goto }, + { "if", 4, kw_if }, + { "include", 1, kw_include }, + { "return", 0, kw_return }, + { "set", 2, kw_set }, + { "subtract", 2, kw_subtract } +}; + +static OpcodeT opcodes[] = { // Note: There is no zero opcode! + { "ADD", 0, 2, 1, OPCODE_ADD }, // Add + { "BOC", 0, 4, 0, OPCODE_BOC }, // Branch On Condition + { "EXE", 0, -1, -1, OPCODE_EXE }, // Execute VM Function (Unknown stack results) ***TODO*** Change to be entirely stack based + { "JMP", 0, 1, 0, OPCODE_JMP }, // Jump to address + { "HLT", 0, 0, 0, OPCODE_HLT }, // Halt Execution + { "POP", 1, 1, 0, OPCODE_POP }, // Pop into Variable + { "PUC", 1, 0, 1, OPCODE_PUC }, // Push Constant + { "PUV", 1, 0, 1, OPCODE_PUV }, // Push Variable + { "SUB", 0, 2, 1, OPCODE_SUB } // Subtract +}; + +static LabelT *labels; +static DefineT *defines; +static VariableT *variables; +static uint16_t variableCount; // Variables declared +static uint16_t pc; // Program Counter +static int sloc; // Source Lines of Code +static int files; // Files compiled +static int opCount; // Operands emitted +static int labelCount; // Labels found +static BitstreamT *bitstream; + + +int argIsNumber(char *arg) { + if (((arg[0] >= '0') && (arg[0] <= '9')) || (arg[0] == '-')) { + return 1; + } + return 0; +} + + +int argIsString(char *arg) { + if ((arg[0] == '\'') || (arg[0] == '"')) { + return 1; + } + return 0; +} + + +int argIsVariable(char *arg) { + return !argIsNumber(arg) && !argIsString(arg); +} + + +void emitOpcode(char *opcode) { + BitstreamT *bits; + + for (int i=0; i<(int)(sizeof(opcodes)/sizeof(OpcodeT)); i++) { + if (strcmp(opcode, opcodes[i].opcode) == 0) { + bits = (BitstreamT *)malloc(sizeof(BitstreamT)); + bits->addr = pc; + bits->opcode = opcodes[i].value; + bits->argument = NULL; + LL_APPEND(bitstream, bits); + pc++; // Opcodes are a single byte + opCount++; + } + } +} + + +void emitStackItem(StackT *item) { + BitstreamT *bits; + + bits = (BitstreamT *)malloc(sizeof(BitstreamT)); + bits->addr = pc; + bits->opcode = 0; + bits->argument = (StackT *)malloc(sizeof(StackT)); + bits->argument->value = item->value; + if (item->text == NULL) { + bits->argument->text = NULL; + pc++; // Non string values still get a 0 written here + } else { + // Remove enclosing quotes + bits->argument->text = strdup(&item->text[1]); + bits->argument->text[strlen(bits->argument->text) - 1] = 0; + bits->argument->value = (jint16)strlen(bits->argument->text); + // Is this a string or label? + if (item->labelLine >= 0) { + // Label. Just add 1 because it will end up a null string. + pc++; + } else { + // String arguments add length + pc += strlen(bits->argument->text); + } + } + if (item->labelFile == NULL) { + bits->argument->labelFile = NULL; + } else { + bits->argument->labelFile = strdup(item->labelFile); + } + bits->argument->labelLine = item->labelLine; + LL_APPEND(bitstream, bits); + pc += 2; // All arguments have a 16 bit integer value +} + + +int16_t findVariable(char *varName) { + VariableT *variable; + int16_t found = -1; + + LL_FOREACH(variables, variable) { + if (strcmp(variable->name, varName) == 0) { + found = variable->index; + } + } + + if (found < 0) { + variable = (VariableT *)malloc(sizeof(VariableT)); + variable->exported = 0; + variable->name = strdup(varName); + variable->index = variableCount++; + found = variable->index; + LL_APPEND(variables, variable); + } + + return found; +} + + +int kw_add(int argc, char *argv[], char *file, int line) { + (void)argc; + + if (!argIsVariable(argv[0])) { + printf("Keyword 'add' requires first argument to be a variable\n"); + return 1; + } + + push(argv[1], 0, file, line); + push(argv[0], 0, file, line); + op_add(); + op_pop(argv[0]); + + return 0; +} + + +int kw_call(int argc, char *argv[], char *file, int line) { + uint16_t addr; + char tempAddr[8]; + char *temp; + unsigned long len; + + (void)argc; + + // We need to calculate the address of the opcode *after* the JMP. + addr = pc + 9; + /* + addr = pc + 4; // +1 for the JMP opcode, +3 for the value we're pushing. + if (argIsString(argv[0])) { + addr += strlen(argv[0]); // Strings are still quoted at this point so the extra two bytes make up for not adding the length bytes + } else { + addr += 3; // Three bytes for a numeric PUC or a PUV + } + */ + snprintf(tempAddr, 8, "%d", addr); + push(tempAddr, 0, file, line); + + // Label (expected to be quoted, but isn't at this point) + len = strlen(argv[0]) + 3; + temp = (char *)malloc(len * sizeof(char)); + snprintf(temp, len, "'%s'", argv[0]); + push(temp, 1, file, line); + free(temp); + + op_jmp(); + + return 0; +} + + +int kw_define(int argc, char *argv[], char *file, int line) { + DefineT *define; + + (void)argc; + (void)file; + (void)line; + + define = (DefineT *)malloc(sizeof(DefineT)); + define->name = strdup(argv[0]); + if (argIsString(argv[1])) { + // Remove Quotes + define->value = strdup(&argv[1][1]); + define->value[strlen(define->value) - 1] = 0; + } else { + define->value = strdup(argv[1]); + } + LL_APPEND(defines, define); + + return 0; +} + + +int kw_do(int argc, char *argv[], char *file, int line) { + char temp[8]; + + // Arguments for EXE + for (int i=argc; i>0; i--) { + push(argv[i - 1], 0, file, line); + } + + // Number of arguments + snprintf(temp, 8, "%d", argc); + push(temp, 0, file, line); + + op_exe(); + + return 0; +} + + +int kw_end(int argc, char *argv[], char *file, int line) { + (void)argc; + (void)argv; + (void)file; + (void)line; + + op_hlt(); + + return 0; +} + + +int kw_export(int argc, char *argv[], char *file, int line) { + VariableT *variable; + + (void)argc; + + if (!argIsVariable(argv[0])) { + printf("Keyword 'export' requires first argument to be a variable\n"); + return 1; + } + + push(argv[1], 0, file, line); + op_pop(argv[0]); + + LL_FOREACH(variables, variable) { + if (strcmp(variable->name, argv[0]) == 0) { + variable->exported = 1; + } + } + + return 0; +} + + +int kw_goto(int argc, char *argv[], char *file, int line) { + char *temp; + unsigned long len; + + (void)argc; + + // Label (expected to be quoted, but isn't at this point) + len = strlen(argv[0]) + 3; + temp = (char *)malloc(len * sizeof(char)); + snprintf(temp, len, "'%s'", argv[0]); + push(temp, 1, file, line); + free(temp); + + op_jmp(); + + return 0; +} + + +int kw_if(int argc, char *argv[], char *file, int line) { + char *temp; + unsigned long len; + + (void)argc; + + // Label (expected to be quoted, but isn't at this point) + len = strlen(argv[3]) + 3; + temp = (char *)malloc(len * sizeof(char)); + snprintf(temp, len, "'%s'", argv[3]); + push(temp, 1, file, line); + free(temp); + + push(argv[2], 0, file, line); // Second argument + + switch(argv[1][0]) { + case '=': + push("0", 0, file, line); + break; + + case '!': + push("1", 0, file, line); + break; + + case '<': + push("2", 0, file, line); + break; + + case '>': + push("3", 0, file, line); + break; + + default: + printf("Unknown conditional operation '%s'\n", argv[1]); + return 1; + } + push(argv[0], 0, file, line); // First argument + op_boc(); + + return 0; +} + + +int kw_include(int argc, char *argv[], char *file, int line) { + (void)argc; + (void)file; + (void)line; + return parse(argv[0]); +} + + +int kw_return(int argc, char *argv[], char *file, int line) { + (void)argc; + (void)argv; + (void)file; + (void)line; + + // The return address *should* be on the stack at this point + op_jmp(); + + return 0; +} + + +int kw_set(int argc, char *argv[], char *file, int line) { + (void)argc; + (void)file; + (void)line; + + if (!argIsVariable(argv[0])) { + printf("Keyword 'set' requires first argument to be a variable\n"); + return 1; + } + + push(argv[1], 0, file, line); + op_pop(argv[0]); + + return 0; +} + + +int kw_subtract(int argc, char *argv[], char *file, int line) { + (void)argc; + + if (!argIsVariable(argv[0])) { + printf("Keyword 'subtract' requires first argument to be a variable\n"); + return 1; + } + + push(argv[1], 0, file, line); + push(argv[0], 0, file, line); + op_sub(); + op_pop(argv[0]); + + return 0; +} + + +void op_add(void) { + // Add + // Add two stack items and push result + emitOpcode("ADD"); +} + + +void op_boc(void) { + // Branch On Condition + // Use four stack items to determine if we should branch + emitOpcode("BOC"); +} + + +void op_exe(void) { + // Execute + // Calls native function number. Unknown stack state. + emitOpcode("EXE"); +} + + +void op_jmp(void) { + // Jump + // Jumps to address on the stack + emitOpcode("JMP"); +} + + +void op_hlt(void) { + // Halt + emitOpcode("HLT"); +} + + +void op_pop(char *varName) { + // Pop + // Pop top stack value into variable + StackT item; + + item.labelLine = -1; + item.labelFile = NULL; + item.text = NULL; + item.value = (int16_t)findVariable(varName); + + emitOpcode("POP"); + emitStackItem(&item); +} + + +void op_puc(StackT *arg) { + // Push Constant + // Pushes the constant value of 'arg' to the stack + emitOpcode("PUC"); + emitStackItem(arg); +} + + +void op_puv(int16_t varIndex) { + // Push Variable + // Pushes address of variable to use onto stack + StackT item; + + item.labelLine = -1; + item.labelFile = NULL; + item.text = NULL; + item.value = varIndex; + + emitOpcode("PUV"); + emitStackItem(&item); +} + + +void op_sub(void) { + // Subtract + // Subtract two stack items and push result + emitOpcode("SUB"); +} + + +int parse(char *sourceIn) { + + char line[MAX_LINE_SIZE]; + char *token; + int tokenLength; + int state; + char **argv; + int argc; + char quote[MAX_ARG_SIZE]; + LabelT *found; + LabelT *label; + DefineT *define; + char buffer[MAX_ARG_SIZE]; + int inQuote = QUOTE_NONE; + int keyword = -1; + int lineNumber = 0; + int bailOut = 0; + + FILE *in = fopen(sourceIn, "rt"); + if (!in) { + printf("Unable to open input file '%s'.\n", sourceIn); + return 1; + } + + files++; + + argv = (char **)malloc(MAX_KEYWORD_ARGS * sizeof(char *)); + for (int i=0; iname, define->value); + } + + //printf("Line: %s\n", line); + + // First token + token = strtok(line, " "); + state = STATE_KEYWORD; + argc = 0; + keyword = -1; + while ((token != NULL) && (!bailOut)) { + tokenLength = (int)strlen(token); + if (tokenLength > 0) { + + // Copy token to buffer so we don't screw up strtok(). + strcpy(buffer, token); + + // Handle Comments + if (buffer[0] == ';') { + break; + } + + // Handle EOLs + if ((buffer[tokenLength - 1] == 10) || (buffer[tokenLength - 1] == 13)) { + tokenLength--; + buffer[tokenLength] = 0; + } + + // Handle Keywords & Arguments + if (tokenLength > 0) { + + // Handle keywords + if (state == STATE_KEYWORD) { + + // Handle labels + if (buffer[tokenLength - 1] == ':') { + buffer[tokenLength - 1] = 0; + // Do we know this label already? + found = NULL; + LL_FOREACH(labels, label) { + if (strcmp(buffer, label->name) == 0) { + found = label; + } + } + if (found) { + // Yes. Die. + printf("Label '%s' already declared in '%s' at line %d.\n", buffer, found->file, found->line); + bailOut = 1; + } else { + // Add it. + label = (LabelT *)malloc(sizeof(LabelT)); + label->addr = pc; + // Is this marked exported? + if (buffer[0] == '>') { + // Yes! + label->name = strdup(&buffer[1]); + label->exported = 1; + } else { + // No + label->name = strdup(buffer); + label->exported = 0; + } + label->line = lineNumber; + label->file = strdup(sourceIn); + LL_APPEND(labels, label); + labelCount++; + //printf("Label: %s\n", buffer); + } + + } else { + + // Not a label, search for known keyword + keyword = -2; + for (int i=0; i<(int)(sizeof(keywords)/sizeof(KeywordT)); i++) { + if (strcmp(buffer, keywords[i].keyword) == 0) { + // Found our keyword + keyword = i; + quote[0] = 0; + inQuote = QUOTE_NONE; + state = STATE_ARGUMENT; + //printf("Keyword: %s\n", keywords[i].keyword); + } + } + + } // Is a label? + } else { + + // Handle argument quoting + if (inQuote == QUOTE_NONE) { + // Are we entering a quoted string? + if ((buffer[0] == '"') || (buffer[0] == '\'')) { + // Does this token also end with quotes? If so, we don't need to enter quote mode. + if (!( + (((buffer[tokenLength - 1] == '"') && (buffer[0] == '"')) || + ((buffer[tokenLength - 1] == '\'') && (buffer[0] == '\''))) && + (buffer[tokenLength - 2] != '\\') + ) || (tokenLength == 1)) { + // Enter quote mode + inQuote = buffer[0] == '"' ? QUOTE_DOUBLE : QUOTE_SINGLE; + } + } + } else { + // Look to leave quote mode + if ( + (((buffer[tokenLength - 1] == '"') && (inQuote == QUOTE_DOUBLE)) || + ((buffer[tokenLength - 1] == '\'') && (inQuote == QUOTE_SINGLE))) && + (buffer[tokenLength - 2] != '\\') + ) { + inQuote = QUOTE_NONE; + } + } + + if (strlen(quote) > 0) { + strcat(quote, " "); + } + strcat(quote, buffer); + + // Can we process this argument yet? + if (inQuote == QUOTE_NONE) { + strcpy(argv[argc], quote); + argc++; + //printf("Argument: [%s]\n", quote); + quote[0] = 0; + } + + } // state + } // tokenLength > 0 + } // tokenLength > 0 + + // Next token + token = strtok(NULL, " "); + + // If this is the end of the line and we found a keyword, process it. + if ((!token) && (keyword >= 0) && (!bailOut)) { + if (keywords[keyword].argc < 0) { + if (argc < abs(keywords[keyword].argc)) { + printf("Keyword '%s' requires at least %d argument(s)\n", keywords[keyword].keyword, abs(keywords[keyword].argc)); + bailOut = 1; + } + } else { + if (argc != keywords[keyword].argc) { + printf("Keyword '%s' requires %d argument(s)\n", keywords[keyword].keyword, keywords[keyword].argc); + bailOut = 1; + } + } + if (!bailOut) { + if (keywords[keyword].handler(argc, argv, sourceIn, lineNumber) != 0) { + bailOut = 1; + } + } + } else { + if (keyword == -2) { + printf("Watchu talkin' bout Willus?\n"); + bailOut = 1; + } + } + } // token != NULL + } // read line + + if (bailOut) { + printf("Location %d in '%s'\n", lineNumber, sourceIn); + } + + for (int i=0; iargument != NULL) { + if (bits->argument->labelFile != NULL) { + labelS = NULL; + LL_FOREACH(labels, label) { + if (strcmp(bits->argument->text, label->name) == 0) { + labelS = label; + } + } + if (labelS) { + //printf("Fixing label '%s' as %04x\n", bits->argument->text, labelS->addr); + bits->argument->value = (int16_t)labelS->addr; + free(bits->argument->text); + bits->argument->text = NULL; + } else { + printf("Unable to locate label '%s' in '%s' at %d\n", bits->argument->text, bits->argument->labelFile, bits->argument->labelLine); + bailOut = 1; + result = 1; + } + } + } + } + } else { + bailOut = 1; + result = 1; + } + + // If everything was okay, produce listing and binary + if (bailOut == 0) { + listing = fopen(listingFile, "wt"); + if (listing) { + binary = fopen(binFile, "wb"); + if (binary) { + + // Write header + fputc('D', binary); + fputc('B', binary); + fputc('L', binary); + fputc(0, binary); // Version + + // Write exported jump table + for (int pass=0; pass<2; pass++) { + LL_FOREACH(labels, label) { + if (label->exported) { + if (pass == 0) { + // First pass, just count + exportedLabels++; + } else { + // Second pass, write it out + fwrite(&label->addr, sizeof(uint16_t), 1, binary); + fputc((char)strlen(label->name), binary); + fwrite(label->name, sizeof(char), strlen(label->name), binary); + } + } + } + // End of first pass, write out header size + if (pass == 0) { + fwrite(&exportedLabels, sizeof(uint16_t), 1, binary); + } + } + + // Write exported variable table + for (int pass=0; pass<2; pass++) { + count = 0; + LL_FOREACH(variables, variable) { + if (variable->exported) { + if (pass == 0) { + // First pass, just count + exportedVariables++; + } else { + // Second pass, write it out + fwrite(&count, sizeof(uint16_t), 1, binary); + fputc((char)strlen(variable->name), binary); + fwrite(variable->name, sizeof(char), strlen(variable->name), binary); + } + } + count++; + } + // End of first pass, write out header size + if (pass == 0) { + fwrite(&exportedVariables, sizeof(uint16_t), 1, binary); + } + } + + // Write number of variables used + fwrite(&variableCount, sizeof(uint16_t), 1, binary); + + headerSize = (uint16_t)ftell(binary); + + // Write program bitstream + LL_FOREACH(bitstream, bits) { + // At this point we should always have a valid opcode + if ((bits->opcode > 0) && (!bailOut)) { + for (int i=0; i<(int)(sizeof(opcodes)/sizeof(OpcodeT)); i++) { + // Find opcode data + if (bits->opcode == opcodes[i].value) { + // Sanity check + if (ftell(binary) - headerSize != bits->addr) { + printf("Bitstream addresses out of sync at %04x\n", (uint16_t)ftell(binary) - headerSize); + bailOut = 1; + result = 1; + break; + } + fprintf(listing, "(%04x) %04x %s (%02x)", (uint16_t)ftell(binary), bits->addr, opcodes[i].opcode, i + 1); + fputc(opcodes[i].value, binary); + // Does this opcode have arguments? + if (opcodes[i].argc > 0) { + // Move on to the argument data + bits = bits->next; + // Should NOT have an opcode + if (bits->opcode > 0) { + printf("Unexpected opcode at address %04x\n", bits->addr); + bailOut = 1; + result = 1; + break; + } else { + // MUST have an argument + if (bits->argument == NULL) { + printf("No argument data at address %04x\n", bits->addr); + bailOut = 1; + result = 1; + break; + } else { + // String data? + if (bits->argument->text != NULL) { + // Yes! + fprintf(listing, " [%s]", bits->argument->text); + fwrite(&bits->argument->value, sizeof(int16_t), 1, binary); + fwrite(bits->argument->text, sizeof(char), strlen(bits->argument->text), binary); + } else { + // Nope. + fprintf(listing, " %04x", bits->argument->value); + fwrite(&bits->argument->value, sizeof(int16_t), 1, binary); + fputc(0, binary); + } + } + } + } + fprintf(listing, "\n"); + break; + } + } + } else { + printf("Invalid opcode at address %04xd\n", bits->addr); + result = 1; + } + } + + fclose(binary); + } else { // binary != null + printf("Unable to create object file '%s'\n", binFile); + result = 1; + } + fclose(listing); + } else { + printf("Unable to create listing file '%s'\n", listingFile); + result = 1; + } + } + + // Stats + if (result == 0) { + printf(" Files Compiled: %d\n", files); + printf(" Lines Compiled: %d\n", sloc); + printf(" Labels declared: %d\n", labelCount); + printf(" Labels Exported: %d\n", exportedLabels); + printf("Variables declared: %d\n", variableCount); + printf("Variables Exported: %d\n", exportedVariables); + printf(" Operands Emitted: %d\n", opCount); + printf(" Bytes Used: %d\n", pc); + } + + // Shut down + LL_FOREACH_SAFE(bitstream, bits, bitsS) { + LL_DELETE(bitstream, bits); + if (bits->argument != NULL) { + if (bits->argument->labelFile != NULL) { + free(bits->argument->labelFile); + } + if (bits->argument->text != NULL) { + free(bits->argument->text); + } + free(bits->argument); + } + } + LL_FOREACH_SAFE(variables, variable, variableS) { + LL_DELETE(variables, variable); + free(variable->name); + } + LL_FOREACH_SAFE(defines, define, defineS) { + LL_DELETE(defines, define); + free(define->name); + free(define->value); + } + LL_FOREACH_SAFE(labels, label, labelS) { + LL_DELETE(labels, label); + free(label->name); + free(label->file); + } + + return result; +} diff --git a/compiler/utlist.h b/compiler/utlist.h new file mode 100644 index 0000000..5bb1ac9 --- /dev/null +++ b/compiler/utlist.h @@ -0,0 +1,1073 @@ +/* +Copyright (c) 2007-2018, Troy D. Hanson http://troydhanson.github.com/uthash/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef UTLIST_H +#define UTLIST_H + +#define UTLIST_VERSION 2.1.0 + +#include + +/* + * This file contains macros to manipulate singly and doubly-linked lists. + * + * 1. LL_ macros: singly-linked lists. + * 2. DL_ macros: doubly-linked lists. + * 3. CDL_ macros: circular doubly-linked lists. + * + * To use singly-linked lists, your structure must have a "next" pointer. + * To use doubly-linked lists, your structure must "prev" and "next" pointers. + * Either way, the pointer to the head of the list must be initialized to NULL. + * + * ----------------.EXAMPLE ------------------------- + * struct item { + * int id; + * struct item *prev, *next; + * } + * + * struct item *list = NULL: + * + * int main() { + * struct item *item; + * ... allocate and populate item ... + * DL_APPEND(list, item); + * } + * -------------------------------------------------- + * + * For doubly-linked lists, the append and delete macros are O(1) + * For singly-linked lists, append and delete are O(n) but prepend is O(1) + * The sort macro is O(n log(n)) for all types of single/double/circular lists. + */ + +/* These macros use decltype or the earlier __typeof GNU extension. + As decltype is only available in newer compilers (VS2010 or gcc 4.3+ + when compiling c++ source) this code uses whatever method is needed + or, for VS2008 where neither is available, uses casting workarounds. */ +#if !defined(LDECLTYPE) && !defined(NO_DECLTYPE) +#if defined(_MSC_VER) /* MS compiler */ +#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ +#define LDECLTYPE(x) decltype(x) +#else /* VS2008 or older (or VS2010 in C mode) */ +#define NO_DECLTYPE +#endif +#elif defined(__BORLANDC__) || defined(__ICCARM__) || defined(__LCC__) || defined(__WATCOMC__) +#define NO_DECLTYPE +#else /* GNU, Sun and other compilers */ +#define LDECLTYPE(x) __typeof(x) +#endif +#endif + +/* for VS2008 we use some workarounds to get around the lack of decltype, + * namely, we always reassign our tmp variable to the list head if we need + * to dereference its prev/next pointers, and save/restore the real head.*/ +#ifdef NO_DECLTYPE +#define IF_NO_DECLTYPE(x) x +#define LDECLTYPE(x) char* +#define UTLIST_SV(elt,list) _tmp = (char*)(list); {char **_alias = (char**)&(list); *_alias = (elt); } +#define UTLIST_NEXT(elt,list,next) ((char*)((list)->next)) +#define UTLIST_NEXTASGN(elt,list,to,next) { char **_alias = (char**)&((list)->next); *_alias=(char*)(to); } +/* #define UTLIST_PREV(elt,list,prev) ((char*)((list)->prev)) */ +#define UTLIST_PREVASGN(elt,list,to,prev) { char **_alias = (char**)&((list)->prev); *_alias=(char*)(to); } +#define UTLIST_RS(list) { char **_alias = (char**)&(list); *_alias=_tmp; } +#define UTLIST_CASTASGN(a,b) { char **_alias = (char**)&(a); *_alias=(char*)(b); } +#else +#define IF_NO_DECLTYPE(x) +#define UTLIST_SV(elt,list) +#define UTLIST_NEXT(elt,list,next) ((elt)->next) +#define UTLIST_NEXTASGN(elt,list,to,next) ((elt)->next)=(to) +/* #define UTLIST_PREV(elt,list,prev) ((elt)->prev) */ +#define UTLIST_PREVASGN(elt,list,to,prev) ((elt)->prev)=(to) +#define UTLIST_RS(list) +#define UTLIST_CASTASGN(a,b) (a)=(b) +#endif + +/****************************************************************************** + * The sort macro is an adaptation of Simon Tatham's O(n log(n)) mergesort * + * Unwieldy variable names used here to avoid shadowing passed-in variables. * + *****************************************************************************/ +#define LL_SORT(list, cmp) \ + LL_SORT2(list, cmp, next) + +#define LL_SORT2(list, cmp, next) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + IF_NO_DECLTYPE(LDECLTYPE(list) _tmp;) \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + UTLIST_CASTASGN(_ls_p,list); \ + (list) = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + UTLIST_SV(_ls_q,list); _ls_q = UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + } else if (_ls_qsize == 0 || !_ls_q) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + } else { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + } \ + if (_ls_tail) { \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_ls_e,next); UTLIST_RS(list); \ + } else { \ + UTLIST_CASTASGN(list,_ls_e); \ + } \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + if (_ls_tail) { \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,NULL,next); UTLIST_RS(list); \ + } \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } \ +} while (0) + + +#define DL_SORT(list, cmp) \ + DL_SORT2(list, cmp, prev, next) + +#define DL_SORT2(list, cmp, prev, next) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + IF_NO_DECLTYPE(LDECLTYPE(list) _tmp;) \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + UTLIST_CASTASGN(_ls_p,list); \ + (list) = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + UTLIST_SV(_ls_q,list); _ls_q = UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while ((_ls_psize > 0) || ((_ls_qsize > 0) && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + } else if ((_ls_qsize == 0) || (!_ls_q)) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + } else { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + } \ + if (_ls_tail) { \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_ls_e,next); UTLIST_RS(list); \ + } else { \ + UTLIST_CASTASGN(list,_ls_e); \ + } \ + UTLIST_SV(_ls_e,list); UTLIST_PREVASGN(_ls_e,list,_ls_tail,prev); UTLIST_RS(list); \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + UTLIST_CASTASGN((list)->prev, _ls_tail); \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,NULL,next); UTLIST_RS(list); \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } \ +} while (0) + +#define CDL_SORT(list, cmp) \ + CDL_SORT2(list, cmp, prev, next) + +#define CDL_SORT2(list, cmp, prev, next) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + LDECLTYPE(list) _ls_oldhead; \ + LDECLTYPE(list) _tmp; \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + UTLIST_CASTASGN(_ls_p,list); \ + UTLIST_CASTASGN(_ls_oldhead,list); \ + (list) = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + UTLIST_SV(_ls_q,list); \ + if (UTLIST_NEXT(_ls_q,list,next) == _ls_oldhead) { \ + _ls_q = NULL; \ + } else { \ + _ls_q = UTLIST_NEXT(_ls_q,list,next); \ + } \ + UTLIST_RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ + } else if (_ls_qsize == 0 || !_ls_q) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ + } else { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ + } \ + if (_ls_tail) { \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_ls_e,next); UTLIST_RS(list); \ + } else { \ + UTLIST_CASTASGN(list,_ls_e); \ + } \ + UTLIST_SV(_ls_e,list); UTLIST_PREVASGN(_ls_e,list,_ls_tail,prev); UTLIST_RS(list); \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + UTLIST_CASTASGN((list)->prev,_ls_tail); \ + UTLIST_CASTASGN(_tmp,list); \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_tmp,next); UTLIST_RS(list); \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } \ +} while (0) + +/****************************************************************************** + * singly linked list macros (non-circular) * + *****************************************************************************/ +#define LL_PREPEND(head,add) \ + LL_PREPEND2(head,add,next) + +#define LL_PREPEND2(head,add,next) \ +do { \ + (add)->next = (head); \ + (head) = (add); \ +} while (0) + +#define LL_CONCAT(head1,head2) \ + LL_CONCAT2(head1,head2,next) + +#define LL_CONCAT2(head1,head2,next) \ +do { \ + LDECLTYPE(head1) _tmp; \ + if (head1) { \ + _tmp = (head1); \ + while (_tmp->next) { _tmp = _tmp->next; } \ + _tmp->next=(head2); \ + } else { \ + (head1)=(head2); \ + } \ +} while (0) + +#define LL_APPEND(head,add) \ + LL_APPEND2(head,add,next) + +#define LL_APPEND2(head,add,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + (add)->next=NULL; \ + if (head) { \ + _tmp = (head); \ + while (_tmp->next) { _tmp = _tmp->next; } \ + _tmp->next=(add); \ + } else { \ + (head)=(add); \ + } \ +} while (0) + +#define LL_INSERT_INORDER(head,add,cmp) \ + LL_INSERT_INORDER2(head,add,cmp,next) + +#define LL_INSERT_INORDER2(head,add,cmp,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + if (head) { \ + LL_LOWER_BOUND2(head, _tmp, add, cmp, next); \ + LL_APPEND_ELEM2(head, _tmp, add, next); \ + } else { \ + (head) = (add); \ + (head)->next = NULL; \ + } \ +} while (0) + +#define LL_LOWER_BOUND(head,elt,like,cmp) \ + LL_LOWER_BOUND2(head,elt,like,cmp,next) + +#define LL_LOWER_BOUND2(head,elt,like,cmp,next) \ + do { \ + if ((head) == NULL || (cmp(head, like)) >= 0) { \ + (elt) = NULL; \ + } else { \ + for ((elt) = (head); (elt)->next != NULL; (elt) = (elt)->next) { \ + if (cmp((elt)->next, like) >= 0) { \ + break; \ + } \ + } \ + } \ + } while (0) + +#define LL_DELETE(head,del) \ + LL_DELETE2(head,del,next) + +#define LL_DELETE2(head,del,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + if ((head) == (del)) { \ + (head)=(head)->next; \ + } else { \ + _tmp = (head); \ + while (_tmp->next && (_tmp->next != (del))) { \ + _tmp = _tmp->next; \ + } \ + if (_tmp->next) { \ + _tmp->next = (del)->next; \ + } \ + } \ +} while (0) + +#define LL_COUNT(head,el,counter) \ + LL_COUNT2(head,el,counter,next) \ + +#define LL_COUNT2(head,el,counter,next) \ +do { \ + (counter) = 0; \ + LL_FOREACH2(head,el,next) { ++(counter); } \ +} while (0) + +#define LL_FOREACH(head,el) \ + LL_FOREACH2(head,el,next) + +#define LL_FOREACH2(head,el,next) \ + for ((el) = (head); el; (el) = (el)->next) + +#define LL_FOREACH_SAFE(head,el,tmp) \ + LL_FOREACH_SAFE2(head,el,tmp,next) + +#define LL_FOREACH_SAFE2(head,el,tmp,next) \ + for ((el) = (head); (el) && ((tmp) = (el)->next, 1); (el) = (tmp)) + +#define LL_SEARCH_SCALAR(head,out,field,val) \ + LL_SEARCH_SCALAR2(head,out,field,val,next) + +#define LL_SEARCH_SCALAR2(head,out,field,val,next) \ +do { \ + LL_FOREACH2(head,out,next) { \ + if ((out)->field == (val)) break; \ + } \ +} while (0) + +#define LL_SEARCH(head,out,elt,cmp) \ + LL_SEARCH2(head,out,elt,cmp,next) + +#define LL_SEARCH2(head,out,elt,cmp,next) \ +do { \ + LL_FOREACH2(head,out,next) { \ + if ((cmp(out,elt))==0) break; \ + } \ +} while (0) + +#define LL_REPLACE_ELEM2(head, el, add, next) \ +do { \ + LDECLTYPE(head) _tmp; \ + assert((head) != NULL); \ + assert((el) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el)->next; \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + _tmp = (head); \ + while (_tmp->next && (_tmp->next != (el))) { \ + _tmp = _tmp->next; \ + } \ + if (_tmp->next) { \ + _tmp->next = (add); \ + } \ + } \ +} while (0) + +#define LL_REPLACE_ELEM(head, el, add) \ + LL_REPLACE_ELEM2(head, el, add, next) + +#define LL_PREPEND_ELEM2(head, el, add, next) \ +do { \ + if (el) { \ + LDECLTYPE(head) _tmp; \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el); \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + _tmp = (head); \ + while (_tmp->next && (_tmp->next != (el))) { \ + _tmp = _tmp->next; \ + } \ + if (_tmp->next) { \ + _tmp->next = (add); \ + } \ + } \ + } else { \ + LL_APPEND2(head, add, next); \ + } \ +} while (0) \ + +#define LL_PREPEND_ELEM(head, el, add) \ + LL_PREPEND_ELEM2(head, el, add, next) + +#define LL_APPEND_ELEM2(head, el, add, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el)->next; \ + (el)->next = (add); \ + } else { \ + LL_PREPEND2(head, add, next); \ + } \ +} while (0) \ + +#define LL_APPEND_ELEM(head, el, add) \ + LL_APPEND_ELEM2(head, el, add, next) + +#ifdef NO_DECLTYPE +/* Here are VS2008 / NO_DECLTYPE replacements for a few functions */ + +#undef LL_CONCAT2 +#define LL_CONCAT2(head1,head2,next) \ +do { \ + char *_tmp; \ + if (head1) { \ + _tmp = (char*)(head1); \ + while ((head1)->next) { (head1) = (head1)->next; } \ + (head1)->next = (head2); \ + UTLIST_RS(head1); \ + } else { \ + (head1)=(head2); \ + } \ +} while (0) + +#undef LL_APPEND2 +#define LL_APPEND2(head,add,next) \ +do { \ + if (head) { \ + (add)->next = head; /* use add->next as a temp variable */ \ + while ((add)->next->next) { (add)->next = (add)->next->next; } \ + (add)->next->next=(add); \ + } else { \ + (head)=(add); \ + } \ + (add)->next=NULL; \ +} while (0) + +#undef LL_INSERT_INORDER2 +#define LL_INSERT_INORDER2(head,add,cmp,next) \ +do { \ + if ((head) == NULL || (cmp(head, add)) >= 0) { \ + (add)->next = (head); \ + (head) = (add); \ + } else { \ + char *_tmp = (char*)(head); \ + while ((head)->next != NULL && (cmp((head)->next, add)) < 0) { \ + (head) = (head)->next; \ + } \ + (add)->next = (head)->next; \ + (head)->next = (add); \ + UTLIST_RS(head); \ + } \ +} while (0) + +#undef LL_DELETE2 +#define LL_DELETE2(head,del,next) \ +do { \ + if ((head) == (del)) { \ + (head)=(head)->next; \ + } else { \ + char *_tmp = (char*)(head); \ + while ((head)->next && ((head)->next != (del))) { \ + (head) = (head)->next; \ + } \ + if ((head)->next) { \ + (head)->next = ((del)->next); \ + } \ + UTLIST_RS(head); \ + } \ +} while (0) + +#undef LL_REPLACE_ELEM2 +#define LL_REPLACE_ELEM2(head, el, add, next) \ +do { \ + assert((head) != NULL); \ + assert((el) != NULL); \ + assert((add) != NULL); \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + (add)->next = head; \ + while ((add)->next->next && ((add)->next->next != (el))) { \ + (add)->next = (add)->next->next; \ + } \ + if ((add)->next->next) { \ + (add)->next->next = (add); \ + } \ + } \ + (add)->next = (el)->next; \ +} while (0) + +#undef LL_PREPEND_ELEM2 +#define LL_PREPEND_ELEM2(head, el, add, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + (add)->next = (head); \ + while ((add)->next->next && ((add)->next->next != (el))) { \ + (add)->next = (add)->next->next; \ + } \ + if ((add)->next->next) { \ + (add)->next->next = (add); \ + } \ + } \ + (add)->next = (el); \ + } else { \ + LL_APPEND2(head, add, next); \ + } \ +} while (0) \ + +#endif /* NO_DECLTYPE */ + +/****************************************************************************** + * doubly linked list macros (non-circular) * + *****************************************************************************/ +#define DL_PREPEND(head,add) \ + DL_PREPEND2(head,add,prev,next) + +#define DL_PREPEND2(head,add,prev,next) \ +do { \ + (add)->next = (head); \ + if (head) { \ + (add)->prev = (head)->prev; \ + (head)->prev = (add); \ + } else { \ + (add)->prev = (add); \ + } \ + (head) = (add); \ +} while (0) + +#define DL_APPEND(head,add) \ + DL_APPEND2(head,add,prev,next) + +#define DL_APPEND2(head,add,prev,next) \ +do { \ + if (head) { \ + (add)->prev = (head)->prev; \ + (head)->prev->next = (add); \ + (head)->prev = (add); \ + (add)->next = NULL; \ + } else { \ + (head)=(add); \ + (head)->prev = (head); \ + (head)->next = NULL; \ + } \ +} while (0) + +#define DL_INSERT_INORDER(head,add,cmp) \ + DL_INSERT_INORDER2(head,add,cmp,prev,next) + +#define DL_INSERT_INORDER2(head,add,cmp,prev,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + if (head) { \ + DL_LOWER_BOUND2(head, _tmp, add, cmp, next); \ + DL_APPEND_ELEM2(head, _tmp, add, prev, next); \ + } else { \ + (head) = (add); \ + (head)->prev = (head); \ + (head)->next = NULL; \ + } \ +} while (0) + +#define DL_LOWER_BOUND(head,elt,like,cmp) \ + DL_LOWER_BOUND2(head,elt,like,cmp,next) + +#define DL_LOWER_BOUND2(head,elt,like,cmp,next) \ +do { \ + if ((head) == NULL || (cmp(head, like)) >= 0) { \ + (elt) = NULL; \ + } else { \ + for ((elt) = (head); (elt)->next != NULL; (elt) = (elt)->next) { \ + if ((cmp((elt)->next, like)) >= 0) { \ + break; \ + } \ + } \ + } \ +} while (0) + +#define DL_CONCAT(head1,head2) \ + DL_CONCAT2(head1,head2,prev,next) + +#define DL_CONCAT2(head1,head2,prev,next) \ +do { \ + LDECLTYPE(head1) _tmp; \ + if (head2) { \ + if (head1) { \ + UTLIST_CASTASGN(_tmp, (head2)->prev); \ + (head2)->prev = (head1)->prev; \ + (head1)->prev->next = (head2); \ + UTLIST_CASTASGN((head1)->prev, _tmp); \ + } else { \ + (head1)=(head2); \ + } \ + } \ +} while (0) + +#define DL_DELETE(head,del) \ + DL_DELETE2(head,del,prev,next) + +#define DL_DELETE2(head,del,prev,next) \ +do { \ + assert((head) != NULL); \ + assert((del)->prev != NULL); \ + if ((del)->prev == (del)) { \ + (head)=NULL; \ + } else if ((del)==(head)) { \ + (del)->next->prev = (del)->prev; \ + (head) = (del)->next; \ + } else { \ + (del)->prev->next = (del)->next; \ + if ((del)->next) { \ + (del)->next->prev = (del)->prev; \ + } else { \ + (head)->prev = (del)->prev; \ + } \ + } \ +} while (0) + +#define DL_COUNT(head,el,counter) \ + DL_COUNT2(head,el,counter,next) \ + +#define DL_COUNT2(head,el,counter,next) \ +do { \ + (counter) = 0; \ + DL_FOREACH2(head,el,next) { ++(counter); } \ +} while (0) + +#define DL_FOREACH(head,el) \ + DL_FOREACH2(head,el,next) + +#define DL_FOREACH2(head,el,next) \ + for ((el) = (head); el; (el) = (el)->next) + +/* this version is safe for deleting the elements during iteration */ +#define DL_FOREACH_SAFE(head,el,tmp) \ + DL_FOREACH_SAFE2(head,el,tmp,next) + +#define DL_FOREACH_SAFE2(head,el,tmp,next) \ + for ((el) = (head); (el) && ((tmp) = (el)->next, 1); (el) = (tmp)) + +/* these are identical to their singly-linked list counterparts */ +#define DL_SEARCH_SCALAR LL_SEARCH_SCALAR +#define DL_SEARCH LL_SEARCH +#define DL_SEARCH_SCALAR2 LL_SEARCH_SCALAR2 +#define DL_SEARCH2 LL_SEARCH2 + +#define DL_REPLACE_ELEM2(head, el, add, prev, next) \ +do { \ + assert((head) != NULL); \ + assert((el) != NULL); \ + assert((add) != NULL); \ + if ((head) == (el)) { \ + (head) = (add); \ + (add)->next = (el)->next; \ + if ((el)->next == NULL) { \ + (add)->prev = (add); \ + } else { \ + (add)->prev = (el)->prev; \ + (add)->next->prev = (add); \ + } \ + } else { \ + (add)->next = (el)->next; \ + (add)->prev = (el)->prev; \ + (add)->prev->next = (add); \ + if ((el)->next == NULL) { \ + (head)->prev = (add); \ + } else { \ + (add)->next->prev = (add); \ + } \ + } \ +} while (0) + +#define DL_REPLACE_ELEM(head, el, add) \ + DL_REPLACE_ELEM2(head, el, add, prev, next) + +#define DL_PREPEND_ELEM2(head, el, add, prev, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el); \ + (add)->prev = (el)->prev; \ + (el)->prev = (add); \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + (add)->prev->next = (add); \ + } \ + } else { \ + DL_APPEND2(head, add, prev, next); \ + } \ +} while (0) \ + +#define DL_PREPEND_ELEM(head, el, add) \ + DL_PREPEND_ELEM2(head, el, add, prev, next) + +#define DL_APPEND_ELEM2(head, el, add, prev, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el)->next; \ + (add)->prev = (el); \ + (el)->next = (add); \ + if ((add)->next) { \ + (add)->next->prev = (add); \ + } else { \ + (head)->prev = (add); \ + } \ + } else { \ + DL_PREPEND2(head, add, prev, next); \ + } \ +} while (0) \ + +#define DL_APPEND_ELEM(head, el, add) \ + DL_APPEND_ELEM2(head, el, add, prev, next) + +#ifdef NO_DECLTYPE +/* Here are VS2008 / NO_DECLTYPE replacements for a few functions */ + +#undef DL_INSERT_INORDER2 +#define DL_INSERT_INORDER2(head,add,cmp,prev,next) \ +do { \ + if ((head) == NULL) { \ + (add)->prev = (add); \ + (add)->next = NULL; \ + (head) = (add); \ + } else if ((cmp(head, add)) >= 0) { \ + (add)->prev = (head)->prev; \ + (add)->next = (head); \ + (head)->prev = (add); \ + (head) = (add); \ + } else { \ + char *_tmp = (char*)(head); \ + while ((head)->next && (cmp((head)->next, add)) < 0) { \ + (head) = (head)->next; \ + } \ + (add)->prev = (head); \ + (add)->next = (head)->next; \ + (head)->next = (add); \ + UTLIST_RS(head); \ + if ((add)->next) { \ + (add)->next->prev = (add); \ + } else { \ + (head)->prev = (add); \ + } \ + } \ +} while (0) +#endif /* NO_DECLTYPE */ + +/****************************************************************************** + * circular doubly linked list macros * + *****************************************************************************/ +#define CDL_APPEND(head,add) \ + CDL_APPEND2(head,add,prev,next) + +#define CDL_APPEND2(head,add,prev,next) \ +do { \ + if (head) { \ + (add)->prev = (head)->prev; \ + (add)->next = (head); \ + (head)->prev = (add); \ + (add)->prev->next = (add); \ + } else { \ + (add)->prev = (add); \ + (add)->next = (add); \ + (head) = (add); \ + } \ +} while (0) + +#define CDL_PREPEND(head,add) \ + CDL_PREPEND2(head,add,prev,next) + +#define CDL_PREPEND2(head,add,prev,next) \ +do { \ + if (head) { \ + (add)->prev = (head)->prev; \ + (add)->next = (head); \ + (head)->prev = (add); \ + (add)->prev->next = (add); \ + } else { \ + (add)->prev = (add); \ + (add)->next = (add); \ + } \ + (head) = (add); \ +} while (0) + +#define CDL_INSERT_INORDER(head,add,cmp) \ + CDL_INSERT_INORDER2(head,add,cmp,prev,next) + +#define CDL_INSERT_INORDER2(head,add,cmp,prev,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + if (head) { \ + CDL_LOWER_BOUND2(head, _tmp, add, cmp, next); \ + CDL_APPEND_ELEM2(head, _tmp, add, prev, next); \ + } else { \ + (head) = (add); \ + (head)->next = (head); \ + (head)->prev = (head); \ + } \ +} while (0) + +#define CDL_LOWER_BOUND(head,elt,like,cmp) \ + CDL_LOWER_BOUND2(head,elt,like,cmp,next) + +#define CDL_LOWER_BOUND2(head,elt,like,cmp,next) \ +do { \ + if ((head) == NULL || (cmp(head, like)) >= 0) { \ + (elt) = NULL; \ + } else { \ + for ((elt) = (head); (elt)->next != (head); (elt) = (elt)->next) { \ + if ((cmp((elt)->next, like)) >= 0) { \ + break; \ + } \ + } \ + } \ +} while (0) + +#define CDL_DELETE(head,del) \ + CDL_DELETE2(head,del,prev,next) + +#define CDL_DELETE2(head,del,prev,next) \ +do { \ + if (((head)==(del)) && ((head)->next == (head))) { \ + (head) = NULL; \ + } else { \ + (del)->next->prev = (del)->prev; \ + (del)->prev->next = (del)->next; \ + if ((del) == (head)) (head)=(del)->next; \ + } \ +} while (0) + +#define CDL_COUNT(head,el,counter) \ + CDL_COUNT2(head,el,counter,next) \ + +#define CDL_COUNT2(head, el, counter,next) \ +do { \ + (counter) = 0; \ + CDL_FOREACH2(head,el,next) { ++(counter); } \ +} while (0) + +#define CDL_FOREACH(head,el) \ + CDL_FOREACH2(head,el,next) + +#define CDL_FOREACH2(head,el,next) \ + for ((el)=(head);el;(el)=(((el)->next==(head)) ? NULL : (el)->next)) + +#define CDL_FOREACH_SAFE(head,el,tmp1,tmp2) \ + CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next) + +#define CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next) \ + for ((el) = (head), (tmp1) = (head) ? (head)->prev : NULL; \ + (el) && ((tmp2) = (el)->next, 1); \ + (el) = ((el) == (tmp1) ? NULL : (tmp2))) + +#define CDL_SEARCH_SCALAR(head,out,field,val) \ + CDL_SEARCH_SCALAR2(head,out,field,val,next) + +#define CDL_SEARCH_SCALAR2(head,out,field,val,next) \ +do { \ + CDL_FOREACH2(head,out,next) { \ + if ((out)->field == (val)) break; \ + } \ +} while (0) + +#define CDL_SEARCH(head,out,elt,cmp) \ + CDL_SEARCH2(head,out,elt,cmp,next) + +#define CDL_SEARCH2(head,out,elt,cmp,next) \ +do { \ + CDL_FOREACH2(head,out,next) { \ + if ((cmp(out,elt))==0) break; \ + } \ +} while (0) + +#define CDL_REPLACE_ELEM2(head, el, add, prev, next) \ +do { \ + assert((head) != NULL); \ + assert((el) != NULL); \ + assert((add) != NULL); \ + if ((el)->next == (el)) { \ + (add)->next = (add); \ + (add)->prev = (add); \ + (head) = (add); \ + } else { \ + (add)->next = (el)->next; \ + (add)->prev = (el)->prev; \ + (add)->next->prev = (add); \ + (add)->prev->next = (add); \ + if ((head) == (el)) { \ + (head) = (add); \ + } \ + } \ +} while (0) + +#define CDL_REPLACE_ELEM(head, el, add) \ + CDL_REPLACE_ELEM2(head, el, add, prev, next) + +#define CDL_PREPEND_ELEM2(head, el, add, prev, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el); \ + (add)->prev = (el)->prev; \ + (el)->prev = (add); \ + (add)->prev->next = (add); \ + if ((head) == (el)) { \ + (head) = (add); \ + } \ + } else { \ + CDL_APPEND2(head, add, prev, next); \ + } \ +} while (0) + +#define CDL_PREPEND_ELEM(head, el, add) \ + CDL_PREPEND_ELEM2(head, el, add, prev, next) + +#define CDL_APPEND_ELEM2(head, el, add, prev, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el)->next; \ + (add)->prev = (el); \ + (el)->next = (add); \ + (add)->next->prev = (add); \ + } else { \ + CDL_PREPEND2(head, add, prev, next); \ + } \ +} while (0) + +#define CDL_APPEND_ELEM(head, el, add) \ + CDL_APPEND_ELEM2(head, el, add, prev, next) + +#ifdef NO_DECLTYPE +/* Here are VS2008 / NO_DECLTYPE replacements for a few functions */ + +#undef CDL_INSERT_INORDER2 +#define CDL_INSERT_INORDER2(head,add,cmp,prev,next) \ +do { \ + if ((head) == NULL) { \ + (add)->prev = (add); \ + (add)->next = (add); \ + (head) = (add); \ + } else if ((cmp(head, add)) >= 0) { \ + (add)->prev = (head)->prev; \ + (add)->next = (head); \ + (add)->prev->next = (add); \ + (head)->prev = (add); \ + (head) = (add); \ + } else { \ + char *_tmp = (char*)(head); \ + while ((char*)(head)->next != _tmp && (cmp((head)->next, add)) < 0) { \ + (head) = (head)->next; \ + } \ + (add)->prev = (head); \ + (add)->next = (head)->next; \ + (add)->next->prev = (add); \ + (head)->next = (add); \ + UTLIST_RS(head); \ + } \ +} while (0) +#endif /* NO_DECLTYPE */ + +#endif /* UTLIST_H */ diff --git a/notes.txt b/notes.txt new file mode 100644 index 0000000..c26d866 --- /dev/null +++ b/notes.txt @@ -0,0 +1,69 @@ +DBLTW ("DoubleThrow") - Don't Build Languages This Way + +Kinda stack based. Stack entries are "variables". A variable is anything. +Mainly using a stack based VM to make dealing with variables easier. +The compiler is going to be odd. It needs to know about EVERY variable used +in EVERY script. Variables are persistent. Setting one in one script makes +it available in other scripts. The game engine needs to be able to set and +read VM variables somehow. Useful game data such as current map and player +position should be in variables for the scripts to use. + +- We say the VM is "kinda stack based" because we skip all the pushing/popping + for operations where it's not needed. This may go away. + +- Variables are assigned "memory addresses" (index into an array) by the + compiler. This prevents the need to look up where a variable is stored. + +- Exported variables are regular variables that additionally get added to + a lookup array so the game engine can find their "memory address" by name. + +- The map compiler needs to generate a script that associates map names with + map numbers so authors can use map names in scripts. + +- Stupidly simple pre-processor for constants. It just replaces + tokens/variable names with another value. + +- All strings must be in quotes. This allows us to easily determine if + text encountered in a script should be treated as a variable name or not. + +- To use a variable in a text string, prefix it with $$. + +- Do we want to load all script data for a map at once? Or individual scripts? + +- The compiler should produce statistics on memory requirements that we can + send to the game at startup to configure the VM and not waste resources. + +--- + +Preprocessor: + + define + include + +Keywords: + + export + set + add + subtract + if [<,>,!=,==]