mirror of
https://github.com/krgamestudios/Toy.git
synced 2026-04-15 23:04:08 +10:00
Added the opaque data type
This commit is contained in:
6
scripts/test/opaque-data-type.toy
Normal file
6
scripts/test/opaque-data-type.toy
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
//test the opaque data type works
|
||||||
|
|
||||||
|
var o = produce();
|
||||||
|
|
||||||
|
consume(o);
|
||||||
|
|
||||||
@@ -165,6 +165,10 @@ Literal copyLiteral(Literal original) {
|
|||||||
return lit;
|
return lit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case LITERAL_OPAQUE: {
|
||||||
|
return original; //literally a shallow copy
|
||||||
|
}
|
||||||
|
|
||||||
case LITERAL_DICTIONARY_INTERMEDIATE: {
|
case LITERAL_DICTIONARY_INTERMEDIATE: {
|
||||||
LiteralArray* array = ALLOCATE(LiteralArray, 1);
|
LiteralArray* array = ALLOCATE(LiteralArray, 1);
|
||||||
initLiteralArray(array);
|
initLiteralArray(array);
|
||||||
@@ -327,6 +331,9 @@ bool literalsAreEqual(Literal lhs, Literal rhs) {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
case LITERAL_OPAQUE:
|
||||||
|
return false; //IDK what this is!
|
||||||
|
|
||||||
case LITERAL_ANY:
|
case LITERAL_ANY:
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
@@ -389,6 +396,7 @@ int hashLiteral(Literal lit) {
|
|||||||
case LITERAL_TYPE:
|
case LITERAL_TYPE:
|
||||||
return AS_TYPE(lit).typeOf; //nothing else I can do
|
return AS_TYPE(lit).typeOf; //nothing else I can do
|
||||||
|
|
||||||
|
case LITERAL_OPAQUE:
|
||||||
case LITERAL_ANY:
|
case LITERAL_ANY:
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
@@ -639,6 +647,10 @@ void printLiteralCustom(Literal literal, void (printFn)(const char*)) {
|
|||||||
printToBuffer("type");
|
printToBuffer("type");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case LITERAL_OPAQUE:
|
||||||
|
printToBuffer("opaque");
|
||||||
|
break;
|
||||||
|
|
||||||
case LITERAL_ANY:
|
case LITERAL_ANY:
|
||||||
printToBuffer("any");
|
printToBuffer("any");
|
||||||
break;
|
break;
|
||||||
@@ -676,6 +688,10 @@ void printLiteralCustom(Literal literal, void (printFn)(const char*)) {
|
|||||||
printFn("Unprintable literal found");
|
printFn("Unprintable literal found");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case LITERAL_OPAQUE:
|
||||||
|
printFn("(opaque)");
|
||||||
|
break;
|
||||||
|
|
||||||
case LITERAL_ANY:
|
case LITERAL_ANY:
|
||||||
printFn("(any)");
|
printFn("(any)");
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ typedef enum {
|
|||||||
LITERAL_FUNCTION,
|
LITERAL_FUNCTION,
|
||||||
LITERAL_IDENTIFIER,
|
LITERAL_IDENTIFIER,
|
||||||
LITERAL_TYPE,
|
LITERAL_TYPE,
|
||||||
|
LITERAL_OPAQUE,
|
||||||
LITERAL_ANY,
|
LITERAL_ANY,
|
||||||
|
|
||||||
//these are meta-level types - not for general use
|
//these are meta-level types - not for general use
|
||||||
@@ -58,6 +59,8 @@ typedef struct {
|
|||||||
int capacity;
|
int capacity;
|
||||||
int count;
|
int count;
|
||||||
} type;
|
} type;
|
||||||
|
|
||||||
|
void* opaque;
|
||||||
} as;
|
} as;
|
||||||
} Literal;
|
} Literal;
|
||||||
|
|
||||||
@@ -72,6 +75,7 @@ typedef struct {
|
|||||||
#define IS_FUNCTION_NATIVE(value) ((value).type == LITERAL_FUNCTION_NATIVE)
|
#define IS_FUNCTION_NATIVE(value) ((value).type == LITERAL_FUNCTION_NATIVE)
|
||||||
#define IS_IDENTIFIER(value) ((value).type == LITERAL_IDENTIFIER)
|
#define IS_IDENTIFIER(value) ((value).type == LITERAL_IDENTIFIER)
|
||||||
#define IS_TYPE(value) ((value).type == LITERAL_TYPE)
|
#define IS_TYPE(value) ((value).type == LITERAL_TYPE)
|
||||||
|
#define IS_OPAQUE(value) ((value).type == LITERAL_OPAQUE)
|
||||||
|
|
||||||
#define AS_BOOLEAN(value) ((value).as.boolean)
|
#define AS_BOOLEAN(value) ((value).as.boolean)
|
||||||
#define AS_INTEGER(value) ((value).as.integer)
|
#define AS_INTEGER(value) ((value).as.integer)
|
||||||
@@ -82,6 +86,7 @@ typedef struct {
|
|||||||
#define AS_FUNCTION(value) ((value).as.function)
|
#define AS_FUNCTION(value) ((value).as.function)
|
||||||
#define AS_IDENTIFIER(value) ((value).as.identifier.ptr)
|
#define AS_IDENTIFIER(value) ((value).as.identifier.ptr)
|
||||||
#define AS_TYPE(value) ((value).as.type)
|
#define AS_TYPE(value) ((value).as.type)
|
||||||
|
#define AS_OPAQUE(value) ((value).as.opaque)
|
||||||
|
|
||||||
#define TO_NULL_LITERAL ((Literal){LITERAL_NULL, { .integer = 0 }})
|
#define TO_NULL_LITERAL ((Literal){LITERAL_NULL, { .integer = 0 }})
|
||||||
#define TO_BOOLEAN_LITERAL(value) ((Literal){LITERAL_BOOLEAN, { .boolean = value }})
|
#define TO_BOOLEAN_LITERAL(value) ((Literal){LITERAL_BOOLEAN, { .boolean = value }})
|
||||||
@@ -93,6 +98,7 @@ typedef struct {
|
|||||||
#define TO_FUNCTION_LITERAL(value, l) ((Literal){LITERAL_FUNCTION, { .function.bytecode = value, .function.scope = NULL, .function.length = l }})
|
#define TO_FUNCTION_LITERAL(value, l) ((Literal){LITERAL_FUNCTION, { .function.bytecode = value, .function.scope = NULL, .function.length = l }})
|
||||||
#define TO_IDENTIFIER_LITERAL(value, l) _toIdentifierLiteral(value, l)
|
#define TO_IDENTIFIER_LITERAL(value, l) _toIdentifierLiteral(value, l)
|
||||||
#define TO_TYPE_LITERAL(value, c) ((Literal){ LITERAL_TYPE, { .type.typeOf = value, .type.constant = c, .type.subtypes = NULL, .type.capacity = 0, .type.count = 0 }})
|
#define TO_TYPE_LITERAL(value, c) ((Literal){ LITERAL_TYPE, { .type.typeOf = value, .type.constant = c, .type.subtypes = NULL, .type.capacity = 0, .type.count = 0 }})
|
||||||
|
#define TO_OPAQUE_LITERAL(value) ((Literal){ LITERAL_OPAQUE, { .opaque = value }})
|
||||||
|
|
||||||
TOY_API void freeLiteral(Literal literal);
|
TOY_API void freeLiteral(Literal literal);
|
||||||
|
|
||||||
|
|||||||
@@ -136,13 +136,18 @@ void freeLiteralDictionary(LiteralDictionary* dictionary) {
|
|||||||
|
|
||||||
void setLiteralDictionary(LiteralDictionary* dictionary, Literal key, Literal value) {
|
void setLiteralDictionary(LiteralDictionary* dictionary, Literal key, Literal value) {
|
||||||
if (IS_NULL(key)) {
|
if (IS_NULL(key)) {
|
||||||
fprintf(stderr, ERROR "Dictionaries can't have null keys (get)\n" RESET);
|
fprintf(stderr, ERROR "Dictionaries can't have null keys (set)\n" RESET);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//BUGFIX: Can't hash a function
|
//BUGFIX: Can't hash a function
|
||||||
if (IS_FUNCTION(key) || IS_FUNCTION_NATIVE(key)) {
|
if (IS_FUNCTION(key) || IS_FUNCTION_NATIVE(key)) {
|
||||||
fprintf(stderr, ERROR "Dictionaries can't have function keys (get)\n" RESET);
|
fprintf(stderr, ERROR "Dictionaries can't have function keys (set)\n" RESET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IS_OPAQUE(key)) {
|
||||||
|
fprintf(stderr, ERROR "Dictionaries can't have opaque keys (set)\n" RESET);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,13 +161,18 @@ void setLiteralDictionary(LiteralDictionary* dictionary, Literal key, Literal va
|
|||||||
|
|
||||||
Literal getLiteralDictionary(LiteralDictionary* dictionary, Literal key) {
|
Literal getLiteralDictionary(LiteralDictionary* dictionary, Literal key) {
|
||||||
if (IS_NULL(key)) {
|
if (IS_NULL(key)) {
|
||||||
fprintf(stderr, ERROR "Dictionaries can't have null keys (set)\n" RESET);
|
fprintf(stderr, ERROR "Dictionaries can't have null keys (get)\n" RESET);
|
||||||
return TO_NULL_LITERAL;
|
return TO_NULL_LITERAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
//BUGFIX: Can't hash a function
|
//BUGFIX: Can't hash a function
|
||||||
if (IS_FUNCTION(key) || IS_FUNCTION_NATIVE(key)) {
|
if (IS_FUNCTION(key) || IS_FUNCTION_NATIVE(key)) {
|
||||||
fprintf(stderr, ERROR "Dictionaries can't have function keys (set)\n" RESET);
|
fprintf(stderr, ERROR "Dictionaries can't have function keys (get)\n" RESET);
|
||||||
|
return TO_NULL_LITERAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IS_OPAQUE(key)) {
|
||||||
|
fprintf(stderr, ERROR "Dictionaries can't have opaque keys (get)\n" RESET);
|
||||||
return TO_NULL_LITERAL;
|
return TO_NULL_LITERAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,6 +198,11 @@ void removeLiteralDictionary(LiteralDictionary* dictionary, Literal key) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (IS_OPAQUE(key)) {
|
||||||
|
fprintf(stderr, ERROR "Dictionaries can't have opaque keys (remove)\n" RESET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, hashLiteral(key), true);
|
_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, hashLiteral(key), true);
|
||||||
|
|
||||||
if (entry != NULL) {
|
if (entry != NULL) {
|
||||||
|
|||||||
165
test/test_opaque_data_type.c
Normal file
165
test/test_opaque_data_type.c
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
#include "lexer.h"
|
||||||
|
#include "parser.h"
|
||||||
|
#include "compiler.h"
|
||||||
|
#include "interpreter.h"
|
||||||
|
|
||||||
|
#include "console_colors.h"
|
||||||
|
|
||||||
|
#include "memory.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
//supress the print output
|
||||||
|
static void noPrintFn(const char* output) {
|
||||||
|
//NO OP
|
||||||
|
}
|
||||||
|
|
||||||
|
//compilation functions
|
||||||
|
char* readFile(char* path, size_t* fileSize) {
|
||||||
|
FILE* file = fopen(path, "rb");
|
||||||
|
|
||||||
|
if (file == NULL) {
|
||||||
|
fprintf(stderr, ERROR "Could not open file \"%s\"\n" RESET, path);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fseek(file, 0L, SEEK_END);
|
||||||
|
*fileSize = ftell(file);
|
||||||
|
rewind(file);
|
||||||
|
|
||||||
|
char* buffer = (char*)malloc(*fileSize + 1);
|
||||||
|
|
||||||
|
if (buffer == NULL) {
|
||||||
|
fprintf(stderr, ERROR "Not enough memory to read \"%s\"\n" RESET, path);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t bytesRead = fread(buffer, sizeof(char), *fileSize, file);
|
||||||
|
|
||||||
|
buffer[*fileSize] = '\0'; //NOTE: fread doesn't append this
|
||||||
|
|
||||||
|
if (bytesRead < *fileSize) {
|
||||||
|
fprintf(stderr, ERROR "Could not read file \"%s\"\n" RESET, path);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(file);
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char* compileString(char* source, size_t* size) {
|
||||||
|
Lexer lexer;
|
||||||
|
Parser parser;
|
||||||
|
Compiler compiler;
|
||||||
|
|
||||||
|
initLexer(&lexer, source);
|
||||||
|
initParser(&parser, &lexer);
|
||||||
|
initCompiler(&compiler);
|
||||||
|
|
||||||
|
//run the parser until the end of the source
|
||||||
|
ASTNode* node = scanParser(&parser);
|
||||||
|
while(node != NULL) {
|
||||||
|
//pack up and leave
|
||||||
|
if (node->type == AST_NODEERROR) {
|
||||||
|
printf(ERROR "error node detected\n" RESET);
|
||||||
|
freeNode(node);
|
||||||
|
freeCompiler(&compiler);
|
||||||
|
freeParser(&parser);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeCompiler(&compiler, node);
|
||||||
|
freeNode(node);
|
||||||
|
node = scanParser(&parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
//get the bytecode dump
|
||||||
|
unsigned char* tb = collateCompiler(&compiler, (int*)(size));
|
||||||
|
|
||||||
|
//cleanup
|
||||||
|
freeCompiler(&compiler);
|
||||||
|
freeParser(&parser);
|
||||||
|
//no lexer to clean up
|
||||||
|
|
||||||
|
//finally
|
||||||
|
return tb;
|
||||||
|
}
|
||||||
|
|
||||||
|
void error(char* msg) {
|
||||||
|
printf(msg);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
//utilities
|
||||||
|
typedef struct ArbitraryData {
|
||||||
|
int value;
|
||||||
|
} ArbitraryData;
|
||||||
|
|
||||||
|
static int produce(Interpreter* interpreter, LiteralArray* arguments) {
|
||||||
|
ArbitraryData* data = ALLOCATE(ArbitraryData, 1);
|
||||||
|
data->value = 42;
|
||||||
|
|
||||||
|
Literal o = TO_OPAQUE_LITERAL(data);
|
||||||
|
|
||||||
|
pushLiteralArray(&interpreter->stack, o);
|
||||||
|
|
||||||
|
freeLiteral(o);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int consume(Interpreter* interpreter, LiteralArray* arguments) {
|
||||||
|
Literal o = popLiteralArray(arguments);
|
||||||
|
|
||||||
|
Literal idn = o;
|
||||||
|
|
||||||
|
if (parseIdentifierToValue(interpreter, &o)) {
|
||||||
|
freeLiteral(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IS_OPAQUE(o) && ((ArbitraryData*)(AS_OPAQUE(o)))->value == 42) {
|
||||||
|
ArbitraryData* data = (ArbitraryData*)AS_OPAQUE(o);
|
||||||
|
|
||||||
|
FREE(ArbitraryData, data);
|
||||||
|
|
||||||
|
//all went well
|
||||||
|
freeLiteral(o);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf(ERROR "opaque failed: %d\n" RESET, IS_OPAQUE(o));
|
||||||
|
|
||||||
|
exit(-1);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
{
|
||||||
|
size_t size = 0;
|
||||||
|
char* source = readFile("../scripts/test/opaque-data-type.toy", &size);
|
||||||
|
unsigned char* tb = compileString(source, &size);
|
||||||
|
free((void*)source);
|
||||||
|
|
||||||
|
if (!tb) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Interpreter interpreter;
|
||||||
|
initInterpreter(&interpreter);
|
||||||
|
|
||||||
|
injectNativeFn(&interpreter, "produce", produce);
|
||||||
|
injectNativeFn(&interpreter, "consume", consume);
|
||||||
|
|
||||||
|
//run teh script
|
||||||
|
runInterpreter(&interpreter, tb, size);
|
||||||
|
|
||||||
|
//clean up
|
||||||
|
freeInterpreter(&interpreter);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf(NOTICE "All good\n" RESET);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user