Arrays and dictionaries have been implemented, read more

The arrays and dictionaries are currently being printed out correctly,
afaik. This means I should be able to go back and work on the type
system, assuming nothing happens.
This commit is contained in:
2022-08-10 17:42:04 +01:00
parent 6a883bde96
commit 67f7b3e436
13 changed files with 819 additions and 36 deletions

View File

@@ -2,6 +2,10 @@
#include "memory.h"
#include "literal.h"
#include "literal_array.h"
#include "literal_dictionary.h"
#include <stdio.h>
void initCompiler(Compiler* compiler) {
@@ -90,6 +94,72 @@ void writeCompiler(Compiler* compiler, Node* node) {
compiler->bytecode[compiler->count++] = (unsigned char)OP_SCOPE_END; //1 byte
break;
case NODE_COMPOUND: {
int index = -1;
//for both, stored as an array
LiteralArray* store = ALLOCATE(LiteralArray, 1);
initLiteralArray(store);
//emit an array or a dictionary definition
if (node->compound.nodes->type == NODE_PAIR) {
//ensure each literal key and value are in the cache, individually
for (int i = 0; i < node->compound.count; i++) {
//keys
int key = findLiteralIndex(&compiler->literalCache, node->compound.nodes[i].pair.left->atomic.literal);
if (key < 0) {
key = pushLiteralArray(&compiler->literalCache, node->compound.nodes[i].pair.left->atomic.literal);
}
//values
int val = findLiteralIndex(&compiler->literalCache, node->compound.nodes[i].pair.right->atomic.literal);
if (val < 0) {
val = pushLiteralArray(&compiler->literalCache, node->compound.nodes[i].pair.right->atomic.literal);
}
pushLiteralArray(store, TO_INTEGER_LITERAL(key));
pushLiteralArray(store, TO_INTEGER_LITERAL(val));
}
//push the store to the cache, with instructions about how pack it
index = pushLiteralArray(&compiler->literalCache, TO_DICTIONARY_LITERAL(store));
}
else {
//ensure each literal value is in the cache, individually
for (int i = 0; i < node->compound.count; i++) {
//values
int val = findLiteralIndex(&compiler->literalCache, node->compound.nodes[i].atomic.literal);
if (val < 0) {
val = pushLiteralArray(&compiler->literalCache, node->compound.nodes[i].atomic.literal);
}
pushLiteralArray(store, TO_INTEGER_LITERAL(val));
}
//push the store to the cache, with instructions about how pack it
index = pushLiteralArray(&compiler->literalCache, TO_ARRAY_LITERAL(store));
}
//push the node opcode to the bytecode
if (index >= 256) {
//push a "long" index
compiler->bytecode[compiler->count++] = OP_LITERAL_LONG; //1 byte
*((unsigned short*)(compiler->bytecode + compiler->count)) = (unsigned short)index; //2 bytes
compiler->count += sizeof(unsigned short);
}
else {
//push the index
compiler->bytecode[compiler->count++] = OP_LITERAL; //1 byte
compiler->bytecode[compiler->count++] = (unsigned char)index; //1 byte
}
}
break;
case NODE_PAIR:
fprintf(stderr, "[Internal] NODE_PAIR encountered in writeCompiler()");
break;
case NODE_VAR_TYPES:
//TODO: OP_TYPE_DECL
break;
@@ -221,6 +291,39 @@ unsigned char* collateCompiler(Compiler* compiler, int* size) {
}
break;
case LITERAL_ARRAY: {
emitByte(&collation, &capacity, &count, LITERAL_ARRAY);
LiteralArray* ptr = AS_ARRAY(compiler->literalCache.literals[i]);
//length of the array, as a short
emitShort(&collation, &capacity, &count, ptr->count);
//each element of the array
for (int i = 0; i < ptr->count; i++) {
emitShort(&collation, &capacity, &count, (unsigned short)AS_INTEGER(ptr->literals[i])); //shorts representing the indexes of the values
}
freeLiteralArray(ptr);
}
break;
case LITERAL_DICTIONARY:
emitByte(&collation, &capacity, &count, LITERAL_DICTIONARY);
LiteralArray* ptr = AS_ARRAY(compiler->literalCache.literals[i]); //used an array for storage above
//length of the array, as a short
emitShort(&collation, &capacity, &count, ptr->count); //count is the array size, NOT the dictionary size
//each element of the array
for (int i = 0; i < ptr->count; i++) {
emitShort(&collation, &capacity, &count, (unsigned short)AS_INTEGER(ptr->literals[i])); //shorts representing the indexes of the values
}
freeLiteralArray(ptr);
break;
default:
fprintf(stderr, "[Internal] Unknown literal type encountered within literal cache\n");
return NULL;

View File

@@ -306,6 +306,11 @@ static void execInterpreter(Interpreter* interpreter) {
}
void runInterpreter(Interpreter* interpreter) {
if (!interpreter->bytecode) {
printf(ERROR "Error: No valid bytecode given\n" RESET);
return;
}
//header section
const unsigned char major = readByte(interpreter->bytecode, &interpreter->count);
const unsigned char minor = readByte(interpreter->bytecode, &interpreter->count);
@@ -385,6 +390,53 @@ void runInterpreter(Interpreter* interpreter) {
}
}
break;
case LITERAL_ARRAY: {
LiteralArray* array = ALLOCATE(LiteralArray, 1);
initLiteralArray(array);
unsigned short length = readShort(interpreter->bytecode, &interpreter->count);
//read each index, then unpack the value from the existing literal cache
for (int i = 0; i < length; i++) {
int index = readShort(interpreter->bytecode, &interpreter->count);
pushLiteralArray(array, interpreter->literalCache.literals[index]);
}
if (command.verbose) {
printf("(array ");
printLiteral(TO_ARRAY_LITERAL(array));
printf(")\n");
}
//finally, push the array proper
pushLiteralArray(&interpreter->literalCache, TO_ARRAY_LITERAL(array));
}
break;
case LITERAL_DICTIONARY: {
LiteralDictionary* dictionary = ALLOCATE(LiteralDictionary, 1);
initLiteralDictionary(dictionary);
unsigned short length = readShort(interpreter->bytecode, &interpreter->count);
//read each index, then unpack the value from the existing literal cache
for (int i = 0; i < length / 2; i++) {
int key = readShort(interpreter->bytecode, &interpreter->count);
int val = readShort(interpreter->bytecode, &interpreter->count);
setLiteralDictionary(dictionary, interpreter->literalCache.literals[key], interpreter->literalCache.literals[val]);
}
if (command.verbose) {
printf("(dictionary ");
printLiteral(TO_DICTIONARY_LITERAL(dictionary));
printf(")\n");
}
//finally, push the dictionary proper
pushLiteralArray(&interpreter->literalCache, TO_DICTIONARY_LITERAL(dictionary));
}
break;
}
}

View File

@@ -69,6 +69,7 @@ static void eatWhitespace(Lexer* lexer) {
advance(lexer);
break;
}
break;
default:
return;

View File

@@ -1,13 +1,35 @@
#include "literal.h"
#include "memory.h"
#include "literal_array.h"
#include "literal_dictionary.h"
#include <stdio.h>
#include <string.h>
//utils
static void stdoutWrapper(const char* output) {
fprintf(stdout, output);
}
//buffer the prints
static char* globalPrintBuffer = NULL;
static size_t globalPrintCapacity = 0;
static size_t globalPrintCount = 0;
static void printToBuffer(const char* str) {
while (strlen(str) + globalPrintCount > globalPrintCapacity) {
int oldCapacity = globalPrintCapacity;
globalPrintCapacity = GROW_CAPACITY(globalPrintCapacity);
globalPrintBuffer = GROW_ARRAY(char, globalPrintBuffer, oldCapacity, globalPrintCapacity);
}
snprintf(globalPrintBuffer + globalPrintCount, strlen(str) + 1, "%s", str);
globalPrintCount += strlen(str);
}
//exposed functions
void printLiteral(Literal literal) {
printLiteralCustom(literal, stdoutWrapper);
}
@@ -37,12 +59,93 @@ void printLiteralCustom(Literal literal, void (printFn)(const char*)) {
break;
case LITERAL_STRING: {
char buffer[256];
snprintf(buffer, 256, "%.*s", STRLEN(literal), AS_STRING(literal));
char buffer[4096];
snprintf(buffer, 4096, "\"%.*s\"", STRLEN(literal), AS_STRING(literal));
printFn(buffer);
}
break;
case LITERAL_ARRAY: {
LiteralArray* ptr = AS_ARRAY(literal);
//hold potential parent-call buffers
char* cacheBuffer = globalPrintBuffer;
globalPrintBuffer = NULL;
int cacheCapacity = globalPrintCapacity;
globalPrintCapacity = 0;
int cacheCount = globalPrintCount;
globalPrintCount = 0;
//print the contents to the global buffer
printToBuffer("[");
for (int i = 0; i < ptr->count; i++) {
printLiteralCustom(ptr->literals[i], printToBuffer);
if (i + 1 < ptr->count) {
printToBuffer(",");
}
}
printToBuffer("]");
//swap the parent-call buffer back into place
char* printBuffer = globalPrintBuffer;
int printCapacity = globalPrintCapacity;
int printCount = globalPrintCount;
globalPrintBuffer = cacheBuffer;
globalPrintCapacity = cacheCapacity;
globalPrintCount = cacheCount;
//finally, output and cleanup
printFn(printBuffer);
FREE_ARRAY(char, printBuffer, printCapacity);
}
break;
case LITERAL_DICTIONARY: {
LiteralDictionary* ptr = AS_DICTIONARY(literal);
//hold potential parent-call buffers
char* cacheBuffer = globalPrintBuffer;
globalPrintBuffer = NULL;
int cacheCapacity = globalPrintCapacity;
globalPrintCapacity = 0;
int cacheCount = globalPrintCount;
globalPrintCount = 0;
//print the contents to the global buffer
int delimCount = 0;
printToBuffer("[");
for (int i = 0; i < ptr->capacity; i++) {
if (ptr->entries[i].key.type == LITERAL_NULL) {
continue;
}
if (delimCount++ > 0) {
printToBuffer(",");
}
printLiteralCustom(ptr->entries[i].key, printToBuffer);
printToBuffer(":");
printLiteralCustom(ptr->entries[i].value, printToBuffer);
}
printToBuffer("]");
//swap the parent-call buffer back into place
char* printBuffer = globalPrintBuffer;
int printCapacity = globalPrintCapacity;
int printCount = globalPrintCount;
globalPrintBuffer = cacheBuffer;
globalPrintCapacity = cacheCapacity;
globalPrintCount = cacheCount;
//finally, output and cleanup
printFn(printBuffer);
FREE_ARRAY(char, printBuffer, printCapacity);
}
break;
case LITERAL_IDENTIFIER: {
char buffer[256];
snprintf(buffer, 256, "%.*s", STRLEN_I(literal), AS_IDENTIFIER(literal));

View File

@@ -10,10 +10,11 @@ typedef enum {
LITERAL_INTEGER,
LITERAL_FLOAT,
LITERAL_STRING,
// LITERAL_ARRAY,
// LITERAL_DICTIONARY,
LITERAL_ARRAY,
LITERAL_DICTIONARY,
// LITERAL_FUNCTION,
LITERAL_IDENTIFIER,
// LITERAL_TYPE,
} LiteralType;
typedef struct {
@@ -27,15 +28,17 @@ typedef struct {
int length;
} string;
// //experimental
// void* array;
// void* dictionary;
void* array;
void* dictionary;
// void* function;
struct { //for variable names
char* ptr;
int length;
} identifier;
//TODO: type
} as;
} Literal;
@@ -53,8 +56,8 @@ typedef struct {
#define AS_INTEGER(value) ((value).as.integer)
#define AS_FLOAT(value) ((value).as.number)
#define AS_STRING(value) ((value).as.string.ptr)
// #define AS_ARRAY(value)
// #define AS_DICTIONARY(value)
#define AS_ARRAY(value) ((value).as.array)
#define AS_DICTIONARY(value) ((value).as.dictionary)
// #define AS_FUNCTION(value)
#define AS_IDENTIFIER(value) ((value).as.identifier.ptr)
@@ -63,8 +66,8 @@ typedef struct {
#define TO_INTEGER_LITERAL(value) ((Literal){LITERAL_INTEGER, { .integer = value }})
#define TO_FLOAT_LITERAL(value) ((Literal){LITERAL_FLOAT, { .number = value }})
#define TO_STRING_LITERAL(value) _toStringLiteral(value)
// #define TO_ARRAY_LITERAL
// #define TO_DICTIONARY_LITERAL
#define TO_ARRAY_LITERAL(value) ((Literal){LITERAL_ARRAY, { .array = value }})
#define TO_DICTIONARY_LITERAL(value) ((Literal){LITERAL_DICTIONARY, { .dictionary = value }})
// #define TO_FUNCTION_LITERAL
#define TO_IDENTIFIER_LITERAL(value) _toIdentifierLiteral(value)

View File

@@ -37,14 +37,26 @@ void freeNode(Node* node) {
for (int i = 0; i < node->block.count; i++) {
freeNode(node->block.nodes + i);
}
//each sub-node gets freed individually
FREE_ARRAY(Node, node->block.nodes, node->block.capacity);
break;
case NODE_COMPOUND:
for (int i = 0; i < node->compound.count; i++) {
freeNode(node->compound.nodes + i);
}
FREE_ARRAY(Node, node->compound.nodes, node->compound.capacity);
break;
case NODE_PAIR:
freeNode(node->pair.left);
freeNode(node->pair.right);
break;
case NODE_VAR_TYPES:
for (int i = 0; i < node->varTypes.count; i++) {
freeNode(node->varTypes.nodes + 1);
}
//each sub-node gets freed individually
FREE_ARRAY(Node, node->varTypes.nodes, node->varTypes.capacity);
break;
case NODE_VAR_DECL:
@@ -53,8 +65,6 @@ void freeNode(Node* node) {
freeNode(node->varDecl.expression);
break;
}
FREE(Node, node);
}
void emitNodeLiteral(Node** nodeHandle, Literal literal) {
@@ -105,6 +115,27 @@ void emitNodeBlock(Node** nodeHandle) {
*nodeHandle = tmp;
}
void emitNodeCompound(Node** nodeHandle) {
Node* tmp = ALLOCATE(Node, 1);
tmp->type = NODE_COMPOUND;
tmp->compound.nodes = NULL;
tmp->compound.capacity = 0;
tmp->compound.count = 0;
*nodeHandle = tmp;
}
void emitNodePair(Node** nodeHandle, Node* left, Node* right) {
Node* tmp = ALLOCATE(Node, 1);
tmp->type = NODE_PAIR;
tmp->pair.left = left;
tmp->pair.right = right;
*nodeHandle = tmp;
}
void emitNodeVarTypes(Node** nodeHandle, unsigned char mask) {
Node* tmp = ALLOCATE(Node, 1);
@@ -172,8 +203,26 @@ void printNode(Node* node) {
printf("}\n");
break;
case NODE_COMPOUND:
printf("compound[\n");
for (int i = 0; i < node->compound.count; i++) {
printNode(&(node->compound.nodes[i]));
}
printf("]\n");
break;
case NODE_PAIR:
printf("pair-left:");
printNode(node->pair.left);
printf(";pair-right:");
printNode(node->pair.right);
printf(";");
break;
case NODE_VAR_TYPES:
printf("[\n");
printf("type[\n");
for (int i = 0; i < node->varTypes.count; i++) {
printNode(&(node->varTypes.nodes[i]));

View File

@@ -9,10 +9,12 @@ typedef union _node Node;
typedef enum NodeType {
NODE_ERROR,
NODE_LITERAL, //a simple value
NODE_UNARY, //one child
NODE_BINARY, //two children, left and right
NODE_UNARY, //one child + opcode
NODE_BINARY, //two children, left and right + opcode
NODE_GROUPING, //one child
NODE_BLOCK, //contains sub-node array
NODE_BLOCK, //contains a sub-node array
NODE_COMPOUND, //contains a sub-node array
NODE_PAIR, //contains a left and right
NODE_VAR_TYPES, //contains a type mask and a sub-node array for compound types
NODE_VAR_DECL, //contains identifier literal, typenode, expression definition
// NODE_CONDITIONAL, //three children: conditional, then path, else path
@@ -48,6 +50,19 @@ typedef struct NodeBlock {
int count;
} NodeBlock;
typedef struct NodeCompound {
NodeType type;
Node* nodes;
int capacity;
int count;
} NodeCompound;
typedef struct NodePair {
NodeType type;
Node* left;
Node* right;
} NodePair;
typedef struct NodeVarTypes {
NodeType type;
unsigned char mask;
@@ -70,6 +85,8 @@ union _node {
NodeBinary binary;
NodeGrouping grouping;
NodeBlock block;
NodeCompound compound;
NodePair pair;
NodeVarTypes varTypes;
NodeVarDecl varDecl;
};
@@ -80,6 +97,8 @@ void emitNodeUnary(Node** nodeHandle, Opcode opcode);
void emitNodeBinary(Node** nodeHandle, Node* rhs, Opcode opcode);
void emitNodeGrouping(Node** nodeHandle);
void emitNodeBlock(Node** nodeHandle);
void emitNodeCompound(Node** nodeHandle);
void emitNodePair(Node** nodeHandle, Node* left, Node* right);
void emitNodeVarTypes(Node** nodeHandle, unsigned char mask);
void emitNodeVarDecl(Node** nodeHandle, Literal identifier, Node* varType, Node* expression);

View File

@@ -36,6 +36,7 @@ static void advance(Parser* parser) {
if (parser->current.type == TOKEN_ERROR) {
error(parser, parser->current, "Lexer error");
printf(parser->lexer->source);
}
}
@@ -115,6 +116,106 @@ static void declaration(Parser* parser, Node** nodeHandle);
static void parsePrecedence(Parser* parser, Node** nodeHandle, PrecedenceRule rule);
//the expression rules
static Opcode compound(Parser* parser, Node** nodeHandle, bool canBeAssigned) {
//read either an array or a dictionary into a literal node
int iterations = 0; //count the number of entries iterated over
//compound nodes to store what is read
Node* array = NULL;
Node* dictionary = NULL;
while (!match(parser, TOKEN_BRACKET_RIGHT)) {
//if empty dictionary, there will be a colon between the brackets
if (iterations == 0 && match(parser, TOKEN_COLON)) {
consume(parser, TOKEN_BRACE_RIGHT, "Expected ']' at the end of empty dictionary definition");
break;
}
if (iterations > 0) {
consume(parser, TOKEN_COMMA, "Expected ',' in array or dictionary");
}
iterations++;
Node* left = NULL;
Node* right = NULL;
//store the left
parsePrecedence(parser, &left, PREC_PRIMARY);
//detect a dictionary
if (match(parser, TOKEN_COLON)) {
parsePrecedence(parser, &right, PREC_PRIMARY);
//check we ARE defining a dictionary
if (array) {
error(parser, parser->previous, "Incorrect detection between array and dictionary");
freeNode(array);
return OP_EOF;
}
//init the dictionary
if (!dictionary) {
emitNodeCompound(&dictionary);
}
//grow the node if needed
if (dictionary->compound.capacity < dictionary->compound.count + 1) {
int oldCapacity = dictionary->compound.capacity;
dictionary->compound.capacity = GROW_CAPACITY(oldCapacity);
dictionary->compound.nodes = GROW_ARRAY(Node, dictionary->compound.nodes, oldCapacity, dictionary->compound.capacity);
}
//store the left and right in the node
Node* pair = NULL;
emitNodePair(&pair, left, right);
dictionary->compound.nodes[dictionary->compound.count++] = *pair;
}
//detect an array
else {
//check we ARE defining an array
if (dictionary) {
error(parser, parser->current, "Incorrect detection between array and dictionary");
freeNode(dictionary);
return OP_EOF;
}
//init the array
if (!array) {
emitNodeCompound(&array);
}
//grow the node if needed
if (array->compound.capacity < array->compound.count + 1) {
int oldCapacity = array->compound.capacity;
array->compound.capacity = GROW_CAPACITY(oldCapacity);
array->compound.nodes = GROW_ARRAY(Node, array->compound.nodes, oldCapacity, array->compound.capacity);
}
//store the left in the array
array->compound.nodes[array->compound.count++] = *left;
}
}
//save the result
if (array) {
(*nodeHandle) = array;
}
else if (dictionary) {
(*nodeHandle) = dictionary;
}
else {
error(parser, parser->current, "[internal] Couldn't determine if should save an array or dictionary");
}
//ignored
return OP_EOF;
}
static Opcode string(Parser* parser, Node** nodeHandle, bool canBeAssigned) {
//handle strings
switch(parser->previous.type) {
@@ -330,7 +431,7 @@ ParseRule parseRules[] = { //must match the token types
//logical operators
{grouping, NULL, PREC_CALL},// TOKEN_PAREN_LEFT,
{NULL, NULL, PREC_NONE},// TOKEN_PAREN_RIGHT,
{NULL, NULL, PREC_NONE},// TOKEN_BRACKET_LEFT,
{compound, NULL, PREC_CALL},// TOKEN_BRACKET_LEFT,
{NULL, NULL, PREC_NONE},// TOKEN_BRACKET_RIGHT,
{NULL, NULL, PREC_NONE},// TOKEN_BRACE_LEFT,
{NULL, NULL, PREC_NONE},// TOKEN_BRACE_RIGHT,
@@ -348,7 +449,7 @@ ParseRule parseRules[] = { //must match the token types
{NULL, NULL, PREC_NONE},// TOKEN_ASSIGN,
{NULL, NULL, PREC_NONE},// TOKEN_COLON,
{NULL, NULL, PREC_NONE},// TOKEN_SEMICOLON,
{NULL, NULL, PREC_NONE},// TOKEN_COMMA,
{NULL, NULL, PREC_CALL},// TOKEN_COMMA,
{NULL, NULL, PREC_NONE},// TOKEN_DOT,
{NULL, NULL, PREC_NONE},// TOKEN_PIPE,
{NULL, NULL, PREC_NONE},// TOKEN_REST,
@@ -570,9 +671,9 @@ static void assertStmt(Parser* parser, Node* node) {
node->type = NODE_BINARY;
node->unary.opcode = OP_ASSERT;
expression(parser, &(node->binary.left));
parsePrecedence(parser, &(node->binary.left), PREC_PRIMARY);
consume(parser, TOKEN_COMMA, "Expected ',' in assert statement");
expression(parser, &(node->binary.right));
parsePrecedence(parser, &(node->binary.right), PREC_PRIMARY);
consume(parser, TOKEN_SEMICOLON, "Expected ';' at end of assert statement");
}
@@ -607,7 +708,7 @@ static void statement(Parser* parser, Node* node) {
//declarations and definitions
static void readVarType(Parser* parser, Node** nodeHandle) {
//TODO: compound types with the "type" keyword
//TODO: custom types with the "type" keyword
advance(parser);
unsigned char typeMask = 0;

View File

@@ -25,7 +25,7 @@ char* readFile(char* path, size_t* fileSize) {
*fileSize = ftell(file);
rewind(file);
char* buffer = (char*)malloc(*fileSize);
char* buffer = (char*)malloc(*fileSize + 1);
if (buffer == NULL) {
fprintf(stderr, "Not enough memory to read \"%s\"\n", path);
@@ -34,6 +34,8 @@ char* readFile(char* path, size_t* fileSize) {
size_t bytesRead = fread(buffer, sizeof(char), *fileSize, file);
buffer[*fileSize] = '\0'; //NOTE: fread doesn't append this
if (bytesRead < *fileSize) {
fprintf(stderr, "Could not read file \"%s\"\n", path);
exit(-1);
@@ -109,13 +111,19 @@ void runBinary(unsigned char* tb, size_t size) {
void runBinaryFile(char* fname) {
size_t size = 0; //not used
unsigned char* tb = (unsigned char*)readFile(fname, &size);
if (!tb) {
return;
}
runBinary(tb, size);
//interpreter takes ownership of the binary data
}
void runSource(char* source) {
size_t size;
size_t size = 0;
unsigned char* tb = compileString(source, &size);
if (!tb) {
return;
}
runBinary(tb, size);
}
@@ -169,11 +177,11 @@ void repl() {
int size = 0;
unsigned char* tb = collateCompiler(&compiler, &size);
// for (int i = 0; i < size; i++) {
// printf("%d ", tb[i]);
// }
for (int i = 0; i < size; i++) {
printf("%d ", tb[i]);
}
// printf("\n");
printf("\n");
//run the bytecode
initInterpreter(&interpreter, tb, size);